mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/YosysHQ/yosys into gowin
This commit is contained in:
		
						commit
						69fb3b8db2
					
				
					 275 changed files with 32888 additions and 2694 deletions
				
			
		
							
								
								
									
										1
									
								
								Brewfile
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Brewfile
									
										
									
									
									
								
							|  | @ -7,3 +7,4 @@ brew "graphviz" | ||||||
| brew "pkg-config" | brew "pkg-config" | ||||||
| brew "python3" | brew "python3" | ||||||
| brew "tcl-tk" | brew "tcl-tk" | ||||||
|  | brew "xdot" | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								CHANGELOG
									
										
									
									
									
								
							|  | @ -27,6 +27,7 @@ Yosys 0.9 .. Yosys 0.9-dev | ||||||
|     - Improve attribute and parameter encoding in JSON to avoid ambiguities between |     - Improve attribute and parameter encoding in JSON to avoid ambiguities between | ||||||
|       bit vectors and strings containing [01xz]* |       bit vectors and strings containing [01xz]* | ||||||
|     - Added "clkbufmap" pass |     - Added "clkbufmap" pass | ||||||
|  |     - Added "extractinv" pass and "invertible_pin" attribute | ||||||
|     - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) |     - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) | ||||||
|     - Added "synth_xilinx -ise" (experimental) |     - Added "synth_xilinx -ise" (experimental) | ||||||
|     - Added "synth_xilinx -iopad" |     - Added "synth_xilinx -iopad" | ||||||
|  | @ -38,6 +39,18 @@ Yosys 0.9 .. Yosys 0.9-dev | ||||||
|     - Improvements in pmgen: slices, choices, define, generate |     - Improvements in pmgen: slices, choices, define, generate | ||||||
|     - Added "xilinx_srl" for Xilinx shift register extraction |     - Added "xilinx_srl" for Xilinx shift register extraction | ||||||
|     - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") |     - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") | ||||||
|  |     - Added "_TECHMAP_WIREINIT_*_" attribute and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass | ||||||
|  |     - Added "-match-init" option to "dff2dffs" pass | ||||||
|  |     - Added "techmap_autopurge" support to techmap | ||||||
|  |     - Added "add -mod <modname[s]>" | ||||||
|  |     - Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones | ||||||
|  |     - Added "ice40_dsp" for Lattice iCE40 DSP packing | ||||||
|  |     - Added "xilinx_dsp" for Xilinx DSP packing | ||||||
|  |     - "synth_xilinx" to now infer DSP blocks (-nodsp to disable) | ||||||
|  |     - "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental) | ||||||
|  |     - "synth_ice40 -dsp" to infer DSP blocks | ||||||
|  |     - Added latch support to synth_xilinx | ||||||
|  |     - Added "check -mapped" | ||||||
| 
 | 
 | ||||||
| Yosys 0.8 .. Yosys 0.9 | Yosys 0.8 .. Yosys 0.9 | ||||||
| ---------------------- | ---------------------- | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -88,7 +88,7 @@ ifeq ($(OS), Darwin) | ||||||
| PLUGIN_LDFLAGS += -undefined dynamic_lookup | PLUGIN_LDFLAGS += -undefined dynamic_lookup | ||||||
| 
 | 
 | ||||||
| # homebrew search paths
 | # homebrew search paths
 | ||||||
| ifneq ($(shell which brew),) | ifneq ($(shell :; command -v brew),) | ||||||
| BREW_PREFIX := $(shell brew --prefix)/opt | BREW_PREFIX := $(shell brew --prefix)/opt | ||||||
| $(info $$BREW_PREFIX is [${BREW_PREFIX}]) | $(info $$BREW_PREFIX is [${BREW_PREFIX}]) | ||||||
| ifeq ($(ENABLE_PYOSYS),1) | ifeq ($(ENABLE_PYOSYS),1) | ||||||
|  | @ -102,8 +102,8 @@ PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) | ||||||
| export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) | export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) | ||||||
| 
 | 
 | ||||||
| # macports search paths
 | # macports search paths
 | ||||||
| else ifneq ($(shell which port),) | else ifneq ($(shell :; command -v port),) | ||||||
| PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port)) | PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) | ||||||
| CXXFLAGS += -I$(PORT_PREFIX)/include | CXXFLAGS += -I$(PORT_PREFIX)/include | ||||||
| LDFLAGS += -L$(PORT_PREFIX)/lib | LDFLAGS += -L$(PORT_PREFIX)/lib | ||||||
| PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) | PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) | ||||||
|  | @ -115,7 +115,7 @@ LDFLAGS += -rdynamic | ||||||
| LDLIBS += -lrt | LDLIBS += -lrt | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| YOSYS_VER := 0.9+36 | YOSYS_VER := 0.9+932 | ||||||
| GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) | GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) | ||||||
| OBJS = kernel/version_$(GIT_REV).o | OBJS = kernel/version_$(GIT_REV).o | ||||||
| 
 | 
 | ||||||
|  | @ -128,7 +128,7 @@ bumpversion: | ||||||
| # is just a symlink to your actual ABC working directory, as 'make mrproper'
 | # is just a symlink to your actual ABC working directory, as 'make mrproper'
 | ||||||
| # will remove the 'abc' directory and you do not want to accidentally
 | # will remove the 'abc' directory and you do not want to accidentally
 | ||||||
| # delete your work on ABC..
 | # delete your work on ABC..
 | ||||||
| ABCREV = 5776ad0 | ABCREV = 623b5e8 | ||||||
| ABCPULL = 1 | ABCPULL = 1 | ||||||
| ABCURL ?= https://github.com/berkeley-abc/abc | ABCURL ?= https://github.com/berkeley-abc/abc | ||||||
| ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 | ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 | ||||||
|  | @ -147,9 +147,9 @@ $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$ | ||||||
| include Makefile.conf | include Makefile.conf | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) | ||||||
| ifeq ($(ENABLE_PYOSYS),1) | ifeq ($(ENABLE_PYOSYS),1) | ||||||
| PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" | PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" | ||||||
| PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) |  | ||||||
| PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") | PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") | ||||||
| PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) | PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) | ||||||
| PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix) | PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix) | ||||||
|  | @ -528,6 +528,7 @@ $(eval $(call add_include_file,kernel/satgen.h)) | ||||||
| $(eval $(call add_include_file,libs/ezsat/ezsat.h)) | $(eval $(call add_include_file,libs/ezsat/ezsat.h)) | ||||||
| $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) | $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) | ||||||
| $(eval $(call add_include_file,libs/sha1/sha1.h)) | $(eval $(call add_include_file,libs/sha1/sha1.h)) | ||||||
|  | $(eval $(call add_include_file,libs/json11/json11.hpp)) | ||||||
| $(eval $(call add_include_file,passes/fsm/fsmdata.h)) | $(eval $(call add_include_file,passes/fsm/fsmdata.h)) | ||||||
| $(eval $(call add_include_file,frontends/ast/ast.h)) | $(eval $(call add_include_file,frontends/ast/ast.h)) | ||||||
| $(eval $(call add_include_file,backends/ilang/ilang_backend.h)) | $(eval $(call add_include_file,backends/ilang/ilang_backend.h)) | ||||||
|  | @ -545,6 +546,8 @@ OBJS += libs/sha1/sha1.o | ||||||
| 
 | 
 | ||||||
| ifneq ($(SMALL),1) | ifneq ($(SMALL),1) | ||||||
| 
 | 
 | ||||||
|  | OBJS += libs/json11/json11.o | ||||||
|  | 
 | ||||||
| OBJS += libs/subcircuit/subcircuit.o | OBJS += libs/subcircuit/subcircuit.o | ||||||
| 
 | 
 | ||||||
| OBJS += libs/ezsat/ezsat.o | OBJS += libs/ezsat/ezsat.o | ||||||
|  | @ -705,11 +708,17 @@ test: $(TARGETS) $(EXTRA_TARGETS) | ||||||
| 	+cd tests/various && bash run-test.sh | 	+cd tests/various && bash run-test.sh | ||||||
| 	+cd tests/sat && bash run-test.sh | 	+cd tests/sat && bash run-test.sh | ||||||
| 	+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) | 	+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/svtypes && bash run-test.sh $(SEEDOPT) | ||||||
| 	+cd tests/proc && bash run-test.sh | 	+cd tests/proc && bash run-test.sh | ||||||
| 	+cd tests/opt && bash run-test.sh | 	+cd tests/opt && bash run-test.sh | ||||||
| 	+cd tests/aiger && bash run-test.sh $(ABCOPT) | 	+cd tests/aiger && bash run-test.sh $(ABCOPT) | ||||||
| 	+cd tests/arch && bash run-test.sh | 	+cd tests/arch && bash run-test.sh | ||||||
| 	+cd tests/ice40 && bash run-test.sh $(SEEDOPT) | 	+cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/arch/efinix && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) | ||||||
|  | 	+cd tests/rpc && bash run-test.sh | ||||||
| 	@echo "" | 	@echo "" | ||||||
| 	@echo "  Passed \"make test\"." | 	@echo "  Passed \"make test\"." | ||||||
| 	@echo "" | 	@echo "" | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
										
									
									
									
								
							|  | @ -332,6 +332,10 @@ Verilog Attributes and non-standard features | ||||||
|   that represent module parameters or localparams (when the HDL front-end |   that represent module parameters or localparams (when the HDL front-end | ||||||
|   is run in ``-pwires`` mode). |   is run in ``-pwires`` mode). | ||||||
| 
 | 
 | ||||||
|  | - Wires marked with the ``hierconn`` attribute are connected to wires with the | ||||||
|  |   same name (format ``cell_name.identifier``) when they are imported from | ||||||
|  |   sub-modules by ``flatten``. | ||||||
|  | 
 | ||||||
| - The ``clkbuf_driver`` attribute can be set on an output port of a blackbox | - The ``clkbuf_driver`` attribute can be set on an output port of a blackbox | ||||||
|   module to mark it as a clock buffer output, and thus prevent ``clkbufmap`` |   module to mark it as a clock buffer output, and thus prevent ``clkbufmap`` | ||||||
|   from inserting another clock buffer on a net driven by such output. |   from inserting another clock buffer on a net driven by such output. | ||||||
|  | @ -343,6 +347,12 @@ Verilog Attributes and non-standard features | ||||||
|   automatic clock buffer insertion by ``clkbufmap``. This behaviour can be |   automatic clock buffer insertion by ``clkbufmap``. This behaviour can be | ||||||
|   overridden by providing a custom selection to ``clkbufmap``. |   overridden by providing a custom selection to ``clkbufmap``. | ||||||
| 
 | 
 | ||||||
|  | - The ``invertible_pin`` attribute can be set on a port to mark it as | ||||||
|  |   invertible via a cell parameter.  The name of the inversion parameter | ||||||
|  |   is specified as the value of this attribute.  The value of the inversion | ||||||
|  |   parameter must be of the same width as the port, with 1 indicating | ||||||
|  |   an inverted bit and 0 indicating a non-inverted bit. | ||||||
|  | 
 | ||||||
| - The ``iopad_external_pin`` attribute on a blackbox module's port marks | - The ``iopad_external_pin`` attribute on a blackbox module's port marks | ||||||
|   it as the external-facing pin of an I/O pad, and prevents ``iopadmap`` |   it as the external-facing pin of an I/O pad, and prevents ``iopadmap`` | ||||||
|   from inserting another pad cell on it. |   from inserting another pad cell on it. | ||||||
|  | @ -351,19 +361,16 @@ Verilog Attributes and non-standard features | ||||||
|   blackbox or whitebox definition to a corresponding entry in a `abc9` |   blackbox or whitebox definition to a corresponding entry in a `abc9` | ||||||
|   box-file. |   box-file. | ||||||
| 
 | 
 | ||||||
| - The port attribute ``abc_scc_break`` indicates a module input port that will |  | ||||||
|   be treated as a primary output during `abc9` techmapping. Doing so eliminates |  | ||||||
|   the possibility of a strongly-connected component (i.e. a combinatorial loop) |  | ||||||
|   existing. Typically, this is specified for sequential inputs on otherwise |  | ||||||
|   combinatorial boxes -- for example, applying ``abc_scc_break`` onto the `D` |  | ||||||
|   port of a LUTRAM cell prevents `abc9` from interpreting any `Q` -> `D` paths |  | ||||||
|   as a combinatorial loop. |  | ||||||
| 
 |  | ||||||
| - The port attribute ``abc_carry`` marks the carry-in (if an input port) and | - The port attribute ``abc_carry`` marks the carry-in (if an input port) and | ||||||
|   carry-out (if output port) ports of a box. This information is necessary for |   carry-out (if output port) ports of a box. This information is necessary for | ||||||
|   `abc9` to preserve the integrity of carry-chains. Specifying this attribute |   `abc9` to preserve the integrity of carry-chains. Specifying this attribute | ||||||
|   onto a bus port will affect only its most significant bit. |   onto a bus port will affect only its most significant bit. | ||||||
| 
 | 
 | ||||||
|  | - The port attribute ``abc_arrival`` specifies an integer (for output ports | ||||||
|  |   only) to be used as the arrival time of this sequential port. It can be used, | ||||||
|  |   for example, to specify the clk-to-Q delay of a flip-flop for consideration | ||||||
|  |   during techmapping. | ||||||
|  | 
 | ||||||
| - In addition to the ``(* ... *)`` attribute syntax, Yosys supports | - In addition to the ``(* ... *)`` attribute syntax, Yosys supports | ||||||
|   the non-standard ``{* ... *}`` attribute syntax to set default attributes |   the non-standard ``{* ... *}`` attribute syntax to set default attributes | ||||||
|   for everything that comes after the ``{* ... *}`` statement. (Reset |   for everything that comes after the ``{* ... *}`` statement. (Reset | ||||||
|  | @ -503,6 +510,8 @@ from SystemVerilog: | ||||||
|   into a design with ``read_verilog``, all its packages are available to |   into a design with ``read_verilog``, all its packages are available to | ||||||
|   SystemVerilog files being read into the same design afterwards. |   SystemVerilog files being read into the same design afterwards. | ||||||
| 
 | 
 | ||||||
|  | - typedefs are supported (including inside packages) | ||||||
|  | 
 | ||||||
| - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether | - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether | ||||||
|   ports are inputs or outputs are supported. |   ports are inputs or outputs are supported. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -101,7 +101,7 @@ struct AigerWriter | ||||||
| 		return a; | 		return a; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module) | 	AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module) | ||||||
| 	{ | 	{ | ||||||
| 		pool<SigBit> undriven_bits; | 		pool<SigBit> undriven_bits; | ||||||
| 		pool<SigBit> unused_bits; | 		pool<SigBit> unused_bits; | ||||||
|  | @ -367,6 +367,12 @@ struct AigerWriter | ||||||
| 				aig_latchin.push_back(a); | 				aig_latchin.push_back(a); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (lmode && aig_l == 0) { | ||||||
|  | 			aig_m++, aig_l++; | ||||||
|  | 			aig_latchinit.push_back(0); | ||||||
|  | 			aig_latchin.push_back(0); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (!initstate_bits.empty() || !init_inputs.empty()) | 		if (!initstate_bits.empty() || !init_inputs.empty()) | ||||||
| 			aig_latchin.push_back(1); | 			aig_latchin.push_back(1); | ||||||
| 
 | 
 | ||||||
|  | @ -704,9 +710,9 @@ struct AigerBackend : public Backend { | ||||||
| 		log("    -vmap <filename>\n"); | 		log("    -vmap <filename>\n"); | ||||||
| 		log("        like -map, but more verbose\n"); | 		log("        like -map, but more verbose\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -I, -O, -B\n"); | 		log("    -I, -O, -B, -L\n"); | ||||||
| 		log("        If the design contains no input/output/assert then create one\n"); | 		log("        If the design contains no input/output/assert/flip-flop then create one\n"); | ||||||
| 		log("        dummy input/output/bad_state pin to make the tools reading the\n"); | 		log("        dummy input/output/bad_state-pin or latch to make the tools reading the\n"); | ||||||
| 		log("        AIGER file happy.\n"); | 		log("        AIGER file happy.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
|  | @ -720,6 +726,7 @@ struct AigerBackend : public Backend { | ||||||
| 		bool imode = false; | 		bool imode = false; | ||||||
| 		bool omode = false; | 		bool omode = false; | ||||||
| 		bool bmode = false; | 		bool bmode = false; | ||||||
|  | 		bool lmode = false; | ||||||
| 		std::string map_filename; | 		std::string map_filename; | ||||||
| 
 | 
 | ||||||
| 		log_header(design, "Executing AIGER backend.\n"); | 		log_header(design, "Executing AIGER backend.\n"); | ||||||
|  | @ -764,16 +771,20 @@ struct AigerBackend : public Backend { | ||||||
| 				bmode = true; | 				bmode = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-L") { | ||||||
|  | 				lmode = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(f, filename, args, argidx); | 		extra_args(f, filename, args, argidx, !ascii_mode); | ||||||
| 
 | 
 | ||||||
| 		Module *top_module = design->top_module(); | 		Module *top_module = design->top_module(); | ||||||
| 
 | 
 | ||||||
| 		if (top_module == nullptr) | 		if (top_module == nullptr) | ||||||
| 			log_error("Can't find top module in current design!\n"); | 			log_error("Can't find top module in current design!\n"); | ||||||
| 
 | 
 | ||||||
| 		AigerWriter writer(top_module, zinit_mode, imode, omode, bmode); | 		AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode); | ||||||
| 		writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); | 		writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); | ||||||
| 
 | 
 | ||||||
| 		if (!map_filename.empty()) { | 		if (!map_filename.empty()) { | ||||||
|  |  | ||||||
|  | @ -83,6 +83,7 @@ struct XAigerWriter | ||||||
| 	dict<SigBit, pair<SigBit, SigBit>> and_map; | 	dict<SigBit, pair<SigBit, SigBit>> and_map; | ||||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; | 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; | ||||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; | 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; | ||||||
|  | 	dict<SigBit, float> arrival_times; | ||||||
| 
 | 
 | ||||||
| 	vector<pair<int, int>> aig_gates; | 	vector<pair<int, int>> aig_gates; | ||||||
| 	vector<int> aig_outputs; | 	vector<int> aig_outputs; | ||||||
|  | @ -202,7 +203,7 @@ struct XAigerWriter | ||||||
| 		//       box ordering, but not individual AIG cells
 | 		//       box ordering, but not individual AIG cells
 | ||||||
| 		dict<SigBit, pool<IdString>> bit_drivers, bit_users; | 		dict<SigBit, pool<IdString>> bit_drivers, bit_users; | ||||||
| 		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; | 		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; | ||||||
| 		bool abc_box_seen = false; | 		bool abc9_box_seen = false; | ||||||
| 
 | 
 | ||||||
| 		for (auto cell : module->selected_cells()) { | 		for (auto cell : module->selected_cells()) { | ||||||
| 			if (cell->type == "$_NOT_") | 			if (cell->type == "$_NOT_") | ||||||
|  | @ -241,20 +242,21 @@ struct XAigerWriter | ||||||
| 			log_assert(!holes_mode); | 			log_assert(!holes_mode); | ||||||
| 
 | 
 | ||||||
| 			RTLIL::Module* inst_module = module->design->module(cell->type); | 			RTLIL::Module* inst_module = module->design->module(cell->type); | ||||||
| 			if (inst_module && inst_module->attributes.count("\\abc_box_id")) { | 			if (inst_module && inst_module->attributes.count("\\abc9_box_id")) { | ||||||
| 				abc_box_seen = true; | 				abc9_box_seen = true; | ||||||
| 
 | 
 | ||||||
| 				if (!holes_mode) { | 				if (!holes_mode) { | ||||||
| 					toposort.node(cell->name); | 					toposort.node(cell->name); | ||||||
| 					for (const auto &conn : cell->connections()) { | 					for (const auto &conn : cell->connections()) { | ||||||
| 						if (cell->input(conn.first)) { | 						auto port_wire = inst_module->wire(conn.first); | ||||||
|  | 						if (port_wire->port_input) { | ||||||
| 							// Ignore inout for the sake of topographical ordering
 | 							// Ignore inout for the sake of topographical ordering
 | ||||||
| 							if (cell->output(conn.first)) continue; | 							if (port_wire->port_output) continue; | ||||||
| 							for (auto bit : sigmap(conn.second)) | 							for (auto bit : sigmap(conn.second)) | ||||||
| 								bit_users[bit].insert(cell->name); | 								bit_users[bit].insert(cell->name); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						if (cell->output(conn.first)) | 						if (port_wire->port_output) | ||||||
| 							for (auto bit : sigmap(conn.second)) | 							for (auto bit : sigmap(conn.second)) | ||||||
| 								bit_drivers[bit].insert(cell->name); | 								bit_drivers[bit].insert(cell->name); | ||||||
| 					} | 					} | ||||||
|  | @ -271,7 +273,7 @@ struct XAigerWriter | ||||||
| 						log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); | 						log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); | ||||||
| 
 | 
 | ||||||
| 					if (is_input) { | 					if (is_input) { | ||||||
| 						for (auto b : c.second.bits()) { | 						for (auto b : c.second) { | ||||||
| 							Wire *w = b.wire; | 							Wire *w = b.wire; | ||||||
| 							if (!w) continue; | 							if (!w) continue; | ||||||
| 							if (!w->port_output || !cell_known) { | 							if (!w->port_output || !cell_known) { | ||||||
|  | @ -287,7 +289,17 @@ struct XAigerWriter | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 					if (is_output) { | 					if (is_output) { | ||||||
| 						for (auto b : c.second.bits()) { | 						int arrival = 0; | ||||||
|  | 						if (port_wire) { | ||||||
|  | 							auto it = port_wire->attributes.find("\\abc9_arrival"); | ||||||
|  | 							if (it != port_wire->attributes.end()) { | ||||||
|  | 								if (it->second.flags != 0) | ||||||
|  | 									log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); | ||||||
|  | 								arrival = it->second.as_int(); | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						for (auto b : c.second) { | ||||||
| 							Wire *w = b.wire; | 							Wire *w = b.wire; | ||||||
| 							if (!w) continue; | 							if (!w) continue; | ||||||
| 							input_bits.insert(b); | 							input_bits.insert(b); | ||||||
|  | @ -295,6 +307,9 @@ struct XAigerWriter | ||||||
| 							if (O != b) | 							if (O != b) | ||||||
| 								alias_map[O] = b; | 								alias_map[O] = b; | ||||||
| 							undriven_bits.erase(O); | 							undriven_bits.erase(O); | ||||||
|  | 
 | ||||||
|  | 							if (arrival) | ||||||
|  | 								arrival_times[b] = arrival; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -303,7 +318,7 @@ struct XAigerWriter | ||||||
| 			//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
 | 			//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (abc_box_seen) { | 		if (abc9_box_seen) { | ||||||
| 			for (auto &it : bit_users) | 			for (auto &it : bit_users) | ||||||
| 				if (bit_drivers.count(it.first)) | 				if (bit_drivers.count(it.first)) | ||||||
| 					for (auto driver_cell : bit_drivers.at(it.first)) | 					for (auto driver_cell : bit_drivers.at(it.first)) | ||||||
|  | @ -332,9 +347,11 @@ struct XAigerWriter | ||||||
| 				log_assert(cell); | 				log_assert(cell); | ||||||
| 
 | 
 | ||||||
| 				RTLIL::Module* box_module = module->design->module(cell->type); | 				RTLIL::Module* box_module = module->design->module(cell->type); | ||||||
| 				if (!box_module || !box_module->attributes.count("\\abc_box_id")) | 				if (!box_module || !box_module->attributes.count("\\abc9_box_id")) | ||||||
| 					continue; | 					continue; | ||||||
| 
 | 
 | ||||||
|  | 				bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); | ||||||
|  | 
 | ||||||
| 				// Fully pad all unused input connections of this box cell with S0
 | 				// Fully pad all unused input connections of this box cell with S0
 | ||||||
| 				// Fully pad all undriven output connections of this box cell with anonymous wires
 | 				// Fully pad all undriven output connections of this box cell with anonymous wires
 | ||||||
| 				// NB: Assume box_module->ports are sorted alphabetically
 | 				// NB: Assume box_module->ports are sorted alphabetically
 | ||||||
|  | @ -379,7 +396,10 @@ struct XAigerWriter | ||||||
| 							rhs = it->second; | 							rhs = it->second; | ||||||
| 						} | 						} | ||||||
| 						else { | 						else { | ||||||
| 							rhs = module->addWire(NEW_ID, GetSize(w)); | 							Wire *wire = module->addWire(NEW_ID, GetSize(w)); | ||||||
|  | 							if (blackbox) | ||||||
|  | 								wire->set_bool_attribute(ID(abc9_padding)); | ||||||
|  | 							rhs = wire; | ||||||
| 							cell->setPort(port_name, rhs); | 							cell->setPort(port_name, rhs); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
|  | @ -390,12 +410,7 @@ struct XAigerWriter | ||||||
| 							if (O != b) | 							if (O != b) | ||||||
| 								alias_map[O] = b; | 								alias_map[O] = b; | ||||||
| 							undriven_bits.erase(O); | 							undriven_bits.erase(O); | ||||||
| 
 | 							input_bits.erase(b); | ||||||
| 							auto jt = input_bits.find(b); |  | ||||||
| 							if (jt != input_bits.end()) { |  | ||||||
| 								log_assert(keep_bits.count(O)); |  | ||||||
| 								input_bits.erase(b); |  | ||||||
| 							} |  | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -414,7 +429,7 @@ struct XAigerWriter | ||||||
| 			// inherit existing inout's drivers
 | 			// inherit existing inout's drivers
 | ||||||
| 			if ((wire->port_input && wire->port_output && !undriven_bits.count(bit)) | 			if ((wire->port_input && wire->port_output && !undriven_bits.count(bit)) | ||||||
| 					|| keep_bits.count(bit)) { | 					|| keep_bits.count(bit)) { | ||||||
| 				RTLIL::IdString wire_name = wire->name.str() + "$inout.out"; | 				RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str()); | ||||||
| 				RTLIL::Wire *new_wire = module->wire(wire_name); | 				RTLIL::Wire *new_wire = module->wire(wire_name); | ||||||
| 				if (!new_wire) | 				if (!new_wire) | ||||||
| 					new_wire = module->addWire(wire_name, GetSize(wire)); | 					new_wire = module->addWire(wire_name, GetSize(wire)); | ||||||
|  | @ -489,16 +504,16 @@ struct XAigerWriter | ||||||
| 			aig_outputs.push_back(bit2aig(bit)); | 			aig_outputs.push_back(bit2aig(bit)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (output_bits.empty()) { | ||||||
|  | 			output_bits.insert(State::S0); | ||||||
|  | 			omode = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		for (auto bit : output_bits) { | 		for (auto bit : output_bits) { | ||||||
| 			ordered_outputs[bit] = aig_o++; | 			ordered_outputs[bit] = aig_o++; | ||||||
| 			aig_outputs.push_back(bit2aig(bit)); | 			aig_outputs.push_back(bit2aig(bit)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (output_bits.empty()) { |  | ||||||
| 			aig_o++; |  | ||||||
| 			aig_outputs.push_back(0); |  | ||||||
| 			omode = true; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void write_aiger(std::ostream &f, bool ascii_mode) | 	void write_aiger(std::ostream &f, bool ascii_mode) | ||||||
|  | @ -560,26 +575,38 @@ struct XAigerWriter | ||||||
| 
 | 
 | ||||||
| 		f << "c"; | 		f << "c"; | ||||||
| 
 | 
 | ||||||
|  | 		log_assert(!output_bits.empty()); | ||||||
|  | 		auto write_buffer = [](std::stringstream &buffer, int i32) { | ||||||
|  | 			int32_t i32_be = to_big_endian(i32); | ||||||
|  | 			buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); | ||||||
|  | 		}; | ||||||
|  | 		std::stringstream h_buffer; | ||||||
|  | 		auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); | ||||||
|  | 		write_h_buffer(1); | ||||||
|  | 		log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); | ||||||
|  | 		write_h_buffer(input_bits.size() + ci_bits.size()); | ||||||
|  | 		log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); | ||||||
|  | 		write_h_buffer(output_bits.size() + GetSize(co_bits)); | ||||||
|  | 		log_debug("piNum = %d\n", GetSize(input_bits)); | ||||||
|  | 		write_h_buffer(input_bits.size()); | ||||||
|  | 		log_debug("poNum = %d\n", GetSize(output_bits)); | ||||||
|  | 		write_h_buffer(output_bits.size()); | ||||||
|  | 		log_debug("boxNum = %d\n", GetSize(box_list)); | ||||||
|  | 		write_h_buffer(box_list.size()); | ||||||
|  | 
 | ||||||
|  | 		auto write_buffer_float = [](std::stringstream &buffer, float f32) { | ||||||
|  | 			buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32)); | ||||||
|  | 		}; | ||||||
|  | 		std::stringstream i_buffer; | ||||||
|  | 		auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); | ||||||
|  | 		for (auto bit : input_bits) | ||||||
|  | 			write_i_buffer(arrival_times.at(bit, 0)); | ||||||
|  | 		//std::stringstream o_buffer;
 | ||||||
|  | 		//auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1);
 | ||||||
|  | 		//for (auto bit : output_bits)
 | ||||||
|  | 		//	write_o_buffer(0);
 | ||||||
|  | 
 | ||||||
| 		if (!box_list.empty()) { | 		if (!box_list.empty()) { | ||||||
| 			auto write_buffer = [](std::stringstream &buffer, int i32) { |  | ||||||
| 				int32_t i32_be = to_big_endian(i32); |  | ||||||
| 				buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); |  | ||||||
| 			}; |  | ||||||
| 
 |  | ||||||
| 			std::stringstream h_buffer; |  | ||||||
| 			auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); |  | ||||||
| 			write_h_buffer(1); |  | ||||||
| 			log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); |  | ||||||
| 			write_h_buffer(input_bits.size() + ci_bits.size()); |  | ||||||
| 			log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); |  | ||||||
| 			write_h_buffer(output_bits.size() + co_bits.size()); |  | ||||||
| 			log_debug("piNum = %d\n", GetSize(input_bits)); |  | ||||||
| 			write_h_buffer(input_bits.size()); |  | ||||||
| 			log_debug("poNum = %d\n", GetSize(output_bits)); |  | ||||||
| 			write_h_buffer(output_bits.size()); |  | ||||||
| 			log_debug("boxNum = %d\n", GetSize(box_list)); |  | ||||||
| 			write_h_buffer(box_list.size()); |  | ||||||
| 
 |  | ||||||
| 			RTLIL::Module *holes_module = module->design->addModule("$__holes__"); | 			RTLIL::Module *holes_module = module->design->addModule("$__holes__"); | ||||||
| 			log_assert(holes_module); | 			log_assert(holes_module); | ||||||
| 
 | 
 | ||||||
|  | @ -639,23 +666,16 @@ struct XAigerWriter | ||||||
| 
 | 
 | ||||||
| 				write_h_buffer(box_inputs); | 				write_h_buffer(box_inputs); | ||||||
| 				write_h_buffer(box_outputs); | 				write_h_buffer(box_outputs); | ||||||
| 				write_h_buffer(box_module->attributes.at("\\abc_box_id").as_int()); | 				write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int()); | ||||||
| 				write_h_buffer(box_count++); | 				write_h_buffer(box_count++); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			f << "h"; |  | ||||||
| 			std::string buffer_str = h_buffer.str(); |  | ||||||
| 			int32_t buffer_size_be = to_big_endian(buffer_str.size()); |  | ||||||
| 			f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); |  | ||||||
| 			f.write(buffer_str.data(), buffer_str.size()); |  | ||||||
| 
 |  | ||||||
| 			std::stringstream r_buffer; | 			std::stringstream r_buffer; | ||||||
| 			auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); | 			auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); | ||||||
| 			write_r_buffer(0); | 			write_r_buffer(0); | ||||||
| 
 |  | ||||||
| 			f << "r"; | 			f << "r"; | ||||||
| 			buffer_str = r_buffer.str(); | 			std::string buffer_str = r_buffer.str(); | ||||||
| 			buffer_size_be = to_big_endian(buffer_str.size()); | 			int32_t buffer_size_be = to_big_endian(buffer_str.size()); | ||||||
| 			f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | 			f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | ||||||
| 			f.write(buffer_str.data(), buffer_str.size()); | 			f.write(buffer_str.data(), buffer_str.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -709,6 +729,23 @@ struct XAigerWriter | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		f << "h"; | ||||||
|  | 		std::string buffer_str = h_buffer.str(); | ||||||
|  | 		int32_t buffer_size_be = to_big_endian(buffer_str.size()); | ||||||
|  | 		f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | ||||||
|  | 		f.write(buffer_str.data(), buffer_str.size()); | ||||||
|  | 
 | ||||||
|  | 		f << "i"; | ||||||
|  | 		buffer_str = i_buffer.str(); | ||||||
|  | 		buffer_size_be = to_big_endian(buffer_str.size()); | ||||||
|  | 		f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | ||||||
|  | 		f.write(buffer_str.data(), buffer_str.size()); | ||||||
|  | 		//f << "o";
 | ||||||
|  | 		//buffer_str = o_buffer.str();
 | ||||||
|  | 		//buffer_size_be = to_big_endian(buffer_str.size());
 | ||||||
|  | 		//f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
 | ||||||
|  | 		//f.write(buffer_str.data(), buffer_str.size());
 | ||||||
|  | 
 | ||||||
| 		f << stringf("Generated by %s\n", yosys_version_str); | 		f << stringf("Generated by %s\n", yosys_version_str); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -760,11 +797,11 @@ struct XAigerWriter | ||||||
| 			f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); | 			f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); | ||||||
| 
 | 
 | ||||||
| 		output_lines.sort(); | 		output_lines.sort(); | ||||||
|  | 		if (omode) | ||||||
|  | 			output_lines[State::S0] = "output 0 0 $__dummy__\n"; | ||||||
| 		for (auto &it : output_lines) | 		for (auto &it : output_lines) | ||||||
| 			f << it.second; | 			f << it.second; | ||||||
| 		log_assert(output_lines.size() == output_bits.size()); | 		log_assert(output_lines.size() == output_bits.size()); | ||||||
| 		if (omode && output_bits.empty()) |  | ||||||
| 			f << "output " << output_lines.size() << " 0 $__dummy__\n"; |  | ||||||
| 
 | 
 | ||||||
| 		wire_lines.sort(); | 		wire_lines.sort(); | ||||||
| 		for (auto &it : wire_lines) | 		for (auto &it : wire_lines) | ||||||
|  | @ -819,7 +856,7 @@ struct XAigerBackend : public Backend { | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(f, filename, args, argidx); | 		extra_args(f, filename, args, argidx, !ascii_mode); | ||||||
| 
 | 
 | ||||||
| 		Module *top_module = design->top_module(); | 		Module *top_module = design->top_module(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -569,7 +569,7 @@ struct BtorWorker | ||||||
| 			int nid_init_val = -1; | 			int nid_init_val = -1; | ||||||
| 
 | 
 | ||||||
| 			if (!initval.is_fully_undef()) | 			if (!initval.is_fully_undef()) | ||||||
| 				nid_init_val = get_sig_nid(initval); | 				nid_init_val = get_sig_nid(initval, -1, false, true); | ||||||
| 
 | 
 | ||||||
| 			int sid = get_bv_sid(GetSize(sig_q)); | 			int sid = get_bv_sid(GetSize(sig_q)); | ||||||
| 			int nid = next_nid++; | 			int nid = next_nid++; | ||||||
|  | @ -681,11 +681,11 @@ struct BtorWorker | ||||||
| 				{ | 				{ | ||||||
| 					if (verbose) | 					if (verbose) | ||||||
| 						btorf("; initval = %s\n", log_signal(firstword)); | 						btorf("; initval = %s\n", log_signal(firstword)); | ||||||
| 					nid_init_val = get_sig_nid(firstword); | 					nid_init_val = get_sig_nid(firstword, -1, false, true); | ||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
| 				{ | 				{ | ||||||
| 					int nid_init_val = next_nid++; | 					nid_init_val = next_nid++; | ||||||
| 					btorf("%d state %d\n", nid_init_val, sid); | 					btorf("%d state %d\n", nid_init_val, sid); | ||||||
| 
 | 
 | ||||||
| 					for (int i = 0; i < nwords; i++) { | 					for (int i = 0; i < nwords; i++) { | ||||||
|  | @ -693,8 +693,8 @@ struct BtorWorker | ||||||
| 						if (thisword.is_fully_undef()) | 						if (thisword.is_fully_undef()) | ||||||
| 							continue; | 							continue; | ||||||
| 						Const thisaddr(i, abits); | 						Const thisaddr(i, abits); | ||||||
| 						int nid_thisword = get_sig_nid(thisword); | 						int nid_thisword = get_sig_nid(thisword, -1, false, true); | ||||||
| 						int nid_thisaddr = get_sig_nid(thisaddr); | 						int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true); | ||||||
| 						int last_nid_init_val = nid_init_val; | 						int last_nid_init_val = nid_init_val; | ||||||
| 						nid_init_val = next_nid++; | 						nid_init_val = next_nid++; | ||||||
| 						if (verbose) | 						if (verbose) | ||||||
|  | @ -792,7 +792,7 @@ struct BtorWorker | ||||||
| 		cell_recursion_guard.erase(cell); | 		cell_recursion_guard.erase(cell); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false) | 	int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false) | ||||||
| 	{ | 	{ | ||||||
| 		int nid = -1; | 		int nid = -1; | ||||||
| 		sigmap.apply(sig); | 		sigmap.apply(sig); | ||||||
|  | @ -823,7 +823,10 @@ struct BtorWorker | ||||||
| 				int sid = get_bv_sid(GetSize(sig)); | 				int sid = get_bv_sid(GetSize(sig)); | ||||||
| 
 | 
 | ||||||
| 				int nid_input = next_nid++; | 				int nid_input = next_nid++; | ||||||
| 				btorf("%d input %d\n", nid_input, sid); | 				if (is_init) | ||||||
|  | 					btorf("%d state %d\n", nid_input, sid); | ||||||
|  | 				else | ||||||
|  | 					btorf("%d input %d\n", nid_input, sid); | ||||||
| 
 | 
 | ||||||
| 				int nid_masked_input; | 				int nid_masked_input; | ||||||
| 				if (sig_mask_undef.is_fully_ones()) { | 				if (sig_mask_undef.is_fully_ones()) { | ||||||
|  | @ -897,9 +900,12 @@ struct BtorWorker | ||||||
| 
 | 
 | ||||||
| 							int sid = get_bv_sid(GetSize(s)); | 							int sid = get_bv_sid(GetSize(s)); | ||||||
| 							int nid = next_nid++; | 							int nid = next_nid++; | ||||||
| 							btorf("%d input %d %s\n", nid, sid); | 							btorf("%d input %d\n", nid, sid); | ||||||
| 							nid_width[nid] = GetSize(s); | 							nid_width[nid] = GetSize(s); | ||||||
| 
 | 
 | ||||||
|  | 							for (int j = 0; j < GetSize(s); j++) | ||||||
|  | 								nidbits.push_back(make_pair(nid, j)); | ||||||
|  | 
 | ||||||
| 							i += GetSize(s)-1; | 							i += GetSize(s)-1; | ||||||
| 							continue; | 							continue; | ||||||
| 						} | 						} | ||||||
|  |  | ||||||
|  | @ -266,7 +266,7 @@ struct ProtobufBackend : public Backend { | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(f, filename, args, argidx); | 		extra_args(f, filename, args, argidx, !text_mode); | ||||||
| 
 | 
 | ||||||
| 		log_header(design, "Executing Protobuf backend.\n"); | 		log_header(design, "Executing Protobuf backend.\n"); | ||||||
| 
 | 
 | ||||||
|  | @ -338,7 +338,7 @@ struct ProtobufPass : public Pass { | ||||||
| 		if (!filename.empty()) { | 		if (!filename.empty()) { | ||||||
| 			rewrite_filename(filename); | 			rewrite_filename(filename); | ||||||
| 			std::ofstream *ff = new std::ofstream; | 			std::ofstream *ff = new std::ofstream; | ||||||
| 			ff->open(filename.c_str(), std::ofstream::trunc); | 			ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary)); | ||||||
| 			if (ff->fail()) { | 			if (ff->fail()) { | ||||||
| 				delete ff; | 				delete ff; | ||||||
| 				log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); | 				log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ yosys-smtbmc-script.py: backends/smt2/smtbmc.py | ||||||
| 		-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ | 		-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ | ||||||
| 
 | 
 | ||||||
| yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py | yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py | ||||||
| 	$(P) gcc -DGUI=0 -O -s -o $@ $< | 	$(P) $(CXX) -DGUI=0 -O -s -o $@ $< | ||||||
| # Other targets
 | # Other targets
 | ||||||
| else | else | ||||||
| TARGETS += yosys-smtbmc | TARGETS += yosys-smtbmc | ||||||
|  |  | ||||||
|  | @ -1256,7 +1256,7 @@ def smt_check_sat(): | ||||||
|     return smt.check_sat() |     return smt.check_sat() | ||||||
| 
 | 
 | ||||||
| if tempind: | if tempind: | ||||||
|     retstatus = False |     retstatus = "FAILED" | ||||||
|     skip_counter = step_size |     skip_counter = step_size | ||||||
|     for step in range(num_steps, -1, -1): |     for step in range(num_steps, -1, -1): | ||||||
|         if smt.forall: |         if smt.forall: | ||||||
|  | @ -1303,7 +1303,7 @@ if tempind: | ||||||
| 
 | 
 | ||||||
|         else: |         else: | ||||||
|             print_msg("Temporal induction successful.") |             print_msg("Temporal induction successful.") | ||||||
|             retstatus = True |             retstatus = "PASSED" | ||||||
|             break |             break | ||||||
| 
 | 
 | ||||||
| elif covermode: | elif covermode: | ||||||
|  | @ -1321,7 +1321,7 @@ elif covermode: | ||||||
|     smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr)) |     smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr)) | ||||||
| 
 | 
 | ||||||
|     step = 0 |     step = 0 | ||||||
|     retstatus = False |     retstatus = "FAILED" | ||||||
|     found_failed_assert = False |     found_failed_assert = False | ||||||
| 
 | 
 | ||||||
|     assert step_size == 1 |     assert step_size == 1 | ||||||
|  | @ -1365,7 +1365,7 @@ elif covermode: | ||||||
|                 if smt_check_sat() == "unsat": |                 if smt_check_sat() == "unsat": | ||||||
|                     print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) |                     print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) | ||||||
|                     found_failed_assert = True |                     found_failed_assert = True | ||||||
|                     retstatus = False |                     retstatus = "FAILED" | ||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|             reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step))) |             reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step))) | ||||||
|  | @ -1400,7 +1400,7 @@ elif covermode: | ||||||
|             break |             break | ||||||
| 
 | 
 | ||||||
|         if "1" not in cover_mask: |         if "1" not in cover_mask: | ||||||
|             retstatus = True |             retstatus = "PASSED" | ||||||
|             break |             break | ||||||
| 
 | 
 | ||||||
|         step += 1 |         step += 1 | ||||||
|  | @ -1412,7 +1412,7 @@ elif covermode: | ||||||
| 
 | 
 | ||||||
| else:  # not tempind, covermode | else:  # not tempind, covermode | ||||||
|     step = 0 |     step = 0 | ||||||
|     retstatus = True |     retstatus = "PASSED" | ||||||
|     while step < num_steps: |     while step < num_steps: | ||||||
|         smt_state(step) |         smt_state(step) | ||||||
|         smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) |         smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) | ||||||
|  | @ -1459,8 +1459,8 @@ else:  # not tempind, covermode | ||||||
|                     print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step)) |                     print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step)) | ||||||
| 
 | 
 | ||||||
|                 if smt_check_sat() == "unsat": |                 if smt_check_sat() == "unsat": | ||||||
|                     print("%s Warmup failed!" % smt.timestamp()) |                     print("%s Assumptions are unsatisfiable!" % smt.timestamp()) | ||||||
|                     retstatus = False |                     retstatus = "PREUNSAT" | ||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|             if not final_only: |             if not final_only: | ||||||
|  | @ -1487,13 +1487,13 @@ else:  # not tempind, covermode | ||||||
|                         print_msg("Re-solving with appended steps..") |                         print_msg("Re-solving with appended steps..") | ||||||
|                         if smt_check_sat() == "unsat": |                         if smt_check_sat() == "unsat": | ||||||
|                             print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) |                             print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) | ||||||
|                             retstatus = False |                             retstatus = "FAILED" | ||||||
|                             break |                             break | ||||||
|                     print_anyconsts(step) |                     print_anyconsts(step) | ||||||
|                     for i in range(step, last_check_step+1): |                     for i in range(step, last_check_step+1): | ||||||
|                         print_failed_asserts(i) |                         print_failed_asserts(i) | ||||||
|                     write_trace(0, last_check_step+1+append_steps, '%') |                     write_trace(0, last_check_step+1+append_steps, '%') | ||||||
|                     retstatus = False |                     retstatus = "FAILED" | ||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|                 smt_pop() |                 smt_pop() | ||||||
|  | @ -1519,7 +1519,7 @@ else:  # not tempind, covermode | ||||||
|                         print_anyconsts(i) |                         print_anyconsts(i) | ||||||
|                         print_failed_asserts(i, final=True) |                         print_failed_asserts(i, final=True) | ||||||
|                         write_trace(0, i+1, '%') |                         write_trace(0, i+1, '%') | ||||||
|                         retstatus = False |                         retstatus = "FAILED" | ||||||
|                         break |                         break | ||||||
| 
 | 
 | ||||||
|                     smt_pop() |                     smt_pop() | ||||||
|  | @ -1534,7 +1534,7 @@ else:  # not tempind, covermode | ||||||
|             print_msg("Solving for step %d.." % (last_check_step)) |             print_msg("Solving for step %d.." % (last_check_step)) | ||||||
|             if smt_check_sat() != "sat": |             if smt_check_sat() != "sat": | ||||||
|                 print("%s No solution found!" % smt.timestamp()) |                 print("%s No solution found!" % smt.timestamp()) | ||||||
|                 retstatus = False |                 retstatus = "FAILED" | ||||||
|                 break |                 break | ||||||
| 
 | 
 | ||||||
|             elif dumpall: |             elif dumpall: | ||||||
|  | @ -1551,5 +1551,5 @@ else:  # not tempind, covermode | ||||||
| smt.write("(exit)") | smt.write("(exit)") | ||||||
| smt.wait() | smt.wait() | ||||||
| 
 | 
 | ||||||
| print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)")) | print_msg("Status: %s" % retstatus) | ||||||
| sys.exit(0 if retstatus else 1) | sys.exit(0 if retstatus == "PASSED" else 1) | ||||||
|  |  | ||||||
|  | @ -285,6 +285,8 @@ end_of_header: | ||||||
| 		} | 		} | ||||||
| 		else if (c == 'c') { | 		else if (c == 'c') { | ||||||
| 			f.ignore(1); | 			f.ignore(1); | ||||||
|  | 			if (f.peek() == '\r') | ||||||
|  | 				f.ignore(1); | ||||||
| 			if (f.peek() == '\n') | 			if (f.peek() == '\n') | ||||||
| 				break; | 				break; | ||||||
| 			// Else constraint (TODO)
 | 			// Else constraint (TODO)
 | ||||||
|  | @ -430,6 +432,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup) | ||||||
| 			else if (c == 'r') { | 			else if (c == 'r') { | ||||||
| 				uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); | 				uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); | ||||||
| 				flopNum = parse_xaiger_literal(f); | 				flopNum = parse_xaiger_literal(f); | ||||||
|  | 				log_debug("flopNum: %u\n", flopNum); | ||||||
| 				log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); | 				log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); | ||||||
| 				f.ignore(flopNum * sizeof(uint32_t)); | 				f.ignore(flopNum * sizeof(uint32_t)); | ||||||
| 			} | 			} | ||||||
|  | @ -496,8 +499,7 @@ void AigerReader::parse_aiger_ascii() | ||||||
| 
 | 
 | ||||||
| 	// Parse latches
 | 	// Parse latches
 | ||||||
| 	RTLIL::Wire *clk_wire = nullptr; | 	RTLIL::Wire *clk_wire = nullptr; | ||||||
| 	if (L > 0) { | 	if (L > 0 && !clk_name.empty()) { | ||||||
| 		log_assert(clk_name != ""); |  | ||||||
| 		clk_wire = module->wire(clk_name); | 		clk_wire = module->wire(clk_name); | ||||||
| 		log_assert(!clk_wire); | 		log_assert(!clk_wire); | ||||||
| 		log_debug2("Creating %s\n", clk_name.c_str()); | 		log_debug2("Creating %s\n", clk_name.c_str()); | ||||||
|  | @ -513,7 +515,10 @@ void AigerReader::parse_aiger_ascii() | ||||||
| 		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); | 		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); | ||||||
| 		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); | 		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); | ||||||
| 
 | 
 | ||||||
| 		module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); | 		if (clk_wire) | ||||||
|  | 			module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); | ||||||
|  | 		else | ||||||
|  | 			module->addFfGate(NEW_ID, d_wire, q_wire); | ||||||
| 
 | 
 | ||||||
| 		// Reset logic is optional in AIGER 1.9
 | 		// Reset logic is optional in AIGER 1.9
 | ||||||
| 		if (f.peek() == ' ') { | 		if (f.peek() == ' ') { | ||||||
|  | @ -621,8 +626,7 @@ void AigerReader::parse_aiger_binary() | ||||||
| 
 | 
 | ||||||
| 	// Parse latches
 | 	// Parse latches
 | ||||||
| 	RTLIL::Wire *clk_wire = nullptr; | 	RTLIL::Wire *clk_wire = nullptr; | ||||||
| 	if (L > 0) { | 	if (L > 0 && !clk_name.empty()) { | ||||||
| 		log_assert(clk_name != ""); |  | ||||||
| 		clk_wire = module->wire(clk_name); | 		clk_wire = module->wire(clk_name); | ||||||
| 		log_assert(!clk_wire); | 		log_assert(!clk_wire); | ||||||
| 		log_debug2("Creating %s\n", clk_name.c_str()); | 		log_debug2("Creating %s\n", clk_name.c_str()); | ||||||
|  | @ -638,7 +642,10 @@ void AigerReader::parse_aiger_binary() | ||||||
| 		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); | 		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); | ||||||
| 		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); | 		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); | ||||||
| 
 | 
 | ||||||
| 		module->addDff(NEW_ID, clk_wire, d_wire, q_wire); | 		if (clk_wire) | ||||||
|  | 			module->addDff(NEW_ID, clk_wire, d_wire, q_wire); | ||||||
|  | 		else | ||||||
|  | 			module->addFf(NEW_ID, d_wire, q_wire); | ||||||
| 
 | 
 | ||||||
| 		// Reset logic is optional in AIGER 1.9
 | 		// Reset logic is optional in AIGER 1.9
 | ||||||
| 		if (f.peek() == ' ') { | 		if (f.peek() == ' ') { | ||||||
|  | @ -733,22 +740,22 @@ void AigerReader::post_process() | ||||||
| 		log_assert(box_module); | 		log_assert(box_module); | ||||||
| 
 | 
 | ||||||
| 		if (seen_boxes.insert(cell->type).second) { | 		if (seen_boxes.insert(cell->type).second) { | ||||||
| 			auto it = box_module->attributes.find("\\abc_carry"); | 			auto it = box_module->attributes.find("\\abc9_carry"); | ||||||
| 			if (it != box_module->attributes.end()) { | 			if (it != box_module->attributes.end()) { | ||||||
| 				RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; | 				RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; | ||||||
| 				auto carry_in_out = it->second.decode_string(); | 				auto carry_in_out = it->second.decode_string(); | ||||||
| 				auto pos = carry_in_out.find(','); | 				auto pos = carry_in_out.find(','); | ||||||
| 				if (pos == std::string::npos) | 				if (pos == std::string::npos) | ||||||
| 					log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); | 					log_error("'abc9_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); | ||||||
| 				auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); | 				auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); | ||||||
| 				carry_in = box_module->wire(carry_in_name); | 				carry_in = box_module->wire(carry_in_name); | ||||||
| 				if (!carry_in || !carry_in->port_input) | 				if (!carry_in || !carry_in->port_input) | ||||||
| 					log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); | 					log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); | ||||||
| 
 | 
 | ||||||
| 				auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1)); | 				auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1)); | ||||||
| 				carry_out = box_module->wire(carry_out_name); | 				carry_out = box_module->wire(carry_out_name); | ||||||
| 				if (!carry_out || !carry_out->port_output) | 				if (!carry_out || !carry_out->port_output) | ||||||
| 					log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str()); | 					log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str()); | ||||||
| 
 | 
 | ||||||
| 				auto &ports = box_module->ports; | 				auto &ports = box_module->ports; | ||||||
| 				for (auto jt = ports.begin(); jt != ports.end(); ) { | 				for (auto jt = ports.begin(); jt != ports.end(); ) { | ||||||
|  | @ -776,19 +783,19 @@ void AigerReader::post_process() | ||||||
| 		// NB: Assume box_module->ports are sorted alphabetically
 | 		// NB: Assume box_module->ports are sorted alphabetically
 | ||||||
| 		//     (as RTLIL::Module::fixup_ports() would do)
 | 		//     (as RTLIL::Module::fixup_ports() would do)
 | ||||||
| 		for (auto port_name : box_module->ports) { | 		for (auto port_name : box_module->ports) { | ||||||
| 			RTLIL::Wire* w = box_module->wire(port_name); | 			RTLIL::Wire* port = box_module->wire(port_name); | ||||||
| 			log_assert(w); | 			log_assert(port); | ||||||
| 			RTLIL::SigSpec rhs; | 			RTLIL::SigSpec rhs; | ||||||
| 			RTLIL::Wire* wire = nullptr; | 			for (int i = 0; i < GetSize(port); i++) { | ||||||
| 			for (int i = 0; i < GetSize(w); i++) { | 				RTLIL::Wire* wire = nullptr; | ||||||
| 				if (w->port_input) { | 				if (port->port_input) { | ||||||
| 					log_assert(co_count < outputs.size()); | 					log_assert(co_count < outputs.size()); | ||||||
| 					wire = outputs[co_count++]; | 					wire = outputs[co_count++]; | ||||||
| 					log_assert(wire); | 					log_assert(wire); | ||||||
| 					log_assert(wire->port_output); | 					log_assert(wire->port_output); | ||||||
| 					wire->port_output = false; | 					wire->port_output = false; | ||||||
| 				} | 				} | ||||||
| 				if (w->port_output) { | 				if (port->port_output) { | ||||||
| 					log_assert((piNum + ci_count) < inputs.size()); | 					log_assert((piNum + ci_count) < inputs.size()); | ||||||
| 					wire = inputs[piNum + ci_count++]; | 					wire = inputs[piNum + ci_count++]; | ||||||
| 					log_assert(wire); | 					log_assert(wire); | ||||||
|  | @ -797,6 +804,7 @@ void AigerReader::post_process() | ||||||
| 				} | 				} | ||||||
| 				rhs.append(wire); | 				rhs.append(wire); | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
| 			cell->setPort(port_name, rhs); | 			cell->setPort(port_name, rhs); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -814,6 +822,7 @@ void AigerReader::post_process() | ||||||
| 				RTLIL::Wire* wire = inputs[variable]; | 				RTLIL::Wire* wire = inputs[variable]; | ||||||
| 				log_assert(wire); | 				log_assert(wire); | ||||||
| 				log_assert(wire->port_input); | 				log_assert(wire->port_input); | ||||||
|  | 				log_debug("Renaming input %s", log_id(wire)); | ||||||
| 
 | 
 | ||||||
| 				if (index == 0) { | 				if (index == 0) { | ||||||
| 					// Cope with the fact that a CI might be identical
 | 					// Cope with the fact that a CI might be identical
 | ||||||
|  | @ -840,6 +849,7 @@ void AigerReader::post_process() | ||||||
| 						wire->port_input = false; | 						wire->port_input = false; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 				log_debug(" -> %s\n", log_id(wire)); | ||||||
| 			} | 			} | ||||||
| 			else if (type == "output") { | 			else if (type == "output") { | ||||||
| 				log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); | 				log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); | ||||||
|  | @ -850,6 +860,7 @@ void AigerReader::post_process() | ||||||
| 					wire->port_output = false; | 					wire->port_output = false; | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
|  | 				log_debug("Renaming output %s", log_id(wire)); | ||||||
| 
 | 
 | ||||||
| 				if (index == 0) { | 				if (index == 0) { | ||||||
| 					// Cope with the fact that a CO might be identical
 | 					// Cope with the fact that a CO might be identical
 | ||||||
|  | @ -859,7 +870,7 @@ void AigerReader::post_process() | ||||||
| 					if (!existing) { | 					if (!existing) { | ||||||
| 						if (escaped_s.ends_with("$inout.out")) { | 						if (escaped_s.ends_with("$inout.out")) { | ||||||
| 							wire->port_output = false; | 							wire->port_output = false; | ||||||
| 							RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10)); | 							RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11)); | ||||||
| 							log_assert(in_wire); | 							log_assert(in_wire); | ||||||
| 							log_assert(in_wire->port_input && !in_wire->port_output); | 							log_assert(in_wire->port_input && !in_wire->port_output); | ||||||
| 							in_wire->port_output = true; | 							in_wire->port_output = true; | ||||||
|  | @ -871,6 +882,7 @@ void AigerReader::post_process() | ||||||
| 					else { | 					else { | ||||||
| 						wire->port_output = false; | 						wire->port_output = false; | ||||||
| 						module->connect(wire, existing); | 						module->connect(wire, existing); | ||||||
|  | 						wire = existing; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				else if (index > 0) { | 				else if (index > 0) { | ||||||
|  | @ -879,7 +891,7 @@ void AigerReader::post_process() | ||||||
| 					if (!existing) { | 					if (!existing) { | ||||||
| 						if (escaped_s.ends_with("$inout.out")) { | 						if (escaped_s.ends_with("$inout.out")) { | ||||||
| 							wire->port_output = false; | 							wire->port_output = false; | ||||||
| 							RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index)); | 							RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index)); | ||||||
| 							log_assert(in_wire); | 							log_assert(in_wire); | ||||||
| 							log_assert(in_wire->port_input && !in_wire->port_output); | 							log_assert(in_wire->port_input && !in_wire->port_output); | ||||||
| 							in_wire->port_output = true; | 							in_wire->port_output = true; | ||||||
|  | @ -896,6 +908,7 @@ void AigerReader::post_process() | ||||||
| 						wire->port_output = false; | 						wire->port_output = false; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 				log_debug(" -> %s\n", log_id(wire)); | ||||||
| 			} | 			} | ||||||
| 			else if (type == "box") { | 			else if (type == "box") { | ||||||
| 				RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); | 				RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); | ||||||
|  | @ -974,7 +987,7 @@ void AigerReader::post_process() | ||||||
| 	// operate (and run checks on) this one module
 | 	// operate (and run checks on) this one module
 | ||||||
| 	RTLIL::Design *mapped_design = new RTLIL::Design; | 	RTLIL::Design *mapped_design = new RTLIL::Design; | ||||||
| 	mapped_design->add(module); | 	mapped_design->add(module); | ||||||
| 	Pass::call(mapped_design, "clean -purge"); | 	Pass::call(mapped_design, "clean"); | ||||||
| 	mapped_design->modules_.erase(module->name); | 	mapped_design->modules_.erase(module->name); | ||||||
| 	delete mapped_design; | 	delete mapped_design; | ||||||
| 
 | 
 | ||||||
|  | @ -1004,8 +1017,8 @@ struct AigerFrontend : public Frontend { | ||||||
| 		log("        Name of module to be created (default: <filename>)\n"); | 		log("        Name of module to be created (default: <filename>)\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -clk_name <wire_name>\n"); | 		log("    -clk_name <wire_name>\n"); | ||||||
| 		log("        AIGER latches to be transformed into posedge DFFs clocked by wire of"); | 		log("        If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); | ||||||
| 		log("        this name (default: clk)\n"); | 		log("        clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -map <filename>\n"); | 		log("    -map <filename>\n"); | ||||||
| 		log("        read file with port and latch symbols\n"); | 		log("        read file with port and latch symbols\n"); | ||||||
|  | @ -1045,13 +1058,15 @@ struct AigerFrontend : public Frontend { | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(f, filename, args, argidx); | 		extra_args(f, filename, args, argidx, true); | ||||||
| 
 | 
 | ||||||
| 		if (module_name.empty()) { | 		if (module_name.empty()) { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| 			char fname[_MAX_FNAME]; | 			char fname[_MAX_FNAME]; | ||||||
| 			_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */); | 			_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */); | ||||||
| 			module_name = fname; | 			char* bn = strdup(fname); | ||||||
|  | 			module_name = RTLIL::escape_id(bn); | ||||||
|  | 			free(bn); | ||||||
| #else | #else | ||||||
| 			char* bn = strdup(filename.c_str()); | 			char* bn = strdup(filename.c_str()); | ||||||
| 			module_name = RTLIL::escape_id(bn); | 			module_name = RTLIL::escape_id(bn); | ||||||
|  |  | ||||||
|  | @ -158,7 +158,14 @@ std::string AST::type2str(AstNodeType type) | ||||||
| 	X(AST_POSEDGE) | 	X(AST_POSEDGE) | ||||||
| 	X(AST_NEGEDGE) | 	X(AST_NEGEDGE) | ||||||
| 	X(AST_EDGE) | 	X(AST_EDGE) | ||||||
|  | 	X(AST_INTERFACE) | ||||||
|  | 	X(AST_INTERFACEPORT) | ||||||
|  | 	X(AST_INTERFACEPORTTYPE) | ||||||
|  | 	X(AST_MODPORT) | ||||||
|  | 	X(AST_MODPORTMEMBER) | ||||||
| 	X(AST_PACKAGE) | 	X(AST_PACKAGE) | ||||||
|  | 	X(AST_WIRETYPE) | ||||||
|  | 	X(AST_TYPEDEF) | ||||||
| #undef X | #undef X | ||||||
| 	default: | 	default: | ||||||
| 		log_abort(); | 		log_abort(); | ||||||
|  | @ -201,6 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch | ||||||
| 	was_checked = false; | 	was_checked = false; | ||||||
| 	range_valid = false; | 	range_valid = false; | ||||||
| 	range_swapped = false; | 	range_swapped = false; | ||||||
|  | 	is_custom_type = false; | ||||||
| 	port_id = 0; | 	port_id = 0; | ||||||
| 	range_left = -1; | 	range_left = -1; | ||||||
| 	range_right = 0; | 	range_right = 0; | ||||||
|  | @ -1099,6 +1107,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast | ||||||
| 
 | 
 | ||||||
| 		ignoreThisSignalsInInitial = RTLIL::SigSpec(); | 		ignoreThisSignalsInInitial = RTLIL::SigSpec(); | ||||||
| 	} | 	} | ||||||
|  | 	else { | ||||||
|  | 		for (auto &attr : ast->attributes) { | ||||||
|  | 			if (attr.second->type != AST_CONSTANT) | ||||||
|  | 				continue; | ||||||
|  | 			current_module->attributes[attr.first] = attr.second->asAttrConst(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ast->type == AST_INTERFACE) | 	if (ast->type == AST_INTERFACE) | ||||||
| 		current_module->set_bool_attribute("\\is_interface"); | 		current_module->set_bool_attribute("\\is_interface"); | ||||||
|  | @ -1284,6 +1299,8 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule | ||||||
| // from AST. The interface members are copied into the AST module with the prefix of the interface.
 | // from AST. The interface members are copied into the AST module with the prefix of the interface.
 | ||||||
| void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) | void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) | ||||||
| { | { | ||||||
|  | 	loadconfig(); | ||||||
|  | 
 | ||||||
| 	bool is_top = false; | 	bool is_top = false; | ||||||
| 	AstNode *new_ast = ast->clone(); | 	AstNode *new_ast = ast->clone(); | ||||||
| 	for (auto &intf : local_interfaces) { | 	for (auto &intf : local_interfaces) { | ||||||
|  | @ -1368,10 +1385,10 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT | ||||||
| 
 | 
 | ||||||
| // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
 | // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
 | ||||||
| // This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
 | // This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
 | ||||||
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) | RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool /*mayfail*/) | ||||||
| { | { | ||||||
| 	AstNode *new_ast = NULL; | 	AstNode *new_ast = NULL; | ||||||
| 	std::string modname = derive_common(design, parameters, &new_ast, mayfail); | 	std::string modname = derive_common(design, parameters, &new_ast); | ||||||
| 
 | 
 | ||||||
| 	// Since interfaces themselves may be instantiated with different parameters,
 | 	// Since interfaces themselves may be instantiated with different parameters,
 | ||||||
| 	// "modname" must also take those into account, so that unique modules
 | 	// "modname" must also take those into account, so that unique modules
 | ||||||
|  | @ -1384,11 +1401,17 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R | ||||||
| 		has_interfaces = true; | 		has_interfaces = true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	std::string new_modname = modname; | ||||||
| 	if (has_interfaces) | 	if (has_interfaces) | ||||||
| 		modname += "$interfaces$" + interf_info; | 		new_modname += "$interfaces$" + interf_info; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	if (!design->has(modname)) { | 	if (!design->has(new_modname)) { | ||||||
|  | 		if (!new_ast) { | ||||||
|  | 			auto mod = dynamic_cast<AstModule*>(design->module(modname)); | ||||||
|  | 			new_ast = mod->ast->clone(); | ||||||
|  | 		} | ||||||
|  | 		modname = new_modname; | ||||||
| 		new_ast->str = modname; | 		new_ast->str = modname; | ||||||
| 
 | 
 | ||||||
| 		// Iterate over all interfaces which are ports in this module:
 | 		// Iterate over all interfaces which are ports in this module:
 | ||||||
|  | @ -1441,10 +1464,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
 | // create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
 | ||||||
| RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) | RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) | ||||||
| { | { | ||||||
| 	AstNode *new_ast = NULL; | 	AstNode *new_ast = NULL; | ||||||
| 	std::string modname = derive_common(design, parameters, &new_ast, mayfail); | 	std::string modname = derive_common(design, parameters, &new_ast); | ||||||
| 
 | 
 | ||||||
| 	if (!design->has(modname)) { | 	if (!design->has(modname)) { | ||||||
| 		new_ast->str = modname; | 		new_ast->str = modname; | ||||||
|  | @ -1459,64 +1482,75 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // create a new parametric module (when needed) and return the name of the generated module
 | // create a new parametric module (when needed) and return the name of the generated module
 | ||||||
| std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool) | std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out) | ||||||
| { | { | ||||||
| 	std::string stripped_name = name.str(); | 	std::string stripped_name = name.str(); | ||||||
| 
 | 
 | ||||||
| 	if (stripped_name.compare(0, 9, "$abstract") == 0) | 	if (stripped_name.compare(0, 9, "$abstract") == 0) | ||||||
| 		stripped_name = stripped_name.substr(9); | 		stripped_name = stripped_name.substr(9); | ||||||
| 
 | 
 | ||||||
| 	log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); |  | ||||||
| 
 |  | ||||||
| 	current_ast = NULL; |  | ||||||
| 	flag_dump_ast1 = false; |  | ||||||
| 	flag_dump_ast2 = false; |  | ||||||
| 	flag_dump_vlog1 = false; |  | ||||||
| 	flag_dump_vlog2 = false; |  | ||||||
| 	flag_nolatches = nolatches; |  | ||||||
| 	flag_nomeminit = nomeminit; |  | ||||||
| 	flag_nomem2reg = nomem2reg; |  | ||||||
| 	flag_mem2reg = mem2reg; |  | ||||||
| 	flag_noblackbox = noblackbox; |  | ||||||
| 	flag_lib = lib; |  | ||||||
| 	flag_nowb = nowb; |  | ||||||
| 	flag_noopt = noopt; |  | ||||||
| 	flag_icells = icells; |  | ||||||
| 	flag_pwires = pwires; |  | ||||||
| 	flag_autowire = autowire; |  | ||||||
| 	use_internal_line_num(); |  | ||||||
| 
 |  | ||||||
| 	std::string para_info; | 	std::string para_info; | ||||||
| 	AstNode *new_ast = ast->clone(); |  | ||||||
| 
 | 
 | ||||||
| 	int para_counter = 0; | 	int para_counter = 0; | ||||||
| 	int orig_parameters_n = parameters.size(); | 	for (const auto child : ast->children) { | ||||||
| 	for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) { |  | ||||||
| 		AstNode *child = *it; |  | ||||||
| 		if (child->type != AST_PARAMETER) | 		if (child->type != AST_PARAMETER) | ||||||
| 			continue; | 			continue; | ||||||
| 		para_counter++; | 		para_counter++; | ||||||
| 		std::string para_id = child->str; | 		std::string para_id = child->str; | ||||||
| 		if (parameters.count(para_id) > 0) { | 		if (parameters.count(para_id) > 0) { | ||||||
| 			log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); | 			log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); | ||||||
| 	rewrite_parameter: |  | ||||||
| 			para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | 			para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | ||||||
| 			delete child->children.at(0); |  | ||||||
| 			if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) { |  | ||||||
| 				child->children[0] = new AstNode(AST_REALVALUE); |  | ||||||
| 				child->children[0]->realvalue = std::stod(parameters[para_id].decode_string()); |  | ||||||
| 			} else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) |  | ||||||
| 				child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string()); |  | ||||||
| 			else |  | ||||||
| 				child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); |  | ||||||
| 			parameters.erase(para_id); |  | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 		para_id = stringf("$%d", para_counter); | 		para_id = stringf("$%d", para_counter); | ||||||
|  | 		if (parameters.count(para_id) > 0) { | ||||||
|  | 			log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | ||||||
|  | 			para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::string modname; | ||||||
|  | 	if (parameters.size() == 0) | ||||||
|  | 		modname = stripped_name; | ||||||
|  | 	else if (para_info.size() > 60) | ||||||
|  | 		modname = "$paramod$" + sha1(para_info) + stripped_name; | ||||||
|  | 	else | ||||||
|  | 		modname = "$paramod" + stripped_name + para_info; | ||||||
|  | 
 | ||||||
|  | 	if (design->has(modname)) | ||||||
|  | 		return modname; | ||||||
|  | 
 | ||||||
|  | 	log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); | ||||||
|  | 	loadconfig(); | ||||||
|  | 
 | ||||||
|  | 	AstNode *new_ast = ast->clone(); | ||||||
|  | 	para_counter = 0; | ||||||
|  | 	for (auto child : new_ast->children) { | ||||||
|  | 		if (child->type != AST_PARAMETER) | ||||||
|  | 			continue; | ||||||
|  | 		para_counter++; | ||||||
|  | 		std::string para_id = child->str; | ||||||
|  | 		if (parameters.count(para_id) > 0) { | ||||||
|  | 			log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); | ||||||
|  | 			goto rewrite_parameter; | ||||||
|  | 		} | ||||||
|  | 		para_id = stringf("$%d", para_counter); | ||||||
| 		if (parameters.count(para_id) > 0) { | 		if (parameters.count(para_id) > 0) { | ||||||
| 			log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | 			log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | ||||||
| 			goto rewrite_parameter; | 			goto rewrite_parameter; | ||||||
| 		} | 		} | ||||||
|  | 		continue; | ||||||
|  | 	rewrite_parameter: | ||||||
|  | 		delete child->children.at(0); | ||||||
|  | 		if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) { | ||||||
|  | 			child->children[0] = new AstNode(AST_REALVALUE); | ||||||
|  | 			child->children[0]->realvalue = std::stod(parameters[para_id].decode_string()); | ||||||
|  | 		} else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) | ||||||
|  | 			child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string()); | ||||||
|  | 		else | ||||||
|  | 			child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); | ||||||
|  | 		parameters.erase(para_id); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (auto param : parameters) { | 	for (auto param : parameters) { | ||||||
|  | @ -1529,16 +1563,6 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString | ||||||
| 		new_ast->children.push_back(defparam); | 		new_ast->children.push_back(defparam); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::string modname; |  | ||||||
| 
 |  | ||||||
| 	if (orig_parameters_n == 0) |  | ||||||
| 		modname = stripped_name; |  | ||||||
| 	else if (para_info.size() > 60) |  | ||||||
| 		modname = "$paramod$" + sha1(para_info) + stripped_name; |  | ||||||
| 	else |  | ||||||
| 		modname = "$paramod" + stripped_name + para_info; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	(*new_ast_out) = new_ast; | 	(*new_ast_out) = new_ast; | ||||||
| 	return modname; | 	return modname; | ||||||
| } | } | ||||||
|  | @ -1565,6 +1589,27 @@ RTLIL::Module *AstModule::clone() const | ||||||
| 	return new_mod; | 	return new_mod; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void AstModule::loadconfig() const | ||||||
|  | { | ||||||
|  | 	current_ast = NULL; | ||||||
|  | 	flag_dump_ast1 = false; | ||||||
|  | 	flag_dump_ast2 = false; | ||||||
|  | 	flag_dump_vlog1 = false; | ||||||
|  | 	flag_dump_vlog2 = false; | ||||||
|  | 	flag_nolatches = nolatches; | ||||||
|  | 	flag_nomeminit = nomeminit; | ||||||
|  | 	flag_nomem2reg = nomem2reg; | ||||||
|  | 	flag_mem2reg = mem2reg; | ||||||
|  | 	flag_noblackbox = noblackbox; | ||||||
|  | 	flag_lib = lib; | ||||||
|  | 	flag_nowb = nowb; | ||||||
|  | 	flag_noopt = noopt; | ||||||
|  | 	flag_icells = icells; | ||||||
|  | 	flag_pwires = pwires; | ||||||
|  | 	flag_autowire = autowire; | ||||||
|  | 	use_internal_line_num(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // internal dummy line number callbacks
 | // internal dummy line number callbacks
 | ||||||
| namespace { | namespace { | ||||||
| 	int internal_line_num; | 	int internal_line_num; | ||||||
|  |  | ||||||
|  | @ -148,7 +148,10 @@ namespace AST | ||||||
| 		AST_INTERFACEPORTTYPE, | 		AST_INTERFACEPORTTYPE, | ||||||
| 		AST_MODPORT, | 		AST_MODPORT, | ||||||
| 		AST_MODPORTMEMBER, | 		AST_MODPORTMEMBER, | ||||||
| 		AST_PACKAGE | 		AST_PACKAGE, | ||||||
|  | 
 | ||||||
|  | 		AST_WIRETYPE, | ||||||
|  | 		AST_TYPEDEF | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	// convert an node type to a string (e.g. for debug output)
 | 	// convert an node type to a string (e.g. for debug output)
 | ||||||
|  | @ -174,7 +177,7 @@ namespace AST | ||||||
| 		// node content - most of it is unused in most node types
 | 		// node content - most of it is unused in most node types
 | ||||||
| 		std::string str; | 		std::string str; | ||||||
| 		std::vector<RTLIL::State> bits; | 		std::vector<RTLIL::State> bits; | ||||||
| 		bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized; | 		bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type; | ||||||
| 		int port_id, range_left, range_right; | 		int port_id, range_left, range_right; | ||||||
| 		uint32_t integer; | 		uint32_t integer; | ||||||
| 		double realvalue; | 		double realvalue; | ||||||
|  | @ -296,9 +299,10 @@ namespace AST | ||||||
| 		~AstModule() YS_OVERRIDE; | 		~AstModule() YS_OVERRIDE; | ||||||
| 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; | 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; | ||||||
| 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; | 		RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; | ||||||
| 		std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail); | 		std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out); | ||||||
| 		void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE; | 		void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE; | ||||||
| 		RTLIL::Module *clone() const YS_OVERRIDE; | 		RTLIL::Module *clone() const YS_OVERRIDE; | ||||||
|  | 		void loadconfig() const; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	// this must be set by the language frontend before parsing the sources
 | 	// this must be set by the language frontend before parsing the sources
 | ||||||
|  |  | ||||||
|  | @ -863,6 +863,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | ||||||
| 	case AST_PACKAGE: | 	case AST_PACKAGE: | ||||||
| 	case AST_MODPORT: | 	case AST_MODPORT: | ||||||
| 	case AST_MODPORTMEMBER: | 	case AST_MODPORTMEMBER: | ||||||
|  | 	case AST_TYPEDEF: | ||||||
| 		break; | 		break; | ||||||
| 	case AST_INTERFACEPORT: { | 	case AST_INTERFACEPORT: { | ||||||
| 		// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
 | 		// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
 | ||||||
|  |  | ||||||
|  | @ -318,7 +318,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
 | 	// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
 | ||||||
| 	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX) | 	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF) | ||||||
| 		const_fold = true; | 		const_fold = true; | ||||||
| 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) | 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) | ||||||
| 		const_fold = true; | 		const_fold = true; | ||||||
|  | @ -336,6 +336,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 		std::map<std::string, AstNode*> this_wire_scope; | 		std::map<std::string, AstNode*> this_wire_scope; | ||||||
| 		for (size_t i = 0; i < children.size(); i++) { | 		for (size_t i = 0; i < children.size(); i++) { | ||||||
| 			AstNode *node = children[i]; | 			AstNode *node = children[i]; | ||||||
|  | 
 | ||||||
| 			if (node->type == AST_WIRE) { | 			if (node->type == AST_WIRE) { | ||||||
| 				if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { | 				if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { | ||||||
| 					for (auto c : node->children[0]->children) { | 					for (auto c : node->children[0]->children) { | ||||||
|  | @ -405,14 +406,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 				this_wire_scope[node->str] = node; | 				this_wire_scope[node->str] = node; | ||||||
| 			} | 			} | ||||||
| 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || | 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || | ||||||
| 					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) { | 					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL || | ||||||
|  | 					node->type == AST_TYPEDEF) { | ||||||
| 				backup_scope[node->str] = current_scope[node->str]; | 				backup_scope[node->str] = current_scope[node->str]; | ||||||
| 				current_scope[node->str] = node; | 				current_scope[node->str] = node; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		for (size_t i = 0; i < children.size(); i++) { | 		for (size_t i = 0; i < children.size(); i++) { | ||||||
| 			AstNode *node = children[i]; | 			AstNode *node = children[i]; | ||||||
| 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY) | 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF) | ||||||
| 				while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) | 				while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) | ||||||
| 					did_something = true; | 					did_something = true; | ||||||
| 		} | 		} | ||||||
|  | @ -780,6 +782,99 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 		delete_children(); | 		delete_children(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// resolve typedefs
 | ||||||
|  | 	if (type == AST_TYPEDEF) { | ||||||
|  | 		log_assert(children.size() == 1); | ||||||
|  | 		log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY); | ||||||
|  | 		while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) | ||||||
|  | 			did_something = true; | ||||||
|  | 		log_assert(!children[0]->is_custom_type); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// resolve types of wires
 | ||||||
|  | 	if (type == AST_WIRE || type == AST_MEMORY) { | ||||||
|  | 		if (is_custom_type) { | ||||||
|  | 			log_assert(children.size() >= 1); | ||||||
|  | 			log_assert(children[0]->type == AST_WIRETYPE); | ||||||
|  | 			if (!current_scope.count(children[0]->str)) | ||||||
|  | 				log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str()); | ||||||
|  | 			AstNode *resolved_type = current_scope.at(children[0]->str); | ||||||
|  | 			if (resolved_type->type != AST_TYPEDEF) | ||||||
|  | 				log_file_error(filename, linenum, "`%s' does not name a type\n", children[0]->str.c_str()); | ||||||
|  | 			log_assert(resolved_type->children.size() == 1); | ||||||
|  | 			AstNode *templ = resolved_type->children[0]; | ||||||
|  | 			// Remove type reference
 | ||||||
|  | 			delete children[0]; | ||||||
|  | 			children.erase(children.begin()); | ||||||
|  | 
 | ||||||
|  | 			// Ensure typedef itself is fully simplified
 | ||||||
|  | 			while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; | ||||||
|  | 
 | ||||||
|  | 			if (type == AST_WIRE) | ||||||
|  | 				type = templ->type; | ||||||
|  | 			is_reg = templ->is_reg; | ||||||
|  | 			is_logic = templ->is_logic; | ||||||
|  | 			is_signed = templ->is_signed; | ||||||
|  | 			is_string = templ->is_string; | ||||||
|  | 			is_custom_type = templ->is_custom_type; | ||||||
|  | 
 | ||||||
|  | 			range_valid = templ->range_valid; | ||||||
|  | 			range_swapped = templ->range_swapped; | ||||||
|  | 			range_left = templ->range_left; | ||||||
|  | 			range_right = templ->range_right; | ||||||
|  | 
 | ||||||
|  | 			// Insert clones children from template at beginning
 | ||||||
|  | 			for (int i  = 0; i < GetSize(templ->children); i++) | ||||||
|  | 				children.insert(children.begin() + i, templ->children[i]->clone()); | ||||||
|  | 			 | ||||||
|  | 			if (type == AST_MEMORY && GetSize(children) == 1) { | ||||||
|  | 				// Single-bit memories must have [0:0] range
 | ||||||
|  | 				AstNode *rng = new AstNode(AST_RANGE); | ||||||
|  | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
|  | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
|  | 				children.insert(children.begin(), rng); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			did_something = true; | ||||||
|  | 		} | ||||||
|  | 		log_assert(!is_custom_type); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// resolve types of parameters
 | ||||||
|  | 	if (type == AST_LOCALPARAM || type == AST_PARAMETER) { | ||||||
|  | 		if (is_custom_type) { | ||||||
|  | 			log_assert(children.size() == 2); | ||||||
|  | 			log_assert(children[1]->type == AST_WIRETYPE); | ||||||
|  | 			if (!current_scope.count(children[1]->str)) | ||||||
|  | 				log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str()); | ||||||
|  | 			AstNode *resolved_type = current_scope.at(children[1]->str); | ||||||
|  | 			if (resolved_type->type != AST_TYPEDEF) | ||||||
|  | 				log_file_error(filename, linenum, "`%s' does not name a type\n", children[1]->str.c_str()); | ||||||
|  | 			log_assert(resolved_type->children.size() == 1); | ||||||
|  | 			AstNode *templ = resolved_type->children[0]; | ||||||
|  | 			delete children[1]; | ||||||
|  | 			children.pop_back(); | ||||||
|  | 
 | ||||||
|  | 			// Ensure typedef itself is fully simplified
 | ||||||
|  | 			while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; | ||||||
|  | 
 | ||||||
|  | 			if (templ->type == AST_MEMORY) | ||||||
|  | 				log_file_error(filename, linenum, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); | ||||||
|  | 			is_signed = templ->is_signed; | ||||||
|  | 			is_string = templ->is_string; | ||||||
|  | 			is_custom_type = templ->is_custom_type; | ||||||
|  | 
 | ||||||
|  | 			range_valid = templ->range_valid; | ||||||
|  | 			range_swapped = templ->range_swapped; | ||||||
|  | 			range_left = templ->range_left; | ||||||
|  | 			range_right = templ->range_right; | ||||||
|  | 			for (auto template_child : templ->children) | ||||||
|  | 				children.push_back(template_child->clone()); | ||||||
|  | 			did_something = true; | ||||||
|  | 		} | ||||||
|  | 		log_assert(!is_custom_type); | ||||||
|  | 	}	 | ||||||
|  | 
 | ||||||
| 	// resolve constant prefixes
 | 	// resolve constant prefixes
 | ||||||
| 	if (type == AST_PREFIX) { | 	if (type == AST_PREFIX) { | ||||||
| 		if (children[0]->type != AST_CONSTANT) { | 		if (children[0]->type != AST_CONSTANT) { | ||||||
|  | @ -1194,7 +1289,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 	if (type == AST_BLOCK && str.empty()) | 	if (type == AST_BLOCK && str.empty()) | ||||||
| 	{ | 	{ | ||||||
| 		for (size_t i = 0; i < children.size(); i++) | 		for (size_t i = 0; i < children.size(); i++) | ||||||
| 			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) | 			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) | ||||||
| 				log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); | 				log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1206,7 +1301,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 
 | 
 | ||||||
| 		std::vector<AstNode*> new_children; | 		std::vector<AstNode*> new_children; | ||||||
| 		for (size_t i = 0; i < children.size(); i++) | 		for (size_t i = 0; i < children.size(); i++) | ||||||
| 			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) { | 			if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) { | ||||||
| 				children[i]->simplify(false, false, false, stage, -1, false, false); | 				children[i]->simplify(false, false, false, stage, -1, false, false); | ||||||
| 				current_ast_mod->children.push_back(children[i]); | 				current_ast_mod->children.push_back(children[i]); | ||||||
| 				current_scope[children[i]->str] = children[i]; | 				current_scope[children[i]->str] = children[i]; | ||||||
|  | @ -1530,10 +1625,16 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		current_scope[wire_en->str] = wire_en; | 		current_scope[wire_en->str] = wire_en; | ||||||
| 		while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } | 		while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } | ||||||
| 
 | 
 | ||||||
| 		std::vector<RTLIL::State> x_bit; | 		AstNode *check_defval; | ||||||
| 		x_bit.push_back(RTLIL::State::Sx); | 		if (type == AST_LIVE || type == AST_FAIR) { | ||||||
|  | 			check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); | ||||||
|  | 		} else { | ||||||
|  | 			std::vector<RTLIL::State> x_bit; | ||||||
|  | 			x_bit.push_back(RTLIL::State::Sx); | ||||||
|  | 			check_defval = mkconst_bits(x_bit, false); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false)); | 		AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval); | ||||||
| 		assign_check->children[0]->str = id_check; | 		assign_check->children[0]->str = id_check; | ||||||
| 		assign_check->children[0]->was_checked = true; | 		assign_check->children[0]->was_checked = true; | ||||||
| 
 | 
 | ||||||
|  | @ -1546,9 +1647,13 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		default_signals->children.push_back(assign_en); | 		default_signals->children.push_back(assign_en); | ||||||
| 		current_top_block->children.insert(current_top_block->children.begin(), default_signals); | 		current_top_block->children.insert(current_top_block->children.begin(), default_signals); | ||||||
| 
 | 
 | ||||||
| 		assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone())); | 		if (type == AST_LIVE || type == AST_FAIR) { | ||||||
| 		assign_check->children[0]->str = id_check; | 			assign_check = nullptr; | ||||||
| 		assign_check->children[0]->was_checked = true; | 		} else { | ||||||
|  | 			assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone())); | ||||||
|  | 			assign_check->children[0]->str = id_check; | ||||||
|  | 			assign_check->children[0]->was_checked = true; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		if (current_always == nullptr || current_always->type != AST_INITIAL) { | 		if (current_always == nullptr || current_always->type != AST_INITIAL) { | ||||||
| 			assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); | 			assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); | ||||||
|  | @ -1560,7 +1665,8 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		assign_en->children[0]->was_checked = true; | 		assign_en->children[0]->was_checked = true; | ||||||
| 
 | 
 | ||||||
| 		newNode = new AstNode(AST_BLOCK); | 		newNode = new AstNode(AST_BLOCK); | ||||||
| 		newNode->children.push_back(assign_check); | 		if (assign_check != nullptr) | ||||||
|  | 			newNode->children.push_back(assign_check); | ||||||
| 		newNode->children.push_back(assign_en); | 		newNode->children.push_back(assign_en); | ||||||
| 
 | 
 | ||||||
| 		AstNode *assertnode = new AstNode(type); | 		AstNode *assertnode = new AstNode(type); | ||||||
|  | @ -2884,11 +2990,18 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m | ||||||
| void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map) | void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map) | ||||||
| { | { | ||||||
| 	if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { | 	if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { | ||||||
| 		current_scope[index_var]->children[0]->cloneInto(this); | 		if (children.empty()) { | ||||||
| 		return; | 			current_scope[index_var]->children[0]->cloneInto(this); | ||||||
|  | 		} else { | ||||||
|  | 			AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); | ||||||
|  | 			p->str = stringf("$genval$%d", autoidx++); | ||||||
|  | 			current_ast_mod->children.push_back(p); | ||||||
|  | 			str = p->str; | ||||||
|  | 			id2ast = p; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0) | 	if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0) | ||||||
| 		str = name_map[str]; | 		str = name_map[str]; | ||||||
| 
 | 
 | ||||||
| 	std::map<std::string, std::string> backup_name_map; | 	std::map<std::string, std::string> backup_name_map; | ||||||
|  | @ -2896,7 +3009,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma | ||||||
| 	for (size_t i = 0; i < children.size(); i++) { | 	for (size_t i = 0; i < children.size(); i++) { | ||||||
| 		AstNode *child = children[i]; | 		AstNode *child = children[i]; | ||||||
| 		if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || | 		if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || | ||||||
| 				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) { | 				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) { | ||||||
| 			if (backup_name_map.size() == 0) | 			if (backup_name_map.size() == 0) | ||||||
| 				backup_name_map = name_map; | 				backup_name_map = name_map; | ||||||
| 			std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; | 			std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; | ||||||
|  | @ -2927,6 +3040,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma | ||||||
| 			child->expand_genblock(index_var, prefix, name_map); | 			child->expand_genblock(index_var, prefix, name_map); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 	if (backup_name_map.size() > 0) | 	if (backup_name_map.size() > 0) | ||||||
| 		name_map.swap(backup_name_map); | 		name_map.swap(backup_name_map); | ||||||
| } | } | ||||||
|  | @ -2980,6 +3094,9 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg | ||||||
| 	uint32_t children_flags = 0; | 	uint32_t children_flags = 0; | ||||||
| 	int lhs_children_counter = 0; | 	int lhs_children_counter = 0; | ||||||
| 
 | 
 | ||||||
|  | 	if (type == AST_TYPEDEF) | ||||||
|  | 		return; // don't touch content of typedefs
 | ||||||
|  | 
 | ||||||
| 	if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) | 	if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) | ||||||
| 	{ | 	{ | ||||||
| 		// mark all memories that are used in a complex expression on the left side of an assignment
 | 		// mark all memories that are used in a complex expression on the left side of an assignment
 | ||||||
|  | @ -3137,6 +3254,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, | ||||||
| 	if (type == AST_FUNCTION || type == AST_TASK) | 	if (type == AST_FUNCTION || type == AST_TASK) | ||||||
| 		return false; | 		return false; | ||||||
| 
 | 
 | ||||||
|  | 	if (type == AST_TYPEDEF) | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
| 	if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) | 	if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) | ||||||
| 	{ | 	{ | ||||||
| 		log_assert(children[0]->type == AST_CONSTANT); | 		log_assert(children[0]->type == AST_CONSTANT); | ||||||
|  |  | ||||||
|  | @ -174,6 +174,12 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool | ||||||
| 			if (module == nullptr) | 			if (module == nullptr) | ||||||
| 				goto error; | 				goto error; | ||||||
| 
 | 
 | ||||||
|  | 			if (!strcmp(cmd, ".blackbox")) | ||||||
|  | 			{ | ||||||
|  | 				module->attributes["\\blackbox"] = RTLIL::Const(1); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			if (!strcmp(cmd, ".end")) | 			if (!strcmp(cmd, ".end")) | ||||||
| 			{ | 			{ | ||||||
| 				for (auto &wp : wideports_cache) | 				for (auto &wp : wideports_cache) | ||||||
|  | @ -280,7 +286,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool | ||||||
| 					goto error_with_reason; | 					goto error_with_reason; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				module->rename(lastcell, p); | 				module->rename(lastcell, RTLIL::escape_id(p)); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								frontends/rpc/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								frontends/rpc/Makefile.inc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | 
 | ||||||
|  | OBJS += frontends/rpc/rpc_frontend.o | ||||||
							
								
								
									
										595
									
								
								frontends/rpc/rpc_frontend.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										595
									
								
								frontends/rpc/rpc_frontend.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,595 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2019  whitequark <whitequark@whitequark.org> | ||||||
|  |  * | ||||||
|  |  *  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. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // The reason the -path mode of connect_rpc uses byte-oriented and not message-oriented sockets, even though
 | ||||||
|  | // it is a message-oriented interface, is that the system can place various limits on the message size, which
 | ||||||
|  | // are not always transparent or easy to change. Given that generated HDL code get be extremely large, it is
 | ||||||
|  | // unwise to rely on those limits being large enough, and using byte-oriented sockets is guaranteed to work.
 | ||||||
|  | 
 | ||||||
|  | #ifndef _WIN32 | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <spawn.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/un.h> | ||||||
|  | extern char **environ; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include "libs/json11/json11.hpp" | ||||||
|  | #include "libs/sha1/sha1.h" | ||||||
|  | #include "kernel/yosys.h" | ||||||
|  | 
 | ||||||
|  | YOSYS_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  | static std::wstring str2wstr(const std::string &in) { | ||||||
|  |     if(in == "") return L""; | ||||||
|  |     std::wstring out; | ||||||
|  |     out.resize(MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/NULL, /*cchWideChar=*/0)); | ||||||
|  |     int written = MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/&out[0], /*cchWideChar=*/(int)out.length()); | ||||||
|  |     log_assert(written == (int)out.length()); | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static std::string wstr2str(const std::wstring &in) { | ||||||
|  | 	if(in == L"") return ""; | ||||||
|  | 	std::string out; | ||||||
|  | 	out.resize(WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/NULL, /*cbMultiByte=*/0, /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL)); | ||||||
|  | 	int written = WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/&out[0], /*cbMultiByte=*/(int)out.length(), /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL); | ||||||
|  | 	log_assert(written == (int)out.length()); | ||||||
|  | 	return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static std::string get_last_error_str() { | ||||||
|  | 	DWORD last_error = GetLastError(); | ||||||
|  | 	LPWSTR out_w; | ||||||
|  | 	DWORD size_w = FormatMessageW(/*dwFlags=*/FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, /*lpSource=*/NULL, /*dwMessageId=*/last_error, /*dwLanguageId=*/0, /*lpBuffer=*/(LPWSTR)&out_w, /*nSize=*/0, /*Arguments=*/NULL); | ||||||
|  | 	if (size_w == 0) | ||||||
|  | 		return std::to_string(last_error); | ||||||
|  | 	std::string out = wstr2str(std::wstring(out_w, size_w)); | ||||||
|  | 	LocalFree(out_w); | ||||||
|  | 	return out; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | using json11::Json; | ||||||
|  | 
 | ||||||
|  | struct RpcServer { | ||||||
|  | 	std::string name; | ||||||
|  | 
 | ||||||
|  | 	RpcServer(const std::string &name) : name(name) { } | ||||||
|  | 	virtual ~RpcServer() { } | ||||||
|  | 
 | ||||||
|  | 	virtual void write(const std::string &data) = 0; | ||||||
|  | 	virtual std::string read() = 0; | ||||||
|  | 
 | ||||||
|  | 	Json call(const Json &json_request) { | ||||||
|  | 		std::string request; | ||||||
|  | 		json_request.dump(request); | ||||||
|  | 		request += '\n'; | ||||||
|  | 		log_debug("RPC frontend request: %s", request.c_str()); | ||||||
|  | 		write(request); | ||||||
|  | 
 | ||||||
|  | 		std::string response = read(); | ||||||
|  | 		log_debug("RPC frontend response: %s", response.c_str()); | ||||||
|  | 		std::string error; | ||||||
|  | 		Json json_response = Json::parse(response, error); | ||||||
|  | 		if (json_response.is_null()) | ||||||
|  | 			log_cmd_error("parsing JSON failed: %s\n", error.c_str()); | ||||||
|  | 		if (json_response["error"].is_string()) | ||||||
|  | 			log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str()); | ||||||
|  | 		return json_response; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::vector<std::string> get_module_names() { | ||||||
|  | 		Json response = call(Json::object { | ||||||
|  | 			{ "method", "modules" }, | ||||||
|  | 		}); | ||||||
|  | 		bool is_valid = true; | ||||||
|  | 		std::vector<std::string> modules; | ||||||
|  | 		if (response["modules"].is_array()) { | ||||||
|  | 			for (auto &json_module : response["modules"].array_items()) { | ||||||
|  | 				if (json_module.is_string()) | ||||||
|  | 					modules.push_back(json_module.string_value()); | ||||||
|  | 				else is_valid = false; | ||||||
|  | 			} | ||||||
|  | 		} else is_valid = false; | ||||||
|  | 		if (!is_valid) | ||||||
|  | 			log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); | ||||||
|  | 		return modules; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::pair<std::string, std::string> derive_module(const std::string &module, const dict<RTLIL::IdString, RTLIL::Const> ¶meters) { | ||||||
|  | 		Json::object json_parameters; | ||||||
|  | 		for (auto ¶m : parameters) { | ||||||
|  | 			std::string type, value; | ||||||
|  | 			if (param.second.flags & RTLIL::CONST_FLAG_REAL) { | ||||||
|  | 				type = "real"; | ||||||
|  | 				value = param.second.decode_string(); | ||||||
|  | 			} else if (param.second.flags & RTLIL::CONST_FLAG_STRING) { | ||||||
|  | 				type = "string"; | ||||||
|  | 				value = param.second.decode_string(); | ||||||
|  | 			} else if ((param.second.flags & ~RTLIL::CONST_FLAG_SIGNED) == RTLIL::CONST_FLAG_NONE) { | ||||||
|  | 				type = (param.second.flags & RTLIL::CONST_FLAG_SIGNED) ? "signed" : "unsigned"; | ||||||
|  | 				value = param.second.as_string(); | ||||||
|  | 			} else | ||||||
|  | 				log_cmd_error("Unserializable constant flags 0x%x\n", param.second.flags); | ||||||
|  | 			json_parameters[param.first.str()] = Json::object { | ||||||
|  | 				{ "type", type }, | ||||||
|  | 				{ "value", value }, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 		Json response = call(Json::object { | ||||||
|  | 			{ "method", "derive" }, | ||||||
|  | 			{ "module", module }, | ||||||
|  | 			{ "parameters", json_parameters }, | ||||||
|  | 		}); | ||||||
|  | 		bool is_valid = true; | ||||||
|  | 		std::string frontend, source; | ||||||
|  | 		if (response["frontend"].is_string()) | ||||||
|  | 			frontend = response["frontend"].string_value(); | ||||||
|  | 		else is_valid = false; | ||||||
|  | 		if (response["source"].is_string()) | ||||||
|  | 			source = response["source"].string_value(); | ||||||
|  | 		else is_valid = false; | ||||||
|  | 		if (!is_valid) | ||||||
|  | 			log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); | ||||||
|  | 		return std::make_pair(frontend, source); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct RpcModule : RTLIL::Module { | ||||||
|  | 	std::shared_ptr<RpcServer> server; | ||||||
|  | 
 | ||||||
|  | 	RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) YS_OVERRIDE { | ||||||
|  | 		std::string stripped_name = name.str(); | ||||||
|  | 		if (stripped_name.compare(0, 9, "$abstract") == 0) | ||||||
|  | 			stripped_name = stripped_name.substr(9); | ||||||
|  | 		log_assert(stripped_name[0] == '\\'); | ||||||
|  | 
 | ||||||
|  | 		log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str()); | ||||||
|  | 
 | ||||||
|  | 		std::string parameter_info; | ||||||
|  | 		for (auto ¶m : parameters) { | ||||||
|  | 			log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); | ||||||
|  | 			parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		std::string derived_name; | ||||||
|  | 		if (parameters.empty()) | ||||||
|  | 			derived_name = stripped_name; | ||||||
|  | 		else if (parameter_info.size() > 60) | ||||||
|  | 			derived_name = "$paramod$" + sha1(parameter_info) + stripped_name; | ||||||
|  | 		else | ||||||
|  | 			derived_name = "$paramod" + stripped_name + parameter_info; | ||||||
|  | 
 | ||||||
|  | 		if (design->has(derived_name)) { | ||||||
|  | 			log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str()); | ||||||
|  | 		} else { | ||||||
|  | 			std::string command, input; | ||||||
|  | 			std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters); | ||||||
|  | 
 | ||||||
|  | 			std::istringstream input_stream(input); | ||||||
|  | 			RTLIL::Design *derived_design = new RTLIL::Design; | ||||||
|  | 			Frontend::frontend_call(derived_design, &input_stream, "<rpc>" + derived_name.substr(8), command); | ||||||
|  | 			derived_design->check(); | ||||||
|  | 
 | ||||||
|  | 			dict<std::string, std::string> name_mangling; | ||||||
|  | 			bool found_derived_top = false; | ||||||
|  | 			for (auto module : derived_design->modules()) { | ||||||
|  | 				std::string original_name = module->name.str(); | ||||||
|  | 				if (original_name == stripped_name) { | ||||||
|  | 					found_derived_top = true; | ||||||
|  | 					name_mangling[original_name] = derived_name; | ||||||
|  | 				} else { | ||||||
|  | 					name_mangling[original_name] = derived_name + module->name.str(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if (!found_derived_top) | ||||||
|  | 				log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str()); | ||||||
|  | 
 | ||||||
|  | 			for (auto module : derived_design->modules()) | ||||||
|  | 				for (auto cell : module->cells()) | ||||||
|  | 					if (name_mangling.count(cell->type.str())) | ||||||
|  | 						cell->type = name_mangling[cell->type.str()]; | ||||||
|  | 
 | ||||||
|  | 			for (auto module : derived_design->modules_) { | ||||||
|  | 				std::string mangled_name = name_mangling[module.first.str()]; | ||||||
|  | 
 | ||||||
|  | 				log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name)); | ||||||
|  | 
 | ||||||
|  | 				module.second->name = mangled_name; | ||||||
|  | 				module.second->design = design; | ||||||
|  | 				module.second->attributes.erase("\\top"); | ||||||
|  | 				design->modules_[mangled_name] = module.second; | ||||||
|  | 				derived_design->modules_.erase(module.first); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			delete derived_design; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return derived_name; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	RTLIL::Module *clone() const YS_OVERRIDE { | ||||||
|  | 		RpcModule *new_mod = new RpcModule; | ||||||
|  | 		new_mod->server = server; | ||||||
|  | 		cloneInto(new_mod); | ||||||
|  | 		return new_mod; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  | 
 | ||||||
|  | #if defined(_MSC_VER) | ||||||
|  | #include <BaseTsd.h> | ||||||
|  | typedef SSIZE_T ssize_t; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct HandleRpcServer : RpcServer { | ||||||
|  | 	HANDLE hsend, hrecv; | ||||||
|  | 
 | ||||||
|  | 	HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv) | ||||||
|  | 		: RpcServer(name), hsend(hsend), hrecv(hrecv) { } | ||||||
|  | 
 | ||||||
|  | 	void write(const std::string &data) YS_OVERRIDE { | ||||||
|  | 		log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); | ||||||
|  | 		ssize_t offset = 0; | ||||||
|  | 		do { | ||||||
|  | 			DWORD data_written; | ||||||
|  | 			if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL)) | ||||||
|  | 				log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 			offset += data_written; | ||||||
|  | 		} while(offset < (ssize_t)data.length()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::string read() YS_OVERRIDE { | ||||||
|  | 		std::string data; | ||||||
|  | 		ssize_t offset = 0; | ||||||
|  | 		while (data.length() == 0 || data[data.length() - 1] != '\n') { | ||||||
|  | 			data.resize(data.length() + 1024); | ||||||
|  | 			DWORD data_read; | ||||||
|  | 			if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL)) | ||||||
|  | 				log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 			offset += data_read; | ||||||
|  | 			data.resize(offset); | ||||||
|  | 			size_t term_pos = data.find('\n', offset); | ||||||
|  | 			if (term_pos != data.length() - 1 && term_pos != std::string::npos) | ||||||
|  | 				log_cmd_error("read failed: more than one response\n"); | ||||||
|  | 		} | ||||||
|  | 		return data; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~HandleRpcServer() { | ||||||
|  | 		CloseHandle(hsend); | ||||||
|  | 		if (hrecv != hsend) | ||||||
|  | 			CloseHandle(hrecv); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | struct FdRpcServer : RpcServer { | ||||||
|  | 	int fdsend, fdrecv; | ||||||
|  | 	pid_t pid; | ||||||
|  | 
 | ||||||
|  | 	FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1) | ||||||
|  | 		: RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { } | ||||||
|  | 
 | ||||||
|  | 	void check_pid() { | ||||||
|  | 		if (pid == -1) return; | ||||||
|  | 		// If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE.
 | ||||||
|  | 		pid_t wait_result = ::waitpid(pid, NULL, WNOHANG); | ||||||
|  | 		if (wait_result == -1) | ||||||
|  | 			log_cmd_error("waitpid failed: %s\n", strerror(errno)); | ||||||
|  | 		if (wait_result == pid) | ||||||
|  | 			log_cmd_error("RPC frontend terminated unexpectedly\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void write(const std::string &data) YS_OVERRIDE { | ||||||
|  | 		log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); | ||||||
|  | 		ssize_t offset = 0; | ||||||
|  | 		do { | ||||||
|  | 			check_pid(); | ||||||
|  | 			ssize_t result = ::write(fdsend, &data[offset], data.length() - offset); | ||||||
|  | 			if (result == -1) | ||||||
|  | 				log_cmd_error("write failed: %s\n", strerror(errno)); | ||||||
|  | 			offset += result; | ||||||
|  | 		} while(offset < (ssize_t)data.length()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::string read() YS_OVERRIDE { | ||||||
|  | 		std::string data; | ||||||
|  | 		ssize_t offset = 0; | ||||||
|  | 		while (data.length() == 0 || data[data.length() - 1] != '\n') { | ||||||
|  | 			data.resize(data.length() + 1024); | ||||||
|  | 			check_pid(); | ||||||
|  | 			ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset); | ||||||
|  | 			if (result == -1) | ||||||
|  | 				log_cmd_error("read failed: %s\n", strerror(errno)); | ||||||
|  | 			offset += result; | ||||||
|  | 			data.resize(offset); | ||||||
|  | 			size_t term_pos = data.find('\n', offset); | ||||||
|  | 			if (term_pos != data.length() - 1 && term_pos != std::string::npos) | ||||||
|  | 				log_cmd_error("read failed: more than one response\n"); | ||||||
|  | 		} | ||||||
|  | 		return data; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~FdRpcServer() { | ||||||
|  | 		close(fdsend); | ||||||
|  | 		if (fdrecv != fdsend) | ||||||
|  | 			close(fdrecv); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // RpcFrontend does not inherit from Frontend since it does not read files.
 | ||||||
|  | struct RpcFrontend : public Pass { | ||||||
|  | 	RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    connect_rpc -exec <command> [args...]\n"); | ||||||
|  | 		log("    connect_rpc -path <path>\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Load modules using an out-of-process frontend.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -exec <command> [args...]\n"); | ||||||
|  | 		log("        run <command> with arguments [args...]. send requests on stdin, read\n"); | ||||||
|  | 		log("        responses from stdout.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -path <path>\n"); | ||||||
|  | 		log("        connect to Unix domain socket at <path>. (Unix)\n"); | ||||||
|  | 		log("        connect to bidirectional byte-type named pipe at <path>. (Windows)\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("A simple JSON-based, newline-delimited protocol is used for communicating with\n"); | ||||||
|  | 		log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n"); | ||||||
|  | 		log("of JSON. Frontend responds with data or error message by replying with exactly\n"); | ||||||
|  | 		log("1 line of JSON as well.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -> {\"method\": \"modules\"}\n"); | ||||||
|  | 		log("    <- {\"modules\": [\"<module-name>\", ...]}\n"); | ||||||
|  | 		log("    <- {\"error\": \"<error-message>\"}\n"); | ||||||
|  | 		log("        request for the list of modules that can be derived by this frontend.\n"); | ||||||
|  | 		log("        the 'hierarchy' command will call back into this frontend if a cell\n"); | ||||||
|  | 		log("        with type <module-name> is instantiated in the design.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -> {\"method\": \"derive\", \"module\": \"<module-name\">, \"parameters\": {\n"); | ||||||
|  | 		log("        \"<param-name>\": {\"type\": \"[unsigned|signed|string|real]\",\n"); | ||||||
|  | 		log("                           \"value\": \"<param-value>\"}, ...}}\n"); | ||||||
|  | 		log("    <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"<source>\"}}\n"); | ||||||
|  | 		log("    <- {\"error\": \"<error-message>\"}\n"); | ||||||
|  | 		log("        request for the module <module-name> to be derived for a specific set of\n"); | ||||||
|  | 		log("        parameters. <param-name> starts with \\ for named parameters, and with $\n"); | ||||||
|  | 		log("        for unnamed parameters, which are numbered starting at 1.<param-value>\n"); | ||||||
|  | 		log("        for integer parameters is always specified as a binary string of unlimited\n"); | ||||||
|  | 		log("        precision. the <source> returned by the frontend is hygienically parsed\n"); | ||||||
|  | 		log("        by a built-in Yosys <frontend>, allowing the RPC frontend to return any\n"); | ||||||
|  | 		log("        convenient representation of the module. the derived module is cached,\n"); | ||||||
|  | 		log("        so the response should be the same whenever the same set of parameters\n"); | ||||||
|  | 		log("        is provided.\n"); | ||||||
|  | 	} | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		log_header(design, "Connecting to RPC frontend.\n"); | ||||||
|  | 
 | ||||||
|  | 		std::vector<std::string> command; | ||||||
|  | 		std::string path; | ||||||
|  | 		size_t argidx; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) { | ||||||
|  | 			std::string arg = args[argidx]; | ||||||
|  | 			if (arg == "-exec" && argidx+1 < args.size()) { | ||||||
|  | 				command.insert(command.begin(), args.begin() + argidx + 1, args.end()); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (arg == "-path" && argidx+1 < args.size()) { | ||||||
|  | 				path = args[argidx+1]; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  | 		if ((!command.empty()) + (!path.empty()) != 1) | ||||||
|  | 			log_cmd_error("Exactly one of -exec, -unix must be specified.\n"); | ||||||
|  | 
 | ||||||
|  | 		std::shared_ptr<RpcServer> server; | ||||||
|  | 		if (!command.empty()) { | ||||||
|  | 			std::string command_line; | ||||||
|  | 			bool first = true; | ||||||
|  | 			for (auto &arg : command) { | ||||||
|  | 				if (!first) command_line += ' '; | ||||||
|  | 				command_line += arg; | ||||||
|  | 				first = false; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifdef _WIN32 | ||||||
|  | 			std::wstring command_w = str2wstr(command[0]); | ||||||
|  | 			std::wstring command_path_w; | ||||||
|  | 			std::wstring command_line_w = str2wstr(command_line); | ||||||
|  | 			DWORD command_path_len_w; | ||||||
|  | 			SECURITY_ATTRIBUTES pipe_attr = {}; | ||||||
|  | 			HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL; | ||||||
|  | 			STARTUPINFOW startup_info = {}; | ||||||
|  | 			PROCESS_INFORMATION proc_info = {}; | ||||||
|  | 
 | ||||||
|  | 			command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL); | ||||||
|  | 			if (command_path_len_w == 0) { | ||||||
|  | 				log_error("SearchPathW failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			command_path_w.resize(command_path_len_w - 1); | ||||||
|  | 			command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL); | ||||||
|  | 			log_assert(command_path_len_w == command_path_w.length()); | ||||||
|  | 
 | ||||||
|  | 			pipe_attr.nLength = sizeof(pipe_attr); | ||||||
|  | 			pipe_attr.bInheritHandle = TRUE; | ||||||
|  | 			pipe_attr.lpSecurityDescriptor = NULL; | ||||||
|  | 			if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) { | ||||||
|  | 				log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) { | ||||||
|  | 				log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) { | ||||||
|  | 				log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) { | ||||||
|  | 				log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			startup_info.cb = sizeof(startup_info); | ||||||
|  | 			startup_info.hStdInput = send_r; | ||||||
|  | 			startup_info.hStdOutput = recv_w; | ||||||
|  | 			startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); | ||||||
|  | 			startup_info.dwFlags |= STARTF_USESTDHANDLES; | ||||||
|  | 			if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) { | ||||||
|  | 				log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			CloseHandle(proc_info.hProcess); | ||||||
|  | 			CloseHandle(proc_info.hThread); | ||||||
|  | 
 | ||||||
|  | 			server = std::make_shared<HandleRpcServer>(path, send_w, recv_r); | ||||||
|  | 			send_w = NULL; | ||||||
|  | 			recv_r = NULL; | ||||||
|  | 
 | ||||||
|  | cleanup_exec: | ||||||
|  | 			if (send_r != NULL) CloseHandle(send_r); | ||||||
|  | 			if (send_w != NULL) CloseHandle(send_w); | ||||||
|  | 			if (recv_r != NULL) CloseHandle(recv_r); | ||||||
|  | 			if (recv_w != NULL) CloseHandle(recv_w); | ||||||
|  | #else | ||||||
|  | 			std::vector<char *> argv; | ||||||
|  | 			int send[2] = {-1,-1}, recv[2] = {-1,-1}; | ||||||
|  | 			posix_spawn_file_actions_t file_actions, *file_actions_p = NULL; | ||||||
|  | 			pid_t pid; | ||||||
|  | 
 | ||||||
|  | 			for (auto &arg : command) | ||||||
|  | 				argv.push_back(&arg[0]); | ||||||
|  | 			argv.push_back(nullptr); | ||||||
|  | 
 | ||||||
|  | 			if (pipe(send) != 0) { | ||||||
|  | 				log_error("pipe failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (pipe(recv) != 0) { | ||||||
|  | 				log_error("pipe failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (posix_spawn_file_actions_init(&file_actions) != 0) { | ||||||
|  | 				log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			file_actions_p = &file_actions; | ||||||
|  | 			if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) { | ||||||
|  | 				log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) { | ||||||
|  | 				log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) { | ||||||
|  | 				log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 			if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) { | ||||||
|  | 				log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) { | ||||||
|  | 				log_error("posix_spawnp failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_exec; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			server = std::make_shared<FdRpcServer>(command_line, send[1], recv[0], pid); | ||||||
|  | 			send[1] = -1; | ||||||
|  | 			recv[0] = -1; | ||||||
|  | 
 | ||||||
|  | cleanup_exec: | ||||||
|  | 			if (send[0] != -1) close(send[0]); | ||||||
|  | 			if (send[1] != -1) close(send[1]); | ||||||
|  | 			if (recv[0] != -1) close(recv[0]); | ||||||
|  | 			if (recv[1] != -1) close(recv[1]); | ||||||
|  | 			if (file_actions_p != NULL) | ||||||
|  | 				posix_spawn_file_actions_destroy(file_actions_p); | ||||||
|  | #endif | ||||||
|  | 		} else if (!path.empty()) { | ||||||
|  | #ifdef _WIN32 | ||||||
|  | 			std::wstring path_w = str2wstr(path); | ||||||
|  | 			HANDLE h; | ||||||
|  | 
 | ||||||
|  | 			h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL); | ||||||
|  | 			if (h == INVALID_HANDLE_VALUE) { | ||||||
|  | 				log_error("CreateFileW failed: %s\n", get_last_error_str().c_str()); | ||||||
|  | 				goto cleanup_path; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			server = std::make_shared<HandleRpcServer>(path, h, h); | ||||||
|  | 
 | ||||||
|  | cleanup_path: | ||||||
|  | 			; | ||||||
|  | #else | ||||||
|  | 			struct sockaddr_un addr; | ||||||
|  | 			addr.sun_family = AF_UNIX; | ||||||
|  | 			strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); | ||||||
|  | 
 | ||||||
|  | 			int fd = socket(AF_UNIX, SOCK_STREAM, 0); | ||||||
|  | 			if (fd == -1) { | ||||||
|  | 				log_error("socket failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_path; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { | ||||||
|  | 				log_error("connect failed: %s\n", strerror(errno)); | ||||||
|  | 				goto cleanup_path; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			server = std::make_shared<FdRpcServer>(path, fd, fd); | ||||||
|  | 			fd = -1; | ||||||
|  | 
 | ||||||
|  | cleanup_path: | ||||||
|  | 			if (fd != -1) close(fd); | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!server) | ||||||
|  | 			log_cmd_error("Failed to connect to RPC frontend.\n"); | ||||||
|  | 
 | ||||||
|  | 		for (auto &module_name : server->get_module_names()) { | ||||||
|  | 			log("Linking module `%s'.\n", module_name.c_str()); | ||||||
|  | 			RpcModule *module = new RpcModule; | ||||||
|  | 			module->name = "$abstract\\" + module_name; | ||||||
|  | 			module->server = server; | ||||||
|  | 			design->add(module); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } RpcFrontend; | ||||||
|  | 
 | ||||||
|  | YOSYS_NAMESPACE_END | ||||||
|  | @ -85,10 +85,8 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le | ||||||
| 			digits.push_back(10 + *str - 'A'); | 			digits.push_back(10 + *str - 'A'); | ||||||
| 		else if (*str == 'x' || *str == 'X') | 		else if (*str == 'x' || *str == 'X') | ||||||
| 			digits.push_back(0xf0); | 			digits.push_back(0xf0); | ||||||
| 		else if (*str == 'z' || *str == 'Z') | 		else if (*str == 'z' || *str == 'Z' || *str == '?') | ||||||
| 			digits.push_back(0xf1); | 			digits.push_back(0xf1); | ||||||
| 		else if (*str == '?') |  | ||||||
| 			digits.push_back(0xf2); |  | ||||||
| 		str++; | 		str++; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -112,8 +110,6 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le | ||||||
| 					data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); | 					data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); | ||||||
| 				else if (*it == 0xf1) | 				else if (*it == 0xf1) | ||||||
| 					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); | 					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); | ||||||
| 				else if (*it == 0xf2) |  | ||||||
| 					data.push_back(RTLIL::Sa); |  | ||||||
| 				else | 				else | ||||||
| 					data.push_back((*it & bitmask) ? State::S1 : State::S0); | 					data.push_back((*it & bitmask) ? State::S1 : State::S0); | ||||||
| 			} | 			} | ||||||
|  | @ -199,13 +195,13 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn | ||||||
| 	if (str == endptr) | 	if (str == endptr) | ||||||
| 		len_in_bits = -1; | 		len_in_bits = -1; | ||||||
| 
 | 
 | ||||||
| 	// The "<bits>'s?[bodhBODH]<digits>" syntax
 | 	// The "<bits>'[sS]?[bodhBODH]<digits>" syntax
 | ||||||
| 	if (*endptr == '\'') | 	if (*endptr == '\'') | ||||||
| 	{ | 	{ | ||||||
| 		std::vector<RTLIL::State> data; | 		std::vector<RTLIL::State> data; | ||||||
| 		bool is_signed = false; | 		bool is_signed = false; | ||||||
| 		bool is_unsized = len_in_bits < 0; | 		bool is_unsized = len_in_bits < 0; | ||||||
| 		if (*(endptr+1) == 's') { | 		if (*(endptr+1) == 's' || *(endptr+1) == 'S') { | ||||||
| 			is_signed = true; | 			is_signed = true; | ||||||
| 			endptr++; | 			endptr++; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -239,7 +239,7 @@ YOSYS_NAMESPACE_END | ||||||
| 	return TOK_CONSTVAL; | 	return TOK_CONSTVAL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| [0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ { | [0-9]*[ \t]*\'[sS]?[bodhBODH]?[ \t\r\n]*[0-9a-fA-FzxZX?_]+ { | ||||||
| 	frontend_verilog_yylval.string = new std::string(yytext); | 	frontend_verilog_yylval.string = new std::string(yytext); | ||||||
| 	return TOK_CONSTVAL; | 	return TOK_CONSTVAL; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -155,7 +155,7 @@ struct specify_rise_fall { | ||||||
| 
 | 
 | ||||||
| %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int | %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int | ||||||
| %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list | %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list | ||||||
| %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id | %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id | ||||||
| %type <boolean> opt_signed opt_property unique_case_attr | %type <boolean> opt_signed opt_property unique_case_attr | ||||||
| %type <al> attr case_attr | %type <al> attr case_attr | ||||||
| 
 | 
 | ||||||
|  | @ -206,6 +206,7 @@ design: | ||||||
| 	task_func_decl design | | 	task_func_decl design | | ||||||
| 	param_decl design | | 	param_decl design | | ||||||
| 	localparam_decl design | | 	localparam_decl design | | ||||||
|  | 	typedef_decl design | | ||||||
| 	package design | | 	package design | | ||||||
| 	interface design | | 	interface design | | ||||||
| 	/* empty */; | 	/* empty */; | ||||||
|  | @ -290,6 +291,9 @@ hierarchical_id: | ||||||
| 		$$ = $1; | 		$$ = $1; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | hierarchical_type_id: | ||||||
|  | 	'(' hierarchical_id ')' { $$ = $2; }; | ||||||
|  | 
 | ||||||
| module: | module: | ||||||
| 	attr TOK_MODULE TOK_ID { | 	attr TOK_MODULE TOK_ID { | ||||||
| 		do_not_require_port_stubs = false; | 		do_not_require_port_stubs = false; | ||||||
|  | @ -324,13 +328,13 @@ single_module_para: | ||||||
| 		astbuf1 = new AstNode(AST_PARAMETER); | 		astbuf1 = new AstNode(AST_PARAMETER); | ||||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
| 		append_attr(astbuf1, $1); | 		append_attr(astbuf1, $1); | ||||||
| 	} param_signed param_integer param_range single_param_decl | | 	} param_type single_param_decl | | ||||||
| 	attr TOK_LOCALPARAM { | 	attr TOK_LOCALPARAM { | ||||||
| 		if (astbuf1) delete astbuf1; | 		if (astbuf1) delete astbuf1; | ||||||
| 		astbuf1 = new AstNode(AST_LOCALPARAM); | 		astbuf1 = new AstNode(AST_LOCALPARAM); | ||||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
| 		append_attr(astbuf1, $1); | 		append_attr(astbuf1, $1); | ||||||
| 	} param_signed param_integer param_range single_param_decl | | 	} param_type single_param_decl | | ||||||
| 	single_param_decl; | 	single_param_decl; | ||||||
| 
 | 
 | ||||||
| module_args_opt: | module_args_opt: | ||||||
|  | @ -426,6 +430,7 @@ package_body: | ||||||
| 	package_body package_body_stmt |; | 	package_body package_body_stmt |; | ||||||
| 
 | 
 | ||||||
| package_body_stmt: | package_body_stmt: | ||||||
|  | 	typedef_decl | | ||||||
| 	localparam_decl; | 	localparam_decl; | ||||||
| 
 | 
 | ||||||
| interface: | interface: | ||||||
|  | @ -452,7 +457,7 @@ interface_body: | ||||||
| 	interface_body interface_body_stmt |; | 	interface_body interface_body_stmt |; | ||||||
| 
 | 
 | ||||||
| interface_body_stmt: | interface_body_stmt: | ||||||
| 	param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | | 	param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | | ||||||
| 	modport_stmt; | 	modport_stmt; | ||||||
| 
 | 
 | ||||||
| non_opt_delay: | non_opt_delay: | ||||||
|  | @ -475,8 +480,14 @@ wire_type: | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| wire_type_token_list: | wire_type_token_list: | ||||||
| 	wire_type_token | wire_type_token_list wire_type_token | | 	wire_type_token | | ||||||
| 	wire_type_token_io ; | 	wire_type_token_list wire_type_token | | ||||||
|  | 	wire_type_token_io | | ||||||
|  | 	hierarchical_type_id { | ||||||
|  | 		astbuf3->is_custom_type = true; | ||||||
|  | 		astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); | ||||||
|  | 		astbuf3->children.back()->str = *$1; | ||||||
|  | 	}; | ||||||
| 
 | 
 | ||||||
| wire_type_token_io: | wire_type_token_io: | ||||||
| 	TOK_INPUT { | 	TOK_INPUT { | ||||||
|  | @ -591,7 +602,7 @@ module_body: | ||||||
| 	/* empty */; | 	/* empty */; | ||||||
| 
 | 
 | ||||||
| module_body_stmt: | module_body_stmt: | ||||||
| 	task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | | 	task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | | ||||||
| 	always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; | 	always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; | ||||||
| 
 | 
 | ||||||
| checker_decl: | checker_decl: | ||||||
|  | @ -1149,12 +1160,20 @@ param_range: | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | param_type: | ||||||
|  | 	param_signed param_integer param_real param_range | | ||||||
|  | 	hierarchical_type_id { | ||||||
|  | 		astbuf1->is_custom_type = true; | ||||||
|  | 		astbuf1->children.push_back(new AstNode(AST_WIRETYPE)); | ||||||
|  | 		astbuf1->children.back()->str = *$1; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| param_decl: | param_decl: | ||||||
| 	attr TOK_PARAMETER { | 	attr TOK_PARAMETER { | ||||||
| 		astbuf1 = new AstNode(AST_PARAMETER); | 		astbuf1 = new AstNode(AST_PARAMETER); | ||||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
| 		append_attr(astbuf1, $1); | 		append_attr(astbuf1, $1); | ||||||
| 	} param_signed param_integer param_real param_range param_decl_list ';' { | 	} param_type param_decl_list ';' { | ||||||
| 		delete astbuf1; | 		delete astbuf1; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | @ -1163,7 +1182,7 @@ localparam_decl: | ||||||
| 		astbuf1 = new AstNode(AST_LOCALPARAM); | 		astbuf1 = new AstNode(AST_LOCALPARAM); | ||||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
| 		append_attr(astbuf1, $1); | 		append_attr(astbuf1, $1); | ||||||
| 	} param_signed param_integer param_real param_range param_decl_list ';' { | 	} param_type param_decl_list ';' { | ||||||
| 		delete astbuf1; | 		delete astbuf1; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | @ -1327,7 +1346,7 @@ wire_name: | ||||||
| 		if ($2 != NULL) { | 		if ($2 != NULL) { | ||||||
| 			if (node->is_input || node->is_output) | 			if (node->is_input || node->is_output) | ||||||
| 				frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); | 				frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); | ||||||
| 			if (!astbuf2) { | 			if (!astbuf2 && !node->is_custom_type) { | ||||||
| 				AstNode *rng = new AstNode(AST_RANGE); | 				AstNode *rng = new AstNode(AST_RANGE); | ||||||
| 				rng->children.push_back(AstNode::mkconst_int(0, true)); | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
| 				rng->children.push_back(AstNode::mkconst_int(0, true)); | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
|  | @ -1377,6 +1396,45 @@ assign_expr: | ||||||
| 		ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); | 		ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | typedef_decl: | ||||||
|  | 	TOK_TYPEDEF wire_type range TOK_ID range_or_multirange ';' { | ||||||
|  | 		astbuf1 = $2; | ||||||
|  | 		astbuf2 = $3; | ||||||
|  | 		if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { | ||||||
|  | 			if (astbuf2) { | ||||||
|  | 				frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); | ||||||
|  | 			} else { | ||||||
|  | 				astbuf2 = new AstNode(AST_RANGE); | ||||||
|  | 				astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); | ||||||
|  | 				astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (astbuf2 && astbuf2->children.size() != 2) | ||||||
|  | 			frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); | ||||||
|  | 		if (astbuf2) | ||||||
|  | 			astbuf1->children.push_back(astbuf2); | ||||||
|  | 
 | ||||||
|  | 		if ($5 != NULL) { | ||||||
|  | 			if (!astbuf2) { | ||||||
|  | 				AstNode *rng = new AstNode(AST_RANGE); | ||||||
|  | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
|  | 				rng->children.push_back(AstNode::mkconst_int(0, true)); | ||||||
|  | 				astbuf1->children.push_back(rng); | ||||||
|  | 			} | ||||||
|  | 			astbuf1->type = AST_MEMORY; | ||||||
|  | 			auto *rangeNode = $5; | ||||||
|  | 			if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { | ||||||
|  | 				// SV array size [n], rewrite as [n-1:0] | ||||||
|  | 				rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); | ||||||
|  | 				rangeNode->children.push_back(AstNode::mkconst_int(0, false)); | ||||||
|  | 			} | ||||||
|  | 			astbuf1->children.push_back(rangeNode); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1)); | ||||||
|  | 		ast_stack.back()->children.back()->str = *$4; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| cell_stmt: | cell_stmt: | ||||||
| 	attr TOK_ID { | 	attr TOK_ID { | ||||||
| 		astbuf1 = new AstNode(AST_CELL); | 		astbuf1 = new AstNode(AST_CELL); | ||||||
|  | @ -1823,7 +1881,7 @@ simple_behavioral_stmt: | ||||||
| 
 | 
 | ||||||
| // this production creates the obligatory if-else shift/reduce conflict | // this production creates the obligatory if-else shift/reduce conflict | ||||||
| behavioral_stmt: | behavioral_stmt: | ||||||
| 	defattr | assert | wire_decl | param_decl | localparam_decl | | 	defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl | | ||||||
| 	non_opt_delay behavioral_stmt | | 	non_opt_delay behavioral_stmt | | ||||||
| 	simple_behavioral_stmt ';' | ';' | | 	simple_behavioral_stmt ';' | ';' | | ||||||
| 	hierarchical_id attr { | 	hierarchical_id attr { | ||||||
|  |  | ||||||
|  | @ -551,6 +551,10 @@ void log_dump_val_worker(RTLIL::SigSpec v) { | ||||||
| 	log("%s", log_signal(v)); | 	log("%s", log_signal(v)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void log_dump_val_worker(RTLIL::State v) { | ||||||
|  | 	log("%s", log_signal(v)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const char *log_signal(const RTLIL::SigSpec &sig, bool autoint) | const char *log_signal(const RTLIL::SigSpec &sig, bool autoint) | ||||||
| { | { | ||||||
| 	std::stringstream buf; | 	std::stringstream buf; | ||||||
|  |  | ||||||
|  | @ -292,6 +292,7 @@ static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p | ||||||
| static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); } | static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); } | ||||||
| void log_dump_val_worker(RTLIL::IdString v); | void log_dump_val_worker(RTLIL::IdString v); | ||||||
| void log_dump_val_worker(RTLIL::SigSpec v); | void log_dump_val_worker(RTLIL::SigSpec v); | ||||||
|  | void log_dump_val_worker(RTLIL::State v); | ||||||
| 
 | 
 | ||||||
| template<typename K, typename T, typename OPS> | template<typename K, typename T, typename OPS> | ||||||
| static inline void log_dump_val_worker(dict<K, T, OPS> &v) { | static inline void log_dump_val_worker(dict<K, T, OPS> &v) { | ||||||
|  |  | ||||||
|  | @ -48,7 +48,7 @@ using zlib to write gzip-compressed data every time the stream is flushed. | ||||||
| */ | */ | ||||||
| class gzip_ostream : public std::ostream  { | class gzip_ostream : public std::ostream  { | ||||||
| public: | public: | ||||||
| 	gzip_ostream() | 	gzip_ostream() : std::ostream(nullptr) | ||||||
| 	{ | 	{ | ||||||
| 		rdbuf(&outbuf); | 		rdbuf(&outbuf); | ||||||
| 	} | 	} | ||||||
|  | @ -71,7 +71,7 @@ private: | ||||||
| 			str(""); | 			str(""); | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
| 		~gzip_streambuf() | 		virtual ~gzip_streambuf() | ||||||
| 		{ | 		{ | ||||||
| 			sync(); | 			sync(); | ||||||
| 			gzclose(gzf); | 			gzclose(gzf); | ||||||
|  | @ -439,7 +439,7 @@ void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design) | ||||||
| FILE *Frontend::current_script_file = NULL; | FILE *Frontend::current_script_file = NULL; | ||||||
| std::string Frontend::last_here_document; | std::string Frontend::last_here_document; | ||||||
| 
 | 
 | ||||||
| void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx) | void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input) | ||||||
| { | { | ||||||
| 	bool called_with_fp = f != NULL; | 	bool called_with_fp = f != NULL; | ||||||
| 
 | 
 | ||||||
|  | @ -489,7 +489,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s | ||||||
| 				next_args.insert(next_args.end(), filenames.begin()+1, filenames.end()); | 				next_args.insert(next_args.end(), filenames.begin()+1, filenames.end()); | ||||||
| 			} | 			} | ||||||
| 			std::ifstream *ff = new std::ifstream; | 			std::ifstream *ff = new std::ifstream; | ||||||
| 			ff->open(filename.c_str()); | 			ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in); | ||||||
| 			yosys_input_files.insert(filename); | 			yosys_input_files.insert(filename); | ||||||
| 			if (ff->fail()) | 			if (ff->fail()) | ||||||
| 				delete ff; | 				delete ff; | ||||||
|  | @ -498,7 +498,15 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s | ||||||
| 			if (f != NULL) { | 			if (f != NULL) { | ||||||
| 				// Check for gzip magic
 | 				// Check for gzip magic
 | ||||||
| 				unsigned char magic[3]; | 				unsigned char magic[3]; | ||||||
| 				int n = readsome(*ff, reinterpret_cast<char*>(magic), 3); | 				int n = 0; | ||||||
|  | 				while (n < 3) | ||||||
|  | 				{ | ||||||
|  | 					int c = ff->get(); | ||||||
|  | 					if (c != EOF) { | ||||||
|  | 						magic[n] = (unsigned char) c; | ||||||
|  | 					} | ||||||
|  | 					n++; | ||||||
|  | 				} | ||||||
| 				if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) { | 				if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) { | ||||||
| 	#ifdef YOSYS_ENABLE_ZLIB | 	#ifdef YOSYS_ENABLE_ZLIB | ||||||
| 					log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str()); | 					log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str()); | ||||||
|  | @ -604,7 +612,7 @@ void Backend::execute(std::vector<std::string> args, RTLIL::Design *design) | ||||||
| 		delete f; | 		delete f; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx) | void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output) | ||||||
| { | { | ||||||
| 	bool called_with_fp = f != NULL; | 	bool called_with_fp = f != NULL; | ||||||
| 
 | 
 | ||||||
|  | @ -639,7 +647,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st | ||||||
| #endif | #endif | ||||||
| 		} else { | 		} else { | ||||||
| 			std::ofstream *ff = new std::ofstream; | 			std::ofstream *ff = new std::ofstream; | ||||||
| 			ff->open(filename.c_str(), std::ofstream::trunc); | 			ff->open(filename.c_str(), bin_output ? (std::ofstream::trunc | std::ofstream::binary) : std::ofstream::trunc); | ||||||
| 			yosys_output_files.insert(filename); | 			yosys_output_files.insert(filename); | ||||||
| 			if (ff->fail()) { | 			if (ff->fail()) { | ||||||
| 				delete ff; | 				delete ff; | ||||||
|  |  | ||||||
|  | @ -94,7 +94,7 @@ struct Frontend : Pass | ||||||
| 	virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0; | 	virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0; | ||||||
| 
 | 
 | ||||||
| 	static std::vector<std::string> next_args; | 	static std::vector<std::string> next_args; | ||||||
| 	void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx); | 	void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input = false); | ||||||
| 
 | 
 | ||||||
| 	static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command); | 	static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command); | ||||||
| 	static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args); | 	static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args); | ||||||
|  | @ -109,7 +109,7 @@ struct Backend : Pass | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; | ||||||
| 	virtual void execute(std::ostream *&f, std::string filename,  std::vector<std::string> args, RTLIL::Design *design) = 0; | 	virtual void execute(std::ostream *&f, std::string filename,  std::vector<std::string> args, RTLIL::Design *design) = 0; | ||||||
| 
 | 
 | ||||||
| 	void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx); | 	void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output = false); | ||||||
| 
 | 
 | ||||||
| 	static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command); | 	static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command); | ||||||
| 	static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args); | 	static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args); | ||||||
|  |  | ||||||
|  | @ -1528,7 +1528,7 @@ std::vector<RTLIL::Wire*> RTLIL::Module::selected_wires() const | ||||||
| std::vector<RTLIL::Cell*> RTLIL::Module::selected_cells() const | std::vector<RTLIL::Cell*> RTLIL::Module::selected_cells() const | ||||||
| { | { | ||||||
| 	std::vector<RTLIL::Cell*> result; | 	std::vector<RTLIL::Cell*> result; | ||||||
| 	result.reserve(wires_.size()); | 	result.reserve(cells_.size()); | ||||||
| 	for (auto &it : cells_) | 	for (auto &it : cells_) | ||||||
| 		if (design->selected(this, it.second)) | 		if (design->selected(this, it.second)) | ||||||
| 			result.push_back(it.second); | 			result.push_back(it.second); | ||||||
|  | @ -3083,6 +3083,7 @@ void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules, RT | ||||||
| 	log_assert(other != NULL); | 	log_assert(other != NULL); | ||||||
| 	log_assert(width_ == other->width_); | 	log_assert(width_ == other->width_); | ||||||
| 
 | 
 | ||||||
|  | 	if (rules.empty()) return; | ||||||
| 	unpack(); | 	unpack(); | ||||||
| 	other->unpack(); | 	other->unpack(); | ||||||
| 
 | 
 | ||||||
|  | @ -3107,6 +3108,7 @@ void RTLIL::SigSpec::replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules | ||||||
| 	log_assert(other != NULL); | 	log_assert(other != NULL); | ||||||
| 	log_assert(width_ == other->width_); | 	log_assert(width_ == other->width_); | ||||||
| 
 | 
 | ||||||
|  | 	if (rules.empty()) return; | ||||||
| 	unpack(); | 	unpack(); | ||||||
| 	other->unpack(); | 	other->unpack(); | ||||||
| 
 | 
 | ||||||
|  | @ -3552,6 +3554,12 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const | ||||||
| 	if (width_ != other.width_) | 	if (width_ != other.width_) | ||||||
| 		return false; | 		return false; | ||||||
| 
 | 
 | ||||||
|  | 	// Without this, SigSpec() == SigSpec(State::S0, 0) will fail
 | ||||||
|  | 	//   since the RHS will contain one SigChunk of width 0 causing
 | ||||||
|  | 	//   the size check below to fail
 | ||||||
|  | 	if (width_ == 0) | ||||||
|  | 		return true; | ||||||
|  | 
 | ||||||
| 	pack(); | 	pack(); | ||||||
| 	other.pack(); | 	other.pack(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -609,8 +609,11 @@ struct RTLIL::Const | ||||||
| 	std::string decode_string() const; | 	std::string decode_string() const; | ||||||
| 
 | 
 | ||||||
| 	inline int size() const { return bits.size(); } | 	inline int size() const { return bits.size(); } | ||||||
|  | 	inline bool empty() const { return bits.empty(); } | ||||||
| 	inline RTLIL::State &operator[](int index) { return bits.at(index); } | 	inline RTLIL::State &operator[](int index) { return bits.at(index); } | ||||||
| 	inline const RTLIL::State &operator[](int index) const { return bits.at(index); } | 	inline const RTLIL::State &operator[](int index) const { return bits.at(index); } | ||||||
|  | 	inline decltype(bits)::iterator begin() { return bits.begin(); } | ||||||
|  | 	inline decltype(bits)::iterator end() { return bits.end(); } | ||||||
| 
 | 
 | ||||||
| 	bool is_fully_zero() const; | 	bool is_fully_zero() const; | ||||||
| 	bool is_fully_ones() const; | 	bool is_fully_ones() const; | ||||||
|  |  | ||||||
|  | @ -135,9 +135,11 @@ struct SigPool | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T, class Compare = std::less<T>> | template <typename T, class Compare = void> | ||||||
| struct SigSet | struct SigSet | ||||||
| { | { | ||||||
|  | 	static_assert(!std::is_same<Compare,void>::value, "Default value for `Compare' class not found for SigSet<T>. Please specify."); | ||||||
|  | 
 | ||||||
| 	struct bitDef_t : public std::pair<RTLIL::Wire*, int> { | 	struct bitDef_t : public std::pair<RTLIL::Wire*, int> { | ||||||
| 		bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } | 		bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } | ||||||
| 		bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } | 		bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } | ||||||
|  | @ -220,6 +222,13 @@ struct SigSet | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | template<typename T> | ||||||
|  | class SigSet<T, typename std::enable_if<!std::is_pointer<T>::value>::type> : public SigSet<T, std::less<T>> {}; | ||||||
|  | template<typename T> | ||||||
|  | using sort_by_name_id_guard = typename std::enable_if<std::is_same<T,RTLIL::Cell*>::value>::type; | ||||||
|  | template<typename T> | ||||||
|  | class SigSet<T, sort_by_name_id_guard<T>> : public SigSet<T, RTLIL::sort_by_name_id<typename std::remove_pointer<T>::type>> {}; | ||||||
|  | 
 | ||||||
| struct SigMap | struct SigMap | ||||||
| { | { | ||||||
| 	mfp<SigBit> database; | 	mfp<SigBit> database; | ||||||
|  |  | ||||||
|  | @ -210,6 +210,7 @@ namespace RTLIL { | ||||||
| 	struct Module; | 	struct Module; | ||||||
| 	struct Design; | 	struct Design; | ||||||
| 	struct Monitor; | 	struct Monitor; | ||||||
|  | 	enum State : unsigned char; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace AST { | namespace AST { | ||||||
|  |  | ||||||
							
								
								
									
										788
									
								
								libs/json11/json11.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										788
									
								
								libs/json11/json11.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,788 @@ | ||||||
|  | /* Copyright (c) 2013 Dropbox, Inc.
 | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "json11.hpp" | ||||||
|  | #include <cassert> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <cstdio> | ||||||
|  | #include <limits> | ||||||
|  | 
 | ||||||
|  | namespace json11 { | ||||||
|  | 
 | ||||||
|  | static const int max_depth = 200; | ||||||
|  | 
 | ||||||
|  | using std::string; | ||||||
|  | using std::vector; | ||||||
|  | using std::map; | ||||||
|  | using std::make_shared; | ||||||
|  | using std::initializer_list; | ||||||
|  | using std::move; | ||||||
|  | 
 | ||||||
|  | /* Helper for representing null - just a do-nothing struct, plus comparison
 | ||||||
|  |  * operators so the helpers in JsonValue work. We can't use nullptr_t because | ||||||
|  |  * it may not be orderable. | ||||||
|  |  */ | ||||||
|  | struct NullStruct { | ||||||
|  |     bool operator==(NullStruct) const { return true; } | ||||||
|  |     bool operator<(NullStruct) const { return false; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Serialization | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static void dump(NullStruct, string &out) { | ||||||
|  |     out += "null"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(double value, string &out) { | ||||||
|  |     if (std::isfinite(value)) { | ||||||
|  |         char buf[32]; | ||||||
|  |         snprintf(buf, sizeof buf, "%.17g", value); | ||||||
|  |         out += buf; | ||||||
|  |     } else { | ||||||
|  |         out += "null"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(int value, string &out) { | ||||||
|  |     char buf[32]; | ||||||
|  |     snprintf(buf, sizeof buf, "%d", value); | ||||||
|  |     out += buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(bool value, string &out) { | ||||||
|  |     out += value ? "true" : "false"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(const string &value, string &out) { | ||||||
|  |     out += '"'; | ||||||
|  |     for (size_t i = 0; i < value.length(); i++) { | ||||||
|  |         const char ch = value[i]; | ||||||
|  |         if (ch == '\\') { | ||||||
|  |             out += "\\\\"; | ||||||
|  |         } else if (ch == '"') { | ||||||
|  |             out += "\\\""; | ||||||
|  |         } else if (ch == '\b') { | ||||||
|  |             out += "\\b"; | ||||||
|  |         } else if (ch == '\f') { | ||||||
|  |             out += "\\f"; | ||||||
|  |         } else if (ch == '\n') { | ||||||
|  |             out += "\\n"; | ||||||
|  |         } else if (ch == '\r') { | ||||||
|  |             out += "\\r"; | ||||||
|  |         } else if (ch == '\t') { | ||||||
|  |             out += "\\t"; | ||||||
|  |         } else if (static_cast<uint8_t>(ch) <= 0x1f) { | ||||||
|  |             char buf[8]; | ||||||
|  |             snprintf(buf, sizeof buf, "\\u%04x", ch); | ||||||
|  |             out += buf; | ||||||
|  |         } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80 | ||||||
|  |                    && static_cast<uint8_t>(value[i+2]) == 0xa8) { | ||||||
|  |             out += "\\u2028"; | ||||||
|  |             i += 2; | ||||||
|  |         } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80 | ||||||
|  |                    && static_cast<uint8_t>(value[i+2]) == 0xa9) { | ||||||
|  |             out += "\\u2029"; | ||||||
|  |             i += 2; | ||||||
|  |         } else { | ||||||
|  |             out += ch; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     out += '"'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(const Json::array &values, string &out) { | ||||||
|  |     bool first = true; | ||||||
|  |     out += "["; | ||||||
|  |     for (const auto &value : values) { | ||||||
|  |         if (!first) | ||||||
|  |             out += ", "; | ||||||
|  |         value.dump(out); | ||||||
|  |         first = false; | ||||||
|  |     } | ||||||
|  |     out += "]"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump(const Json::object &values, string &out) { | ||||||
|  |     bool first = true; | ||||||
|  |     out += "{"; | ||||||
|  |     for (const auto &kv : values) { | ||||||
|  |         if (!first) | ||||||
|  |             out += ", "; | ||||||
|  |         dump(kv.first, out); | ||||||
|  |         out += ": "; | ||||||
|  |         kv.second.dump(out); | ||||||
|  |         first = false; | ||||||
|  |     } | ||||||
|  |     out += "}"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Json::dump(string &out) const { | ||||||
|  |     m_ptr->dump(out); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Value wrappers | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | template <Json::Type tag, typename T> | ||||||
|  | class Value : public JsonValue { | ||||||
|  | protected: | ||||||
|  | 
 | ||||||
|  |     // Constructors
 | ||||||
|  |     explicit Value(const T &value) : m_value(value) {} | ||||||
|  |     explicit Value(T &&value)      : m_value(move(value)) {} | ||||||
|  | 
 | ||||||
|  |     // Get type tag
 | ||||||
|  |     Json::Type type() const override { | ||||||
|  |         return tag; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Comparisons
 | ||||||
|  |     bool equals(const JsonValue * other) const override { | ||||||
|  |         return m_value == static_cast<const Value<tag, T> *>(other)->m_value; | ||||||
|  |     } | ||||||
|  |     bool less(const JsonValue * other) const override { | ||||||
|  |         return m_value < static_cast<const Value<tag, T> *>(other)->m_value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const T m_value; | ||||||
|  |     void dump(string &out) const override { json11::dump(m_value, out); } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonDouble final : public Value<Json::NUMBER, double> { | ||||||
|  |     double number_value() const override { return m_value; } | ||||||
|  |     int int_value() const override { return static_cast<int>(m_value); } | ||||||
|  |     bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } | ||||||
|  |     bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); } | ||||||
|  | public: | ||||||
|  |     explicit JsonDouble(double value) : Value(value) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonInt final : public Value<Json::NUMBER, int> { | ||||||
|  |     double number_value() const override { return m_value; } | ||||||
|  |     int int_value() const override { return m_value; } | ||||||
|  |     bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } | ||||||
|  |     bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); } | ||||||
|  | public: | ||||||
|  |     explicit JsonInt(int value) : Value(value) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonBoolean final : public Value<Json::BOOL, bool> { | ||||||
|  |     bool bool_value() const override { return m_value; } | ||||||
|  | public: | ||||||
|  |     explicit JsonBoolean(bool value) : Value(value) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonString final : public Value<Json::STRING, string> { | ||||||
|  |     const string &string_value() const override { return m_value; } | ||||||
|  | public: | ||||||
|  |     explicit JsonString(const string &value) : Value(value) {} | ||||||
|  |     explicit JsonString(string &&value)      : Value(move(value)) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonArray final : public Value<Json::ARRAY, Json::array> { | ||||||
|  |     const Json::array &array_items() const override { return m_value; } | ||||||
|  |     const Json & operator[](size_t i) const override; | ||||||
|  | public: | ||||||
|  |     explicit JsonArray(const Json::array &value) : Value(value) {} | ||||||
|  |     explicit JsonArray(Json::array &&value)      : Value(move(value)) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonObject final : public Value<Json::OBJECT, Json::object> { | ||||||
|  |     const Json::object &object_items() const override { return m_value; } | ||||||
|  |     const Json & operator[](const string &key) const override; | ||||||
|  | public: | ||||||
|  |     explicit JsonObject(const Json::object &value) : Value(value) {} | ||||||
|  |     explicit JsonObject(Json::object &&value)      : Value(move(value)) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonNull final : public Value<Json::NUL, NullStruct> { | ||||||
|  | public: | ||||||
|  |     JsonNull() : Value({}) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Static globals - static-init-safe | ||||||
|  |  */ | ||||||
|  | struct Statics { | ||||||
|  |     const std::shared_ptr<JsonValue> null = make_shared<JsonNull>(); | ||||||
|  |     const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true); | ||||||
|  |     const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false); | ||||||
|  |     const string empty_string; | ||||||
|  |     const vector<Json> empty_vector; | ||||||
|  |     const map<string, Json> empty_map; | ||||||
|  |     Statics() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const Statics & statics() { | ||||||
|  |     static const Statics s {}; | ||||||
|  |     return s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const Json & static_null() { | ||||||
|  |     // This has to be separate, not in Statics, because Json() accesses statics().null.
 | ||||||
|  |     static const Json json_null; | ||||||
|  |     return json_null; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Constructors | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | Json::Json() noexcept                  : m_ptr(statics().null) {} | ||||||
|  | Json::Json(std::nullptr_t) noexcept    : m_ptr(statics().null) {} | ||||||
|  | Json::Json(double value)               : m_ptr(make_shared<JsonDouble>(value)) {} | ||||||
|  | Json::Json(int value)                  : m_ptr(make_shared<JsonInt>(value)) {} | ||||||
|  | Json::Json(bool value)                 : m_ptr(value ? statics().t : statics().f) {} | ||||||
|  | Json::Json(const string &value)        : m_ptr(make_shared<JsonString>(value)) {} | ||||||
|  | Json::Json(string &&value)             : m_ptr(make_shared<JsonString>(move(value))) {} | ||||||
|  | Json::Json(const char * value)         : m_ptr(make_shared<JsonString>(value)) {} | ||||||
|  | Json::Json(const Json::array &values)  : m_ptr(make_shared<JsonArray>(values)) {} | ||||||
|  | Json::Json(Json::array &&values)       : m_ptr(make_shared<JsonArray>(move(values))) {} | ||||||
|  | Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {} | ||||||
|  | Json::Json(Json::object &&values)      : m_ptr(make_shared<JsonObject>(move(values))) {} | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Accessors | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | Json::Type Json::type()                           const { return m_ptr->type();         } | ||||||
|  | double Json::number_value()                       const { return m_ptr->number_value(); } | ||||||
|  | int Json::int_value()                             const { return m_ptr->int_value();    } | ||||||
|  | bool Json::bool_value()                           const { return m_ptr->bool_value();   } | ||||||
|  | const string & Json::string_value()               const { return m_ptr->string_value(); } | ||||||
|  | const vector<Json> & Json::array_items()          const { return m_ptr->array_items();  } | ||||||
|  | const map<string, Json> & Json::object_items()    const { return m_ptr->object_items(); } | ||||||
|  | const Json & Json::operator[] (size_t i)          const { return (*m_ptr)[i];           } | ||||||
|  | const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key];         } | ||||||
|  | 
 | ||||||
|  | double                    JsonValue::number_value()              const { return 0; } | ||||||
|  | int                       JsonValue::int_value()                 const { return 0; } | ||||||
|  | bool                      JsonValue::bool_value()                const { return false; } | ||||||
|  | const string &            JsonValue::string_value()              const { return statics().empty_string; } | ||||||
|  | const vector<Json> &      JsonValue::array_items()               const { return statics().empty_vector; } | ||||||
|  | const map<string, Json> & JsonValue::object_items()              const { return statics().empty_map; } | ||||||
|  | const Json &              JsonValue::operator[] (size_t)         const { return static_null(); } | ||||||
|  | const Json &              JsonValue::operator[] (const string &) const { return static_null(); } | ||||||
|  | 
 | ||||||
|  | const Json & JsonObject::operator[] (const string &key) const { | ||||||
|  |     auto iter = m_value.find(key); | ||||||
|  |     return (iter == m_value.end()) ? static_null() : iter->second; | ||||||
|  | } | ||||||
|  | const Json & JsonArray::operator[] (size_t i) const { | ||||||
|  |     if (i >= m_value.size()) return static_null(); | ||||||
|  |     else return m_value[i]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Comparison | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | bool Json::operator== (const Json &other) const { | ||||||
|  |     if (m_ptr == other.m_ptr) | ||||||
|  |         return true; | ||||||
|  |     if (m_ptr->type() != other.m_ptr->type()) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     return m_ptr->equals(other.m_ptr.get()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Json::operator< (const Json &other) const { | ||||||
|  |     if (m_ptr == other.m_ptr) | ||||||
|  |         return false; | ||||||
|  |     if (m_ptr->type() != other.m_ptr->type()) | ||||||
|  |         return m_ptr->type() < other.m_ptr->type(); | ||||||
|  | 
 | ||||||
|  |     return m_ptr->less(other.m_ptr.get()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Parsing | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* esc(c)
 | ||||||
|  |  * | ||||||
|  |  * Format char c suitable for printing in an error message. | ||||||
|  |  */ | ||||||
|  | static inline string esc(char c) { | ||||||
|  |     char buf[12]; | ||||||
|  |     if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) { | ||||||
|  |         snprintf(buf, sizeof buf, "'%c' (%d)", c, c); | ||||||
|  |     } else { | ||||||
|  |         snprintf(buf, sizeof buf, "(%d)", c); | ||||||
|  |     } | ||||||
|  |     return string(buf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool in_range(long x, long lower, long upper) { | ||||||
|  |     return (x >= lower && x <= upper); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | /* JsonParser
 | ||||||
|  |  * | ||||||
|  |  * Object that tracks all state of an in-progress parse. | ||||||
|  |  */ | ||||||
|  | struct JsonParser final { | ||||||
|  | 
 | ||||||
|  |     /* State
 | ||||||
|  |      */ | ||||||
|  |     const string &str; | ||||||
|  |     size_t i; | ||||||
|  |     string &err; | ||||||
|  |     bool failed; | ||||||
|  |     const JsonParse strategy; | ||||||
|  | 
 | ||||||
|  |     /* fail(msg, err_ret = Json())
 | ||||||
|  |      * | ||||||
|  |      * Mark this parse as failed. | ||||||
|  |      */ | ||||||
|  |     Json fail(string &&msg) { | ||||||
|  |         return fail(move(msg), Json()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename T> | ||||||
|  |     T fail(string &&msg, const T err_ret) { | ||||||
|  |         if (!failed) | ||||||
|  |             err = std::move(msg); | ||||||
|  |         failed = true; | ||||||
|  |         return err_ret; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* consume_whitespace()
 | ||||||
|  |      * | ||||||
|  |      * Advance until the current character is non-whitespace. | ||||||
|  |      */ | ||||||
|  |     void consume_whitespace() { | ||||||
|  |         while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') | ||||||
|  |             i++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* consume_comment()
 | ||||||
|  |      * | ||||||
|  |      * Advance comments (c-style inline and multiline). | ||||||
|  |      */ | ||||||
|  |     bool consume_comment() { | ||||||
|  |       bool comment_found = false; | ||||||
|  |       if (str[i] == '/') { | ||||||
|  |         i++; | ||||||
|  |         if (i == str.size()) | ||||||
|  |           return fail("unexpected end of input after start of comment", false); | ||||||
|  |         if (str[i] == '/') { // inline comment
 | ||||||
|  |           i++; | ||||||
|  |           // advance until next line, or end of input
 | ||||||
|  |           while (i < str.size() && str[i] != '\n') { | ||||||
|  |             i++; | ||||||
|  |           } | ||||||
|  |           comment_found = true; | ||||||
|  |         } | ||||||
|  |         else if (str[i] == '*') { // multiline comment
 | ||||||
|  |           i++; | ||||||
|  |           if (i > str.size()-2) | ||||||
|  |             return fail("unexpected end of input inside multi-line comment", false); | ||||||
|  |           // advance until closing tokens
 | ||||||
|  |           while (!(str[i] == '*' && str[i+1] == '/')) { | ||||||
|  |             i++; | ||||||
|  |             if (i > str.size()-2) | ||||||
|  |               return fail( | ||||||
|  |                 "unexpected end of input inside multi-line comment", false); | ||||||
|  |           } | ||||||
|  |           i += 2; | ||||||
|  |           comment_found = true; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |           return fail("malformed comment", false); | ||||||
|  |       } | ||||||
|  |       return comment_found; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* consume_garbage()
 | ||||||
|  |      * | ||||||
|  |      * Advance until the current character is non-whitespace and non-comment. | ||||||
|  |      */ | ||||||
|  |     void consume_garbage() { | ||||||
|  |       consume_whitespace(); | ||||||
|  |       if(strategy == JsonParse::COMMENTS) { | ||||||
|  |         bool comment_found = false; | ||||||
|  |         do { | ||||||
|  |           comment_found = consume_comment(); | ||||||
|  |           if (failed) return; | ||||||
|  |           consume_whitespace(); | ||||||
|  |         } | ||||||
|  |         while(comment_found); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* get_next_token()
 | ||||||
|  |      * | ||||||
|  |      * Return the next non-whitespace character. If the end of the input is reached, | ||||||
|  |      * flag an error and return 0. | ||||||
|  |      */ | ||||||
|  |     char get_next_token() { | ||||||
|  |         consume_garbage(); | ||||||
|  |         if (failed) return static_cast<char>(0); | ||||||
|  |         if (i == str.size()) | ||||||
|  |             return fail("unexpected end of input", static_cast<char>(0)); | ||||||
|  | 
 | ||||||
|  |         return str[i++]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* encode_utf8(pt, out)
 | ||||||
|  |      * | ||||||
|  |      * Encode pt as UTF-8 and add it to out. | ||||||
|  |      */ | ||||||
|  |     void encode_utf8(long pt, string & out) { | ||||||
|  |         if (pt < 0) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         if (pt < 0x80) { | ||||||
|  |             out += static_cast<char>(pt); | ||||||
|  |         } else if (pt < 0x800) { | ||||||
|  |             out += static_cast<char>((pt >> 6) | 0xC0); | ||||||
|  |             out += static_cast<char>((pt & 0x3F) | 0x80); | ||||||
|  |         } else if (pt < 0x10000) { | ||||||
|  |             out += static_cast<char>((pt >> 12) | 0xE0); | ||||||
|  |             out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80); | ||||||
|  |             out += static_cast<char>((pt & 0x3F) | 0x80); | ||||||
|  |         } else { | ||||||
|  |             out += static_cast<char>((pt >> 18) | 0xF0); | ||||||
|  |             out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80); | ||||||
|  |             out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80); | ||||||
|  |             out += static_cast<char>((pt & 0x3F) | 0x80); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* parse_string()
 | ||||||
|  |      * | ||||||
|  |      * Parse a string, starting at the current position. | ||||||
|  |      */ | ||||||
|  |     string parse_string() { | ||||||
|  |         string out; | ||||||
|  |         long last_escaped_codepoint = -1; | ||||||
|  |         while (true) { | ||||||
|  |             if (i == str.size()) | ||||||
|  |                 return fail("unexpected end of input in string", ""); | ||||||
|  | 
 | ||||||
|  |             char ch = str[i++]; | ||||||
|  | 
 | ||||||
|  |             if (ch == '"') { | ||||||
|  |                 encode_utf8(last_escaped_codepoint, out); | ||||||
|  |                 return out; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (in_range(ch, 0, 0x1f)) | ||||||
|  |                 return fail("unescaped " + esc(ch) + " in string", ""); | ||||||
|  | 
 | ||||||
|  |             // The usual case: non-escaped characters
 | ||||||
|  |             if (ch != '\\') { | ||||||
|  |                 encode_utf8(last_escaped_codepoint, out); | ||||||
|  |                 last_escaped_codepoint = -1; | ||||||
|  |                 out += ch; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Handle escapes
 | ||||||
|  |             if (i == str.size()) | ||||||
|  |                 return fail("unexpected end of input in string", ""); | ||||||
|  | 
 | ||||||
|  |             ch = str[i++]; | ||||||
|  | 
 | ||||||
|  |             if (ch == 'u') { | ||||||
|  |                 // Extract 4-byte escape sequence
 | ||||||
|  |                 string esc = str.substr(i, 4); | ||||||
|  |                 // Explicitly check length of the substring. The following loop
 | ||||||
|  |                 // relies on std::string returning the terminating NUL when
 | ||||||
|  |                 // accessing str[length]. Checking here reduces brittleness.
 | ||||||
|  |                 if (esc.length() < 4) { | ||||||
|  |                     return fail("bad \\u escape: " + esc, ""); | ||||||
|  |                 } | ||||||
|  |                 for (size_t j = 0; j < 4; j++) { | ||||||
|  |                     if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') | ||||||
|  |                             && !in_range(esc[j], '0', '9')) | ||||||
|  |                         return fail("bad \\u escape: " + esc, ""); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 long codepoint = strtol(esc.data(), nullptr, 16); | ||||||
|  | 
 | ||||||
|  |                 // JSON specifies that characters outside the BMP shall be encoded as a pair
 | ||||||
|  |                 // of 4-hex-digit \u escapes encoding their surrogate pair components. Check
 | ||||||
|  |                 // whether we're in the middle of such a beast: the previous codepoint was an
 | ||||||
|  |                 // escaped lead (high) surrogate, and this is a trail (low) surrogate.
 | ||||||
|  |                 if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) | ||||||
|  |                         && in_range(codepoint, 0xDC00, 0xDFFF)) { | ||||||
|  |                     // Reassemble the two surrogate pairs into one astral-plane character, per
 | ||||||
|  |                     // the UTF-16 algorithm.
 | ||||||
|  |                     encode_utf8((((last_escaped_codepoint - 0xD800) << 10) | ||||||
|  |                                  | (codepoint - 0xDC00)) + 0x10000, out); | ||||||
|  |                     last_escaped_codepoint = -1; | ||||||
|  |                 } else { | ||||||
|  |                     encode_utf8(last_escaped_codepoint, out); | ||||||
|  |                     last_escaped_codepoint = codepoint; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 i += 4; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             encode_utf8(last_escaped_codepoint, out); | ||||||
|  |             last_escaped_codepoint = -1; | ||||||
|  | 
 | ||||||
|  |             if (ch == 'b') { | ||||||
|  |                 out += '\b'; | ||||||
|  |             } else if (ch == 'f') { | ||||||
|  |                 out += '\f'; | ||||||
|  |             } else if (ch == 'n') { | ||||||
|  |                 out += '\n'; | ||||||
|  |             } else if (ch == 'r') { | ||||||
|  |                 out += '\r'; | ||||||
|  |             } else if (ch == 't') { | ||||||
|  |                 out += '\t'; | ||||||
|  |             } else if (ch == '"' || ch == '\\' || ch == '/') { | ||||||
|  |                 out += ch; | ||||||
|  |             } else { | ||||||
|  |                 return fail("invalid escape character " + esc(ch), ""); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* parse_number()
 | ||||||
|  |      * | ||||||
|  |      * Parse a double. | ||||||
|  |      */ | ||||||
|  |     Json parse_number() { | ||||||
|  |         size_t start_pos = i; | ||||||
|  | 
 | ||||||
|  |         if (str[i] == '-') | ||||||
|  |             i++; | ||||||
|  | 
 | ||||||
|  |         // Integer part
 | ||||||
|  |         if (str[i] == '0') { | ||||||
|  |             i++; | ||||||
|  |             if (in_range(str[i], '0', '9')) | ||||||
|  |                 return fail("leading 0s not permitted in numbers"); | ||||||
|  |         } else if (in_range(str[i], '1', '9')) { | ||||||
|  |             i++; | ||||||
|  |             while (in_range(str[i], '0', '9')) | ||||||
|  |                 i++; | ||||||
|  |         } else { | ||||||
|  |             return fail("invalid " + esc(str[i]) + " in number"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' | ||||||
|  |                 && (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) { | ||||||
|  |             return std::atoi(str.c_str() + start_pos); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Decimal part
 | ||||||
|  |         if (str[i] == '.') { | ||||||
|  |             i++; | ||||||
|  |             if (!in_range(str[i], '0', '9')) | ||||||
|  |                 return fail("at least one digit required in fractional part"); | ||||||
|  | 
 | ||||||
|  |             while (in_range(str[i], '0', '9')) | ||||||
|  |                 i++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Exponent part
 | ||||||
|  |         if (str[i] == 'e' || str[i] == 'E') { | ||||||
|  |             i++; | ||||||
|  | 
 | ||||||
|  |             if (str[i] == '+' || str[i] == '-') | ||||||
|  |                 i++; | ||||||
|  | 
 | ||||||
|  |             if (!in_range(str[i], '0', '9')) | ||||||
|  |                 return fail("at least one digit required in exponent"); | ||||||
|  | 
 | ||||||
|  |             while (in_range(str[i], '0', '9')) | ||||||
|  |                 i++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return std::strtod(str.c_str() + start_pos, nullptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* expect(str, res)
 | ||||||
|  |      * | ||||||
|  |      * Expect that 'str' starts at the character that was just read. If it does, advance | ||||||
|  |      * the input and return res. If not, flag an error. | ||||||
|  |      */ | ||||||
|  |     Json expect(const string &expected, Json res) { | ||||||
|  |         assert(i != 0); | ||||||
|  |         i--; | ||||||
|  |         if (str.compare(i, expected.length(), expected) == 0) { | ||||||
|  |             i += expected.length(); | ||||||
|  |             return res; | ||||||
|  |         } else { | ||||||
|  |             return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* parse_json()
 | ||||||
|  |      * | ||||||
|  |      * Parse a JSON object. | ||||||
|  |      */ | ||||||
|  |     Json parse_json(int depth) { | ||||||
|  |         if (depth > max_depth) { | ||||||
|  |             return fail("exceeded maximum nesting depth"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         char ch = get_next_token(); | ||||||
|  |         if (failed) | ||||||
|  |             return Json(); | ||||||
|  | 
 | ||||||
|  |         if (ch == '-' || (ch >= '0' && ch <= '9')) { | ||||||
|  |             i--; | ||||||
|  |             return parse_number(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (ch == 't') | ||||||
|  |             return expect("true", true); | ||||||
|  | 
 | ||||||
|  |         if (ch == 'f') | ||||||
|  |             return expect("false", false); | ||||||
|  | 
 | ||||||
|  |         if (ch == 'n') | ||||||
|  |             return expect("null", Json()); | ||||||
|  | 
 | ||||||
|  |         if (ch == '"') | ||||||
|  |             return parse_string(); | ||||||
|  | 
 | ||||||
|  |         if (ch == '{') { | ||||||
|  |             map<string, Json> data; | ||||||
|  |             ch = get_next_token(); | ||||||
|  |             if (ch == '}') | ||||||
|  |                 return data; | ||||||
|  | 
 | ||||||
|  |             while (1) { | ||||||
|  |                 if (ch != '"') | ||||||
|  |                     return fail("expected '\"' in object, got " + esc(ch)); | ||||||
|  | 
 | ||||||
|  |                 string key = parse_string(); | ||||||
|  |                 if (failed) | ||||||
|  |                     return Json(); | ||||||
|  | 
 | ||||||
|  |                 ch = get_next_token(); | ||||||
|  |                 if (ch != ':') | ||||||
|  |                     return fail("expected ':' in object, got " + esc(ch)); | ||||||
|  | 
 | ||||||
|  |                 data[std::move(key)] = parse_json(depth + 1); | ||||||
|  |                 if (failed) | ||||||
|  |                     return Json(); | ||||||
|  | 
 | ||||||
|  |                 ch = get_next_token(); | ||||||
|  |                 if (ch == '}') | ||||||
|  |                     break; | ||||||
|  |                 if (ch != ',') | ||||||
|  |                     return fail("expected ',' in object, got " + esc(ch)); | ||||||
|  | 
 | ||||||
|  |                 ch = get_next_token(); | ||||||
|  |             } | ||||||
|  |             return data; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (ch == '[') { | ||||||
|  |             vector<Json> data; | ||||||
|  |             ch = get_next_token(); | ||||||
|  |             if (ch == ']') | ||||||
|  |                 return data; | ||||||
|  | 
 | ||||||
|  |             while (1) { | ||||||
|  |                 i--; | ||||||
|  |                 data.push_back(parse_json(depth + 1)); | ||||||
|  |                 if (failed) | ||||||
|  |                     return Json(); | ||||||
|  | 
 | ||||||
|  |                 ch = get_next_token(); | ||||||
|  |                 if (ch == ']') | ||||||
|  |                     break; | ||||||
|  |                 if (ch != ',') | ||||||
|  |                     return fail("expected ',' in list, got " + esc(ch)); | ||||||
|  | 
 | ||||||
|  |                 ch = get_next_token(); | ||||||
|  |                 (void)ch; | ||||||
|  |             } | ||||||
|  |             return data; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return fail("expected value, got " + esc(ch)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | }//namespace {
 | ||||||
|  | 
 | ||||||
|  | Json Json::parse(const string &in, string &err, JsonParse strategy) { | ||||||
|  |     JsonParser parser { in, 0, err, false, strategy }; | ||||||
|  |     Json result = parser.parse_json(0); | ||||||
|  | 
 | ||||||
|  |     // Check for any trailing garbage
 | ||||||
|  |     parser.consume_garbage(); | ||||||
|  |     if (parser.failed) | ||||||
|  |         return Json(); | ||||||
|  |     if (parser.i != in.size()) | ||||||
|  |         return parser.fail("unexpected trailing " + esc(in[parser.i])); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Documented in json11.hpp
 | ||||||
|  | vector<Json> Json::parse_multi(const string &in, | ||||||
|  |                                std::string::size_type &parser_stop_pos, | ||||||
|  |                                string &err, | ||||||
|  |                                JsonParse strategy) { | ||||||
|  |     JsonParser parser { in, 0, err, false, strategy }; | ||||||
|  |     parser_stop_pos = 0; | ||||||
|  |     vector<Json> json_vec; | ||||||
|  |     while (parser.i != in.size() && !parser.failed) { | ||||||
|  |         json_vec.push_back(parser.parse_json(0)); | ||||||
|  |         if (parser.failed) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         // Check for another object
 | ||||||
|  |         parser.consume_garbage(); | ||||||
|  |         if (parser.failed) | ||||||
|  |             break; | ||||||
|  |         parser_stop_pos = parser.i; | ||||||
|  |     } | ||||||
|  |     return json_vec; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* * * * * * * * * * * * * * * * * * * *
 | ||||||
|  |  * Shape-checking | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | bool Json::has_shape(const shape & types, string & err) const { | ||||||
|  |     if (!is_object()) { | ||||||
|  |         err = "expected JSON object, got " + dump(); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (auto & item : types) { | ||||||
|  |         if ((*this)[item.first].type() != item.second) { | ||||||
|  |             err = "bad type for " + item.first + " in " + dump(); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace json11
 | ||||||
							
								
								
									
										232
									
								
								libs/json11/json11.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								libs/json11/json11.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,232 @@ | ||||||
|  | /* json11
 | ||||||
|  |  * | ||||||
|  |  * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. | ||||||
|  |  * | ||||||
|  |  * The core object provided by the library is json11::Json. A Json object represents any JSON | ||||||
|  |  * value: null, bool, number (int or double), string (std::string), array (std::vector), or | ||||||
|  |  * object (std::map). | ||||||
|  |  * | ||||||
|  |  * Json objects act like values: they can be assigned, copied, moved, compared for equality or | ||||||
|  |  * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and | ||||||
|  |  * Json::parse (static) to parse a std::string as a Json object. | ||||||
|  |  * | ||||||
|  |  * Internally, the various types of Json object are represented by the JsonValue class | ||||||
|  |  * hierarchy. | ||||||
|  |  * | ||||||
|  |  * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, | ||||||
|  |  * so some JSON implementations distinguish between integers and floating-point numbers, while | ||||||
|  |  * some don't. In json11, we choose the latter. Because some JSON implementations (namely | ||||||
|  |  * Javascript itself) treat all numbers as the same type, distinguishing the two leads | ||||||
|  |  * to JSON that will be *silently* changed by a round-trip through those implementations. | ||||||
|  |  * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also | ||||||
|  |  * provides integer helpers. | ||||||
|  |  * | ||||||
|  |  * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the | ||||||
|  |  * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 | ||||||
|  |  * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch | ||||||
|  |  * will be exact for +/- 275 years.) | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* Copyright (c) 2013 Dropbox, Inc.
 | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <map> | ||||||
|  | #include <memory> | ||||||
|  | #include <initializer_list> | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  |     #if _MSC_VER <= 1800 // VS 2013
 | ||||||
|  |         #ifndef noexcept | ||||||
|  |             #define noexcept throw() | ||||||
|  |         #endif | ||||||
|  | 
 | ||||||
|  |         #ifndef snprintf | ||||||
|  |             #define snprintf _snprintf_s | ||||||
|  |         #endif | ||||||
|  |     #endif | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace json11 { | ||||||
|  | 
 | ||||||
|  | enum JsonParse { | ||||||
|  |     STANDARD, COMMENTS | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class JsonValue; | ||||||
|  | 
 | ||||||
|  | class Json final { | ||||||
|  | public: | ||||||
|  |     // Types
 | ||||||
|  |     enum Type { | ||||||
|  |         NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Array and object typedefs
 | ||||||
|  |     typedef std::vector<Json> array; | ||||||
|  |     typedef std::map<std::string, Json> object; | ||||||
|  | 
 | ||||||
|  |     // Constructors for the various types of JSON value.
 | ||||||
|  |     Json() noexcept;                // NUL
 | ||||||
|  |     Json(std::nullptr_t) noexcept;  // NUL
 | ||||||
|  |     Json(double value);             // NUMBER
 | ||||||
|  |     Json(int value);                // NUMBER
 | ||||||
|  |     Json(bool value);               // BOOL
 | ||||||
|  |     Json(const std::string &value); // STRING
 | ||||||
|  |     Json(std::string &&value);      // STRING
 | ||||||
|  |     Json(const char * value);       // STRING
 | ||||||
|  |     Json(const array &values);      // ARRAY
 | ||||||
|  |     Json(array &&values);           // ARRAY
 | ||||||
|  |     Json(const object &values);     // OBJECT
 | ||||||
|  |     Json(object &&values);          // OBJECT
 | ||||||
|  | 
 | ||||||
|  |     // Implicit constructor: anything with a to_json() function.
 | ||||||
|  |     template <class T, class = decltype(&T::to_json)> | ||||||
|  |     Json(const T & t) : Json(t.to_json()) {} | ||||||
|  | 
 | ||||||
|  |     // Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
 | ||||||
|  |     template <class M, typename std::enable_if< | ||||||
|  |         std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value | ||||||
|  |         && std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value, | ||||||
|  |             int>::type = 0> | ||||||
|  |     Json(const M & m) : Json(object(m.begin(), m.end())) {} | ||||||
|  | 
 | ||||||
|  |     // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
 | ||||||
|  |     template <class V, typename std::enable_if< | ||||||
|  |         std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value, | ||||||
|  |             int>::type = 0> | ||||||
|  |     Json(const V & v) : Json(array(v.begin(), v.end())) {} | ||||||
|  | 
 | ||||||
|  |     // This prevents Json(some_pointer) from accidentally producing a bool. Use
 | ||||||
|  |     // Json(bool(some_pointer)) if that behavior is desired.
 | ||||||
|  |     Json(void *) = delete; | ||||||
|  | 
 | ||||||
|  |     // Accessors
 | ||||||
|  |     Type type() const; | ||||||
|  | 
 | ||||||
|  |     bool is_null()   const { return type() == NUL; } | ||||||
|  |     bool is_number() const { return type() == NUMBER; } | ||||||
|  |     bool is_bool()   const { return type() == BOOL; } | ||||||
|  |     bool is_string() const { return type() == STRING; } | ||||||
|  |     bool is_array()  const { return type() == ARRAY; } | ||||||
|  |     bool is_object() const { return type() == OBJECT; } | ||||||
|  | 
 | ||||||
|  |     // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
 | ||||||
|  |     // distinguish between integer and non-integer numbers - number_value() and int_value()
 | ||||||
|  |     // can both be applied to a NUMBER-typed object.
 | ||||||
|  |     double number_value() const; | ||||||
|  |     int int_value() const; | ||||||
|  | 
 | ||||||
|  |     // Return the enclosed value if this is a boolean, false otherwise.
 | ||||||
|  |     bool bool_value() const; | ||||||
|  |     // Return the enclosed string if this is a string, "" otherwise.
 | ||||||
|  |     const std::string &string_value() const; | ||||||
|  |     // Return the enclosed std::vector if this is an array, or an empty vector otherwise.
 | ||||||
|  |     const array &array_items() const; | ||||||
|  |     // Return the enclosed std::map if this is an object, or an empty map otherwise.
 | ||||||
|  |     const object &object_items() const; | ||||||
|  | 
 | ||||||
|  |     // Return a reference to arr[i] if this is an array, Json() otherwise.
 | ||||||
|  |     const Json & operator[](size_t i) const; | ||||||
|  |     // Return a reference to obj[key] if this is an object, Json() otherwise.
 | ||||||
|  |     const Json & operator[](const std::string &key) const; | ||||||
|  | 
 | ||||||
|  |     // Serialize.
 | ||||||
|  |     void dump(std::string &out) const; | ||||||
|  |     std::string dump() const { | ||||||
|  |         std::string out; | ||||||
|  |         dump(out); | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Parse. If parse fails, return Json() and assign an error message to err.
 | ||||||
|  |     static Json parse(const std::string & in, | ||||||
|  |                       std::string & err, | ||||||
|  |                       JsonParse strategy = JsonParse::STANDARD); | ||||||
|  |     static Json parse(const char * in, | ||||||
|  |                       std::string & err, | ||||||
|  |                       JsonParse strategy = JsonParse::STANDARD) { | ||||||
|  |         if (in) { | ||||||
|  |             return parse(std::string(in), err, strategy); | ||||||
|  |         } else { | ||||||
|  |             err = "null input"; | ||||||
|  |             return nullptr; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Parse multiple objects, concatenated or separated by whitespace
 | ||||||
|  |     static std::vector<Json> parse_multi( | ||||||
|  |         const std::string & in, | ||||||
|  |         std::string::size_type & parser_stop_pos, | ||||||
|  |         std::string & err, | ||||||
|  |         JsonParse strategy = JsonParse::STANDARD); | ||||||
|  | 
 | ||||||
|  |     static inline std::vector<Json> parse_multi( | ||||||
|  |         const std::string & in, | ||||||
|  |         std::string & err, | ||||||
|  |         JsonParse strategy = JsonParse::STANDARD) { | ||||||
|  |         std::string::size_type parser_stop_pos; | ||||||
|  |         return parse_multi(in, parser_stop_pos, err, strategy); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool operator== (const Json &rhs) const; | ||||||
|  |     bool operator<  (const Json &rhs) const; | ||||||
|  |     bool operator!= (const Json &rhs) const { return !(*this == rhs); } | ||||||
|  |     bool operator<= (const Json &rhs) const { return !(rhs < *this); } | ||||||
|  |     bool operator>  (const Json &rhs) const { return  (rhs < *this); } | ||||||
|  |     bool operator>= (const Json &rhs) const { return !(*this < rhs); } | ||||||
|  | 
 | ||||||
|  |     /* has_shape(types, err)
 | ||||||
|  |      * | ||||||
|  |      * Return true if this is a JSON object and, for each item in types, has a field of | ||||||
|  |      * the given type. If not, return false and set err to a descriptive message. | ||||||
|  |      */ | ||||||
|  |     typedef std::initializer_list<std::pair<std::string, Type>> shape; | ||||||
|  |     bool has_shape(const shape & types, std::string & err) const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::shared_ptr<JsonValue> m_ptr; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Internal class hierarchy - JsonValue objects are not exposed to users of this API.
 | ||||||
|  | class JsonValue { | ||||||
|  | protected: | ||||||
|  |     friend class Json; | ||||||
|  |     friend class JsonInt; | ||||||
|  |     friend class JsonDouble; | ||||||
|  |     virtual Json::Type type() const = 0; | ||||||
|  |     virtual bool equals(const JsonValue * other) const = 0; | ||||||
|  |     virtual bool less(const JsonValue * other) const = 0; | ||||||
|  |     virtual void dump(std::string &out) const = 0; | ||||||
|  |     virtual double number_value() const; | ||||||
|  |     virtual int int_value() const; | ||||||
|  |     virtual bool bool_value() const; | ||||||
|  |     virtual const std::string &string_value() const; | ||||||
|  |     virtual const Json::array &array_items() const; | ||||||
|  |     virtual const Json &operator[](size_t i) const; | ||||||
|  |     virtual const Json::object &object_items() const; | ||||||
|  |     virtual const Json &operator[](const std::string &key) const; | ||||||
|  |     virtual ~JsonValue() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace json11
 | ||||||
|  | @ -65,7 +65,7 @@ SOFTWARE. */ | ||||||
| 
 | 
 | ||||||
| int child_pid=0; | int child_pid=0; | ||||||
| 
 | 
 | ||||||
| int fail(char *format, char *data) { | int fail(const char *format, const char *data) { | ||||||
|     /* Print error message to stderr and return 2 */ |     /* Print error message to stderr and return 2 */ | ||||||
|     fprintf(stderr, format, data); |     fprintf(stderr, format, data); | ||||||
|     return 2; |     return 2; | ||||||
|  | @ -76,7 +76,7 @@ char *quoted(char *data) { | ||||||
| 
 | 
 | ||||||
|     /* We allocate twice as much space as needed to deal with worse-case
 |     /* We allocate twice as much space as needed to deal with worse-case
 | ||||||
|        of having to escape everything. */ |        of having to escape everything. */ | ||||||
|     char *result = calloc(ln*2+3, sizeof(char)); |     char *result = (char *)calloc(ln*2+3, sizeof(char)); | ||||||
|     char *presult = result; |     char *presult = result; | ||||||
| 
 | 
 | ||||||
|     *presult++ = '"'; |     *presult++ = '"'; | ||||||
|  | @ -120,7 +120,7 @@ char *loadable_exe(char *exename) { | ||||||
|     if (!hPython) return NULL; */ |     if (!hPython) return NULL; */ | ||||||
| 
 | 
 | ||||||
|     /* Return the absolute filename for spawnv */ |     /* Return the absolute filename for spawnv */ | ||||||
|     result = calloc(MAX_PATH, sizeof(char)); |     result = (char *)calloc(MAX_PATH, sizeof(char)); | ||||||
|     strncpy(result, exename, MAX_PATH); |     strncpy(result, exename, MAX_PATH); | ||||||
|     /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
 |     /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
 | ||||||
| 
 | 
 | ||||||
|  | @ -158,7 +158,7 @@ char **parse_argv(char *cmdline, int *argc) | ||||||
| { | { | ||||||
|     /* Parse a command line in-place using MS C rules */ |     /* Parse a command line in-place using MS C rules */ | ||||||
| 
 | 
 | ||||||
|     char **result = calloc(strlen(cmdline), sizeof(char *)); |     char **result = (char **)calloc(strlen(cmdline), sizeof(char *)); | ||||||
|     char *output = cmdline; |     char *output = cmdline; | ||||||
|     char c; |     char c; | ||||||
|     int nb = 0; |     int nb = 0; | ||||||
|  |  | ||||||
|  | @ -1081,6 +1081,8 @@ class WConstructor: | ||||||
| 		con.args = [] | 		con.args = [] | ||||||
| 		con.duplicate = False | 		con.duplicate = False | ||||||
| 		con.protected = protected | 		con.protected = protected | ||||||
|  | 		if str.startswith(str_def, "inline "): | ||||||
|  | 			str_def = str_def[7:] | ||||||
| 		if not str.startswith(str_def, class_.name + "("): | 		if not str.startswith(str_def, class_.name + "("): | ||||||
| 			return None | 			return None | ||||||
| 		str_def = str_def[len(class_.name)+1:] | 		str_def = str_def[len(class_.name)+1:] | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ OBJS += passes/cmds/plugin.o | ||||||
| OBJS += passes/cmds/check.o | OBJS += passes/cmds/check.o | ||||||
| OBJS += passes/cmds/qwp.o | OBJS += passes/cmds/qwp.o | ||||||
| OBJS += passes/cmds/edgetypes.o | OBJS += passes/cmds/edgetypes.o | ||||||
|  | OBJS += passes/cmds/portlist.o | ||||||
| OBJS += passes/cmds/chformal.o | OBJS += passes/cmds/chformal.o | ||||||
| OBJS += passes/cmds/chtype.o | OBJS += passes/cmds/chtype.o | ||||||
| OBJS += passes/cmds/blackbox.o | OBJS += passes/cmds/blackbox.o | ||||||
|  |  | ||||||
|  | @ -105,6 +105,11 @@ struct AddPass : public Pass { | ||||||
| 		log("Like 'add -input', but also connect the signal between instances of the\n"); | 		log("Like 'add -input', but also connect the signal between instances of the\n"); | ||||||
| 		log("selected modules.\n"); | 		log("selected modules.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    add -mod <name[s]>\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Add module[s] with the specified name[s].\n"); | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  | @ -113,6 +118,7 @@ struct AddPass : public Pass { | ||||||
| 		bool arg_flag_input = false; | 		bool arg_flag_input = false; | ||||||
| 		bool arg_flag_output = false; | 		bool arg_flag_output = false; | ||||||
| 		bool arg_flag_global = false; | 		bool arg_flag_global = false; | ||||||
|  | 		bool mod_mode = false; | ||||||
| 		int arg_width = 0; | 		int arg_width = 0; | ||||||
| 
 | 
 | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
|  | @ -133,8 +139,20 @@ struct AddPass : public Pass { | ||||||
| 				arg_width = atoi(args[++argidx].c_str()); | 				arg_width = atoi(args[++argidx].c_str()); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (arg == "-mod") { | ||||||
|  | 				mod_mode = true; | ||||||
|  | 				argidx++; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (mod_mode) { | ||||||
|  | 			for (; argidx < args.size(); argidx++) | ||||||
|  | 				design->addModule(RTLIL::escape_id(args[argidx])); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
| 		for (auto &mod : design->modules_) | 		for (auto &mod : design->modules_) | ||||||
|  |  | ||||||
|  | @ -41,14 +41,24 @@ struct CheckPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log(" - used wires that do not have a driver\n"); | 		log(" - used wires that do not have a driver\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("When called with -noinit then this command also checks for wires which have\n"); | 		log("Options:\n"); | ||||||
| 		log("the 'init' attribute set.\n"); |  | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("When called with -initdrv then this command also checks for wires which have\n"); | 		log("  -noinit\n"); | ||||||
| 		log("the 'init' attribute set and aren't driven by a FF cell type.\n"); | 		log("    Also check for wires which have the 'init' attribute set.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("When called with -assert then the command will produce an error if any\n"); | 		log("  -initdrv\n"); | ||||||
| 		log("problems are found in the current design.\n"); | 		log("    Also check for wires that have the 'init' attribute set and are not\n"); | ||||||
|  | 		log("    driven by an FF cell type.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("  -mapped\n"); | ||||||
|  | 		log("    Also check for internal cells that have not been mapped to cells of the\n"); | ||||||
|  | 		log("    target architecture.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("  -allow-tbuf\n"); | ||||||
|  | 		log("    Modify the -mapped behavior to still allow $_TBUF_ cells.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("  -assert\n"); | ||||||
|  | 		log("    Produce a runtime error if any problems are found in the current design.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | @ -56,6 +66,8 @@ struct CheckPass : public Pass { | ||||||
| 		int counter = 0; | 		int counter = 0; | ||||||
| 		bool noinit = false; | 		bool noinit = false; | ||||||
| 		bool initdrv = false; | 		bool initdrv = false; | ||||||
|  | 		bool mapped = false; | ||||||
|  | 		bool allow_tbuf = false; | ||||||
| 		bool assert_mode = false; | 		bool assert_mode = false; | ||||||
| 
 | 
 | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
|  | @ -68,6 +80,14 @@ struct CheckPass : public Pass { | ||||||
| 				initdrv = true; | 				initdrv = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-mapped") { | ||||||
|  | 				mapped = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (args[argidx] == "-allow-tbuf") { | ||||||
|  | 				allow_tbuf = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			if (args[argidx] == "-assert") { | 			if (args[argidx] == "-assert") { | ||||||
| 				assert_mode = true; | 				assert_mode = true; | ||||||
| 				continue; | 				continue; | ||||||
|  | @ -135,29 +155,37 @@ struct CheckPass : public Pass { | ||||||
| 			TopoSort<string> topo; | 			TopoSort<string> topo; | ||||||
| 
 | 
 | ||||||
| 			for (auto cell : module->cells()) | 			for (auto cell : module->cells()) | ||||||
| 			for (auto &conn : cell->connections()) { | 			{ | ||||||
| 				SigSpec sig = sigmap(conn.second); | 				if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { | ||||||
| 				bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); | 					if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed; | ||||||
| 				if (cell->input(conn.first)) | 					log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type)); | ||||||
| 					for (auto bit : sig) | 					counter++; | ||||||
| 						if (bit.wire) { | 				cell_allowed:; | ||||||
|  | 				} | ||||||
|  | 				for (auto &conn : cell->connections()) { | ||||||
|  | 					SigSpec sig = sigmap(conn.second); | ||||||
|  | 					bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); | ||||||
|  | 					if (cell->input(conn.first)) | ||||||
|  | 						for (auto bit : sig) | ||||||
|  | 							if (bit.wire) { | ||||||
|  | 								if (logic_cell) | ||||||
|  | 									topo.edge(stringf("wire %s", log_signal(bit)), | ||||||
|  | 											stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); | ||||||
|  | 								used_wires.insert(bit); | ||||||
|  | 							} | ||||||
|  | 					if (cell->output(conn.first)) | ||||||
|  | 						for (int i = 0; i < GetSize(sig); i++) { | ||||||
| 							if (logic_cell) | 							if (logic_cell) | ||||||
| 								topo.edge(stringf("wire %s", log_signal(bit)), | 								topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), | ||||||
| 										stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); | 										stringf("wire %s", log_signal(sig[i]))); | ||||||
| 							used_wires.insert(bit); | 							if (sig[i].wire) | ||||||
|  | 								wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", | ||||||
|  | 										log_id(conn.first), i, log_id(cell), log_id(cell->type))); | ||||||
| 						} | 						} | ||||||
| 				if (cell->output(conn.first)) | 					if (!cell->input(conn.first) && cell->output(conn.first)) | ||||||
| 					for (int i = 0; i < GetSize(sig); i++) { | 						for (auto bit : sig) | ||||||
| 						if (logic_cell) | 							if (bit.wire) wire_drivers_count[bit]++; | ||||||
| 							topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), | 				} | ||||||
| 									stringf("wire %s", log_signal(sig[i]))); |  | ||||||
| 						if (sig[i].wire) |  | ||||||
| 							wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", |  | ||||||
| 									log_id(conn.first), i, log_id(cell), log_id(cell->type))); |  | ||||||
| 					} |  | ||||||
| 				if (!cell->input(conn.first) && cell->output(conn.first)) |  | ||||||
| 					for (auto bit : sig) |  | ||||||
| 						if (bit.wire) wire_drivers_count[bit]++; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			pool<SigBit> init_bits; | 			pool<SigBit> init_bits; | ||||||
|  |  | ||||||
							
								
								
									
										93
									
								
								passes/cmds/portlist.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								passes/cmds/portlist.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  * | ||||||
|  |  *  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 "kernel/yosys.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
|  | 
 | ||||||
|  | USING_YOSYS_NAMESPACE | ||||||
|  | PRIVATE_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | struct PortlistPass : public Pass { | ||||||
|  | 	PortlistPass() : Pass("portlist", "list (top-level) ports") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    portlist [options] [selection]\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("This command lists all module ports found in the selected modules.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("If no selection is provided then it lists the ports on the top module.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("  -m\n"); | ||||||
|  | 		log("    print verilog blackbox module definitions instead of port lists\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		bool m_mode = false; | ||||||
|  | 
 | ||||||
|  | 		size_t argidx; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) { | ||||||
|  | 			if (args[argidx] == "-m") { | ||||||
|  | 				m_mode = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bool first_module = true; | ||||||
|  | 
 | ||||||
|  | 		auto handle_module = [&](RTLIL::Module *module) { | ||||||
|  | 			vector<string> ports; | ||||||
|  | 			if (first_module) | ||||||
|  | 				first_module = false; | ||||||
|  | 			else | ||||||
|  | 				log("\n"); | ||||||
|  | 			for (auto port : module->ports) { | ||||||
|  | 				auto *w = module->wire(port); | ||||||
|  | 				ports.push_back(stringf("%s [%d:%d] %s", w->port_input ? w->port_output ? "inout" : "input" : "output", | ||||||
|  | 						w->upto ? w->start_offset : w->start_offset + w->width - 1, | ||||||
|  | 						w->upto ? w->start_offset + w->width - 1 : w->start_offset, | ||||||
|  | 						log_id(w))); | ||||||
|  | 			} | ||||||
|  | 			log("module %s%s\n", log_id(module), m_mode ? " (" : ""); | ||||||
|  | 			for (int i = 0; i < GetSize(ports); i++) | ||||||
|  | 				log("%s%s\n", ports[i].c_str(), m_mode && i+1 < GetSize(ports) ? "," : ""); | ||||||
|  | 			if (m_mode) | ||||||
|  | 				log(");\nendmodule\n"); | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		if (argidx == args.size()) | ||||||
|  | 		{ | ||||||
|  | 			auto *top = design->top_module(); | ||||||
|  | 			if (top == nullptr) | ||||||
|  | 				log_cmd_error("Can't find top module in current design!\n"); | ||||||
|  | 			handle_module(top); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			extra_args(args, argidx, design); | ||||||
|  | 			for (auto module : design->selected_modules()) | ||||||
|  | 				handle_module(module); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } PortlistPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
|  | @ -26,6 +26,10 @@ | ||||||
| #  include <dirent.h> | #  include <dirent.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef __APPLE__ | ||||||
|  | #  include <unistd.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef YOSYS_ENABLE_READLINE | #ifdef YOSYS_ENABLE_READLINE | ||||||
| #  include <readline/readline.h> | #  include <readline/readline.h> | ||||||
| #endif | #endif | ||||||
|  | @ -866,7 +870,11 @@ struct ShowPass : public Pass { | ||||||
| 				log_cmd_error("Shell command failed!\n"); | 				log_cmd_error("Shell command failed!\n"); | ||||||
| 		} else | 		} else | ||||||
| 		if (format.empty()) { | 		if (format.empty()) { | ||||||
|  | 			#ifdef __APPLE__ | ||||||
|  | 			std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str()); | ||||||
|  | 			#else | ||||||
| 			std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); | 			std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); | ||||||
|  | 			#endif | ||||||
| 			log("Exec: %s\n", cmd.c_str()); | 			log("Exec: %s\n", cmd.c_str()); | ||||||
| 			if (run_command(cmd) != 0) | 			if (run_command(cmd) != 0) | ||||||
| 				log_cmd_error("Shell command failed!\n"); | 				log_cmd_error("Shell command failed!\n"); | ||||||
|  |  | ||||||
|  | @ -32,7 +32,8 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    equiv_opt [options] [command]\n"); | 		log("    equiv_opt [options] [command]\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("This command checks circuit equivalence before and after an optimization pass.\n"); | 		log("This command uses temporal induction to check circuit equivalence before and\n"); | ||||||
|  | 		log("after an optimization pass.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -run <from_label>:<to_label>\n"); | 		log("    -run <from_label>:<to_label>\n"); | ||||||
| 		log("        only run the commands between the labels (see below). an empty\n"); | 		log("        only run the commands between the labels (see below). an empty\n"); | ||||||
|  | @ -46,6 +47,12 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		log("    -assert\n"); | 		log("    -assert\n"); | ||||||
| 		log("        produce an error if the circuits are not equivalent.\n"); | 		log("        produce an error if the circuits are not equivalent.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -multiclock\n"); | ||||||
|  | 		log("        run clk2fflogic before equivalence checking.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -async2sync\n"); | ||||||
|  | 		log("        run async2sync before equivalence checking.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -undef\n"); | 		log("    -undef\n"); | ||||||
| 		log("        enable modelling of undef states during equiv_induct.\n"); | 		log("        enable modelling of undef states during equiv_induct.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -55,7 +62,7 @@ struct EquivOptPass:public ScriptPass | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::string command, techmap_opts; | 	std::string command, techmap_opts; | ||||||
| 	bool assert, undef; | 	bool assert, undef, multiclock, async2sync; | ||||||
| 
 | 
 | ||||||
| 	void clear_flags() YS_OVERRIDE | 	void clear_flags() YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  | @ -63,6 +70,8 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		techmap_opts = ""; | 		techmap_opts = ""; | ||||||
| 		assert = false; | 		assert = false; | ||||||
| 		undef = false; | 		undef = false; | ||||||
|  | 		multiclock = false; | ||||||
|  | 		async2sync = false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE | 	void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE | ||||||
|  | @ -92,6 +101,14 @@ struct EquivOptPass:public ScriptPass | ||||||
| 				undef = true; | 				undef = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-multiclock") { | ||||||
|  | 				multiclock = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (args[argidx] == "-async2sync") { | ||||||
|  | 				async2sync = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -111,6 +128,9 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		if (!design->full_selection()) | 		if (!design->full_selection()) | ||||||
| 			log_cmd_error("This command only operates on fully selected designs!\n"); | 			log_cmd_error("This command only operates on fully selected designs!\n"); | ||||||
| 
 | 
 | ||||||
|  | 		if (async2sync && multiclock) | ||||||
|  | 			log_cmd_error("The '-async2sync' and '-multiclock' options are mutually exclusive!\n"); | ||||||
|  | 
 | ||||||
| 		log_header(design, "Executing EQUIV_OPT pass.\n"); | 		log_header(design, "Executing EQUIV_OPT pass.\n"); | ||||||
| 		log_push(); | 		log_push(); | ||||||
| 
 | 
 | ||||||
|  | @ -146,6 +166,10 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (check_label("prove")) { | 		if (check_label("prove")) { | ||||||
|  | 			if (multiclock || help_mode) | ||||||
|  | 				run("clk2fflogic", "(only with -multiclock)"); | ||||||
|  | 			if (async2sync || help_mode) | ||||||
|  | 				run("async2sync", " (only with -async2sync)"); | ||||||
| 			run("equiv_make gold gate equiv"); | 			run("equiv_make gold gate equiv"); | ||||||
| 			if (help_mode) | 			if (help_mode) | ||||||
| 				run("equiv_induct [-undef] equiv"); | 				run("equiv_induct [-undef] equiv"); | ||||||
|  |  | ||||||
|  | @ -808,6 +808,30 @@ struct HierarchyPass : public Pass { | ||||||
| 				if (mod_it.second->get_bool_attribute("\\top")) | 				if (mod_it.second->get_bool_attribute("\\top")) | ||||||
| 					top_mod = mod_it.second; | 					top_mod = mod_it.second; | ||||||
| 
 | 
 | ||||||
|  | 		if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) { | ||||||
|  | 			IdString top_name = top_mod->name.substr(strlen("$abstract")); | ||||||
|  | 
 | ||||||
|  | 			dict<RTLIL::IdString, RTLIL::Const> top_parameters; | ||||||
|  | 			for (auto ¶ : parameters) { | ||||||
|  | 				SigSpec sig_value; | ||||||
|  | 				if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) | ||||||
|  | 					log_cmd_error("Can't decode value '%s'!\n", para.second.c_str()); | ||||||
|  | 				top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			top_mod = design->module(top_mod->derive(design, top_parameters)); | ||||||
|  | 
 | ||||||
|  | 			if (top_mod != nullptr && top_mod->name != top_name) { | ||||||
|  | 				Module *m = top_mod->clone(); | ||||||
|  | 				m->name = top_name; | ||||||
|  | 				Module *old_mod = design->module(top_name); | ||||||
|  | 				if (old_mod) | ||||||
|  | 					design->remove(old_mod); | ||||||
|  | 				design->add(m); | ||||||
|  | 				top_mod = m; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (top_mod == nullptr && auto_top_mode) { | 		if (top_mod == nullptr && auto_top_mode) { | ||||||
| 			log_header(design, "Finding top of design hierarchy..\n"); | 			log_header(design, "Finding top of design hierarchy..\n"); | ||||||
| 			dict<Module*, int> db; | 			dict<Module*, int> db; | ||||||
|  |  | ||||||
|  | @ -953,6 +953,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (b.is_fully_const()) { | 			if (b.is_fully_const()) { | ||||||
|  | 				if (b.is_fully_undef()) { | ||||||
|  | 					RTLIL::SigSpec input = b; | ||||||
|  | 					ACTION_DO(ID::Y, Const(State::Sx, GetSize(cell->getPort(ID::Y)))); | ||||||
|  | 				} else | ||||||
| 				if (b.as_bool() == (cell->type == ID($eq))) { | 				if (b.as_bool() == (cell->type == ID($eq))) { | ||||||
| 					RTLIL::SigSpec input = b; | 					RTLIL::SigSpec input = b; | ||||||
| 					ACTION_DO(ID::Y, cell->getPort(ID::A)); | 					ACTION_DO(ID::Y, cell->getPort(ID::A)); | ||||||
|  |  | ||||||
|  | @ -108,12 +108,13 @@ bool cell_supported(RTLIL::Cell *cell) | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::map<IdString, IdString> mergeable_type_map{ | std::map<IdString, IdString> mergeable_type_map; | ||||||
|   {ID($sub), ID($add)}, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b) | bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b) | ||||||
| { | { | ||||||
|  | 	if (mergeable_type_map.empty()) { | ||||||
|  | 		mergeable_type_map.insert({ID($sub), ID($add)}); | ||||||
|  | 	} | ||||||
| 	auto a_type = a->type; | 	auto a_type = a->type; | ||||||
| 	if (mergeable_type_map.count(a_type)) | 	if (mergeable_type_map.count(a_type)) | ||||||
| 		a_type = mergeable_type_map.at(a_type); | 		a_type = mergeable_type_map.at(a_type); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| %_pm.h: passes/pmgen/pmgen.py %.pmg | %_pm.h: passes/pmgen/pmgen.py %.pmg | ||||||
| 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^) | 	$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^) | ||||||
| 
 | 
 | ||||||
| # --------------------------------------
 | # --------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  | @ -21,15 +21,24 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) | ||||||
| 
 | 
 | ||||||
| # --------------------------------------
 | # --------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  | OBJS += passes/pmgen/xilinx_dsp.o | ||||||
|  | passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h | ||||||
|  | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) | ||||||
|  | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h)) | ||||||
|  | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) | ||||||
|  | 
 | ||||||
|  | # --------------------------------------
 | ||||||
|  | 
 | ||||||
| OBJS += passes/pmgen/peepopt.o | OBJS += passes/pmgen/peepopt.o | ||||||
| passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h | passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h | ||||||
| $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) | $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) | ||||||
| 
 | 
 | ||||||
| PEEPOPT_PATTERN  = passes/pmgen/peepopt_shiftmul.pmg | PEEPOPT_PATTERN  = passes/pmgen/peepopt_shiftmul.pmg | ||||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | ||||||
|  | PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg | ||||||
| 
 | 
 | ||||||
| passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | ||||||
| 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) | 	$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) | ||||||
| 
 | 
 | ||||||
| # --------------------------------------
 | # --------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -190,7 +190,7 @@ create matches for different sections of a cell. For example: | ||||||
|         select pmux->type == $pmux |         select pmux->type == $pmux | ||||||
|         slice idx GetSize(port(pmux, \S)) |         slice idx GetSize(port(pmux, \S)) | ||||||
|         index <SigBit> port(pmux, \S)[idx] === port(eq, \Y) |         index <SigBit> port(pmux, \S)[idx] === port(eq, \Y) | ||||||
| 	set pmux_slice idx |         set pmux_slice idx | ||||||
|     endmatch |     endmatch | ||||||
| 
 | 
 | ||||||
| The first argument to `slice` is the local variable name used to identify the | The first argument to `slice` is the local variable name used to identify the | ||||||
|  | @ -352,7 +352,7 @@ state variables used to pass arguments. | ||||||
|     subpattern tail |     subpattern tail | ||||||
|     ... |     ... | ||||||
| 
 | 
 | ||||||
| Subpatterns cann be called recursively. | Subpatterns can be called recursively. | ||||||
| 
 | 
 | ||||||
| If a `subpattern` statement is preceded by a `fallthrough` statement, this is | If a `subpattern` statement is preceded by a `fallthrough` statement, this is | ||||||
| equivalent to calling the subpattern at the end of the preceding block. | equivalent to calling the subpattern at the end of the preceding block. | ||||||
|  |  | ||||||
							
								
								
									
										140
									
								
								passes/pmgen/generate.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								passes/pmgen/generate.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,140 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  * | ||||||
|  |  *  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. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef PMGEN_GENERATE | ||||||
|  | #define PMGEN_GENERATE | ||||||
|  | 
 | ||||||
|  | #define GENERATE_PATTERN(pmclass, pattern) \ | ||||||
|  | 	generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design) | ||||||
|  | 
 | ||||||
|  | void pmtest_addports(Module *module) | ||||||
|  | { | ||||||
|  | 	pool<SigBit> driven_bits, used_bits; | ||||||
|  | 	SigMap sigmap(module); | ||||||
|  | 	int icnt = 0, ocnt = 0; | ||||||
|  | 
 | ||||||
|  | 	for (auto cell : module->cells()) | ||||||
|  | 	for (auto conn : cell->connections()) | ||||||
|  | 	{ | ||||||
|  | 		if (cell->input(conn.first)) | ||||||
|  | 			for (auto bit : sigmap(conn.second)) | ||||||
|  | 				used_bits.insert(bit); | ||||||
|  | 		if (cell->output(conn.first)) | ||||||
|  | 			for (auto bit : sigmap(conn.second)) | ||||||
|  | 				driven_bits.insert(bit); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (auto wire : vector<Wire*>(module->wires())) | ||||||
|  | 	{ | ||||||
|  | 		SigSpec ibits, obits; | ||||||
|  | 		for (auto bit : sigmap(wire)) { | ||||||
|  | 			if (!used_bits.count(bit)) | ||||||
|  | 				obits.append(bit); | ||||||
|  | 			if (!driven_bits.count(bit)) | ||||||
|  | 				ibits.append(bit); | ||||||
|  | 		} | ||||||
|  | 		if (!ibits.empty()) { | ||||||
|  | 			Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits)); | ||||||
|  | 			w->port_input = true; | ||||||
|  | 			module->connect(ibits, w); | ||||||
|  | 		} | ||||||
|  | 		if (!obits.empty()) { | ||||||
|  | 			Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits)); | ||||||
|  | 			w->port_output = true; | ||||||
|  | 			module->connect(w, obits); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	module->fixup_ports(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <class pm> | ||||||
|  | void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design) | ||||||
|  | { | ||||||
|  | 	log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); | ||||||
|  | 
 | ||||||
|  | 	int modcnt = 0; | ||||||
|  | 	int maxmodcnt = 100; | ||||||
|  | 	int maxsubcnt = 4; | ||||||
|  | 	int timeout = 0; | ||||||
|  | 	vector<Module*> mods; | ||||||
|  | 
 | ||||||
|  | 	while (modcnt < maxmodcnt) | ||||||
|  | 	{ | ||||||
|  | 		int submodcnt = 0, itercnt = 0, cellcnt = 0; | ||||||
|  | 		Module *mod = design->addModule(NEW_ID); | ||||||
|  | 
 | ||||||
|  | 		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000) | ||||||
|  | 		{ | ||||||
|  | 			if (timeout++ > 10000) | ||||||
|  | 				log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n"); | ||||||
|  | 
 | ||||||
|  | 			pm matcher(mod, mod->cells()); | ||||||
|  | 
 | ||||||
|  | 			matcher.rng(1); | ||||||
|  | 			matcher.rngseed += modcnt; | ||||||
|  | 			matcher.rng(1); | ||||||
|  | 			matcher.rngseed += submodcnt; | ||||||
|  | 			matcher.rng(1); | ||||||
|  | 			matcher.rngseed += itercnt; | ||||||
|  | 			matcher.rng(1); | ||||||
|  | 			matcher.rngseed += cellcnt; | ||||||
|  | 			matcher.rng(1); | ||||||
|  | 
 | ||||||
|  | 			if (GetSize(mod->cells()) != cellcnt) | ||||||
|  | 			{ | ||||||
|  | 				bool found_match = false; | ||||||
|  | 				run(matcher, [&](){ found_match = true; }); | ||||||
|  | 				cellcnt = GetSize(mod->cells()); | ||||||
|  | 
 | ||||||
|  | 				if (found_match) { | ||||||
|  | 					Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d", | ||||||
|  | 							pmclass, pattern, modcnt++)); | ||||||
|  | 					log("Creating module %s with %d cells.\n", log_id(m), cellcnt); | ||||||
|  | 					mod->cloneInto(m); | ||||||
|  | 					pmtest_addports(m); | ||||||
|  | 					mods.push_back(m); | ||||||
|  | 					submodcnt++; | ||||||
|  | 					timeout = 0; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			matcher.generate_mode = true; | ||||||
|  | 			run(matcher, [](){}); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (submodcnt && maxsubcnt < (1 << 16)) | ||||||
|  | 			maxsubcnt *= 2; | ||||||
|  | 
 | ||||||
|  | 		design->remove(mod); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern)); | ||||||
|  | 	log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods)); | ||||||
|  | 	for (auto mod : mods) { | ||||||
|  | 		Cell *c = m->addCell(mod->name, mod->name); | ||||||
|  | 		for (auto port : mod->ports) { | ||||||
|  | 			Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port))); | ||||||
|  | 			c->setPort(port, w); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pmtest_addports(m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -29,19 +29,19 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | ||||||
| { | { | ||||||
| 	auto &st = pm.st_ice40_dsp; | 	auto &st = pm.st_ice40_dsp; | ||||||
| 
 | 
 | ||||||
| #if 0 |  | ||||||
| 	log("\n"); |  | ||||||
| 	log("ffA:   %s\n", log_id(st.ffA, "--")); |  | ||||||
| 	log("ffB:   %s\n", log_id(st.ffB, "--")); |  | ||||||
| 	log("mul:   %s\n", log_id(st.mul, "--")); |  | ||||||
| 	log("ffY:   %s\n", log_id(st.ffY, "--")); |  | ||||||
| 	log("addAB: %s\n", log_id(st.addAB, "--")); |  | ||||||
| 	log("muxAB: %s\n", log_id(st.muxAB, "--")); |  | ||||||
| 	log("ffS:   %s\n", log_id(st.ffS, "--")); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); | 	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); | ||||||
| 
 | 
 | ||||||
|  | 	log_debug("ffA:    %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--")); | ||||||
|  | 	log_debug("ffB:    %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--")); | ||||||
|  | 	log_debug("ffCD:   %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--")); | ||||||
|  | 	log_debug("mul:    %s\n", log_id(st.mul, "--")); | ||||||
|  | 	log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--")); | ||||||
|  | 	log_debug("ffH:    %s\n", log_id(st.ffH, "--")); | ||||||
|  | 	log_debug("add:    %s\n", log_id(st.add, "--")); | ||||||
|  | 	log_debug("mux:    %s\n", log_id(st.mux, "--")); | ||||||
|  | 	log_debug("ffO:    %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--")); | ||||||
|  | 	log_debug("\n"); | ||||||
|  | 
 | ||||||
| 	if (GetSize(st.sigA) > 16) { | 	if (GetSize(st.sigA) > 16) { | ||||||
| 		log("  input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); | 		log("  input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); | ||||||
| 		return; | 		return; | ||||||
|  | @ -52,59 +52,85 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (GetSize(st.sigS) > 32) { | 	if (GetSize(st.sigO) > 33) { | ||||||
| 		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); | 		log("  adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO)); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (GetSize(st.sigY) > 32) { | 	if (GetSize(st.sigH) > 32) { | ||||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); | 		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH)); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool(); | 	Cell *cell = st.mul; | ||||||
|  | 	if (cell->type == ID($mul)) { | ||||||
|  | 		log("  replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type)); | ||||||
| 
 | 
 | ||||||
| 	log("  replacing $mul with SB_MAC16 cell.\n"); | 		cell = pm.module->addCell(NEW_ID, ID(SB_MAC16)); | ||||||
| 
 | 		pm.module->swap_names(cell, st.mul); | ||||||
| 	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); | 	} | ||||||
| 	pm.module->swap_names(cell, st.mul); | 	else log_assert(cell->type == ID(SB_MAC16)); | ||||||
| 
 | 
 | ||||||
| 	// SB_MAC16 Input Interface
 | 	// SB_MAC16 Input Interface
 | ||||||
| 
 |  | ||||||
| 	SigSpec A = st.sigA; | 	SigSpec A = st.sigA; | ||||||
| 	A.extend_u0(16, mul_signed); | 	A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool()); | ||||||
|  | 	log_assert(GetSize(A) == 16); | ||||||
| 
 | 
 | ||||||
| 	SigSpec B = st.sigB; | 	SigSpec B = st.sigB; | ||||||
| 	B.extend_u0(16, mul_signed); | 	B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool()); | ||||||
|  | 	log_assert(GetSize(B) == 16); | ||||||
| 
 | 
 | ||||||
| 	SigSpec CD; | 	SigSpec CD = st.sigCD; | ||||||
| 	if (st.muxA) | 	if (CD.empty()) | ||||||
| 		CD = st.muxA->getPort("\\B"); | 		CD = RTLIL::Const(0, 32); | ||||||
| 	if (st.muxB) | 	else | ||||||
| 		CD = st.muxB->getPort("\\A"); | 		log_assert(GetSize(CD) == 32); | ||||||
| 	CD.extend_u0(32, mul_signed); |  | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\A", A); | 	cell->setPort(ID::A, A); | ||||||
| 	cell->setPort("\\B", B); | 	cell->setPort(ID::B, B); | ||||||
| 	cell->setPort("\\C", CD.extract(0, 16)); | 	cell->setPort(ID(C), CD.extract(16, 16)); | ||||||
| 	cell->setPort("\\D", CD.extract(16, 16)); | 	cell->setPort(ID(D), CD.extract(0, 16)); | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); | 	cell->setParam(ID(A_REG), st.ffA ? State::S1 : State::S0); | ||||||
| 	cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); | 	cell->setParam(ID(B_REG), st.ffB ? State::S1 : State::S0); | ||||||
|  | 	cell->setParam(ID(C_REG), st.ffCD ? State::S1 : State::S0); | ||||||
|  | 	cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0); | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\AHOLD", State::S0); | 	SigSpec AHOLD, BHOLD, CDHOLD; | ||||||
| 	cell->setPort("\\BHOLD", State::S0); | 	if (st.ffAholdmux) | ||||||
| 	cell->setPort("\\CHOLD", State::S0); | 		AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S))); | ||||||
| 	cell->setPort("\\DHOLD", State::S0); | 	else | ||||||
|  | 		AHOLD = State::S0; | ||||||
|  | 	if (st.ffBholdmux) | ||||||
|  | 		BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		BHOLD = State::S0; | ||||||
|  | 	if (st.ffCDholdmux) | ||||||
|  | 		CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		CDHOLD = State::S0; | ||||||
|  | 	cell->setPort(ID(AHOLD), AHOLD); | ||||||
|  | 	cell->setPort(ID(BHOLD), BHOLD); | ||||||
|  | 	cell->setPort(ID(CHOLD), CDHOLD); | ||||||
|  | 	cell->setPort(ID(DHOLD), CDHOLD); | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\IRSTTOP", State::S0); | 	SigSpec IRSTTOP, IRSTBOT; | ||||||
| 	cell->setPort("\\IRSTBOT", State::S0); | 	if (st.ffArstmux) | ||||||
|  | 		IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		IRSTTOP = State::S0; | ||||||
|  | 	if (st.ffBrstmux) | ||||||
|  | 		IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		IRSTBOT = State::S0; | ||||||
|  | 	cell->setPort(ID(IRSTTOP), IRSTTOP); | ||||||
|  | 	cell->setPort(ID(IRSTBOT), IRSTBOT); | ||||||
| 
 | 
 | ||||||
| 	if (st.clock_vld) | 	if (st.clock != SigBit()) | ||||||
| 	{ | 	{ | ||||||
| 		cell->setPort("\\CLK", st.clock); | 		cell->setPort(ID(CLK), st.clock); | ||||||
| 		cell->setPort("\\CE", State::S1); | 		cell->setPort(ID(CE), State::S1); | ||||||
| 		cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1); | 		cell->setParam(ID(NEG_TRIGGER), st.clock_pol ? State::S0 : State::S1); | ||||||
| 
 | 
 | ||||||
| 		log("  clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge"); | 		log("  clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge"); | ||||||
| 
 | 
 | ||||||
|  | @ -114,91 +140,137 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | ||||||
| 		if (st.ffB) | 		if (st.ffB) | ||||||
| 			log(" ffB:%s", log_id(st.ffB)); | 			log(" ffB:%s", log_id(st.ffB)); | ||||||
| 
 | 
 | ||||||
| 		if (st.ffY) | 		if (st.ffCD) | ||||||
| 			log(" ffY:%s", log_id(st.ffY)); | 			log(" ffCD:%s", log_id(st.ffCD)); | ||||||
| 
 | 
 | ||||||
| 		if (st.ffS) | 		if (st.ffFJKG) | ||||||
| 			log(" ffS:%s", log_id(st.ffS)); | 			log(" ffFJKG:%s", log_id(st.ffFJKG)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffH) | ||||||
|  | 			log(" ffH:%s", log_id(st.ffH)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffO) | ||||||
|  | 			log(" ffO:%s", log_id(st.ffO)); | ||||||
| 
 | 
 | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		cell->setPort("\\CLK", State::S0); | 		cell->setPort(ID(CLK), State::S0); | ||||||
| 		cell->setPort("\\CE", State::S0); | 		cell->setPort(ID(CE), State::S0); | ||||||
| 		cell->setParam("\\NEG_TRIGGER", State::S0); | 		cell->setParam(ID(NEG_TRIGGER), State::S0); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// SB_MAC16 Cascade Interface
 | 	// SB_MAC16 Cascade Interface
 | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\SIGNEXTIN", State::Sx); | 	cell->setPort(ID(SIGNEXTIN), State::Sx); | ||||||
| 	cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); | 	cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID)); | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\CI", State::Sx); | 	cell->setPort(ID(CI), State::Sx); | ||||||
| 	cell->setPort("\\CO", pm.module->addWire(NEW_ID)); |  | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\ACCUMCI", State::Sx); | 	cell->setPort(ID(ACCUMCI), State::Sx); | ||||||
| 	cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); | 	cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID)); | ||||||
| 
 | 
 | ||||||
| 	// SB_MAC16 Output Interface
 | 	// SB_MAC16 Output Interface
 | ||||||
| 
 | 
 | ||||||
| 	SigSpec O = st.ffS ? st.sigS : st.sigY; | 	SigSpec O = st.sigO; | ||||||
|  | 	int O_width = GetSize(O); | ||||||
|  | 	if (O_width == 33) { | ||||||
|  | 		log_assert(st.add); | ||||||
|  | 		// If we have a signed multiply-add, then perform sign extension
 | ||||||
|  | 		if (st.add->getParam(ID(A_SIGNED)).as_bool() && st.add->getParam(ID(B_SIGNED)).as_bool()) | ||||||
|  | 			pm.module->connect(O[32], O[31]); | ||||||
|  | 		else | ||||||
|  | 			cell->setPort(ID(CO), O[32]); | ||||||
|  | 		O.remove(O_width-1); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		cell->setPort(ID(CO), pm.module->addWire(NEW_ID)); | ||||||
|  | 	log_assert(GetSize(O) <= 32); | ||||||
| 	if (GetSize(O) < 32) | 	if (GetSize(O) < 32) | ||||||
| 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); | 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\O", O); | 	cell->setPort(ID(O), O); | ||||||
| 
 | 
 | ||||||
| 	if (st.addAB) { | 	bool accum = false; | ||||||
| 		log("  accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); | 	if (st.add) { | ||||||
| 		cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); | 		accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO); | ||||||
| 		cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); | 		if (accum) | ||||||
|  | 			log("  accumulator %s (%s)\n", log_id(st.add), log_id(st.add->type)); | ||||||
|  | 		else | ||||||
|  | 			log("  adder %s (%s)\n", log_id(st.add), log_id(st.add->type)); | ||||||
|  | 		cell->setPort(ID(ADDSUBTOP), st.add->type == ID($add) ? State::S0 : State::S1); | ||||||
|  | 		cell->setPort(ID(ADDSUBBOT), st.add->type == ID($add) ? State::S0 : State::S1); | ||||||
| 	} else { | 	} else { | ||||||
| 		cell->setPort("\\ADDSUBTOP", State::S0); | 		cell->setPort(ID(ADDSUBTOP), State::S0); | ||||||
| 		cell->setPort("\\ADDSUBBOT", State::S0); | 		cell->setPort(ID(ADDSUBBOT), State::S0); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\ORSTTOP", State::S0); | 	SigSpec OHOLD; | ||||||
| 	cell->setPort("\\ORSTBOT", State::S0); | 	if (st.ffOholdmux) | ||||||
|  | 		OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		OHOLD = State::S0; | ||||||
|  | 	cell->setPort(ID(OHOLDTOP), OHOLD); | ||||||
|  | 	cell->setPort(ID(OHOLDBOT), OHOLD); | ||||||
| 
 | 
 | ||||||
| 	cell->setPort("\\OHOLDTOP", State::S0); | 	SigSpec ORST; | ||||||
| 	cell->setPort("\\OHOLDBOT", State::S0); | 	if (st.ffOrstmux) | ||||||
|  | 		ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID(S))); | ||||||
|  | 	else | ||||||
|  | 		ORST = State::S0; | ||||||
|  | 	cell->setPort(ID(ORSTTOP), ORST); | ||||||
|  | 	cell->setPort(ID(ORSTBOT), ORST); | ||||||
| 
 | 
 | ||||||
| 	SigSpec acc_reset = State::S0; | 	SigSpec acc_reset = State::S0; | ||||||
| 	if (st.muxA) | 	if (st.mux) { | ||||||
| 		acc_reset = st.muxA->getPort("\\S"); | 		if (st.muxAB == ID::A) | ||||||
| 	if (st.muxB) | 			acc_reset = st.mux->getPort(ID(S)); | ||||||
| 		acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S")); | 		else | ||||||
| 
 | 			acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S))); | ||||||
| 	cell->setPort("\\OLOADTOP", acc_reset); | 	} | ||||||
| 	cell->setPort("\\OLOADBOT", acc_reset); | 	cell->setPort(ID(OLOADTOP), acc_reset); | ||||||
|  | 	cell->setPort(ID(OLOADBOT), acc_reset); | ||||||
| 
 | 
 | ||||||
| 	// SB_MAC16 Remaining Parameters
 | 	// SB_MAC16 Remaining Parameters
 | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\C_REG", State::S0); | 	cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0); | ||||||
| 	cell->setParam("\\D_REG", State::S0); | 	cell->setParam(ID(BOT_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0); | ||||||
|  | 	cell->setParam(ID(PIPELINE_16x16_MULT_REG1), st.ffFJKG ? State::S1 : State::S0); | ||||||
|  | 	cell->setParam(ID(PIPELINE_16x16_MULT_REG2), st.ffH ? State::S1 : State::S0); | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); | 	cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2)); | ||||||
| 	cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); | 	cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1); | ||||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0); | 	cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2)); | ||||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); |  | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | 	cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2)); | ||||||
| 	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); | 	cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1); | ||||||
| 	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); | 	cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2)); | ||||||
| 	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); |  | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | 	cell->setParam(ID(MODE_8x8), State::S0); | ||||||
| 	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); | 	cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool()); | ||||||
| 	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); | 	cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool()); | ||||||
| 	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); |  | ||||||
| 
 | 
 | ||||||
| 	cell->setParam("\\MODE_8x8", State::S0); | 	if (st.ffO) { | ||||||
| 	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); | 		if (st.o_lo) | ||||||
| 	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); | 			cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2)); | ||||||
|  | 		else | ||||||
|  | 			cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2)); | ||||||
| 
 | 
 | ||||||
| 	pm.autoremove(st.mul); | 		st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O))); | ||||||
| 	pm.autoremove(st.ffY); | 		cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2)); | ||||||
| 	pm.autoremove(st.ffS); | 	} | ||||||
|  | 	else { | ||||||
|  | 		cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2)); | ||||||
|  | 		cell->setParam(ID(BOTOUTPUT_SELECT), Const(st.add ? 0 : 3, 2)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (cell != st.mul) | ||||||
|  | 		pm.autoremove(st.mul); | ||||||
|  | 	else | ||||||
|  | 		pm.blacklist(st.mul); | ||||||
|  | 	pm.autoremove(st.ffFJKG); | ||||||
|  | 	pm.autoremove(st.add); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Ice40DspPass : public Pass { | struct Ice40DspPass : public Pass { | ||||||
|  | @ -209,7 +281,17 @@ struct Ice40DspPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    ice40_dsp [options] [selection]\n"); | 		log("    ice40_dsp [options] [selection]\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n"); | 		log("Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n"); | ||||||
|  | 		log("cells into iCE40 DSP resources.\n"); | ||||||
|  | 		log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n"); | ||||||
|  | 		log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n"); | ||||||
|  | 		log("optional hold), and post-adder into into the SB_MAC16 resource.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n"); | ||||||
|  | 		log("input will be folded into the DSP. In this scenario only, resetting the\n"); | ||||||
|  | 		log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  |  | ||||||
|  | @ -1,163 +1,574 @@ | ||||||
| pattern ice40_dsp | pattern ice40_dsp | ||||||
| 
 | 
 | ||||||
| state <SigBit> clock | state <SigBit> clock | ||||||
| state <bool> clock_pol clock_vld | state <bool> clock_pol cd_signed o_lo | ||||||
| state <SigSpec> sigA sigB sigY sigS | state <SigSpec> sigA sigB sigCD sigH sigO | ||||||
| state <Cell*> addAB muxAB | state <Cell*> add mux | ||||||
|  | state <IdString> addAB muxAB | ||||||
|  | 
 | ||||||
|  | state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol | ||||||
|  | state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol | ||||||
|  | 
 | ||||||
|  | state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux | ||||||
|  | state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux | ||||||
|  | 
 | ||||||
|  | // subpattern | ||||||
|  | state <SigSpec> argQ argD | ||||||
|  | state <bool> ffholdpol ffrstpol | ||||||
|  | state <int> ffoffset | ||||||
|  | udata <SigSpec> dffD dffQ | ||||||
|  | udata <SigBit> dffclock | ||||||
|  | udata <Cell*> dff dffholdmux dffrstmux | ||||||
|  | udata <bool> dffholdpol dffrstpol dffclock_pol | ||||||
| 
 | 
 | ||||||
| match mul | match mul | ||||||
| 	select mul->type.in($mul) | 	select mul->type.in($mul, \SB_MAC16) | ||||||
| 	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 | 	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 | ||||||
| 	select GetSize(mul->getPort(\Y)) > 10 |  | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| match ffA | code sigA sigB sigH | ||||||
| 	select ffA->type.in($dff) | 	auto unextend = [](const SigSpec &sig) { | ||||||
| 	// select nusers(port(ffA, \Q)) == 2 | 		int i; | ||||||
| 	index <SigSpec> port(ffA, \Q) === port(mul, \A) | 		for (i = GetSize(sig)-1; i > 0; i--) | ||||||
| 	optional | 			if (sig[i] != sig[i-1]) | ||||||
| endmatch | 				break; | ||||||
|  | 		// Do not remove non-const sign bit | ||||||
|  | 		if (sig[i].wire) | ||||||
|  | 			++i; | ||||||
|  | 		return sig.extract(0, i); | ||||||
|  | 	}; | ||||||
|  | 	sigA = unextend(port(mul, \A)); | ||||||
|  | 	sigB = unextend(port(mul, \B)); | ||||||
| 
 | 
 | ||||||
| code sigA clock clock_pol clock_vld | 	SigSpec O; | ||||||
| 	sigA = port(mul, \A); | 	if (mul->type == $mul) | ||||||
|  | 		O = mul->getPort(\Y); | ||||||
|  | 	else if (mul->type == \SB_MAC16) | ||||||
|  | 		O = mul->getPort(\O); | ||||||
|  | 	else log_abort(); | ||||||
|  | 	if (GetSize(O) <= 10) | ||||||
|  | 		reject; | ||||||
| 
 | 
 | ||||||
| 	if (ffA) { | 	// Only care about those bits that are used | ||||||
| 		sigA = port(ffA, \D); | 	int i; | ||||||
|  | 	for (i = 0; i < GetSize(O); i++) { | ||||||
|  | 		if (nusers(O[i]) <= 1) | ||||||
|  | 			break; | ||||||
|  | 		sigH.append(O[i]); | ||||||
|  | 	} | ||||||
|  | 	log_assert(nusers(O.extract_end(i)) <= 1); | ||||||
|  | endcode | ||||||
| 
 | 
 | ||||||
| 		clock = port(ffA, \CLK).as_bit(); | code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol | ||||||
| 		clock_pol = param(ffA, \CLK_POLARITY).as_bool(); | 	if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { | ||||||
| 		clock_vld = true; | 		argQ = sigA; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffA = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			clock_pol = dffclock_pol; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffArstmux = dffrstmux; | ||||||
|  | 				ffArstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffholdmux) { | ||||||
|  | 				ffAholdmux = dffholdmux; | ||||||
|  | 				ffAholdpol = dffholdpol; | ||||||
|  | 			} | ||||||
|  | 			sigA = dffD; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| match ffB | code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol | ||||||
| 	select ffB->type.in($dff) | 	if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { | ||||||
| 	// select nusers(port(ffB, \Q)) == 2 | 		argQ = sigB; | ||||||
| 	index <SigSpec> port(ffB, \Q) === port(mul, \B) | 		subpattern(in_dffe); | ||||||
| 	optional | 		if (dff) { | ||||||
| endmatch | 			ffB = dff; | ||||||
| 
 | 			clock = dffclock; | ||||||
| code sigB clock clock_pol clock_vld | 			clock_pol = dffclock_pol; | ||||||
| 	sigB = port(mul, \B); | 			if (dffrstmux) { | ||||||
| 
 | 				ffBrstmux = dffrstmux; | ||||||
| 	if (ffB) { | 				ffBrstpol = dffrstpol; | ||||||
| 		sigB = port(ffB, \D); | 			} | ||||||
| 		SigBit c = port(ffB, \CLK).as_bit(); | 			if (dffholdmux) { | ||||||
| 		bool cp = param(ffB, \CLK_POLARITY).as_bool(); | 				ffBholdmux = dffholdmux; | ||||||
| 
 | 				ffBholdpol = dffholdpol; | ||||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | 			} | ||||||
| 			reject; | 			sigB = dffD; | ||||||
| 
 | 		} | ||||||
| 		clock = c; |  | ||||||
| 		clock_pol = cp; |  | ||||||
| 		clock_vld = true; |  | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| match ffY | code argD ffFJKG sigH clock clock_pol | ||||||
| 	select ffY->type.in($dff) | 	if (nusers(sigH) == 2 && | ||||||
| 	select nusers(port(ffY, \D)) == 2 | 			(mul->type != \SB_MAC16 || | ||||||
| 	index <SigSpec> port(ffY, \D) === port(mul, \Y) | 			 (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) { | ||||||
| 	optional | 		argD = sigH; | ||||||
| endmatch | 		subpattern(out_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			// F/J/K/G do not have a CE-like (hold) input | ||||||
|  | 			if (dffholdmux) | ||||||
|  | 				goto reject_ffFJKG; | ||||||
| 
 | 
 | ||||||
| code sigY clock clock_pol clock_vld | 			// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT) | ||||||
| 	sigY = port(mul, \Y); | 			//   shared with A and B | ||||||
|  | 			if ((ffArstmux != NULL) != (dffrstmux != NULL)) | ||||||
|  | 				goto reject_ffFJKG; | ||||||
|  | 			if ((ffBrstmux != NULL) != (dffrstmux != NULL)) | ||||||
|  | 				goto reject_ffFJKG; | ||||||
|  | 			if (ffArstmux) { | ||||||
|  | 				if (port(ffArstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto reject_ffFJKG; | ||||||
|  | 				if (ffArstpol != dffrstpol) | ||||||
|  | 					goto reject_ffFJKG; | ||||||
|  | 			} | ||||||
|  | 			if (ffBrstmux) { | ||||||
|  | 				if (port(ffBrstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto reject_ffFJKG; | ||||||
|  | 				if (ffBrstpol != dffrstpol) | ||||||
|  | 					goto reject_ffFJKG; | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 	if (ffY) { | 			ffFJKG = dff; | ||||||
| 		sigY = port(ffY, \Q); | 			clock = dffclock; | ||||||
| 		SigBit c = port(ffY, \CLK).as_bit(); | 			clock_pol = dffclock_pol; | ||||||
| 		bool cp = param(ffY, \CLK_POLARITY).as_bool(); | 			sigH = dffQ; | ||||||
| 
 | 
 | ||||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | reject_ffFJKG: 		; | ||||||
| 			reject; | 		} | ||||||
| 
 |  | ||||||
| 		clock = c; |  | ||||||
| 		clock_pol = cp; |  | ||||||
| 		clock_vld = true; |  | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| match addA | code argD ffH sigH sigO clock clock_pol | ||||||
| 	select addA->type.in($add) | 	if (ffFJKG && nusers(sigH) == 2 && | ||||||
| 	select nusers(port(addA, \A)) == 2 | 			(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { | ||||||
| 	index <SigSpec> port(addA, \A) === sigY | 		argD = sigH; | ||||||
|  | 		subpattern(out_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			// H does not have a CE-like (hold) input | ||||||
|  | 			if (dffholdmux) | ||||||
|  | 				goto reject_ffH; | ||||||
|  | 
 | ||||||
|  | 			// Reset signal of H (IRSTBOT) shared with B | ||||||
|  | 			if ((ffBrstmux != NULL) != (dffrstmux != NULL)) | ||||||
|  | 				goto reject_ffH; | ||||||
|  | 			if (ffBrstmux) { | ||||||
|  | 				if (port(ffBrstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto reject_ffH; | ||||||
|  | 				if (ffBrstpol != dffrstpol) | ||||||
|  | 					goto reject_ffH; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			ffH = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			clock_pol = dffclock_pol; | ||||||
|  | 			sigH = dffQ; | ||||||
|  | 
 | ||||||
|  | reject_ffH:		; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sigO = sigH; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match add | ||||||
|  | 	if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3) | ||||||
|  | 
 | ||||||
|  | 	select add->type.in($add) | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	select nusers(port(add, AB)) == 2 | ||||||
|  | 
 | ||||||
|  | 	index <SigBit> port(add, AB)[0] === sigH[0] | ||||||
|  | 	filter GetSize(port(add, AB)) <= GetSize(sigH) | ||||||
|  | 	filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB))) | ||||||
|  | 	filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1 | ||||||
|  | 	set addAB AB | ||||||
| 	optional | 	optional | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| match addB | code sigCD sigO cd_signed | ||||||
| 	if !addA | 	if (add) { | ||||||
| 	select addB->type.in($add, $sub) | 		sigCD = port(add, addAB == \A ? \B : \A); | ||||||
| 	select nusers(port(addB, \B)) == 2 | 		cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool(); | ||||||
| 	index <SigSpec> port(addB, \B) === sigY |  | ||||||
| 	optional |  | ||||||
| endmatch |  | ||||||
| 
 | 
 | ||||||
| code addAB sigS |  | ||||||
| 	if (addA) { |  | ||||||
| 		addAB = addA; |  | ||||||
| 		sigS = port(addA, \B); |  | ||||||
| 	} |  | ||||||
| 	if (addB) { |  | ||||||
| 		addAB = addB; |  | ||||||
| 		sigS = port(addB, \A); |  | ||||||
| 	} |  | ||||||
| 	if (addAB) { |  | ||||||
| 		int natural_mul_width = GetSize(sigA) + GetSize(sigB); | 		int natural_mul_width = GetSize(sigA) + GetSize(sigB); | ||||||
| 		int actual_mul_width = GetSize(sigY); | 		int actual_mul_width = GetSize(sigH); | ||||||
| 		int actual_acc_width = GetSize(sigS); | 		int actual_acc_width = GetSize(sigCD); | ||||||
| 
 | 
 | ||||||
| 		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) | 		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) | ||||||
| 			reject; | 			reject; | ||||||
| 		if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) | 		// If accumulator, check adder width and signedness | ||||||
|  | 		if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool())) | ||||||
| 			reject; | 			reject; | ||||||
|  | 
 | ||||||
|  | 		sigO = port(add, \Y); | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| match muxA | match mux | ||||||
| 	if addAB | 	select mux->type == $mux | ||||||
| 	select muxA->type.in($mux) | 	choice <IdString> AB {\A, \B} | ||||||
| 	select nusers(port(muxA, \A)) == 2 | 	select nusers(port(mux, AB)) == 2 | ||||||
| 	index <SigSpec> port(muxA, \A) === port(addAB, \Y) | 	index <SigSpec> port(mux, AB) === sigO | ||||||
|  | 	set muxAB AB | ||||||
| 	optional | 	optional | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| match muxB | code sigO | ||||||
| 	if addAB | 	if (mux) | ||||||
| 	if !muxA | 		sigO = port(mux, \Y); | ||||||
| 	select muxB->type.in($mux) |  | ||||||
| 	select nusers(port(muxB, \B)) == 2 |  | ||||||
| 	index <SigSpec> port(muxB, \B) === port(addAB, \Y) |  | ||||||
| 	optional |  | ||||||
| endmatch |  | ||||||
| 
 |  | ||||||
| code muxAB |  | ||||||
| 	muxAB = addAB; |  | ||||||
| 	if (muxA) |  | ||||||
| 		muxAB = muxA; |  | ||||||
| 	if (muxB) |  | ||||||
| 		muxAB = muxB; |  | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| match ffS | code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo | ||||||
| 	if muxAB | 	if (mul->type != \SB_MAC16 || | ||||||
| 	select ffS->type.in($dff) | 			// Ensure that register is not already used | ||||||
| 	select nusers(port(ffS, \D)) == 2 | 			((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) && | ||||||
| 	index <SigSpec> port(ffS, \D) === port(muxAB, \Y) | 			 // Ensure that OLOADTOP/OLOADBOT is unused or zero | ||||||
| 	index <SigSpec> port(ffS, \Q) === sigS | 			 (port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) { | ||||||
| endmatch |  | ||||||
| 
 | 
 | ||||||
| code clock clock_pol clock_vld | 		dff = nullptr; | ||||||
| 	if (ffS) { |  | ||||||
| 		SigBit c = port(ffS, \CLK).as_bit(); |  | ||||||
| 		bool cp = param(ffS, \CLK_POLARITY).as_bool(); |  | ||||||
| 
 | 
 | ||||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | 		// First try entire sigO | ||||||
| 			reject; | 		if (nusers(sigO) == 2) { | ||||||
|  | 			argD = sigO; | ||||||
|  | 			subpattern(out_dffe); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		clock = c; | 		// Otherwise try just its least significant 16 bits | ||||||
| 		clock_pol = cp; | 		if (!dff && GetSize(sigO) > 16) { | ||||||
| 		clock_vld = true; | 			argD = sigO.extract(0, 16); | ||||||
|  | 			if (nusers(argD) == 2) { | ||||||
|  | 				subpattern(out_dffe); | ||||||
|  | 				o_lo = dff; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffO = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			clock_pol = dffclock_pol; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffOrstmux = dffrstmux; | ||||||
|  | 				ffOrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffholdmux) { | ||||||
|  | 				ffOholdmux = dffholdmux; | ||||||
|  | 				ffOholdpol = dffholdpol; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Loading value into output register is not | ||||||
|  | 		//   supported unless using accumulator | ||||||
|  | 		if (mux) { | ||||||
|  | 			if (sigCD != sigO) | ||||||
|  | 				reject; | ||||||
|  | 			sigCD = port(mux, muxAB == \B ? \A : \B); | ||||||
|  | 
 | ||||||
|  | 			cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool(); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol | ||||||
|  | 	if (!sigCD.empty() && sigCD != sigO && | ||||||
|  | 			(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) { | ||||||
|  | 		argQ = sigCD; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			if (dffholdmux) { | ||||||
|  | 				ffCDholdmux = dffholdmux; | ||||||
|  | 				ffCDholdpol = dffholdpol; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Reset signal of C (IRSTTOP) and D (IRSTBOT) | ||||||
|  | 			//   shared with A and B | ||||||
|  | 			if ((ffArstmux != NULL) != (dffrstmux != NULL)) | ||||||
|  | 				goto reject_ffCD; | ||||||
|  | 			if ((ffBrstmux != NULL) != (dffrstmux != NULL)) | ||||||
|  | 				goto reject_ffCD; | ||||||
|  | 			if (ffArstmux) { | ||||||
|  | 				if (port(ffArstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto reject_ffCD; | ||||||
|  | 				if (ffArstpol != dffrstpol) | ||||||
|  | 					goto reject_ffCD; | ||||||
|  | 			} | ||||||
|  | 			if (ffBrstmux) { | ||||||
|  | 				if (port(ffBrstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto reject_ffCD; | ||||||
|  | 				if (ffBrstpol != dffrstpol) | ||||||
|  | 					goto reject_ffCD; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			ffCD = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			clock_pol = dffclock_pol; | ||||||
|  | 			sigCD = dffD; | ||||||
|  | 
 | ||||||
|  | reject_ffCD: 		; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code sigCD | ||||||
|  | 	sigCD.extend_u0(32, cd_signed); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code | ||||||
| 	accept; | 	accept; | ||||||
| endcode | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | subpattern in_dffe | ||||||
|  | arg argD argQ clock clock_pol | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (auto c : argQ.chunks()) { | ||||||
|  | 		if (!c.wire) | ||||||
|  | 			reject; | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | 		Const init = c.wire->attributes.at(\init, State::Sx); | ||||||
|  | 		if (!init.is_fully_undef() && !init.is_fully_zero()) | ||||||
|  | 			reject; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \Q)[offset] === argQ[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argQ is present | ||||||
|  | 	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) | ||||||
|  | 	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ argD | ||||||
|  | { | ||||||
|  | 	if (clock != SigBit()) { | ||||||
|  | 		if (port(ff, \CLK) != clock) | ||||||
|  | 			reject; | ||||||
|  | 		if (param(ff, \CLK_POLARITY).as_bool() != clock_pol) | ||||||
|  | 			reject; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | 	dffclock_pol = param(ff, \CLK_POLARITY).as_bool(); | ||||||
|  | 	dffD = argQ; | ||||||
|  | 	argD = port(ff, \D); | ||||||
|  | 	argQ = Q; | ||||||
|  | 	dffD.replace(argQ, argD); | ||||||
|  | 	// Only search for ffrstmux if dffD only | ||||||
|  | 	//   has two (ff, ffrstmux) users | ||||||
|  | 	if (nusers(dffD) > 2) | ||||||
|  | 		argD = SigSpec(); | ||||||
|  | } | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ffrstmux | ||||||
|  | 	if false /* TODO: ice40 resets are actually async */ | ||||||
|  | 
 | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffrstmux, \Y) === argD | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 		argD = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffrstmux, \Y), argD); | ||||||
|  | 
 | ||||||
|  | 		// Only search for ffholdmux if argQ has at | ||||||
|  | 		//   least 3 users (ff, <upstream>, ffrstmux) and | ||||||
|  | 		//   dffD only has two (ff, ffrstmux) | ||||||
|  | 		if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) | ||||||
|  | 			argD = SigSpec(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffrstmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ffholdmux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffholdmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffholdmux, \Y) === argD | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	index <SigSpec> port(ffholdmux, BA) === argQ | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffholdpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffholdmux) { | ||||||
|  | 		dffholdmux = ffholdmux; | ||||||
|  | 		dffholdpol = ffholdpol; | ||||||
|  | 		argD = port(ffholdmux, ffholdpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffholdmux, \Y), argD); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffholdmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | subpattern out_dffe | ||||||
|  | arg argD argQ clock clock_pol | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (auto c : argD.chunks()) | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ffholdmux | ||||||
|  | 	select ffholdmux->type.in($mux) | ||||||
|  | 	// ffholdmux output must have two users: ffholdmux and ff.D | ||||||
|  | 	select nusers(port(ffholdmux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s) | ||||||
|  | 	select nusers(port(ffholdmux, BA)) >= 3 | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffholdmux, \Y)) | ||||||
|  | 	define <IdString> AB (BA == \B ? \A : \B) | ||||||
|  | 	index <SigBit> port(ffholdmux, AB)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffholdpol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffholdmux = ffholdmux; | ||||||
|  | 	if (ffholdmux) { | ||||||
|  | 		SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B); | ||||||
|  | 		SigSpec Y = port(ffholdmux, \Y); | ||||||
|  | 		argQ = argD; | ||||||
|  | 		argD.replace(AB, Y); | ||||||
|  | 		argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A)); | ||||||
|  | 
 | ||||||
|  | 		dffholdmux = ffholdmux; | ||||||
|  | 		dffholdpol = ffholdpol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ffrstmux | ||||||
|  | 	if false /* TODO: ice40 resets are actually async */ | ||||||
|  | 
 | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	// ffrstmux output must have two users: ffrstmux and ff.D | ||||||
|  | 	select nusers(port(ffrstmux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffrstmux, \Y)) | ||||||
|  | 	define <IdString> AB (BA == \B ? \A : \B) | ||||||
|  | 	index <SigBit> port(ffrstmux, AB)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter !ffholdmux || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffrstmux = ffrstmux; | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		SigSpec Y = port(ffrstmux, \Y); | ||||||
|  | 		argD.replace(AB, Y); | ||||||
|  | 
 | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \D)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter (!ffholdmux && !ffrstmux) || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ff, \D)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ff, \D).extract(offset, GetSize(argD)) == argD | ||||||
|  | 	// Check that FF.Q is connected to CE-mux | ||||||
|  | 	filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ | ||||||
|  | 	if (ff) { | ||||||
|  | 		if (clock != SigBit()) { | ||||||
|  | 			if (port(ff, \CLK) != clock) | ||||||
|  | 				reject; | ||||||
|  | 			if (param(ff, \CLK_POLARITY).as_bool() != clock_pol) | ||||||
|  | 				reject; | ||||||
|  | 		} | ||||||
|  | 		SigSpec D = port(ff, \D); | ||||||
|  | 		SigSpec Q = port(ff, \Q); | ||||||
|  | 		if (!ffholdmux) { | ||||||
|  | 			argQ = argD; | ||||||
|  | 			argQ.replace(D, Q); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (auto c : argQ.chunks()) { | ||||||
|  | 			Const init = c.wire->attributes.at(\init, State::Sx); | ||||||
|  | 			if (!init.is_fully_undef() && !init.is_fully_zero()) | ||||||
|  | 				reject; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		dff = ff; | ||||||
|  | 		dffQ = argQ; | ||||||
|  | 		dffclock = port(ff, \CLK); | ||||||
|  | 		dffclock_pol = param(ff, \CLK_POLARITY).as_bool(); | ||||||
|  | 	} | ||||||
|  | 	// No enable/reset mux possible without flop | ||||||
|  | 	else if (dffholdmux || dffrstmux) | ||||||
|  | 		reject; | ||||||
|  | endcode | ||||||
|  |  | ||||||
|  | @ -9,3 +9,7 @@ match lut | ||||||
| 	index <SigSpec> port(lut, \I1) === port(carry, \I0) | 	index <SigSpec> port(lut, \I1) === port(carry, \I0) | ||||||
| 	index <SigSpec> port(lut, \I2) === port(carry, \I1) | 	index <SigSpec> port(lut, \I2) === port(carry, \I1) | ||||||
| endmatch | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	accept; | ||||||
|  | endcode | ||||||
|  |  | ||||||
|  | @ -24,8 +24,11 @@ USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
| bool did_something; | bool did_something; | ||||||
|  | dict<SigBit, State> initbits; | ||||||
|  | pool<SigBit> rminitbits; | ||||||
| 
 | 
 | ||||||
| #include "passes/pmgen/peepopt_pm.h" | #include "passes/pmgen/peepopt_pm.h" | ||||||
|  | #include "generate.h" | ||||||
| 
 | 
 | ||||||
| struct PeepoptPass : public Pass { | struct PeepoptPass : public Pass { | ||||||
| 	PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } | 	PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } | ||||||
|  | @ -40,26 +43,86 @@ struct PeepoptPass : public Pass { | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  | 		std::string genmode; | ||||||
|  | 
 | ||||||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||||
| 
 | 
 | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
| 		{ | 		{ | ||||||
| 			// if (args[argidx] == "-singleton") {
 | 			if (args[argidx] == "-generate" && argidx+1 < args.size()) { | ||||||
| 			// 	singleton_mode = true;
 | 				genmode = args[++argidx]; | ||||||
| 			// 	continue;
 | 				continue; | ||||||
| 			// }
 | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
| 		for (auto module : design->selected_modules()) { | 		if (!genmode.empty()) | ||||||
|  | 		{ | ||||||
|  | 			initbits.clear(); | ||||||
|  | 			rminitbits.clear(); | ||||||
|  | 
 | ||||||
|  | 			if (genmode == "shiftmul") | ||||||
|  | 				GENERATE_PATTERN(peepopt_pm, shiftmul); | ||||||
|  | 			else if (genmode == "muldiv") | ||||||
|  | 				GENERATE_PATTERN(peepopt_pm, muldiv); | ||||||
|  | 			else if (genmode == "dffmux") | ||||||
|  | 				GENERATE_PATTERN(peepopt_pm, dffmux); | ||||||
|  | 			else | ||||||
|  | 				log_abort(); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (auto module : design->selected_modules()) | ||||||
|  | 		{ | ||||||
| 			did_something = true; | 			did_something = true; | ||||||
| 			while (did_something) { | 
 | ||||||
|  | 			while (did_something) | ||||||
|  | 			{ | ||||||
| 				did_something = false; | 				did_something = false; | ||||||
| 				peepopt_pm pm(module, module->selected_cells()); | 				initbits.clear(); | ||||||
|  | 				rminitbits.clear(); | ||||||
|  | 
 | ||||||
|  | 				peepopt_pm pm(module); | ||||||
|  | 
 | ||||||
|  | 				for (auto w : module->wires()) { | ||||||
|  | 					auto it = w->attributes.find(ID(init)); | ||||||
|  | 					if (it != w->attributes.end()) { | ||||||
|  | 						SigSpec sig = pm.sigmap(w); | ||||||
|  | 						Const val = it->second; | ||||||
|  | 						int len = std::min(GetSize(sig), GetSize(val)); | ||||||
|  | 						for (int i = 0; i < len; i++) { | ||||||
|  | 							if (sig[i].wire == nullptr) | ||||||
|  | 								continue; | ||||||
|  | 							if (val[i] != State::S0 && val[i] != State::S1) | ||||||
|  | 								continue; | ||||||
|  | 							initbits[sig[i]] = val[i]; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				pm.setup(module->selected_cells()); | ||||||
|  | 
 | ||||||
| 				pm.run_shiftmul(); | 				pm.run_shiftmul(); | ||||||
| 				pm.run_muldiv(); | 				pm.run_muldiv(); | ||||||
|  | 				pm.run_dffmux(); | ||||||
|  | 
 | ||||||
|  | 				for (auto w : module->wires()) { | ||||||
|  | 					auto it = w->attributes.find(ID(init)); | ||||||
|  | 					if (it != w->attributes.end()) { | ||||||
|  | 						SigSpec sig = pm.sigmap(w); | ||||||
|  | 						Const &val = it->second; | ||||||
|  | 						int len = std::min(GetSize(sig), GetSize(val)); | ||||||
|  | 						for (int i = 0; i < len; i++) { | ||||||
|  | 							if (rminitbits.count(sig[i])) | ||||||
|  | 								val[i] = State::Sx; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				initbits.clear(); | ||||||
|  | 				rminitbits.clear(); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										171
									
								
								passes/pmgen/peepopt_dffmux.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								passes/pmgen/peepopt_dffmux.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,171 @@ | ||||||
|  | pattern dffmux | ||||||
|  | 
 | ||||||
|  | state <IdString> cemuxAB rstmuxBA | ||||||
|  | state <SigSpec> sigD | ||||||
|  | 
 | ||||||
|  | match dff | ||||||
|  | 	select dff->type == $dff | ||||||
|  | 	select GetSize(port(dff, \D)) > 1 | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigD | ||||||
|  | 	sigD = port(dff, \D); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match rstmux | ||||||
|  | 	select rstmux->type == $mux | ||||||
|  | 	select GetSize(port(rstmux, \Y)) > 1 | ||||||
|  | 	index <SigSpec> port(rstmux, \Y) === sigD | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	select port(rstmux, BA).is_fully_const() | ||||||
|  | 	set rstmuxBA BA | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigD | ||||||
|  | 	if (rstmux) | ||||||
|  | 		sigD = port(rstmux, rstmuxBA == \B ? \A : \B); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match cemux | ||||||
|  | 	select cemux->type == $mux | ||||||
|  | 	select GetSize(port(cemux, \Y)) > 1 | ||||||
|  | 	index <SigSpec> port(cemux, \Y) === sigD | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(cemux, AB) === port(dff, \Q) | ||||||
|  | 	set cemuxAB AB | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	if (!cemux && !rstmux) | ||||||
|  | 		reject; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	Const rst; | ||||||
|  | 	SigSpec D; | ||||||
|  | 	if (cemux) { | ||||||
|  | 		D = port(cemux, cemuxAB == \A ? \B : \A); | ||||||
|  | 		if (rstmux) | ||||||
|  | 			rst = port(rstmux, rstmuxBA).as_const(); | ||||||
|  | 		else | ||||||
|  | 			rst = Const(State::Sx, GetSize(D)); | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		log_assert(rstmux); | ||||||
|  | 		D = port(rstmux, rstmuxBA  == \B ? \A : \B); | ||||||
|  | 		rst = port(rstmux, rstmuxBA).as_const(); | ||||||
|  | 	} | ||||||
|  | 	SigSpec Q = port(dff, \Q); | ||||||
|  | 	int width = GetSize(D); | ||||||
|  | 
 | ||||||
|  | 	SigSpec dffD = dff->getPort(\D); | ||||||
|  | 	SigSpec dffQ = dff->getPort(\Q); | ||||||
|  | 
 | ||||||
|  | 	Const initval; | ||||||
|  | 	for (auto b : Q) { | ||||||
|  | 		auto it = initbits.find(b); | ||||||
|  | 		initval.bits.push_back(it == initbits.end() ? State::Sx : it->second); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	auto cmpx = [=](State lhs, State rhs) { | ||||||
|  | 		if (lhs == State::Sx || rhs == State::Sx) | ||||||
|  | 			return true; | ||||||
|  | 		return lhs == rhs; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	int i = width-1; | ||||||
|  | 	while (i > 1) { | ||||||
|  | 		if (D[i] != D[i-1]) | ||||||
|  | 			break; | ||||||
|  | 		if (!cmpx(rst[i], rst[i-1])) | ||||||
|  | 			break; | ||||||
|  | 		if (!cmpx(initval[i], initval[i-1])) | ||||||
|  | 			break; | ||||||
|  | 		if (!cmpx(rst[i], initval[i])) | ||||||
|  | 			break; | ||||||
|  | 		rminitbits.insert(Q[i]); | ||||||
|  | 		module->connect(Q[i], Q[i-1]); | ||||||
|  | 		i--; | ||||||
|  | 	} | ||||||
|  | 	if (i < width-1) { | ||||||
|  | 		did_something = true; | ||||||
|  | 		if (cemux) { | ||||||
|  | 			SigSpec ceA = cemux->getPort(\A); | ||||||
|  | 			SigSpec ceB = cemux->getPort(\B); | ||||||
|  | 			SigSpec ceY = cemux->getPort(\Y); | ||||||
|  | 			ceA.remove(i, width-1-i); | ||||||
|  | 			ceB.remove(i, width-1-i); | ||||||
|  | 			ceY.remove(i, width-1-i); | ||||||
|  | 			cemux->setPort(\A, ceA); | ||||||
|  | 			cemux->setPort(\B, ceB); | ||||||
|  | 			cemux->setPort(\Y, ceY); | ||||||
|  | 			cemux->fixup_parameters(); | ||||||
|  | 			blacklist(cemux); | ||||||
|  | 		} | ||||||
|  | 		if (rstmux) { | ||||||
|  | 			SigSpec rstA = rstmux->getPort(\A); | ||||||
|  | 			SigSpec rstB = rstmux->getPort(\B); | ||||||
|  | 			SigSpec rstY = rstmux->getPort(\Y); | ||||||
|  | 			rstA.remove(i, width-1-i); | ||||||
|  | 			rstB.remove(i, width-1-i); | ||||||
|  | 			rstY.remove(i, width-1-i); | ||||||
|  | 			rstmux->setPort(\A, rstA); | ||||||
|  | 			rstmux->setPort(\B, rstB); | ||||||
|  | 			rstmux->setPort(\Y, rstY); | ||||||
|  | 			rstmux->fixup_parameters(); | ||||||
|  | 			blacklist(rstmux); | ||||||
|  | 		} | ||||||
|  | 		dffD.remove(i, width-1-i); | ||||||
|  | 		dffQ.remove(i, width-1-i); | ||||||
|  | 		dff->setPort(\D, dffD); | ||||||
|  | 		dff->setPort(\Q, dffQ); | ||||||
|  | 		dff->fixup_parameters(); | ||||||
|  | 		blacklist(dff); | ||||||
|  | 
 | ||||||
|  | 		log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux, "n/a"), log_id(rstmux, "n/a"), width-1-i); | ||||||
|  | 		width = i+1; | ||||||
|  | 	} | ||||||
|  | 	if (cemux) { | ||||||
|  | 		SigSpec ceA = cemux->getPort(\A); | ||||||
|  | 		SigSpec ceB = cemux->getPort(\B); | ||||||
|  | 		SigSpec ceY = cemux->getPort(\Y); | ||||||
|  | 
 | ||||||
|  | 		int count = 0; | ||||||
|  | 		for (int i = width-1; i >= 0; i--) { | ||||||
|  | 			if (D[i].wire) | ||||||
|  | 				continue; | ||||||
|  | 			if (cmpx(rst[i], D[i].data) && cmpx(initval[i], D[i].data)) { | ||||||
|  | 				count++; | ||||||
|  | 				rminitbits.insert(Q[i]); | ||||||
|  | 				module->connect(Q[i], D[i]); | ||||||
|  | 				ceA.remove(i); | ||||||
|  | 				ceB.remove(i); | ||||||
|  | 				ceY.remove(i); | ||||||
|  | 				dffD.remove(i); | ||||||
|  | 				dffQ.remove(i); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (count > 0) | ||||||
|  | 		{ | ||||||
|  | 			did_something = true; | ||||||
|  | 
 | ||||||
|  | 			cemux->setPort(\A, ceA); | ||||||
|  | 			cemux->setPort(\B, ceB); | ||||||
|  | 			cemux->setPort(\Y, ceY); | ||||||
|  | 			cemux->fixup_parameters(); | ||||||
|  | 			blacklist(cemux); | ||||||
|  | 
 | ||||||
|  | 			dff->setPort(\D, dffD); | ||||||
|  | 			dff->setPort(\Q, dffQ); | ||||||
|  | 			dff->fixup_parameters(); | ||||||
|  | 			blacklist(dff); | ||||||
|  | 
 | ||||||
|  | 			log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), log_id(rstmux, "n/a"), count); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (did_something) | ||||||
|  | 		accept; | ||||||
|  | endcode | ||||||
|  | @ -286,7 +286,7 @@ def process_pmgfile(f, filename): | ||||||
|                         block["gencode"].append(rewrite_cpp(l.rstrip())) |                         block["gencode"].append(rewrite_cpp(l.rstrip())) | ||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|                 assert False |                 raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr)) | ||||||
| 
 | 
 | ||||||
|             if block["optional"]: |             if block["optional"]: | ||||||
|                 assert not block["semioptional"] |                 assert not block["semioptional"] | ||||||
|  | @ -305,7 +305,8 @@ def process_pmgfile(f, filename): | ||||||
|             block["states"] = set() |             block["states"] = set() | ||||||
| 
 | 
 | ||||||
|             for s in line.split()[1:]: |             for s in line.split()[1:]: | ||||||
|                 assert s in state_types[current_pattern] |                 if s not in state_types[current_pattern]: | ||||||
|  |                     raise RuntimeError("'%s' not in state_types" % s) | ||||||
|                 block["states"].add(s) |                 block["states"].add(s) | ||||||
| 
 | 
 | ||||||
|             codetype = "code" |             codetype = "code" | ||||||
|  | @ -327,7 +328,7 @@ def process_pmgfile(f, filename): | ||||||
|             blocks.append(block) |             blocks.append(block) | ||||||
|             continue |             continue | ||||||
| 
 | 
 | ||||||
|         assert False |         raise RuntimeError("'%s' command not recognised" % cmd) | ||||||
| 
 | 
 | ||||||
| for fn in pmgfiles: | for fn in pmgfiles: | ||||||
|     with open(fn, "r") as f: |     with open(fn, "r") as f: | ||||||
|  | @ -361,6 +362,7 @@ with open(outfile, "w") as f: | ||||||
|     print("  Module *module;", file=f) |     print("  Module *module;", file=f) | ||||||
|     print("  SigMap sigmap;", file=f) |     print("  SigMap sigmap;", file=f) | ||||||
|     print("  std::function<void()> on_accept;", file=f) |     print("  std::function<void()> on_accept;", file=f) | ||||||
|  |     print("  bool setup_done;", file=f) | ||||||
|     print("  bool generate_mode;", file=f) |     print("  bool generate_mode;", file=f) | ||||||
|     print("  int accept_cnt;", file=f) |     print("  int accept_cnt;", file=f) | ||||||
|     print("", file=f) |     print("", file=f) | ||||||
|  | @ -452,11 +454,19 @@ with open(outfile, "w") as f: | ||||||
|     print("    return sigmap(cell->getPort(portname));", file=f) |     print("    return sigmap(cell->getPort(portname));", file=f) | ||||||
|     print("  }", file=f) |     print("  }", file=f) | ||||||
|     print("", file=f) |     print("", file=f) | ||||||
|  |     print("  SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f) | ||||||
|  |     print("    return sigmap(cell->connections_.at(portname, defval));", file=f) | ||||||
|  |     print("  }", file=f) | ||||||
|  |     print("", file=f) | ||||||
| 
 | 
 | ||||||
|     print("  Const param(Cell *cell, IdString paramname) {", file=f) |     print("  Const param(Cell *cell, IdString paramname) {", file=f) | ||||||
|     print("    return cell->getParam(paramname);", file=f) |     print("    return cell->getParam(paramname);", file=f) | ||||||
|     print("  }", file=f) |     print("  }", file=f) | ||||||
|     print("", file=f) |     print("", file=f) | ||||||
|  |     print("  Const param(Cell *cell, IdString paramname, const Const& defval) {", file=f) | ||||||
|  |     print("    return cell->parameters.at(paramname, defval);", file=f) | ||||||
|  |     print("  }", file=f) | ||||||
|  |     print("", file=f) | ||||||
| 
 | 
 | ||||||
|     print("  int nusers(const SigSpec &sig) {", file=f) |     print("  int nusers(const SigSpec &sig) {", file=f) | ||||||
|     print("    pool<Cell*> users;", file=f) |     print("    pool<Cell*> users;", file=f) | ||||||
|  | @ -468,7 +478,17 @@ with open(outfile, "w") as f: | ||||||
|     print("", file=f) |     print("", file=f) | ||||||
| 
 | 
 | ||||||
|     print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) |     print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) | ||||||
|     print("      module(module), sigmap(module), generate_mode(false), rngseed(12345678) {", file=f) |     print("      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) | ||||||
|  |     print("    setup(cells);", file=f) | ||||||
|  |     print("  }", file=f) | ||||||
|  |     print("", file=f) | ||||||
|  | 
 | ||||||
|  |     print("  {}_pm(Module *module) :".format(prefix), file=f) | ||||||
|  |     print("      module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) | ||||||
|  |     print("  }", file=f) | ||||||
|  |     print("", file=f) | ||||||
|  | 
 | ||||||
|  |     print("  void setup(const vector<Cell*> &cells) {", file=f) | ||||||
|     for current_pattern in sorted(patterns.keys()): |     for current_pattern in sorted(patterns.keys()): | ||||||
|         for s, t in sorted(udata_types[current_pattern].items()): |         for s, t in sorted(udata_types[current_pattern].items()): | ||||||
|             if t.endswith("*"): |             if t.endswith("*"): | ||||||
|  | @ -476,6 +496,8 @@ with open(outfile, "w") as f: | ||||||
|             else: |             else: | ||||||
|                 print("    ud_{}.{} = {}();".format(current_pattern, s, t), file=f) |                 print("    ud_{}.{} = {}();".format(current_pattern, s, t), file=f) | ||||||
|     current_pattern = None |     current_pattern = None | ||||||
|  |     print("    log_assert(!setup_done);", file=f) | ||||||
|  |     print("    setup_done = true;", file=f) | ||||||
|     print("    for (auto port : module->ports)", file=f) |     print("    for (auto port : module->ports)", file=f) | ||||||
|     print("      add_siguser(module->wire(port), nullptr);", file=f) |     print("      add_siguser(module->wire(port), nullptr);", file=f) | ||||||
|     print("    for (auto cell : module->cells())", file=f) |     print("    for (auto cell : module->cells())", file=f) | ||||||
|  | @ -530,6 +552,7 @@ with open(outfile, "w") as f: | ||||||
| 
 | 
 | ||||||
|     for current_pattern in sorted(patterns.keys()): |     for current_pattern in sorted(patterns.keys()): | ||||||
|         print("  int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) |         print("  int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) | ||||||
|  |         print("    log_assert(setup_done);", file=f) | ||||||
|         print("    accept_cnt = 0;", file=f) |         print("    accept_cnt = 0;", file=f) | ||||||
|         print("    on_accept = on_accept_f;", file=f) |         print("    on_accept = on_accept_f;", file=f) | ||||||
|         print("    rollback = 0;", file=f) |         print("    rollback = 0;", file=f) | ||||||
|  |  | ||||||
|  | @ -23,13 +23,11 @@ | ||||||
| USING_YOSYS_NAMESPACE | USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
| // for peepopt_pm
 |  | ||||||
| bool did_something; |  | ||||||
| 
 |  | ||||||
| #include "passes/pmgen/test_pmgen_pm.h" | #include "passes/pmgen/test_pmgen_pm.h" | ||||||
| #include "passes/pmgen/ice40_dsp_pm.h" | #include "passes/pmgen/ice40_dsp_pm.h" | ||||||
| #include "passes/pmgen/xilinx_srl_pm.h" | #include "passes/pmgen/xilinx_srl_pm.h" | ||||||
| #include "passes/pmgen/peepopt_pm.h" | 
 | ||||||
|  | #include "generate.h" | ||||||
| 
 | 
 | ||||||
| void reduce_chain(test_pmgen_pm &pm) | void reduce_chain(test_pmgen_pm &pm) | ||||||
| { | { | ||||||
|  | @ -118,123 +116,6 @@ void opt_eqpmux(test_pmgen_pm &pm) | ||||||
| 	log("    -> %s (%s)\n", log_id(c), log_id(c->type)); | 	log("    -> %s (%s)\n", log_id(c), log_id(c->type)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define GENERATE_PATTERN(pmclass, pattern) \ |  | ||||||
| 	generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design) |  | ||||||
| 
 |  | ||||||
| void pmtest_addports(Module *module) |  | ||||||
| { |  | ||||||
| 	pool<SigBit> driven_bits, used_bits; |  | ||||||
| 	SigMap sigmap(module); |  | ||||||
| 	int icnt = 0, ocnt = 0; |  | ||||||
| 
 |  | ||||||
| 	for (auto cell : module->cells()) |  | ||||||
| 	for (auto conn : cell->connections()) |  | ||||||
| 	{ |  | ||||||
| 		if (cell->input(conn.first)) |  | ||||||
| 			for (auto bit : sigmap(conn.second)) |  | ||||||
| 				used_bits.insert(bit); |  | ||||||
| 		if (cell->output(conn.first)) |  | ||||||
| 			for (auto bit : sigmap(conn.second)) |  | ||||||
| 				driven_bits.insert(bit); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (auto wire : vector<Wire*>(module->wires())) |  | ||||||
| 	{ |  | ||||||
| 		SigSpec ibits, obits; |  | ||||||
| 		for (auto bit : sigmap(wire)) { |  | ||||||
| 			if (!used_bits.count(bit)) |  | ||||||
| 				obits.append(bit); |  | ||||||
| 			if (!driven_bits.count(bit)) |  | ||||||
| 				ibits.append(bit); |  | ||||||
| 		} |  | ||||||
| 		if (!ibits.empty()) { |  | ||||||
| 			Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits)); |  | ||||||
| 			w->port_input = true; |  | ||||||
| 			module->connect(ibits, w); |  | ||||||
| 		} |  | ||||||
| 		if (!obits.empty()) { |  | ||||||
| 			Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits)); |  | ||||||
| 			w->port_output = true; |  | ||||||
| 			module->connect(w, obits); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	module->fixup_ports(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class pm> |  | ||||||
| void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design) |  | ||||||
| { |  | ||||||
| 	log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass); |  | ||||||
| 
 |  | ||||||
| 	int modcnt = 0; |  | ||||||
| 	int maxmodcnt = 100; |  | ||||||
| 	int maxsubcnt = 4; |  | ||||||
| 	int timeout = 0; |  | ||||||
| 	vector<Module*> mods; |  | ||||||
| 
 |  | ||||||
| 	while (modcnt < maxmodcnt) |  | ||||||
| 	{ |  | ||||||
| 		int submodcnt = 0, itercnt = 0, cellcnt = 0; |  | ||||||
| 		Module *mod = design->addModule(NEW_ID); |  | ||||||
| 
 |  | ||||||
| 		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000) |  | ||||||
| 		{ |  | ||||||
| 			if (timeout++ > 10000) |  | ||||||
| 				log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n"); |  | ||||||
| 
 |  | ||||||
| 			pm matcher(mod, mod->cells()); |  | ||||||
| 
 |  | ||||||
| 			matcher.rng(1); |  | ||||||
| 			matcher.rngseed += modcnt; |  | ||||||
| 			matcher.rng(1); |  | ||||||
| 			matcher.rngseed += submodcnt; |  | ||||||
| 			matcher.rng(1); |  | ||||||
| 			matcher.rngseed += itercnt; |  | ||||||
| 			matcher.rng(1); |  | ||||||
| 			matcher.rngseed += cellcnt; |  | ||||||
| 			matcher.rng(1); |  | ||||||
| 
 |  | ||||||
| 			if (GetSize(mod->cells()) != cellcnt) |  | ||||||
| 			{ |  | ||||||
| 				bool found_match = false; |  | ||||||
| 				run(matcher, [&](){ found_match = true; }); |  | ||||||
| 				cellcnt = GetSize(mod->cells()); |  | ||||||
| 
 |  | ||||||
| 				if (found_match) { |  | ||||||
| 					Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d", |  | ||||||
| 							pmclass, pattern, modcnt++)); |  | ||||||
| 					log("Creating module %s with %d cells.\n", log_id(m), cellcnt); |  | ||||||
| 					mod->cloneInto(m); |  | ||||||
| 					pmtest_addports(m); |  | ||||||
| 					mods.push_back(m); |  | ||||||
| 					submodcnt++; |  | ||||||
| 					timeout = 0; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			matcher.generate_mode = true; |  | ||||||
| 			run(matcher, [](){}); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (submodcnt && maxsubcnt < (1 << 16)) |  | ||||||
| 			maxsubcnt *= 2; |  | ||||||
| 
 |  | ||||||
| 		design->remove(mod); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern)); |  | ||||||
| 	log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods)); |  | ||||||
| 	for (auto mod : mods) { |  | ||||||
| 		Cell *c = m->addCell(mod->name, mod->name); |  | ||||||
| 		for (auto port : mod->ports) { |  | ||||||
| 			Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port))); |  | ||||||
| 			c->setPort(port, w); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	pmtest_addports(m); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct TestPmgenPass : public Pass { | struct TestPmgenPass : public Pass { | ||||||
| 	TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } | 	TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } | ||||||
| 	void help() YS_OVERRIDE | 	void help() YS_OVERRIDE | ||||||
|  | @ -355,12 +236,6 @@ struct TestPmgenPass : public Pass { | ||||||
| 		if (pattern == "xilinx_srl.variable") | 		if (pattern == "xilinx_srl.variable") | ||||||
| 			return GENERATE_PATTERN(xilinx_srl_pm, variable); | 			return GENERATE_PATTERN(xilinx_srl_pm, variable); | ||||||
| 
 | 
 | ||||||
| 		if (pattern == "peepopt-muldiv") |  | ||||||
| 			return GENERATE_PATTERN(peepopt_pm, muldiv); |  | ||||||
| 
 |  | ||||||
| 		if (pattern == "peepopt-shiftmul") |  | ||||||
| 			return GENERATE_PATTERN(peepopt_pm, shiftmul); |  | ||||||
| 
 |  | ||||||
| 		log_cmd_error("Unknown pattern: %s\n", pattern.c_str()); | 		log_cmd_error("Unknown pattern: %s\n", pattern.c_str()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										646
									
								
								passes/pmgen/xilinx_dsp.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										646
									
								
								passes/pmgen/xilinx_dsp.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,646 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  *                2019  Eddie Hung    <eddie@fpgeh.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 "kernel/yosys.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
|  | #include <deque> | ||||||
|  | 
 | ||||||
|  | USING_YOSYS_NAMESPACE | ||||||
|  | PRIVATE_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | #include "passes/pmgen/xilinx_dsp_pm.h" | ||||||
|  | #include "passes/pmgen/xilinx_dsp_CREG_pm.h" | ||||||
|  | #include "passes/pmgen/xilinx_dsp_cascade_pm.h" | ||||||
|  | 
 | ||||||
|  | static Cell* addDsp(Module *module) { | ||||||
|  | 	Cell *cell = module->addCell(NEW_ID, ID(DSP48E1)); | ||||||
|  | 	cell->setParam(ID(ACASCREG), 0); | ||||||
|  | 	cell->setParam(ID(ADREG), 0); | ||||||
|  | 	cell->setParam(ID(A_INPUT), Const("DIRECT")); | ||||||
|  | 	cell->setParam(ID(ALUMODEREG), 0); | ||||||
|  | 	cell->setParam(ID(AREG), 0); | ||||||
|  | 	cell->setParam(ID(BCASCREG), 0); | ||||||
|  | 	cell->setParam(ID(B_INPUT), Const("DIRECT")); | ||||||
|  | 	cell->setParam(ID(BREG), 0); | ||||||
|  | 	cell->setParam(ID(CARRYINREG), 0); | ||||||
|  | 	cell->setParam(ID(CARRYINSELREG), 0); | ||||||
|  | 	cell->setParam(ID(CREG), 0); | ||||||
|  | 	cell->setParam(ID(DREG), 0); | ||||||
|  | 	cell->setParam(ID(INMODEREG), 0); | ||||||
|  | 	cell->setParam(ID(MREG), 0); | ||||||
|  | 	cell->setParam(ID(OPMODEREG), 0); | ||||||
|  | 	cell->setParam(ID(PREG), 0); | ||||||
|  | 	cell->setParam(ID(USE_MULT), Const("NONE")); | ||||||
|  | 	cell->setParam(ID(USE_SIMD), Const("ONE48")); | ||||||
|  | 	cell->setParam(ID(USE_DPORT), Const("FALSE")); | ||||||
|  | 
 | ||||||
|  | 	cell->setPort(ID(D), Const(0, 25)); | ||||||
|  | 	cell->setPort(ID(INMODE), Const(0, 5)); | ||||||
|  | 	cell->setPort(ID(ALUMODE), Const(0, 4)); | ||||||
|  | 	cell->setPort(ID(OPMODE), Const(0, 7)); | ||||||
|  | 	cell->setPort(ID(CARRYINSEL), Const(0, 3)); | ||||||
|  | 	cell->setPort(ID(ACIN), Const(0, 30)); | ||||||
|  | 	cell->setPort(ID(BCIN), Const(0, 18)); | ||||||
|  | 	cell->setPort(ID(PCIN), Const(0, 48)); | ||||||
|  | 	cell->setPort(ID(CARRYIN), Const(0, 1)); | ||||||
|  | 	return cell; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void xilinx_simd_pack(Module *module, const std::vector<Cell*> &selected_cells) | ||||||
|  | { | ||||||
|  | 	std::deque<Cell*> simd12_add, simd12_sub; | ||||||
|  | 	std::deque<Cell*> simd24_add, simd24_sub; | ||||||
|  | 
 | ||||||
|  | 	for (auto cell : selected_cells) { | ||||||
|  | 		if (!cell->type.in(ID($add), ID($sub))) | ||||||
|  | 			continue; | ||||||
|  | 		SigSpec Y = cell->getPort(ID(Y)); | ||||||
|  | 		if (!Y.is_chunk()) | ||||||
|  | 			continue; | ||||||
|  | 		if (!Y.as_chunk().wire->get_strpool_attribute(ID(use_dsp)).count("simd")) | ||||||
|  | 			continue; | ||||||
|  | 		if (GetSize(Y) > 25) | ||||||
|  | 			continue; | ||||||
|  | 		SigSpec A = cell->getPort(ID(A)); | ||||||
|  | 		SigSpec B = cell->getPort(ID(B)); | ||||||
|  | 		if (GetSize(Y) <= 13) { | ||||||
|  | 			if (GetSize(A) > 12) | ||||||
|  | 				continue; | ||||||
|  | 			if (GetSize(B) > 12) | ||||||
|  | 				continue; | ||||||
|  | 			if (cell->type == ID($add)) | ||||||
|  | 				simd12_add.push_back(cell); | ||||||
|  | 			else if (cell->type == ID($sub)) | ||||||
|  | 				simd12_sub.push_back(cell); | ||||||
|  | 		} | ||||||
|  | 		else if (GetSize(Y) <= 25) { | ||||||
|  | 			if (GetSize(A) > 24) | ||||||
|  | 				continue; | ||||||
|  | 			if (GetSize(B) > 24) | ||||||
|  | 				continue; | ||||||
|  | 			if (cell->type == ID($add)) | ||||||
|  | 				simd24_add.push_back(cell); | ||||||
|  | 			else if (cell->type == ID($sub)) | ||||||
|  | 				simd24_sub.push_back(cell); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	auto f12 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) { | ||||||
|  | 		SigSpec A = lane->getPort(ID(A)); | ||||||
|  | 		SigSpec B = lane->getPort(ID(B)); | ||||||
|  | 		SigSpec Y = lane->getPort(ID(Y)); | ||||||
|  | 		A.extend_u0(12, lane->getParam(ID(A_SIGNED)).as_bool()); | ||||||
|  | 		B.extend_u0(12, lane->getParam(ID(B_SIGNED)).as_bool()); | ||||||
|  | 		AB.append(A); | ||||||
|  | 		C.append(B); | ||||||
|  | 		if (GetSize(Y) < 13) | ||||||
|  | 			Y.append(module->addWire(NEW_ID, 13-GetSize(Y))); | ||||||
|  | 		else | ||||||
|  | 			log_assert(GetSize(Y) == 13); | ||||||
|  | 		P.append(Y.extract(0, 12)); | ||||||
|  | 		CARRYOUT.append(Y[12]); | ||||||
|  | 	}; | ||||||
|  | 	auto g12 = [&f12,module](std::deque<Cell*> &simd12) { | ||||||
|  | 		while (simd12.size() > 1) { | ||||||
|  | 			SigSpec AB, C, P, CARRYOUT; | ||||||
|  | 
 | ||||||
|  | 			Cell *lane1 = simd12.front(); | ||||||
|  | 			simd12.pop_front(); | ||||||
|  | 			Cell *lane2 = simd12.front(); | ||||||
|  | 			simd12.pop_front(); | ||||||
|  | 			Cell *lane3 = nullptr; | ||||||
|  | 			Cell *lane4 = nullptr; | ||||||
|  | 
 | ||||||
|  | 			if (!simd12.empty()) { | ||||||
|  | 				lane3 = simd12.front(); | ||||||
|  | 				simd12.pop_front(); | ||||||
|  | 				if (!simd12.empty()) { | ||||||
|  | 					lane4 = simd12.front(); | ||||||
|  | 					simd12.pop_front(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			log("Analysing %s.%s for Xilinx DSP SIMD12 packing.\n", log_id(module), log_id(lane1)); | ||||||
|  | 
 | ||||||
|  | 			Cell *cell = addDsp(module); | ||||||
|  | 			cell->setParam(ID(USE_SIMD), Const("FOUR12")); | ||||||
|  | 			// X = A:B
 | ||||||
|  | 			// Y = 0
 | ||||||
|  | 			// Z = C
 | ||||||
|  | 			cell->setPort(ID(OPMODE), Const::from_string("0110011")); | ||||||
|  | 
 | ||||||
|  | 			log_assert(lane1); | ||||||
|  | 			log_assert(lane2); | ||||||
|  | 			f12(AB, C, P, CARRYOUT, lane1); | ||||||
|  | 			f12(AB, C, P, CARRYOUT, lane2); | ||||||
|  | 			if (lane3) { | ||||||
|  | 				f12(AB, C, P, CARRYOUT, lane3); | ||||||
|  | 				if (lane4) | ||||||
|  | 					f12(AB, C, P, CARRYOUT, lane4); | ||||||
|  | 				else { | ||||||
|  | 					AB.append(Const(0, 12)); | ||||||
|  | 					C.append(Const(0, 12)); | ||||||
|  | 					P.append(module->addWire(NEW_ID, 12)); | ||||||
|  | 					CARRYOUT.append(module->addWire(NEW_ID, 1)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				AB.append(Const(0, 24)); | ||||||
|  | 				C.append(Const(0, 24)); | ||||||
|  | 				P.append(module->addWire(NEW_ID, 24)); | ||||||
|  | 				CARRYOUT.append(module->addWire(NEW_ID, 2)); | ||||||
|  | 			} | ||||||
|  | 			log_assert(GetSize(AB) == 48); | ||||||
|  | 			log_assert(GetSize(C) == 48); | ||||||
|  | 			log_assert(GetSize(P) == 48); | ||||||
|  | 			log_assert(GetSize(CARRYOUT) == 4); | ||||||
|  | 			cell->setPort(ID(A), AB.extract(18, 30)); | ||||||
|  | 			cell->setPort(ID(B), AB.extract(0, 18)); | ||||||
|  | 			cell->setPort(ID(C), C); | ||||||
|  | 			cell->setPort(ID(P), P); | ||||||
|  | 			cell->setPort(ID(CARRYOUT), CARRYOUT); | ||||||
|  | 			if (lane1->type == ID($sub)) | ||||||
|  | 				cell->setPort(ID(ALUMODE), Const::from_string("0011")); | ||||||
|  | 
 | ||||||
|  | 			module->remove(lane1); | ||||||
|  | 			module->remove(lane2); | ||||||
|  | 			if (lane3) module->remove(lane3); | ||||||
|  | 			if (lane4) module->remove(lane4); | ||||||
|  | 
 | ||||||
|  | 			module->design->select(module, cell); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 	g12(simd12_add); | ||||||
|  | 	g12(simd12_sub); | ||||||
|  | 
 | ||||||
|  | 	auto f24 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) { | ||||||
|  | 		SigSpec A = lane->getPort(ID(A)); | ||||||
|  | 		SigSpec B = lane->getPort(ID(B)); | ||||||
|  | 		SigSpec Y = lane->getPort(ID(Y)); | ||||||
|  | 		A.extend_u0(24, lane->getParam(ID(A_SIGNED)).as_bool()); | ||||||
|  | 		B.extend_u0(24, lane->getParam(ID(B_SIGNED)).as_bool()); | ||||||
|  | 		C.append(A); | ||||||
|  | 		AB.append(B); | ||||||
|  | 		if (GetSize(Y) < 25) | ||||||
|  | 			Y.append(module->addWire(NEW_ID, 25-GetSize(Y))); | ||||||
|  | 		else | ||||||
|  | 			log_assert(GetSize(Y) == 25); | ||||||
|  | 		P.append(Y.extract(0, 24)); | ||||||
|  | 		CARRYOUT.append(module->addWire(NEW_ID)); // TWO24 uses every other bit
 | ||||||
|  | 		CARRYOUT.append(Y[24]); | ||||||
|  | 	}; | ||||||
|  | 	auto g24 = [&f24,module](std::deque<Cell*> &simd24) { | ||||||
|  | 		while (simd24.size() > 1) { | ||||||
|  | 			SigSpec AB; | ||||||
|  | 			SigSpec C; | ||||||
|  | 			SigSpec P; | ||||||
|  | 			SigSpec CARRYOUT; | ||||||
|  | 
 | ||||||
|  | 			Cell *lane1 = simd24.front(); | ||||||
|  | 			simd24.pop_front(); | ||||||
|  | 			Cell *lane2 = simd24.front(); | ||||||
|  | 			simd24.pop_front(); | ||||||
|  | 
 | ||||||
|  | 			log("Analysing %s.%s for Xilinx DSP SIMD24 packing.\n", log_id(module), log_id(lane1)); | ||||||
|  | 
 | ||||||
|  | 			Cell *cell = addDsp(module); | ||||||
|  | 			cell->setParam(ID(USE_SIMD), Const("TWO24")); | ||||||
|  | 			// X = A:B
 | ||||||
|  | 			// Y = 0
 | ||||||
|  | 			// Z = C
 | ||||||
|  | 			cell->setPort(ID(OPMODE), Const::from_string("0110011")); | ||||||
|  | 
 | ||||||
|  | 			log_assert(lane1); | ||||||
|  | 			log_assert(lane2); | ||||||
|  | 			f24(AB, C, P, CARRYOUT, lane1); | ||||||
|  | 			f24(AB, C, P, CARRYOUT, lane2); | ||||||
|  | 			log_assert(GetSize(AB) == 48); | ||||||
|  | 			log_assert(GetSize(C) == 48); | ||||||
|  | 			log_assert(GetSize(P) == 48); | ||||||
|  | 			log_assert(GetSize(CARRYOUT) == 4); | ||||||
|  | 			cell->setPort(ID(A), AB.extract(18, 30)); | ||||||
|  | 			cell->setPort(ID(B), AB.extract(0, 18)); | ||||||
|  | 			cell->setPort(ID(C), C); | ||||||
|  | 			cell->setPort(ID(P), P); | ||||||
|  | 			cell->setPort(ID(CARRYOUT), CARRYOUT); | ||||||
|  | 			if (lane1->type == ID($sub)) | ||||||
|  | 				cell->setPort(ID(ALUMODE), Const::from_string("0011")); | ||||||
|  | 
 | ||||||
|  | 			module->remove(lane1); | ||||||
|  | 			module->remove(lane2); | ||||||
|  | 
 | ||||||
|  | 			module->design->select(module, cell); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 	g24(simd24_add); | ||||||
|  | 	g24(simd24_sub); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void xilinx_dsp_pack(xilinx_dsp_pm &pm) | ||||||
|  | { | ||||||
|  | 	auto &st = pm.st_xilinx_dsp_pack; | ||||||
|  | 
 | ||||||
|  | 	log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp)); | ||||||
|  | 
 | ||||||
|  | 	log_debug("preAdd:     %s\n", log_id(st.preAdd, "--")); | ||||||
|  | 	log_debug("ffAD:       %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--")); | ||||||
|  | 	log_debug("ffA2:       %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--")); | ||||||
|  | 	log_debug("ffA1:       %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--")); | ||||||
|  | 	log_debug("ffB2:       %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--")); | ||||||
|  | 	log_debug("ffB1:       %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--")); | ||||||
|  | 	log_debug("ffD:        %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--")); | ||||||
|  | 	log_debug("dsp:        %s\n", log_id(st.dsp, "--")); | ||||||
|  | 	log_debug("ffM:        %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--")); | ||||||
|  | 	log_debug("postAdd:    %s\n", log_id(st.postAdd, "--")); | ||||||
|  | 	log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--")); | ||||||
|  | 	log_debug("ffP:        %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--")); | ||||||
|  | 	log_debug("overflow:   %s\n", log_id(st.overflow, "--")); | ||||||
|  | 
 | ||||||
|  | 	Cell *cell = st.dsp; | ||||||
|  | 
 | ||||||
|  | 	if (st.preAdd) { | ||||||
|  | 		log("  preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type)); | ||||||
|  | 		bool A_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool(); | ||||||
|  | 		bool D_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool(); | ||||||
|  | 		if (st.sigA == st.preAdd->getPort(ID(B))) | ||||||
|  | 			std::swap(A_SIGNED, D_SIGNED); | ||||||
|  | 		st.sigA.extend_u0(30, A_SIGNED); | ||||||
|  | 		st.sigD.extend_u0(25, D_SIGNED); | ||||||
|  | 		cell->setPort(ID(A), st.sigA); | ||||||
|  | 		cell->setPort(ID(D), st.sigD); | ||||||
|  | 		cell->setPort(ID(INMODE), Const::from_string("00100")); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffAD) { | ||||||
|  | 			if (st.ffADcemux) { | ||||||
|  | 				SigSpec S = st.ffADcemux->getPort(ID(S)); | ||||||
|  | 				cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				cell->setPort(ID(CEAD), State::S1); | ||||||
|  | 			cell->setParam(ID(ADREG), 1); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		cell->setParam(ID(USE_DPORT), Const("TRUE")); | ||||||
|  | 
 | ||||||
|  | 		pm.autoremove(st.preAdd); | ||||||
|  | 	} | ||||||
|  | 	if (st.postAdd) { | ||||||
|  | 		log("  postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type)); | ||||||
|  | 
 | ||||||
|  | 		SigSpec &opmode = cell->connections_.at(ID(OPMODE)); | ||||||
|  | 		if (st.postAddMux) { | ||||||
|  | 			log_assert(st.ffP); | ||||||
|  | 			opmode[4] = st.postAddMux->getPort(ID(S)); | ||||||
|  | 			pm.autoremove(st.postAddMux); | ||||||
|  | 		} | ||||||
|  | 		else if (st.ffP && st.sigC == st.sigP) | ||||||
|  | 			opmode[4] = State::S0; | ||||||
|  | 		else | ||||||
|  | 			opmode[4] = State::S1; | ||||||
|  | 		opmode[6] = State::S0; | ||||||
|  | 		opmode[5] = State::S1; | ||||||
|  | 
 | ||||||
|  | 		if (opmode[4] != State::S0) { | ||||||
|  | 			if (st.postAddMuxAB == ID(A)) | ||||||
|  | 				st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool()); | ||||||
|  | 			else | ||||||
|  | 				st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool()); | ||||||
|  | 			cell->setPort(ID(C), st.sigC); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		pm.autoremove(st.postAdd); | ||||||
|  | 	} | ||||||
|  | 	if (st.overflow) { | ||||||
|  | 		log("  overflow %s (%s)\n", log_id(st.overflow), log_id(st.overflow->type)); | ||||||
|  | 		cell->setParam(ID(USE_PATTERN_DETECT), Const("PATDET")); | ||||||
|  | 		cell->setParam(ID(SEL_PATTERN), Const("PATTERN")); | ||||||
|  | 		cell->setParam(ID(SEL_MASK), Const("MASK")); | ||||||
|  | 
 | ||||||
|  | 		if (st.overflow->type == ID($ge)) { | ||||||
|  | 			Const B = st.overflow->getPort(ID(B)).as_const(); | ||||||
|  | 			log_assert(std::count(B.bits.begin(), B.bits.end(), State::S1) == 1); | ||||||
|  | 			// Since B is an exact power of 2, subtract 1
 | ||||||
|  | 			//   by inverting all bits up until hitting
 | ||||||
|  | 			//   that one hi bit
 | ||||||
|  | 			for (auto &b : B.bits) | ||||||
|  | 				if (b == State::S0) b = State::S1; | ||||||
|  | 				else if (b == State::S1) { | ||||||
|  | 					b = State::S0; | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			B.extu(48); | ||||||
|  | 
 | ||||||
|  | 			cell->setParam(ID(MASK), B); | ||||||
|  | 			cell->setParam(ID(PATTERN), Const(0, 48)); | ||||||
|  | 			cell->setPort(ID(OVERFLOW), st.overflow->getPort(ID(Y))); | ||||||
|  | 		} | ||||||
|  | 		else log_abort(); | ||||||
|  | 
 | ||||||
|  | 		pm.autoremove(st.overflow); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (st.clock != SigBit()) | ||||||
|  | 	{ | ||||||
|  | 		cell->setPort(ID(CLK), st.clock); | ||||||
|  | 
 | ||||||
|  | 		auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { | ||||||
|  | 			SigSpec D = ff->getPort(ID(D)); | ||||||
|  | 			SigSpec Q = pm.sigmap(ff->getPort(ID(Q))); | ||||||
|  | 			if (!A.empty()) | ||||||
|  | 				A.replace(Q, D); | ||||||
|  | 			if (rstmux) { | ||||||
|  | 				SigSpec Y = rstmux->getPort(ID(Y)); | ||||||
|  | 				SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, AB); | ||||||
|  | 				if (rstport != IdString()) { | ||||||
|  | 					SigSpec S = rstmux->getPort(ID(S)); | ||||||
|  | 					cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else if (rstport != IdString()) | ||||||
|  | 				cell->setPort(rstport, State::S0); | ||||||
|  | 			if (cemux) { | ||||||
|  | 				SigSpec Y = cemux->getPort(ID(Y)); | ||||||
|  | 				SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A)); | ||||||
|  | 				SigSpec S = cemux->getPort(ID(S)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, BA); | ||||||
|  | 				cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				cell->setPort(ceport, State::S1); | ||||||
|  | 
 | ||||||
|  | 			for (auto c : Q.chunks()) { | ||||||
|  | 				auto it = c.wire->attributes.find(ID(init)); | ||||||
|  | 				if (it == c.wire->attributes.end()) | ||||||
|  | 					continue; | ||||||
|  | 				for (int i = c.offset; i < c.offset+c.width; i++) { | ||||||
|  | 					log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx); | ||||||
|  | 					it->second[i] = State::Sx; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		if (st.ffA2) { | ||||||
|  | 			SigSpec A = cell->getPort(ID(A)); | ||||||
|  | 			f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA)); | ||||||
|  | 			if (st.ffA1) { | ||||||
|  | 				f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString()); | ||||||
|  | 				cell->setParam(ID(AREG), 2); | ||||||
|  | 				cell->setParam(ID(ACASCREG), 2); | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				cell->setParam(ID(AREG), 1); | ||||||
|  | 				cell->setParam(ID(ACASCREG), 1); | ||||||
|  | 			} | ||||||
|  | 			pm.add_siguser(A, cell); | ||||||
|  | 			cell->setPort(ID(A), A); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffB2) { | ||||||
|  | 			SigSpec B = cell->getPort(ID(B)); | ||||||
|  | 			f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB)); | ||||||
|  | 			if (st.ffB1) { | ||||||
|  | 				f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString()); | ||||||
|  | 				cell->setParam(ID(BREG), 2); | ||||||
|  | 				cell->setParam(ID(BCASCREG), 2); | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				cell->setParam(ID(BREG), 1); | ||||||
|  | 				cell->setParam(ID(BCASCREG), 1); | ||||||
|  | 			} | ||||||
|  | 			pm.add_siguser(B, cell); | ||||||
|  | 			cell->setPort(ID(B), B); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffD) { | ||||||
|  | 			SigSpec D = cell->getPort(ID(D)); | ||||||
|  | 			f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD)); | ||||||
|  | 			pm.add_siguser(D, cell); | ||||||
|  | 			cell->setPort(ID(D), D); | ||||||
|  | 			cell->setParam(ID(DREG), 1); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffM) { | ||||||
|  | 			SigSpec M; // unused
 | ||||||
|  | 			f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM)); | ||||||
|  | 			st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM))); | ||||||
|  | 			cell->setParam(ID(MREG), State::S1); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffP) { | ||||||
|  | 			SigSpec P; // unused
 | ||||||
|  | 			f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP)); | ||||||
|  | 			st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP))); | ||||||
|  | 			cell->setParam(ID(PREG), State::S1); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		log("  clock: %s (%s)", log_signal(st.clock), "posedge"); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffA2) { | ||||||
|  | 			log(" ffA2:%s", log_id(st.ffA2)); | ||||||
|  | 			if (st.ffA1) | ||||||
|  | 				log(" ffA1:%s", log_id(st.ffA1)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (st.ffAD) | ||||||
|  | 			log(" ffAD:%s", log_id(st.ffAD)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffB2) { | ||||||
|  | 			log(" ffB2:%s", log_id(st.ffB2)); | ||||||
|  | 			if (st.ffB1) | ||||||
|  | 				log(" ffB1:%s", log_id(st.ffB1)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (st.ffD) | ||||||
|  | 			log(" ffD:%s", log_id(st.ffD)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffM) | ||||||
|  | 			log(" ffM:%s", log_id(st.ffM)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffP) | ||||||
|  | 			log(" ffP:%s", log_id(st.ffP)); | ||||||
|  | 	} | ||||||
|  | 	log("\n"); | ||||||
|  | 
 | ||||||
|  | 	SigSpec P = st.sigP; | ||||||
|  | 	if (GetSize(P) < 48) | ||||||
|  | 		P.append(pm.module->addWire(NEW_ID, 48-GetSize(P))); | ||||||
|  | 	cell->setPort(ID(P), P); | ||||||
|  | 
 | ||||||
|  | 	pm.blacklist(cell); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) | ||||||
|  | { | ||||||
|  | 	auto &st = pm.st_xilinx_dsp_packC; | ||||||
|  | 
 | ||||||
|  | 	log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp)); | ||||||
|  | 	log_debug("ffC:        %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--")); | ||||||
|  | 
 | ||||||
|  | 	Cell *cell = st.dsp; | ||||||
|  | 
 | ||||||
|  | 	if (st.clock != SigBit()) | ||||||
|  | 	{ | ||||||
|  | 		cell->setPort(ID(CLK), st.clock); | ||||||
|  | 
 | ||||||
|  | 		auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { | ||||||
|  | 			SigSpec D = ff->getPort(ID(D)); | ||||||
|  | 			SigSpec Q = pm.sigmap(ff->getPort(ID(Q))); | ||||||
|  | 			if (!A.empty()) | ||||||
|  | 				A.replace(Q, D); | ||||||
|  | 			if (rstmux) { | ||||||
|  | 				SigSpec Y = rstmux->getPort(ID(Y)); | ||||||
|  | 				SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, AB); | ||||||
|  | 				if (rstport != IdString()) { | ||||||
|  | 					SigSpec S = rstmux->getPort(ID(S)); | ||||||
|  | 					cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else if (rstport != IdString()) | ||||||
|  | 				cell->setPort(rstport, State::S0); | ||||||
|  | 			if (cemux) { | ||||||
|  | 				SigSpec Y = cemux->getPort(ID(Y)); | ||||||
|  | 				SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A)); | ||||||
|  | 				SigSpec S = cemux->getPort(ID(S)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, BA); | ||||||
|  | 				cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				cell->setPort(ceport, State::S1); | ||||||
|  | 
 | ||||||
|  | 			for (auto c : Q.chunks()) { | ||||||
|  | 				auto it = c.wire->attributes.find(ID(init)); | ||||||
|  | 				if (it == c.wire->attributes.end()) | ||||||
|  | 					continue; | ||||||
|  | 				for (int i = c.offset; i < c.offset+c.width; i++) { | ||||||
|  | 					log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx); | ||||||
|  | 					it->second[i] = State::Sx; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		if (st.ffC) { | ||||||
|  | 			SigSpec C = cell->getPort(ID(C)); | ||||||
|  | 			f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC)); | ||||||
|  | 			pm.add_siguser(C, cell); | ||||||
|  | 			cell->setPort(ID(C), C); | ||||||
|  | 			cell->setParam(ID(CREG), 1); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		log("  clock: %s (%s)", log_signal(st.clock), "posedge"); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffC) | ||||||
|  | 			log(" ffC:%s", log_id(st.ffC)); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pm.blacklist(cell); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct XilinxDspPass : public Pass { | ||||||
|  | 	XilinxDspPass() : Pass("xilinx_dsp", "Xilinx: pack resources into DSPs") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    xilinx_dsp [options] [selection]\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Pack input registers (A2, A1, B2, B1, C, D, AD; with optional enable/reset),\n"); | ||||||
|  | 		log("pipeline registers (M; with optional enable/reset), output registers (P; with\n"); | ||||||
|  | 		log("optional enable/reset), pre-adder and/or post-adder into Xilinx DSP resources.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n"); | ||||||
|  | 		log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n"); | ||||||
|  | 		log("used to override the current accumulation result with a new value, which will\n"); | ||||||
|  | 		log("be added to the multiplier result to form the next accumulation result.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n"); | ||||||
|  | 		log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n"); | ||||||
|  | 		log("input to the post-adder -- a pattern common for summing partial products to\n"); | ||||||
|  | 		log("implement wide multipliers). Limited support also exists for similar cascading\n"); | ||||||
|  | 		log("for A and B using '[AB]COUT' -> '[AB]CIN'. Currently, cascade chains are limited\n"); | ||||||
|  | 		log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n"); | ||||||
|  | 		log("device.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n"); | ||||||
|  | 		log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n"); | ||||||
|  | 		log("the add/subtract operator will cause those operations to be implemented using\n"); | ||||||
|  | 		log("the 'SIMD' feature of DSPs.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Experimental feature: the presence of a `$ge' cell attached to the registered\n"); | ||||||
|  | 		log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n"); | ||||||
|  | 		log("into using the DSP48E1's pattern detector feature for overflow detection.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n"); | ||||||
|  | 
 | ||||||
|  | 		size_t argidx; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
|  | 		{ | ||||||
|  | 			// if (args[argidx] == "-singleton") {
 | ||||||
|  | 			// 	singleton_mode = true;
 | ||||||
|  | 			// 	continue;
 | ||||||
|  | 			// }
 | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  | 		for (auto module : design->selected_modules()) { | ||||||
|  | 			// Experimental feature: pack $add/$sub cells with
 | ||||||
|  | 			//   (* use_dsp48="simd" *) into DSP48E1's using its
 | ||||||
|  | 			//   SIMD feature
 | ||||||
|  | 			xilinx_simd_pack(module, module->selected_cells()); | ||||||
|  | 
 | ||||||
|  | 			// Match for all features ([ABDMP][12]?REG, pre-adder,
 | ||||||
|  | 			// post-adder, pattern detector, etc.) except for CREG
 | ||||||
|  | 			{ | ||||||
|  | 				xilinx_dsp_pm pm(module, module->selected_cells()); | ||||||
|  | 				pm.run_xilinx_dsp_pack(xilinx_dsp_pack); | ||||||
|  | 			} | ||||||
|  | 			// Separating out CREG packing is necessary since there
 | ||||||
|  | 			//   is no guarantee that the cell ordering corresponds
 | ||||||
|  | 			//   to the "expected" case (i.e. the order in which
 | ||||||
|  | 			//   they appear in the source) thus the possiblity
 | ||||||
|  | 			//   existed that a register got packed as a CREG into a
 | ||||||
|  | 			//   downstream DSP that should have otherwise been a
 | ||||||
|  | 			//   PREG of an upstream DSP that had not been visited
 | ||||||
|  | 			//   yet
 | ||||||
|  | 			{ | ||||||
|  | 				xilinx_dsp_CREG_pm pm(module, module->selected_cells()); | ||||||
|  | 				pm.run_xilinx_dsp_packC(xilinx_dsp_packC); | ||||||
|  | 			} | ||||||
|  | 			// Lastly, identify and utilise PCOUT -> PCIN,
 | ||||||
|  | 			//   ACOUT -> ACIN, and BCOUT-> BCIN dedicated cascade
 | ||||||
|  | 			//   chains
 | ||||||
|  | 			{ | ||||||
|  | 				xilinx_dsp_cascade_pm pm(module, module->selected_cells()); | ||||||
|  | 				pm.run_xilinx_dsp_cascade(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } XilinxDspPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
							
								
								
									
										725
									
								
								passes/pmgen/xilinx_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										725
									
								
								passes/pmgen/xilinx_dsp.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,725 @@ | ||||||
|  | // This file describes the main pattern matcher setup (of three total) that | ||||||
|  | //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | ||||||
|  | // At a high level, it works as follows: | ||||||
|  | //   ( 1) Starting from a DSP48E1 cell | ||||||
|  | //   ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG) | ||||||
|  | //        (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //         reset functionality, using a subpattern discussed below) | ||||||
|  | //        If ADREG matched, treat 'A' input as input of ADREG | ||||||
|  | //   ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell | ||||||
|  | //       (pre-adder) | ||||||
|  | //   ( 4) If pre-adder was present, find match 'A' input for A2REG | ||||||
|  | //        If pre-adder was not present, move ADREG to A2REG | ||||||
|  | //        If A2REG, then match 'A' input for A1REG | ||||||
|  | //   ( 5) Match 'B' input for B2REG | ||||||
|  | //        If B2REG, then match 'B' input for B1REG | ||||||
|  | //   ( 6) Match 'D' input for DREG | ||||||
|  | //   ( 7) Match 'P' output that exclusively drives an MREG | ||||||
|  | //   ( 8) Match 'P' output that exclusively drives one of two inputs to an $add | ||||||
|  | //        cell (post-adder). | ||||||
|  | //        The other input to the adder is assumed to come in from the 'C' input | ||||||
|  | //        (note: 'P' -> 'C' connections that exist for accumulators are | ||||||
|  | //         recognised in xilinx_dsp.cc). | ||||||
|  | //   ( 9) Match 'P' output that exclusively drives a PREG | ||||||
|  | //   (10) If post-adder and PREG both present, match for a $mux cell driving | ||||||
|  | //        the 'C' input, where one of the $mux's inputs is the PREG output. | ||||||
|  | //        This indicates an accumulator situation, and one where a $mux exists | ||||||
|  | //        to override the accumulated value: | ||||||
|  | //             +--------------------------------+ | ||||||
|  | //             |   ____                         | | ||||||
|  | //             +--|    \                        | | ||||||
|  | //                |$mux|-+                      | | ||||||
|  | //         'C' ---|____/ |                      | | ||||||
|  | //                       | /-------\   +----+   | | ||||||
|  | //            +----+     +-| post- |___|PREG|---+ 'P' | ||||||
|  | //            |MREG|------ | adder |   +----+ | ||||||
|  | //            +----+       \-------/ | ||||||
|  | //   (11) If PREG present, match for a greater-than-or-equal $ge cell attached | ||||||
|  | //        to the 'P' output where it is compared to a constant that is a | ||||||
|  | //        power-of-2: e.g. `assign overflow = (PREG >= 2**40);` | ||||||
|  | //        In this scenario, the pattern detector functionality of a DSP48E1 can | ||||||
|  | //        to implement this function | ||||||
|  | // Notes: | ||||||
|  | //   - The intention of this pattern matcher is for it to be compatible with | ||||||
|  | //     DSP48E1 cells inferred from multiply operations by Yosys, as well as for | ||||||
|  | //     user instantiations that may already contain the cells being packed... | ||||||
|  | //     (though the latter is currently untested) | ||||||
|  | //   - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used | ||||||
|  | //     for each *REG match, it has been factored out into two subpatterns: | ||||||
|  | //     in_dffe and out_dffe located at the bottom of this file. | ||||||
|  | //   - Matching for pattern detector features is currently incomplete. For | ||||||
|  | //     example, matching for underflow as well as overflow detection is | ||||||
|  | //     possible, as would auto-reset, enabling saturated arithmetic, detecting | ||||||
|  | //     custom patterns, etc. | ||||||
|  | 
 | ||||||
|  | pattern xilinx_dsp_pack | ||||||
|  | 
 | ||||||
|  | state <SigBit> clock | ||||||
|  | state <SigSpec> sigA sigB sigC sigD sigM sigP | ||||||
|  | state <IdString> postAddAB postAddMuxAB | ||||||
|  | state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol | ||||||
|  | state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol | ||||||
|  | state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux | ||||||
|  | state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux | ||||||
|  | state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux | ||||||
|  | 
 | ||||||
|  | // Variables used for subpatterns | ||||||
|  | state <SigSpec> argQ argD | ||||||
|  | state <bool> ffcepol ffrstpol | ||||||
|  | state <int> ffoffset | ||||||
|  | udata <SigSpec> dffD dffQ | ||||||
|  | udata <SigBit> dffclock | ||||||
|  | udata <Cell*> dff dffcemux dffrstmux | ||||||
|  | udata <bool> dffcepol dffrstpol | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a DSP48E1 cell | ||||||
|  | match dsp | ||||||
|  | 	select dsp->type.in(\DSP48E1) | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigA sigB sigC sigD sigM clock | ||||||
|  | 	auto unextend = [](const SigSpec &sig) { | ||||||
|  | 		int i; | ||||||
|  | 		for (i = GetSize(sig)-1; i > 0; i--) | ||||||
|  | 			if (sig[i] != sig[i-1]) | ||||||
|  | 				break; | ||||||
|  | 		// Do not remove non-const sign bit | ||||||
|  | 		if (sig[i].wire) | ||||||
|  | 			++i; | ||||||
|  | 		return sig.extract(0, i); | ||||||
|  | 	}; | ||||||
|  | 	sigA = unextend(port(dsp, \A)); | ||||||
|  | 	sigB = unextend(port(dsp, \B)); | ||||||
|  | 
 | ||||||
|  | 	sigC = port(dsp, \C, SigSpec()); | ||||||
|  | 	sigD = port(dsp, \D, SigSpec()); | ||||||
|  | 
 | ||||||
|  | 	SigSpec P = port(dsp, \P); | ||||||
|  | 	if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { | ||||||
|  | 		// Only care about those bits that are used | ||||||
|  | 		int i; | ||||||
|  | 		for (i = 0; i < GetSize(P); i++) { | ||||||
|  | 			if (nusers(P[i]) <= 1) | ||||||
|  | 				break; | ||||||
|  | 			sigM.append(P[i]); | ||||||
|  | 		} | ||||||
|  | 		log_assert(nusers(P.extract_end(i)) <= 1); | ||||||
|  | 		// This sigM could have no users if downstream sinks (e.g. $add) is | ||||||
|  | 		//   narrower than $mul result, for example | ||||||
|  | 		if (sigM.empty()) | ||||||
|  | 			reject; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		sigM = P; | ||||||
|  | 
 | ||||||
|  | 	clock = port(dsp, \CLK, SigBit()); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match the driver of the 'A' input to a possible $dff cell (ADREG) | ||||||
|  | //     (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //      reset functionality, using a subpattern discussed above) | ||||||
|  | //     If matched, treat 'A' input as input of ADREG | ||||||
|  | code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock | ||||||
|  | 	if (param(dsp, \ADREG).as_int() == 0) { | ||||||
|  | 		argQ = sigA; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffAD = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffADrstmux = dffrstmux; | ||||||
|  | 				ffADrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffADcemux = dffcemux; | ||||||
|  | 				ffADcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigA = dffD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match the driver of the 'A' and 'D' inputs for a possible $add cell | ||||||
|  | //     (pre-adder) | ||||||
|  | match preAdd | ||||||
|  | 	if sigD.empty() || sigD.is_fully_zero() | ||||||
|  | 	// Ensure that preAdder not already used | ||||||
|  | 	if param(dsp, \USE_DPORT, Const("FALSE")).decode_string() == "FALSE" | ||||||
|  | 	if port(dsp, \INMODE, Const(0, 5)).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	select preAdd->type.in($add) | ||||||
|  | 	// Output has to be 25 bits or less | ||||||
|  | 	select GetSize(port(preAdd, \Y)) <= 25 | ||||||
|  | 	select nusers(port(preAdd, \Y)) == 2 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	// A port has to be 30 bits or less | ||||||
|  | 	select GetSize(port(preAdd, AB)) <= 30 | ||||||
|  | 	define <IdString> BA (AB == \A ? \B : \A) | ||||||
|  | 	// D port has to be 25 bits or less | ||||||
|  | 	select GetSize(port(preAdd, BA)) <= 25 | ||||||
|  | 	index <SigSpec> port(preAdd, \Y) === sigA | ||||||
|  | 
 | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigA sigD | ||||||
|  | 	if (preAdd) { | ||||||
|  | 		sigA = port(preAdd, \A); | ||||||
|  | 		sigD = port(preAdd, \B); | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (4) If pre-adder was present, find match 'A' input for A2REG | ||||||
|  | //     If pre-adder was not present, move ADREG to A2REG | ||||||
|  | //     Then match 'A' input for A1REG | ||||||
|  | code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol | ||||||
|  | 	// Only search for ffA2 if there was a pre-adder | ||||||
|  | 	//   (otherwise ffA2 would have been matched as ffAD) | ||||||
|  | 	if (preAdd) { | ||||||
|  | 		if (param(dsp, \AREG).as_int() == 0) { | ||||||
|  | 			argQ = sigA; | ||||||
|  | 			subpattern(in_dffe); | ||||||
|  | 			if (dff) { | ||||||
|  | 				ffA2 = dff; | ||||||
|  | 				clock = dffclock; | ||||||
|  | 				if (dffrstmux) { | ||||||
|  | 					ffA2rstmux = dffrstmux; | ||||||
|  | 					ffArstpol = dffrstpol; | ||||||
|  | 				} | ||||||
|  | 				if (dffcemux) { | ||||||
|  | 					ffA2cepol = dffcepol; | ||||||
|  | 					ffA2cemux = dffcemux; | ||||||
|  | 				} | ||||||
|  | 				sigA = dffD; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// And if there wasn't a pre-adder, | ||||||
|  | 	//   move AD register to A | ||||||
|  | 	else if (ffAD) { | ||||||
|  | 		log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux); | ||||||
|  | 		std::swap(ffA2, ffAD); | ||||||
|  | 		std::swap(ffA2cemux, ffADcemux); | ||||||
|  | 		std::swap(ffA2rstmux, ffADrstmux); | ||||||
|  | 		ffA2cepol = ffADcepol; | ||||||
|  | 		ffArstpol = ffADrstpol; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Now attempt to match A1 | ||||||
|  | 	if (ffA2) { | ||||||
|  | 		argQ = sigA; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr)) | ||||||
|  | 				goto ffA1_end; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				if (ffArstpol != dffrstpol) | ||||||
|  | 					goto ffA1_end; | ||||||
|  | 				if (port(ffA2rstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 					goto ffA1_end; | ||||||
|  | 				ffA1rstmux = dffrstmux; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			ffA1 = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 
 | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffA1cemux = dffcemux; | ||||||
|  | 				ffA1cepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigA = dffD; | ||||||
|  | 
 | ||||||
|  | ffA1_end:		; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (5) Match 'B' input for B2REG | ||||||
|  | //     If B2REG, then match 'B' input for B1REG | ||||||
|  | code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol | ||||||
|  | 	if (param(dsp, \BREG).as_int() == 0) { | ||||||
|  | 		argQ = sigB; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffB2 = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffB2rstmux = dffrstmux; | ||||||
|  | 				ffBrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffB2cemux = dffcemux; | ||||||
|  | 				ffB2cepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigB = dffD; | ||||||
|  | 
 | ||||||
|  | 			// Now attempt to match B1 | ||||||
|  | 			if (ffB2) { | ||||||
|  | 				argQ = sigB; | ||||||
|  | 				subpattern(in_dffe); | ||||||
|  | 				if (dff) { | ||||||
|  | 					if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr)) | ||||||
|  | 						goto ffB1_end; | ||||||
|  | 					if (dffrstmux) { | ||||||
|  | 						if (ffBrstpol != dffrstpol) | ||||||
|  | 							goto ffB1_end; | ||||||
|  | 						if (port(ffB2rstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 							goto ffB1_end; | ||||||
|  | 						ffB1rstmux = dffrstmux; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					ffB1 = dff; | ||||||
|  | 					clock = dffclock; | ||||||
|  | 
 | ||||||
|  | 					if (dffcemux) { | ||||||
|  | 						ffB1cemux = dffcemux; | ||||||
|  | 						ffB1cepol = dffcepol; | ||||||
|  | 					} | ||||||
|  | 					sigB = dffD; | ||||||
|  | 
 | ||||||
|  | ffB1_end:				; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (6) Match 'D' input for DREG | ||||||
|  | code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock | ||||||
|  | 	if (param(dsp, \DREG).as_int() == 0) { | ||||||
|  | 		argQ = sigD; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffD = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffDrstmux = dffrstmux; | ||||||
|  | 				ffDrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffDcemux = dffcemux; | ||||||
|  | 				ffDcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigD = dffD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (7) Match 'P' output that exclusively drives an MREG | ||||||
|  | code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock | ||||||
|  | 	if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { | ||||||
|  | 		argD = sigM; | ||||||
|  | 		subpattern(out_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffM = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffMrstmux = dffrstmux; | ||||||
|  | 				ffMrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffMcemux = dffcemux; | ||||||
|  | 				ffMcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigM = dffQ; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	sigP = sigM; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (8) Match 'P' output that exclusively drives one of two inputs to an $add | ||||||
|  | //     cell (post-adder). | ||||||
|  | //     The other input to the adder is assumed to come in from the 'C' input | ||||||
|  | //     (note: 'P' -> 'C' connections that exist for accumulators are | ||||||
|  | //      recognised in xilinx_dsp.cc). | ||||||
|  | match postAdd | ||||||
|  | 	// Ensure that Z mux is not already used | ||||||
|  | 	if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	select postAdd->type.in($add) | ||||||
|  | 	select GetSize(port(postAdd, \Y)) <= 48 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	select nusers(port(postAdd, AB)) <= 3 | ||||||
|  | 	filter ffMcemux || nusers(port(postAdd, AB)) == 2 | ||||||
|  | 	filter !ffMcemux || nusers(port(postAdd, AB)) == 3 | ||||||
|  | 
 | ||||||
|  | 	index <SigBit> port(postAdd, AB)[0] === sigP[0] | ||||||
|  | 	filter GetSize(port(postAdd, AB)) >= GetSize(sigP) | ||||||
|  | 	filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP | ||||||
|  | 	// Check that remainder of AB is a sign-extension | ||||||
|  | 	define <bool> AB_SIGNED (param(postAdd, AB == \A ? \A_SIGNED : \B_SIGNED).as_bool()) | ||||||
|  | 	filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(AB_SIGNED ? sigP[GetSize(sigP)-1] : State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) | ||||||
|  | 	set postAddAB AB | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigC sigP | ||||||
|  | 	if (postAdd) { | ||||||
|  | 		sigC = port(postAdd, postAddAB == \A ? \B : \A); | ||||||
|  | 		sigP = port(postAdd, \Y); | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (9) Match 'P' output that exclusively drives a PREG | ||||||
|  | code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock | ||||||
|  | 	if (param(dsp, \PREG).as_int() == 0) { | ||||||
|  | 		int users = 2; | ||||||
|  | 		// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux | ||||||
|  | 		if (ffMcemux && !postAdd) users++; | ||||||
|  | 		if (nusers(sigP) == users) { | ||||||
|  | 			argD = sigP; | ||||||
|  | 			subpattern(out_dffe); | ||||||
|  | 			if (dff) { | ||||||
|  | 				ffP = dff; | ||||||
|  | 				clock = dffclock; | ||||||
|  | 				if (dffrstmux) { | ||||||
|  | 					ffPrstmux = dffrstmux; | ||||||
|  | 					ffPrstpol = dffrstpol; | ||||||
|  | 				} | ||||||
|  | 				if (dffcemux) { | ||||||
|  | 					ffPcemux = dffcemux; | ||||||
|  | 					ffPcepol = dffcepol; | ||||||
|  | 				} | ||||||
|  | 				sigP = dffQ; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (10) If post-adder and PREG both present, match for a $mux cell driving | ||||||
|  | //      the 'C' input, where one of the $mux's inputs is the PREG output. | ||||||
|  | //      This indicates an accumulator situation, and one where a $mux exists | ||||||
|  | //      to override the accumulated value: | ||||||
|  | //           +--------------------------------+ | ||||||
|  | //           |   ____                         | | ||||||
|  | //           +--|    \                        | | ||||||
|  | //              |$mux|-+                      | | ||||||
|  | //       'C' ---|____/ |                      | | ||||||
|  | //                     | /-------\   +----+   | | ||||||
|  | //          +----+     +-| post- |___|PREG|---+ 'P' | ||||||
|  | //          |MREG|------ | adder |   +----+ | ||||||
|  | //          +----+       \-------/ | ||||||
|  | match postAddMux | ||||||
|  | 	if postAdd | ||||||
|  | 	if ffP | ||||||
|  | 	select postAddMux->type.in($mux) | ||||||
|  | 	select nusers(port(postAddMux, \Y)) == 2 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(postAddMux, AB) === sigP | ||||||
|  | 	index <SigSpec> port(postAddMux, \Y) === sigC | ||||||
|  | 	set postAddMuxAB AB | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigC | ||||||
|  | 	if (postAddMux) | ||||||
|  | 		sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (11) If PREG present, match for a greater-than-or-equal $ge cell attached to | ||||||
|  | //      the 'P' output where it is compared to a constant that is a power-of-2: | ||||||
|  | //      e.g. `assign overflow = (PREG >= 2**40);` | ||||||
|  | //      In this scenario, the pattern detector functionality of a DSP48E1 can | ||||||
|  | //      to implement this function | ||||||
|  | match overflow | ||||||
|  | 	if ffP | ||||||
|  | 	if param(dsp, \USE_PATTERN_DETECT, Const("NO_PATDET")).decode_string() == "NO_PATDET" | ||||||
|  | 	select overflow->type.in($ge) | ||||||
|  | 	select GetSize(port(overflow, \Y)) <= 48 | ||||||
|  | 	select port(overflow, \B).is_fully_const() | ||||||
|  | 	define <Const> B port(overflow, \B).as_const() | ||||||
|  | 	select std::count(B.bits.begin(), B.bits.end(), State::S1) == 1 | ||||||
|  | 	index <SigSpec> port(overflow, \A) === sigP | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	accept; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against input registers, based on knowledge of the | ||||||
|  | //   'Q' input. Typically, identifying registers with clock-enable and reset | ||||||
|  | //   capability would be a task would be handled by other Yosys passes such as | ||||||
|  | //   dff2dffe, but since DSP inference happens much before this, these patterns | ||||||
|  | //   have to be manually identified. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //       'Q' argument | ||||||
|  | //   (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //       one that exclusively drives the 'D' input of the $dff, with one of its | ||||||
|  | //       $mux inputs being fully zero | ||||||
|  | //   (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //       exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //       the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //       the 'Q' output of the $dff | ||||||
|  | subpattern in_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (const auto &c : argQ.chunks()) { | ||||||
|  | 		// Abandon matches when 'Q' is a constant | ||||||
|  | 		if (!c.wire) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 		// (not supported by DSP48E1) | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		if (!init.empty()) | ||||||
|  | 			for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 				if (b != State::Sx && b != State::S0) | ||||||
|  | 					reject; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //     'Q' argument | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \Q)[offset] === argQ[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argQ is present | ||||||
|  | 	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) | ||||||
|  | 	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ argD | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | 	dffD = argQ; | ||||||
|  | 	argD = port(ff, \D); | ||||||
|  | 	argQ = Q; | ||||||
|  | 	dffD.replace(argQ, argD); | ||||||
|  | 	// Only search for ffrstmux if dffD only | ||||||
|  | 	//   has two (ff, ffrstmux) users | ||||||
|  | 	if (nusers(dffD) > 2) | ||||||
|  | 		argD = SigSpec(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //     exclusively drives the 'D' input of the $dff, with one of the $mux | ||||||
|  | //     inputs being fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffrstmux, \Y) === argD | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 		argD = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffrstmux, \Y), argD); | ||||||
|  | 
 | ||||||
|  | 		// Only search for ffcemux if argQ has at | ||||||
|  | 		//   least 3 users (ff, <upstream>, ffrstmux) and | ||||||
|  | 		//   dffD only has two (ff, ffrstmux) | ||||||
|  | 		if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) | ||||||
|  | 			argD = SigSpec(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffrstmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //     exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //     the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //     the 'Q' output of the $dff | ||||||
|  | match ffcemux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffcemux, \Y) === argD | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(ffcemux, AB) === argQ | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 		argD = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		dffD.replace(port(ffcemux, \Y), argD); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffcemux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against output registers, based on knowledge of the | ||||||
|  | //   'D' input. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from an optional $mux cell that implements clock enable | ||||||
|  | //       semantics --- one where the given 'D' argument (partially or fully) | ||||||
|  | //       drives one of its two inputs | ||||||
|  | //   (2) Starting from, or continuing onto, another optional $mux cell that | ||||||
|  | //       implements synchronous reset semantics --- one where the given 'D' | ||||||
|  | //       argument (or the clock enable $mux output) drives one of its two inputs | ||||||
|  | //       and where the other input is fully zero | ||||||
|  | //   (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the | ||||||
|  | //       output of the previous clock enable or reset $mux cells) | ||||||
|  | subpattern out_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (auto c : argD.chunks()) | ||||||
|  | 		// Abandon matches when 'D' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from an optional $mux cell that implements clock enable | ||||||
|  | //     semantics --- one where the given 'D' argument (partially or fully) | ||||||
|  | //     drives one of its two inputs | ||||||
|  | match ffcemux | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	// ffcemux output must have two users: ffcemux and ff.D | ||||||
|  | 	select nusers(port(ffcemux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s) | ||||||
|  | 	select nusers(port(ffcemux, AB)) >= 3 | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffcemux, \Y)) | ||||||
|  | 	define <IdString> BA (AB == \A ? \B : \A) | ||||||
|  | 	index <SigBit> port(ffcemux, BA)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffcemux = ffcemux; | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		SigSpec BA = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		SigSpec Y = port(ffcemux, \Y); | ||||||
|  | 		argQ = argD; | ||||||
|  | 		argD.replace(BA, Y); | ||||||
|  | 		argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); | ||||||
|  | 
 | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Starting from, or continuing onto, another optional $mux cell that | ||||||
|  | //     implements synchronous reset semantics --- one where the given 'D' | ||||||
|  | //     argument (or the clock enable $mux output) drives one of its two inputs | ||||||
|  | //     and where the other input is fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	// ffrstmux output must have two users: ffrstmux and ff.D | ||||||
|  | 	select nusers(port(ffrstmux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffrstmux, \Y)) | ||||||
|  | 	define <IdString> AB (BA == \B ? \A : \B) | ||||||
|  | 	index <SigBit> port(ffrstmux, AB)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter !ffcemux || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffrstmux = ffrstmux; | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		SigSpec Y = port(ffrstmux, \Y); | ||||||
|  | 		argD.replace(AB, Y); | ||||||
|  | 
 | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the | ||||||
|  | //     output of the previous clock enable or reset $mux cells) | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \D)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter (!ffcemux && !ffrstmux) || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ff, \D)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ff, \D).extract(offset, GetSize(argD)) == argD | ||||||
|  | 	// Check that FF.Q is connected to CE-mux | ||||||
|  | 	filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ | ||||||
|  | 	SigSpec D = port(ff, \D); | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	if (!ffcemux) { | ||||||
|  | 		argQ = argD; | ||||||
|  | 		argQ.replace(D, Q); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 	// (not supported by DSP48E1) | ||||||
|  | 	for (auto c : argQ.chunks()) { | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		if (!init.empty()) | ||||||
|  | 			for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 				if (b != State::Sx && b != State::S0) | ||||||
|  | 					reject; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffQ = argQ; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | endcode | ||||||
							
								
								
									
										234
									
								
								passes/pmgen/xilinx_dsp_CREG.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								passes/pmgen/xilinx_dsp_CREG.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,234 @@ | ||||||
|  | // This file describes the second of three pattern matcher setups that | ||||||
|  | //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | ||||||
|  | // At a high level, it works as follows: | ||||||
|  | //   (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, | ||||||
|  | //       and (b) uses the 'C' port | ||||||
|  | //   (2) Match the driver of the 'C' input to a possible $dff cell (CREG) | ||||||
|  | //       (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //        reset functionality, using a subpattern discussed below) | ||||||
|  | // Notes: | ||||||
|  | //   - Running CREG packing after xilinx_dsp_pack is necessary since there is no | ||||||
|  | //     guarantee that the cell ordering corresponds to the "expected" case (i.e. | ||||||
|  | //     the order in which they appear in the source) thus the possiblity existed | ||||||
|  | //     that a register got packed as a CREG into a downstream DSP that should | ||||||
|  | //     have otherwise been a PREG of an upstream DSP that had not been visited | ||||||
|  | //     yet | ||||||
|  | //   - The reason this is separated out from the xilinx_dsp.pmg file is | ||||||
|  | //     for efficiency --- each *.pmg file creates a class of the same basename, | ||||||
|  | //     which when constructed, creates a custom database tailored to the | ||||||
|  | //     pattern(s) contained within. Since the pattern in this file must be | ||||||
|  | //     executed after the pattern contained in xilinx_dsp.pmg, it is necessary | ||||||
|  | //     to reconstruct this database. Separating the two patterns into | ||||||
|  | //     independent files causes two smaller, more specific, databases. | ||||||
|  | 
 | ||||||
|  | pattern xilinx_dsp_packC | ||||||
|  | 
 | ||||||
|  | udata <std::function<SigSpec(const SigSpec&)>> unextend | ||||||
|  | state <SigBit> clock | ||||||
|  | state <SigSpec> sigC sigP | ||||||
|  | state <bool> ffCcepol ffCrstpol | ||||||
|  | state <Cell*> ffC ffCcemux ffCrstmux | ||||||
|  | 
 | ||||||
|  | // Variables used for subpatterns | ||||||
|  | state <SigSpec> argQ argD | ||||||
|  | state <bool> ffcepol ffrstpol | ||||||
|  | state <int> ffoffset | ||||||
|  | udata <SigSpec> dffD dffQ | ||||||
|  | udata <SigBit> dffclock | ||||||
|  | udata <Cell*> dff dffcemux dffrstmux | ||||||
|  | udata <bool> dffcepol dffrstpol | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, | ||||||
|  | //     and (b) uses the 'C' port | ||||||
|  | match dsp | ||||||
|  | 	select dsp->type.in(\DSP48E1) | ||||||
|  | 	select param(dsp, \CREG, 1).as_int() == 0 | ||||||
|  | 	select nusers(port(dsp, \C, SigSpec())) > 1 | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigC sigP clock | ||||||
|  | 	unextend = [](const SigSpec &sig) { | ||||||
|  | 		int i; | ||||||
|  | 		for (i = GetSize(sig)-1; i > 0; i--) | ||||||
|  | 			if (sig[i] != sig[i-1]) | ||||||
|  | 				break; | ||||||
|  | 		// Do not remove non-const sign bit | ||||||
|  | 		if (sig[i].wire) | ||||||
|  | 			++i; | ||||||
|  | 		return sig.extract(0, i); | ||||||
|  | 	}; | ||||||
|  | 	sigC = unextend(port(dsp, \C, SigSpec())); | ||||||
|  | 
 | ||||||
|  | 	SigSpec P = port(dsp, \P); | ||||||
|  | 	if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { | ||||||
|  | 		// Only care about those bits that are used | ||||||
|  | 		int i; | ||||||
|  | 		for (i = 0; i < GetSize(P); i++) { | ||||||
|  | 			if (nusers(P[i]) <= 1) | ||||||
|  | 				break; | ||||||
|  | 			sigP.append(P[i]); | ||||||
|  | 		} | ||||||
|  | 		log_assert(nusers(P.extract_end(i)) <= 1); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		sigP = P; | ||||||
|  | 
 | ||||||
|  | 	clock = port(dsp, \CLK, SigBit()); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match the driver of the 'C' input to a possible $dff cell (CREG) | ||||||
|  | //     (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //      reset functionality, using the in_dffe subpattern) | ||||||
|  | code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock | ||||||
|  | 	argQ = sigC; | ||||||
|  | 	subpattern(in_dffe); | ||||||
|  | 	if (dff) { | ||||||
|  | 		ffC = dff; | ||||||
|  | 		clock = dffclock; | ||||||
|  | 		if (dffrstmux) { | ||||||
|  | 			ffCrstmux = dffrstmux; | ||||||
|  | 			ffCrstpol = dffrstpol; | ||||||
|  | 		} | ||||||
|  | 		if (dffcemux) { | ||||||
|  | 			ffCcemux = dffcemux; | ||||||
|  | 			ffCcepol = dffcepol; | ||||||
|  | 		} | ||||||
|  | 		sigC = dffD; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	if (ffC) | ||||||
|  | 		accept; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against input registers, based on knowledge of the | ||||||
|  | //   'Q' input. Typically, identifying registers with clock-enable and reset | ||||||
|  | //   capability would be a task would be handled by other Yosys passes such as | ||||||
|  | //   dff2dffe, but since DSP inference happens much before this, these patterns | ||||||
|  | //   have to be manually identified. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //       'Q' argument | ||||||
|  | //   (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //       one that exclusively drives the 'D' input of the $dff, with one of its | ||||||
|  | //       $mux inputs being fully zero | ||||||
|  | //   (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //       exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //       the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //       the 'Q' output of the $dff | ||||||
|  | subpattern in_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (const auto &c : argQ.chunks()) { | ||||||
|  | 		// Abandon matches when 'Q' is a constant | ||||||
|  | 		if (!c.wire) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 		// (not supported by DSP48E1) | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 			if (b != State::Sx && b != State::S0) | ||||||
|  | 				reject; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //     'Q' argument | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \Q)[offset] === argQ[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argQ is present | ||||||
|  | 	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) | ||||||
|  | 	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ argD | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | 	dffD = argQ; | ||||||
|  | 	argD = port(ff, \D); | ||||||
|  | 	argQ = Q; | ||||||
|  | 	dffD.replace(argQ, argD); | ||||||
|  | 	// Only search for ffrstmux if dffD only | ||||||
|  | 	//   has two (ff, ffrstmux) users | ||||||
|  | 	if (nusers(dffD) > 2) | ||||||
|  | 		argD = SigSpec(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //     exclusively drives the 'D' input of the $dff, with one of the $mux | ||||||
|  | //     inputs being fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffrstmux, \Y) === argD | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 		argD = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffrstmux, \Y), argD); | ||||||
|  | 
 | ||||||
|  | 		// Only search for ffcemux if argQ has at | ||||||
|  | 		//   least 3 users (ff, <upstream>, ffrstmux) and | ||||||
|  | 		//   dffD only has two (ff, ffrstmux) | ||||||
|  | 		if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) | ||||||
|  | 			argD = SigSpec(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffrstmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //     exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //     the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //     the 'Q' output of the $dff | ||||||
|  | match ffcemux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffcemux, \Y) === argD | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(ffcemux, AB) === argQ | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 		argD = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		dffD.replace(port(ffcemux, \Y), argD); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffcemux = nullptr; | ||||||
|  | endcode | ||||||
							
								
								
									
										427
									
								
								passes/pmgen/xilinx_dsp_cascade.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								passes/pmgen/xilinx_dsp_cascade.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,427 @@ | ||||||
|  | // This file describes the third of three pattern matcher setups that | ||||||
|  | //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | ||||||
|  | // At a high level, it works as follows: | ||||||
|  | //   (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer | ||||||
|  | //       (controlled by OPMODE[6:4]) set to zero and (b) doesn't already | ||||||
|  | //       use the 'PCOUT' port | ||||||
|  | //   (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled, | ||||||
|  | //         (b) has its Z multiplexer output set to the 'C' port, which is | ||||||
|  | //         driven by the 'P' output of the previous DSP cell, and (c) has its | ||||||
|  | //         'PCIN' port unused | ||||||
|  | //   (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the | ||||||
|  | //         previous DSP cell right-shifted by 17 bits | ||||||
|  | //   (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) | ||||||
|  | //       if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this | ||||||
|  | //       DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already | ||||||
|  | //       have an ACOUT -> ACIN cascade, (d) the previous DSP does not already | ||||||
|  | //       use its ACOUT port, then examine if an ACOUT -> ACIN cascade | ||||||
|  | //       opportunity exists by matching for a $dff-with-optional-clock-enable- | ||||||
|  | //       or-reset and checking that the 'D' input of this register is the same | ||||||
|  | //       as the 'A' input of the previous DSP | ||||||
|  | //   (4) Same as (3) but for BCOUT -> BCIN cascade | ||||||
|  | //   (5) Recursively go to (2.1) until no more matches possible, keeping track | ||||||
|  | //       of the longest possible chain found | ||||||
|  | //   (6) The longest chain is then divided into chunks of no more than | ||||||
|  | //       MAX_DSP_CASCADE in length (to prevent long cascades that exceed the | ||||||
|  | //       height of a DSP column) with each DSP in each chunk being rewritten | ||||||
|  | //       to use [ABP]COUT -> [ABP]CIN cascading as appropriate | ||||||
|  | // Notes: | ||||||
|  | //   - Currently, [AB]COUT -> [AB]COUT cascades (3 or 4) are only considered | ||||||
|  | //     if a PCOUT -> PCIN cascade is (2.1 or 2.2) first identified; this need | ||||||
|  | //     not be the case --- [AB] cascades can exist independently of a P cascade | ||||||
|  | //     (though all three cascades must come from the same DSP). This situation | ||||||
|  | //     is not handled currently. | ||||||
|  | //   - In addition, [AB]COUT -> [AB]COUT cascades (3 or 4) are currently | ||||||
|  | //     conservative in that they examine the situation where (a) the previous | ||||||
|  | //     DSP has [AB]2REG or [AB]1REG enabled, (b) that the downstream DSP has no | ||||||
|  | //     registers enabled, and (c) that there exists only one additional register | ||||||
|  | //     between the upstream and downstream DSPs. This can certainly be relaxed | ||||||
|  | //     to identify situations ranging from (i) neither DSP uses any registers, | ||||||
|  | //     to (ii) upstream DSP has 2 registers, downstream DSP has 2 registers, and | ||||||
|  | //     there exists a further 2 registers between them. This remains a TODO | ||||||
|  | //     item. | ||||||
|  | 
 | ||||||
|  | pattern xilinx_dsp_cascade | ||||||
|  | 
 | ||||||
|  | udata <std::function<SigSpec(const SigSpec&)>> unextend | ||||||
|  | udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain | ||||||
|  | state <Cell*> next | ||||||
|  | state <SigSpec> clock | ||||||
|  | state <int> AREG BREG | ||||||
|  | 
 | ||||||
|  | // Variables used for subpatterns | ||||||
|  | state <SigSpec> argQ argD | ||||||
|  | state <bool> ffcepol ffrstpol | ||||||
|  | state <int> ffoffset | ||||||
|  | udata <SigSpec> dffD dffQ | ||||||
|  | udata <SigBit> dffclock | ||||||
|  | udata <Cell*> dff dffcemux dffrstmux | ||||||
|  | udata <bool> dffcepol dffrstpol | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | #define MAX_DSP_CASCADE 20 | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer | ||||||
|  | //     (controlled by OPMODE[6:4]) set to zero and (b) doesn't already | ||||||
|  | //     use the 'PCOUT' port | ||||||
|  | match first | ||||||
|  | 	select first->type.in(\DSP48E1) | ||||||
|  | 	select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000") | ||||||
|  | 	select nusers(port(first, \PCOUT, SigSpec())) <= 1 | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | // (6) The longest chain is then divided into chunks of no more than | ||||||
|  | //     MAX_DSP_CASCADE in length (to prevent long cascades that exceed the | ||||||
|  | //     height of a DSP column) with each DSP in each chunk being rewritten | ||||||
|  | //     to use [ABP]COUT -> [ABP]CIN cascading as appropriate | ||||||
|  | code | ||||||
|  | 	longest_chain.clear(); | ||||||
|  | 	chain.emplace_back(first, -1, -1, -1); | ||||||
|  | 	subpattern(tail); | ||||||
|  | finally | ||||||
|  | 	chain.pop_back(); | ||||||
|  | 	log_assert(chain.empty()); | ||||||
|  | 	if (GetSize(longest_chain) > 1) { | ||||||
|  | 		Cell *dsp = std::get<0>(longest_chain.front()); | ||||||
|  | 
 | ||||||
|  | 		Cell *dsp_pcin; | ||||||
|  | 		int P, AREG, BREG; | ||||||
|  | 		for (int i = 1; i < GetSize(longest_chain); i++) { | ||||||
|  | 			std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i]; | ||||||
|  | 
 | ||||||
|  | 			if (i % MAX_DSP_CASCADE > 0) { | ||||||
|  | 				if (P >= 0) { | ||||||
|  | 					Wire *cascade = module->addWire(NEW_ID, 48); | ||||||
|  | 					dsp_pcin->setPort(ID(C), Const(0, 48)); | ||||||
|  | 					dsp_pcin->setPort(ID(PCIN), cascade); | ||||||
|  | 					dsp->setPort(ID(PCOUT), cascade); | ||||||
|  | 					add_siguser(cascade, dsp_pcin); | ||||||
|  | 					add_siguser(cascade, dsp); | ||||||
|  | 
 | ||||||
|  | 					SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | ||||||
|  | 					if (P == 17) | ||||||
|  | 						opmode[6] = State::S1; | ||||||
|  | 					else if (P == 0) | ||||||
|  | 						opmode[6] = State::S0; | ||||||
|  | 					else log_abort(); | ||||||
|  | 
 | ||||||
|  | 					opmode[5] = State::S0; | ||||||
|  | 					opmode[4] = State::S1; | ||||||
|  | 					dsp_pcin->setPort(\OPMODE, opmode); | ||||||
|  | 
 | ||||||
|  | 					log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||||
|  | 				} | ||||||
|  | 				if (AREG >= 0) { | ||||||
|  | 					Wire *cascade = module->addWire(NEW_ID, 30); | ||||||
|  | 					dsp_pcin->setPort(ID(A), Const(0, 30)); | ||||||
|  | 					dsp_pcin->setPort(ID(ACIN), cascade); | ||||||
|  | 					dsp->setPort(ID(ACOUT), cascade); | ||||||
|  | 					add_siguser(cascade, dsp_pcin); | ||||||
|  | 					add_siguser(cascade, dsp); | ||||||
|  | 
 | ||||||
|  | 					dsp->setParam(ID(ACASCREG), AREG); | ||||||
|  | 					dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); | ||||||
|  | 
 | ||||||
|  | 					log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||||
|  | 				} | ||||||
|  | 				if (BREG >= 0) { | ||||||
|  | 					Wire *cascade = module->addWire(NEW_ID, 18); | ||||||
|  | 					dsp_pcin->setPort(ID(B), Const(0, 18)); | ||||||
|  | 					dsp_pcin->setPort(ID(BCIN), cascade); | ||||||
|  | 					dsp->setPort(ID(BCOUT), cascade); | ||||||
|  | 					add_siguser(cascade, dsp_pcin); | ||||||
|  | 					add_siguser(cascade, dsp); | ||||||
|  | 
 | ||||||
|  | 					dsp->setParam(ID(BCASCREG), BREG); | ||||||
|  | 					dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); | ||||||
|  | 
 | ||||||
|  | 					log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				log_debug("  Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			dsp = dsp_pcin; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		accept; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | subpattern tail | ||||||
|  | arg first | ||||||
|  | arg next | ||||||
|  | 
 | ||||||
|  | // (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled, | ||||||
|  | //       (b) has its Z multiplexer output set to the 'C' port, which is | ||||||
|  | //       driven by the 'P' output of the previous DSP cell, and (c) has its | ||||||
|  | //       'PCIN' port unused | ||||||
|  | match nextP | ||||||
|  | 	select nextP->type.in(\DSP48E1) | ||||||
|  | 	select !param(nextP, \CREG, State::S1).as_bool() | ||||||
|  | 	select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||||
|  | 	select nusers(port(nextP, \C, SigSpec())) > 1 | ||||||
|  | 	select nusers(port(nextP, \PCIN, SigSpec())) == 0 | ||||||
|  | 	index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | // (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the | ||||||
|  | //       previous DSP cell right-shifted by 17 bits | ||||||
|  | match nextP_shift17 | ||||||
|  | 	if !nextP | ||||||
|  | 	select nextP_shift17->type.in(\DSP48E1) | ||||||
|  | 	select !param(nextP_shift17, \CREG, State::S1).as_bool() | ||||||
|  | 	select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||||
|  | 	select nusers(port(nextP_shift17, \C, SigSpec())) > 1 | ||||||
|  | 	select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0 | ||||||
|  | 	index <SigBit> port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17] | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code next | ||||||
|  | 	next = nextP; | ||||||
|  | 	if (!nextP) | ||||||
|  | 		next = nextP_shift17; | ||||||
|  | 	if (next) { | ||||||
|  | 		unextend = [](const SigSpec &sig) { | ||||||
|  | 			int i; | ||||||
|  | 			for (i = GetSize(sig)-1; i > 0; i--) | ||||||
|  | 				if (sig[i] != sig[i-1]) | ||||||
|  | 					break; | ||||||
|  | 			// Do not remove non-const sign bit | ||||||
|  | 			if (sig[i].wire) | ||||||
|  | 				++i; | ||||||
|  | 			return sig.extract(0, i); | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) | ||||||
|  | //     if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this | ||||||
|  | //     DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already | ||||||
|  | //     have an ACOUT -> ACIN cascade, (d) the previous DSP does not already | ||||||
|  | //     use its ACOUT port, then examine if an ACOUT -> ACIN cascade | ||||||
|  | //     opportunity exists by matching for a $dff-with-optional-clock-enable- | ||||||
|  | //     or-reset and checking that the 'D' input of this register is the same | ||||||
|  | //     as the 'A' input of the previous DSP | ||||||
|  | code argQ clock AREG | ||||||
|  | 	AREG = -1; | ||||||
|  | 	if (next) { | ||||||
|  | 		Cell *prev = std::get<0>(chain.back()); | ||||||
|  | 		if (param(prev, \AREG, 2).as_int() > 0 && | ||||||
|  | 				param(next, \AREG, 2).as_int() > 0 && | ||||||
|  | 				param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||||
|  | 				nusers(port(prev, \ACOUT, SigSpec())) <= 1) { | ||||||
|  | 			argQ = unextend(port(next, \A)); | ||||||
|  | 			clock = port(prev, \CLK); | ||||||
|  | 			subpattern(in_dffe); | ||||||
|  | 			if (dff) { | ||||||
|  | 				if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | ||||||
|  | 					goto reject_AREG; | ||||||
|  | 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | ||||||
|  | 					goto reject_AREG; | ||||||
|  | 				if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) | ||||||
|  | 					goto reject_AREG; | ||||||
|  | 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) | ||||||
|  | 					goto reject_AREG; | ||||||
|  | 				if (dffD == unextend(port(prev, \A))) | ||||||
|  | 					AREG = 1; | ||||||
|  | reject_AREG:			; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (4) Same as (3) but for BCOUT -> BCIN cascade | ||||||
|  | code argQ clock BREG | ||||||
|  | 	BREG = -1; | ||||||
|  | 	if (next) { | ||||||
|  | 		Cell *prev = std::get<0>(chain.back()); | ||||||
|  | 		if (param(prev, \BREG, 2).as_int() > 0 && | ||||||
|  | 				param(next, \BREG, 2).as_int() > 0 && | ||||||
|  | 				param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||||
|  | 				port(next, \BCIN, SigSpec()).is_fully_zero() && | ||||||
|  | 				nusers(port(prev, \BCOUT, SigSpec())) <= 1) { | ||||||
|  | 			argQ = unextend(port(next, \B)); | ||||||
|  | 			clock = port(prev, \CLK); | ||||||
|  | 			subpattern(in_dffe); | ||||||
|  | 			if (dff) { | ||||||
|  | 				if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | ||||||
|  | 					goto reject_BREG; | ||||||
|  | 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | ||||||
|  | 					goto reject_BREG; | ||||||
|  | 				if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) | ||||||
|  | 					goto reject_BREG; | ||||||
|  | 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) | ||||||
|  | 					goto reject_BREG; | ||||||
|  | 				if (dffD == unextend(port(prev, \B))) | ||||||
|  | 					BREG = 1; | ||||||
|  | reject_BREG:			; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (5) Recursively go to (2.1) until no more matches possible, recording the | ||||||
|  | //     longest possible chain | ||||||
|  | code | ||||||
|  | 	if (next) { | ||||||
|  | 		chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG); | ||||||
|  | 
 | ||||||
|  | 		SigSpec sigC = unextend(port(next, \C)); | ||||||
|  | 
 | ||||||
|  | 		if (nextP_shift17) { | ||||||
|  | 			if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) && | ||||||
|  | 					port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC) | ||||||
|  | 				subpattern(tail); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) && | ||||||
|  | 					port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC) | ||||||
|  | 				subpattern(tail); | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if (GetSize(chain) > GetSize(longest_chain)) | ||||||
|  | 			longest_chain = chain; | ||||||
|  | 	} | ||||||
|  | finally | ||||||
|  | 	if (next) | ||||||
|  | 		chain.pop_back(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against input registers, based on knowledge of the | ||||||
|  | //   'Q' input. Typically, identifying registers with clock-enable and reset | ||||||
|  | //   capability would be a task would be handled by other Yosys passes such as | ||||||
|  | //   dff2dffe, but since DSP inference happens much before this, these patterns | ||||||
|  | //   have to be manually identified. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //       'Q' argument | ||||||
|  | //   (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //       one that exclusively drives the 'D' input of the $dff, with one of its | ||||||
|  | //       $mux inputs being fully zero | ||||||
|  | //   (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //       exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //       the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //       the 'Q' output of the $dff | ||||||
|  | subpattern in_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (const auto &c : argQ.chunks()) { | ||||||
|  | 		// Abandon matches when 'Q' is a constant | ||||||
|  | 		if (!c.wire) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 		// (not supported by DSP48E1) | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 			if (b != State::Sx && b != State::S0) | ||||||
|  | 				reject; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //     'Q' argument | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \Q)[offset] === argQ[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argQ is present | ||||||
|  | 	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) | ||||||
|  | 	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ argD | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | 	dffD = argQ; | ||||||
|  | 	argD = port(ff, \D); | ||||||
|  | 	argQ = Q; | ||||||
|  | 	dffD.replace(argQ, argD); | ||||||
|  | 	// Only search for ffrstmux if dffD only | ||||||
|  | 	//   has two (ff, ffrstmux) users | ||||||
|  | 	if (nusers(dffD) > 2) | ||||||
|  | 		argD = SigSpec(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //     exclusively drives the 'D' input of the $dff, with one of the $mux | ||||||
|  | //     inputs being fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffrstmux, \Y) === argD | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 		argD = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffrstmux, \Y), argD); | ||||||
|  | 
 | ||||||
|  | 		// Only search for ffcemux if argQ has at | ||||||
|  | 		//   least 3 users (ff, <upstream>, ffrstmux) and | ||||||
|  | 		//   dffD only has two (ff, ffrstmux) | ||||||
|  | 		if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) | ||||||
|  | 			argD = SigSpec(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffrstmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //     exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //     the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //     the 'Q' output of the $dff | ||||||
|  | match ffcemux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffcemux, \Y) === argD | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(ffcemux, AB) === argQ | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 		argD = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		dffD.replace(port(ffcemux, \Y), argD); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffcemux = nullptr; | ||||||
|  | endcode | ||||||
|  | @ -13,9 +13,9 @@ endcode | ||||||
| match first | match first | ||||||
| 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
| 	select !first->has_keep_attr() | 	select !first->has_keep_attr() | ||||||
| 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | 	select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool() | ||||||
| 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | 	select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool() | ||||||
| 	select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | 	select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero() | ||||||
| 	filter !non_first_cells.count(first) | 	filter !non_first_cells.count(first) | ||||||
| generate | generate | ||||||
| 	SigSpec C = module->addWire(NEW_ID); | 	SigSpec C = module->addWire(NEW_ID); | ||||||
|  | @ -84,9 +84,9 @@ arg en_port | ||||||
| match first | match first | ||||||
| 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
| 	select !first->has_keep_attr() | 	select !first->has_keep_attr() | ||||||
| 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | 	select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool() | ||||||
| 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | 	select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool() | ||||||
| 	select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | 	select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero() | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| code clk_port en_port | code clk_port en_port | ||||||
|  | @ -111,10 +111,10 @@ match next | ||||||
| 	index <SigBit> port(next, \Q) === port(first, \D) | 	index <SigBit> port(next, \Q) === port(first, \D) | ||||||
| 	filter port(next, clk_port) == port(first, clk_port) | 	filter port(next, clk_port) == port(first, clk_port) | ||||||
| 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | 	filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero() | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| code | code | ||||||
|  | @ -138,10 +138,10 @@ match next | ||||||
| 	index <SigBit> port(next, \Q) === port(chain.back(), \D) | 	index <SigBit> port(next, \Q) === port(chain.back(), \D) | ||||||
| 	filter port(next, clk_port) == port(first, clk_port) | 	filter port(next, clk_port) == port(first, clk_port) | ||||||
| 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | 	filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool() | ||||||
| 	filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | 	filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero() | ||||||
| generate | generate | ||||||
| 	Cell *cell = module->addCell(NEW_ID, chain.back()->type); | 	Cell *cell = module->addCell(NEW_ID, chain.back()->type); | ||||||
| 	cell->setPort(\C, chain.back()->getPort(\C)); | 	cell->setPort(\C, chain.back()->getPort(\C)); | ||||||
|  | @ -149,7 +149,7 @@ generate | ||||||
| 	cell->setPort(\Q, chain.back()->getPort(\D)); | 	cell->setPort(\Q, chain.back()->getPort(\D)); | ||||||
| 	if (cell->type == \FDRE) { | 	if (cell->type == \FDRE) { | ||||||
| 		if (rng(2) == 0) | 		if (rng(2) == 0) | ||||||
| 			cell->setPort(\R, chain.back()->connections_.at(\R, State::S0)); | 			cell->setPort(\R, port(chain.back(), \R, State::S0)); | ||||||
| 		cell->setPort(\CE, chain.back()->getPort(\CE)); | 		cell->setPort(\CE, chain.back()->getPort(\CE)); | ||||||
| 	} | 	} | ||||||
| 	else if (cell->type.begins_with("$_DFFE_")) | 	else if (cell->type.begins_with("$_DFFE_")) | ||||||
|  |  | ||||||
|  | @ -198,6 +198,7 @@ struct Async2syncPass : public Pass { | ||||||
| 						module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q); | 						module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q); | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
|  | 					cell->setPort("\\D", sig_q); | ||||||
| 					cell->setPort("\\Q", new_q); | 					cell->setPort("\\Q", new_q); | ||||||
| 					cell->unsetPort("\\EN"); | 					cell->unsetPort("\\EN"); | ||||||
| 					cell->unsetParam("\\EN_POLARITY"); | 					cell->unsetParam("\\EN_POLARITY"); | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ OBJS += passes/techmap/attrmap.o | ||||||
| OBJS += passes/techmap/zinit.o | OBJS += passes/techmap/zinit.o | ||||||
| OBJS += passes/techmap/dff2dffs.o | OBJS += passes/techmap/dff2dffs.o | ||||||
| OBJS += passes/techmap/flowmap.o | OBJS += passes/techmap/flowmap.o | ||||||
|  | OBJS += passes/techmap/extractinv.o | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| GENFILES += passes/techmap/techmap.inc | GENFILES += passes/techmap/techmap.inc | ||||||
|  |  | ||||||
|  | @ -71,22 +71,21 @@ RTLIL::Module *module; | ||||||
| bool clk_polarity, en_polarity; | bool clk_polarity, en_polarity; | ||||||
| RTLIL::SigSpec clk_sig, en_sig; | RTLIL::SigSpec clk_sig, en_sig; | ||||||
| 
 | 
 | ||||||
| inline std::string remap_name(RTLIL::IdString abc_name) | inline std::string remap_name(RTLIL::IdString abc9_name) | ||||||
| { | { | ||||||
| 	return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1); | 	return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void handle_loops(RTLIL::Design *design, | void handle_loops(RTLIL::Design *design) | ||||||
| 		const dict<IdString,pool<IdString>> &scc_break_inputs) |  | ||||||
| { | { | ||||||
| 	Pass::call(design, "scc -set_attr abc_scc_id {}"); | 	Pass::call(design, "scc -set_attr abc9_scc_id {}"); | ||||||
| 
 | 
 | ||||||
| 	// For every unique SCC found, (arbitrarily) find the first
 | 	// For every unique SCC found, (arbitrarily) find the first
 | ||||||
| 	// cell in the component, and select (and mark) all its output
 | 	// cell in the component, and select (and mark) all its output
 | ||||||
| 	// wires
 | 	// wires
 | ||||||
| 	pool<RTLIL::Const> ids_seen; | 	pool<RTLIL::Const> ids_seen; | ||||||
| 	for (auto cell : module->cells()) { | 	for (auto cell : module->cells()) { | ||||||
| 		auto it = cell->attributes.find(ID(abc_scc_id)); | 		auto it = cell->attributes.find(ID(abc9_scc_id)); | ||||||
| 		if (it != cell->attributes.end()) { | 		if (it != cell->attributes.end()) { | ||||||
| 			auto r = ids_seen.insert(it->second); | 			auto r = ids_seen.insert(it->second); | ||||||
| 			if (r.second) { | 			if (r.second) { | ||||||
|  | @ -106,7 +105,7 @@ void handle_loops(RTLIL::Design *design, | ||||||
| 							log_assert(w->port_input); | 							log_assert(w->port_input); | ||||||
| 							log_assert(b.offset < GetSize(w)); | 							log_assert(b.offset < GetSize(w)); | ||||||
| 						} | 						} | ||||||
| 						w->set_bool_attribute(ID(abc_scc_break)); | 						w->set_bool_attribute(ID(abc9_scc_break)); | ||||||
| 						module->swap_names(b.wire, w); | 						module->swap_names(b.wire, w); | ||||||
| 						c.second = RTLIL::SigBit(w, b.offset); | 						c.second = RTLIL::SigBit(w, b.offset); | ||||||
| 					} | 					} | ||||||
|  | @ -114,36 +113,12 @@ void handle_loops(RTLIL::Design *design, | ||||||
| 			} | 			} | ||||||
| 			cell->attributes.erase(it); | 			cell->attributes.erase(it); | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		auto jt = scc_break_inputs.find(cell->type); |  | ||||||
| 		if (jt != scc_break_inputs.end()) |  | ||||||
| 			for (auto port_name : jt->second) { |  | ||||||
| 				RTLIL::SigSpec sig; |  | ||||||
| 				auto &rhs = cell->connections_.at(port_name); |  | ||||||
| 				for (auto b : rhs) { |  | ||||||
| 					Wire *w = b.wire; |  | ||||||
| 					if (!w) continue; |  | ||||||
| 					w->port_output = true; |  | ||||||
| 					w->set_bool_attribute(ID(abc_scc_break)); |  | ||||||
| 					w = module->wire(stringf("%s.abci", w->name.c_str())); |  | ||||||
| 					if (!w) { |  | ||||||
| 						w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire)); |  | ||||||
| 						w->port_input = true; |  | ||||||
| 					} |  | ||||||
| 					else { |  | ||||||
| 						log_assert(b.offset < GetSize(w)); |  | ||||||
| 						log_assert(w->port_input); |  | ||||||
| 					} |  | ||||||
| 					sig.append(RTLIL::SigBit(w, b.offset)); |  | ||||||
| 				} |  | ||||||
| 				rhs = sig; |  | ||||||
| 			} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	module->fixup_ports(); | 	module->fixup_ports(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string add_echos_to_abc_cmd(std::string str) | std::string add_echos_to_abc9_cmd(std::string str) | ||||||
| { | { | ||||||
| 	std::string new_str, token; | 	std::string new_str, token; | ||||||
| 	for (size_t i = 0; i < str.size(); i++) { | 	for (size_t i = 0; i < str.size(); i++) { | ||||||
|  | @ -165,7 +140,7 @@ std::string add_echos_to_abc_cmd(std::string str) | ||||||
| 	return new_str; | 	return new_str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string fold_abc_cmd(std::string str) | std::string fold_abc9_cmd(std::string str) | ||||||
| { | { | ||||||
| 	std::string token, new_str = "          "; | 	std::string token, new_str = "          "; | ||||||
| 	int char_counter = 10; | 	int char_counter = 10; | ||||||
|  | @ -209,7 +184,7 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho | ||||||
| 	return text; | 	return text; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct abc_output_filter | struct abc9_output_filter | ||||||
| { | { | ||||||
| 	bool got_cr; | 	bool got_cr; | ||||||
| 	int escape_seq_state; | 	int escape_seq_state; | ||||||
|  | @ -217,7 +192,7 @@ struct abc_output_filter | ||||||
| 	std::string tempdir_name; | 	std::string tempdir_name; | ||||||
| 	bool show_tempdir; | 	bool show_tempdir; | ||||||
| 
 | 
 | ||||||
| 	abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) | 	abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) | ||||||
| 	{ | 	{ | ||||||
| 		got_cr = false; | 		got_cr = false; | ||||||
| 		escape_seq_state = 0; | 		escape_seq_state = 0; | ||||||
|  | @ -272,8 +247,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, | 		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, | ||||||
| 		bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, | 		bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, | ||||||
| 		bool show_tempdir, std::string box_file, std::string lut_file, | 		bool show_tempdir, std::string box_file, std::string lut_file, | ||||||
| 		std::string wire_delay, const dict<int,IdString> &box_lookup, | 		std::string wire_delay, const dict<int,IdString> &box_lookup, bool nomfs | ||||||
| 		const dict<IdString,pool<IdString>> &scc_break_inputs |  | ||||||
| ) | ) | ||||||
| { | { | ||||||
| 	module = current_module; | 	module = current_module; | ||||||
|  | @ -319,68 +293,72 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 	log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", | 	log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", | ||||||
| 			module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); | 			module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); | ||||||
| 
 | 
 | ||||||
| 	std::string abc_script; | 	std::string abc9_script; | ||||||
| 
 | 
 | ||||||
| 	if (!lut_costs.empty()) { | 	if (!lut_costs.empty()) { | ||||||
| 		abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); | 		abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); | ||||||
| 		if (!box_file.empty()) | 		if (!box_file.empty()) | ||||||
| 			abc_script += stringf("read_box -v %s; ", box_file.c_str()); | 			abc9_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	if (!lut_file.empty()) { | 	if (!lut_file.empty()) { | ||||||
| 		abc_script += stringf("read_lut %s; ", lut_file.c_str()); | 		abc9_script += stringf("read_lut %s; ", lut_file.c_str()); | ||||||
| 		if (!box_file.empty()) | 		if (!box_file.empty()) | ||||||
| 			abc_script += stringf("read_box -v %s; ", box_file.c_str()); | 			abc9_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		log_abort(); | 		log_abort(); | ||||||
| 
 | 
 | ||||||
| 	abc_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); | 	abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); | ||||||
| 
 | 
 | ||||||
| 	if (!script_file.empty()) { | 	if (!script_file.empty()) { | ||||||
| 		if (script_file[0] == '+') { | 		if (script_file[0] == '+') { | ||||||
| 			for (size_t i = 1; i < script_file.size(); i++) | 			for (size_t i = 1; i < script_file.size(); i++) | ||||||
| 				if (script_file[i] == '\'') | 				if (script_file[i] == '\'') | ||||||
| 					abc_script += "'\\''"; | 					abc9_script += "'\\''"; | ||||||
| 				else if (script_file[i] == ',') | 				else if (script_file[i] == ',') | ||||||
| 					abc_script += " "; | 					abc9_script += " "; | ||||||
| 				else | 				else | ||||||
| 					abc_script += script_file[i]; | 					abc9_script += script_file[i]; | ||||||
| 		} else | 		} else | ||||||
| 			abc_script += stringf("source %s", script_file.c_str()); | 			abc9_script += stringf("source %s", script_file.c_str()); | ||||||
| 	} else if (!lut_costs.empty() || !lut_file.empty()) { | 	} else if (!lut_costs.empty() || !lut_file.empty()) { | ||||||
| 		//bool all_luts_cost_same = true;
 | 		//bool all_luts_cost_same = true;
 | ||||||
| 		//for (int this_cost : lut_costs)
 | 		//for (int this_cost : lut_costs)
 | ||||||
| 		//	if (this_cost != lut_costs.front())
 | 		//	if (this_cost != lut_costs.front())
 | ||||||
| 		//		all_luts_cost_same = false;
 | 		//		all_luts_cost_same = false;
 | ||||||
| 		abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; | 		abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; | ||||||
| 		//if (all_luts_cost_same && !fast_mode)
 | 		//if (all_luts_cost_same && !fast_mode)
 | ||||||
| 		//	abc_script += "; lutpack {S}";
 | 		//	abc9_script += "; lutpack {S}";
 | ||||||
| 	} else | 	} else | ||||||
| 		log_abort(); | 		log_abort(); | ||||||
| 
 | 
 | ||||||
| 	//if (script_file.empty() && !delay_target.empty())
 | 	//if (script_file.empty() && !delay_target.empty())
 | ||||||
| 	//	for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
 | 	//	for (size_t pos = abc9_script.find("dretime;"); pos != std::string::npos; pos = abc9_script.find("dretime;", pos+1))
 | ||||||
| 	//		abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
 | 	//		abc9_script = abc9_script.substr(0, pos) + "dretime; retime -o {D};" + abc9_script.substr(pos+8);
 | ||||||
| 
 | 
 | ||||||
| 	for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) | 	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) | ||||||
| 		abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); | 		abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); | ||||||
| 
 | 
 | ||||||
| 	//for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
 | 	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
 | ||||||
| 	//	abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
 | 	//	abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
 | ||||||
| 
 | 
 | ||||||
| 	for (size_t pos = abc_script.find("{W}"); pos != std::string::npos; pos = abc_script.find("{W}", pos)) | 	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) | ||||||
| 		abc_script = abc_script.substr(0, pos) + wire_delay + abc_script.substr(pos+3); | 		abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); | ||||||
| 
 | 
 | ||||||
| 	abc_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); | 	if (nomfs) | ||||||
| 	abc_script = add_echos_to_abc_cmd(abc_script); | 		for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos)) | ||||||
|  | 			abc9_script = abc9_script.erase(pos, strlen("&mfs")); | ||||||
| 
 | 
 | ||||||
| 	for (size_t i = 0; i+1 < abc_script.size(); i++) | 	abc9_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); | ||||||
| 		if (abc_script[i] == ';' && abc_script[i+1] == ' ') | 	abc9_script = add_echos_to_abc9_cmd(abc9_script); | ||||||
| 			abc_script[i+1] = '\n'; | 
 | ||||||
|  | 	for (size_t i = 0; i+1 < abc9_script.size(); i++) | ||||||
|  | 		if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') | ||||||
|  | 			abc9_script[i+1] = '\n'; | ||||||
| 
 | 
 | ||||||
| 	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); | 	FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); | ||||||
| 	fprintf(f, "%s\n", abc_script.c_str()); | 	fprintf(f, "%s\n", abc9_script.c_str()); | ||||||
| 	fclose(f); | 	fclose(f); | ||||||
| 
 | 
 | ||||||
| 	if (dff_mode || !clk_str.empty()) | 	if (dff_mode || !clk_str.empty()) | ||||||
|  | @ -413,7 +391,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 		RTLIL::Selection& sel = design->selection_stack.back(); | 		RTLIL::Selection& sel = design->selection_stack.back(); | ||||||
| 		sel.select(module); | 		sel.select(module); | ||||||
| 
 | 
 | ||||||
| 		handle_loops(design, scc_break_inputs); | 		handle_loops(design); | ||||||
| 
 | 
 | ||||||
| 		Pass::call(design, "aigmap"); | 		Pass::call(design, "aigmap"); | ||||||
| 
 | 
 | ||||||
|  | @ -446,7 +424,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 		// the expose operation -- remove them from PO/PI
 | 		// the expose operation -- remove them from PO/PI
 | ||||||
| 		// and re-connecting them back together
 | 		// and re-connecting them back together
 | ||||||
| 		for (auto wire : module->wires()) { | 		for (auto wire : module->wires()) { | ||||||
| 			auto it = wire->attributes.find(ID(abc_scc_break)); | 			auto it = wire->attributes.find(ID(abc9_scc_break)); | ||||||
| 			if (it != wire->attributes.end()) { | 			if (it != wire->attributes.end()) { | ||||||
| 				wire->attributes.erase(it); | 				wire->attributes.erase(it); | ||||||
| 				log_assert(wire->port_output); | 				log_assert(wire->port_output); | ||||||
|  | @ -476,28 +454,28 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 		log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); | 		log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); | ||||||
| 
 | 
 | ||||||
| #ifndef YOSYS_LINK_ABC | #ifndef YOSYS_LINK_ABC | ||||||
| 		abc_output_filter filt(tempdir_name, show_tempdir); | 		abc9_output_filter filt(tempdir_name, show_tempdir); | ||||||
| 		int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); | 		int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); | ||||||
| #else | #else | ||||||
| 		// These needs to be mutable, supposedly due to getopt
 | 		// These needs to be mutable, supposedly due to getopt
 | ||||||
| 		char *abc_argv[5]; | 		char *abc9_argv[5]; | ||||||
| 		string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); | 		string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); | ||||||
| 		abc_argv[0] = strdup(exe_file.c_str()); | 		abc9_argv[0] = strdup(exe_file.c_str()); | ||||||
| 		abc_argv[1] = strdup("-s"); | 		abc9_argv[1] = strdup("-s"); | ||||||
| 		abc_argv[2] = strdup("-f"); | 		abc9_argv[2] = strdup("-f"); | ||||||
| 		abc_argv[3] = strdup(tmp_script_name.c_str()); | 		abc9_argv[3] = strdup(tmp_script_name.c_str()); | ||||||
| 		abc_argv[4] = 0; | 		abc9_argv[4] = 0; | ||||||
| 		int ret = Abc_RealMain(4, abc_argv); | 		int ret = Abc_RealMain(4, abc9_argv); | ||||||
| 		free(abc_argv[0]); | 		free(abc9_argv[0]); | ||||||
| 		free(abc_argv[1]); | 		free(abc9_argv[1]); | ||||||
| 		free(abc_argv[2]); | 		free(abc9_argv[2]); | ||||||
| 		free(abc_argv[3]); | 		free(abc9_argv[3]); | ||||||
| #endif | #endif | ||||||
| 		if (ret != 0) | 		if (ret != 0) | ||||||
| 			log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); | 			log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); | ||||||
| 
 | 
 | ||||||
| 		buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); | 		buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); | ||||||
| 		ifs.open(buffer); | 		ifs.open(buffer, std::ifstream::binary); | ||||||
| 		if (ifs.fail()) | 		if (ifs.fail()) | ||||||
| 			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); | 			log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); | ||||||
| 
 | 
 | ||||||
|  | @ -539,7 +517,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 			signal = std::move(bits); | 			signal = std::move(bits); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		dict<IdString, bool> abc_box; | 		dict<IdString, bool> abc9_box; | ||||||
| 		vector<RTLIL::Cell*> boxes; | 		vector<RTLIL::Cell*> boxes; | ||||||
| 		for (const auto &it : module->cells_) { | 		for (const auto &it : module->cells_) { | ||||||
| 			auto cell = it.second; | 			auto cell = it.second; | ||||||
|  | @ -547,10 +525,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 				module->remove(cell); | 				module->remove(cell); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			auto jt = abc_box.find(cell->type); | 			auto jt = abc9_box.find(cell->type); | ||||||
| 			if (jt == abc_box.end()) { | 			if (jt == abc9_box.end()) { | ||||||
| 				RTLIL::Module* box_module = design->module(cell->type); | 				RTLIL::Module* box_module = design->module(cell->type); | ||||||
| 				jt = abc_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc_box_id)))).first; | 				jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; | ||||||
| 			} | 			} | ||||||
| 			if (jt->second) | 			if (jt->second) | ||||||
| 				boxes.emplace_back(cell); | 				boxes.emplace_back(cell); | ||||||
|  | @ -632,7 +610,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 				existing_cell = module->cell(c->name); | 				existing_cell = module->cell(c->name); | ||||||
| 				log_assert(existing_cell); | 				log_assert(existing_cell); | ||||||
| 				cell = module->addCell(remap_name(c->name), c->type); | 				cell = module->addCell(remap_name(c->name), c->type); | ||||||
| 				module->swap_names(cell, existing_cell); |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; | 			if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx; | ||||||
|  | @ -668,8 +645,22 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for (auto cell : boxes) | 		for (auto existing_cell : boxes) { | ||||||
| 			module->remove(cell); | 			Cell *cell = module->cell(remap_name(existing_cell->name)); | ||||||
|  | 			if (cell) { | ||||||
|  | 				for (auto &conn : existing_cell->connections()) { | ||||||
|  | 					if (!conn.second.is_wire()) | ||||||
|  | 						continue; | ||||||
|  | 					Wire *wire = conn.second.as_wire(); | ||||||
|  | 					if (!wire->get_bool_attribute(ID(abc9_padding))) | ||||||
|  | 						continue; | ||||||
|  | 					cell->unsetPort(conn.first); | ||||||
|  | 					log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second)); | ||||||
|  | 				} | ||||||
|  | 				module->swap_names(cell, existing_cell); | ||||||
|  | 			} | ||||||
|  | 			module->remove(existing_cell); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// Copy connections (and rename) from mapped_mod to module
 | 		// Copy connections (and rename) from mapped_mod to module
 | ||||||
| 		for (auto conn : mapped_mod->connections()) { | 		for (auto conn : mapped_mod->connections()) { | ||||||
|  | @ -840,17 +831,17 @@ struct Abc9Pass : public Pass { | ||||||
| 		log("        if no -script parameter is given, the following scripts are used:\n"); | 		log("        if no -script parameter is given, the following scripts are used:\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("        for -lut/-luts (only one LUT size):\n"); | 		log("        for -lut/-luts (only one LUT size):\n"); | ||||||
| 		log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); | 		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("        for -lut/-luts (different LUT sizes):\n"); | 		log("        for -lut/-luts (different LUT sizes):\n"); | ||||||
| 		log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); | 		log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str()); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -fast\n"); | 		log("    -fast\n"); | ||||||
| 		log("        use different default scripts that are slightly faster (at the cost\n"); | 		log("        use different default scripts that are slightly faster (at the cost\n"); | ||||||
| 		log("        of output quality):\n"); | 		log("        of output quality):\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("        for -lut/-luts:\n"); | 		log("        for -lut/-luts:\n"); | ||||||
| 		log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); | 		log("%s\n", fold_abc9_cmd(ABC_FAST_COMMAND_LUT).c_str()); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -D <picoseconds>\n"); | 		log("    -D <picoseconds>\n"); | ||||||
| 		log("        set delay target. the string {D} in the default scripts above is\n"); | 		log("        set delay target. the string {D} in the default scripts above is\n"); | ||||||
|  | @ -934,6 +925,7 @@ struct Abc9Pass : public Pass { | ||||||
| 		std::string delay_target, lutin_shared = "-S 1", wire_delay; | 		std::string delay_target, lutin_shared = "-S 1", wire_delay; | ||||||
| 		bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; | 		bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; | ||||||
| 		bool show_tempdir = false; | 		bool show_tempdir = false; | ||||||
|  | 		bool nomfs = false; | ||||||
| 		vector<int> lut_costs; | 		vector<int> lut_costs; | ||||||
| 		markgroups = false; | 		markgroups = false; | ||||||
| 
 | 
 | ||||||
|  | @ -1050,23 +1042,31 @@ struct Abc9Pass : public Pass { | ||||||
| 			} | 			} | ||||||
| 			if (arg == "-box" && argidx+1 < args.size()) { | 			if (arg == "-box" && argidx+1 < args.size()) { | ||||||
| 				box_file = args[++argidx]; | 				box_file = args[++argidx]; | ||||||
| 				rewrite_filename(box_file); |  | ||||||
| 				if (!box_file.empty() && !is_absolute_path(box_file)) |  | ||||||
| 					box_file = std::string(pwd) + "/" + box_file; |  | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			if (arg == "-W" && argidx+1 < args.size()) { | 			if (arg == "-W" && argidx+1 < args.size()) { | ||||||
| 				wire_delay = "-W " + args[++argidx]; | 				wire_delay = "-W " + args[++argidx]; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (arg == "-nomfs") { | ||||||
|  | 				nomfs = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
|  | 		// ABC expects a box file for XAIG
 | ||||||
|  | 		if (box_file.empty()) | ||||||
|  | 		    box_file = "+/dummy.box"; | ||||||
|  | 
 | ||||||
|  | 		rewrite_filename(box_file); | ||||||
|  | 		if (!box_file.empty() && !is_absolute_path(box_file)) | ||||||
|  | 		    box_file = std::string(pwd) + "/" + box_file; | ||||||
|  | 
 | ||||||
| 		dict<int,IdString> box_lookup; | 		dict<int,IdString> box_lookup; | ||||||
| 		dict<IdString,pool<IdString>> scc_break_inputs; |  | ||||||
| 		for (auto m : design->modules()) { | 		for (auto m : design->modules()) { | ||||||
| 			auto it = m->attributes.find(ID(abc_box_id)); | 			auto it = m->attributes.find(ID(abc9_box_id)); | ||||||
| 			if (it == m->attributes.end()) | 			if (it == m->attributes.end()) | ||||||
| 				continue; | 				continue; | ||||||
| 			if (m->name.begins_with("$paramod")) | 			if (m->name.begins_with("$paramod")) | ||||||
|  | @ -1074,7 +1074,7 @@ struct Abc9Pass : public Pass { | ||||||
| 			auto id = it->second.as_int(); | 			auto id = it->second.as_int(); | ||||||
| 			auto r = box_lookup.insert(std::make_pair(id, m->name)); | 			auto r = box_lookup.insert(std::make_pair(id, m->name)); | ||||||
| 			if (!r.second) | 			if (!r.second) | ||||||
| 				log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n", | 				log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n", | ||||||
| 						log_id(m), id, log_id(r.first->second)); | 						log_id(m), id, log_id(r.first->second)); | ||||||
| 			log_assert(r.second); | 			log_assert(r.second); | ||||||
| 
 | 
 | ||||||
|  | @ -1082,28 +1082,24 @@ struct Abc9Pass : public Pass { | ||||||
| 			for (auto p : m->ports) { | 			for (auto p : m->ports) { | ||||||
| 				auto w = m->wire(p); | 				auto w = m->wire(p); | ||||||
| 				log_assert(w); | 				log_assert(w); | ||||||
| 				if (w->port_input) { | 				if (w->attributes.count(ID(abc9_carry))) { | ||||||
| 					if (w->attributes.count(ID(abc_scc_break))) | 					if (w->port_input) { | ||||||
| 						scc_break_inputs[m->name].insert(p); |  | ||||||
| 					if (w->attributes.count(ID(abc_carry))) { |  | ||||||
| 						if (carry_in) | 						if (carry_in) | ||||||
| 							log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m)); | 							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); | ||||||
| 						carry_in = w; | 						carry_in = w; | ||||||
| 					} | 					} | ||||||
| 				} | 					else if (w->port_output) { | ||||||
| 				if (w->port_output) { |  | ||||||
| 					if (w->attributes.count(ID(abc_carry))) { |  | ||||||
| 						if (carry_out) | 						if (carry_out) | ||||||
| 							log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m)); | 							log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); | ||||||
| 						carry_out = w; | 						carry_out = w; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if (carry_in || carry_out) { | 			if (carry_in || carry_out) { | ||||||
| 				if (carry_in && !carry_out) | 				if (carry_in && !carry_out) | ||||||
| 					log_error("Module '%s' contains an 'abc_carry' input port but no output port.\n", log_id(m)); | 					log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); | ||||||
| 				if (!carry_in && carry_out) | 				if (!carry_in && carry_out) | ||||||
| 					log_error("Module '%s' contains an 'abc_carry' output port but no input port.\n", log_id(m)); | 					log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); | ||||||
| 				// Make carry_in the last PI, and carry_out the last PO
 | 				// Make carry_in the last PI, and carry_out the last PO
 | ||||||
| 				//   since ABC requires it this way
 | 				//   since ABC requires it this way
 | ||||||
| 				auto &ports = m->ports; | 				auto &ports = m->ports; | ||||||
|  | @ -1131,7 +1127,7 @@ struct Abc9Pass : public Pass { | ||||||
| 
 | 
 | ||||||
| 		for (auto mod : design->selected_modules()) | 		for (auto mod : design->selected_modules()) | ||||||
| 		{ | 		{ | ||||||
| 			if (mod->attributes.count(ID(abc_box_id))) | 			if (mod->attributes.count(ID(abc9_box_id))) | ||||||
| 				continue; | 				continue; | ||||||
| 
 | 
 | ||||||
| 			if (mod->processes.size() > 0) { | 			if (mod->processes.size() > 0) { | ||||||
|  | @ -1144,7 +1140,7 @@ struct Abc9Pass : public Pass { | ||||||
| 			if (!dff_mode || !clk_str.empty()) { | 			if (!dff_mode || !clk_str.empty()) { | ||||||
| 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff, | 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff, | ||||||
| 						delay_target, lutin_shared, fast_mode, show_tempdir, | 						delay_target, lutin_shared, fast_mode, show_tempdir, | ||||||
| 						box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); | 						box_file, lut_file, wire_delay, box_lookup, nomfs); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -1290,7 +1286,7 @@ struct Abc9Pass : public Pass { | ||||||
| 				en_sig = assign_map(std::get<3>(it.first)); | 				en_sig = assign_map(std::get<3>(it.first)); | ||||||
| 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", | 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", | ||||||
| 						keepff, delay_target, lutin_shared, fast_mode, show_tempdir, | 						keepff, delay_target, lutin_shared, fast_mode, show_tempdir, | ||||||
| 						box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); | 						box_file, lut_file, wire_delay, box_lookup, nomfs); | ||||||
| 				assign_map.set(mod); | 				assign_map.set(mod); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -27,6 +27,7 @@ struct AigmapPass : public Pass { | ||||||
| 	AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } | 	AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } | ||||||
| 	void help() YS_OVERRIDE | 	void help() YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    aigmap [options] [selection]\n"); | 		log("    aigmap [options] [selection]\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -36,10 +37,15 @@ struct AigmapPass : public Pass { | ||||||
| 		log("    -nand\n"); | 		log("    -nand\n"); | ||||||
| 		log("        Enable creation of $_NAND_ cells\n"); | 		log("        Enable creation of $_NAND_ cells\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -select\n"); | ||||||
|  | 		log("        Overwrite replaced cells in the current selection with new $_AND_,\n"); | ||||||
|  | 		log("        $_NOT_, and $_NAND_, cells\n"); | ||||||
|  | 
 | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
| 		bool nand_mode = false; | 		bool nand_mode = false, select_mode = false; | ||||||
| 
 | 
 | ||||||
| 		log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); | 		log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); | ||||||
| 
 | 
 | ||||||
|  | @ -50,6 +56,10 @@ struct AigmapPass : public Pass { | ||||||
| 				nand_mode = true; | 				nand_mode = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-select") { | ||||||
|  | 				select_mode = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
|  | @ -62,6 +72,7 @@ struct AigmapPass : public Pass { | ||||||
| 			dict<IdString, int> stat_not_replaced; | 			dict<IdString, int> stat_not_replaced; | ||||||
| 			int orig_num_cells = GetSize(module->cells()); | 			int orig_num_cells = GetSize(module->cells()); | ||||||
| 
 | 
 | ||||||
|  | 			pool<IdString> new_sel; | ||||||
| 			for (auto cell : module->selected_cells()) | 			for (auto cell : module->selected_cells()) | ||||||
| 			{ | 			{ | ||||||
| 				Aig aig(cell); | 				Aig aig(cell); | ||||||
|  | @ -75,6 +86,8 @@ struct AigmapPass : public Pass { | ||||||
| 				if (aig.name.empty()) { | 				if (aig.name.empty()) { | ||||||
| 					not_replaced_count++; | 					not_replaced_count++; | ||||||
| 					stat_not_replaced[cell->type]++; | 					stat_not_replaced[cell->type]++; | ||||||
|  | 					if (select_mode) | ||||||
|  | 						new_sel.insert(cell->name); | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | @ -95,19 +108,33 @@ struct AigmapPass : public Pass { | ||||||
| 						SigBit A = sigs.at(node.left_parent); | 						SigBit A = sigs.at(node.left_parent); | ||||||
| 						SigBit B = sigs.at(node.right_parent); | 						SigBit B = sigs.at(node.right_parent); | ||||||
| 						if (nand_mode && node.inverter) { | 						if (nand_mode && node.inverter) { | ||||||
| 							bit = module->NandGate(NEW_ID, A, B); | 							bit = module->addWire(NEW_ID); | ||||||
|  | 							auto gate = module->addNandGate(NEW_ID, A, B, bit); | ||||||
|  | 							if (select_mode) | ||||||
|  | 								new_sel.insert(gate->name); | ||||||
|  | 
 | ||||||
| 							goto skip_inverter; | 							goto skip_inverter; | ||||||
| 						} else { | 						} else { | ||||||
| 							pair<int, int> key(node.left_parent, node.right_parent); | 							pair<int, int> key(node.left_parent, node.right_parent); | ||||||
| 							if (and_cache.count(key)) | 							if (and_cache.count(key)) | ||||||
| 								bit = and_cache.at(key); | 								bit = and_cache.at(key); | ||||||
| 							else | 							else { | ||||||
| 								bit = module->AndGate(NEW_ID, A, B); | 								bit = module->addWire(NEW_ID); | ||||||
|  | 								auto gate = module->addAndGate(NEW_ID, A, B, bit); | ||||||
|  | 								if (select_mode) | ||||||
|  | 									new_sel.insert(gate->name); | ||||||
|  | 							} | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					if (node.inverter) | 					if (node.inverter) { | ||||||
| 						bit = module->NotGate(NEW_ID, bit); | 						SigBit new_bit = module->addWire(NEW_ID); | ||||||
|  | 						auto gate = module->addNotGate(NEW_ID, bit, new_bit); | ||||||
|  | 						bit = new_bit; | ||||||
|  | 						if (select_mode) | ||||||
|  | 							new_sel.insert(gate->name); | ||||||
|  | 
 | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 				skip_inverter: | 				skip_inverter: | ||||||
| 					for (auto &op : node.outports) | 					for (auto &op : node.outports) | ||||||
|  | @ -142,6 +169,13 @@ struct AigmapPass : public Pass { | ||||||
| 
 | 
 | ||||||
| 			for (auto cell : replaced_cells) | 			for (auto cell : replaced_cells) | ||||||
| 				module->remove(cell); | 				module->remove(cell); | ||||||
|  | 
 | ||||||
|  | 			if (select_mode) { | ||||||
|  | 				log_assert(!design->selection_stack.empty()); | ||||||
|  | 				RTLIL::Selection& sel = design->selection_stack.back(); | ||||||
|  | 				sel.selected_members[module->name] = std::move(new_sel); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } AigmapPass; | } AigmapPass; | ||||||
|  |  | ||||||
|  | @ -48,14 +48,25 @@ struct AlumaccWorker | ||||||
| 		RTLIL::SigSpec cached_cf, cached_of, cached_sf; | 		RTLIL::SigSpec cached_cf, cached_of, cached_sf; | ||||||
| 
 | 
 | ||||||
| 		RTLIL::SigSpec get_lt() { | 		RTLIL::SigSpec get_lt() { | ||||||
| 			if (GetSize(cached_lt) == 0) | 			if (GetSize(cached_lt) == 0) { | ||||||
| 				cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf(); | 				if (is_signed) { | ||||||
|  | 					get_of(); | ||||||
|  | 					get_sf(); | ||||||
|  | 					cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf); | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 					cached_lt = get_cf(); | ||||||
|  | 			} | ||||||
| 			return cached_lt; | 			return cached_lt; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		RTLIL::SigSpec get_gt() { | 		RTLIL::SigSpec get_gt() { | ||||||
| 			if (GetSize(cached_gt) == 0) | 			if (GetSize(cached_gt) == 0) { | ||||||
| 				cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute()); | 				get_lt(); | ||||||
|  | 				get_eq(); | ||||||
|  | 				SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq); | ||||||
|  | 				cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute()); | ||||||
|  | 			} | ||||||
| 			return cached_gt; | 			return cached_gt; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -34,11 +34,16 @@ struct Dff2dffsPass : public Pass { | ||||||
| 		log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n"); | 		log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n"); | ||||||
| 		log("dff2dffe for SR over CE priority.\n"); | 		log("dff2dffe for SR over CE priority.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -match-init\n"); | ||||||
|  | 		log("        Disallow merging synchronous set/reset that has polarity opposite of the\n"); | ||||||
|  | 		log("        output wire's init attribute (if any).\n"); | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
| 		log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); | 		log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); | ||||||
| 
 | 
 | ||||||
|  | 		bool match_init = false; | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
| 		{ | 		{ | ||||||
|  | @ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass { | ||||||
| 			// 	singleton_mode = true;
 | 			// 	singleton_mode = true;
 | ||||||
| 			// 	continue;
 | 			// 	continue;
 | ||||||
| 			// }
 | 			// }
 | ||||||
|  | 			if (args[argidx] == "-match-init") { | ||||||
|  | 				match_init = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
|  | @ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass { | ||||||
| 				SigBit bit_b = sigmap(mux_cell->getPort(ID::B)); | 				SigBit bit_b = sigmap(mux_cell->getPort(ID::B)); | ||||||
| 				SigBit bit_s = sigmap(mux_cell->getPort(ID(S))); | 				SigBit bit_s = sigmap(mux_cell->getPort(ID(S))); | ||||||
| 
 | 
 | ||||||
| 				log("  Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), |  | ||||||
| 						log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); |  | ||||||
| 
 |  | ||||||
| 				SigBit sr_val, sr_sig; | 				SigBit sr_val, sr_sig; | ||||||
| 				bool invert_sr; | 				bool invert_sr; | ||||||
| 				sr_sig = bit_s; | 				sr_sig = bit_s; | ||||||
|  | @ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass { | ||||||
| 					invert_sr = false; | 					invert_sr = false; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | 				if (match_init) { | ||||||
|  | 					SigBit bit_q = cell->getPort(ID(Q)); | ||||||
|  | 					if (bit_q.wire) { | ||||||
|  | 						auto it = bit_q.wire->attributes.find(ID(init)); | ||||||
|  | 						if (it != bit_q.wire->attributes.end()) { | ||||||
|  | 							auto init_val = it->second[bit_q.offset]; | ||||||
|  | 							if (init_val == State::S1 && sr_val != State::S1) | ||||||
|  | 								continue; | ||||||
|  | 							if (init_val == State::S0 && sr_val != State::S0) | ||||||
|  | 								continue; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				log("  Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), | ||||||
|  | 						log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); | ||||||
|  | 
 | ||||||
| 				if (sr_val == State::S1) { | 				if (sr_val == State::S1) { | ||||||
| 					if (cell->type == ID($_DFF_N_)) { | 					if (cell->type == ID($_DFF_N_)) { | ||||||
| 						if (invert_sr) cell->type = ID($__DFFS_NN1_); | 						if (invert_sr) cell->type = ID($__DFFS_NN1_); | ||||||
|  |  | ||||||
							
								
								
									
										123
									
								
								passes/techmap/extractinv.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								passes/techmap/extractinv.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  *  Copyright (C) 2019  Marcin Kościelnicki <mwk@0x04.net> | ||||||
|  |  * | ||||||
|  |  *  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 "kernel/yosys.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
|  | 
 | ||||||
|  | USING_YOSYS_NAMESPACE | ||||||
|  | PRIVATE_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | void split_portname_pair(std::string &port1, std::string &port2) | ||||||
|  | { | ||||||
|  | 	size_t pos = port1.find_first_of(':'); | ||||||
|  | 	if (pos != std::string::npos) { | ||||||
|  | 		port2 = port1.substr(pos+1); | ||||||
|  | 		port1 = port1.substr(0, pos); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct ExtractinvPass : public Pass { | ||||||
|  | 	ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    extractinv [options] [selection]\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Searches the design for all cells with invertible pins controlled by a cell\n"); | ||||||
|  | 		log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n"); | ||||||
|  | 		log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n"); | ||||||
|  | 		log("the pin instead.  Normally used for output to ISE, which does not support the\n"); | ||||||
|  | 		log("inversion parameters.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n"); | ||||||
|  | 		log("on the wire in the blackbox module.  The parameter value should have\n"); | ||||||
|  | 		log("the same width as the port, and will be effectively XORed with it.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -inv <celltype> <portname_out>:<portname_in>\n"); | ||||||
|  | 		log("        Specifies the cell type to use for the inverters and its port names.\n"); | ||||||
|  | 		log("        This option is required.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n"); | ||||||
|  | 
 | ||||||
|  | 		std::string inv_celltype, inv_portname, inv_portname2; | ||||||
|  | 
 | ||||||
|  | 		size_t argidx; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
|  | 		{ | ||||||
|  | 			std::string arg = args[argidx]; | ||||||
|  | 			if (arg == "-inv" && argidx+2 < args.size()) { | ||||||
|  | 				inv_celltype = args[++argidx]; | ||||||
|  | 				inv_portname = args[++argidx]; | ||||||
|  | 				split_portname_pair(inv_portname, inv_portname2); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  | 		if (inv_celltype.empty()) | ||||||
|  | 			log_error("The -inv option is required.\n"); | ||||||
|  | 
 | ||||||
|  | 		for (auto module : design->selected_modules()) | ||||||
|  | 		{ | ||||||
|  | 			for (auto cell : module->selected_cells()) | ||||||
|  | 			for (auto port : cell->connections()) { | ||||||
|  | 				auto cell_module = design->module(cell->type); | ||||||
|  | 				if (!cell_module) | ||||||
|  | 					continue; | ||||||
|  | 				auto cell_wire = cell_module->wire(port.first); | ||||||
|  | 				if (!cell_wire) | ||||||
|  | 					continue; | ||||||
|  | 				auto it = cell_wire->attributes.find("\\invertible_pin"); | ||||||
|  | 				if (it == cell_wire->attributes.end()) | ||||||
|  | 					continue; | ||||||
|  | 				IdString param_name = RTLIL::escape_id(it->second.decode_string()); | ||||||
|  | 				auto it2 = cell->parameters.find(param_name); | ||||||
|  | 				// Inversion not used -- skip.
 | ||||||
|  | 				if (it2 == cell->parameters.end()) | ||||||
|  | 					continue; | ||||||
|  | 				SigSpec sig = port.second; | ||||||
|  | 				if (it2->second.size() != sig.size()) | ||||||
|  | 					log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name)); | ||||||
|  | 				RTLIL::Const invmask = it2->second; | ||||||
|  | 				cell->parameters.erase(param_name); | ||||||
|  | 				if (invmask.is_fully_zero()) | ||||||
|  | 					continue; | ||||||
|  | 				Wire *iwire = module->addWire(NEW_ID, sig.size()); | ||||||
|  | 				for (int i = 0; i < sig.size(); i++) | ||||||
|  | 					if (invmask[i] == State::S1) { | ||||||
|  | 						RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype)); | ||||||
|  | 						icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i)); | ||||||
|  | 						icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]); | ||||||
|  | 						log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i); | ||||||
|  | 						sig[i] = SigBit(iwire, i); | ||||||
|  | 					} | ||||||
|  | 				cell->setPort(port.first, sig); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } ExtractinvPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
|  | @ -205,21 +205,64 @@ struct TechmapWorker | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; | 		std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; | ||||||
|  | 		dict<Wire*, IdString> temp_renamed_wires; | ||||||
|  | 		pool<SigBit> autopurge_tpl_bits; | ||||||
| 
 | 
 | ||||||
| 		for (auto &it : tpl->wires_) { | 		for (auto &it : tpl->wires_) | ||||||
|  | 		{ | ||||||
| 			if (it.second->port_id > 0) | 			if (it.second->port_id > 0) | ||||||
| 				positional_ports[stringf("$%d", it.second->port_id)] = it.first; | 			{ | ||||||
|  | 				IdString posportname = stringf("$%d", it.second->port_id); | ||||||
|  | 				positional_ports[posportname] = it.first; | ||||||
|  | 
 | ||||||
|  | 				if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) && | ||||||
|  | 						(!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) && | ||||||
|  | 						(!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname)))) | ||||||
|  | 				{ | ||||||
|  | 					if (sigmaps.count(tpl) == 0) | ||||||
|  | 						sigmaps[tpl].set(tpl); | ||||||
|  | 
 | ||||||
|  | 					for (auto bit : sigmaps.at(tpl)(it.second)) | ||||||
|  | 						if (bit.wire != nullptr) | ||||||
|  | 							autopurge_tpl_bits.insert(bit); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			IdString w_name = it.second->name; | 			IdString w_name = it.second->name; | ||||||
| 			apply_prefix(cell->name, w_name); | 			apply_prefix(cell->name, w_name); | ||||||
| 			RTLIL::Wire *w = module->addWire(w_name, it.second); | 			RTLIL::Wire *w = module->wire(w_name); | ||||||
| 			w->port_input = false; | 			if (w != nullptr) { | ||||||
| 			w->port_output = false; | 				if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) { | ||||||
| 			w->port_id = 0; | 					temp_renamed_wires[w] = w->name; | ||||||
| 			if (it.second->get_bool_attribute(ID(_techmap_special_))) | 					module->rename(w, NEW_ID); | ||||||
| 				w->attributes.clear(); | 					w = nullptr; | ||||||
| 			if (w->attributes.count(ID(src))) | 				} else { | ||||||
| 				w->add_strpool_attribute(ID(src), extra_src_attrs); | 					w->attributes.erase(ID(hierconn)); | ||||||
|  | 					if (GetSize(w) < GetSize(it.second)) { | ||||||
|  | 						log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w), | ||||||
|  | 								log_id(tpl), log_id(it.second), log_id(module), log_id(cell)); | ||||||
|  | 						w->width = GetSize(it.second); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if (w == nullptr) { | ||||||
|  | 				w = module->addWire(w_name, it.second); | ||||||
|  | 				w->port_input = false; | ||||||
|  | 				w->port_output = false; | ||||||
|  | 				w->port_id = 0; | ||||||
|  | 				if (!flatten_mode) | ||||||
|  | 					w->attributes.erase(ID(techmap_autopurge)); | ||||||
|  | 				if (it.second->get_bool_attribute(ID(_techmap_special_))) | ||||||
|  | 					w->attributes.clear(); | ||||||
|  | 				if (w->attributes.count(ID(src))) | ||||||
|  | 					w->add_strpool_attribute(ID(src), extra_src_attrs); | ||||||
|  | 			} | ||||||
| 			design->select(module, w); | 			design->select(module, w); | ||||||
|  | 
 | ||||||
|  | 			if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) { | ||||||
|  | 				IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), it.second->name.c_str() + strlen("\\_TECHMAP_REPLACE_")); | ||||||
|  | 				Wire *replace_w = module->addWire(replace_name, it.second); | ||||||
|  | 				module->connect(replace_w, w); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		SigMap tpl_sigmap(tpl); | 		SigMap tpl_sigmap(tpl); | ||||||
|  | @ -322,6 +365,12 @@ struct TechmapWorker | ||||||
| 				for (auto &attr : w->attributes) { | 				for (auto &attr : w->attributes) { | ||||||
| 					if (attr.first == ID(src)) | 					if (attr.first == ID(src)) | ||||||
| 						continue; | 						continue; | ||||||
|  | 					auto lhs = GetSize(extra_connect.first); | ||||||
|  | 					auto rhs = GetSize(extra_connect.second); | ||||||
|  | 					if (lhs > rhs) | ||||||
|  | 						extra_connect.first.remove(rhs, lhs-rhs); | ||||||
|  | 					else if (rhs > lhs) | ||||||
|  | 						extra_connect.second.remove(lhs, rhs-lhs); | ||||||
| 					module->connect(extra_connect); | 					module->connect(extra_connect); | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
|  | @ -335,6 +384,8 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 			if (techmap_replace_cell) | 			if (techmap_replace_cell) | ||||||
| 				c_name = orig_cell_name; | 				c_name = orig_cell_name; | ||||||
|  | 			else if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) | ||||||
|  | 				c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_")); | ||||||
| 			else | 			else | ||||||
| 				apply_prefix(cell->name, c_name); | 				apply_prefix(cell->name, c_name); | ||||||
| 
 | 
 | ||||||
|  | @ -344,11 +395,31 @@ struct TechmapWorker | ||||||
| 			if (!flatten_mode && c->type.begins_with("\\$")) | 			if (!flatten_mode && c->type.begins_with("\\$")) | ||||||
| 				c->type = c->type.substr(1); | 				c->type = c->type.substr(1); | ||||||
| 
 | 
 | ||||||
| 			for (auto &it2 : c->connections_) { | 			vector<IdString> autopurge_ports; | ||||||
| 				apply_prefix(cell->name, it2.second, module); | 
 | ||||||
| 				port_signal_map.apply(it2.second); | 			for (auto &it2 : c->connections_) | ||||||
|  | 			{ | ||||||
|  | 				bool autopurge = false; | ||||||
|  | 				if (!autopurge_tpl_bits.empty()) { | ||||||
|  | 					autopurge = GetSize(it2.second) != 0; | ||||||
|  | 					for (auto &bit : sigmaps.at(tpl)(it2.second)) | ||||||
|  | 						if (!autopurge_tpl_bits.count(bit)) { | ||||||
|  | 							autopurge = false; | ||||||
|  | 							break; | ||||||
|  | 						} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				if (autopurge) { | ||||||
|  | 					autopurge_ports.push_back(it2.first); | ||||||
|  | 				} else { | ||||||
|  | 					apply_prefix(cell->name, it2.second, module); | ||||||
|  | 					port_signal_map.apply(it2.second); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			for (auto &it2 : autopurge_ports) | ||||||
|  | 				c->unsetPort(it2); | ||||||
|  | 
 | ||||||
| 			if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) { | 			if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) { | ||||||
| 				IdString memid = c->getParam(ID(MEMID)).decode_string(); | 				IdString memid = c->getParam(ID(MEMID)).decode_string(); | ||||||
| 				log_assert(memory_renames.count(memid) != 0); | 				log_assert(memory_renames.count(memid) != 0); | ||||||
|  | @ -380,6 +451,16 @@ struct TechmapWorker | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		module->remove(cell); | 		module->remove(cell); | ||||||
|  | 
 | ||||||
|  | 		for (auto &it : temp_renamed_wires) | ||||||
|  | 		{ | ||||||
|  | 			Wire *w = it.first; | ||||||
|  | 			IdString name = it.second; | ||||||
|  | 			IdString altname = module->uniquify(name); | ||||||
|  | 			Wire *other_w = module->wire(name); | ||||||
|  | 			module->rename(other_w, altname); | ||||||
|  | 			module->rename(w, name); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, | 	bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, | ||||||
|  | @ -396,6 +477,18 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 		SigMap sigmap(module); | 		SigMap sigmap(module); | ||||||
| 
 | 
 | ||||||
|  | 		dict<SigBit, State> init_bits; | ||||||
|  | 		pool<SigBit> remove_init_bits; | ||||||
|  | 
 | ||||||
|  | 		for (auto wire : module->wires()) { | ||||||
|  | 			if (wire->attributes.count("\\init")) { | ||||||
|  | 				Const value = wire->attributes.at("\\init"); | ||||||
|  | 				for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) | ||||||
|  | 					if (value[i] != State::Sx) | ||||||
|  | 						init_bits[sigmap(SigBit(wire, i))] = value[i]; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells; | 		TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells; | ||||||
| 		std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; | 		std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; | ||||||
| 		std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; | 		std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; | ||||||
|  | @ -633,6 +726,17 @@ struct TechmapWorker | ||||||
| 									bit = RTLIL::SigBit(RTLIL::State::Sx); | 									bit = RTLIL::SigBit(RTLIL::State::Sx); | ||||||
| 							parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); | 							parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); | ||||||
| 						} | 						} | ||||||
|  | 						if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) { | ||||||
|  | 							auto sig = sigmap(conn.second); | ||||||
|  | 							RTLIL::Const value(State::Sx, sig.size()); | ||||||
|  | 							for (int i = 0; i < sig.size(); i++) { | ||||||
|  | 								auto it = init_bits.find(sig[i]); | ||||||
|  | 								if (it != init_bits.end()) { | ||||||
|  | 									value[i] = it->second; | ||||||
|  | 								} | ||||||
|  | 							} | ||||||
|  | 							parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value; | ||||||
|  | 						} | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					int unique_bit_id_counter = 0; | 					int unique_bit_id_counter = 0; | ||||||
|  | @ -833,7 +937,7 @@ struct TechmapWorker | ||||||
| 
 | 
 | ||||||
| 					TechmapWires twd = techmap_find_special_wires(tpl); | 					TechmapWires twd = techmap_find_special_wires(tpl); | ||||||
| 					for (auto &it : twd) { | 					for (auto &it : twd) { | ||||||
| 						if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") | 						if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") | ||||||
| 							log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str()); | 							log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str()); | ||||||
| 						if (techmap_do_cache[tpl]) | 						if (techmap_do_cache[tpl]) | ||||||
| 							for (auto &it2 : it.second) | 							for (auto &it2 : it.second) | ||||||
|  | @ -864,6 +968,23 @@ struct TechmapWorker | ||||||
| 					mkdebug.off(); | 					mkdebug.off(); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | 				TechmapWires twd = techmap_find_special_wires(tpl); | ||||||
|  | 				for (auto &it : twd) { | ||||||
|  | 					if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") { | ||||||
|  | 						for (auto &it2 : it.second) { | ||||||
|  | 							auto val = it2.value.as_const(); | ||||||
|  | 							auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1)); | ||||||
|  | 							auto it = cell->connections().find(wirename); | ||||||
|  | 							if (it != cell->connections().end()) { | ||||||
|  | 								auto sig = sigmap(it->second); | ||||||
|  | 								for (int i = 0; i < sig.size(); i++) | ||||||
|  | 									if (val[i] == State::S1) | ||||||
|  | 										remove_init_bits.insert(sig[i]); | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				if (extern_mode && !in_recursion) | 				if (extern_mode && !in_recursion) | ||||||
| 				{ | 				{ | ||||||
| 					std::string m_name = stringf("$extern:%s", log_id(tpl)); | 					std::string m_name = stringf("$extern:%s", log_id(tpl)); | ||||||
|  | @ -907,6 +1028,25 @@ struct TechmapWorker | ||||||
| 			handled_cells.insert(cell); | 			handled_cells.insert(cell); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (!remove_init_bits.empty()) { | ||||||
|  | 			for (auto wire : module->wires()) | ||||||
|  | 				if (wire->attributes.count("\\init")) { | ||||||
|  | 					Const &value = wire->attributes.at("\\init"); | ||||||
|  | 					bool do_cleanup = true; | ||||||
|  | 					for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { | ||||||
|  | 						SigBit bit = sigmap(SigBit(wire, i)); | ||||||
|  | 						if (remove_init_bits.count(bit)) | ||||||
|  | 							value[i] = State::Sx; | ||||||
|  | 						else if (value[i] != State::Sx) | ||||||
|  | 							do_cleanup = false; | ||||||
|  | 					} | ||||||
|  | 					if (do_cleanup) { | ||||||
|  | 						log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); | ||||||
|  | 						wire->attributes.erase("\\init"); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (log_continue) { | 		if (log_continue) { | ||||||
| 			log_header(design, "Continuing TECHMAP pass.\n"); | 			log_header(design, "Continuing TECHMAP pass.\n"); | ||||||
| 			log_continue = false; | 			log_continue = false; | ||||||
|  | @ -981,6 +1121,11 @@ struct TechmapPass : public Pass { | ||||||
| 		log("will create a wrapper for the cell and then run the command string that the\n"); | 		log("will create a wrapper for the cell and then run the command string that the\n"); | ||||||
| 		log("attribute is set to on the wrapper module.\n"); | 		log("attribute is set to on the wrapper module.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n"); | ||||||
|  | 		log("set, and that port is not connected in the instantiation that is mapped, then\n"); | ||||||
|  | 		log("then a cell port connected only to such wires will be omitted in the mapped\n"); | ||||||
|  | 		log("version of the circuit.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n"); | 		log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n"); | ||||||
| 		log("or *._TECHMAP_* are special wires that are used to pass instructions from\n"); | 		log("or *._TECHMAP_* are special wires that are used to pass instructions from\n"); | ||||||
| 		log("the mapping module to the techmap command. At the moment the following special\n"); | 		log("the mapping module to the techmap command. At the moment the following special\n"); | ||||||
|  | @ -1019,6 +1164,13 @@ struct TechmapPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("        It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n"); | 		log("        It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    _TECHMAP_REMOVEINIT_<port-name>_\n"); | ||||||
|  | 		log("        When this wire is set to a constant value, the init attribute of the wire(s)\n"); | ||||||
|  | 		log("        connected to this port will be consumed.  This wire must have the same\n"); | ||||||
|  | 		log("        width as the given port, and for every bit that is set to 1 in the value,\n"); | ||||||
|  | 		log("        the corresponding init attribute bit will be changed to 1'bx.  If all\n"); | ||||||
|  | 		log("        bits of an init attribute are left as x, it will be removed.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("In addition to this special wires, techmap also supports special parameters in\n"); | 		log("In addition to this special wires, techmap also supports special parameters in\n"); | ||||||
| 		log("modules in the map file:\n"); | 		log("modules in the map file:\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -1032,6 +1184,13 @@ struct TechmapPass : public Pass { | ||||||
| 		log("        former has a 1-bit for each constant input bit and the latter has the\n"); | 		log("        former has a 1-bit for each constant input bit and the latter has the\n"); | ||||||
| 		log("        value for this bit. The unused bits of the latter are set to undef (x).\n"); | 		log("        value for this bit. The unused bits of the latter are set to undef (x).\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    _TECHMAP_WIREINIT_<port-name>_\n"); | ||||||
|  | 		log("        When a parameter with this name exists, it will be set to the initial\n"); | ||||||
|  | 		log("        value of the wire(s) connected to the given port, as specified by the init\n"); | ||||||
|  | 		log("        attribute. If the attribute doesn't exist, x will be filled for the\n"); | ||||||
|  | 		log("        missing bits.  To remove the init attribute bits used, use the\n"); | ||||||
|  | 		log("        _TECHMAP_REMOVEINIT_*_ wires.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    _TECHMAP_BITS_CONNMAP_\n"); | 		log("    _TECHMAP_BITS_CONNMAP_\n"); | ||||||
| 		log("    _TECHMAP_CONNMAP_<port-name>_\n"); | 		log("    _TECHMAP_CONNMAP_<port-name>_\n"); | ||||||
| 		log("        For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n"); | 		log("        For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n"); | ||||||
|  | @ -1047,6 +1206,12 @@ struct TechmapPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); | 		log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); | ||||||
| 		log("and attributes of the cell that is being replaced.\n"); | 		log("and attributes of the cell that is being replaced.\n"); | ||||||
|  | 		log("A cell with a name of the form `_TECHMAP_REPLACE_.<suffix>` in the map file will\n"); | ||||||
|  | 		log("be named thus but with the `_TECHMAP_REPLACE_' prefix substituted with the name\n"); | ||||||
|  | 		log("of the cell being replaced.\n"); | ||||||
|  | 		log("Similarly, a wire named in the form `_TECHMAP_REPLACE_.<suffix>` will cause a\n"); | ||||||
|  | 		log("new wire alias to be created and named as above but with the `_TECHMAP_REPLACE_'\n"); | ||||||
|  | 		log("prefix also substituted.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("See 'help extract' for a pass that does the opposite thing.\n"); | 		log("See 'help extract' for a pass that does the opposite thing.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  |  | ||||||
|  | @ -345,9 +345,17 @@ struct TestAutotbBackend : public Backend { | ||||||
| 		log("value after initialization. This can e.g. be used to force a reset signal\n"); | 		log("value after initialization. This can e.g. be used to force a reset signal\n"); | ||||||
| 		log("low in order to explore more inner states in a state machine.\n"); | 		log("low in order to explore more inner states in a state machine.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n"); | ||||||
|  | 		log("generation.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -n <int>\n"); | 		log("    -n <int>\n"); | ||||||
| 		log("        number of iterations the test bench should run (default = 1000)\n"); | 		log("        number of iterations the test bench should run (default = 1000)\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -seed <int>\n"); | ||||||
|  | 		log("        seed used for pseudo-random number generation (default = 0).\n"); | ||||||
|  | 		log("        a value of 0 will cause an arbitrary seed to be chosen, based on\n"); | ||||||
|  | 		log("        the current system time.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| module AL_MAP_SEQ ( | module AL_MAP_SEQ ( | ||||||
| 	output q, | 	output reg q, | ||||||
| 	input ce, | 	input ce, | ||||||
| 	input clk, | 	input clk, | ||||||
| 	input sr, | 	input sr, | ||||||
|  | @ -9,6 +9,71 @@ module AL_MAP_SEQ ( | ||||||
| 	parameter REGSET = "RESET"; //RESET/SET | 	parameter REGSET = "RESET"; //RESET/SET | ||||||
| 	parameter SRMUX = "SR"; //SR/INV | 	parameter SRMUX = "SR"; //SR/INV | ||||||
| 	parameter SRMODE = "SYNC"; //SYNC/ASYNC | 	parameter SRMODE = "SYNC"; //SYNC/ASYNC | ||||||
|  | 
 | ||||||
|  | 	wire clk_ce; | ||||||
|  | 	assign clk_ce = ce ? clk : 1'b0; | ||||||
|  | 
 | ||||||
|  | 	wire srmux; | ||||||
|  | 	generate | ||||||
|  | 		case (SRMUX) | ||||||
|  | 			"SR": assign srmux = sr; | ||||||
|  | 			"INV": assign srmux = ~sr; | ||||||
|  | 			default: assign srmux = sr; | ||||||
|  | 		endcase | ||||||
|  | 	endgenerate	 | ||||||
|  | 
 | ||||||
|  | 	wire regset; | ||||||
|  | 	generate | ||||||
|  | 		case (REGSET) | ||||||
|  | 			"RESET": assign regset = 1'b0; | ||||||
|  | 			"SET": assign regset = 1'b1; | ||||||
|  | 			default: assign regset = 1'b0; | ||||||
|  | 		endcase | ||||||
|  | 	endgenerate | ||||||
|  | 
 | ||||||
|  | 	initial q = regset; | ||||||
|  | 
 | ||||||
|  | 	generate | ||||||
|  |    		if (DFFMODE == "FF")  | ||||||
|  | 		begin | ||||||
|  | 			if (SRMODE == "ASYNC")  | ||||||
|  | 			begin | ||||||
|  | 				always @(posedge clk_ce, posedge srmux) | ||||||
|  | 					if (srmux) | ||||||
|  | 						q <= regset; | ||||||
|  | 					else  | ||||||
|  | 						q <= d;	 | ||||||
|  | 			end  | ||||||
|  | 			else | ||||||
|  | 			begin | ||||||
|  | 				always @(posedge clk_ce) | ||||||
|  | 					if (srmux) | ||||||
|  | 						q <= regset; | ||||||
|  | 					else  | ||||||
|  | 						q <= d;	 | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		else | ||||||
|  | 		begin | ||||||
|  | 			// DFFMODE == "LATCH" | ||||||
|  | 			if (SRMODE == "ASYNC")  | ||||||
|  | 			begin | ||||||
|  | 				always @(clk_ce, srmux) | ||||||
|  | 					if (srmux) | ||||||
|  | 						q <= regset; | ||||||
|  | 					else  | ||||||
|  | 						q <= d;	 | ||||||
|  | 			end  | ||||||
|  | 			else | ||||||
|  | 			begin | ||||||
|  | 				always @(clk_ce) | ||||||
|  | 					if (srmux) | ||||||
|  | 						q <= regset; | ||||||
|  | 					else  | ||||||
|  | 						q <= d;	 | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  |     endgenerate | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module AL_MAP_LUT1 ( | module AL_MAP_LUT1 ( | ||||||
|  | @ -17,7 +82,8 @@ module AL_MAP_LUT1 ( | ||||||
| ); | ); | ||||||
| 	parameter [1:0] INIT = 2'h0; | 	parameter [1:0] INIT = 2'h0; | ||||||
| 	parameter EQN = "(A)"; | 	parameter EQN = "(A)"; | ||||||
| 	assign o = INIT >> a; | 
 | ||||||
|  | 	assign o = a ? INIT[1] : INIT[0];	 | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module AL_MAP_LUT2 ( | module AL_MAP_LUT2 ( | ||||||
|  | @ -27,7 +93,9 @@ module AL_MAP_LUT2 ( | ||||||
| ); | ); | ||||||
| 	parameter [3:0] INIT = 4'h0; | 	parameter [3:0] INIT = 4'h0; | ||||||
| 	parameter EQN = "(A)"; | 	parameter EQN = "(A)"; | ||||||
| 	assign o = INIT >> {b, a}; | 
 | ||||||
|  | 	wire [1:0] s1 = b ? INIT[ 3:2] : INIT[1:0]; | ||||||
|  | 	assign o = a ? s1[1] : s1[0];	 | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module AL_MAP_LUT3 ( | module AL_MAP_LUT3 ( | ||||||
|  | @ -38,7 +106,10 @@ module AL_MAP_LUT3 ( | ||||||
| ); | ); | ||||||
| 	parameter [7:0] INIT = 8'h0; | 	parameter [7:0] INIT = 8'h0; | ||||||
| 	parameter EQN = "(A)"; | 	parameter EQN = "(A)"; | ||||||
| 	assign o = INIT >> {c, b, a}; | 
 | ||||||
|  | 	wire [3:0] s2 = c ? INIT[ 7:4] : INIT[3:0]; | ||||||
|  | 	wire [1:0] s1 = b ?   s2[ 3:2] :   s2[1:0]; | ||||||
|  | 	assign o = a ? s1[1] : s1[0];	 | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module AL_MAP_LUT4 ( | module AL_MAP_LUT4 ( | ||||||
|  | @ -50,7 +121,11 @@ module AL_MAP_LUT4 ( | ||||||
| ); | ); | ||||||
| 	parameter [15:0] INIT = 16'h0; | 	parameter [15:0] INIT = 16'h0; | ||||||
| 	parameter EQN = "(A)"; | 	parameter EQN = "(A)"; | ||||||
| 	assign o = INIT >> {d, c, b, a}; | 
 | ||||||
|  | 	wire [7:0] s3 = d ? INIT[15:8] : INIT[7:0]; | ||||||
|  | 	wire [3:0] s2 = c ?   s3[ 7:4] :   s3[3:0]; | ||||||
|  | 	wire [1:0] s1 = b ?   s2[ 3:2] :   s2[1:0]; | ||||||
|  | 	assign o = a ? s1[1] : s1[0];	 | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module AL_MAP_LUT5 ( | module AL_MAP_LUT5 ( | ||||||
|  | @ -100,4 +175,18 @@ module AL_MAP_ADDER ( | ||||||
|   output [1:0] o |   output [1:0] o | ||||||
| ); | ); | ||||||
| 	parameter ALUTYPE = "ADD"; | 	parameter ALUTYPE = "ADD"; | ||||||
|  | 
 | ||||||
|  | 	generate | ||||||
|  | 		case (ALUTYPE) | ||||||
|  | 			"ADD": 		 assign o = a + b + c; | ||||||
|  | 			"SUB": 		 assign o = a - b - c; | ||||||
|  | 			"A_LE_B":    assign o = a - b - c; | ||||||
|  | 
 | ||||||
|  | 			"ADD_CARRY":    assign o = {  a, 1'b0 }; | ||||||
|  | 			"SUB_CARRY":    assign o = { ~a, 1'b0 }; | ||||||
|  | 			"A_LE_B_CARRY": assign o = {  a, 1'b0 }; | ||||||
|  | 			default: assign o = a + b + c; | ||||||
|  | 		endcase | ||||||
|  | 	endgenerate	 | ||||||
|  | 
 | ||||||
| endmodule | endmodule | ||||||
|  |  | ||||||
|  | @ -9,12 +9,12 @@ GENFILES += techlibs/common/simcells_help.inc | ||||||
| 
 | 
 | ||||||
| techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v | techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v | ||||||
| 	$(Q) mkdir -p techlibs/common | 	$(Q) mkdir -p techlibs/common | ||||||
| 	$(P) python3 $^ > $@.new | 	$(P) $(PYTHON_EXECUTABLE) $^ > $@.new | ||||||
| 	$(Q) mv $@.new $@ | 	$(Q) mv $@.new $@ | ||||||
| 
 | 
 | ||||||
| techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v | techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v | ||||||
| 	$(Q) mkdir -p techlibs/common | 	$(Q) mkdir -p techlibs/common | ||||||
| 	$(P) python3 $^ > $@.new | 	$(P) $(PYTHON_EXECUTABLE) $^ > $@.new | ||||||
| 	$(Q) mv $@.new $@ | 	$(Q) mv $@.new $@ | ||||||
| 
 | 
 | ||||||
| kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc | kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc | ||||||
|  | @ -28,3 +28,5 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v)) | ||||||
| $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) | $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) | ||||||
| $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) | $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) | ||||||
| $(eval $(call add_share_file,share,techlibs/common/cells.lib)) | $(eval $(call add_share_file,share,techlibs/common/cells.lib)) | ||||||
|  | $(eval $(call add_share_file,share,techlibs/common/mul2dsp.v)) | ||||||
|  | $(eval $(call add_share_file,share,techlibs/common/dummy.box)) | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								techlibs/common/dummy.box
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								techlibs/common/dummy.box
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | (dummy) 1 0 0 0 | ||||||
							
								
								
									
										296
									
								
								techlibs/common/mul2dsp.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								techlibs/common/mul2dsp.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,296 @@ | ||||||
|  | /* | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  *                2019  Eddie Hung    <eddie@fpgeh.com> | ||||||
|  |  *                2019  David Shah    <dave@ds0.me> | ||||||
|  |  * | ||||||
|  |  *  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. | ||||||
|  |  * | ||||||
|  |  *  --- | ||||||
|  |  * | ||||||
|  |  *  Tech-mapping rules for decomposing arbitrarily-sized $mul cells | ||||||
|  |  *  into an equivalent collection of smaller `DSP_NAME cells (with the  | ||||||
|  |  *  same interface as $mul) no larger than `DSP_[AB]_MAXWIDTH, attached  | ||||||
|  |  *  to $shl and $add cells. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | `ifndef DSP_A_MAXWIDTH | ||||||
|  | $fatal(1, "Macro DSP_A_MAXWIDTH must be defined"); | ||||||
|  | `endif | ||||||
|  | `ifndef DSP_B_MAXWIDTH | ||||||
|  | $fatal(1, "Macro DSP_B_MAXWIDTH must be defined"); | ||||||
|  | `endif | ||||||
|  | `ifndef DSP_B_MAXWIDTH | ||||||
|  | $fatal(1, "Macro DSP_B_MAXWIDTH must be defined"); | ||||||
|  | `endif | ||||||
|  | `ifndef DSP_A_MAXWIDTH_PARTIAL | ||||||
|  | `define DSP_A_MAXWIDTH_PARTIAL `DSP_A_MAXWIDTH | ||||||
|  | `endif | ||||||
|  | `ifndef DSP_B_MAXWIDTH_PARTIAL | ||||||
|  | `define DSP_B_MAXWIDTH_PARTIAL `DSP_B_MAXWIDTH | ||||||
|  | `endif | ||||||
|  | 
 | ||||||
|  | `ifndef DSP_NAME | ||||||
|  | $fatal(1, "Macro DSP_NAME must be defined"); | ||||||
|  | `endif | ||||||
|  | 
 | ||||||
|  | `define MAX(a,b) (a > b ? a : b) | ||||||
|  | `define MIN(a,b) (a < b ? a : b) | ||||||
|  | 
 | ||||||
|  | (* techmap_celltype = "$mul $__mul" *) | ||||||
|  | module _80_mul (A, B, Y); | ||||||
|  | 	parameter A_SIGNED = 0; | ||||||
|  | 	parameter B_SIGNED = 0; | ||||||
|  | 	parameter A_WIDTH = 1; | ||||||
|  | 	parameter B_WIDTH = 1; | ||||||
|  | 	parameter Y_WIDTH = 1; | ||||||
|  | 
 | ||||||
|  | 	input [A_WIDTH-1:0] A; | ||||||
|  | 	input [B_WIDTH-1:0] B; | ||||||
|  | 	output [Y_WIDTH-1:0] Y; | ||||||
|  | 
 | ||||||
|  | 	parameter _TECHMAP_CELLTYPE_ = ""; | ||||||
|  | 
 | ||||||
|  | 	generate | ||||||
|  | 	if (0) begin end | ||||||
|  | `ifdef DSP_A_MINWIDTH | ||||||
|  | 	else if (A_WIDTH < `DSP_A_MINWIDTH) | ||||||
|  | 		wire _TECHMAP_FAIL_ = 1; | ||||||
|  | `endif | ||||||
|  | `ifdef DSP_B_MINWIDTH | ||||||
|  | 	else if (B_WIDTH < `DSP_B_MINWIDTH) | ||||||
|  | 		wire _TECHMAP_FAIL_ = 1; | ||||||
|  | `endif | ||||||
|  | `ifdef DSP_Y_MINWIDTH | ||||||
|  | 	else if (Y_WIDTH < `DSP_Y_MINWIDTH) | ||||||
|  | 		wire _TECHMAP_FAIL_ = 1; | ||||||
|  | `endif | ||||||
|  | `ifdef DSP_SIGNEDONLY | ||||||
|  | 	else if (_TECHMAP_CELLTYPE_ == "$mul" && !A_SIGNED && !B_SIGNED) | ||||||
|  | 		\$mul #( | ||||||
|  | 			.A_SIGNED(1), | ||||||
|  | 			.B_SIGNED(1), | ||||||
|  | 			.A_WIDTH(A_WIDTH + 1), | ||||||
|  | 			.B_WIDTH(B_WIDTH + 1), | ||||||
|  | 			.Y_WIDTH(Y_WIDTH) | ||||||
|  | 		) _TECHMAP_REPLACE_ ( | ||||||
|  | 			.A({1'b0, A}), | ||||||
|  | 			.B({1'b0, B}), | ||||||
|  | 			.Y(Y) | ||||||
|  | 		); | ||||||
|  | `endif | ||||||
|  | 	else if (_TECHMAP_CELLTYPE_ == "$mul" && A_WIDTH < B_WIDTH) | ||||||
|  | 		\$mul #( | ||||||
|  | 			.A_SIGNED(B_SIGNED), | ||||||
|  | 			.B_SIGNED(A_SIGNED), | ||||||
|  | 			.A_WIDTH(B_WIDTH), | ||||||
|  | 			.B_WIDTH(A_WIDTH), | ||||||
|  | 			.Y_WIDTH(Y_WIDTH) | ||||||
|  | 		) _TECHMAP_REPLACE_ ( | ||||||
|  | 			.A(B), | ||||||
|  | 			.B(A), | ||||||
|  | 			.Y(Y) | ||||||
|  | 		); | ||||||
|  | 	else begin | ||||||
|  | 		wire [1023:0] _TECHMAP_DO_ = "proc; clean"; | ||||||
|  | 
 | ||||||
|  | `ifdef DSP_SIGNEDONLY | ||||||
|  | 		localparam sign_headroom = 1; | ||||||
|  | `else | ||||||
|  | 		localparam sign_headroom = 0; | ||||||
|  | `endif | ||||||
|  | 
 | ||||||
|  | 		genvar i; | ||||||
|  | 		if (A_WIDTH > `DSP_A_MAXWIDTH) begin | ||||||
|  | 			localparam n = (A_WIDTH-`DSP_A_MAXWIDTH+`DSP_A_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_A_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
|  | 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL); | ||||||
|  | 			localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
|  | 			localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH; | ||||||
|  | 			if (A_SIGNED && B_SIGNED) begin | ||||||
|  | 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
|  | 				wire signed [last_Y_WIDTH-1:0] last_partial; | ||||||
|  | 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
|  | 			end | ||||||
|  | 			else begin | ||||||
|  | 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
|  | 				wire [last_Y_WIDTH-1:0] last_partial; | ||||||
|  | 				wire [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
|  | 			end | ||||||
|  | 
 | ||||||
|  | 			for (i = 0; i < n; i=i+1) begin:sliceA | ||||||
|  | 				\$__mul #( | ||||||
|  | 					.A_SIGNED(sign_headroom), | ||||||
|  | 					.B_SIGNED(B_SIGNED), | ||||||
|  | 					.A_WIDTH(`DSP_A_MAXWIDTH_PARTIAL), | ||||||
|  | 					.B_WIDTH(B_WIDTH), | ||||||
|  | 					.Y_WIDTH(partial_Y_WIDTH) | ||||||
|  | 				) mul ( | ||||||
|  | 					.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}), | ||||||
|  | 					.B(B), | ||||||
|  | 					.Y(partial[i]) | ||||||
|  | 				); | ||||||
|  | 				// TODO: Currently a 'cascade' approach to summing the partial | ||||||
|  | 				//       products is taken here, but a more efficient 'binary | ||||||
|  | 				//       reduction' approach also exists... | ||||||
|  | 				if (i == 0) | ||||||
|  | 					assign partial_sum[i] = partial[i]; | ||||||
|  | 				else | ||||||
|  | 					assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1]; | ||||||
|  | 			end | ||||||
|  | 
 | ||||||
|  | 			\$__mul #( | ||||||
|  | 				.A_SIGNED(A_SIGNED), | ||||||
|  | 				.B_SIGNED(B_SIGNED), | ||||||
|  | 				.A_WIDTH(last_A_WIDTH), | ||||||
|  | 				.B_WIDTH(B_WIDTH), | ||||||
|  | 				.Y_WIDTH(last_Y_WIDTH) | ||||||
|  | 			) sliceA.last ( | ||||||
|  | 				.A(A[A_WIDTH-1 -: last_A_WIDTH]), | ||||||
|  | 				.B(B), | ||||||
|  | 				.Y(last_partial) | ||||||
|  | 			); | ||||||
|  | 			assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1]; | ||||||
|  | 			assign Y = partial_sum[n]; | ||||||
|  | 		end | ||||||
|  | 		else if (B_WIDTH > `DSP_B_MAXWIDTH) begin | ||||||
|  | 			localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
|  | 			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL); | ||||||
|  | 			localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom); | ||||||
|  | 			localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH; | ||||||
|  | 			if (A_SIGNED && B_SIGNED) begin | ||||||
|  | 				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
|  | 				wire signed [last_Y_WIDTH-1:0] last_partial; | ||||||
|  | 				wire signed [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
|  | 			end | ||||||
|  | 			else begin | ||||||
|  | 				wire [partial_Y_WIDTH-1:0] partial [n-1:0]; | ||||||
|  | 				wire [last_Y_WIDTH-1:0] last_partial; | ||||||
|  | 				wire [Y_WIDTH-1:0] partial_sum [n:0]; | ||||||
|  | 			end | ||||||
|  | 
 | ||||||
|  | 			for (i = 0; i < n; i=i+1) begin:sliceB | ||||||
|  | 				\$__mul #( | ||||||
|  | 					.A_SIGNED(A_SIGNED), | ||||||
|  | 					.B_SIGNED(sign_headroom), | ||||||
|  | 					.A_WIDTH(A_WIDTH), | ||||||
|  | 					.B_WIDTH(`DSP_B_MAXWIDTH_PARTIAL), | ||||||
|  | 					.Y_WIDTH(partial_Y_WIDTH) | ||||||
|  | 				) mul ( | ||||||
|  | 					.A(A), | ||||||
|  | 					.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}), | ||||||
|  | 					.Y(partial[i]) | ||||||
|  | 				); | ||||||
|  | 				// TODO: Currently a 'cascade' approach to summing the partial | ||||||
|  | 				//       products is taken here, but a more efficient 'binary | ||||||
|  | 				//       reduction' approach also exists... | ||||||
|  | 				if (i == 0) | ||||||
|  | 					assign partial_sum[i] = partial[i]; | ||||||
|  | 				else | ||||||
|  | 					assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1]; | ||||||
|  | 			end | ||||||
|  | 
 | ||||||
|  | 			\$__mul #( | ||||||
|  | 				.A_SIGNED(A_SIGNED), | ||||||
|  | 				.B_SIGNED(B_SIGNED), | ||||||
|  | 				.A_WIDTH(A_WIDTH), | ||||||
|  | 				.B_WIDTH(last_B_WIDTH), | ||||||
|  | 				.Y_WIDTH(last_Y_WIDTH) | ||||||
|  | 			) mul_sliceB_last ( | ||||||
|  | 				.A(A), | ||||||
|  | 				.B(B[B_WIDTH-1 -: last_B_WIDTH]), | ||||||
|  | 				.Y(last_partial) | ||||||
|  | 			); | ||||||
|  | 			assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1]; | ||||||
|  | 			assign Y = partial_sum[n]; | ||||||
|  | 		end | ||||||
|  | 		else begin | ||||||
|  | 			if (A_SIGNED) | ||||||
|  | 				wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A); | ||||||
|  | 			else | ||||||
|  | 				wire [`DSP_A_MAXWIDTH-1:0] Aext = A; | ||||||
|  | 			if (B_SIGNED) | ||||||
|  | 				wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B); | ||||||
|  | 			else | ||||||
|  | 				wire [`DSP_B_MAXWIDTH-1:0] Bext = B; | ||||||
|  | 
 | ||||||
|  | 			`DSP_NAME #( | ||||||
|  | 				.A_SIGNED(A_SIGNED), | ||||||
|  | 				.B_SIGNED(B_SIGNED), | ||||||
|  | 				.A_WIDTH(`DSP_A_MAXWIDTH), | ||||||
|  | 				.B_WIDTH(`DSP_B_MAXWIDTH), | ||||||
|  | 				.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)), | ||||||
|  | 			) _TECHMAP_REPLACE_ ( | ||||||
|  | 				.A(Aext), | ||||||
|  | 				.B(Bext), | ||||||
|  | 				.Y(Y) | ||||||
|  | 			); | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | (* techmap_celltype = "$mul $__mul" *) | ||||||
|  | module _90_soft_mul (A, B, Y); | ||||||
|  | 	parameter A_SIGNED = 0; | ||||||
|  | 	parameter B_SIGNED = 0; | ||||||
|  | 	parameter A_WIDTH = 1; | ||||||
|  | 	parameter B_WIDTH = 1; | ||||||
|  | 	parameter Y_WIDTH = 1; | ||||||
|  | 
 | ||||||
|  | 	input [A_WIDTH-1:0] A; | ||||||
|  | 	input [B_WIDTH-1:0] B; | ||||||
|  | 	output [Y_WIDTH-1:0] Y; | ||||||
|  | 
 | ||||||
|  | 	// Indirection necessary since mapping | ||||||
|  | 	//   back to $mul will cause recursion | ||||||
|  | 	generate | ||||||
|  | 	if (A_SIGNED && !B_SIGNED) | ||||||
|  | 		\$__soft_mul #( | ||||||
|  | 			.A_SIGNED(A_SIGNED), | ||||||
|  | 			.B_SIGNED(1), | ||||||
|  | 			.A_WIDTH(A_WIDTH), | ||||||
|  | 			.B_WIDTH(B_WIDTH+1), | ||||||
|  | 			.Y_WIDTH(Y_WIDTH) | ||||||
|  | 		) _TECHMAP_REPLACE_ ( | ||||||
|  | 			.A(A), | ||||||
|  | 			.B({1'b0,B}), | ||||||
|  | 			.Y(Y) | ||||||
|  | 		); | ||||||
|  | 	else if (!A_SIGNED && B_SIGNED) | ||||||
|  | 		\$__soft_mul #( | ||||||
|  | 			.A_SIGNED(1), | ||||||
|  | 			.B_SIGNED(B_SIGNED), | ||||||
|  | 			.A_WIDTH(A_WIDTH+1), | ||||||
|  | 			.B_WIDTH(B_WIDTH), | ||||||
|  | 			.Y_WIDTH(Y_WIDTH) | ||||||
|  | 		) _TECHMAP_REPLACE_ ( | ||||||
|  | 			.A({1'b0,A}), | ||||||
|  | 			.B(B), | ||||||
|  | 			.Y(Y) | ||||||
|  | 		); | ||||||
|  | 	else | ||||||
|  | 		\$__soft_mul #( | ||||||
|  | 			.A_SIGNED(A_SIGNED), | ||||||
|  | 			.B_SIGNED(B_SIGNED), | ||||||
|  | 			.A_WIDTH(A_WIDTH), | ||||||
|  | 			.B_WIDTH(B_WIDTH), | ||||||
|  | 			.Y_WIDTH(Y_WIDTH) | ||||||
|  | 		) _TECHMAP_REPLACE_ ( | ||||||
|  | 			.A(A), | ||||||
|  | 			.B(B), | ||||||
|  | 			.Y(Y) | ||||||
|  | 		); | ||||||
|  | 	endgenerate | ||||||
|  | endmodule | ||||||
							
								
								
									
										1
									
								
								techlibs/ecp5/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								techlibs/ecp5/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -6,4 +6,5 @@ bram_conn_2.vh | ||||||
| bram_conn_4.vh | bram_conn_4.vh | ||||||
| bram_conn_9.vh | bram_conn_9.vh | ||||||
| bram_conn_18.vh | bram_conn_18.vh | ||||||
|  | bram_conn_36.vh | ||||||
| brams_connect.mk | brams_connect.mk | ||||||
|  |  | ||||||
|  | @ -13,22 +13,26 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v)) | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) | ||||||
|  | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v)) | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_map.v)) | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_unmap.v)) | ||||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut)) | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_model.v)) | ||||||
|  | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g.box)) | ||||||
|  | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g.lut)) | ||||||
|  | $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g_nowide.lut)) | ||||||
| 
 | 
 | ||||||
| EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk | EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk | ||||||
| .SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk | .SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk | ||||||
| 
 | 
 | ||||||
| techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py | techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py | ||||||
| 	$(Q) mkdir -p techlibs/ecp5 | 	$(Q) mkdir -p techlibs/ecp5 | ||||||
| 	$(P) python3 $< | 	$(P) $(PYTHON_EXECUTABLE) $< | ||||||
| 	$(Q) touch $@ | 	$(Q) touch $@ | ||||||
| 
 | 
 | ||||||
| techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py | techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py | ||||||
| 	$(Q) mkdir -p techlibs/ecp5 | 	$(Q) mkdir -p techlibs/ecp5 | ||||||
| 	$(P) python3 $< | 	$(P) $(PYTHON_EXECUTABLE) $< | ||||||
| 	$(Q) touch $@ | 	$(Q) touch $@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -40,6 +44,7 @@ techlibs/ecp5/bram_conn_2.vh: techlibs/ecp5/brams_connect.mk | ||||||
| techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk | techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk | ||||||
| techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk | techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk | ||||||
| techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk | techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk | ||||||
|  | techlibs/ecp5/bram_conn_36.vh: techlibs/ecp5/brams_connect.mk | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh)) | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh)) | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh)) | ||||||
|  | @ -49,3 +54,4 @@ $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_2.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh)) | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh)) | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh)) | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh)) | ||||||
|  | $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_36.vh)) | ||||||
|  |  | ||||||
|  | @ -15,16 +15,16 @@ CCU2C   1      1   9      3 | ||||||
| 630  379  630  379  526   275  392  141  273 | 630  379  630  379  526   275  392  141  273 | ||||||
| 516  516  516  516  412   412  278  278  43 | 516  516  516  516  412   412  278  278  43 | ||||||
| 
 | 
 | ||||||
| # Box 2 : TRELLIS_DPR16X4 (16x4 dist ram) | # Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram) | ||||||
| # Outputs: DO0, DO1, DO2, DO3 | # Outputs: DO0, DO1, DO2, DO3 | ||||||
| # name            ID  w/b   ins   outs | # name               ID  w/b   ins   outs | ||||||
| TRELLIS_DPR16X4   2     0   14    4 | $__ABC9_DPR16X4_COMB  2     0   8    4 | ||||||
| 
 | 
 | ||||||
| #DI0   DI1   DI2   DI3   RAD0   RAD1   RAD2   RAD3   WAD0    WAD1   WAD2   WAD3  WCK   WRE | #A0   A1   A2   A3   RAD0   RAD1   RAD2   RAD3 | ||||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | 0     0    0    0    141    379    275    379 | ||||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | 0     0    0    0    141    379    275    379 | ||||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | 0     0    0    0    141    379    275    379 | ||||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | 0     0    0    0    141    379    275    379 | ||||||
| 
 | 
 | ||||||
| # Box 3 : PFUMX (MUX2) | # Box 3 : PFUMX (MUX2) | ||||||
| # Outputs: Z | # Outputs: Z | ||||||
							
								
								
									
										24
									
								
								techlibs/ecp5/abc9_map.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								techlibs/ecp5/abc9_map.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | // --------------------------------------- | ||||||
|  | 
 | ||||||
|  | module TRELLIS_DPR16X4 ( | ||||||
|  | 	input  [3:0] DI, | ||||||
|  | 	input  [3:0] WAD, | ||||||
|  | 	input        WRE, | ||||||
|  | 	input        WCK, | ||||||
|  | 	input  [3:0] RAD, | ||||||
|  | 	output [3:0] DO | ||||||
|  | ); | ||||||
|  | 	parameter WCKMUX = "WCK"; | ||||||
|  | 	parameter WREMUX = "WRE"; | ||||||
|  | 	parameter [63:0] INITVAL = 64'h0000000000000000; | ||||||
|  |     wire [3:0] \$DO ; | ||||||
|  | 
 | ||||||
|  |     TRELLIS_DPR16X4 #( | ||||||
|  |       .WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL) | ||||||
|  |     ) _TECHMAP_REPLACE_ ( | ||||||
|  |       .DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK), | ||||||
|  |       .RAD(RAD), .DO(\$DO ) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     \$__ABC9_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO)); | ||||||
|  | endmodule | ||||||
							
								
								
									
										5
									
								
								techlibs/ecp5/abc9_model.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								techlibs/ecp5/abc9_model.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | // --------------------------------------- | ||||||
|  | 
 | ||||||
|  | (* abc9_box_id=2 *) | ||||||
|  | module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); | ||||||
|  | endmodule | ||||||
							
								
								
									
										5
									
								
								techlibs/ecp5/abc9_unmap.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								techlibs/ecp5/abc9_unmap.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | // --------------------------------------- | ||||||
|  | 
 | ||||||
|  | module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); | ||||||
|  |     assign Y = A; | ||||||
|  | endmodule | ||||||
|  | @ -1,3 +1,18 @@ | ||||||
|  | bram $__ECP5_PDPW16KD | ||||||
|  |   init 1 | ||||||
|  | 
 | ||||||
|  |   abits 9 | ||||||
|  |   dbits 36 | ||||||
|  | 
 | ||||||
|  |   groups 2 | ||||||
|  |   ports 1 1 | ||||||
|  |   wrmode 1 0 | ||||||
|  |   enable 4 1 | ||||||
|  |   transp 0 0 | ||||||
|  |   clocks 2 3 | ||||||
|  |   clkpol 2 3 | ||||||
|  | endbram | ||||||
|  | 
 | ||||||
| bram $__ECP5_DP16KD | bram $__ECP5_DP16KD | ||||||
|   init 1 |   init 1 | ||||||
| 
 | 
 | ||||||
|  | @ -22,8 +37,16 @@ bram $__ECP5_DP16KD | ||||||
|   clkpol 2 3 |   clkpol 2 3 | ||||||
| endbram | endbram | ||||||
| 
 | 
 | ||||||
|  | match $__ECP5_PDPW16KD | ||||||
|  |   min bits 2048 | ||||||
|  |   min efficiency 5 | ||||||
|  |   shuffle_enable A | ||||||
|  |   make_transp | ||||||
|  |   or_next_if_better | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
| match $__ECP5_DP16KD | match $__ECP5_DP16KD | ||||||
|   min bits 2048 |   min bits 2048 | ||||||
|   min efficiency 5 |   min efficiency 5 | ||||||
|   shuffle_enable B |   shuffle_enable A | ||||||
| endmatch | endmatch | ||||||
|  |  | ||||||
|  | @ -10,6 +10,18 @@ def write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits): | ||||||
|     print("    %s," % ", ".join(dia_conn), file=f) |     print("    %s," % ", ".join(dia_conn), file=f) | ||||||
|     print("    %s," % ", ".join(dob_conn), file=f) |     print("    %s," % ", ".join(dob_conn), file=f) | ||||||
| 
 | 
 | ||||||
|  | def write_bus_ports_pdp(f, adw_bits, adr_bits, di_bits, do_bits, be_bits): | ||||||
|  |     adw_conn = [".ADW%d(%s)" % (i, adw_bits[i]) for i in range(len(adw_bits))] | ||||||
|  |     adr_conn = [".ADR%d(%s)" % (i, adr_bits[i]) for i in range(len(adr_bits))] | ||||||
|  |     di_conn = [".DI%d(%s)" % (i, di_bits[i]) for i in range(len(di_bits))] | ||||||
|  |     do_conn = [".DO%d(%s)" % (i, do_bits[i]) for i in range(len(do_bits))] | ||||||
|  |     be_conn = [".BE%d(%s)" % (i, be_bits[i]) for i in range(len(be_bits))] | ||||||
|  |     print("    %s," % ", ".join(adw_conn), file=f) | ||||||
|  |     print("    %s," % ", ".join(adr_conn), file=f) | ||||||
|  |     print("    %s," % ", ".join(di_conn), file=f) | ||||||
|  |     print("    %s," % ", ".join(do_conn), file=f) | ||||||
|  |     print("    %s," % ", ".join(be_conn), file=f) | ||||||
|  | 
 | ||||||
| with open("techlibs/ecp5/bram_conn_1.vh", "w") as f: | with open("techlibs/ecp5/bram_conn_1.vh", "w") as f: | ||||||
|     ada_bits = ["A1ADDR[%d]" % i for i in range(14)] |     ada_bits = ["A1ADDR[%d]" % i for i in range(14)] | ||||||
|     adb_bits = ["B1ADDR[%d]" % i for i in range(14)] |     adb_bits = ["B1ADDR[%d]" % i for i in range(14)] | ||||||
|  | @ -44,3 +56,11 @@ with open("techlibs/ecp5/bram_conn_18.vh", "w") as f: | ||||||
|     dia_bits = ["A1DATA[%d]" % i for i in range(18)] |     dia_bits = ["A1DATA[%d]" % i for i in range(18)] | ||||||
|     dob_bits = ["B1DATA[%d]" % i for i in range(18)] |     dob_bits = ["B1DATA[%d]" % i for i in range(18)] | ||||||
|     write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) |     write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) | ||||||
|  | 
 | ||||||
|  | with open("techlibs/ecp5/bram_conn_36.vh", "w") as f: | ||||||
|  |     adw_bits = ["A1ADDR[%d]" % i for i in range(9)] | ||||||
|  |     adr_bits = ["1'b0", "1'b0", "1'b0", "1'b0", "1'b0"] + ["B1ADDR[%d]" % i for i in range(9)] | ||||||
|  |     di_bits = ["A1DATA[%d]" % i for i in range(36)] | ||||||
|  |     do_bits = ["B1DATA[%d]" % (i + 18) for i in range(18)] + ["B1DATA[%d]" % i for i in range(18)] | ||||||
|  |     be_bits = ["A1EN[%d]" % i for i in range(4)] | ||||||
|  |     write_bus_ports_pdp(f, adw_bits, adr_bits, di_bits, do_bits, be_bits) | ||||||
|  |  | ||||||
|  | @ -113,3 +113,45 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); | ||||||
| 		wire TECHMAP_FAIL = 1'b1; | 		wire TECHMAP_FAIL = 1'b1; | ||||||
| 	end endgenerate | 	end endgenerate | ||||||
| endmodule | endmodule | ||||||
|  | 
 | ||||||
|  | module \$__ECP5_PDPW16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); | ||||||
|  | 	parameter CFG_ABITS = 9; | ||||||
|  | 	parameter CFG_DBITS = 36; | ||||||
|  | 	parameter CFG_ENABLE_A = 4; | ||||||
|  | 
 | ||||||
|  | 	parameter CLKPOL2 = 1; | ||||||
|  | 	parameter CLKPOL3 = 1; | ||||||
|  | 	parameter [18431:0] INIT = 18432'bx; | ||||||
|  | 
 | ||||||
|  | 	input CLK2; | ||||||
|  | 	input CLK3; | ||||||
|  | 
 | ||||||
|  | 	input [CFG_ABITS-1:0] A1ADDR; | ||||||
|  | 	input [CFG_DBITS-1:0] A1DATA; | ||||||
|  | 	input [CFG_ENABLE_A-1:0] A1EN; | ||||||
|  | 
 | ||||||
|  | 	input [CFG_ABITS-1:0] B1ADDR; | ||||||
|  | 	output [CFG_DBITS-1:0] B1DATA; | ||||||
|  | 	input B1EN; | ||||||
|  | 
 | ||||||
|  | 	localparam CLKWMUX = CLKPOL2 ? "CLKA" : "INV"; | ||||||
|  | 	localparam CLKRMUX = CLKPOL3 ? "CLKB" : "INV"; | ||||||
|  | 
 | ||||||
|  | 	localparam WRITEMODE_A = TRANSP2 ? "WRITETHROUGH" : "READBEFOREWRITE"; | ||||||
|  | 
 | ||||||
|  | 	PDPW16KD #( | ||||||
|  | 		`include "bram_init_9_18_36.vh" | ||||||
|  | 		.DATA_WIDTH_W(36), | ||||||
|  | 		.DATA_WIDTH_R(36), | ||||||
|  | 		.CLKWMUX(CLKWMUX), | ||||||
|  | 		.CLKRMUX(CLKRMUX), | ||||||
|  | 		.GSR("AUTO") | ||||||
|  | 	) _TECHMAP_REPLACE_ ( | ||||||
|  | 		`include "bram_conn_36.vh" | ||||||
|  | 		.CLKW(CLK2), .CLKR(CLK3), | ||||||
|  | 		.CEW(1'b1), | ||||||
|  | 		.CER(B1EN), .OCER(1'b1), | ||||||
|  | 		.RST(1'b0) | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  |  | ||||||
|  | @ -333,6 +333,13 @@ module ECLKSYNCB( | ||||||
| ); | ); | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  | (* blackbox *) | ||||||
|  | module ECLKBRIDGECS( | ||||||
|  | 	input CLK0, CLK1, SEL, | ||||||
|  | 	output ECSOUT | ||||||
|  | ); | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
| (* blackbox *) | (* blackbox *) | ||||||
| module DCCA( | module DCCA( | ||||||
| 	input CLKI, CE, | 	input CLKI, CE, | ||||||
|  | @ -684,3 +691,97 @@ module SGSR ( | ||||||
| 	input GSR, CLK | 	input GSR, CLK | ||||||
| ); | ); | ||||||
| endmodule | endmodule | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | (* blackbox *) | ||||||
|  | module PDPW16KD ( | ||||||
|  | 	input DI35, DI34, DI33, DI32, DI31, DI30, DI29, DI28, DI27, DI26, DI25, DI24, DI23, DI22, DI21, DI20, DI19, DI18, | ||||||
|  | 	input DI17, DI16, DI15, DI14, DI13, DI12, DI11, DI10, DI9, DI8, DI7, DI6, DI5, DI4, DI3, DI2, DI1, DI0, | ||||||
|  | 	input ADW8, ADW7, ADW6, ADW5, ADW4, ADW3, ADW2, ADW1, ADW0, | ||||||
|  | 	input BE3,  BE2,  BE1, BE0, CEW, CLKW, CSW2, CSW1, CSW0, | ||||||
|  | 	input ADR13, ADR12, ADR11, ADR10, ADR9, ADR8, ADR7, ADR6, ADR5, ADR4, ADR3, ADR2, ADR1, ADR0, | ||||||
|  | 	input CER, OCER, CLKR, CSR2, CSR1, CSR0, RST, | ||||||
|  | 	output DO35, DO34, DO33, DO32, DO31, DO30, DO29, DO28, DO27, DO26, DO25, DO24, DO23, DO22, DO21, DO20, DO19, DO18, | ||||||
|  | 	output DO17, DO16, DO15, DO14, DO13, DO12, DO11, DO10, DO9, DO8, DO7, DO6, DO5, DO4, DO3, DO2, DO1, DO0 | ||||||
|  | ); | ||||||
|  | 	parameter DATA_WIDTH_W = 36; | ||||||
|  | 	parameter DATA_WIDTH_R = 36; | ||||||
|  | 	parameter GSR = "ENABLED"; | ||||||
|  | 
 | ||||||
|  | 	parameter REGMODE = "NOREG"; | ||||||
|  | 
 | ||||||
|  | 	parameter RESETMODE = "SYNC"; | ||||||
|  | 	parameter ASYNC_RESET_RELEASE = "SYNC"; | ||||||
|  | 
 | ||||||
|  | 	parameter CSDECODE_W = "0b000"; | ||||||
|  | 	parameter CSDECODE_R = "0b000"; | ||||||
|  | 
 | ||||||
|  | 	parameter INITVAL_00 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_01 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_02 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_03 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_04 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_05 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_06 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_07 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_08 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_09 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_0F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_10 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_11 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_12 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_13 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_14 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_15 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_16 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_17 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_18 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_19 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_1F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_20 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_21 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_22 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_23 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_24 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_25 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_26 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_27 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_28 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_29 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_2F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_30 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_31 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_32 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_33 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_34 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_35 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_36 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_37 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_38 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_39 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; | ||||||
|  | 	parameter INIT_DATA = "STATIC"; | ||||||
|  | 	parameter CLKWMUX = "CLKW"; | ||||||
|  | 	parameter CLKRMUX = "CLKR"; | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  |  | ||||||
|  | @ -23,15 +23,15 @@ module FD1S3JX(input PD, D,     CK, output Q); parameter GSR = "ENABLED"; TRELLI | ||||||
| // module FL1S3AY(); endmodule | // module FL1S3AY(); endmodule | ||||||
| 
 | 
 | ||||||
| // Diamond I/O registers | // Diamond I/O registers | ||||||
| module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| 
 | 
 | ||||||
| module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))       _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule | ||||||
| 
 | 
 | ||||||
| // TODO: Diamond I/O latches | // TODO: Diamond I/O latches | ||||||
| // module IFS1S1B(input PD, D, SCLK, output Q); endmodule | // module IFS1S1B(input PD, D, SCLK, output Q); endmodule | ||||||
|  |  | ||||||
|  | @ -9,19 +9,19 @@ module LUT4(input A, B, C, D, output Z); | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| (* abc_box_id=4, lib_whitebox *) | (* abc9_box_id=4, lib_whitebox *) | ||||||
| module L6MUX21 (input D0, D1, SD, output Z); | module L6MUX21 (input D0, D1, SD, output Z); | ||||||
| 	assign Z = SD ? D1 : D0; | 	assign Z = SD ? D1 : D0; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| (* abc_box_id=1, lib_whitebox *) | (* abc9_box_id=1, lib_whitebox *) | ||||||
| module CCU2C( | module CCU2C( | ||||||
| 	(* abc_carry *) | 	(* abc9_carry *) | ||||||
| 	input  CIN, | 	input  CIN, | ||||||
| 	input  A0, B0, C0, D0, A1, B1, C1, D1, | 	input  A0, B0, C0, D0, A1, B1, C1, D1, | ||||||
| 	output S0, S1, | 	output S0, S1, | ||||||
| 	(* abc_carry *) | 	(* abc9_carry *) | ||||||
| 	output COUT | 	output COUT | ||||||
| ); | ); | ||||||
| 	parameter [15:0] INIT0 = 16'h0000; | 	parameter [15:0] INIT0 = 16'h0000; | ||||||
|  | @ -103,22 +103,19 @@ module TRELLIS_RAM16X2 ( | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| (* abc_box_id=3, lib_whitebox *) | (* abc9_box_id=3, lib_whitebox *) | ||||||
| module PFUMX (input ALUT, BLUT, C0, output Z); | module PFUMX (input ALUT, BLUT, C0, output Z); | ||||||
| 	assign Z = C0 ? ALUT : BLUT; | 	assign Z = C0 ? ALUT : BLUT; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| //(* abc_box_id=2 *) |  | ||||||
| module TRELLIS_DPR16X4 ( | module TRELLIS_DPR16X4 ( | ||||||
| 	(* abc_scc_break *) |  | ||||||
| 	input  [3:0] DI, | 	input  [3:0] DI, | ||||||
| 	(* abc_scc_break *) |  | ||||||
| 	input  [3:0] WAD, | 	input  [3:0] WAD, | ||||||
| 	(* abc_scc_break *) |  | ||||||
| 	input        WRE, | 	input        WRE, | ||||||
| 	input        WCK, | 	input        WCK, | ||||||
| 	input  [3:0] RAD, | 	input  [3:0] RAD, | ||||||
|  | 	/* (* abc9_arrival=<TODO> *) */ | ||||||
| 	output [3:0] DO | 	output [3:0] DO | ||||||
| ); | ); | ||||||
| 	parameter WCKMUX = "WCK"; | 	parameter WCKMUX = "WCK"; | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								techlibs/ecp5/dsp_map.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								techlibs/ecp5/dsp_map.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); | ||||||
|  | 
 | ||||||
|  | 	parameter A_WIDTH = 18; | ||||||
|  | 	parameter B_WIDTH = 18; | ||||||
|  | 	parameter Y_WIDTH = 36; | ||||||
|  | 	parameter A_SIGNED = 0; | ||||||
|  | 	parameter B_SIGNED = 0; | ||||||
|  | 
 | ||||||
|  | 	MULT18X18D _TECHMAP_REPLACE_ ( | ||||||
|  | 		.A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]), | ||||||
|  | 		.B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]), | ||||||
|  | 		.C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0), | ||||||
|  | 		.SIGNEDA(A_SIGNED), .SIGNEDB(B_SIGNED), .SOURCEA(1'b0), .SOURCEB(1'b0), | ||||||
|  | 
 | ||||||
|  | 		.P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35]) | ||||||
|  | 	); | ||||||
|  | endmodule | ||||||
|  | @ -89,6 +89,9 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 		log("        generate an output netlist (and BLIF file) suitable for VPR\n"); | 		log("        generate an output netlist (and BLIF file) suitable for VPR\n"); | ||||||
| 		log("        (this feature is experimental and incomplete)\n"); | 		log("        (this feature is experimental and incomplete)\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -nodsp\n"); | ||||||
|  | 		log("        do not map multipliers to MULT18X18D\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("The following commands are executed by this synthesis command:\n"); | 		log("The following commands are executed by this synthesis command:\n"); | ||||||
| 		help_script(); | 		help_script(); | ||||||
|  | @ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	string top_opt, blif_file, edif_file, json_file; | 	string top_opt, blif_file, edif_file, json_file; | ||||||
| 	bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr; | 	bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr; | ||||||
| 
 | 
 | ||||||
| 	void clear_flags() YS_OVERRIDE | 	void clear_flags() YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
|  | @ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 		abc2 = false; | 		abc2 = false; | ||||||
| 		vpr = false; | 		vpr = false; | ||||||
| 		abc9 = false; | 		abc9 = false; | ||||||
|  | 		nodsp = false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | @ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 				abc9 = true; | 				abc9 = true; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-nodsp") { | ||||||
|  | 				nodsp = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
|  | @ -218,17 +226,34 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (flatten && check_label("flatten", "(unless -noflatten)")) |  | ||||||
| 		{ |  | ||||||
| 			run("proc"); |  | ||||||
| 			run("flatten"); |  | ||||||
| 			run("tribuf -logic"); |  | ||||||
| 			run("deminout"); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (check_label("coarse")) | 		if (check_label("coarse")) | ||||||
| 		{ | 		{ | ||||||
| 			run("synth -run coarse"); | 			run("proc"); | ||||||
|  | 			if (flatten || help_mode) | ||||||
|  | 				run("flatten"); | ||||||
|  | 			run("tribuf -logic"); | ||||||
|  | 			run("deminout"); | ||||||
|  | 			run("opt_expr"); | ||||||
|  | 			run("opt_clean"); | ||||||
|  | 			run("check"); | ||||||
|  | 			run("opt"); | ||||||
|  | 			run("wreduce"); | ||||||
|  | 			run("peepopt"); | ||||||
|  | 			run("opt_clean"); | ||||||
|  | 			run("share"); | ||||||
|  | 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | ||||||
|  | 			run("opt_expr"); | ||||||
|  | 			run("opt_clean"); | ||||||
|  | 			if (!nodsp) { | ||||||
|  | 				run("techmap -map +/mul2dsp.v -map +/ecp5/dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18  -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2  -D DSP_NAME=$__MUL18X18", "(unless -nodsp)"); | ||||||
|  | 				run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)"); | ||||||
|  | 			} | ||||||
|  | 			run("alumacc"); | ||||||
|  | 			run("opt"); | ||||||
|  | 			run("fsm"); | ||||||
|  | 			run("opt -fast"); | ||||||
|  | 			run("memory -nomap"); | ||||||
|  | 			run("opt_clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (!nobram && check_label("map_bram", "(skip if -nobram)")) | 		if (!nobram && check_label("map_bram", "(skip if -nobram)")) | ||||||
|  | @ -272,6 +297,7 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 			run("simplemap"); | 			run("simplemap"); | ||||||
| 			run("ecp5_ffinit"); | 			run("ecp5_ffinit"); | ||||||
| 			run("ecp5_gsr"); | 			run("ecp5_gsr"); | ||||||
|  | 			run("attrmvcp -copy -attr syn_useioff"); | ||||||
| 			run("opt_clean"); | 			run("opt_clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -280,12 +306,18 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 			if (abc2 || help_mode) { | 			if (abc2 || help_mode) { | ||||||
| 				run("abc", "      (only if -abc2)"); | 				run("abc", "      (only if -abc2)"); | ||||||
| 			} | 			} | ||||||
| 			run("techmap -map +/ecp5/latches_map.v"); | 			std::string techmap_args = "-map +/ecp5/latches_map.v"; | ||||||
|  | 			if (abc9) | ||||||
|  | 				techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1"; | ||||||
|  | 			run("techmap " + techmap_args); | ||||||
|  | 
 | ||||||
| 			if (abc9) { | 			if (abc9) { | ||||||
|  | 				run("read_verilog -icells -lib +/ecp5/abc9_model.v"); | ||||||
| 				if (nowidelut) | 				if (nowidelut) | ||||||
| 					run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200"); | 					run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs"); | ||||||
| 				else | 				else | ||||||
| 					run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200"); | 					run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs"); | ||||||
|  | 				run("techmap -map +/ecp5/abc9_unmap.v"); | ||||||
| 			} else { | 			} else { | ||||||
| 				if (nowidelut) | 				if (nowidelut) | ||||||
| 					run("abc -lut 4 -dress"); | 					run("abc -lut 4 -dress"); | ||||||
|  |  | ||||||
|  | @ -17,6 +17,18 @@ module  \$_DFF_NP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE | ||||||
| module  \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule | module  \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule | ||||||
| module  \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule | module  \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule | ||||||
| 
 | 
 | ||||||
|  | module \$_DLATCH_N_ (E, D, Q); | ||||||
|  |   wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; | ||||||
|  |   input E, D; | ||||||
|  |   output Q = !E ? D : Q; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module \$_DLATCH_P_ (E, D, Q); | ||||||
|  |   wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; | ||||||
|  |   input E, D; | ||||||
|  |   output Q = E ? D : Q; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
| `ifndef NO_LUT | `ifndef NO_LUT | ||||||
| module \$lut (A, Y); | module \$lut (A, Y); | ||||||
|   parameter WIDTH = 0; |   parameter WIDTH = 0; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,12 @@ module EFX_LUT4( | ||||||
|    input I2, |    input I2, | ||||||
|    input I3 |    input I3 | ||||||
| ); | ); | ||||||
|    parameter LUTMASK  = 16'h0000; | 	parameter LUTMASK = 16'h0000; | ||||||
|  | 
 | ||||||
|  | 	wire [7:0] s3 = I3 ? LUTMASK[15:8] : LUTMASK[7:0]; | ||||||
|  | 	wire [3:0] s2 = I2 ?      s3[ 7:4] :      s3[3:0]; | ||||||
|  | 	wire [1:0] s1 = I1 ?      s2[ 3:2] :      s2[1:0]; | ||||||
|  | 	assign O = I0 ? s1[1] : s1[0];	    | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module EFX_ADD( | module EFX_ADD( | ||||||
|  | @ -17,10 +22,18 @@ module EFX_ADD( | ||||||
| ); | ); | ||||||
|    parameter I0_POLARITY   = 1; |    parameter I0_POLARITY   = 1; | ||||||
|    parameter I1_POLARITY   = 1; |    parameter I1_POLARITY   = 1; | ||||||
|  | 
 | ||||||
|  |    wire i0; | ||||||
|  |    wire i1; | ||||||
|  | 
 | ||||||
|  |    assign i0 = I0_POLARITY ? I0 : ~I0; | ||||||
|  |    assign i1 = I1_POLARITY ? I1 : ~I1; | ||||||
|  | 
 | ||||||
|  |    assign {CO, O} = i0 + i1 + CI; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module EFX_FF( | module EFX_FF( | ||||||
|    output Q, |    output reg Q, | ||||||
|    input D, |    input D, | ||||||
|    input CE, |    input CE, | ||||||
|    input CLK, |    input CLK, | ||||||
|  | @ -33,6 +46,55 @@ module EFX_FF( | ||||||
|    parameter SR_VALUE = 0; |    parameter SR_VALUE = 0; | ||||||
|    parameter SR_SYNC_PRIORITY = 0; |    parameter SR_SYNC_PRIORITY = 0; | ||||||
|    parameter D_POLARITY = 1; |    parameter D_POLARITY = 1; | ||||||
|  | 
 | ||||||
|  |    wire clk; | ||||||
|  |    wire ce; | ||||||
|  |    wire sr; | ||||||
|  |    wire d; | ||||||
|  |    wire prio; | ||||||
|  |    wire sync; | ||||||
|  |    wire async; | ||||||
|  | 
 | ||||||
|  |    assign clk = CLK_POLARITY ? CLK : ~CLK; | ||||||
|  |    assign ce = CE_POLARITY ? CE : ~CE; | ||||||
|  |    assign sr = SR_POLARITY ? SR : ~SR; | ||||||
|  |    assign d = D_POLARITY ? D : ~D; | ||||||
|  | 
 | ||||||
|  | 	initial Q = 1'b0; | ||||||
|  | 
 | ||||||
|  |    generate | ||||||
|  |    	if (SR_SYNC == 1)  | ||||||
|  |       begin | ||||||
|  |          if (SR_SYNC_PRIORITY == 1)  | ||||||
|  |          begin | ||||||
|  |             always @(posedge clk) | ||||||
|  |                if (sr) | ||||||
|  |                   Q <= SR_VALUE; | ||||||
|  |                else if (ce) | ||||||
|  |                   Q <= d; | ||||||
|  |          end | ||||||
|  |          else | ||||||
|  |          begin | ||||||
|  |             always @(posedge clk) | ||||||
|  |                if (ce) | ||||||
|  |                begin | ||||||
|  |                   if (sr) | ||||||
|  |                      Q <= SR_VALUE; | ||||||
|  |                   else | ||||||
|  |                      Q <= d; | ||||||
|  |                end | ||||||
|  |          end | ||||||
|  |       end | ||||||
|  |       else | ||||||
|  |       begin | ||||||
|  |          always @(posedge clk or posedge sr) | ||||||
|  |             if (sr) | ||||||
|  |                Q <= SR_VALUE; | ||||||
|  |             else if (ce) | ||||||
|  |                Q <= d; | ||||||
|  |           | ||||||
|  |       end | ||||||
|  |    endgenerate | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module EFX_GBUFCE( | module EFX_GBUFCE( | ||||||
|  | @ -41,6 +103,12 @@ module EFX_GBUFCE( | ||||||
|    output O |    output O | ||||||
| ); | ); | ||||||
|    parameter CE_POLARITY = 1'b1; |    parameter CE_POLARITY = 1'b1; | ||||||
|  | 
 | ||||||
|  |    wire ce; | ||||||
|  |    assign ce = CE_POLARITY ? CE : ~CE; | ||||||
|  |     | ||||||
|  |    assign O = I & ce; | ||||||
|  |     | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module EFX_RAM_5K( | module EFX_RAM_5K( | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ EXTRA_OBJS += techlibs/ice40/brams_init.mk | ||||||
| 
 | 
 | ||||||
| techlibs/ice40/brams_init.mk: techlibs/ice40/brams_init.py | techlibs/ice40/brams_init.mk: techlibs/ice40/brams_init.py | ||||||
| 	$(Q) mkdir -p techlibs/ice40 | 	$(Q) mkdir -p techlibs/ice40 | ||||||
| 	$(P) python3 $< | 	$(P) $(PYTHON_EXECUTABLE) $< | ||||||
| 	$(Q) touch techlibs/ice40/brams_init.mk | 	$(Q) touch techlibs/ice40/brams_init.mk | ||||||
| 
 | 
 | ||||||
| techlibs/ice40/brams_init1.vh: techlibs/ice40/brams_init.mk | techlibs/ice40/brams_init1.vh: techlibs/ice40/brams_init.mk | ||||||
|  | @ -27,12 +27,14 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_model.v)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_hx.box)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.lut)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_hx.lut)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.box)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_lp.box)) | ||||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.lut)) | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_lp.lut)) | ||||||
|  | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_u.box)) | ||||||
|  | $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_u.lut)) | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh)) | $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh)) | $(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh)) | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								techlibs/ice40/abc9_model.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								techlibs/ice40/abc9_model.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | (* abc9_box_id = 1, lib_whitebox *) | ||||||
|  | module \$__ICE40_CARRY_WRAPPER ( | ||||||
|  | 	(* abc9_carry *) | ||||||
|  | 	output CO, | ||||||
|  | 	output O, | ||||||
|  | 	input A, B, | ||||||
|  | 	(* abc9_carry *) | ||||||
|  | 	input CI, | ||||||
|  | 	input I0, I3 | ||||||
|  | ); | ||||||
|  | 	parameter LUT = 0; | ||||||
|  | 	SB_CARRY carry ( | ||||||
|  | 		.I0(A), | ||||||
|  | 		.I1(B), | ||||||
|  | 		.CI(CI), | ||||||
|  | 		.CO(CO) | ||||||
|  | 	); | ||||||
|  | 	SB_LUT4 #( | ||||||
|  | 		.LUT_INIT(LUT) | ||||||
|  | 	) adder ( | ||||||
|  | 		.I0(I0), | ||||||
|  | 		.I1(A), | ||||||
|  | 		.I2(B), | ||||||
|  | 		.I3(I3), | ||||||
|  | 		.O(O) | ||||||
|  | 	); | ||||||
|  | endmodule | ||||||
|  | @ -2,6 +2,10 @@ | ||||||
| `define SB_DFF_REG reg Q = 0 | `define SB_DFF_REG reg Q = 0 | ||||||
| // `define SB_DFF_REG reg Q | // `define SB_DFF_REG reg Q | ||||||
| 
 | 
 | ||||||
|  | `define ABC9_ARRIVAL_HX(TIME) `ifdef ICE40_HX (* abc9_arrival=TIME *) `endif | ||||||
|  | `define ABC9_ARRIVAL_LP(TIME) `ifdef ICE40_LP (* abc9_arrival=TIME *) `endif | ||||||
|  | `define ABC9_ARRIVAL_U(TIME)  `ifdef ICE40_U (* abc9_arrival=TIME *) `endif | ||||||
|  | 
 | ||||||
| // SiliconBlue IO Cells | // SiliconBlue IO Cells | ||||||
| 
 | 
 | ||||||
| module SB_IO ( | module SB_IO ( | ||||||
|  | @ -141,48 +145,42 @@ module SB_CARRY (output CO, input I0, I1, CI); | ||||||
| 	assign CO = (I0 && I1) || ((I0 || I1) && CI); | 	assign CO = (I0 && I1) || ((I0 || I1) && CI); | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| (* abc_box_id = 1, lib_whitebox *) | // Max delay from: https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L90 | ||||||
| module \$__ICE40_CARRY_WRAPPER ( | //                 https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L90 | ||||||
| 	(* abc_carry *) | //                 https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L102 | ||||||
| 	output CO, |  | ||||||
| 	output O, |  | ||||||
| 	input A, B, |  | ||||||
| 	(* abc_carry *) |  | ||||||
| 	input CI, |  | ||||||
| 	input I0, I3 |  | ||||||
| ); |  | ||||||
| 	parameter LUT = 0; |  | ||||||
| 	SB_CARRY carry ( |  | ||||||
| 		.I0(A), |  | ||||||
| 		.I1(B), |  | ||||||
| 		.CI(CI), |  | ||||||
| 		.CO(CO) |  | ||||||
| 	); |  | ||||||
| 	SB_LUT4 #( |  | ||||||
| 		.LUT_INIT(LUT) |  | ||||||
| 	) adder ( |  | ||||||
| 		.I0(I0), |  | ||||||
| 		.I1(A), |  | ||||||
| 		.I2(B), |  | ||||||
| 		.I3(I3), |  | ||||||
| 		.O(O) |  | ||||||
| 	); |  | ||||||
| endmodule |  | ||||||
| 
 | 
 | ||||||
| // Positive Edge SiliconBlue FF Cells | // Positive Edge SiliconBlue FF Cells | ||||||
| 
 | 
 | ||||||
| module SB_DFF (output `SB_DFF_REG, input C, D); | module SB_DFF ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		Q <= D; | 		Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFE (output `SB_DFF_REG, input C, E, D); | module SB_DFFE ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		if (E) | 		if (E) | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFSR (output `SB_DFF_REG, input C, R, D); | module SB_DFFSR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, R, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -190,7 +188,13 @@ module SB_DFFSR (output `SB_DFF_REG, input C, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFR (output `SB_DFF_REG, input C, R, D); | module SB_DFFR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, R, D | ||||||
|  | ); | ||||||
| 	always @(posedge C, posedge R) | 	always @(posedge C, posedge R) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -198,7 +202,13 @@ module SB_DFFR (output `SB_DFF_REG, input C, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFSS (output `SB_DFF_REG, input C, S, D); | module SB_DFFSS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, S, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -206,7 +216,13 @@ module SB_DFFSS (output `SB_DFF_REG, input C, S, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFS (output `SB_DFF_REG, input C, S, D); | module SB_DFFS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, S, D | ||||||
|  | ); | ||||||
| 	always @(posedge C, posedge S) | 	always @(posedge C, posedge S) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -214,7 +230,13 @@ module SB_DFFS (output `SB_DFF_REG, input C, S, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); | module SB_DFFESR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, R, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		if (E) begin | 		if (E) begin | ||||||
| 			if (R) | 			if (R) | ||||||
|  | @ -224,7 +246,13 @@ module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); | ||||||
| 		end | 		end | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); | module SB_DFFER ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, R, D | ||||||
|  | ); | ||||||
| 	always @(posedge C, posedge R) | 	always @(posedge C, posedge R) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -232,7 +260,13 @@ module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); | module SB_DFFESS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, S, D | ||||||
|  | ); | ||||||
| 	always @(posedge C) | 	always @(posedge C) | ||||||
| 		if (E) begin | 		if (E) begin | ||||||
| 			if (S) | 			if (S) | ||||||
|  | @ -242,7 +276,13 @@ module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); | ||||||
| 		end | 		end | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFES (output `SB_DFF_REG, input C, E, S, D); | module SB_DFFES ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, S, D | ||||||
|  | ); | ||||||
| 	always @(posedge C, posedge S) | 	always @(posedge C, posedge S) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -252,18 +292,36 @@ endmodule | ||||||
| 
 | 
 | ||||||
| // Negative Edge SiliconBlue FF Cells | // Negative Edge SiliconBlue FF Cells | ||||||
| 
 | 
 | ||||||
| module SB_DFFN (output `SB_DFF_REG, input C, D); | module SB_DFFN ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		Q <= D; | 		Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNE (output `SB_DFF_REG, input C, E, D); | module SB_DFFNE ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		if (E) | 		if (E) | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); | module SB_DFFNSR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, R, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -271,7 +329,13 @@ module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNR (output `SB_DFF_REG, input C, R, D); | module SB_DFFNR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, R, D | ||||||
|  | ); | ||||||
| 	always @(negedge C, posedge R) | 	always @(negedge C, posedge R) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -279,7 +343,13 @@ module SB_DFFNR (output `SB_DFF_REG, input C, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); | module SB_DFFNSS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, S, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -287,7 +357,13 @@ module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNS (output `SB_DFF_REG, input C, S, D); | module SB_DFFNS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, S, D | ||||||
|  | ); | ||||||
| 	always @(negedge C, posedge S) | 	always @(negedge C, posedge S) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -295,7 +371,13 @@ module SB_DFFNS (output `SB_DFF_REG, input C, S, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); | module SB_DFFNESR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, R, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		if (E) begin | 		if (E) begin | ||||||
| 			if (R) | 			if (R) | ||||||
|  | @ -305,7 +387,13 @@ module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); | ||||||
| 		end | 		end | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); | module SB_DFFNER ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, R, D | ||||||
|  | ); | ||||||
| 	always @(negedge C, posedge R) | 	always @(negedge C, posedge R) | ||||||
| 		if (R) | 		if (R) | ||||||
| 			Q <= 0; | 			Q <= 0; | ||||||
|  | @ -313,7 +401,13 @@ module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); | ||||||
| 			Q <= D; | 			Q <= D; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); | module SB_DFFNESS ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, S, D | ||||||
|  | ); | ||||||
| 	always @(negedge C) | 	always @(negedge C) | ||||||
| 		if (E) begin | 		if (E) begin | ||||||
| 			if (S) | 			if (S) | ||||||
|  | @ -323,7 +417,13 @@ module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); | ||||||
| 		end | 		end | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_DFFNES (output `SB_DFF_REG, input C, E, S, D); | module SB_DFFNES ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output `SB_DFF_REG, | ||||||
|  | 	input C, E, S, D | ||||||
|  | ); | ||||||
| 	always @(negedge C, posedge S) | 	always @(negedge C, posedge S) | ||||||
| 		if (S) | 		if (S) | ||||||
| 			Q <= 1; | 			Q <= 1; | ||||||
|  | @ -334,6 +434,9 @@ endmodule | ||||||
| // SiliconBlue RAM Cells | // SiliconBlue RAM Cells | ||||||
| 
 | 
 | ||||||
| module SB_RAM40_4K ( | module SB_RAM40_4K ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_U(1179)  // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 | ||||||
| 	output [15:0] RDATA, | 	output [15:0] RDATA, | ||||||
| 	input         RCLK, RCLKE, RE, | 	input         RCLK, RCLKE, RE, | ||||||
| 	input  [10:0] RADDR, | 	input  [10:0] RADDR, | ||||||
|  | @ -502,6 +605,9 @@ module SB_RAM40_4K ( | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_RAM40_4KNR ( | module SB_RAM40_4KNR ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_U(1179)  // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 | ||||||
| 	output [15:0] RDATA, | 	output [15:0] RDATA, | ||||||
| 	input         RCLKN, RCLKE, RE, | 	input         RCLKN, RCLKE, RE, | ||||||
| 	input  [10:0] RADDR, | 	input  [10:0] RADDR, | ||||||
|  | @ -567,6 +673,9 @@ module SB_RAM40_4KNR ( | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_RAM40_4KNW ( | module SB_RAM40_4KNW ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_U(1179)  // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 | ||||||
| 	output [15:0] RDATA, | 	output [15:0] RDATA, | ||||||
| 	input         RCLK, RCLKE, RE, | 	input         RCLK, RCLKE, RE, | ||||||
| 	input  [10:0] RADDR, | 	input  [10:0] RADDR, | ||||||
|  | @ -632,6 +741,9 @@ module SB_RAM40_4KNW ( | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module SB_RAM40_4KNRNW ( | module SB_RAM40_4KNRNW ( | ||||||
|  | 	`ABC9_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401 | ||||||
|  | 	`ABC9_ARRIVAL_U(1179)  // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026 | ||||||
| 	output [15:0] RDATA, | 	output [15:0] RDATA, | ||||||
| 	input         RCLKN, RCLKE, RE, | 	input         RCLKN, RCLKE, RE, | ||||||
| 	input  [10:0] RADDR, | 	input  [10:0] RADDR, | ||||||
|  | @ -700,7 +812,12 @@ endmodule | ||||||
| 
 | 
 | ||||||
| module ICESTORM_LC ( | module ICESTORM_LC ( | ||||||
| 	input I0, I1, I2, I3, CIN, CLK, CEN, SR, | 	input I0, I1, I2, I3, CIN, CLK, CEN, SR, | ||||||
| 	output LO, O, COUT | 	output LO, | ||||||
|  | 	`ABC9_ARRIVAL_HX(540) | ||||||
|  | 	`ABC9_ARRIVAL_LP(796) | ||||||
|  | 	`ABC9_ARRIVAL_U(1391) | ||||||
|  | 	output O, | ||||||
|  | 	output COUT | ||||||
| ); | ); | ||||||
| 	parameter [15:0] LUT_INIT = 0; | 	parameter [15:0] LUT_INIT = 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								techlibs/ice40/dsp_map.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								techlibs/ice40/dsp_map.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | module \$__MUL16X16 (input [15:0] A, input [15:0] B, output [31:0] Y); | ||||||
|  | 	parameter A_SIGNED = 0; | ||||||
|  | 	parameter B_SIGNED = 0; | ||||||
|  | 	parameter A_WIDTH = 0; | ||||||
|  | 	parameter B_WIDTH = 0; | ||||||
|  | 	parameter Y_WIDTH = 0; | ||||||
|  | 
 | ||||||
|  | 	SB_MAC16 #( | ||||||
|  | 		.NEG_TRIGGER(1'b0), | ||||||
|  | 		.C_REG(1'b0), | ||||||
|  | 		.A_REG(1'b0), | ||||||
|  | 		.B_REG(1'b0), | ||||||
|  | 		.D_REG(1'b0), | ||||||
|  | 		.TOP_8x8_MULT_REG(1'b0), | ||||||
|  | 		.BOT_8x8_MULT_REG(1'b0), | ||||||
|  | 		.PIPELINE_16x16_MULT_REG1(1'b0), | ||||||
|  | 		.PIPELINE_16x16_MULT_REG2(1'b0), | ||||||
|  | 		.TOPOUTPUT_SELECT(2'b11), | ||||||
|  | 		.TOPADDSUB_LOWERINPUT(2'b0), | ||||||
|  | 		.TOPADDSUB_UPPERINPUT(1'b0), | ||||||
|  | 		.TOPADDSUB_CARRYSELECT(2'b0), | ||||||
|  | 		.BOTOUTPUT_SELECT(2'b11), | ||||||
|  | 		.BOTADDSUB_LOWERINPUT(2'b0), | ||||||
|  | 		.BOTADDSUB_UPPERINPUT(1'b0), | ||||||
|  | 		.BOTADDSUB_CARRYSELECT(2'b0), | ||||||
|  | 		.MODE_8x8(1'b0), | ||||||
|  | 		.A_SIGNED(A_SIGNED), | ||||||
|  | 		.B_SIGNED(B_SIGNED) | ||||||
|  | 	) _TECHMAP_REPLACE_ ( | ||||||
|  | 		.A(A), | ||||||
|  | 		.B(B), | ||||||
|  | 		.O(Y), | ||||||
|  | 	); | ||||||
|  | endmodule | ||||||
|  | @ -238,7 +238,14 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 	{ | 	{ | ||||||
| 		if (check_label("begin")) | 		if (check_label("begin")) | ||||||
| 		{ | 		{ | ||||||
| 			run("read_verilog -icells -lib +/ice40/cells_sim.v"); | 			std::string define; | ||||||
|  | 			if (device_opt == "lp") | ||||||
|  | 				define = "-D ICE40_LP"; | ||||||
|  | 			else if (device_opt == "u") | ||||||
|  | 				define = "-D ICE40_U"; | ||||||
|  | 			else | ||||||
|  | 				define = "-D ICE40_HX"; | ||||||
|  | 			run("read_verilog " + define + " -lib +/ice40/cells_sim.v"); | ||||||
| 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | ||||||
| 			run("proc"); | 			run("proc"); | ||||||
| 		} | 		} | ||||||
|  | @ -265,8 +272,18 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | ||||||
| 			run("opt_expr"); | 			run("opt_expr"); | ||||||
| 			run("opt_clean"); | 			run("opt_clean"); | ||||||
| 			if (help_mode || dsp) | 			if (help_mode || dsp) { | ||||||
| 				run("ice40_dsp", "(if -dsp)"); | 				run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 " | ||||||
|  | 						"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 " | ||||||
|  | 						"-D DSP_NAME=$__MUL16X16", "(if -dsp)"); | ||||||
|  | 				run("select a:mul2dsp", "              (if -dsp)"); | ||||||
|  | 				run("setattr -unset mul2dsp", "        (if -dsp)"); | ||||||
|  | 				run("opt_expr -fine", "                (if -dsp)"); | ||||||
|  | 				run("wreduce", "                       (if -dsp)"); | ||||||
|  | 				run("select -clear", "                 (if -dsp)"); | ||||||
|  | 				run("ice40_dsp", "                     (if -dsp)"); | ||||||
|  | 				run("chtype -set $mul t:$__soft_mul", "(if -dsp)"); | ||||||
|  | 			} | ||||||
| 			run("alumacc"); | 			run("alumacc"); | ||||||
| 			run("opt"); | 			run("opt"); | ||||||
| 			run("fsm"); | 			run("fsm"); | ||||||
|  | @ -332,6 +349,7 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 			} | 			} | ||||||
| 			if (!noabc) { | 			if (!noabc) { | ||||||
| 				if (abc == "abc9") { | 				if (abc == "abc9") { | ||||||
|  | 					run("read_verilog -icells -lib +/ice40/abc9_model.v"); | ||||||
| 					int wire_delay; | 					int wire_delay; | ||||||
| 					if (device_opt == "lp") | 					if (device_opt == "lp") | ||||||
| 						wire_delay = 400; | 						wire_delay = 400; | ||||||
|  | @ -339,7 +357,7 @@ struct SynthIce40Pass : public ScriptPass | ||||||
| 						wire_delay = 750; | 						wire_delay = 750; | ||||||
| 					else | 					else | ||||||
| 						wire_delay = 250; | 						wire_delay = 250; | ||||||
| 					run(abc + stringf(" -W %d -lut +/ice40/abc_%s.lut -box +/ice40/abc_%s.box", wire_delay, device_opt.c_str(), device_opt.c_str()), "(skip if -noabc)"); | 					run(abc + stringf(" -W %d -lut +/ice40/abc9_%s.lut -box +/ice40/abc9_%s.box", wire_delay, device_opt.c_str(), device_opt.c_str()), "(skip if -noabc)"); | ||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
| 					run(abc + " -dress -lut 4", "(skip if -noabc)"); | 					run(abc + " -dress -lut 4", "(skip if -noabc)"); | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ EXTRA_OBJS += techlibs/xilinx/brams_init.mk | ||||||
| 
 | 
 | ||||||
| techlibs/xilinx/brams_init.mk: techlibs/xilinx/brams_init.py | techlibs/xilinx/brams_init.mk: techlibs/xilinx/brams_init.py | ||||||
| 	$(Q) mkdir -p techlibs/xilinx | 	$(Q) mkdir -p techlibs/xilinx | ||||||
| 	$(P) python3 $< | 	$(P) $(PYTHON_EXECUTABLE) $< | ||||||
| 	$(Q) touch $@ | 	$(Q) touch $@ | ||||||
| 
 | 
 | ||||||
| techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk | techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk | ||||||
|  | @ -25,7 +25,10 @@ techlibs/xilinx/brams_init_8.vh: techlibs/xilinx/brams_init.mk | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_cells_xtra.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6v_cells_xtra.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_cells_xtra.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcu_cells_xtra.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v)) | ||||||
|  | @ -35,13 +38,18 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_bb.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams.txt)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams.txt)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_ff_map.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_ff_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/dsp_map.v)) | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_map.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_unmap.v)) | ||||||
| $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7_nowide.lut)) | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_model.v)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_xc7.box)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_xc7.lut)) | ||||||
|  | $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc9_xc7_nowide.lut)) | ||||||
| 
 | 
 | ||||||
| $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh)) | $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh)) | ||||||
| $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh)) | $(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh)) | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue