mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +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 "python3" | ||||
| 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 | ||||
|       bit vectors and strings containing [01xz]* | ||||
|     - Added "clkbufmap" pass | ||||
|     - Added "extractinv" pass and "invertible_pin" attribute | ||||
|     - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) | ||||
|     - Added "synth_xilinx -ise" (experimental) | ||||
|     - Added "synth_xilinx -iopad" | ||||
|  | @ -38,6 +39,18 @@ Yosys 0.9 .. Yosys 0.9-dev | |||
|     - Improvements in pmgen: slices, choices, define, generate | ||||
|     - Added "xilinx_srl" for Xilinx shift register extraction | ||||
|     - 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 | ||||
| ---------------------- | ||||
|  |  | |||
							
								
								
									
										23
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -88,7 +88,7 @@ ifeq ($(OS), Darwin) | |||
| PLUGIN_LDFLAGS += -undefined dynamic_lookup | ||||
| 
 | ||||
| # homebrew search paths
 | ||||
| ifneq ($(shell which brew),) | ||||
| ifneq ($(shell :; command -v brew),) | ||||
| BREW_PREFIX := $(shell brew --prefix)/opt | ||||
| $(info $$BREW_PREFIX is [${BREW_PREFIX}]) | ||||
| 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) | ||||
| 
 | ||||
| # macports search paths
 | ||||
| else ifneq ($(shell which port),) | ||||
| PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port)) | ||||
| else ifneq ($(shell :; command -v port),) | ||||
| PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) | ||||
| CXXFLAGS += -I$(PORT_PREFIX)/include | ||||
| LDFLAGS += -L$(PORT_PREFIX)/lib | ||||
| PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) | ||||
|  | @ -115,7 +115,7 @@ LDFLAGS += -rdynamic | |||
| LDLIBS += -lrt | ||||
| 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) | ||||
| OBJS = kernel/version_$(GIT_REV).o | ||||
| 
 | ||||
|  | @ -128,7 +128,7 @@ bumpversion: | |||
| # 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
 | ||||
| # delete your work on ABC..
 | ||||
| ABCREV = 5776ad0 | ||||
| ABCREV = 623b5e8 | ||||
| ABCPULL = 1 | ||||
| ABCURL ?= https://github.com/berkeley-abc/abc | ||||
| 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 | ||||
| endif | ||||
| 
 | ||||
| PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) | ||||
| ifeq ($(ENABLE_PYOSYS),1) | ||||
| 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_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) | ||||
| 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/ezminisat.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,frontends/ast/ast.h)) | ||||
| $(eval $(call add_include_file,backends/ilang/ilang_backend.h)) | ||||
|  | @ -545,6 +546,8 @@ OBJS += libs/sha1/sha1.o | |||
| 
 | ||||
| ifneq ($(SMALL),1) | ||||
| 
 | ||||
| OBJS += libs/json11/json11.o | ||||
| 
 | ||||
| OBJS += libs/subcircuit/subcircuit.o | ||||
| 
 | ||||
| OBJS += libs/ezsat/ezsat.o | ||||
|  | @ -705,11 +708,17 @@ test: $(TARGETS) $(EXTRA_TARGETS) | |||
| 	+cd tests/various && bash run-test.sh | ||||
| 	+cd tests/sat && bash run-test.sh | ||||
| 	+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/opt && bash run-test.sh | ||||
| 	+cd tests/aiger && bash run-test.sh $(ABCOPT) | ||||
| 	+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 "  Passed \"make test\"." | ||||
| 	@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 | ||||
|   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 | ||||
|   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. | ||||
|  | @ -343,6 +347,12 @@ Verilog Attributes and non-standard features | |||
|   automatic clock buffer insertion by ``clkbufmap``. This behaviour can be | ||||
|   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 | ||||
|   it as the external-facing pin of an I/O pad, and prevents ``iopadmap`` | ||||
|   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` | ||||
|   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 | ||||
|   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 | ||||
|   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 | ||||
|   the non-standard ``{* ... *}`` attribute syntax to set default attributes | ||||
|   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 | ||||
|   SystemVerilog files being read into the same design afterwards. | ||||
| 
 | ||||
| - typedefs are supported (including inside packages) | ||||
| 
 | ||||
| - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether | ||||
|   ports are inputs or outputs are supported. | ||||
| 
 | ||||
|  |  | |||
|  | @ -101,7 +101,7 @@ struct AigerWriter | |||
| 		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> unused_bits; | ||||
|  | @ -367,6 +367,12 @@ struct AigerWriter | |||
| 				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()) | ||||
| 			aig_latchin.push_back(1); | ||||
| 
 | ||||
|  | @ -704,9 +710,9 @@ struct AigerBackend : public Backend { | |||
| 		log("    -vmap <filename>\n"); | ||||
| 		log("        like -map, but more verbose\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -I, -O, -B\n"); | ||||
| 		log("        If the design contains no input/output/assert then create one\n"); | ||||
| 		log("        dummy input/output/bad_state pin to make the tools reading the\n"); | ||||
| 		log("    -I, -O, -B, -L\n"); | ||||
| 		log("        If the design contains no input/output/assert/flip-flop then create one\n"); | ||||
| 		log("        dummy input/output/bad_state-pin or latch to make the tools reading the\n"); | ||||
| 		log("        AIGER file happy.\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
|  | @ -720,6 +726,7 @@ struct AigerBackend : public Backend { | |||
| 		bool imode = false; | ||||
| 		bool omode = false; | ||||
| 		bool bmode = false; | ||||
| 		bool lmode = false; | ||||
| 		std::string map_filename; | ||||
| 
 | ||||
| 		log_header(design, "Executing AIGER backend.\n"); | ||||
|  | @ -764,16 +771,20 @@ struct AigerBackend : public Backend { | |||
| 				bmode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-L") { | ||||
| 				lmode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(f, filename, args, argidx); | ||||
| 		extra_args(f, filename, args, argidx, !ascii_mode); | ||||
| 
 | ||||
| 		Module *top_module = design->top_module(); | ||||
| 
 | ||||
| 		if (top_module == nullptr) | ||||
| 			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); | ||||
| 
 | ||||
| 		if (!map_filename.empty()) { | ||||
|  |  | |||
|  | @ -83,6 +83,7 @@ struct XAigerWriter | |||
| 	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,int>> co_bits; | ||||
| 	dict<SigBit, float> arrival_times; | ||||
| 
 | ||||
| 	vector<pair<int, int>> aig_gates; | ||||
| 	vector<int> aig_outputs; | ||||
|  | @ -202,7 +203,7 @@ struct XAigerWriter | |||
| 		//       box ordering, but not individual AIG cells
 | ||||
| 		dict<SigBit, pool<IdString>> bit_drivers, bit_users; | ||||
| 		TopoSort<IdString, RTLIL::sort_by_id_str> toposort; | ||||
| 		bool abc_box_seen = false; | ||||
| 		bool abc9_box_seen = false; | ||||
| 
 | ||||
| 		for (auto cell : module->selected_cells()) { | ||||
| 			if (cell->type == "$_NOT_") | ||||
|  | @ -241,20 +242,21 @@ struct XAigerWriter | |||
| 			log_assert(!holes_mode); | ||||
| 
 | ||||
| 			RTLIL::Module* inst_module = module->design->module(cell->type); | ||||
| 			if (inst_module && inst_module->attributes.count("\\abc_box_id")) { | ||||
| 				abc_box_seen = true; | ||||
| 			if (inst_module && inst_module->attributes.count("\\abc9_box_id")) { | ||||
| 				abc9_box_seen = true; | ||||
| 
 | ||||
| 				if (!holes_mode) { | ||||
| 					toposort.node(cell->name); | ||||
| 					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
 | ||||
| 							if (cell->output(conn.first)) continue; | ||||
| 							if (port_wire->port_output) continue; | ||||
| 							for (auto bit : sigmap(conn.second)) | ||||
| 								bit_users[bit].insert(cell->name); | ||||
| 						} | ||||
| 
 | ||||
| 						if (cell->output(conn.first)) | ||||
| 						if (port_wire->port_output) | ||||
| 							for (auto bit : sigmap(conn.second)) | ||||
| 								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)); | ||||
| 
 | ||||
| 					if (is_input) { | ||||
| 						for (auto b : c.second.bits()) { | ||||
| 						for (auto b : c.second) { | ||||
| 							Wire *w = b.wire; | ||||
| 							if (!w) continue; | ||||
| 							if (!w->port_output || !cell_known) { | ||||
|  | @ -287,7 +289,17 @@ struct XAigerWriter | |||
| 						} | ||||
| 					} | ||||
| 					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; | ||||
| 							if (!w) continue; | ||||
| 							input_bits.insert(b); | ||||
|  | @ -295,6 +307,9 @@ struct XAigerWriter | |||
| 							if (O != b) | ||||
| 								alias_map[O] = b; | ||||
| 							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));
 | ||||
| 		} | ||||
| 
 | ||||
| 		if (abc_box_seen) { | ||||
| 		if (abc9_box_seen) { | ||||
| 			for (auto &it : bit_users) | ||||
| 				if (bit_drivers.count(it.first)) | ||||
| 					for (auto driver_cell : bit_drivers.at(it.first)) | ||||
|  | @ -332,9 +347,11 @@ struct XAigerWriter | |||
| 				log_assert(cell); | ||||
| 
 | ||||
| 				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; | ||||
| 
 | ||||
| 				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 undriven output connections of this box cell with anonymous wires
 | ||||
| 				// NB: Assume box_module->ports are sorted alphabetically
 | ||||
|  | @ -379,7 +396,10 @@ struct XAigerWriter | |||
| 							rhs = it->second; | ||||
| 						} | ||||
| 						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); | ||||
| 						} | ||||
| 
 | ||||
|  | @ -390,12 +410,7 @@ struct XAigerWriter | |||
| 							if (O != b) | ||||
| 								alias_map[O] = b; | ||||
| 							undriven_bits.erase(O); | ||||
| 
 | ||||
| 							auto jt = input_bits.find(b); | ||||
| 							if (jt != input_bits.end()) { | ||||
| 								log_assert(keep_bits.count(O)); | ||||
| 								input_bits.erase(b); | ||||
| 							} | ||||
| 							input_bits.erase(b); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  | @ -414,7 +429,7 @@ struct XAigerWriter | |||
| 			// inherit existing inout's drivers
 | ||||
| 			if ((wire->port_input && wire->port_output && !undriven_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); | ||||
| 				if (!new_wire) | ||||
| 					new_wire = module->addWire(wire_name, GetSize(wire)); | ||||
|  | @ -489,16 +504,16 @@ struct XAigerWriter | |||
| 			aig_outputs.push_back(bit2aig(bit)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (output_bits.empty()) { | ||||
| 			output_bits.insert(State::S0); | ||||
| 			omode = true; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto bit : output_bits) { | ||||
| 			ordered_outputs[bit] = aig_o++; | ||||
| 			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) | ||||
|  | @ -560,26 +575,38 @@ struct XAigerWriter | |||
| 
 | ||||
| 		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()) { | ||||
| 			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__"); | ||||
| 			log_assert(holes_module); | ||||
| 
 | ||||
|  | @ -639,23 +666,16 @@ struct XAigerWriter | |||
| 
 | ||||
| 				write_h_buffer(box_inputs); | ||||
| 				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++); | ||||
| 			} | ||||
| 
 | ||||
| 			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; | ||||
| 			auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); | ||||
| 			write_r_buffer(0); | ||||
| 
 | ||||
| 			f << "r"; | ||||
| 			buffer_str = r_buffer.str(); | ||||
| 			buffer_size_be = to_big_endian(buffer_str.size()); | ||||
| 			std::string buffer_str = r_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()); | ||||
| 
 | ||||
|  | @ -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); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -760,11 +797,11 @@ struct XAigerWriter | |||
| 			f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); | ||||
| 
 | ||||
| 		output_lines.sort(); | ||||
| 		if (omode) | ||||
| 			output_lines[State::S0] = "output 0 0 $__dummy__\n"; | ||||
| 		for (auto &it : output_lines) | ||||
| 			f << it.second; | ||||
| 		log_assert(output_lines.size() == output_bits.size()); | ||||
| 		if (omode && output_bits.empty()) | ||||
| 			f << "output " << output_lines.size() << " 0 $__dummy__\n"; | ||||
| 
 | ||||
| 		wire_lines.sort(); | ||||
| 		for (auto &it : wire_lines) | ||||
|  | @ -819,7 +856,7 @@ struct XAigerBackend : public Backend { | |||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(f, filename, args, argidx); | ||||
| 		extra_args(f, filename, args, argidx, !ascii_mode); | ||||
| 
 | ||||
| 		Module *top_module = design->top_module(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -569,7 +569,7 @@ struct BtorWorker | |||
| 			int nid_init_val = -1; | ||||
| 
 | ||||
| 			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 nid = next_nid++; | ||||
|  | @ -681,11 +681,11 @@ struct BtorWorker | |||
| 				{ | ||||
| 					if (verbose) | ||||
| 						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 | ||||
| 				{ | ||||
| 					int nid_init_val = next_nid++; | ||||
| 					nid_init_val = next_nid++; | ||||
| 					btorf("%d state %d\n", nid_init_val, sid); | ||||
| 
 | ||||
| 					for (int i = 0; i < nwords; i++) { | ||||
|  | @ -693,8 +693,8 @@ struct BtorWorker | |||
| 						if (thisword.is_fully_undef()) | ||||
| 							continue; | ||||
| 						Const thisaddr(i, abits); | ||||
| 						int nid_thisword = get_sig_nid(thisword); | ||||
| 						int nid_thisaddr = get_sig_nid(thisaddr); | ||||
| 						int nid_thisword = get_sig_nid(thisword, -1, false, true); | ||||
| 						int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true); | ||||
| 						int last_nid_init_val = nid_init_val; | ||||
| 						nid_init_val = next_nid++; | ||||
| 						if (verbose) | ||||
|  | @ -792,7 +792,7 @@ struct BtorWorker | |||
| 		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; | ||||
| 		sigmap.apply(sig); | ||||
|  | @ -823,7 +823,10 @@ struct BtorWorker | |||
| 				int sid = get_bv_sid(GetSize(sig)); | ||||
| 
 | ||||
| 				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; | ||||
| 				if (sig_mask_undef.is_fully_ones()) { | ||||
|  | @ -897,9 +900,12 @@ struct BtorWorker | |||
| 
 | ||||
| 							int sid = get_bv_sid(GetSize(s)); | ||||
| 							int nid = next_nid++; | ||||
| 							btorf("%d input %d %s\n", nid, sid); | ||||
| 							btorf("%d input %d\n", nid, sid); | ||||
| 							nid_width[nid] = GetSize(s); | ||||
| 
 | ||||
| 							for (int j = 0; j < GetSize(s); j++) | ||||
| 								nidbits.push_back(make_pair(nid, j)); | ||||
| 
 | ||||
| 							i += GetSize(s)-1; | ||||
| 							continue; | ||||
| 						} | ||||
|  |  | |||
|  | @ -266,7 +266,7 @@ struct ProtobufBackend : public Backend { | |||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(f, filename, args, argidx); | ||||
| 		extra_args(f, filename, args, argidx, !text_mode); | ||||
| 
 | ||||
| 		log_header(design, "Executing Protobuf backend.\n"); | ||||
| 
 | ||||
|  | @ -338,7 +338,7 @@ struct ProtobufPass : public Pass { | |||
| 		if (!filename.empty()) { | ||||
| 			rewrite_filename(filename); | ||||
| 			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()) { | ||||
| 				delete ff; | ||||
| 				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)|" < $< > $@ | ||||
| 
 | ||||
| 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
 | ||||
| else | ||||
| TARGETS += yosys-smtbmc | ||||
|  |  | |||
|  | @ -1256,7 +1256,7 @@ def smt_check_sat(): | |||
|     return smt.check_sat() | ||||
| 
 | ||||
| if tempind: | ||||
|     retstatus = False | ||||
|     retstatus = "FAILED" | ||||
|     skip_counter = step_size | ||||
|     for step in range(num_steps, -1, -1): | ||||
|         if smt.forall: | ||||
|  | @ -1303,7 +1303,7 @@ if tempind: | |||
| 
 | ||||
|         else: | ||||
|             print_msg("Temporal induction successful.") | ||||
|             retstatus = True | ||||
|             retstatus = "PASSED" | ||||
|             break | ||||
| 
 | ||||
| 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)) | ||||
| 
 | ||||
|     step = 0 | ||||
|     retstatus = False | ||||
|     retstatus = "FAILED" | ||||
|     found_failed_assert = False | ||||
| 
 | ||||
|     assert step_size == 1 | ||||
|  | @ -1365,7 +1365,7 @@ elif covermode: | |||
|                 if smt_check_sat() == "unsat": | ||||
|                     print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) | ||||
|                     found_failed_assert = True | ||||
|                     retstatus = False | ||||
|                     retstatus = "FAILED" | ||||
|                     break | ||||
| 
 | ||||
|             reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step))) | ||||
|  | @ -1400,7 +1400,7 @@ elif covermode: | |||
|             break | ||||
| 
 | ||||
|         if "1" not in cover_mask: | ||||
|             retstatus = True | ||||
|             retstatus = "PASSED" | ||||
|             break | ||||
| 
 | ||||
|         step += 1 | ||||
|  | @ -1412,7 +1412,7 @@ elif covermode: | |||
| 
 | ||||
| else:  # not tempind, covermode | ||||
|     step = 0 | ||||
|     retstatus = True | ||||
|     retstatus = "PASSED" | ||||
|     while step < num_steps: | ||||
|         smt_state(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)) | ||||
| 
 | ||||
|                 if smt_check_sat() == "unsat": | ||||
|                     print("%s Warmup failed!" % smt.timestamp()) | ||||
|                     retstatus = False | ||||
|                     print("%s Assumptions are unsatisfiable!" % smt.timestamp()) | ||||
|                     retstatus = "PREUNSAT" | ||||
|                     break | ||||
| 
 | ||||
|             if not final_only: | ||||
|  | @ -1487,13 +1487,13 @@ else:  # not tempind, covermode | |||
|                         print_msg("Re-solving with appended steps..") | ||||
|                         if smt_check_sat() == "unsat": | ||||
|                             print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) | ||||
|                             retstatus = False | ||||
|                             retstatus = "FAILED" | ||||
|                             break | ||||
|                     print_anyconsts(step) | ||||
|                     for i in range(step, last_check_step+1): | ||||
|                         print_failed_asserts(i) | ||||
|                     write_trace(0, last_check_step+1+append_steps, '%') | ||||
|                     retstatus = False | ||||
|                     retstatus = "FAILED" | ||||
|                     break | ||||
| 
 | ||||
|                 smt_pop() | ||||
|  | @ -1519,7 +1519,7 @@ else:  # not tempind, covermode | |||
|                         print_anyconsts(i) | ||||
|                         print_failed_asserts(i, final=True) | ||||
|                         write_trace(0, i+1, '%') | ||||
|                         retstatus = False | ||||
|                         retstatus = "FAILED" | ||||
|                         break | ||||
| 
 | ||||
|                     smt_pop() | ||||
|  | @ -1534,7 +1534,7 @@ else:  # not tempind, covermode | |||
|             print_msg("Solving for step %d.." % (last_check_step)) | ||||
|             if smt_check_sat() != "sat": | ||||
|                 print("%s No solution found!" % smt.timestamp()) | ||||
|                 retstatus = False | ||||
|                 retstatus = "FAILED" | ||||
|                 break | ||||
| 
 | ||||
|             elif dumpall: | ||||
|  | @ -1551,5 +1551,5 @@ else:  # not tempind, covermode | |||
| smt.write("(exit)") | ||||
| smt.wait() | ||||
| 
 | ||||
| print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)")) | ||||
| sys.exit(0 if retstatus else 1) | ||||
| print_msg("Status: %s" % retstatus) | ||||
| sys.exit(0 if retstatus == "PASSED" else 1) | ||||
|  |  | |||
|  | @ -285,6 +285,8 @@ end_of_header: | |||
| 		} | ||||
| 		else if (c == 'c') { | ||||
| 			f.ignore(1); | ||||
| 			if (f.peek() == '\r') | ||||
| 				f.ignore(1); | ||||
| 			if (f.peek() == '\n') | ||||
| 				break; | ||||
| 			// Else constraint (TODO)
 | ||||
|  | @ -430,6 +432,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup) | |||
| 			else if (c == 'r') { | ||||
| 				uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); | ||||
| 				flopNum = parse_xaiger_literal(f); | ||||
| 				log_debug("flopNum: %u\n", flopNum); | ||||
| 				log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); | ||||
| 				f.ignore(flopNum * sizeof(uint32_t)); | ||||
| 			} | ||||
|  | @ -496,8 +499,7 @@ void AigerReader::parse_aiger_ascii() | |||
| 
 | ||||
| 	// Parse latches
 | ||||
| 	RTLIL::Wire *clk_wire = nullptr; | ||||
| 	if (L > 0) { | ||||
| 		log_assert(clk_name != ""); | ||||
| 	if (L > 0 && !clk_name.empty()) { | ||||
| 		clk_wire = module->wire(clk_name); | ||||
| 		log_assert(!clk_wire); | ||||
| 		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 *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
 | ||||
| 		if (f.peek() == ' ') { | ||||
|  | @ -621,8 +626,7 @@ void AigerReader::parse_aiger_binary() | |||
| 
 | ||||
| 	// Parse latches
 | ||||
| 	RTLIL::Wire *clk_wire = nullptr; | ||||
| 	if (L > 0) { | ||||
| 		log_assert(clk_name != ""); | ||||
| 	if (L > 0 && !clk_name.empty()) { | ||||
| 		clk_wire = module->wire(clk_name); | ||||
| 		log_assert(!clk_wire); | ||||
| 		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 *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
 | ||||
| 		if (f.peek() == ' ') { | ||||
|  | @ -733,22 +740,22 @@ void AigerReader::post_process() | |||
| 		log_assert(box_module); | ||||
| 
 | ||||
| 		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()) { | ||||
| 				RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; | ||||
| 				auto carry_in_out = it->second.decode_string(); | ||||
| 				auto pos = carry_in_out.find(','); | ||||
| 				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)); | ||||
| 				carry_in = box_module->wire(carry_in_name); | ||||
| 				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)); | ||||
| 				carry_out = box_module->wire(carry_out_name); | ||||
| 				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; | ||||
| 				for (auto jt = ports.begin(); jt != ports.end(); ) { | ||||
|  | @ -776,19 +783,19 @@ void AigerReader::post_process() | |||
| 		// NB: Assume box_module->ports are sorted alphabetically
 | ||||
| 		//     (as RTLIL::Module::fixup_ports() would do)
 | ||||
| 		for (auto port_name : box_module->ports) { | ||||
| 			RTLIL::Wire* w = box_module->wire(port_name); | ||||
| 			log_assert(w); | ||||
| 			RTLIL::Wire* port = box_module->wire(port_name); | ||||
| 			log_assert(port); | ||||
| 			RTLIL::SigSpec rhs; | ||||
| 			RTLIL::Wire* wire = nullptr; | ||||
| 			for (int i = 0; i < GetSize(w); i++) { | ||||
| 				if (w->port_input) { | ||||
| 			for (int i = 0; i < GetSize(port); i++) { | ||||
| 				RTLIL::Wire* wire = nullptr; | ||||
| 				if (port->port_input) { | ||||
| 					log_assert(co_count < outputs.size()); | ||||
| 					wire = outputs[co_count++]; | ||||
| 					log_assert(wire); | ||||
| 					log_assert(wire->port_output); | ||||
| 					wire->port_output = false; | ||||
| 				} | ||||
| 				if (w->port_output) { | ||||
| 				if (port->port_output) { | ||||
| 					log_assert((piNum + ci_count) < inputs.size()); | ||||
| 					wire = inputs[piNum + ci_count++]; | ||||
| 					log_assert(wire); | ||||
|  | @ -797,6 +804,7 @@ void AigerReader::post_process() | |||
| 				} | ||||
| 				rhs.append(wire); | ||||
| 			} | ||||
| 
 | ||||
| 			cell->setPort(port_name, rhs); | ||||
| 		} | ||||
| 	} | ||||
|  | @ -814,6 +822,7 @@ void AigerReader::post_process() | |||
| 				RTLIL::Wire* wire = inputs[variable]; | ||||
| 				log_assert(wire); | ||||
| 				log_assert(wire->port_input); | ||||
| 				log_debug("Renaming input %s", log_id(wire)); | ||||
| 
 | ||||
| 				if (index == 0) { | ||||
| 					// Cope with the fact that a CI might be identical
 | ||||
|  | @ -840,6 +849,7 @@ void AigerReader::post_process() | |||
| 						wire->port_input = false; | ||||
| 					} | ||||
| 				} | ||||
| 				log_debug(" -> %s\n", log_id(wire)); | ||||
| 			} | ||||
| 			else if (type == "output") { | ||||
| 				log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); | ||||
|  | @ -850,6 +860,7 @@ void AigerReader::post_process() | |||
| 					wire->port_output = false; | ||||
| 					continue; | ||||
| 				} | ||||
| 				log_debug("Renaming output %s", log_id(wire)); | ||||
| 
 | ||||
| 				if (index == 0) { | ||||
| 					// Cope with the fact that a CO might be identical
 | ||||
|  | @ -859,7 +870,7 @@ void AigerReader::post_process() | |||
| 					if (!existing) { | ||||
| 						if (escaped_s.ends_with("$inout.out")) { | ||||
| 							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->port_input && !in_wire->port_output); | ||||
| 							in_wire->port_output = true; | ||||
|  | @ -871,6 +882,7 @@ void AigerReader::post_process() | |||
| 					else { | ||||
| 						wire->port_output = false; | ||||
| 						module->connect(wire, existing); | ||||
| 						wire = existing; | ||||
| 					} | ||||
| 				} | ||||
| 				else if (index > 0) { | ||||
|  | @ -879,7 +891,7 @@ void AigerReader::post_process() | |||
| 					if (!existing) { | ||||
| 						if (escaped_s.ends_with("$inout.out")) { | ||||
| 							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->port_input && !in_wire->port_output); | ||||
| 							in_wire->port_output = true; | ||||
|  | @ -896,6 +908,7 @@ void AigerReader::post_process() | |||
| 						wire->port_output = false; | ||||
| 					} | ||||
| 				} | ||||
| 				log_debug(" -> %s\n", log_id(wire)); | ||||
| 			} | ||||
| 			else if (type == "box") { | ||||
| 				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
 | ||||
| 	RTLIL::Design *mapped_design = new RTLIL::Design; | ||||
| 	mapped_design->add(module); | ||||
| 	Pass::call(mapped_design, "clean -purge"); | ||||
| 	Pass::call(mapped_design, "clean"); | ||||
| 	mapped_design->modules_.erase(module->name); | ||||
| 	delete mapped_design; | ||||
| 
 | ||||
|  | @ -1004,8 +1017,8 @@ struct AigerFrontend : public Frontend { | |||
| 		log("        Name of module to be created (default: <filename>)\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -clk_name <wire_name>\n"); | ||||
| 		log("        AIGER latches to be transformed into posedge DFFs clocked by wire of"); | ||||
| 		log("        this name (default: clk)\n"); | ||||
| 		log("        If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); | ||||
| 		log("        clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -map <filename>\n"); | ||||
| 		log("        read file with port and latch symbols\n"); | ||||
|  | @ -1045,13 +1058,15 @@ struct AigerFrontend : public Frontend { | |||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(f, filename, args, argidx); | ||||
| 		extra_args(f, filename, args, argidx, true); | ||||
| 
 | ||||
| 		if (module_name.empty()) { | ||||
| #ifdef _WIN32 | ||||
| 			char fname[_MAX_FNAME]; | ||||
| 			_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 | ||||
| 			char* bn = strdup(filename.c_str()); | ||||
| 			module_name = RTLIL::escape_id(bn); | ||||
|  |  | |||
|  | @ -158,7 +158,14 @@ std::string AST::type2str(AstNodeType type) | |||
| 	X(AST_POSEDGE) | ||||
| 	X(AST_NEGEDGE) | ||||
| 	X(AST_EDGE) | ||||
| 	X(AST_INTERFACE) | ||||
| 	X(AST_INTERFACEPORT) | ||||
| 	X(AST_INTERFACEPORTTYPE) | ||||
| 	X(AST_MODPORT) | ||||
| 	X(AST_MODPORTMEMBER) | ||||
| 	X(AST_PACKAGE) | ||||
| 	X(AST_WIRETYPE) | ||||
| 	X(AST_TYPEDEF) | ||||
| #undef X | ||||
| 	default: | ||||
| 		log_abort(); | ||||
|  | @ -201,6 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch | |||
| 	was_checked = false; | ||||
| 	range_valid = false; | ||||
| 	range_swapped = false; | ||||
| 	is_custom_type = false; | ||||
| 	port_id = 0; | ||||
| 	range_left = -1; | ||||
| 	range_right = 0; | ||||
|  | @ -1099,6 +1107,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast | |||
| 
 | ||||
| 		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) | ||||
| 		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.
 | ||||
| void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) | ||||
| { | ||||
| 	loadconfig(); | ||||
| 
 | ||||
| 	bool is_top = false; | ||||
| 	AstNode *new_ast = ast->clone(); | ||||
| 	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
 | ||||
| // 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; | ||||
| 	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,
 | ||||
| 	// "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; | ||||
| 	} | ||||
| 
 | ||||
| 	std::string new_modname = modname; | ||||
| 	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; | ||||
| 
 | ||||
| 		// 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
 | ||||
| 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; | ||||
| 	std::string modname = derive_common(design, parameters, &new_ast, mayfail); | ||||
| 	std::string modname = derive_common(design, parameters, &new_ast); | ||||
| 
 | ||||
| 	if (!design->has(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
 | ||||
| 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(); | ||||
| 
 | ||||
| 	if (stripped_name.compare(0, 9, "$abstract") == 0) | ||||
| 		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; | ||||
| 	AstNode *new_ast = ast->clone(); | ||||
| 
 | ||||
| 	int para_counter = 0; | ||||
| 	int orig_parameters_n = parameters.size(); | ||||
| 	for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) { | ||||
| 		AstNode *child = *it; | ||||
| 	for (const auto child : 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]))); | ||||
| 	rewrite_parameter: | ||||
| 			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; | ||||
| 		} | ||||
| 		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) { | ||||
| 			log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); | ||||
| 			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) { | ||||
|  | @ -1529,16 +1563,6 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString | |||
| 		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; | ||||
| 	return modname; | ||||
| } | ||||
|  | @ -1565,6 +1589,27 @@ RTLIL::Module *AstModule::clone() const | |||
| 	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
 | ||||
| namespace { | ||||
| 	int internal_line_num; | ||||
|  |  | |||
|  | @ -148,7 +148,10 @@ namespace AST | |||
| 		AST_INTERFACEPORTTYPE, | ||||
| 		AST_MODPORT, | ||||
| 		AST_MODPORTMEMBER, | ||||
| 		AST_PACKAGE | ||||
| 		AST_PACKAGE, | ||||
| 
 | ||||
| 		AST_WIRETYPE, | ||||
| 		AST_TYPEDEF | ||||
| 	}; | ||||
| 
 | ||||
| 	// 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
 | ||||
| 		std::string str; | ||||
| 		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; | ||||
| 		uint32_t integer; | ||||
| 		double realvalue; | ||||
|  | @ -296,9 +299,10 @@ namespace AST | |||
| 		~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, 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; | ||||
| 		RTLIL::Module *clone() const YS_OVERRIDE; | ||||
| 		void loadconfig() const; | ||||
| 	}; | ||||
| 
 | ||||
| 	// 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_MODPORT: | ||||
| 	case AST_MODPORTMEMBER: | ||||
| 	case AST_TYPEDEF: | ||||
| 		break; | ||||
| 	case AST_INTERFACEPORT: { | ||||
| 		// 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.)
 | ||||
| 	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; | ||||
| 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) | ||||
| 		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; | ||||
| 		for (size_t i = 0; i < children.size(); i++) { | ||||
| 			AstNode *node = children[i]; | ||||
| 
 | ||||
| 			if (node->type == AST_WIRE) { | ||||
| 				if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { | ||||
| 					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; | ||||
| 			} | ||||
| 			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]; | ||||
| 				current_scope[node->str] = node; | ||||
| 			} | ||||
| 		} | ||||
| 		for (size_t i = 0; i < children.size(); 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)) | ||||
| 					did_something = true; | ||||
| 		} | ||||
|  | @ -780,6 +782,99 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 		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
 | ||||
| 	if (type == AST_PREFIX) { | ||||
| 		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()) | ||||
| 	{ | ||||
| 		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"); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1206,7 +1301,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 
 | ||||
| 		std::vector<AstNode*> new_children; | ||||
| 		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); | ||||
| 				current_ast_mod->children.push_back(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; | ||||
| 		while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } | ||||
| 
 | ||||
| 		std::vector<RTLIL::State> x_bit; | ||||
| 		x_bit.push_back(RTLIL::State::Sx); | ||||
| 		AstNode *check_defval; | ||||
| 		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]->was_checked = true; | ||||
| 
 | ||||
|  | @ -1546,9 +1647,13 @@ skip_dynamic_range_lvalue_expansion:; | |||
| 		default_signals->children.push_back(assign_en); | ||||
| 		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())); | ||||
| 		assign_check->children[0]->str = id_check; | ||||
| 		assign_check->children[0]->was_checked = true; | ||||
| 		if (type == AST_LIVE || type == AST_FAIR) { | ||||
| 			assign_check = nullptr; | ||||
| 		} 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) { | ||||
| 			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; | ||||
| 
 | ||||
| 		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); | ||||
| 
 | ||||
| 		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) | ||||
| { | ||||
| 	if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { | ||||
| 		current_scope[index_var]->children[0]->cloneInto(this); | ||||
| 		return; | ||||
| 		if (children.empty()) { | ||||
| 			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]; | ||||
| 
 | ||||
| 	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++) { | ||||
| 		AstNode *child = children[i]; | ||||
| 		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) | ||||
| 				backup_name_map = name_map; | ||||
| 			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); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	if (backup_name_map.size() > 0) | ||||
| 		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; | ||||
| 	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) | ||||
| 	{ | ||||
| 		// 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) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (type == AST_TYPEDEF) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) | ||||
| 	{ | ||||
| 		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) | ||||
| 				goto error; | ||||
| 
 | ||||
| 			if (!strcmp(cmd, ".blackbox")) | ||||
| 			{ | ||||
| 				module->attributes["\\blackbox"] = RTLIL::Const(1); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (!strcmp(cmd, ".end")) | ||||
| 			{ | ||||
| 				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; | ||||
| 				} | ||||
| 
 | ||||
| 				module->rename(lastcell, p); | ||||
| 				module->rename(lastcell, RTLIL::escape_id(p)); | ||||
| 				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'); | ||||
| 		else if (*str == 'x' || *str == 'X') | ||||
| 			digits.push_back(0xf0); | ||||
| 		else if (*str == 'z' || *str == 'Z') | ||||
| 		else if (*str == 'z' || *str == 'Z' || *str == '?') | ||||
| 			digits.push_back(0xf1); | ||||
| 		else if (*str == '?') | ||||
| 			digits.push_back(0xf2); | ||||
| 		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); | ||||
| 				else if (*it == 0xf1) | ||||
| 					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); | ||||
| 				else if (*it == 0xf2) | ||||
| 					data.push_back(RTLIL::Sa); | ||||
| 				else | ||||
| 					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) | ||||
| 		len_in_bits = -1; | ||||
| 
 | ||||
| 	// The "<bits>'s?[bodhBODH]<digits>" syntax
 | ||||
| 	// The "<bits>'[sS]?[bodhBODH]<digits>" syntax
 | ||||
| 	if (*endptr == '\'') | ||||
| 	{ | ||||
| 		std::vector<RTLIL::State> data; | ||||
| 		bool is_signed = false; | ||||
| 		bool is_unsized = len_in_bits < 0; | ||||
| 		if (*(endptr+1) == 's') { | ||||
| 		if (*(endptr+1) == 's' || *(endptr+1) == 'S') { | ||||
| 			is_signed = true; | ||||
| 			endptr++; | ||||
| 		} | ||||
|  |  | |||
|  | @ -239,7 +239,7 @@ YOSYS_NAMESPACE_END | |||
| 	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); | ||||
| 	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> 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 <al> attr case_attr | ||||
| 
 | ||||
|  | @ -206,6 +206,7 @@ design: | |||
| 	task_func_decl design | | ||||
| 	param_decl design | | ||||
| 	localparam_decl design | | ||||
| 	typedef_decl design | | ||||
| 	package design | | ||||
| 	interface design | | ||||
| 	/* empty */; | ||||
|  | @ -290,6 +291,9 @@ hierarchical_id: | |||
| 		$$ = $1; | ||||
| 	}; | ||||
| 
 | ||||
| hierarchical_type_id: | ||||
| 	'(' hierarchical_id ')' { $$ = $2; }; | ||||
| 
 | ||||
| module: | ||||
| 	attr TOK_MODULE TOK_ID { | ||||
| 		do_not_require_port_stubs = false; | ||||
|  | @ -324,13 +328,13 @@ single_module_para: | |||
| 		astbuf1 = new AstNode(AST_PARAMETER); | ||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||
| 		append_attr(astbuf1, $1); | ||||
| 	} param_signed param_integer param_range single_param_decl | | ||||
| 	} param_type single_param_decl | | ||||
| 	attr TOK_LOCALPARAM { | ||||
| 		if (astbuf1) delete astbuf1; | ||||
| 		astbuf1 = new AstNode(AST_LOCALPARAM); | ||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||
| 		append_attr(astbuf1, $1); | ||||
| 	} param_signed param_integer param_range single_param_decl | | ||||
| 	} param_type single_param_decl | | ||||
| 	single_param_decl; | ||||
| 
 | ||||
| module_args_opt: | ||||
|  | @ -426,6 +430,7 @@ package_body: | |||
| 	package_body package_body_stmt |; | ||||
| 
 | ||||
| package_body_stmt: | ||||
| 	typedef_decl | | ||||
| 	localparam_decl; | ||||
| 
 | ||||
| interface: | ||||
|  | @ -452,7 +457,7 @@ interface_body: | |||
| 	interface_body 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; | ||||
| 
 | ||||
| non_opt_delay: | ||||
|  | @ -475,8 +480,14 @@ wire_type: | |||
| 	}; | ||||
| 
 | ||||
| wire_type_token_list: | ||||
| 	wire_type_token | wire_type_token_list wire_type_token | | ||||
| 	wire_type_token_io ; | ||||
| 	wire_type_token | | ||||
| 	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: | ||||
| 	TOK_INPUT { | ||||
|  | @ -591,7 +602,7 @@ module_body: | |||
| 	/* empty */; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 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: | ||||
| 	attr TOK_PARAMETER { | ||||
| 		astbuf1 = new AstNode(AST_PARAMETER); | ||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||
| 		append_attr(astbuf1, $1); | ||||
| 	} param_signed param_integer param_real param_range param_decl_list ';' { | ||||
| 	} param_type param_decl_list ';' { | ||||
| 		delete astbuf1; | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -1163,7 +1182,7 @@ localparam_decl: | |||
| 		astbuf1 = new AstNode(AST_LOCALPARAM); | ||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||
| 		append_attr(astbuf1, $1); | ||||
| 	} param_signed param_integer param_real param_range param_decl_list ';' { | ||||
| 	} param_type param_decl_list ';' { | ||||
| 		delete astbuf1; | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -1327,7 +1346,7 @@ wire_name: | |||
| 		if ($2 != NULL) { | ||||
| 			if (node->is_input || node->is_output) | ||||
| 				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); | ||||
| 				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)); | ||||
| 	}; | ||||
| 
 | ||||
| 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: | ||||
| 	attr TOK_ID { | ||||
| 		astbuf1 = new AstNode(AST_CELL); | ||||
|  | @ -1823,7 +1881,7 @@ simple_behavioral_stmt: | |||
| 
 | ||||
| // this production creates the obligatory if-else shift/reduce conflict | ||||
| 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 | | ||||
| 	simple_behavioral_stmt ';' | ';' | | ||||
| 	hierarchical_id attr { | ||||
|  |  | |||
|  | @ -551,6 +551,10 @@ void log_dump_val_worker(RTLIL::SigSpec 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) | ||||
| { | ||||
| 	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); } | ||||
| void log_dump_val_worker(RTLIL::IdString v); | ||||
| void log_dump_val_worker(RTLIL::SigSpec v); | ||||
| void log_dump_val_worker(RTLIL::State v); | ||||
| 
 | ||||
| template<typename K, typename T, typename OPS> | ||||
| 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  { | ||||
| public: | ||||
| 	gzip_ostream() | ||||
| 	gzip_ostream() : std::ostream(nullptr) | ||||
| 	{ | ||||
| 		rdbuf(&outbuf); | ||||
| 	} | ||||
|  | @ -71,7 +71,7 @@ private: | |||
| 			str(""); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		~gzip_streambuf() | ||||
| 		virtual ~gzip_streambuf() | ||||
| 		{ | ||||
| 			sync(); | ||||
| 			gzclose(gzf); | ||||
|  | @ -439,7 +439,7 @@ void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design) | |||
| FILE *Frontend::current_script_file = NULL; | ||||
| 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; | ||||
| 
 | ||||
|  | @ -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()); | ||||
| 			} | ||||
| 			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); | ||||
| 			if (ff->fail()) | ||||
| 				delete ff; | ||||
|  | @ -498,7 +498,15 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s | |||
| 			if (f != NULL) { | ||||
| 				// Check for gzip magic
 | ||||
| 				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) { | ||||
| 	#ifdef YOSYS_ENABLE_ZLIB | ||||
| 					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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
|  | @ -639,7 +647,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st | |||
| #endif | ||||
| 		} else { | ||||
| 			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); | ||||
| 			if (ff->fail()) { | ||||
| 				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; | ||||
| 
 | ||||
| 	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::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; | ||||
| 	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::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*> result; | ||||
| 	result.reserve(wires_.size()); | ||||
| 	result.reserve(cells_.size()); | ||||
| 	for (auto &it : cells_) | ||||
| 		if (design->selected(this, 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(width_ == other->width_); | ||||
| 
 | ||||
| 	if (rules.empty()) return; | ||||
| 	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(width_ == other->width_); | ||||
| 
 | ||||
| 	if (rules.empty()) return; | ||||
| 	unpack(); | ||||
| 	other->unpack(); | ||||
| 
 | ||||
|  | @ -3552,6 +3554,12 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const | |||
| 	if (width_ != other.width_) | ||||
| 		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(); | ||||
| 	other.pack(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -609,8 +609,11 @@ struct RTLIL::Const | |||
| 	std::string decode_string() const; | ||||
| 
 | ||||
| 	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 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_ones() const; | ||||
|  |  | |||
|  | @ -135,9 +135,11 @@ struct SigPool | |||
| 	} | ||||
| }; | ||||
| 
 | ||||
| template <typename T, class Compare = std::less<T>> | ||||
| template <typename T, class Compare = void> | ||||
| 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> { | ||||
| 		bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } | ||||
| 		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 | ||||
| { | ||||
| 	mfp<SigBit> database; | ||||
|  |  | |||
|  | @ -210,6 +210,7 @@ namespace RTLIL { | |||
| 	struct Module; | ||||
| 	struct Design; | ||||
| 	struct Monitor; | ||||
| 	enum State : unsigned char; | ||||
| } | ||||
| 
 | ||||
| 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 fail(char *format, char *data) { | ||||
| int fail(const char *format, const char *data) { | ||||
|     /* Print error message to stderr and return 2 */ | ||||
|     fprintf(stderr, format, data); | ||||
|     return 2; | ||||
|  | @ -76,7 +76,7 @@ char *quoted(char *data) { | |||
| 
 | ||||
|     /* We allocate twice as much space as needed to deal with worse-case
 | ||||
|        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; | ||||
| 
 | ||||
|     *presult++ = '"'; | ||||
|  | @ -120,7 +120,7 @@ char *loadable_exe(char *exename) { | |||
|     if (!hPython) return NULL; */ | ||||
| 
 | ||||
|     /* Return the absolute filename for spawnv */ | ||||
|     result = calloc(MAX_PATH, sizeof(char)); | ||||
|     result = (char *)calloc(MAX_PATH, sizeof(char)); | ||||
|     strncpy(result, exename, 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 */ | ||||
| 
 | ||||
|     char **result = calloc(strlen(cmdline), sizeof(char *)); | ||||
|     char **result = (char **)calloc(strlen(cmdline), sizeof(char *)); | ||||
|     char *output = cmdline; | ||||
|     char c; | ||||
|     int nb = 0; | ||||
|  |  | |||
|  | @ -1081,6 +1081,8 @@ class WConstructor: | |||
| 		con.args = [] | ||||
| 		con.duplicate = False | ||||
| 		con.protected = protected | ||||
| 		if str.startswith(str_def, "inline "): | ||||
| 			str_def = str_def[7:] | ||||
| 		if not str.startswith(str_def, class_.name + "("): | ||||
| 			return None | ||||
| 		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/qwp.o | ||||
| OBJS += passes/cmds/edgetypes.o | ||||
| OBJS += passes/cmds/portlist.o | ||||
| OBJS += passes/cmds/chformal.o | ||||
| OBJS += passes/cmds/chtype.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("selected modules.\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 | ||||
| 	{ | ||||
|  | @ -113,6 +118,7 @@ struct AddPass : public Pass { | |||
| 		bool arg_flag_input = false; | ||||
| 		bool arg_flag_output = false; | ||||
| 		bool arg_flag_global = false; | ||||
| 		bool mod_mode = false; | ||||
| 		int arg_width = 0; | ||||
| 
 | ||||
| 		size_t argidx; | ||||
|  | @ -133,8 +139,20 @@ struct AddPass : public Pass { | |||
| 				arg_width = atoi(args[++argidx].c_str()); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (arg == "-mod") { | ||||
| 				mod_mode = true; | ||||
| 				argidx++; | ||||
| 				break; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (mod_mode) { | ||||
| 			for (; argidx < args.size(); argidx++) | ||||
| 				design->addModule(RTLIL::escape_id(args[argidx])); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto &mod : design->modules_) | ||||
|  |  | |||
|  | @ -41,14 +41,24 @@ struct CheckPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log(" - used wires that do not have a driver\n"); | ||||
| 		log("\n"); | ||||
| 		log("When called with -noinit then this command also checks for wires which have\n"); | ||||
| 		log("the 'init' attribute set.\n"); | ||||
| 		log("Options:\n"); | ||||
| 		log("\n"); | ||||
| 		log("When called with -initdrv then this command also checks for wires which have\n"); | ||||
| 		log("the 'init' attribute set and aren't driven by a FF cell type.\n"); | ||||
| 		log("  -noinit\n"); | ||||
| 		log("    Also check for wires which have the 'init' attribute set.\n"); | ||||
| 		log("\n"); | ||||
| 		log("When called with -assert then the command will produce an error if any\n"); | ||||
| 		log("problems are found in the current design.\n"); | ||||
| 		log("  -initdrv\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"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
|  | @ -56,6 +66,8 @@ struct CheckPass : public Pass { | |||
| 		int counter = 0; | ||||
| 		bool noinit = false; | ||||
| 		bool initdrv = false; | ||||
| 		bool mapped = false; | ||||
| 		bool allow_tbuf = false; | ||||
| 		bool assert_mode = false; | ||||
| 
 | ||||
| 		size_t argidx; | ||||
|  | @ -68,6 +80,14 @@ struct CheckPass : public Pass { | |||
| 				initdrv = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-mapped") { | ||||
| 				mapped = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-allow-tbuf") { | ||||
| 				allow_tbuf = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-assert") { | ||||
| 				assert_mode = true; | ||||
| 				continue; | ||||
|  | @ -135,29 +155,37 @@ struct CheckPass : public Pass { | |||
| 			TopoSort<string> topo; | ||||
| 
 | ||||
| 			for (auto cell : module->cells()) | ||||
| 			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 (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { | ||||
| 					if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed; | ||||
| 					log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type)); | ||||
| 					counter++; | ||||
| 				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) | ||||
| 								topo.edge(stringf("wire %s", log_signal(bit)), | ||||
| 										stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); | ||||
| 							used_wires.insert(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->output(conn.first)) | ||||
| 					for (int i = 0; i < GetSize(sig); i++) { | ||||
| 						if (logic_cell) | ||||
| 							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]++; | ||||
| 					if (!cell->input(conn.first) && cell->output(conn.first)) | ||||
| 						for (auto bit : sig) | ||||
| 							if (bit.wire) wire_drivers_count[bit]++; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			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> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef __APPLE__ | ||||
| #  include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef YOSYS_ENABLE_READLINE | ||||
| #  include <readline/readline.h> | ||||
| #endif | ||||
|  | @ -866,7 +870,11 @@ struct ShowPass : public Pass { | |||
| 				log_cmd_error("Shell command failed!\n"); | ||||
| 		} else | ||||
| 		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()); | ||||
| 			#endif | ||||
| 			log("Exec: %s\n", cmd.c_str()); | ||||
| 			if (run_command(cmd) != 0) | ||||
| 				log_cmd_error("Shell command failed!\n"); | ||||
|  |  | |||
|  | @ -32,7 +32,8 @@ struct EquivOptPass:public ScriptPass | |||
| 		log("\n"); | ||||
| 		log("    equiv_opt [options] [command]\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("    -run <from_label>:<to_label>\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("        produce an error if the circuits are not equivalent.\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("        enable modelling of undef states during equiv_induct.\n"); | ||||
| 		log("\n"); | ||||
|  | @ -55,7 +62,7 @@ struct EquivOptPass:public ScriptPass | |||
| 	} | ||||
| 
 | ||||
| 	std::string command, techmap_opts; | ||||
| 	bool assert, undef; | ||||
| 	bool assert, undef, multiclock, async2sync; | ||||
| 
 | ||||
| 	void clear_flags() YS_OVERRIDE | ||||
| 	{ | ||||
|  | @ -63,6 +70,8 @@ struct EquivOptPass:public ScriptPass | |||
| 		techmap_opts = ""; | ||||
| 		assert = false; | ||||
| 		undef = false; | ||||
| 		multiclock = false; | ||||
| 		async2sync = false; | ||||
| 	} | ||||
| 
 | ||||
| 	void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE | ||||
|  | @ -92,6 +101,14 @@ struct EquivOptPass:public ScriptPass | |||
| 				undef = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-multiclock") { | ||||
| 				multiclock = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-async2sync") { | ||||
| 				async2sync = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -111,6 +128,9 @@ struct EquivOptPass:public ScriptPass | |||
| 		if (!design->full_selection()) | ||||
| 			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_push(); | ||||
| 
 | ||||
|  | @ -146,6 +166,10 @@ struct EquivOptPass:public ScriptPass | |||
| 		} | ||||
| 
 | ||||
| 		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"); | ||||
| 			if (help_mode) | ||||
| 				run("equiv_induct [-undef] equiv"); | ||||
|  |  | |||
|  | @ -808,6 +808,30 @@ struct HierarchyPass : public Pass { | |||
| 				if (mod_it.second->get_bool_attribute("\\top")) | ||||
| 					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) { | ||||
| 			log_header(design, "Finding top of design hierarchy..\n"); | ||||
| 			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_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))) { | ||||
| 					RTLIL::SigSpec input = b; | ||||
| 					ACTION_DO(ID::Y, cell->getPort(ID::A)); | ||||
|  |  | |||
|  | @ -108,12 +108,13 @@ bool cell_supported(RTLIL::Cell *cell) | |||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| std::map<IdString, IdString> mergeable_type_map{ | ||||
|   {ID($sub), ID($add)}, | ||||
| }; | ||||
| std::map<IdString, IdString> mergeable_type_map; | ||||
| 
 | ||||
| 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; | ||||
| 	if (mergeable_type_map.count(a_type)) | ||||
| 		a_type = mergeable_type_map.at(a_type); | ||||
|  |  | |||
							
								
								
									
										2
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | |||
| /*_pm.h | ||||
| /*_pm.h | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| %_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 | ||||
| passes/pmgen/peepopt.o: 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_muldiv.pmg | ||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg | ||||
| 
 | ||||
| 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 | ||||
|         slice idx GetSize(port(pmux, \S)) | ||||
|         index <SigBit> port(pmux, \S)[idx] === port(eq, \Y) | ||||
| 	set pmux_slice idx | ||||
|         set pmux_slice idx | ||||
|     endmatch | ||||
| 
 | ||||
| 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 | ||||
|     ... | ||||
| 
 | ||||
| Subpatterns cann be called recursively. | ||||
| Subpatterns can be called recursively. | ||||
| 
 | ||||
| If a `subpattern` statement is preceded by a `fallthrough` statement, this is | ||||
| 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; | ||||
| 
 | ||||
| #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_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) { | ||||
| 		log("  input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); | ||||
| 		return; | ||||
|  | @ -52,59 +52,85 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(st.sigS) > 32) { | ||||
| 		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); | ||||
| 	if (GetSize(st.sigO) > 33) { | ||||
| 		log("  adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(st.sigY) > 32) { | ||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); | ||||
| 	if (GetSize(st.sigH) > 32) { | ||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH)); | ||||
| 		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 *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); | ||||
| 	pm.module->swap_names(cell, st.mul); | ||||
| 		cell = pm.module->addCell(NEW_ID, ID(SB_MAC16)); | ||||
| 		pm.module->swap_names(cell, st.mul); | ||||
| 	} | ||||
| 	else log_assert(cell->type == ID(SB_MAC16)); | ||||
| 
 | ||||
| 	// SB_MAC16 Input Interface
 | ||||
| 
 | ||||
| 	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; | ||||
| 	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; | ||||
| 	if (st.muxA) | ||||
| 		CD = st.muxA->getPort("\\B"); | ||||
| 	if (st.muxB) | ||||
| 		CD = st.muxB->getPort("\\A"); | ||||
| 	CD.extend_u0(32, mul_signed); | ||||
| 	SigSpec CD = st.sigCD; | ||||
| 	if (CD.empty()) | ||||
| 		CD = RTLIL::Const(0, 32); | ||||
| 	else | ||||
| 		log_assert(GetSize(CD) == 32); | ||||
| 
 | ||||
| 	cell->setPort("\\A", A); | ||||
| 	cell->setPort("\\B", B); | ||||
| 	cell->setPort("\\C", CD.extract(0, 16)); | ||||
| 	cell->setPort("\\D", CD.extract(16, 16)); | ||||
| 	cell->setPort(ID::A, A); | ||||
| 	cell->setPort(ID::B, B); | ||||
| 	cell->setPort(ID(C), CD.extract(16, 16)); | ||||
| 	cell->setPort(ID(D), CD.extract(0, 16)); | ||||
| 
 | ||||
| 	cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); | ||||
| 	cell->setParam(ID(A_REG), st.ffA ? 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); | ||||
| 	cell->setPort("\\BHOLD", State::S0); | ||||
| 	cell->setPort("\\CHOLD", State::S0); | ||||
| 	cell->setPort("\\DHOLD", State::S0); | ||||
| 	SigSpec AHOLD, BHOLD, CDHOLD; | ||||
| 	if (st.ffAholdmux) | ||||
| 		AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S))); | ||||
| 	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); | ||||
| 	cell->setPort("\\IRSTBOT", State::S0); | ||||
| 	SigSpec IRSTTOP, IRSTBOT; | ||||
| 	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("\\CE", State::S1); | ||||
| 		cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1); | ||||
| 		cell->setPort(ID(CLK), st.clock); | ||||
| 		cell->setPort(ID(CE), 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"); | ||||
| 
 | ||||
|  | @ -114,91 +140,137 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 		if (st.ffB) | ||||
| 			log(" ffB:%s", log_id(st.ffB)); | ||||
| 
 | ||||
| 		if (st.ffY) | ||||
| 			log(" ffY:%s", log_id(st.ffY)); | ||||
| 		if (st.ffCD) | ||||
| 			log(" ffCD:%s", log_id(st.ffCD)); | ||||
| 
 | ||||
| 		if (st.ffS) | ||||
| 			log(" ffS:%s", log_id(st.ffS)); | ||||
| 		if (st.ffFJKG) | ||||
| 			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"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		cell->setPort("\\CLK", State::S0); | ||||
| 		cell->setPort("\\CE", State::S0); | ||||
| 		cell->setParam("\\NEG_TRIGGER", State::S0); | ||||
| 		cell->setPort(ID(CLK), State::S0); | ||||
| 		cell->setPort(ID(CE), State::S0); | ||||
| 		cell->setParam(ID(NEG_TRIGGER), State::S0); | ||||
| 	} | ||||
| 
 | ||||
| 	// SB_MAC16 Cascade Interface
 | ||||
| 
 | ||||
| 	cell->setPort("\\SIGNEXTIN", State::Sx); | ||||
| 	cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); | ||||
| 	cell->setPort(ID(SIGNEXTIN), State::Sx); | ||||
| 	cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID)); | ||||
| 
 | ||||
| 	cell->setPort("\\CI", State::Sx); | ||||
| 	cell->setPort("\\CO", pm.module->addWire(NEW_ID)); | ||||
| 	cell->setPort(ID(CI), State::Sx); | ||||
| 
 | ||||
| 	cell->setPort("\\ACCUMCI", State::Sx); | ||||
| 	cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); | ||||
| 	cell->setPort(ID(ACCUMCI), State::Sx); | ||||
| 	cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID)); | ||||
| 
 | ||||
| 	// 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) | ||||
| 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); | ||||
| 
 | ||||
| 	cell->setPort("\\O", O); | ||||
| 	cell->setPort(ID(O), O); | ||||
| 
 | ||||
| 	if (st.addAB) { | ||||
| 		log("  accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); | ||||
| 		cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 		cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 	bool accum = false; | ||||
| 	if (st.add) { | ||||
| 		accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO); | ||||
| 		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 { | ||||
| 		cell->setPort("\\ADDSUBTOP", State::S0); | ||||
| 		cell->setPort("\\ADDSUBBOT", State::S0); | ||||
| 		cell->setPort(ID(ADDSUBTOP), State::S0); | ||||
| 		cell->setPort(ID(ADDSUBBOT), State::S0); | ||||
| 	} | ||||
| 
 | ||||
| 	cell->setPort("\\ORSTTOP", State::S0); | ||||
| 	cell->setPort("\\ORSTBOT", State::S0); | ||||
| 	SigSpec OHOLD; | ||||
| 	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); | ||||
| 	cell->setPort("\\OHOLDBOT", State::S0); | ||||
| 	SigSpec ORST; | ||||
| 	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; | ||||
| 	if (st.muxA) | ||||
| 		acc_reset = st.muxA->getPort("\\S"); | ||||
| 	if (st.muxB) | ||||
| 		acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S")); | ||||
| 
 | ||||
| 	cell->setPort("\\OLOADTOP", acc_reset); | ||||
| 	cell->setPort("\\OLOADBOT", acc_reset); | ||||
| 	if (st.mux) { | ||||
| 		if (st.muxAB == ID::A) | ||||
| 			acc_reset = st.mux->getPort(ID(S)); | ||||
| 		else | ||||
| 			acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S))); | ||||
| 	} | ||||
| 	cell->setPort(ID(OLOADTOP), acc_reset); | ||||
| 	cell->setPort(ID(OLOADBOT), acc_reset); | ||||
| 
 | ||||
| 	// SB_MAC16 Remaining Parameters
 | ||||
| 
 | ||||
| 	cell->setParam("\\C_REG", State::S0); | ||||
| 	cell->setParam("\\D_REG", State::S0); | ||||
| 	cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : 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("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); | ||||
| 	cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2)); | ||||
| 	cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1); | ||||
| 	cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2)); | ||||
| 
 | ||||
| 	cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); | ||||
| 	cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2)); | ||||
| 	cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1); | ||||
| 	cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2)); | ||||
| 
 | ||||
| 	cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); | ||||
| 	cell->setParam(ID(MODE_8x8), State::S0); | ||||
| 	cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool()); | ||||
| 	cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool()); | ||||
| 
 | ||||
| 	cell->setParam("\\MODE_8x8", State::S0); | ||||
| 	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 	if (st.ffO) { | ||||
| 		if (st.o_lo) | ||||
| 			cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2)); | ||||
| 		else | ||||
| 			cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2)); | ||||
| 
 | ||||
| 	pm.autoremove(st.mul); | ||||
| 	pm.autoremove(st.ffY); | ||||
| 	pm.autoremove(st.ffS); | ||||
| 		st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O))); | ||||
| 		cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2)); | ||||
| 	} | ||||
| 	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 { | ||||
|  | @ -209,7 +281,17 @@ struct Ice40DspPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log("    ice40_dsp [options] [selection]\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"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
|  |  | |||
|  | @ -1,163 +1,574 @@ | |||
| pattern ice40_dsp | ||||
| 
 | ||||
| state <SigBit> clock | ||||
| state <bool> clock_pol clock_vld | ||||
| state <SigSpec> sigA sigB sigY sigS | ||||
| state <Cell*> addAB muxAB | ||||
| state <bool> clock_pol cd_signed o_lo | ||||
| state <SigSpec> sigA sigB sigCD sigH sigO | ||||
| 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 | ||||
| 	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(\Y)) > 10 | ||||
| endmatch | ||||
| 
 | ||||
| match ffA | ||||
| 	select ffA->type.in($dff) | ||||
| 	// select nusers(port(ffA, \Q)) == 2 | ||||
| 	index <SigSpec> port(ffA, \Q) === port(mul, \A) | ||||
| 	optional | ||||
| endmatch | ||||
| code sigA sigB sigH | ||||
| 	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(mul, \A)); | ||||
| 	sigB = unextend(port(mul, \B)); | ||||
| 
 | ||||
| code sigA clock clock_pol clock_vld | ||||
| 	sigA = port(mul, \A); | ||||
| 	SigSpec O; | ||||
| 	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) { | ||||
| 		sigA = port(ffA, \D); | ||||
| 	// Only care about those bits that are used | ||||
| 	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(); | ||||
| 		clock_pol = param(ffA, \CLK_POLARITY).as_bool(); | ||||
| 		clock_vld = true; | ||||
| code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol | ||||
| 	if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { | ||||
| 		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 | ||||
| 
 | ||||
| match ffB | ||||
| 	select ffB->type.in($dff) | ||||
| 	// select nusers(port(ffB, \Q)) == 2 | ||||
| 	index <SigSpec> port(ffB, \Q) === port(mul, \B) | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| code sigB clock clock_pol clock_vld | ||||
| 	sigB = port(mul, \B); | ||||
| 
 | ||||
| 	if (ffB) { | ||||
| 		sigB = port(ffB, \D); | ||||
| 		SigBit c = port(ffB, \CLK).as_bit(); | ||||
| 		bool cp = param(ffB, \CLK_POLARITY).as_bool(); | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol | ||||
| 	if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { | ||||
| 		argQ = sigB; | ||||
| 		subpattern(in_dffe); | ||||
| 		if (dff) { | ||||
| 			ffB = dff; | ||||
| 			clock = dffclock; | ||||
| 			clock_pol = dffclock_pol; | ||||
| 			if (dffrstmux) { | ||||
| 				ffBrstmux = dffrstmux; | ||||
| 				ffBrstpol = dffrstpol; | ||||
| 			} | ||||
| 			if (dffholdmux) { | ||||
| 				ffBholdmux = dffholdmux; | ||||
| 				ffBholdpol = dffholdpol; | ||||
| 			} | ||||
| 			sigB = dffD; | ||||
| 		} | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match ffY | ||||
| 	select ffY->type.in($dff) | ||||
| 	select nusers(port(ffY, \D)) == 2 | ||||
| 	index <SigSpec> port(ffY, \D) === port(mul, \Y) | ||||
| 	optional | ||||
| endmatch | ||||
| code argD ffFJKG sigH clock clock_pol | ||||
| 	if (nusers(sigH) == 2 && | ||||
| 			(mul->type != \SB_MAC16 || | ||||
| 			 (!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()))) { | ||||
| 		argD = sigH; | ||||
| 		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 | ||||
| 	sigY = port(mul, \Y); | ||||
| 			// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT) | ||||
| 			//   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) { | ||||
| 		sigY = port(ffY, \Q); | ||||
| 		SigBit c = port(ffY, \CLK).as_bit(); | ||||
| 		bool cp = param(ffY, \CLK_POLARITY).as_bool(); | ||||
| 			ffFJKG = dff; | ||||
| 			clock = dffclock; | ||||
| 			clock_pol = dffclock_pol; | ||||
| 			sigH = dffQ; | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| reject_ffFJKG: 		; | ||||
| 		} | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match addA | ||||
| 	select addA->type.in($add) | ||||
| 	select nusers(port(addA, \A)) == 2 | ||||
| 	index <SigSpec> port(addA, \A) === sigY | ||||
| code argD ffH sigH sigO clock clock_pol | ||||
| 	if (ffFJKG && nusers(sigH) == 2 && | ||||
| 			(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { | ||||
| 		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 | ||||
| endmatch | ||||
| 
 | ||||
| match addB | ||||
| 	if !addA | ||||
| 	select addB->type.in($add, $sub) | ||||
| 	select nusers(port(addB, \B)) == 2 | ||||
| 	index <SigSpec> port(addB, \B) === sigY | ||||
| 	optional | ||||
| endmatch | ||||
| code sigCD sigO cd_signed | ||||
| 	if (add) { | ||||
| 		sigCD = port(add, addAB == \A ? \B : \A); | ||||
| 		cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool(); | ||||
| 
 | ||||
| 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 actual_mul_width = GetSize(sigY); | ||||
| 		int actual_acc_width = GetSize(sigS); | ||||
| 		int actual_mul_width = GetSize(sigH); | ||||
| 		int actual_acc_width = GetSize(sigCD); | ||||
| 
 | ||||
| 		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) | ||||
| 			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; | ||||
| 
 | ||||
| 		sigO = port(add, \Y); | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| match muxA | ||||
| 	if addAB | ||||
| 	select muxA->type.in($mux) | ||||
| 	select nusers(port(muxA, \A)) == 2 | ||||
| 	index <SigSpec> port(muxA, \A) === port(addAB, \Y) | ||||
| match mux | ||||
| 	select mux->type == $mux | ||||
| 	choice <IdString> AB {\A, \B} | ||||
| 	select nusers(port(mux, AB)) == 2 | ||||
| 	index <SigSpec> port(mux, AB) === sigO | ||||
| 	set muxAB AB | ||||
| 	optional | ||||
| endmatch | ||||
| 
 | ||||
| match muxB | ||||
| 	if addAB | ||||
| 	if !muxA | ||||
| 	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; | ||||
| code sigO | ||||
| 	if (mux) | ||||
| 		sigO = port(mux, \Y); | ||||
| endcode | ||||
| 
 | ||||
| match ffS | ||||
| 	if muxAB | ||||
| 	select ffS->type.in($dff) | ||||
| 	select nusers(port(ffS, \D)) == 2 | ||||
| 	index <SigSpec> port(ffS, \D) === port(muxAB, \Y) | ||||
| 	index <SigSpec> port(ffS, \Q) === sigS | ||||
| endmatch | ||||
| code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo | ||||
| 	if (mul->type != \SB_MAC16 || | ||||
| 			// Ensure that register is not already used | ||||
| 			((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) && | ||||
| 			 // Ensure that OLOADTOP/OLOADBOT is unused or zero | ||||
| 			 (port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) { | ||||
| 
 | ||||
| code clock clock_pol clock_vld | ||||
| 	if (ffS) { | ||||
| 		SigBit c = port(ffS, \CLK).as_bit(); | ||||
| 		bool cp = param(ffS, \CLK_POLARITY).as_bool(); | ||||
| 		dff = nullptr; | ||||
| 
 | ||||
| 		if (clock_vld && (c != clock || cp != clock_pol)) | ||||
| 			reject; | ||||
| 		// First try entire sigO | ||||
| 		if (nusers(sigO) == 2) { | ||||
| 			argD = sigO; | ||||
| 			subpattern(out_dffe); | ||||
| 		} | ||||
| 
 | ||||
| 		clock = c; | ||||
| 		clock_pol = cp; | ||||
| 		clock_vld = true; | ||||
| 		// Otherwise try just its least significant 16 bits | ||||
| 		if (!dff && GetSize(sigO) > 16) { | ||||
| 			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; | ||||
| 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, \I2) === port(carry, \I1) | ||||
| endmatch | ||||
| 
 | ||||
| code | ||||
| 	accept; | ||||
| endcode | ||||
|  |  | |||
|  | @ -24,8 +24,11 @@ USING_YOSYS_NAMESPACE | |||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| bool did_something; | ||||
| dict<SigBit, State> initbits; | ||||
| pool<SigBit> rminitbits; | ||||
| 
 | ||||
| #include "passes/pmgen/peepopt_pm.h" | ||||
| #include "generate.h" | ||||
| 
 | ||||
| struct PeepoptPass : public Pass { | ||||
| 	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 | ||||
| 	{ | ||||
| 		std::string genmode; | ||||
| 
 | ||||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||
| 
 | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
| 			// if (args[argidx] == "-singleton") {
 | ||||
| 			// 	singleton_mode = true;
 | ||||
| 			// 	continue;
 | ||||
| 			// }
 | ||||
| 			if (args[argidx] == "-generate" && argidx+1 < args.size()) { | ||||
| 				genmode = args[++argidx]; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		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; | ||||
| 			while (did_something) { | ||||
| 
 | ||||
| 			while (did_something) | ||||
| 			{ | ||||
| 				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_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())) | ||||
|                     break | ||||
| 
 | ||||
|                 assert False | ||||
|                 raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr)) | ||||
| 
 | ||||
|             if block["optional"]: | ||||
|                 assert not block["semioptional"] | ||||
|  | @ -305,7 +305,8 @@ def process_pmgfile(f, filename): | |||
|             block["states"] = set() | ||||
| 
 | ||||
|             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) | ||||
| 
 | ||||
|             codetype = "code" | ||||
|  | @ -327,7 +328,7 @@ def process_pmgfile(f, filename): | |||
|             blocks.append(block) | ||||
|             continue | ||||
| 
 | ||||
|         assert False | ||||
|         raise RuntimeError("'%s' command not recognised" % cmd) | ||||
| 
 | ||||
| for fn in pmgfiles: | ||||
|     with open(fn, "r") as f: | ||||
|  | @ -361,6 +362,7 @@ with open(outfile, "w") as f: | |||
|     print("  Module *module;", file=f) | ||||
|     print("  SigMap sigmap;", file=f) | ||||
|     print("  std::function<void()> on_accept;", file=f) | ||||
|     print("  bool setup_done;", file=f) | ||||
|     print("  bool generate_mode;", file=f) | ||||
|     print("  int accept_cnt;", file=f) | ||||
|     print("", file=f) | ||||
|  | @ -452,11 +454,19 @@ with open(outfile, "w") as f: | |||
|     print("    return sigmap(cell->getPort(portname));", 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("    return cell->getParam(paramname);", 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("    pool<Cell*> users;", file=f) | ||||
|  | @ -468,7 +478,17 @@ with open(outfile, "w") as f: | |||
|     print("", 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 s, t in sorted(udata_types[current_pattern].items()): | ||||
|             if t.endswith("*"): | ||||
|  | @ -476,6 +496,8 @@ with open(outfile, "w") as f: | |||
|             else: | ||||
|                 print("    ud_{}.{} = {}();".format(current_pattern, s, t), file=f) | ||||
|     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("      add_siguser(module->wire(port), nullptr);", 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()): | ||||
|         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("    on_accept = on_accept_f;", file=f) | ||||
|         print("    rollback = 0;", file=f) | ||||
|  |  | |||
|  | @ -23,13 +23,11 @@ | |||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| // for peepopt_pm
 | ||||
| bool did_something; | ||||
| 
 | ||||
| #include "passes/pmgen/test_pmgen_pm.h" | ||||
| #include "passes/pmgen/ice40_dsp_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) | ||||
| { | ||||
|  | @ -118,123 +116,6 @@ void opt_eqpmux(test_pmgen_pm &pm) | |||
| 	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 { | ||||
| 	TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { } | ||||
| 	void help() YS_OVERRIDE | ||||
|  | @ -355,12 +236,6 @@ struct TestPmgenPass : public Pass { | |||
| 		if (pattern == "xilinx_srl.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()); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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 | ||||
| 	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->type.in(\FDRE) || !first->parameters.at(\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, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | ||||
| 	select !first->type.in(\FDRE) || !param(first, \IS_R_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) || port(first, \R, State::S0).is_fully_zero() | ||||
| 	filter !non_first_cells.count(first) | ||||
| generate | ||||
| 	SigSpec C = module->addWire(NEW_ID); | ||||
|  | @ -84,9 +84,9 @@ arg en_port | |||
| match first | ||||
| 	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->type.in(\FDRE) || !first->parameters.at(\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, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | ||||
| 	select !first->type.in(\FDRE) || !param(first, \IS_R_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) || port(first, \R, State::S0).is_fully_zero() | ||||
| endmatch | ||||
| 
 | ||||
| code clk_port en_port | ||||
|  | @ -111,10 +111,10 @@ match next | |||
| 	index <SigBit> port(next, \Q) === port(first, \D) | ||||
| 	filter port(next, clk_port) == port(first, clk_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) || 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) || 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, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | ||||
| 	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) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_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) || port(next, \R, State::S0).is_fully_zero() | ||||
| endmatch | ||||
| 
 | ||||
| code | ||||
|  | @ -138,10 +138,10 @@ match next | |||
| 	index <SigBit> port(next, \Q) === port(chain.back(), \D) | ||||
| 	filter port(next, clk_port) == port(first, clk_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) || 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) || 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, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | ||||
| 	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) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_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) || port(next, \R, State::S0).is_fully_zero() | ||||
| generate | ||||
| 	Cell *cell = module->addCell(NEW_ID, chain.back()->type); | ||||
| 	cell->setPort(\C, chain.back()->getPort(\C)); | ||||
|  | @ -149,7 +149,7 @@ generate | |||
| 	cell->setPort(\Q, chain.back()->getPort(\D)); | ||||
| 	if (cell->type == \FDRE) { | ||||
| 		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)); | ||||
| 	} | ||||
| 	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); | ||||
| 					} | ||||
| 
 | ||||
| 					cell->setPort("\\D", sig_q); | ||||
| 					cell->setPort("\\Q", new_q); | ||||
| 					cell->unsetPort("\\EN"); | ||||
| 					cell->unsetParam("\\EN_POLARITY"); | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ OBJS += passes/techmap/attrmap.o | |||
| OBJS += passes/techmap/zinit.o | ||||
| OBJS += passes/techmap/dff2dffs.o | ||||
| OBJS += passes/techmap/flowmap.o | ||||
| OBJS += passes/techmap/extractinv.o | ||||
| endif | ||||
| 
 | ||||
| GENFILES += passes/techmap/techmap.inc | ||||
|  |  | |||
|  | @ -71,22 +71,21 @@ RTLIL::Module *module; | |||
| bool clk_polarity, en_polarity; | ||||
| 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, | ||||
| 		const dict<IdString,pool<IdString>> &scc_break_inputs) | ||||
| void handle_loops(RTLIL::Design *design) | ||||
| { | ||||
| 	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
 | ||||
| 	// cell in the component, and select (and mark) all its output
 | ||||
| 	// wires
 | ||||
| 	pool<RTLIL::Const> ids_seen; | ||||
| 	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()) { | ||||
| 			auto r = ids_seen.insert(it->second); | ||||
| 			if (r.second) { | ||||
|  | @ -106,7 +105,7 @@ void handle_loops(RTLIL::Design *design, | |||
| 							log_assert(w->port_input); | ||||
| 							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); | ||||
| 						c.second = RTLIL::SigBit(w, b.offset); | ||||
| 					} | ||||
|  | @ -114,36 +113,12 @@ void handle_loops(RTLIL::Design *design, | |||
| 			} | ||||
| 			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(); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	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; | ||||
| } | ||||
| 
 | ||||
| std::string fold_abc_cmd(std::string str) | ||||
| std::string fold_abc9_cmd(std::string str) | ||||
| { | ||||
| 	std::string token, new_str = "          "; | ||||
| 	int char_counter = 10; | ||||
|  | @ -209,7 +184,7 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho | |||
| 	return text; | ||||
| } | ||||
| 
 | ||||
| struct abc_output_filter | ||||
| struct abc9_output_filter | ||||
| { | ||||
| 	bool got_cr; | ||||
| 	int escape_seq_state; | ||||
|  | @ -217,7 +192,7 @@ struct abc_output_filter | |||
| 	std::string tempdir_name; | ||||
| 	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; | ||||
| 		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 /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, | ||||
| 		bool show_tempdir, std::string box_file, std::string lut_file, | ||||
| 		std::string wire_delay, const dict<int,IdString> &box_lookup, | ||||
| 		const dict<IdString,pool<IdString>> &scc_break_inputs | ||||
| 		std::string wire_delay, const dict<int,IdString> &box_lookup, bool nomfs | ||||
| ) | ||||
| { | ||||
| 	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", | ||||
| 			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()) { | ||||
| 		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()) | ||||
| 			abc_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||
| 			abc9_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||
| 	} | ||||
| 	else | ||||
| 	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()) | ||||
| 			abc_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||
| 			abc9_script += stringf("read_box -v %s; ", box_file.c_str()); | ||||
| 	} | ||||
| 	else | ||||
| 		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[0] == '+') { | ||||
| 			for (size_t i = 1; i < script_file.size(); i++) | ||||
| 				if (script_file[i] == '\'') | ||||
| 					abc_script += "'\\''"; | ||||
| 					abc9_script += "'\\''"; | ||||
| 				else if (script_file[i] == ',') | ||||
| 					abc_script += " "; | ||||
| 					abc9_script += " "; | ||||
| 				else | ||||
| 					abc_script += script_file[i]; | ||||
| 					abc9_script += script_file[i]; | ||||
| 		} 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()) { | ||||
| 		//bool all_luts_cost_same = true;
 | ||||
| 		//for (int this_cost : lut_costs)
 | ||||
| 		//	if (this_cost != lut_costs.front())
 | ||||
| 		//		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)
 | ||||
| 		//	abc_script += "; lutpack {S}";
 | ||||
| 		//	abc9_script += "; lutpack {S}";
 | ||||
| 	} else | ||||
| 		log_abort(); | ||||
| 
 | ||||
| 	//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))
 | ||||
| 	//		abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
 | ||||
| 	//	for (size_t pos = abc9_script.find("dretime;"); pos != std::string::npos; pos = abc9_script.find("dretime;", pos+1))
 | ||||
| 	//		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)) | ||||
| 		abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); | ||||
| 	for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) | ||||
| 		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))
 | ||||
| 	//	abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
 | ||||
| 	//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
 | ||||
| 	//	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)) | ||||
| 		abc_script = abc_script.substr(0, pos) + wire_delay + abc_script.substr(pos+3); | ||||
| 	for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) | ||||
| 		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()); | ||||
| 	abc_script = add_echos_to_abc_cmd(abc_script); | ||||
| 	if (nomfs) | ||||
| 		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++) | ||||
| 		if (abc_script[i] == ';' && abc_script[i+1] == ' ') | ||||
| 			abc_script[i+1] = '\n'; | ||||
| 	abc9_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); | ||||
| 	abc9_script = add_echos_to_abc9_cmd(abc9_script); | ||||
| 
 | ||||
| 	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"); | ||||
| 	fprintf(f, "%s\n", abc_script.c_str()); | ||||
| 	fprintf(f, "%s\n", abc9_script.c_str()); | ||||
| 	fclose(f); | ||||
| 
 | ||||
| 	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(); | ||||
| 		sel.select(module); | ||||
| 
 | ||||
| 		handle_loops(design, scc_break_inputs); | ||||
| 		handle_loops(design); | ||||
| 
 | ||||
| 		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
 | ||||
| 		// and re-connecting them back together
 | ||||
| 		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()) { | ||||
| 				wire->attributes.erase(it); | ||||
| 				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()); | ||||
| 
 | ||||
| #ifndef YOSYS_LINK_ABC | ||||
| 		abc_output_filter filt(tempdir_name, show_tempdir); | ||||
| 		int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); | ||||
| 		abc9_output_filter filt(tempdir_name, show_tempdir); | ||||
| 		int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); | ||||
| #else | ||||
| 		// 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()); | ||||
| 		abc_argv[0] = strdup(exe_file.c_str()); | ||||
| 		abc_argv[1] = strdup("-s"); | ||||
| 		abc_argv[2] = strdup("-f"); | ||||
| 		abc_argv[3] = strdup(tmp_script_name.c_str()); | ||||
| 		abc_argv[4] = 0; | ||||
| 		int ret = Abc_RealMain(4, abc_argv); | ||||
| 		free(abc_argv[0]); | ||||
| 		free(abc_argv[1]); | ||||
| 		free(abc_argv[2]); | ||||
| 		free(abc_argv[3]); | ||||
| 		abc9_argv[0] = strdup(exe_file.c_str()); | ||||
| 		abc9_argv[1] = strdup("-s"); | ||||
| 		abc9_argv[2] = strdup("-f"); | ||||
| 		abc9_argv[3] = strdup(tmp_script_name.c_str()); | ||||
| 		abc9_argv[4] = 0; | ||||
| 		int ret = Abc_RealMain(4, abc9_argv); | ||||
| 		free(abc9_argv[0]); | ||||
| 		free(abc9_argv[1]); | ||||
| 		free(abc9_argv[2]); | ||||
| 		free(abc9_argv[3]); | ||||
| #endif | ||||
| 		if (ret != 0) | ||||
| 			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"); | ||||
| 		ifs.open(buffer); | ||||
| 		ifs.open(buffer, std::ifstream::binary); | ||||
| 		if (ifs.fail()) | ||||
| 			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); | ||||
| 		} | ||||
| 
 | ||||
| 		dict<IdString, bool> abc_box; | ||||
| 		dict<IdString, bool> abc9_box; | ||||
| 		vector<RTLIL::Cell*> boxes; | ||||
| 		for (const auto &it : module->cells_) { | ||||
| 			auto cell = it.second; | ||||
|  | @ -547,10 +525,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri | |||
| 				module->remove(cell); | ||||
| 				continue; | ||||
| 			} | ||||
| 			auto jt = abc_box.find(cell->type); | ||||
| 			if (jt == abc_box.end()) { | ||||
| 			auto jt = abc9_box.find(cell->type); | ||||
| 			if (jt == abc9_box.end()) { | ||||
| 				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) | ||||
| 				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); | ||||
| 				log_assert(existing_cell); | ||||
| 				cell = module->addCell(remap_name(c->name), c->type); | ||||
| 				module->swap_names(cell, existing_cell); | ||||
| 			} | ||||
| 
 | ||||
| 			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) | ||||
| 			module->remove(cell); | ||||
| 		for (auto existing_cell : boxes) { | ||||
| 			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
 | ||||
| 		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("\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("        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("    -fast\n"); | ||||
| 		log("        use different default scripts that are slightly faster (at the cost\n"); | ||||
| 		log("        of output quality):\n"); | ||||
| 		log("\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("    -D <picoseconds>\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; | ||||
| 		bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; | ||||
| 		bool show_tempdir = false; | ||||
| 		bool nomfs = false; | ||||
| 		vector<int> lut_costs; | ||||
| 		markgroups = false; | ||||
| 
 | ||||
|  | @ -1050,23 +1042,31 @@ struct Abc9Pass : public Pass { | |||
| 			} | ||||
| 			if (arg == "-box" && argidx+1 < args.size()) { | ||||
| 				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; | ||||
| 			} | ||||
| 			if (arg == "-W" && argidx+1 < args.size()) { | ||||
| 				wire_delay = "-W " + args[++argidx]; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (arg == "-nomfs") { | ||||
| 				nomfs = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		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<IdString,pool<IdString>> scc_break_inputs; | ||||
| 		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()) | ||||
| 				continue; | ||||
| 			if (m->name.begins_with("$paramod")) | ||||
|  | @ -1074,7 +1074,7 @@ struct Abc9Pass : public Pass { | |||
| 			auto id = it->second.as_int(); | ||||
| 			auto r = box_lookup.insert(std::make_pair(id, m->name)); | ||||
| 			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_assert(r.second); | ||||
| 
 | ||||
|  | @ -1082,28 +1082,24 @@ struct Abc9Pass : public Pass { | |||
| 			for (auto p : m->ports) { | ||||
| 				auto w = m->wire(p); | ||||
| 				log_assert(w); | ||||
| 				if (w->port_input) { | ||||
| 					if (w->attributes.count(ID(abc_scc_break))) | ||||
| 						scc_break_inputs[m->name].insert(p); | ||||
| 					if (w->attributes.count(ID(abc_carry))) { | ||||
| 				if (w->attributes.count(ID(abc9_carry))) { | ||||
| 					if (w->port_input) { | ||||
| 						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; | ||||
| 					} | ||||
| 				} | ||||
| 				if (w->port_output) { | ||||
| 					if (w->attributes.count(ID(abc_carry))) { | ||||
| 					else if (w->port_output) { | ||||
| 						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; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			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) | ||||
| 					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
 | ||||
| 				//   since ABC requires it this way
 | ||||
| 				auto &ports = m->ports; | ||||
|  | @ -1131,7 +1127,7 @@ struct Abc9Pass : public Pass { | |||
| 
 | ||||
| 		for (auto mod : design->selected_modules()) | ||||
| 		{ | ||||
| 			if (mod->attributes.count(ID(abc_box_id))) | ||||
| 			if (mod->attributes.count(ID(abc9_box_id))) | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (mod->processes.size() > 0) { | ||||
|  | @ -1144,7 +1140,7 @@ struct Abc9Pass : public Pass { | |||
| 			if (!dff_mode || !clk_str.empty()) { | ||||
| 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, 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); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
|  | @ -1290,7 +1286,7 @@ struct Abc9Pass : public Pass { | |||
| 				en_sig = assign_map(std::get<3>(it.first)); | ||||
| 				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$", | ||||
| 						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); | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ struct AigmapPass : public Pass { | |||
| 	AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } | ||||
| 	void help() YS_OVERRIDE | ||||
| 	{ | ||||
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||
| 		log("\n"); | ||||
| 		log("    aigmap [options] [selection]\n"); | ||||
| 		log("\n"); | ||||
|  | @ -36,10 +37,15 @@ struct AigmapPass : public Pass { | |||
| 		log("    -nand\n"); | ||||
| 		log("        Enable creation of $_NAND_ cells\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 | ||||
| 	{ | ||||
| 		bool nand_mode = false; | ||||
| 		bool nand_mode = false, select_mode = false; | ||||
| 
 | ||||
| 		log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); | ||||
| 
 | ||||
|  | @ -50,6 +56,10 @@ struct AigmapPass : public Pass { | |||
| 				nand_mode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-select") { | ||||
| 				select_mode = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
|  | @ -62,6 +72,7 @@ struct AigmapPass : public Pass { | |||
| 			dict<IdString, int> stat_not_replaced; | ||||
| 			int orig_num_cells = GetSize(module->cells()); | ||||
| 
 | ||||
| 			pool<IdString> new_sel; | ||||
| 			for (auto cell : module->selected_cells()) | ||||
| 			{ | ||||
| 				Aig aig(cell); | ||||
|  | @ -75,6 +86,8 @@ struct AigmapPass : public Pass { | |||
| 				if (aig.name.empty()) { | ||||
| 					not_replaced_count++; | ||||
| 					stat_not_replaced[cell->type]++; | ||||
| 					if (select_mode) | ||||
| 						new_sel.insert(cell->name); | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
|  | @ -95,19 +108,33 @@ struct AigmapPass : public Pass { | |||
| 						SigBit A = sigs.at(node.left_parent); | ||||
| 						SigBit B = sigs.at(node.right_parent); | ||||
| 						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; | ||||
| 						} else { | ||||
| 							pair<int, int> key(node.left_parent, node.right_parent); | ||||
| 							if (and_cache.count(key)) | ||||
| 								bit = and_cache.at(key); | ||||
| 							else | ||||
| 								bit = module->AndGate(NEW_ID, A, B); | ||||
| 							else { | ||||
| 								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) | ||||
| 						bit = module->NotGate(NEW_ID, bit); | ||||
| 					if (node.inverter) { | ||||
| 						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: | ||||
| 					for (auto &op : node.outports) | ||||
|  | @ -142,6 +169,13 @@ struct AigmapPass : public Pass { | |||
| 
 | ||||
| 			for (auto cell : replaced_cells) | ||||
| 				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; | ||||
|  |  | |||
|  | @ -48,14 +48,25 @@ struct AlumaccWorker | |||
| 		RTLIL::SigSpec cached_cf, cached_of, cached_sf; | ||||
| 
 | ||||
| 		RTLIL::SigSpec get_lt() { | ||||
| 			if (GetSize(cached_lt) == 0) | ||||
| 				cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf(); | ||||
| 			if (GetSize(cached_lt) == 0) { | ||||
| 				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; | ||||
| 		} | ||||
| 
 | ||||
| 		RTLIL::SigSpec get_gt() { | ||||
| 			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()); | ||||
| 			if (GetSize(cached_gt) == 0) { | ||||
| 				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; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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("dff2dffe for SR over CE priority.\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 | ||||
| 	{ | ||||
| 		log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); | ||||
| 
 | ||||
| 		bool match_init = false; | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
|  | @ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass { | |||
| 			// 	singleton_mode = true;
 | ||||
| 			// 	continue;
 | ||||
| 			// }
 | ||||
| 			if (args[argidx] == "-match-init") { | ||||
| 				match_init = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
|  | @ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass { | |||
| 				SigBit bit_b = sigmap(mux_cell->getPort(ID::B)); | ||||
| 				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; | ||||
| 				bool invert_sr; | ||||
| 				sr_sig = bit_s; | ||||
|  | @ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass { | |||
| 					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 (cell->type == ID($_DFF_N_)) { | ||||
| 						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; | ||||
| 		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) | ||||
| 				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; | ||||
| 			apply_prefix(cell->name, w_name); | ||||
| 			RTLIL::Wire *w = module->addWire(w_name, it.second); | ||||
| 			w->port_input = false; | ||||
| 			w->port_output = false; | ||||
| 			w->port_id = 0; | ||||
| 			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); | ||||
| 			RTLIL::Wire *w = module->wire(w_name); | ||||
| 			if (w != nullptr) { | ||||
| 				if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) { | ||||
| 					temp_renamed_wires[w] = w->name; | ||||
| 					module->rename(w, NEW_ID); | ||||
| 					w = nullptr; | ||||
| 				} else { | ||||
| 					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); | ||||
| 
 | ||||
| 			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); | ||||
|  | @ -322,6 +365,12 @@ struct TechmapWorker | |||
| 				for (auto &attr : w->attributes) { | ||||
| 					if (attr.first == ID(src)) | ||||
| 						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); | ||||
| 					break; | ||||
| 				} | ||||
|  | @ -335,6 +384,8 @@ struct TechmapWorker | |||
| 
 | ||||
| 			if (techmap_replace_cell) | ||||
| 				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 | ||||
| 				apply_prefix(cell->name, c_name); | ||||
| 
 | ||||
|  | @ -344,11 +395,31 @@ struct TechmapWorker | |||
| 			if (!flatten_mode && c->type.begins_with("\\$")) | ||||
| 				c->type = c->type.substr(1); | ||||
| 
 | ||||
| 			for (auto &it2 : c->connections_) { | ||||
| 				apply_prefix(cell->name, it2.second, module); | ||||
| 				port_signal_map.apply(it2.second); | ||||
| 			vector<IdString> autopurge_ports; | ||||
| 
 | ||||
| 			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))) { | ||||
| 				IdString memid = c->getParam(ID(MEMID)).decode_string(); | ||||
| 				log_assert(memory_renames.count(memid) != 0); | ||||
|  | @ -380,6 +451,16 @@ struct TechmapWorker | |||
| 		} | ||||
| 
 | ||||
| 		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, | ||||
|  | @ -396,6 +477,18 @@ struct TechmapWorker | |||
| 
 | ||||
| 		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; | ||||
| 		std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; | ||||
| 		std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; | ||||
|  | @ -633,6 +726,17 @@ struct TechmapWorker | |||
| 									bit = RTLIL::SigBit(RTLIL::State::Sx); | ||||
| 							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; | ||||
|  | @ -833,7 +937,7 @@ struct TechmapWorker | |||
| 
 | ||||
| 					TechmapWires twd = techmap_find_special_wires(tpl); | ||||
| 					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()); | ||||
| 						if (techmap_do_cache[tpl]) | ||||
| 							for (auto &it2 : it.second) | ||||
|  | @ -864,6 +968,23 @@ struct TechmapWorker | |||
| 					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) | ||||
| 				{ | ||||
| 					std::string m_name = stringf("$extern:%s", log_id(tpl)); | ||||
|  | @ -907,6 +1028,25 @@ struct TechmapWorker | |||
| 			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) { | ||||
| 			log_header(design, "Continuing TECHMAP pass.\n"); | ||||
| 			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("attribute is set to on the wrapper module.\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("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"); | ||||
|  | @ -1019,6 +1164,13 @@ struct TechmapPass : public Pass { | |||
| 		log("\n"); | ||||
| 		log("        It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\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("modules in the map file:\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("        value for this bit. The unused bits of the latter are set to undef (x).\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_CONNMAP_<port-name>_\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("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("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("See 'help extract' for a pass that does the opposite thing.\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("low in order to explore more inner states in a state machine.\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("        number of iterations the test bench should run (default = 1000)\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 | ||||
| 	{ | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| module AL_MAP_SEQ ( | ||||
| 	output q, | ||||
| 	output reg q, | ||||
| 	input ce, | ||||
| 	input clk, | ||||
| 	input sr, | ||||
|  | @ -9,6 +9,71 @@ module AL_MAP_SEQ ( | |||
| 	parameter REGSET = "RESET"; //RESET/SET | ||||
| 	parameter SRMUX = "SR"; //SR/INV | ||||
| 	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 | ||||
| 
 | ||||
| module AL_MAP_LUT1 ( | ||||
|  | @ -17,7 +82,8 @@ module AL_MAP_LUT1 ( | |||
| ); | ||||
| 	parameter [1:0] INIT = 2'h0; | ||||
| 	parameter EQN = "(A)"; | ||||
| 	assign o = INIT >> a; | ||||
| 
 | ||||
| 	assign o = a ? INIT[1] : INIT[0];	 | ||||
| endmodule | ||||
| 
 | ||||
| module AL_MAP_LUT2 ( | ||||
|  | @ -27,7 +93,9 @@ module AL_MAP_LUT2 ( | |||
| ); | ||||
| 	parameter [3:0] INIT = 4'h0; | ||||
| 	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 | ||||
| 
 | ||||
| module AL_MAP_LUT3 ( | ||||
|  | @ -38,7 +106,10 @@ module AL_MAP_LUT3 ( | |||
| ); | ||||
| 	parameter [7:0] INIT = 8'h0; | ||||
| 	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 | ||||
| 
 | ||||
| module AL_MAP_LUT4 ( | ||||
|  | @ -50,7 +121,11 @@ module AL_MAP_LUT4 ( | |||
| ); | ||||
| 	parameter [15:0] INIT = 16'h0; | ||||
| 	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 | ||||
| 
 | ||||
| module AL_MAP_LUT5 ( | ||||
|  | @ -100,4 +175,18 @@ module AL_MAP_ADDER ( | |||
|   output [1:0] o | ||||
| ); | ||||
| 	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 | ||||
|  |  | |||
|  | @ -9,12 +9,12 @@ GENFILES += techlibs/common/simcells_help.inc | |||
| 
 | ||||
| techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v | ||||
| 	$(Q) mkdir -p techlibs/common | ||||
| 	$(P) python3 $^ > $@.new | ||||
| 	$(P) $(PYTHON_EXECUTABLE) $^ > $@.new | ||||
| 	$(Q) mv $@.new $@ | ||||
| 
 | ||||
| techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v | ||||
| 	$(Q) mkdir -p techlibs/common | ||||
| 	$(P) python3 $^ > $@.new | ||||
| 	$(P) $(PYTHON_EXECUTABLE) $^ > $@.new | ||||
| 	$(Q) mv $@.new $@ | ||||
| 
 | ||||
| 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/cmp2lut.v)) | ||||
| $(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_9.vh | ||||
| bram_conn_18.vh | ||||
| bram_conn_36.vh | ||||
| 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/arith_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/abc_5g.lut)) | ||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut)) | ||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_map.v)) | ||||
| $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_unmap.v)) | ||||
| $(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 | ||||
| .SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk | ||||
| 
 | ||||
| techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py | ||||
| 	$(Q) mkdir -p techlibs/ecp5 | ||||
| 	$(P) python3 $< | ||||
| 	$(P) $(PYTHON_EXECUTABLE) $< | ||||
| 	$(Q) touch $@ | ||||
| 
 | ||||
| techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py | ||||
| 	$(Q) mkdir -p techlibs/ecp5 | ||||
| 	$(P) python3 $< | ||||
| 	$(P) $(PYTHON_EXECUTABLE) $< | ||||
| 	$(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_9.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_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_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_36.vh)) | ||||
|  |  | |||
|  | @ -15,16 +15,16 @@ CCU2C   1      1   9      3 | |||
| 630  379  630  379  526   275  392  141  273 | ||||
| 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 | ||||
| # name            ID  w/b   ins   outs | ||||
| TRELLIS_DPR16X4   2     0   14    4 | ||||
| # name               ID  w/b   ins   outs | ||||
| $__ABC9_DPR16X4_COMB  2     0   8    4 | ||||
| 
 | ||||
| #DI0   DI1   DI2   DI3   RAD0   RAD1   RAD2   RAD3   WAD0    WAD1   WAD2   WAD3  WCK   WRE | ||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | ||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | ||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | ||||
| -      -     -     -     141    379    275    379    -       -      -      -     -     - | ||||
| #A0   A1   A2   A3   RAD0   RAD1   RAD2   RAD3 | ||||
| 0     0    0    0    141    379    275    379 | ||||
| 0     0    0    0    141    379    275    379 | ||||
| 0     0    0    0    141    379    275    379 | ||||
| 0     0    0    0    141    379    275    379 | ||||
| 
 | ||||
| # Box 3 : PFUMX (MUX2) | ||||
| # 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 | ||||
|   init 1 | ||||
| 
 | ||||
|  | @ -22,8 +37,16 @@ bram $__ECP5_DP16KD | |||
|   clkpol 2 3 | ||||
| endbram | ||||
| 
 | ||||
| match $__ECP5_PDPW16KD | ||||
|   min bits 2048 | ||||
|   min efficiency 5 | ||||
|   shuffle_enable A | ||||
|   make_transp | ||||
|   or_next_if_better | ||||
| endmatch | ||||
| 
 | ||||
| match $__ECP5_DP16KD | ||||
|   min bits 2048 | ||||
|   min efficiency 5 | ||||
|   shuffle_enable B | ||||
|   shuffle_enable A | ||||
| 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(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: | ||||
|     ada_bits = ["A1ADDR[%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)] | ||||
|     dob_bits = ["B1DATA[%d]" % i for i in range(18)] | ||||
|     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; | ||||
| 	end endgenerate | ||||
| 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 | ||||
| 
 | ||||
| (* blackbox *) | ||||
| module ECLKBRIDGECS( | ||||
| 	input CLK0, CLK1, SEL, | ||||
| 	output ECSOUT | ||||
| ); | ||||
| endmodule | ||||
| 
 | ||||
| (* blackbox *) | ||||
| module DCCA( | ||||
| 	input CLKI, CE, | ||||
|  | @ -683,4 +690,98 @@ endmodule | |||
| module SGSR ( | ||||
| 	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 | ||||
| 
 | ||||
| // 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 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 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 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 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"; (* 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"; (* 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"; (* 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 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 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 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 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"; (* 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"; (* 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"; (* 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 | ||||
| // module IFS1S1B(input PD, D, SCLK, output Q); endmodule | ||||
|  |  | |||
|  | @ -9,19 +9,19 @@ module LUT4(input A, B, C, D, output Z); | |||
| endmodule | ||||
| 
 | ||||
| // --------------------------------------- | ||||
| (* abc_box_id=4, lib_whitebox *) | ||||
| (* abc9_box_id=4, lib_whitebox *) | ||||
| module L6MUX21 (input D0, D1, SD, output Z); | ||||
| 	assign Z = SD ? D1 : D0; | ||||
| endmodule | ||||
| 
 | ||||
| // --------------------------------------- | ||||
| (* abc_box_id=1, lib_whitebox *) | ||||
| (* abc9_box_id=1, lib_whitebox *) | ||||
| module CCU2C( | ||||
| 	(* abc_carry *) | ||||
| 	(* abc9_carry *) | ||||
| 	input  CIN, | ||||
| 	input  A0, B0, C0, D0, A1, B1, C1, D1, | ||||
| 	output S0, S1, | ||||
| 	(* abc_carry *) | ||||
| 	(* abc9_carry *) | ||||
| 	output COUT | ||||
| ); | ||||
| 	parameter [15:0] INIT0 = 16'h0000; | ||||
|  | @ -103,22 +103,19 @@ module TRELLIS_RAM16X2 ( | |||
| endmodule | ||||
| 
 | ||||
| // --------------------------------------- | ||||
| (* abc_box_id=3, lib_whitebox *) | ||||
| (* abc9_box_id=3, lib_whitebox *) | ||||
| module PFUMX (input ALUT, BLUT, C0, output Z); | ||||
| 	assign Z = C0 ? ALUT : BLUT; | ||||
| endmodule | ||||
| 
 | ||||
| // --------------------------------------- | ||||
| //(* abc_box_id=2 *) | ||||
| module TRELLIS_DPR16X4 ( | ||||
| 	(* abc_scc_break *) | ||||
| 	input  [3:0] DI, | ||||
| 	(* abc_scc_break *) | ||||
| 	input  [3:0] WAD, | ||||
| 	(* abc_scc_break *) | ||||
| 	input        WRE, | ||||
| 	input        WCK, | ||||
| 	input  [3:0] RAD, | ||||
| 	/* (* abc9_arrival=<TODO> *) */ | ||||
| 	output [3:0] DO | ||||
| ); | ||||
| 	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("        (this feature is experimental and incomplete)\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -nodsp\n"); | ||||
| 		log("        do not map multipliers to MULT18X18D\n"); | ||||
| 		log("\n"); | ||||
| 		log("\n"); | ||||
| 		log("The following commands are executed by this synthesis command:\n"); | ||||
| 		help_script(); | ||||
|  | @ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| 	{ | ||||
|  | @ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 		abc2 = false; | ||||
| 		vpr = false; | ||||
| 		abc9 = false; | ||||
| 		nodsp = false; | ||||
| 	} | ||||
| 
 | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
|  | @ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 				abc9 = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-nodsp") { | ||||
| 				nodsp = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		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())); | ||||
| 		} | ||||
| 
 | ||||
| 		if (flatten && check_label("flatten", "(unless -noflatten)")) | ||||
| 		{ | ||||
| 			run("proc"); | ||||
| 			run("flatten"); | ||||
| 			run("tribuf -logic"); | ||||
| 			run("deminout"); | ||||
| 		} | ||||
| 
 | ||||
| 		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)")) | ||||
|  | @ -272,6 +297,7 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 			run("simplemap"); | ||||
| 			run("ecp5_ffinit"); | ||||
| 			run("ecp5_gsr"); | ||||
| 			run("attrmvcp -copy -attr syn_useioff"); | ||||
| 			run("opt_clean"); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -280,12 +306,18 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 			if (abc2 || help_mode) { | ||||
| 				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) { | ||||
| 				run("read_verilog -icells -lib +/ecp5/abc9_model.v"); | ||||
| 				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 | ||||
| 					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 { | ||||
| 				if (nowidelut) | ||||
| 					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_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 | ||||
| module \$lut (A, Y); | ||||
|   parameter WIDTH = 0; | ||||
|  |  | |||
|  | @ -5,7 +5,12 @@ module EFX_LUT4( | |||
|    input I2, | ||||
|    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 | ||||
| 
 | ||||
| module EFX_ADD( | ||||
|  | @ -17,10 +22,18 @@ module EFX_ADD( | |||
| ); | ||||
|    parameter I0_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 | ||||
| 
 | ||||
| module EFX_FF( | ||||
|    output Q, | ||||
|    output reg Q, | ||||
|    input D, | ||||
|    input CE, | ||||
|    input CLK, | ||||
|  | @ -33,6 +46,55 @@ module EFX_FF( | |||
|    parameter SR_VALUE = 0; | ||||
|    parameter SR_SYNC_PRIORITY = 0; | ||||
|    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 | ||||
| 
 | ||||
| module EFX_GBUFCE( | ||||
|  | @ -41,6 +103,12 @@ module EFX_GBUFCE( | |||
|    output O | ||||
| ); | ||||
|    parameter CE_POLARITY = 1'b1; | ||||
| 
 | ||||
|    wire ce; | ||||
|    assign ce = CE_POLARITY ? CE : ~CE; | ||||
|     | ||||
|    assign O = I & ce; | ||||
|     | ||||
| endmodule | ||||
| 
 | ||||
| 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 | ||||
| 	$(Q) mkdir -p techlibs/ice40 | ||||
| 	$(P) python3 $< | ||||
| 	$(P) $(PYTHON_EXECUTABLE) $< | ||||
| 	$(Q) touch 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/brams.txt)) | ||||
| $(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/abc_hx.lut)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.lut)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.box)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.lut)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_model.v)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_hx.box)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_hx.lut)) | ||||
| $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_lp.box)) | ||||
| $(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_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 | ||||
| 
 | ||||
| `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 | ||||
| 
 | ||||
