# Top level -*- Makefile -*- for GHDL. # Copyright (C) 2002 - 2014 Tristan Gingold # # GHDL is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2, or (at your option) any later # version. # # GHDL is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with GCC; see the file COPYING. If not, write to the Free # Software Foundation, 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. CC=@CC@ CXX=@CXX@ build=@build@ srcdir=@srcdir@ abs_srcdir=@abs_srcdir@ GNATMAKE=@GNATMAKE@ MAKE=@MAKE@ prefix=@prefix@ backend=@backend@ libdirsuffix=@libdirsuffix@ libdirreverse=@libdirreverse@ EXEEXT=@EXEEXT@ SOEXT=@SOEXT@ PIC_FLAGS=@PIC_FLAGS@ gcc_src_dir=@gcc_src_dir@ LLVM_CONFIG=@llvm_config@ LDFLAGS=@LDFLAGS@ LIBBACKTRACE=@backtrace_lib@ llvm_be=@llvm_be@ build_mode=@build_mode@ ghdl_version=@ghdl_version@ libghdl_version=@libghdl_version@ enable_werror=@enable_werror@ enable_checks=@enable_checks@ enable_openieee=@enable_openieee@ enable_libghdl=@enable_libghdl@ default_pic=@default_pic@ INSTALL_PROGRAM=install -m 755 INSTALL_DATA=install -m 644 PWD?=$(shell pwd) DESTDIR= bindir=$(prefix)/bin libdir=$(prefix)/lib incdir=$(prefix)/include MKDIR=mkdir LN=ln -s CP=cp MV=mv SED=sed GRT_RANLIB=ranlib GHDL_DESC?=tarball VHDL_LIB_DIR=$(prefix)/$(libdirsuffix) ifeq "$(enable_checks)" "true" # Debug + checks OPT_FLAGS+=-g ADA_FLAGS+=-gnata LIB_CFLAGS+= else # Optimize + no checks ADA_FLAGS+=-gnatpn OPT_FLAGS+=-O LIB_CFLAGS=-O endif # Profiling. #ADA_FLAGS+=-gnatn #OPT_FLAGS+=-pg -O # Coverage ifeq "$(build_mode)" "coverage" OPT_FLAGS+=-fprofile-arcs -ftest-coverage endif GNATFLAGS=-gnat12 -gnaty3befhkmr -gnatwa -gnatwC -gnatf $(OPT_FLAGS) $(ADA_FLAGS) GRT_FLAGS=$(OPT_FLAGS) ifeq "$(default_pic)" "true" GRT_FLAGS+=$(PIC_FLAGS) endif WARN_CFLAGS=-Wall GNAT_BARGS=-bargs -E # Warnings as errors. Comment this line if a warning looks not valid. ifeq "$(enable_werror)" "true" GNATFLAGS+=-gnatwe endif target=$(build) #target=i686-pc-linux-gnu #target=x86_64-pc-linux-gnu #target=i686-apple-darwin #target=x86_64-apple-darwin #target=i386-pc-mingw32 ifeq ($(SOEXT),.so) SHLIB_FLAGS=-Wl,-soname,$@ else ifeq ($(SOEXT),.dll) SHLIB_FLAGS= else ifeq ($(SOEXT),.dylib) # Allow the use of -rpath in executable SHLIB_FLAGS=-Wl,-install_name,@rpath/$@ endif # Flags for libraries LIBSRC_DIR:=$(srcdir)/libraries LIBDST_DIR:=$(libdirsuffix) LIBVHDL_FLAGS_TO_PASS=\ LIBSRC_DIR="$(LIBSRC_DIR)" \ LIBDST_DIR="$(LIBDST_DIR)" \ enable_openieee="$(enable_openieee)" \ LN="$(LN)" CP="$(CP)" MKDIR="$(MKDIR)" # Object files from grt for the synthesis. GRT_SYNTH_OBJS=grt-cstdio.o all: Makefile all.$(backend) all.libghdl install: install.$(backend) # Generate std.standard package VHDL source $(DESTDIR)$(bindir)/ghdl$(EXEEXT) --disp-standard --std=87 > $(DESTDIR)$(VHDL_LIB_DIR)/src/std/v87/standard.vhdl $(DESTDIR)$(bindir)/ghdl$(EXEEXT) --disp-standard --std=93 > $(DESTDIR)$(VHDL_LIB_DIR)/src/std/v93/standard.vhdl $(DESTDIR)$(bindir)/ghdl$(EXEEXT) --disp-standard --std=08 > $(DESTDIR)$(VHDL_LIB_DIR)/src/std/v08/standard.vhdl uninstall: uninstall.$(backend) test: test.$(backend) copy-sources: copy-sources.$(backend) libs: libs.vhdl.$(backend) ./config.status: $(srcdir)/configure @if [ -e $@ ]; then \ echo "./config.status --reconfigure"; \ ./config.status --reconfigure; \ else \ echo "config.status is lost, please configure manually" ; \ exit 1; \ fi Makefile: $(srcdir)/Makefile.in ./config.status ./config.status setup.py: $(srcdir)/setup.py.in ./config.status ./config.status GHDL_COMMON_INCFLAGS=-aI$(srcdir)/src -aI$(srcdir)/src/vhdl -aI$(srcdir)/src/synth -aI$(srcdir)/src/grt -aI$(srcdir)/src/psl -aI$(srcdir)/src/vhdl/translate GRTSRCDIR=$(abs_srcdir)/src/grt include $(srcdir)/src/grt/Makefile.inc version.tmp: $(srcdir)/src/version.in force # Create version.tmp from version.in, using git date/hash, or envvar GHDL_DESC. Defaults to 'tarball'. if test -d $(srcdir)/.git && desc=`cd $(srcdir); git describe --dirty`; then GHDL_DESC="$$desc"; fi; \ $(SED) -e "s/[(].*[)]/($$GHDL_DESC)/" -e "s/@VER@/$(ghdl_version)/" < $< > $@; \ version.ads: version.tmp # Change version.ads only if version.tmp has been modified to avoid # useless rebuild. if [ ! -r $@ ] || ! cmp $< $@ > /dev/null; then $(CP) $< $@; fi #################### For mcode backend ############################## all.mcode: ghdl_mcode$(EXEEXT) libs.vhdl.mcode all.vpi GHDL_MCODE_INCFLAGS=$(GHDL_COMMON_INCFLAGS) -aI$(srcdir)/src/ghdldrv -aI$(srcdir)/src/ortho -aI$(srcdir)/src/ortho/mcode -aI$(srcdir)/src/synth ghdl_mcode$(EXEEXT): GRT_FLAGS+=-DWITH_GNAT_RUN_TIME ghdl_mcode$(EXEEXT): $(GRT_ADD_OBJS) $(GRT_SRC_DEPS) $(ORTHO_DEPS) \ memsegs_c.o chkstk.o version.ads force $(GNATMAKE) -o $@ -gnat12 $(GHDL_MCODE_INCFLAGS) $(GNATFLAGS) -gnatw.A ghdl_jit.adb $(GNAT_BARGS) -largs memsegs_c.o chkstk.o $(GRT_ADD_OBJS) $(LDFLAGS) $(GNAT_LARGS) $(subst @,$(GRTSRCDIR),$(GRT_EXTRA_LIB) $(GRT_EXEC_OPTS)) memsegs_c.o: $(srcdir)/src/ortho/mcode/memsegs_c.c $(CC) -c $(OPT_FLAGS) -o $@ $< libs.vhdl.mcode: ghdl_mcode$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_mcode$(EXEEXT) GHDL_FLAGS="" VHDL_COPY_OBJS=no vhdl.libs.all install.mcode.program: install.dirs ghdl_mcode$(EXEEXT) $(INSTALL_PROGRAM) ghdl_mcode$(EXEEXT) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) uninstall.mcode.program: $(RM) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) install.mcode: install.mcode.program install.vhdllib install.vpi install.libghdl uninstall.mcode: uninstall.mcode.program uninstall.vhdllib uninstall.vpi uninstall.libghdl test.mcode: ghdl_mcode$(EXEEXT) cd $(srcdir)/testsuite; GHDL=$(CURDIR)/ghdl_mcode$(EXEEXT) ./testsuite.sh oread-mcode$(EXEEXT): force $(MAKE) -f $(srcdir)/src/ortho/mcode/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATFLAGS="-aI$(srcdir)/src/ortho/oread $(GNATFLAGS)" all #################### For gcc backend ############################## gcc_vhdl_dir=$(gcc_src_dir)/gcc/vhdl all.gcc: @echo "For installing sources in gcc, do:" @echo "1) $(MAKE) copy-sources" @echo "2) configure, build and install gcc" @echo "3) $(MAKE) ghdllib" @echo "4) $(MAKE) install" copy-sources.gcc: version.ads $(RM) -rf $(gcc_vhdl_dir) $(MKDIR) $(gcc_vhdl_dir) $(CP) -p version.ads ghdlsynth_maybe.ads $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/vhdl/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/vhdl/translate/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/synth/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/psl/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-types.ads $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-c.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-fcvt.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-algos.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-vstrings.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-cstdio.c $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-stdio.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-table.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-files_operations.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-to_strings.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/grt/grt-severity.ads $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/ortho/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/ortho/gcc/*.ad? $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/ortho/gcc/*.c $(gcc_vhdl_dir) $(CP) -p $(srcdir)/src/ortho/gcc/*.opt $(gcc_vhdl_dir) base_ver=`cat $(gcc_src_dir)/gcc/BASE-VER`; \ case $$base_ver in \ 4.9*) gcc_ortho_lang=ortho-lang-49.c ;; \ 5.*) gcc_ortho_lang=ortho-lang-5.c ;; \ 6 | 6.*) gcc_ortho_lang=ortho-lang-6.c ;; \ 7.*) gcc_ortho_lang=ortho-lang-7.c ;; \ 8.*) gcc_ortho_lang=ortho-lang-8.c ;; \ 9.*) gcc_ortho_lang=ortho-lang-9.c ;; \ 10.*) gcc_ortho_lang=ortho-lang-9.c ;; \ *) echo "Mismatch gcc version from $(gcc_src_dir)"; \ echo "Need gcc version 4.9.x, 5.x, 6.x, 7.x, 8.x, 9.x or 10.x"; \ exit 1 ;; \ esac; \ $(CP) -p $(srcdir)/src/ortho/gcc/$$gcc_ortho_lang \ $(gcc_vhdl_dir)/ortho-lang.c $(CP) -p $(srcdir)/doc/ghdl.texi $(srcdir)/doc/ghdl.1 $(gcc_vhdl_dir) $(MKDIR) $(gcc_vhdl_dir)/ghdldrv $(CP) -pR $(srcdir)/src/ghdldrv/*.ad? $(gcc_vhdl_dir)/ghdldrv $(CP) -p $(srcdir)/dist/gcc/Make-lang.in $(gcc_vhdl_dir) $(CP) -p $(srcdir)/dist/gcc/config-lang.in $(gcc_vhdl_dir) $(CP) -p $(srcdir)/dist/gcc/lang-options.h $(gcc_vhdl_dir) $(CP) -p $(srcdir)/dist/gcc/lang-specs.h $(gcc_vhdl_dir) # To build vhdl libs using a non-installed ghdl, define GHDL_GCC_BIN to the # path of ghdl and GHDL1_GCC_BIN to path of ghdl1 GHDL_GCC_BIN=$(DESTDIR)$(bindir)/ghdl$(EXEEXT) GHDL1_GCC_BIN= # --GHDL1=/path/to/ghdl1 libs.vhdl.gcc: $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL="$(GHDL_GCC_BIN)" GHDL_FLAGS="$(GHDL1_GCC_BIN) $(LIB_CFLAGS)" vhdl.libs.all libs.vhdl.standard ghdllib: libs.vhdl.gcc $(libdirsuffix)/libgrt.a all.libghdl install.gcc: install.vhdllib install.grt install.vpi install.libghdl uninstall.gcc: uninstall.vhdllib uninstall.grt uninstall.vpi uninstall.libghdl test.gcc: cd $(srcdir)/testsuite; GHDL=$(GHDL_GCC_BIN) ./testsuite.sh #################### For gcc backend - development only (local build) #### GHDL_GCC_INCFLAGS=$(GHDL_COMMON_INCFLAGS) ghdl1-gcc$(EXEEXT): version.ads force $(MAKE) -f $(srcdir)/src/ortho/gcc/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATFLAGS="$(GHDL_GCC_INCFLAGS) $(GNATFLAGS)" all ghdl_gcc$(EXEEXT): version.ads $(GRT_SYNTH_OBJS) force $(GNATMAKE) $(GHDL_GCC_INCFLAGS) -aI$(srcdir)/src/ghdldrv \ $(GNATFLAGS) ghdl_gcc $(GNAT_BARGS) \ -largs $(GRT_SYNTH_OBJS) $(GNAT_LARGS) libs.vhdl.local_gcc: ghdl_gcc$(EXEEXT) ghdl1-gcc$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_gcc$(EXEEXT) GHDL_FLAGS="--GHDL1=$(PWD)/ghdl1-gcc$(EXEEXT) $(LIB_CFLAGS)" vhdl.libs.all libs.vhdl.standard ghdl1-debug$(EXEEXT): version.ads force $(MAKE) -f $(srcdir)/src/ortho/debug/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATFLAGS="$(GHDL_GCC_INCFLAGS) $(GNATFLAGS)" \ GNATMAKE="$(GNATMAKE)" all oread-gcc$(EXEEXT): force $(MAKE) -f $(srcdir)/src/ortho/gcc/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATFLAGS="-aI$(srcdir)/src/ortho/oread $(GNATFLAGS)" all #################### For llvm backend ############################## GHDL_LLVM_INCFLAGS=$(GHDL_COMMON_INCFLAGS) -aI$(srcdir)/src/ghdldrv -aI$(srcdir)/src/ortho -aI$(srcdir)/src/ortho/$(llvm_be) -aI$(srcdir)/src/synth all.llvm: ghdl1-llvm$(EXEEXT) ghdl_llvm$(EXEEXT) grt-all libs.vhdl.llvm all.vpi all.llvm_jit: ghdl_llvm_jit$(EXEEXT) ghdl_llvm_jit$(EXEEXT): GRT_FLAGS+=-DWITH_GNAT_RUN_TIME ghdl_llvm_jit$(EXEEXT): $(GRT_ADD_OBJS) $(GRT_SRC_DEPS) $(ORTHO_DEPS) \ llvm-cbindings.o version.ads force $(GNATMAKE) -o $@ $(GHDL_LLVM_INCFLAGS) $(GNATFLAGS) ghdl_jit.adb \ $(GNAT_BARGS) -largs llvm-cbindings.o $(GNAT_LARGS) $(GRT_ADD_OBJS) \ $(subst @,$(GRTSRCDIR),$(GRT_EXTRA_LIB)) --LINK=$(CXX) \ `$(LLVM_CONFIG) --ldflags --libs --system-libs` $(LDFLAGS) llvm-cbindings.o: $(srcdir)/src/ortho/$(llvm_be)/llvm-cbindings.cpp $(CXX) -c `$(LLVM_CONFIG) --includedir --cxxflags` \ $(OPT_FLAGS) -o $@ $< libs.vhdl.llvmjit: ghdl_llvm_jit$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_llvm$(EXEEXT) GHDL_FLAGS="" VHDLLIBS_COPY_OBJS=no vhdl.libs.all libs.vhdl.llvm: ghdl_llvm$(EXEEXT) ghdl1-llvm$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_llvm$(EXEEXT) GHDL_FLAGS="--GHDL1=$(PWD)/ghdl1-llvm$(EXEEXT) $(LIB_CFLAGS)" vhdl.libs.all libs.vhdl.standard libs.vhdl.debug-llvm: ghdl_llvm$(EXEEXT) ghdl1-debug$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_llvm$(EXEEXT) GHDL_FLAGS="--GHDL1=$(PWD)/ghdl1-debug$(EXEEXT) $(LIB_CFLAGS)" vhdl.libs.all libs.vhdl.standard ghdl_llvm$(EXEEXT): version.ads $(GRT_SYNTH_OBJS) force $(GNATMAKE) $(GHDL_LLVM_INCFLAGS) \ -aI$(srcdir)/src/ghdldrv $(GNATFLAGS) \ ghdl_llvm $(GNAT_BARGS) \ -largs $(LDFLAGS) $(GRT_SYNTH_OBJS) $(GNAT_LARGS) ghdl1-llvm$(EXEEXT): version.ads force $(MAKE) -f $(srcdir)/src/ortho/$(llvm_be)/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATFLAGS="$(GHDL_LLVM_INCFLAGS) $(GNATFLAGS)" LDFLAGS="$(LDFLAGS)" \ LLVM_CONFIG="$(LLVM_CONFIG)" CXX="$(CXX)" \ CFLAGS="$(WARN_FLAGS) $(OPT_FLAGS)" \ GNATMAKE="$(GNATMAKE)" all oread-llvm$(EXEEXT): force $(MAKE) -f $(srcdir)/src/ortho/$(llvm_be)/Makefile \ ortho_srcdir=$(srcdir)/src/ortho ortho_exec=$@ \ GNATMAKE="$(GNATMAKE)" \ GNATFLAGS="-aI$(srcdir)/src/ortho/oread $(GNATFLAGS)" \ LLVM_CONFIG="$(LLVM_CONFIG)" CXX="$(CXX)" install.llvm.program: install.dirs ghdl1-llvm$(EXEEXT) ghdl_llvm$(EXEEXT) $(INSTALL_PROGRAM) ghdl_llvm$(EXEEXT) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) $(INSTALL_PROGRAM) ghdl1-llvm$(EXEEXT) $(DESTDIR)$(bindir)/ghdl1-llvm$(EXEEXT) test.llvm: ghdl_llvm$(EXEEXT) cd $(srcdir)/testsuite; GHDL=$(CURDIR)/ghdl_llvm$(EXEEXT) ./testsuite.sh uninstall.llvm.program: $(RM) $(DESTDIR)$(bindir)/ghdl1-llvm$(EXEEXT) $(RM) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) install.llvm: install.llvm.program install.vhdllib install.grt install.vpi install.libghdl uninstall.llvm: uninstall.llvm.program uninstall.vhdllib uninstall.grt uninstall.vpi uninstall.libghdl ################ For simul (no code generation, interpretation) ########## GHDL_SIMUL_INCFLAGS=$(GHDL_COMMON_INCFLAGS) -aI$(srcdir)/src/ghdldrv -aI$(srcdir)/src/vhdl/simulate -aI$(srcdir)/src/synth ghdl_simul$(EXEEXT): $(GRT_ADD_OBJS) $(GRT_SRC_DEPS) version.ads force $(GNATMAKE) $(GHDL_SIMUL_INCFLAGS) $(GNATFLAGS) -gnat12 ghdl_simul $(GNAT_BARGS) -largs $(GNAT_LARGS) $(GRT_ADD_OBJS) $(subst @,$(GRTSRCDIR),$(GRT_EXTRA_LIB)) libs.vhdl.simul: ghdl_simul$(EXEEXT) $(MAKE) -f $(srcdir)/libraries/Makefile.inc $(LIBVHDL_FLAGS_TO_PASS) GHDL=$(PWD)/ghdl_simul$(EXEEXT) GHDL_FLAGS="" VHDLLIBS_COPY_OBJS=no vhdl.libs.all install.simul.program: install.dirs ghdl_simul$(EXEEXT) $(INSTALL_PROGRAM) ghdl_simul$(EXEEXT) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) uninstall.simul.program: $(RM) $(DESTDIR)$(bindir)/ghdl$(EXEEXT) install.simul: install.simul.program install.vhdllib uninstall.simul: uninstall.simul.program uninstall.vhdllib ################ libghdl (library for the vhdl part) #################### LIBGHDL_INCFLAGS=$(GHDL_COMMON_INCFLAGS) -aI$(srcdir)/src/synth -aI$(srcdir)/src/vhdl/libghdl -aI$(srcdir)/src/ghdldrv libghdl_name=libghdl-$(libghdl_version)$(SOEXT) LIBGHDL_GRT_OBJS= pic/grt-cstdio.o $(libghdl_name): $(GRT_SRC_DEPS) $(LIBGHDL_GRT_OBJS) version.ads force # Use -g for gnatlink so that the binder file is not removed. We need # it for libghdl.a $(GNATMAKE) -I- -aI. -D pic -z libghdl -o $@ -gnat12 $(GNATFLAGS) $(PIC_FLAGS) $(LIBGHDL_INCFLAGS) -bargs -shared -Llibghdl_ -largs -g -shared $(SHLIB_FLAGS) $(LIBGHDL_GRT_OBJS) # On windows, gnatmake (via Osint.Executable_Name) always appends .exe # Adjust. (Other solution: use gnatmake for compilation and binding, # then use gnatlink directly for linking). ifeq ($(SOEXT),.dll) -mv $(libghdl_name).exe $(libghdl_name) endif libghdl.a: $(libghdl_name) # Also create a static library sed -e '1,/-- BEGIN/d' -e '/-- END/,$$d' -e 's/^ -- //' < b~libghdl.adb > libghdl.bind $(RM) -f $@ ar rc $@ b~libghdl.o $(LIBGHDL_GRT_OBJS) `sed -e /^-/d < libghdl.bind` grep adalib libghdl.bind | sed -e 's/^-L//' -e 's@adalib/@adalib/libgnat.a@' > libghdl.link $(srcdir)/src/synth/ghdlsynth_gates.h: $(srcdir)/src/synth/netlists.ads $(srcdir)/src/synth/netlists-gates.ads echo "/* DO NOT MODIFY" > $@ echo " This file is automatically generated by Makefile. */" >> $@ echo "enum Module_Id {" >> $@ grep -h "constant Module_Id :=" $^ | sed -e '/constant Module_Id :=/s/:.*://' -e 's/;/,/' -e 's/ *--.*//' >> $@ echo "};" >> $@ echo "" >> $@ echo "enum Param_Type {" >> $@ grep ' Param_' $(srcdir)/src/synth/netlists.ads >> $@ echo "};" >> $@ all.libghdl.true: $(libghdl_name) libghdl.a all.libghdl.false: all.libghdl: all.libghdl.$(enable_libghdl) install.libghdl.include: install.dirs $(srcdir)/src/synth/ghdlsynth_gates.h $(INSTALL_DATA) -p $(srcdir)/src/synth/ghdlsynth.h $(DESTDIR)$(incdir)/ $(INSTALL_DATA) -p $(srcdir)/src/synth/ghdlsynth_gates.h $(DESTDIR)$(incdir)/ install.libghdl.lib: $(INSTALL_PROGRAM) -p $(libghdl_name) $(DESTDIR)$(libdir)/ $(INSTALL_PROGRAM) -p libghdl.a $(DESTDIR)$(libdir)/ $(INSTALL_DATA) -p libghdl.link $(DESTDIR)$(libdir)/ install.libghdl.true: install.libghdl.lib install.libghdl.include install.libghdl.false: install.libghdl: install.libghdl.$(enable_libghdl) uninstall.libghdl: $(RM) $(DESTDIR)$(libdir)/$(libghdl_name) libghdl-py.tgz: [ -d pythonb ] || $(MKDIR) pythonb $(CP) -r $(srcdir)/pyGHDL/libghdl $(srcdir)/setup.py pythonb tar -zcvf $@ -C pythonb . ################ ghwdump ################################################# GHWDUMP_OBJS=ghwdump.o ghwlib.o ghwdump: $(GHWDUMP_OBJS) $(CC) -o $@ $(GHWDUMP_OBJS) ghwdump.o: $(srcdir)/src/grt/ghwdump.c $(srcdir)/src/grt/ghwlib.h $(CC) -c -o $@ $< $(OPT_FLAGS) $(WARN_CFLAGS) ghwlib.o: $(srcdir)/src/grt/ghwlib.c $(srcdir)/src/grt/ghwlib.h $(CC) -c -o $@ $< $(OPT_FLAGS) $(WARN_CFLAGS) ############### grt ##################################################### install.grt: \ $(libdirsuffix)/libgrt.a \ $(libdirsuffix)/grt.lst \ $(libdirsuffix)/grt-exec.lst \ $(libdirsuffix)/grt-shared.lst \ install.dirs $(INSTALL_DATA) $(libdirsuffix)/libgrt.a $(DESTDIR)$(VHDL_LIB_DIR)/libgrt.a $(INSTALL_DATA) $(libdirsuffix)/grt.lst $(DESTDIR)$(VHDL_LIB_DIR)/grt.lst $(INSTALL_DATA) $(libdirsuffix)/grt-exec.lst $(DESTDIR)$(VHDL_LIB_DIR)/grt-exec.lst $(INSTALL_DATA) $(libdirsuffix)/grt-shared.lst $(DESTDIR)$(VHDL_LIB_DIR)/grt-shared.lst $(INSTALL_DATA) $(GRTSRCDIR)/grt.ver $(DESTDIR)$(VHDL_LIB_DIR)/grt.ver test "x$(LIBBACKTRACE)" = x || test "x$(backend)" = xgcc || \ $(INSTALL_DATA) $(LIBBACKTRACE) $(DESTDIR)$(VHDL_LIB_DIR)/libbacktrace.a uninstall.grt: $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/libgrt.a $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/grt.lst $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/grt-exec.lst $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/grt-shared.lst $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/grt.ver $(RM) $(DESTDIR)$(VHDL_LIB_DIR)/libbacktrace.a ################ VPI ##################################################### vpi_thunk.o: $(GRTSRCDIR)/vpi_thunk.c $(GRTSRCDIR)/vpi_thunk.h # Do not use OPT_FLAGS (do not enable coverage) $(CC) -c -o $@ $< $(PIC_FLAGS) -O $(WARN_CFLAGS) libghdlvpi$(SOEXT): vpi_thunk.o $(CC) -o $@ $< -shared $(SHLIB_FLAGS) all.vpi: libghdlvpi$(SOEXT) install.vpi: all.vpi install.dirs $(INSTALL_PROGRAM) -p libghdlvpi$(SOEXT) $(DESTDIR)$(libdir)/ $(INSTALL_DATA) -p $(GRTSRCDIR)/vpi_user.h $(DESTDIR)$(incdir)/ uninstall.vpi: $(RM) -f $(DESTDIR)$(libdir)/libghdlvpi$(SOEXT) $(RM) -f $(DESTDIR)$(incdir)/vpi_user.h install.vpi.local: all.vpi $(MK
/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  David Shah <david@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "cells.h"
#include <algorithm>
#include "design_utils.h"
#include "log.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
{
    IdString id = ctx->id(name);
    cell->ports[id] = PortInfo{id, nullptr, dir};
}

std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::string name)
{
    static int auto_idx = 0;
    std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
    if (name.empty()) {
        new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
    } else {
        new_cell->name = ctx->id(name);
    }
    new_cell->type = type;

    auto copy_bel_ports = [&]() {
        // First find a Bel of the target type
        BelId tgt;
        for (auto bel : ctx->getBels()) {
            if (ctx->getBelType(bel) == type) {
                tgt = bel;
                break;
            }
        }
        NPNR_ASSERT(tgt != BelId());
        for (auto port : ctx->getBelPins(tgt)) {
            add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port));
        }
    };

    if (type == ctx->id("TRELLIS_SLICE")) {
        new_cell->params[ctx->id("MODE")] = std::string("LOGIC");
        new_cell->params[ctx->id("GSR")] = std::string("DISABLED");
        new_cell->params[ctx->id("SRMODE")] = std::string("LSR_OVER_CE");
        new_cell->params[ctx->id("CEMUX")] = std::string("1");
        new_cell->params[ctx->id("CLKMUX")] = std::string("CLK");
        new_cell->params[ctx->id("LSRMUX")] = std::string("LSR");
        new_cell->params[ctx->id("LUT0_INITVAL")] = Property(0, 16);
        new_cell->params[ctx->id("LUT1_INITVAL")] = Property(0, 16);
        new_cell->params[ctx->id("REG0_SD")] = std::string("0");
        new_cell->params[ctx->id("REG1_SD")] = std::string("0");
        new_cell->params[ctx->id("REG0_REGSET")] = std::string("RESET");
        new_cell->params[ctx->id("REG1_REGSET")] = std::string("RESET");
        new_cell->params[ctx->id("CCU2_INJECT1_0")] = std::string("NO");
        new_cell->params[ctx->id("CCU2_INJECT1_1")] = std::string("NO");
        new_cell->params[ctx->id("WREMUX")] = std::string("WRE");

        add_port(ctx, new_cell.get(), "A0", PORT_IN);
        add_port(ctx, new_cell.get(), "B0", PORT_IN);
        add_port(ctx, new_cell.get(), "C0", PORT_IN);
        add_port(ctx, new_cell.get(), "D0", PORT_IN);

        add_port(ctx, new_cell.get(), "A1", PORT_IN);
        add_port(ctx, new_cell.get(), "B1", PORT_IN);
        add_port(ctx, new_cell.get(), "C1", PORT_IN);
        add_port(ctx, new_cell.get(), "D1", PORT_IN);

        add_port(ctx, new_cell.get(), "M0", PORT_IN);
        add_port(ctx, new_cell.get(), "M1", PORT_IN);

        add_port(ctx, new_cell.get(), "FCI", PORT_IN);
        add_port(ctx, new_cell.get(), "FXA", PORT_IN);
        add_port(ctx, new_cell.get(), "FXB", PORT_IN);

        add_port(ctx, new_cell.get(), "CLK", PORT_IN);
        add_port(ctx, new_cell.get(), "LSR", PORT_IN);
        add_port(ctx, new_cell.get(), "CE", PORT_IN);

        add_port(ctx, new_cell.get(), "DI0", PORT_IN);
        add_port(ctx, new_cell.get(), "DI1", PORT_IN);

        add_port(ctx, new_cell.get(), "WD0", PORT_IN);
        add_port(ctx, new_cell.get(), "WD1", PORT_IN);
        add_port(ctx, new_cell.get(), "WAD0", PORT_IN);
        add_port(ctx, new_cell.get(), "WAD1", PORT_IN);
        add_port(ctx, new_cell.get(), "WAD2", PORT_IN);
        add_port(ctx, new_cell.get(), "WAD3", PORT_IN);
        add_port(ctx, new_cell.get(), "WRE", PORT_IN);
        add_port(ctx, new_cell.get(), "WCK", PORT_IN);

        add_port(ctx, new_cell.get(), "F0", PORT_OUT);
        add_port(ctx, new_cell.get(), "Q0", PORT_OUT);
        add_port(ctx, new_cell.get(), "F1", PORT_OUT);
        add_port(ctx, new_cell.get(), "Q1", PORT_OUT);

        add_port(ctx, new_cell.get(), "FCO", PORT_OUT);
        add_port(ctx, new_cell.get(), "OFX0", PORT_OUT);
        add_port(ctx, new_cell.get(), "OFX1", PORT_OUT);

        add_port(ctx, new_cell.get(), "WDO0", PORT_OUT);
        add_port(ctx, new_cell.get(), "WDO1", PORT_OUT);
        add_port(ctx, new_cell.get(), "WDO2", PORT_OUT);
        add_port(ctx, new_cell.get(), "WDO3", PORT_OUT);
        add_port(ctx, new_cell.get(), "WADO0", PORT_OUT);
        add_port(ctx, new_cell.get(), "WADO1", PORT_OUT);
        add_port(ctx, new_cell.get(), "WADO2", PORT_OUT);
        add_port(ctx, new_cell.get(), "WADO3", PORT_OUT);
    } else if (type == ctx->id("TRELLIS_IO")) {
        new_cell->params[ctx->id("DIR")] = std::string("INPUT");
        new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33");
        new_cell->params[ctx->id("DATAMUX_ODDR")] = std::string("PADDO");
        new_cell->params[ctx->id("DATAMUX_MDDR")] = std::string("PADDO");

        add_port(ctx, new_cell.get(), "B", PORT_INOUT);
        add_port(ctx, new_cell.get(), "I", PORT_IN);
        add_port(ctx, new_cell.get(), "T", PORT_IN);
        add_port(ctx, new_cell.get(), "O", PORT_OUT);

        add_port(ctx, new_cell.get(), "IOLDO", PORT_IN);
        add_port(ctx, new_cell.get(), "IOLTO", PORT_IN);

    } else if (type == ctx->id("LUT4")) {
        new_cell->params[ctx->id("INIT")] = Property(0, 16);

        add_port(ctx, new_cell.get(), "A", PORT_IN);
        add_port(ctx, new_cell.get(), "B", PORT_IN);
        add_port(ctx, new_cell.get(), "C", PORT_IN);
        add_port(ctx, new_cell.get(), "D", PORT_IN);
        add_port(ctx, new_cell.get(), "Z", PORT_OUT);
    } else if (type == ctx->id("CCU2C")) {
        new_cell->params[ctx->id("INIT0")] = Property(0, 16);
        new_cell->params[ctx->id("INIT1")] = Property(0, 16);
        new_cell->params[ctx->id("INJECT1_0")] = std::string("YES");
        new_cell->params[ctx->id("INJECT1_1")] = std::string("YES");

        add_port(ctx, new_cell.get(), "CIN", PORT_IN);

        add_port(ctx, new_cell.get(), "A0", PORT_IN);
        add_port(ctx, new_cell.get(), "B0", PORT_IN);
        add_port(ctx, new_cell.get(), "C0", PORT_IN);
        add_port(ctx, new_cell.get(), "D0", PORT_IN);

        add_port(ctx, new_cell.get(), "A1", PORT_IN);
        add_port(ctx, new_cell.get(), "B1", PORT_IN);
        add_port(ctx, new_cell.get(), "C1", PORT_IN);
        add_port(ctx, new_cell.get(), "D1", PORT_IN);

        add_port(ctx, new_cell.get(), "S0", PORT_OUT);
        add_port(ctx, new_cell.get(), "S1", PORT_OUT);
        add_port(ctx, new_cell.get(), "COUT", PORT_OUT);

    } else if (type == ctx->id("DCCA")) {
        add_port(ctx, new_cell.get(), "CLKI", PORT_IN);
        add_port(ctx, new_cell.get(), "CLKO", PORT_OUT);
        add_port(ctx, new_cell.get(), "CE", PORT_IN);
    } else if (type == id_IOLOGIC || type == id_SIOLOGIC) {
        new_cell->params[ctx->id("MODE")] = std::string("NONE");
        new_cell->params[ctx->id("GSR")] = std::string("DISABLED");
        new_cell->params[ctx->id("CLKIMUX")] = std::string("CLK");
        new_cell->params[ctx->id("CLKOMUX")] = std::string("CLK");
        new_cell->params[ctx->id("LSRIMUX")] = std::string("0");
        new_cell->params[ctx->id("LSROMUX")] = std::string("0");
        new_cell->params[ctx->id("LSRMUX")] = std::string("LSR");

        new_cell->params[ctx->id("DELAY.OUTDEL")] = std::string("DISABLED");
        new_cell->params[ctx->id("DELAY.DEL_VALUE")] = Property(0, 7);
        new_cell->params[ctx->id("DELAY.WAIT_FOR_EDGE")] = std::string("DISABLED");

        if (type == id_IOLOGIC) {
            new_cell->params[ctx->id("IDDRXN.MODE")] = std::string("NONE");
            new_cell->params[ctx->id("ODDRXN.MODE")] = std::string("NONE");

            new_cell->params[ctx->id("MIDDRX.MODE")] = std::string("NONE");
            new_cell->params[ctx->id("MODDRX.MODE")] = std::string("NONE");
            new_cell->params[ctx->id("MTDDRX.MODE")] = std::string("NONE");

            new_cell->params[ctx->id("IOLTOMUX")] = std::string("NONE");
            new_cell->params[ctx->id("MTDDRX.DQSW_INVERT")] = std::string("DISABLED");
            new_cell->params[ctx->id("MTDDRX.REGSET")] = std::string("RESET");

            new_cell->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = std::string("NONE");
        }
        // Just copy ports from the Bel
        copy_bel_ports();
    } else if (type == id_TRELLIS_ECLKBUF) {
        add_port(ctx, new_cell.get(), "ECLKI", PORT_IN);
        add_port(ctx, new_cell.get(), "ECLKO", PORT_OUT);
    } else {
        log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
    }
    return new_cell;
}

static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value)
{
    NPNR_ASSERT(!has_ff || lc->params.at(name) == value);
    lc->params[name] = value;
}

static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port)
{
    if (has_ff) {
        NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net);
        NetInfo *ffnet = ff->ports.at(ff_port).net;
        if (ffnet != nullptr)
            ffnet->users.erase(
                    std::remove_if(ffnet->users.begin(), ffnet->users.end(),
                                   [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }),
                    ffnet->users.end());
    } else {
        replace_port(ff, ff_port, lc, lc_port);
    }
}

void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut)
{
    if (lc->hierpath == IdString())
        lc->hierpath = ff->hierpath;
    bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr;
    std::string reg = "REG" + std::to_string(index);
    set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE"));
    set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED"));
    set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1"));
    set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"));
    set_param_safe(has_ff, lc, ctx->id("CLKMUX"), str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"));

    lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0");
    lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET");
    lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, ctx->id("LSRMODE"), "LSR");
    replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK"));
    if (ff->ports.find(ctx->id("LSR")) != ff->ports.end())
        replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR"));
    if (ff->ports.find(ctx->id("CE")) != ff->ports.end())
        replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE"));

    replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index)));
    if (get_net_or_empty(ff, ctx->id("M")) != nullptr) {
        // PRLD FFs that use both M and DI
        NPNR_ASSERT(!driven_by_lut);
        // As M is used; must route DI through a new LUT
        lc->params[ctx->id(reg + "_SD")] = std::string("1");
        lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16);
        replace_port(ff, ctx->id("DI"), lc, ctx->id("D" + std::to_string(index)));
        replace_port(ff, ctx->id("M"), lc, ctx->id("M" + std::to_string(index)));
        connect_ports(ctx, lc, ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index)));
    } else {
        if (driven_by_lut) {
            replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index)));
        } else {
            replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index)));
        }
    }
}

void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
{
    if (lc->hierpath == IdString())
        lc->hierpath = lut->hierpath;
    lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] =
            get_or_default(lut->params, ctx->id("INIT"), Property(0, 16));
    replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index)));
    replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index)));
    replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));
    replace_port(lut, ctx->id("D"), lc, ctx->id("D" + std::to_string(index)));
    replace_port(lut, ctx->id("Z"), lc, ctx->id("F" + std::to_string(index)));
}

void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
{
    if (lc->hierpath == IdString())
        lc->hierpath = ccu->hierpath;
    lc->params[ctx->id("MODE")] = std::string("CCU2");
    lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16));
    lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16));

    lc->params[ctx->id("CCU2_INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES");
    lc->params[ctx->id("CCU2_INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES");

    replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI"));

    replace_port(ccu, ctx->id("A0"), lc, ctx->id("A0"));
    replace_port(ccu, ctx->id("B0"), lc, ctx->id("B0"));
    replace_port(ccu, ctx->id("C0"), lc, ctx->id("C0"));
    replace_port(ccu, ctx->id("D0"), lc, ctx->id("D0"));

    replace_port(ccu, ctx->id("A1"), lc, ctx->id("A1"));
    replace_port(ccu, ctx->id("B1"), lc, ctx->id("B1"));
    replace_port(ccu, ctx->id("C1"), lc, ctx->id("C1"));
    replace_port(ccu, ctx->id("D1"), lc, ctx->id("D1"));

    replace_port(ccu, ctx->id("S0"), lc, ctx->id("F0"));
    replace_port(ccu, ctx->id("S1"), lc, ctx->id("F1"));

    replace_port(ccu, ctx->id("COUT"), lc, ctx->id("FCO"));
}

void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc)
{
    if (lc->hierpath == IdString())
        lc->hierpath = ram->hierpath;
    lc->params[ctx->id("MODE")] = std::string("RAMW");
    replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0"));
    replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0"));
    replace_port(ram, ctx->id("WAD[2]"), lc, ctx->id("C0"));
    replace_port(ram, ctx->id("WAD[3]"), lc, ctx->id("A0"));

    replace_port(ram, ctx->id("DI[0]"), lc, ctx->id("C1"));
    replace_port(ram, ctx->id("DI[1]"), lc, ctx->id("A1"));
    replace_port(ram, ctx->id("DI[2]"), lc, ctx->id("D1"));
    replace_port(ram, ctx->id("DI[3]"), lc, ctx->id("B1"));
}