| module SB_IO ( | ||||
|  | @ -141,48 +145,42 @@ module SB_CARRY (output CO, input I0, I1, CI); | |||
| 	assign CO = (I0 && I1) || ((I0 || I1) && CI); | ||||
| endmodule | ||||
| 
 | ||||
| (* abc_box_id = 1, lib_whitebox *) | ||||
| module \$__ICE40_CARRY_WRAPPER ( | ||||
| 	(* abc_carry *) | ||||
| 	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 | ||||
| // Max delay from: https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L90 | ||||
| //                 https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L90 | ||||
| //                 https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L102 | ||||
| 
 | ||||
| // 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) | ||||
| 		Q <= D; | ||||
| 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) | ||||
| 		if (E) | ||||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -190,7 +188,13 @@ module SB_DFFSR (output `SB_DFF_REG, input C, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -198,7 +202,13 @@ module SB_DFFR (output `SB_DFF_REG, input C, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -206,7 +216,13 @@ module SB_DFFSS (output `SB_DFF_REG, input C, S, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -214,7 +230,13 @@ module SB_DFFS (output `SB_DFF_REG, input C, S, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (E) begin | ||||
| 			if (R) | ||||
|  | @ -224,7 +246,13 @@ module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); | |||
| 		end | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -232,7 +260,13 @@ module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (E) begin | ||||
| 			if (S) | ||||
|  | @ -242,7 +276,13 @@ module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); | |||
| 		end | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -252,18 +292,36 @@ endmodule | |||
| 
 | ||||
| // 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) | ||||
| 		Q <= D; | ||||
| 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) | ||||
| 		if (E) | ||||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -271,7 +329,13 @@ module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -279,7 +343,13 @@ module SB_DFFNR (output `SB_DFF_REG, input C, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -287,7 +357,13 @@ module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -295,7 +371,13 @@ module SB_DFFNS (output `SB_DFF_REG, input C, S, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (E) begin | ||||
| 			if (R) | ||||
|  | @ -305,7 +387,13 @@ module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); | |||
| 		end | ||||
| 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) | ||||
| 		if (R) | ||||
| 			Q <= 0; | ||||
|  | @ -313,7 +401,13 @@ module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); | |||
| 			Q <= D; | ||||
| 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) | ||||
| 		if (E) begin | ||||
| 			if (S) | ||||
|  | @ -323,7 +417,13 @@ module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); | |||
| 		end | ||||
| 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) | ||||
| 		if (S) | ||||
| 			Q <= 1; | ||||
|  | @ -334,6 +434,9 @@ endmodule | |||
| // SiliconBlue RAM Cells | ||||
| 
 | ||||
| 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, | ||||
| 	input         RCLK, RCLKE, RE, | ||||
| 	input  [10:0] RADDR, | ||||
|  | @ -502,6 +605,9 @@ module SB_RAM40_4K ( | |||
| endmodule | ||||
| 
 | ||||
| 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, | ||||
| 	input         RCLKN, RCLKE, RE, | ||||
| 	input  [10:0] RADDR, | ||||
|  | @ -567,6 +673,9 @@ module SB_RAM40_4KNR ( | |||
| endmodule | ||||
| 
 | ||||
| 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, | ||||
| 	input         RCLK, RCLKE, RE, | ||||
| 	input  [10:0] RADDR, | ||||
|  | @ -632,6 +741,9 @@ module SB_RAM40_4KNW ( | |||
| endmodule | ||||
| 
 | ||||
| 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, | ||||
| 	input         RCLKN, RCLKE, RE, | ||||
| 	input  [10:0] RADDR, | ||||
|  | @ -700,7 +812,12 @@ endmodule | |||
| 
 | ||||
| module ICESTORM_LC ( | ||||
| 	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; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										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")) | ||||
| 		{ | ||||
| 			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("proc"); | ||||
| 		} | ||||
|  | @ -265,8 +272,18 @@ struct SynthIce40Pass : public ScriptPass | |||
| 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | ||||
| 			run("opt_expr"); | ||||
| 			run("opt_clean"); | ||||
| 			if (help_mode || dsp) | ||||
| 				run("ice40_dsp", "(if -dsp)"); | ||||
| 			if (help_mode || 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("opt"); | ||||
| 			run("fsm"); | ||||
|  | @ -332,6 +349,7 @@ struct SynthIce40Pass : public ScriptPass | |||
| 			} | ||||
| 			if (!noabc) { | ||||
| 				if (abc == "abc9") { | ||||
| 					run("read_verilog -icells -lib +/ice40/abc9_model.v"); | ||||
| 					int wire_delay; | ||||
| 					if (device_opt == "lp") | ||||
| 						wire_delay = 400; | ||||
|  | @ -339,7 +357,7 @@ struct SynthIce40Pass : public ScriptPass | |||
| 						wire_delay = 750; | ||||
| 					else | ||||
| 						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 | ||||
| 					run(abc + " -dress -lut 4", "(skip if -noabc)"); | ||||
|  |  | |||
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