static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
{
    auto init_prop = get_or_default(ram->params, ctx->id("INITVAL"), Property(0, 64));
    NPNR_ASSERT(!init_prop.is_string);
    const std::string &idata = init_prop.str;
    NPNR_ASSERT(idata.length() == 64);
    unsigned value = 0;
    for (int i = 0; i < 16; i++) {
        char c = idata.at(4 * i + bit);
        if (c == '1')
            value |= (1 << i);
        else
            NPNR_ASSERT(c == '0' || c == 'x');
    }
    return value;
}

void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index)
{
    if (lc->hierpath == IdString())
        lc->hierpath = ram->hierpath;
    lc->params[ctx->id("MODE")] = std::string("DPRAM");
    lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE");
    lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK");

    unsigned permuted_init0 = 0, permuted_init1 = 0;
    unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1);

    for (int i = 0; i < 16; i++) {
        int permuted_addr = 0;
        if (i & 1)
            permuted_addr |= 8;
        if (i & 2)
            permuted_addr |= 2;
        if (i & 4)
            permuted_addr |= 4;
        if (i & 8)
            permuted_addr |= 1;
        if (init0 & (1 << permuted_addr))
            permuted_init0 |= (1 << i);
        if (init1 & (1 << permuted_addr))
            permuted_init1 |= (1 << i);
    }

    lc->params[ctx->id("LUT0_INITVAL")] = Property(permuted_init0, 16);
    lc->params[ctx->id("LUT1_INITVAL")] = Property(permuted_init1, 16);

    if (ram->ports.count(ctx->id("RAD[0]"))) {
        connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D0"));
        connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D1"));
    }
    if (ram->ports.count(ctx->id("RAD[1]"))) {
        connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B0"));
        connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B1"));
    }
    if (ram->ports.count(ctx->id("RAD[2]"))) {
        connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C0"));
        connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C1"));
    }
    if (ram->ports.count(ctx->id("RAD[3]"))) {
        connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A0"));
        connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A1"));
    }

    if (ram->ports.count(ctx->id("WRE")))
        connect_port(ctx, ram->ports.at(ctx->id("WRE")).net, lc, ctx->id("WRE"));
    if (ram->ports.count(ctx->id("WCK")))
        connect_port(ctx, ram->ports.at(ctx->id("WCK")).net, lc, ctx->id("WCK"));

    connect_ports(ctx, ramw, id_WADO0, lc, id_WAD0);
    connect_ports(ctx, ramw, id_WADO1, lc, id_WAD1);
    connect_ports(ctx, ramw, id_WADO2, lc, id_WAD2);
    connect_ports(ctx, ramw, id_WADO3, lc, id_WAD3);

    if (index == 0) {
        connect_ports(ctx, ramw, id_WDO0, lc, id_WD0);
        connect_ports(ctx, ramw, id_WDO1, lc, id_WD1);

        replace_port(ram, ctx->id("DO[0]"), lc, id_F0);
        replace_port(ram, ctx->id("DO[1]"), lc, id_F1);

    } else if (index == 1) {
        connect_ports(ctx, ramw, id_WDO2, lc, id_WD0);
        connect_ports(ctx, ramw, id_WDO3, lc, id_WD1);

        replace_port(ram, ctx->id("DO[2]"), lc, id_F0);
        replace_port(ram, ctx->id("DO[3]"), lc, id_F1);
    } else {
        NPNR_ASSERT_FALSE("bad DPRAM index");
    }
}

void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
                std::unordered_set<IdString> &todelete_cells)
{
    if (nxio->type == ctx->id("$nextpnr_ibuf")) {
        trio->params[ctx->id("DIR")] = std::string("INPUT");
        replace_port(nxio, ctx->id("O"), trio, ctx->id("O"));
    } else if (nxio->type == ctx->id("$nextpnr_obuf")) {
        trio->params[ctx->id("DIR")] = std::string("OUTPUT");
        replace_port(nxio, ctx->id("I"), trio, ctx->id("I"));
    } else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
        // N.B. tristate will be dealt with below
        NetInfo *i = get_net_or_empty(nxio, ctx->id("I"));
        if (i == nullptr || i->driver.cell == nullptr)
            trio->params[ctx->id("DIR")] = std::string("INPUT");
        else {
            log_info("%s: %s.%s\n", ctx->nameOf(i), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port));
            trio->params[ctx->id("DIR")] = std::string("BIDIR");
        }
        replace_port(nxio, ctx->id("I"), trio, ctx->id("I"));
        replace_port(nxio, ctx->id("O"), trio, ctx->id("O"));
    } else {
        NPNR_ASSERT(false);
    }
    NetInfo *donet = trio->ports.at(ctx->id("I")).net, *dinet = trio->ports.at(ctx->id("O")).net;

    // Rename I/O nets to avoid conflicts
    if (donet != nullptr && donet->name == nxio->name)
        rename_net(ctx, donet, ctx->id(donet->name.str(ctx) + "$TRELLIS_IO_OUT"));
    if (dinet != nullptr && dinet->name == nxio->name)
        rename_net(ctx, dinet, ctx->id(dinet->name.str(ctx) + "$TRELLIS_IO_IN"));

    if (ctx->nets.count(nxio->name)) {
        int i = 0;
        IdString new_name;
        do {
            new_name = ctx->id(nxio->name.str(ctx) + "$rename$" + std::to_string(i++));
        } while (ctx->nets.count(new_name));
        rename_net(ctx, ctx->nets.at(nxio->name).get(), new_name);
    }

    // Create a new top port net for accurate IO timing analysis and simulation netlists
    if (ctx->ports.count(nxio->name)) {
        IdString tn_netname = nxio->name;
        NPNR_ASSERT(!ctx->nets.count(tn_netname));
        std::unique_ptr<NetInfo> toplevel_net{new NetInfo};
        toplevel_net->name = tn_netname;
        connect_port(ctx, toplevel_net.get(), trio, ctx->id("B"));
        ctx->ports[nxio->name].net = toplevel_net.get();
        ctx->nets[tn_netname] = std::move(toplevel_net);
    }

    CellInfo *tbuf = net_driven_by(
            ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
            ctx->id("Y"));
    if (tbuf) {
        replace_port(tbuf, ctx->id("A"), trio, ctx->id("I"));
        // Need to invert E to form T
        std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T");
        replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A"));
        inv_lut->params[ctx->id("INIT")] = Property(21845, 16);
        connect_ports(ctx, inv_lut.get(), ctx->id("Z"), trio, ctx->id("T"));
        created_cells.push_back(std::move(inv_lut));

        if (donet->users.size() > 1) {
            for (auto user : donet->users)
                log_info("     remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
            log_error("unsupported tristate IO pattern for IO buffer '%s', "
                      "instantiate SB_IO manually to ensure correct behaviour\n",
                      nxio->name.c_str(ctx));
        }
        ctx->nets.erase(donet->name);
        todelete_cells.insert(tbuf->name);
    }
}

NEXTPNR_NAMESPACE_END