mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-03 21:09:12 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into eddie/verific_help
This commit is contained in:
		
						commit
						f443695a38
					
				
					 208 changed files with 10136 additions and 4961 deletions
				
			
		| 
						 | 
				
			
			@ -50,7 +50,15 @@ Yosys 0.9 .. Yosys 0.9-dev
 | 
			
		|||
    - "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 support for flip-flops with synchronous reset to synth_xilinx
 | 
			
		||||
    - Added support for flip-flops with reset and enable to synth_xilinx
 | 
			
		||||
    - Added "check -mapped"
 | 
			
		||||
    - Added checking of SystemVerilog always block types (always_comb,
 | 
			
		||||
      always_latch and always_ff)
 | 
			
		||||
    - Added "xilinx_dffopt" pass
 | 
			
		||||
    - Added "scratchpad" pass
 | 
			
		||||
    - Added "abc9 -dff"
 | 
			
		||||
    - Added "synth_xilinx -dff"
 | 
			
		||||
 | 
			
		||||
Yosys 0.8 .. Yosys 0.9
 | 
			
		||||
----------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										46
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										46
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
 | 
			
		|||
LDLIBS += -lrt
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
YOSYS_VER := 0.9+932
 | 
			
		||||
YOSYS_VER := 0.9+1706
 | 
			
		||||
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 = 623b5e8
 | 
			
		||||
ABCREV = 71f2b40
 | 
			
		||||
ABCPULL = 1
 | 
			
		||||
ABCURL ?= https://github.com/berkeley-abc/abc
 | 
			
		||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +152,15 @@ ifeq ($(ENABLE_PYOSYS),1)
 | 
			
		|||
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
 | 
			
		||||
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
 | 
			
		||||
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
 | 
			
		||||
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
 | 
			
		||||
 | 
			
		||||
ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1)
 | 
			
		||||
ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1)
 | 
			
		||||
PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed
 | 
			
		||||
else
 | 
			
		||||
PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
PYTHON_PREFIX := $(shell $(PYTHON_CONFIG) --prefix)
 | 
			
		||||
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
 | 
			
		||||
 | 
			
		||||
# Reload Makefile.conf to override python specific variables if defined
 | 
			
		||||
| 
						 | 
				
			
			@ -305,17 +313,17 @@ ifeq ($(ENABLE_PYOSYS),1)
 | 
			
		|||
#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
 | 
			
		||||
ifeq ($(OS), Darwin)
 | 
			
		||||
BOOST_PYTHON_LIB ?= $(shell \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;        then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))";       else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;  then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;           then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))";          else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;     then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))";    else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;        then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))";       else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;  then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;           then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))";          else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;     then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))";    else \
 | 
			
		||||
                                                                                                                                                                                        echo ""; fi; fi; fi; fi;)
 | 
			
		||||
else
 | 
			
		||||
BOOST_PYTHON_LIB ?= $(shell \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;        then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))";       else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;  then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;           then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))";          else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;     then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))";    else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;        then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))";       else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;  then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;           then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))";          else \
 | 
			
		||||
	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;     then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))";    else \
 | 
			
		||||
                                                                                                                                                                                        echo ""; fi; fi; fi; fi;)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -325,19 +333,19 @@ endif
 | 
			
		|||
 | 
			
		||||
ifeq ($(OS), Darwin)
 | 
			
		||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
 | 
			
		||||
else
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
 | 
			
		||||
endif
 | 
			
		||||
else
 | 
			
		||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
 | 
			
		||||
else
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
 | 
			
		||||
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
 | 
			
		||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -343,6 +343,13 @@ Verilog Attributes and non-standard features
 | 
			
		|||
- The ``clkbuf_sink`` attribute can be set on an input port of a module to
 | 
			
		||||
  request clock buffer insertion by the ``clkbufmap`` pass.
 | 
			
		||||
 | 
			
		||||
- The ``clkbuf_inv`` attribute can be set on an output port of a module
 | 
			
		||||
  with the value set to the name of an input port of that module.  When
 | 
			
		||||
  the ``clkbufmap`` would otherwise insert a clock buffer on this output,
 | 
			
		||||
  it will instead try inserting the clock buffer on the input port (this
 | 
			
		||||
  is used to implement clock inverter cells that clock buffer insertion
 | 
			
		||||
  will "see through").
 | 
			
		||||
 | 
			
		||||
- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
 | 
			
		||||
  automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
 | 
			
		||||
  overridden by providing a custom selection to ``clkbufmap``.
 | 
			
		||||
| 
						 | 
				
			
			@ -357,19 +364,28 @@ Verilog Attributes and non-standard features
 | 
			
		|||
  it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
 | 
			
		||||
  from inserting another pad cell on it.
 | 
			
		||||
 | 
			
		||||
- The module attribute ``abc_box_id`` specifies a positive integer linking a
 | 
			
		||||
- The module attribute ``abc9_box_id`` specifies a positive integer linking a
 | 
			
		||||
  blackbox or whitebox definition to a corresponding entry in a `abc9`
 | 
			
		||||
  box-file.
 | 
			
		||||
 | 
			
		||||
- The port attribute ``abc_carry`` marks the carry-in (if an input port) and
 | 
			
		||||
- The port attribute ``abc9_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
 | 
			
		||||
- The port attribute ``abc9_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.
 | 
			
		||||
  during `abc9` techmapping.
 | 
			
		||||
 | 
			
		||||
- The module attribute ``abc9_flop`` is a boolean marking the module as a
 | 
			
		||||
  flip-flop. This allows `abc9` to analyse its contents in order to perform
 | 
			
		||||
  sequential synthesis.
 | 
			
		||||
 | 
			
		||||
- The frontend sets attributes ``always_comb``, ``always_latch`` and
 | 
			
		||||
  ``always_ff`` on processes derived from SystemVerilog style always blocks
 | 
			
		||||
  according to the type of the always. These are checked for correctness in
 | 
			
		||||
  ``proc_dlatch``.
 | 
			
		||||
 | 
			
		||||
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
 | 
			
		||||
  the non-standard ``{* ... *}`` attribute syntax to set default attributes
 | 
			
		||||
| 
						 | 
				
			
			@ -442,10 +458,10 @@ Verilog Attributes and non-standard features
 | 
			
		|||
  expressions over parameters and constant values are allowed). The intended
 | 
			
		||||
  use for this is synthesis-time DRC.
 | 
			
		||||
 | 
			
		||||
- There is limited support for converting specify .. endspecify statements to
 | 
			
		||||
  special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in
 | 
			
		||||
  blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this
 | 
			
		||||
  functionality. (By default specify .. endspecify blocks are ignored.)
 | 
			
		||||
- There is limited support for converting ``specify`` .. ``endspecify``
 | 
			
		||||
  statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells,
 | 
			
		||||
  for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to
 | 
			
		||||
  enable this functionality. (By default these blocks are ignored.)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Non-standard or SystemVerilog features for formal verification
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -787,6 +787,14 @@ struct AigerBackend : public Backend {
 | 
			
		|||
		if (top_module == nullptr)
 | 
			
		||||
			log_error("Can't find top module in current design!\n");
 | 
			
		||||
 | 
			
		||||
		if (!design->selected_whole_module(top_module))
 | 
			
		||||
			log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
 | 
			
		||||
 | 
			
		||||
		if (!top_module->processes.empty())
 | 
			
		||||
			log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module));
 | 
			
		||||
		if (!top_module->memories.empty())
 | 
			
		||||
			log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module));
 | 
			
		||||
 | 
			
		||||
		AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
 | 
			
		||||
		writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,8 +81,8 @@ struct XAigerWriter
 | 
			
		|||
	pool<SigBit> input_bits, output_bits;
 | 
			
		||||
	dict<SigBit, SigBit> not_map, alias_map;
 | 
			
		||||
	dict<SigBit, pair<SigBit, SigBit>> and_map;
 | 
			
		||||
	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
 | 
			
		||||
	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
 | 
			
		||||
	vector<SigBit> ci_bits, co_bits;
 | 
			
		||||
	dict<SigBit, Cell*> ff_bits;
 | 
			
		||||
	dict<SigBit, float> arrival_times;
 | 
			
		||||
 | 
			
		||||
	vector<pair<int, int>> aig_gates;
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ struct XAigerWriter
 | 
			
		|||
	dict<SigBit, int> ordered_outputs;
 | 
			
		||||
 | 
			
		||||
	vector<Cell*> box_list;
 | 
			
		||||
	bool omode = false;
 | 
			
		||||
	dict<IdString, std::vector<IdString>> box_ports;
 | 
			
		||||
 | 
			
		||||
	int mkgate(int a0, int a1)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +141,6 @@ struct XAigerWriter
 | 
			
		|||
	{
 | 
			
		||||
		pool<SigBit> undriven_bits;
 | 
			
		||||
		pool<SigBit> unused_bits;
 | 
			
		||||
		pool<SigBit> keep_bits;
 | 
			
		||||
 | 
			
		||||
		// promote public wires
 | 
			
		||||
		for (auto wire : module->wires())
 | 
			
		||||
| 
						 | 
				
			
			@ -153,51 +152,38 @@ struct XAigerWriter
 | 
			
		|||
			if (wire->port_input)
 | 
			
		||||
				sigmap.add(wire);
 | 
			
		||||
 | 
			
		||||
		// promote output wires
 | 
			
		||||
		// promote keep wires
 | 
			
		||||
		for (auto wire : module->wires())
 | 
			
		||||
			if (wire->port_output)
 | 
			
		||||
			if (wire->get_bool_attribute(ID::keep))
 | 
			
		||||
				sigmap.add(wire);
 | 
			
		||||
 | 
			
		||||
		for (auto wire : module->wires())
 | 
			
		||||
		{
 | 
			
		||||
			bool keep = wire->attributes.count("\\keep");
 | 
			
		||||
 | 
			
		||||
		for (auto wire : module->wires())
 | 
			
		||||
			for (int i = 0; i < GetSize(wire); i++)
 | 
			
		||||
			{
 | 
			
		||||
				SigBit wirebit(wire, i);
 | 
			
		||||
				SigBit bit = sigmap(wirebit);
 | 
			
		||||
 | 
			
		||||
				if (bit.wire) {
 | 
			
		||||
					undriven_bits.insert(bit);
 | 
			
		||||
					unused_bits.insert(bit);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (keep)
 | 
			
		||||
					keep_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
				if (wire->port_input || keep) {
 | 
			
		||||
					if (bit != wirebit)
 | 
			
		||||
						alias_map[bit] = wirebit;
 | 
			
		||||
					input_bits.insert(wirebit);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (wire->port_output || keep) {
 | 
			
		||||
					if (bit != RTLIL::Sx) {
 | 
			
		||||
						if (bit != wirebit)
 | 
			
		||||
							alias_map[wirebit] = bit;
 | 
			
		||||
				if (bit.wire == nullptr) {
 | 
			
		||||
					if (wire->port_output) {
 | 
			
		||||
						aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
 | 
			
		||||
						output_bits.insert(wirebit);
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
						log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit));
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				undriven_bits.insert(bit);
 | 
			
		||||
				unused_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
				if (wire->port_input)
 | 
			
		||||
					input_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
				if (wire->port_output) {
 | 
			
		||||
					if (bit != wirebit)
 | 
			
		||||
						alias_map[wirebit] = bit;
 | 
			
		||||
					output_bits.insert(wirebit);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto bit : input_bits)
 | 
			
		||||
			undriven_bits.erase(sigmap(bit));
 | 
			
		||||
		for (auto bit : output_bits)
 | 
			
		||||
			if (!bit.wire->port_input)
 | 
			
		||||
				unused_bits.erase(bit);
 | 
			
		||||
 | 
			
		||||
		// TODO: Speed up toposort -- ultimately we care about
 | 
			
		||||
		//       box ordering, but not individual AIG cells
 | 
			
		||||
| 
						 | 
				
			
			@ -213,11 +199,9 @@ struct XAigerWriter
 | 
			
		|||
				unused_bits.erase(A);
 | 
			
		||||
				undriven_bits.erase(Y);
 | 
			
		||||
				not_map[Y] = A;
 | 
			
		||||
				if (!holes_mode) {
 | 
			
		||||
					toposort.node(cell->name);
 | 
			
		||||
					bit_users[A].insert(cell->name);
 | 
			
		||||
					bit_drivers[Y].insert(cell->name);
 | 
			
		||||
				}
 | 
			
		||||
				toposort.node(cell->name);
 | 
			
		||||
				bit_users[A].insert(cell->name);
 | 
			
		||||
				bit_drivers[Y].insert(cell->name);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -230,89 +214,92 @@ struct XAigerWriter
 | 
			
		|||
				unused_bits.erase(B);
 | 
			
		||||
				undriven_bits.erase(Y);
 | 
			
		||||
				and_map[Y] = make_pair(A, B);
 | 
			
		||||
				if (!holes_mode) {
 | 
			
		||||
					toposort.node(cell->name);
 | 
			
		||||
					bit_users[A].insert(cell->name);
 | 
			
		||||
					bit_users[B].insert(cell->name);
 | 
			
		||||
					bit_drivers[Y].insert(cell->name);
 | 
			
		||||
				}
 | 
			
		||||
				toposort.node(cell->name);
 | 
			
		||||
				bit_users[A].insert(cell->name);
 | 
			
		||||
				bit_users[B].insert(cell->name);
 | 
			
		||||
				bit_drivers[Y].insert(cell->name);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log_assert(!holes_mode);
 | 
			
		||||
			if (cell->type == "$__ABC9_FF_" &&
 | 
			
		||||
                                        // The presence of an abc9_mergeability attribute indicates
 | 
			
		||||
                                        //   that we do want to pass this flop to ABC
 | 
			
		||||
                                        cell->attributes.count("\\abc9_mergeability"))
 | 
			
		||||
			{
 | 
			
		||||
				SigBit D = sigmap(cell->getPort("\\D").as_bit());
 | 
			
		||||
				SigBit Q = sigmap(cell->getPort("\\Q").as_bit());
 | 
			
		||||
				unused_bits.erase(D);
 | 
			
		||||
				undriven_bits.erase(Q);
 | 
			
		||||
				alias_map[Q] = D;
 | 
			
		||||
				auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell));
 | 
			
		||||
				log_assert(r.second);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			RTLIL::Module* inst_module = module->design->module(cell->type);
 | 
			
		||||
			if (inst_module && inst_module->attributes.count("\\abc9_box_id")) {
 | 
			
		||||
				abc9_box_seen = true;
 | 
			
		||||
			if (inst_module) {
 | 
			
		||||
				bool abc9_box = inst_module->attributes.count("\\abc9_box_id");
 | 
			
		||||
				bool abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
 | 
			
		||||
				if (abc9_box && cell->get_bool_attribute("\\abc9_keep"))
 | 
			
		||||
					abc9_box = false;
 | 
			
		||||
 | 
			
		||||
				if (!holes_mode) {
 | 
			
		||||
					toposort.node(cell->name);
 | 
			
		||||
					for (const auto &conn : cell->connections()) {
 | 
			
		||||
						auto port_wire = inst_module->wire(conn.first);
 | 
			
		||||
						if (port_wire->port_input) {
 | 
			
		||||
							// Ignore inout for the sake of topographical ordering
 | 
			
		||||
							if (port_wire->port_output) continue;
 | 
			
		||||
				for (const auto &conn : cell->connections()) {
 | 
			
		||||
					auto port_wire = inst_module->wire(conn.first);
 | 
			
		||||
 | 
			
		||||
					if (abc9_box) {
 | 
			
		||||
						// Ignore inout for the sake of topographical ordering
 | 
			
		||||
						if (port_wire->port_input && !port_wire->port_output)
 | 
			
		||||
							for (auto bit : sigmap(conn.second))
 | 
			
		||||
								bit_users[bit].insert(cell->name);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						if (port_wire->port_output)
 | 
			
		||||
							for (auto bit : sigmap(conn.second))
 | 
			
		||||
								bit_drivers[bit].insert(cell->name);
 | 
			
		||||
 | 
			
		||||
						if (!abc9_flop)
 | 
			
		||||
							continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (port_wire->port_output) {
 | 
			
		||||
						int arrival = 0;
 | 
			
		||||
						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();
 | 
			
		||||
						}
 | 
			
		||||
						if (arrival)
 | 
			
		||||
							for (auto bit : sigmap(conn.second))
 | 
			
		||||
								arrival_times[bit] = arrival;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (abc9_box) {
 | 
			
		||||
					abc9_box_seen = true;
 | 
			
		||||
					toposort.node(cell->name);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				bool cell_known = inst_module || cell->known();
 | 
			
		||||
				for (const auto &c : cell->connections()) {
 | 
			
		||||
					if (c.second.is_fully_const()) continue;
 | 
			
		||||
					auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
 | 
			
		||||
					auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
 | 
			
		||||
					auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
 | 
			
		||||
					if (!is_input && !is_output)
 | 
			
		||||
						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) {
 | 
			
		||||
							Wire *w = b.wire;
 | 
			
		||||
							if (!w) continue;
 | 
			
		||||
							if (!w->port_output || !cell_known) {
 | 
			
		||||
								SigBit I = sigmap(b);
 | 
			
		||||
								if (I != b)
 | 
			
		||||
									alias_map[b] = I;
 | 
			
		||||
								output_bits.insert(b);
 | 
			
		||||
								unused_bits.erase(b);
 | 
			
		||||
			bool cell_known = inst_module || cell->known();
 | 
			
		||||
			for (const auto &c : cell->connections()) {
 | 
			
		||||
				if (c.second.is_fully_const()) continue;
 | 
			
		||||
				auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
 | 
			
		||||
				auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
 | 
			
		||||
				auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
 | 
			
		||||
				if (!is_input && !is_output)
 | 
			
		||||
					log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
 | 
			
		||||
 | 
			
		||||
								if (!cell_known)
 | 
			
		||||
									keep_bits.insert(b);
 | 
			
		||||
							}
 | 
			
		||||
				if (is_input)
 | 
			
		||||
					for (auto b : c.second) {
 | 
			
		||||
						Wire *w = b.wire;
 | 
			
		||||
						if (!w) continue;
 | 
			
		||||
						if (!w->port_output || !cell_known) {
 | 
			
		||||
							SigBit I = sigmap(b);
 | 
			
		||||
							if (I != b)
 | 
			
		||||
								alias_map[b] = I;
 | 
			
		||||
							output_bits.insert(b);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (is_output) {
 | 
			
		||||
						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);
 | 
			
		||||
							SigBit O = sigmap(b);
 | 
			
		||||
							if (O != b)
 | 
			
		||||
								alias_map[O] = b;
 | 
			
		||||
							undriven_bits.erase(O);
 | 
			
		||||
 | 
			
		||||
							if (arrival)
 | 
			
		||||
								arrival_times[b] = arrival;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
 | 
			
		||||
| 
						 | 
				
			
			@ -352,12 +339,44 @@ struct XAigerWriter
 | 
			
		|||
 | 
			
		||||
				bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
 | 
			
		||||
 | 
			
		||||
				auto r = box_ports.insert(cell->type);
 | 
			
		||||
				if (r.second) {
 | 
			
		||||
					// Make carry in the last PI, and carry out the last PO
 | 
			
		||||
					//   since ABC requires it this way
 | 
			
		||||
					IdString carry_in, carry_out;
 | 
			
		||||
					for (const auto &port_name : box_module->ports) {
 | 
			
		||||
						auto w = box_module->wire(port_name);
 | 
			
		||||
						log_assert(w);
 | 
			
		||||
						if (w->get_bool_attribute("\\abc9_carry")) {
 | 
			
		||||
							if (w->port_input) {
 | 
			
		||||
								if (carry_in != IdString())
 | 
			
		||||
									log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module));
 | 
			
		||||
								carry_in = port_name;
 | 
			
		||||
							}
 | 
			
		||||
							if (w->port_output) {
 | 
			
		||||
								if (carry_out != IdString())
 | 
			
		||||
									log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module));
 | 
			
		||||
								carry_out = port_name;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						else
 | 
			
		||||
							r.first->second.push_back(port_name);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (carry_in != IdString() && carry_out == IdString())
 | 
			
		||||
						log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module));
 | 
			
		||||
					if (carry_in == IdString() && carry_out != IdString())
 | 
			
		||||
						log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module));
 | 
			
		||||
					if (carry_in != IdString()) {
 | 
			
		||||
						r.first->second.push_back(carry_in);
 | 
			
		||||
						r.first->second.push_back(carry_out);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// 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
 | 
			
		||||
				//     (as RTLIL::Module::fixup_ports() would do)
 | 
			
		||||
				for (const auto &port_name : box_module->ports) {
 | 
			
		||||
					RTLIL::Wire* w = box_module->wire(port_name);
 | 
			
		||||
				for (auto port_name : r.first->second) {
 | 
			
		||||
					auto w = box_module->wire(port_name);
 | 
			
		||||
					log_assert(w);
 | 
			
		||||
					auto it = cell->connections_.find(port_name);
 | 
			
		||||
					if (w->port_input) {
 | 
			
		||||
| 
						 | 
				
			
			@ -372,8 +391,7 @@ struct XAigerWriter
 | 
			
		|||
							cell->setPort(port_name, rhs);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						int offset = 0;
 | 
			
		||||
						for (auto b : rhs.bits()) {
 | 
			
		||||
						for (auto b : rhs) {
 | 
			
		||||
							SigBit I = sigmap(b);
 | 
			
		||||
							if (b == RTLIL::Sx)
 | 
			
		||||
								b = State::S0;
 | 
			
		||||
| 
						 | 
				
			
			@ -383,13 +401,13 @@ struct XAigerWriter
 | 
			
		|||
								else
 | 
			
		||||
									alias_map[b] = I;
 | 
			
		||||
							}
 | 
			
		||||
							co_bits.emplace_back(b, cell, port_name, offset++, 0);
 | 
			
		||||
							unused_bits.erase(b);
 | 
			
		||||
							co_bits.emplace_back(b);
 | 
			
		||||
							unused_bits.erase(I);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (w->port_output) {
 | 
			
		||||
						RTLIL::SigSpec rhs;
 | 
			
		||||
						auto it = cell->connections_.find(w->name);
 | 
			
		||||
						auto it = cell->connections_.find(port_name);
 | 
			
		||||
						if (it != cell->connections_.end()) {
 | 
			
		||||
							if (GetSize(it->second) < GetSize(w))
 | 
			
		||||
								it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second)));
 | 
			
		||||
| 
						 | 
				
			
			@ -403,117 +421,105 @@ struct XAigerWriter
 | 
			
		|||
							cell->setPort(port_name, rhs);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						int offset = 0;
 | 
			
		||||
						for (const auto &b : rhs.bits()) {
 | 
			
		||||
							ci_bits.emplace_back(b, cell, port_name, offset++);
 | 
			
		||||
							SigBit O = sigmap(b);
 | 
			
		||||
							if (O != b)
 | 
			
		||||
								alias_map[O] = b;
 | 
			
		||||
							ci_bits.emplace_back(b);
 | 
			
		||||
							undriven_bits.erase(O);
 | 
			
		||||
							input_bits.erase(b);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box
 | 
			
		||||
				if (box_module->get_bool_attribute("\\abc9_flop")) {
 | 
			
		||||
					SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
 | 
			
		||||
					if (rhs.empty())
 | 
			
		||||
						log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module));
 | 
			
		||||
 | 
			
		||||
					for (auto b : rhs) {
 | 
			
		||||
						SigBit I = sigmap(b);
 | 
			
		||||
						if (b == RTLIL::Sx)
 | 
			
		||||
							b = State::S0;
 | 
			
		||||
						else if (I != b) {
 | 
			
		||||
							if (I == RTLIL::Sx)
 | 
			
		||||
								alias_map[b] = State::S0;
 | 
			
		||||
							else
 | 
			
		||||
								alias_map[b] = I;
 | 
			
		||||
						}
 | 
			
		||||
						co_bits.emplace_back(b);
 | 
			
		||||
						unused_bits.erase(I);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				box_list.emplace_back(cell);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// TODO: Free memory from toposort, bit_drivers, bit_users
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto bit : input_bits) {
 | 
			
		||||
			if (!output_bits.count(bit))
 | 
			
		||||
				continue;
 | 
			
		||||
			RTLIL::Wire *wire = bit.wire;
 | 
			
		||||
			// If encountering an inout port, or a keep-ed wire, then create a new wire
 | 
			
		||||
			// with $inout.out suffix, make it a PO driven by the existing inout, and
 | 
			
		||||
			// inherit existing inout's drivers
 | 
			
		||||
			if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
 | 
			
		||||
					|| keep_bits.count(bit)) {
 | 
			
		||||
				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));
 | 
			
		||||
				SigBit new_bit(new_wire, bit.offset);
 | 
			
		||||
				module->connect(new_bit, bit);
 | 
			
		||||
				if (not_map.count(bit)) {
 | 
			
		||||
					auto a = not_map.at(bit);
 | 
			
		||||
					not_map[new_bit] = a;
 | 
			
		||||
				}
 | 
			
		||||
				else if (and_map.count(bit)) {
 | 
			
		||||
					auto a = and_map.at(bit);
 | 
			
		||||
					and_map[new_bit] = a;
 | 
			
		||||
				}
 | 
			
		||||
				else if (alias_map.count(bit)) {
 | 
			
		||||
					auto a = alias_map.at(bit);
 | 
			
		||||
					alias_map[new_bit] = a;
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
					alias_map[new_bit] = bit;
 | 
			
		||||
				output_bits.erase(bit);
 | 
			
		||||
				output_bits.insert(new_bit);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto bit : input_bits)
 | 
			
		||||
			undriven_bits.erase(bit);
 | 
			
		||||
		for (auto bit : output_bits)
 | 
			
		||||
			unused_bits.erase(sigmap(bit));
 | 
			
		||||
		for (auto bit : unused_bits)
 | 
			
		||||
			undriven_bits.erase(bit);
 | 
			
		||||
 | 
			
		||||
		if (!undriven_bits.empty() && !holes_mode) {
 | 
			
		||||
			undriven_bits.sort();
 | 
			
		||||
			for (auto bit : undriven_bits) {
 | 
			
		||||
				log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
 | 
			
		||||
				input_bits.insert(bit);
 | 
			
		||||
			}
 | 
			
		||||
			log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
 | 
			
		||||
		// Make all undriven bits a primary input
 | 
			
		||||
		for (auto bit : undriven_bits) {
 | 
			
		||||
			input_bits.insert(bit);
 | 
			
		||||
			undriven_bits.erase(bit);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (holes_mode) {
 | 
			
		||||
			struct sort_by_port_id {
 | 
			
		||||
				bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
 | 
			
		||||
					return a.wire->port_id < b.wire->port_id;
 | 
			
		||||
					return a.wire->port_id < b.wire->port_id ||
 | 
			
		||||
					    (a.wire->port_id == b.wire->port_id && a.offset < b.offset);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			input_bits.sort(sort_by_port_id());
 | 
			
		||||
			output_bits.sort(sort_by_port_id());
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			input_bits.sort();
 | 
			
		||||
			output_bits.sort();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		not_map.sort();
 | 
			
		||||
		and_map.sort();
 | 
			
		||||
 | 
			
		||||
		aig_map[State::S0] = 0;
 | 
			
		||||
		aig_map[State::S1] = 1;
 | 
			
		||||
 | 
			
		||||
		for (auto bit : input_bits) {
 | 
			
		||||
		for (const auto &bit : input_bits) {
 | 
			
		||||
			aig_m++, aig_i++;
 | 
			
		||||
			log_assert(!aig_map.count(bit));
 | 
			
		||||
			aig_map[bit] = 2*aig_m;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &c : ci_bits) {
 | 
			
		||||
			RTLIL::SigBit bit = std::get<0>(c);
 | 
			
		||||
		for (const auto &i : ff_bits) {
 | 
			
		||||
			const Cell *cell = i.second;
 | 
			
		||||
			const SigBit &q = sigmap(cell->getPort("\\Q"));
 | 
			
		||||
			aig_m++, aig_i++;
 | 
			
		||||
			log_assert(!aig_map.count(q));
 | 
			
		||||
			aig_map[q] = 2*aig_m;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &bit : ci_bits) {
 | 
			
		||||
			aig_m++, aig_i++;
 | 
			
		||||
			log_assert(!aig_map.count(bit));
 | 
			
		||||
			aig_map[bit] = 2*aig_m;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &c : co_bits) {
 | 
			
		||||
			RTLIL::SigBit bit = std::get<0>(c);
 | 
			
		||||
			std::get<4>(c) = ordered_outputs[bit] = aig_o++;
 | 
			
		||||
			aig_outputs.push_back(bit2aig(bit));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (output_bits.empty()) {
 | 
			
		||||
			output_bits.insert(State::S0);
 | 
			
		||||
			omode = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto bit : output_bits) {
 | 
			
		||||
		for (auto bit : co_bits) {
 | 
			
		||||
			ordered_outputs[bit] = aig_o++;
 | 
			
		||||
			aig_outputs.push_back(bit2aig(bit));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (const auto &bit : output_bits) {
 | 
			
		||||
			ordered_outputs[bit] = aig_o++;
 | 
			
		||||
			aig_outputs.push_back(bit2aig(bit));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &i : ff_bits) {
 | 
			
		||||
			const SigBit &d = i.first;
 | 
			
		||||
			aig_o++;
 | 
			
		||||
			aig_outputs.push_back(aig_map.at(d));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_aiger(std::ostream &f, bool ascii_mode)
 | 
			
		||||
| 
						 | 
				
			
			@ -575,7 +581,6 @@ 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));
 | 
			
		||||
| 
						 | 
				
			
			@ -583,14 +588,14 @@ struct XAigerWriter
 | 
			
		|||
		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("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
 | 
			
		||||
		write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
 | 
			
		||||
		log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
 | 
			
		||||
		write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
 | 
			
		||||
		log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
 | 
			
		||||
		write_h_buffer(input_bits.size() + ff_bits.size());
 | 
			
		||||
		log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
 | 
			
		||||
		write_h_buffer(output_bits.size() + ff_bits.size());
 | 
			
		||||
		log_debug("boxNum = %d\n", GetSize(box_list));
 | 
			
		||||
		write_h_buffer(box_list.size());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -606,79 +611,154 @@ struct XAigerWriter
 | 
			
		|||
		//for (auto bit : output_bits)
 | 
			
		||||
		//	write_o_buffer(0);
 | 
			
		||||
 | 
			
		||||
		if (!box_list.empty()) {
 | 
			
		||||
		if (!box_list.empty() || !ff_bits.empty()) {
 | 
			
		||||
			RTLIL::Module *holes_module = module->design->addModule("$__holes__");
 | 
			
		||||
			log_assert(holes_module);
 | 
			
		||||
 | 
			
		||||
			dict<IdString, std::tuple<Cell*,int,int,int>> cell_cache;
 | 
			
		||||
 | 
			
		||||
			int port_id = 1;
 | 
			
		||||
			int box_count = 0;
 | 
			
		||||
			for (auto cell : box_list) {
 | 
			
		||||
				RTLIL::Module* box_module = module->design->module(cell->type);
 | 
			
		||||
				int box_inputs = 0, box_outputs = 0;
 | 
			
		||||
				Cell *holes_cell = nullptr;
 | 
			
		||||
				if (box_module->get_bool_attribute("\\whitebox")) {
 | 
			
		||||
					holes_cell = holes_module->addCell(cell->name, cell->type);
 | 
			
		||||
					holes_cell->parameters = cell->parameters;
 | 
			
		||||
				}
 | 
			
		||||
				RTLIL::Module* orig_box_module = module->design->module(cell->type);
 | 
			
		||||
				log_assert(orig_box_module);
 | 
			
		||||
				IdString derived_name = orig_box_module->derive(module->design, cell->parameters);
 | 
			
		||||
				RTLIL::Module* box_module = module->design->module(derived_name);
 | 
			
		||||
 | 
			
		||||
				// NB: Assume box_module->ports are sorted alphabetically
 | 
			
		||||
				//     (as RTLIL::Module::fixup_ports() would do)
 | 
			
		||||
				for (const auto &port_name : box_module->ports) {
 | 
			
		||||
					RTLIL::Wire *w = box_module->wire(port_name);
 | 
			
		||||
					log_assert(w);
 | 
			
		||||
					RTLIL::Wire *holes_wire;
 | 
			
		||||
					RTLIL::SigSpec port_wire;
 | 
			
		||||
					if (w->port_input) {
 | 
			
		||||
						for (int i = 0; i < GetSize(w); i++) {
 | 
			
		||||
				auto r = cell_cache.insert(derived_name);
 | 
			
		||||
				auto &v = r.first->second;
 | 
			
		||||
				if (r.second) {
 | 
			
		||||
					if (box_module->has_processes())
 | 
			
		||||
						Pass::call_on_module(module->design, box_module, "proc");
 | 
			
		||||
 | 
			
		||||
					int box_inputs = 0, box_outputs = 0;
 | 
			
		||||
					if (box_module->get_bool_attribute("\\whitebox")) {
 | 
			
		||||
						auto holes_cell = holes_module->addCell(cell->name, derived_name);
 | 
			
		||||
						for (auto port_name : box_ports.at(cell->type)) {
 | 
			
		||||
							RTLIL::Wire *w = box_module->wire(port_name);
 | 
			
		||||
							log_assert(w);
 | 
			
		||||
							log_assert(!w->port_input || !w->port_output);
 | 
			
		||||
							auto &conn = holes_cell->connections_[port_name];
 | 
			
		||||
							if (w->port_input) {
 | 
			
		||||
								for (int i = 0; i < GetSize(w); i++) {
 | 
			
		||||
									box_inputs++;
 | 
			
		||||
									RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
 | 
			
		||||
									if (!holes_wire) {
 | 
			
		||||
										holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
 | 
			
		||||
										holes_wire->port_input = true;
 | 
			
		||||
										holes_wire->port_id = port_id++;
 | 
			
		||||
										holes_module->ports.push_back(holes_wire->name);
 | 
			
		||||
									}
 | 
			
		||||
									conn.append(holes_wire);
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							else if (w->port_output) {
 | 
			
		||||
								box_outputs += GetSize(w);
 | 
			
		||||
								conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w));
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// For flops only, create an extra 1-bit input that drives a new wire
 | 
			
		||||
						//   called "<cell>.abc9_ff.Q" that is used below
 | 
			
		||||
						if (box_module->get_bool_attribute("\\abc9_flop")) {
 | 
			
		||||
							box_inputs++;
 | 
			
		||||
							holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
 | 
			
		||||
							Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
 | 
			
		||||
							if (!holes_wire) {
 | 
			
		||||
								holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
 | 
			
		||||
								holes_wire->port_input = true;
 | 
			
		||||
								holes_wire->port_id = port_id++;
 | 
			
		||||
								holes_module->ports.push_back(holes_wire->name);
 | 
			
		||||
							}
 | 
			
		||||
							if (holes_cell)
 | 
			
		||||
								port_wire.append(holes_wire);
 | 
			
		||||
							Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
 | 
			
		||||
							holes_module->connect(Q, holes_wire);
 | 
			
		||||
						}
 | 
			
		||||
						if (!port_wire.empty())
 | 
			
		||||
							holes_cell->setPort(w->name, port_wire);
 | 
			
		||||
 | 
			
		||||
						std::get<0>(v) = holes_cell;
 | 
			
		||||
					}
 | 
			
		||||
					if (w->port_output) {
 | 
			
		||||
						box_outputs += GetSize(w);
 | 
			
		||||
						for (int i = 0; i < GetSize(w); i++) {
 | 
			
		||||
							if (GetSize(w) == 1)
 | 
			
		||||
								holes_wire = holes_module->addWire(stringf("%s.%s", cell->name.c_str(), w->name.c_str()));
 | 
			
		||||
							else
 | 
			
		||||
								holes_wire = holes_module->addWire(stringf("%s.%s[%d]", cell->name.c_str(), w->name.c_str(), i));
 | 
			
		||||
							holes_wire->port_output = true;
 | 
			
		||||
							holes_wire->port_id = port_id++;
 | 
			
		||||
							holes_module->ports.push_back(holes_wire->name);
 | 
			
		||||
							if (holes_cell)
 | 
			
		||||
								port_wire.append(holes_wire);
 | 
			
		||||
							else
 | 
			
		||||
								holes_module->connect(holes_wire, State::S0);
 | 
			
		||||
					else {
 | 
			
		||||
						for (auto port_name : box_ports.at(cell->type)) {
 | 
			
		||||
							RTLIL::Wire *w = box_module->wire(port_name);
 | 
			
		||||
							log_assert(w);
 | 
			
		||||
							log_assert(!w->port_input || !w->port_output);
 | 
			
		||||
							if (w->port_input)
 | 
			
		||||
								box_inputs += GetSize(w);
 | 
			
		||||
							else if (w->port_output)
 | 
			
		||||
								box_outputs += GetSize(w);
 | 
			
		||||
						}
 | 
			
		||||
						if (!port_wire.empty())
 | 
			
		||||
							holes_cell->setPort(w->name, port_wire);
 | 
			
		||||
						log_assert(std::get<0>(v) == nullptr);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					std::get<1>(v) = box_inputs;
 | 
			
		||||
					std::get<2>(v) = box_outputs;
 | 
			
		||||
					std::get<3>(v) = box_module->attributes.at("\\abc9_box_id").as_int();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				write_h_buffer(box_inputs);
 | 
			
		||||
				write_h_buffer(box_outputs);
 | 
			
		||||
				write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int());
 | 
			
		||||
				auto holes_cell = std::get<0>(v);
 | 
			
		||||
				for (auto port_name : box_ports.at(cell->type)) {
 | 
			
		||||
					RTLIL::Wire *w = box_module->wire(port_name);
 | 
			
		||||
					log_assert(w);
 | 
			
		||||
					if (!w->port_output)
 | 
			
		||||
						continue;
 | 
			
		||||
					Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w));
 | 
			
		||||
					holes_wire->port_output = true;
 | 
			
		||||
					holes_wire->port_id = port_id++;
 | 
			
		||||
					holes_module->ports.push_back(holes_wire->name);
 | 
			
		||||
					if (holes_cell) // whitebox
 | 
			
		||||
						holes_module->connect(holes_wire, holes_cell->getPort(port_name));
 | 
			
		||||
					else // blackbox
 | 
			
		||||
						holes_module->connect(holes_wire, Const(State::S0, GetSize(w)));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				write_h_buffer(std::get<1>(v));
 | 
			
		||||
				write_h_buffer(std::get<2>(v));
 | 
			
		||||
				write_h_buffer(std::get<3>(v));
 | 
			
		||||
				write_h_buffer(box_count++);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::stringstream r_buffer;
 | 
			
		||||
			auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
 | 
			
		||||
			write_r_buffer(0);
 | 
			
		||||
			log_debug("flopNum = %d\n", GetSize(ff_bits));
 | 
			
		||||
			write_r_buffer(ff_bits.size());
 | 
			
		||||
 | 
			
		||||
			std::stringstream s_buffer;
 | 
			
		||||
			auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
 | 
			
		||||
			write_s_buffer(ff_bits.size());
 | 
			
		||||
 | 
			
		||||
			for (const auto &i : ff_bits) {
 | 
			
		||||
				const SigBit &d = i.first;
 | 
			
		||||
				const Cell *cell = i.second;
 | 
			
		||||
 | 
			
		||||
				int mergeability = cell->attributes.at(ID(abc9_mergeability)).as_int();
 | 
			
		||||
				log_assert(mergeability > 0);
 | 
			
		||||
				write_r_buffer(mergeability);
 | 
			
		||||
 | 
			
		||||
				Const init = cell->attributes.at(ID(abc9_init));
 | 
			
		||||
				log_assert(GetSize(init) == 1);
 | 
			
		||||
				if (init == State::S1)
 | 
			
		||||
					write_s_buffer(1);
 | 
			
		||||
				else if (init == State::S0)
 | 
			
		||||
					write_s_buffer(0);
 | 
			
		||||
				else {
 | 
			
		||||
					log_assert(init == State::Sx);
 | 
			
		||||
					write_s_buffer(0);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				write_i_buffer(arrival_times.at(d, 0));
 | 
			
		||||
				//write_o_buffer(0);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			f << "r";
 | 
			
		||||
			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());
 | 
			
		||||
 | 
			
		||||
			f << "s";
 | 
			
		||||
			buffer_str = s_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());
 | 
			
		||||
 | 
			
		||||
			if (holes_module) {
 | 
			
		||||
				log_push();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -686,37 +766,66 @@ struct XAigerWriter
 | 
			
		|||
				//holes_module->fixup_ports();
 | 
			
		||||
				holes_module->check();
 | 
			
		||||
 | 
			
		||||
				holes_module->design->selection_stack.emplace_back(false);
 | 
			
		||||
				RTLIL::Selection& sel = holes_module->design->selection_stack.back();
 | 
			
		||||
				sel.select(holes_module);
 | 
			
		||||
				// Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger
 | 
			
		||||
				//   since boxes may contain parameters in which case `flatten` would have
 | 
			
		||||
				//   created a new $paramod ...
 | 
			
		||||
				Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap");
 | 
			
		||||
 | 
			
		||||
				// TODO: Should not need to opt_merge if we only instantiate
 | 
			
		||||
				//       each box type once...
 | 
			
		||||
				Pass::call(holes_module->design, "opt_merge -share_all");
 | 
			
		||||
				SigMap holes_sigmap(holes_module);
 | 
			
		||||
 | 
			
		||||
				Pass::call(holes_module->design, "flatten -wb");
 | 
			
		||||
 | 
			
		||||
				// TODO: Should techmap/aigmap/check all lib_whitebox-es just once,
 | 
			
		||||
				//       instead of per write_xaiger call
 | 
			
		||||
				Pass::call(holes_module->design, "techmap");
 | 
			
		||||
				Pass::call(holes_module->design, "aigmap");
 | 
			
		||||
				for (auto cell : holes_module->cells())
 | 
			
		||||
					if (!cell->type.in("$_NOT_", "$_AND_"))
 | 
			
		||||
				dict<SigSpec, SigSpec> replace;
 | 
			
		||||
				for (auto it = holes_module->cells_.begin(); it != holes_module->cells_.end(); ) {
 | 
			
		||||
					auto cell = it->second;
 | 
			
		||||
					if (cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
 | 
			
		||||
								"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) {
 | 
			
		||||
						SigBit D = cell->getPort("\\D");
 | 
			
		||||
						SigBit Q = cell->getPort("\\Q");
 | 
			
		||||
						// Remove the $_DFF_* cell from what needs to be a combinatorial box
 | 
			
		||||
						it = holes_module->cells_.erase(it);
 | 
			
		||||
						Wire *port;
 | 
			
		||||
						if (GetSize(Q.wire) == 1)
 | 
			
		||||
							port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str()));
 | 
			
		||||
						else
 | 
			
		||||
							port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset));
 | 
			
		||||
						log_assert(port);
 | 
			
		||||
						// Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;"
 | 
			
		||||
						//   in order to extract just the combinatorial control logic that feeds the box
 | 
			
		||||
						//   (i.e. clock enable, synchronous reset, etc.)
 | 
			
		||||
						replace.insert(std::make_pair(Q,D));
 | 
			
		||||
						// Since `flatten` above would have created wires named "<cell>.Q",
 | 
			
		||||
						//   extract the pre-techmap cell name
 | 
			
		||||
						auto pos = Q.wire->name.str().rfind(".");
 | 
			
		||||
						log_assert(pos != std::string::npos);
 | 
			
		||||
						IdString driver = Q.wire->name.substr(0, pos);
 | 
			
		||||
						// And drive the signal that was previously driven by "DFF.Q" (typically
 | 
			
		||||
						//   used to implement clock-enable functionality) with the "<cell>.abc9_ff.Q"
 | 
			
		||||
						//   wire (which itself is driven by an input port) we inserted above
 | 
			
		||||
						Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str()));
 | 
			
		||||
						log_assert(currQ);
 | 
			
		||||
						holes_module->connect(Q, currQ);
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					else if (!cell->type.in("$_NOT_", "$_AND_"))
 | 
			
		||||
						log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n");
 | 
			
		||||
					++it;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				holes_module->design->selection_stack.pop_back();
 | 
			
		||||
				for (auto &conn : holes_module->connections_) {
 | 
			
		||||
					auto it = replace.find(sigmap(conn.second));
 | 
			
		||||
					if (it != replace.end())
 | 
			
		||||
						conn.second = it->second;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Move into a new (temporary) design so that "clean" will only
 | 
			
		||||
				// operate (and run checks on) this one module
 | 
			
		||||
				RTLIL::Design *holes_design = new RTLIL::Design;
 | 
			
		||||
				holes_module->design->modules_.erase(holes_module->name);
 | 
			
		||||
				module->design->modules_.erase(holes_module->name);
 | 
			
		||||
				holes_design->add(holes_module);
 | 
			
		||||
				Pass::call(holes_design, "clean -purge");
 | 
			
		||||
				Pass::call(holes_design, "opt -purge");
 | 
			
		||||
 | 
			
		||||
				std::stringstream a_buffer;
 | 
			
		||||
				XAigerWriter writer(holes_module, true /* holes_mode */);
 | 
			
		||||
				writer.write_aiger(a_buffer, false /*ascii_mode*/);
 | 
			
		||||
 | 
			
		||||
				delete holes_design;
 | 
			
		||||
 | 
			
		||||
				f << "a";
 | 
			
		||||
| 
						 | 
				
			
			@ -747,19 +856,20 @@ struct XAigerWriter
 | 
			
		|||
		//f.write(buffer_str.data(), buffer_str.size());
 | 
			
		||||
 | 
			
		||||
		f << stringf("Generated by %s\n", yosys_version_str);
 | 
			
		||||
 | 
			
		||||
		module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
 | 
			
		||||
		module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
 | 
			
		||||
		module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size());
 | 
			
		||||
		module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_map(std::ostream &f, bool verbose_map)
 | 
			
		||||
	void write_map(std::ostream &f)
 | 
			
		||||
	{
 | 
			
		||||
		dict<int, string> input_lines;
 | 
			
		||||
		dict<int, string> output_lines;
 | 
			
		||||
		dict<int, string> wire_lines;
 | 
			
		||||
 | 
			
		||||
		for (auto wire : module->wires())
 | 
			
		||||
		{
 | 
			
		||||
			//if (!verbose_map && wire->name[0] == '$')
 | 
			
		||||
			//	continue;
 | 
			
		||||
 | 
			
		||||
			SigSpec sig = sigmap(wire);
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < GetSize(wire); i++)
 | 
			
		||||
| 
						 | 
				
			
			@ -773,17 +883,10 @@ struct XAigerWriter
 | 
			
		|||
 | 
			
		||||
				if (output_bits.count(b)) {
 | 
			
		||||
					int o = ordered_outputs.at(b);
 | 
			
		||||
					output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), i, log_id(wire));
 | 
			
		||||
					int init = 2;
 | 
			
		||||
					output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (verbose_map) {
 | 
			
		||||
					if (aig_map.count(sig[i]) == 0)
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					int a = aig_map.at(sig[i]);
 | 
			
		||||
					wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -797,15 +900,9 @@ 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());
 | 
			
		||||
 | 
			
		||||
		wire_lines.sort();
 | 
			
		||||
		for (auto &it : wire_lines)
 | 
			
		||||
			f << it.second;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -817,23 +914,21 @@ struct XAigerBackend : public Backend {
 | 
			
		|||
		log("\n");
 | 
			
		||||
		log("    write_xaiger [options] [filename]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Write the current design to an XAIGER file. The design must be flattened and\n");
 | 
			
		||||
		log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\n");
 | 
			
		||||
		log("Write the top module (according to the (* top *) attribute or if only one module\n");
 | 
			
		||||
		log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or");
 | 
			
		||||
		log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n");
 | 
			
		||||
		log("pseudo-outputs.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -ascii\n");
 | 
			
		||||
		log("        write ASCII version of AIGER format\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -map <filename>\n");
 | 
			
		||||
		log("        write an extra file with port and latch symbols\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -vmap <filename>\n");
 | 
			
		||||
		log("        like -map, but more verbose\n");
 | 
			
		||||
		log("        write an extra file with port and box symbols\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		bool ascii_mode = false;
 | 
			
		||||
		bool verbose_map = false;
 | 
			
		||||
		std::string map_filename;
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing XAIGER backend.\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -849,11 +944,6 @@ struct XAigerBackend : public Backend {
 | 
			
		|||
				map_filename = args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
 | 
			
		||||
				map_filename = args[++argidx];
 | 
			
		||||
				verbose_map = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(f, filename, args, argidx, !ascii_mode);
 | 
			
		||||
| 
						 | 
				
			
			@ -863,6 +953,14 @@ struct XAigerBackend : public Backend {
 | 
			
		|||
		if (top_module == nullptr)
 | 
			
		||||
			log_error("Can't find top module in current design!\n");
 | 
			
		||||
 | 
			
		||||
		if (!design->selected_whole_module(top_module))
 | 
			
		||||
			log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
 | 
			
		||||
 | 
			
		||||
		if (!top_module->processes.empty())
 | 
			
		||||
			log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module));
 | 
			
		||||
		if (!top_module->memories.empty())
 | 
			
		||||
			log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module));
 | 
			
		||||
 | 
			
		||||
		XAigerWriter writer(top_module);
 | 
			
		||||
		writer.write_aiger(*f, ascii_mode);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -871,7 +969,7 @@ struct XAigerBackend : public Backend {
 | 
			
		|||
			mapf.open(map_filename.c_str(), std::ofstream::trunc);
 | 
			
		||||
			if (mapf.fail())
 | 
			
		||||
				log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
 | 
			
		||||
			writer.write_map(mapf, verbose_map);
 | 
			
		||||
			writer.write_map(mapf);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
} XAigerBackend;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -300,6 +300,26 @@ struct EdifBackend : public Backend {
 | 
			
		|||
		*f << stringf("  (library DESIGN\n");
 | 
			
		||||
		*f << stringf("    (edifLevel 0)\n");
 | 
			
		||||
		*f << stringf("    (technology (numberDefinition))\n");
 | 
			
		||||
 | 
			
		||||
		auto add_prop = [&](IdString name, Const val) {
 | 
			
		||||
			if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
 | 
			
		||||
				*f << stringf("\n            (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
 | 
			
		||||
			else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
 | 
			
		||||
				*f << stringf("\n            (property %s (integer %u))", EDIF_DEF(name), val.as_int());
 | 
			
		||||
			else {
 | 
			
		||||
				std::string hex_string = "";
 | 
			
		||||
				for (size_t i = 0; i < val.bits.size(); i += 4) {
 | 
			
		||||
					int digit_value = 0;
 | 
			
		||||
					if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
 | 
			
		||||
					if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
 | 
			
		||||
					if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
 | 
			
		||||
					if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
 | 
			
		||||
					char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
 | 
			
		||||
					hex_string = std::string(digit_str) + hex_string;
 | 
			
		||||
				}
 | 
			
		||||
				*f << stringf("\n            (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
 | 
			
		||||
			}
 | 
			
		||||
		};		
 | 
			
		||||
		for (auto module : sorted_modules)
 | 
			
		||||
		{
 | 
			
		||||
			if (module->get_blackbox_attribute())
 | 
			
		||||
| 
						 | 
				
			
			@ -323,14 +343,23 @@ struct EdifBackend : public Backend {
 | 
			
		|||
				else if (!wire->port_input)
 | 
			
		||||
					dir = "OUTPUT";
 | 
			
		||||
				if (wire->width == 1) {
 | 
			
		||||
					*f << stringf("          (port %s (direction %s))\n", EDIF_DEF(wire->name), dir);
 | 
			
		||||
					*f << stringf("          (port %s (direction %s)", EDIF_DEF(wire->name), dir);
 | 
			
		||||
					if (attr_properties)
 | 
			
		||||
						for (auto &p : wire->attributes)
 | 
			
		||||
							add_prop(p.first, p.second);
 | 
			
		||||
					*f << ")\n";
 | 
			
		||||
					RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
 | 
			
		||||
					net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
 | 
			
		||||
				} else {
 | 
			
		||||
					int b[2];
 | 
			
		||||
					b[wire->upto ? 0 : 1] = wire->start_offset;
 | 
			
		||||
					b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1;
 | 
			
		||||
					*f << stringf("          (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
 | 
			
		||||
					*f << stringf("          (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
 | 
			
		||||
					if (attr_properties)
 | 
			
		||||
						for (auto &p : wire->attributes)
 | 
			
		||||
							add_prop(p.first, p.second);
 | 
			
		||||
 | 
			
		||||
					*f << ")\n";
 | 
			
		||||
					for (int i = 0; i < wire->width; i++) {
 | 
			
		||||
						RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
 | 
			
		||||
						net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
 | 
			
		||||
| 
						 | 
				
			
			@ -348,27 +377,6 @@ struct EdifBackend : public Backend {
 | 
			
		|||
				*f << stringf("          (instance %s\n", EDIF_DEF(cell->name));
 | 
			
		||||
				*f << stringf("            (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
 | 
			
		||||
						lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
 | 
			
		||||
 | 
			
		||||
				auto add_prop = [&](IdString name, Const val) {
 | 
			
		||||
					if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
 | 
			
		||||
						*f << stringf("\n            (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
 | 
			
		||||
					else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
 | 
			
		||||
						*f << stringf("\n            (property %s (integer %u))", EDIF_DEF(name), val.as_int());
 | 
			
		||||
					else {
 | 
			
		||||
						std::string hex_string = "";
 | 
			
		||||
						for (size_t i = 0; i < val.bits.size(); i += 4) {
 | 
			
		||||
							int digit_value = 0;
 | 
			
		||||
							if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
 | 
			
		||||
							if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
 | 
			
		||||
							if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
 | 
			
		||||
							if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
 | 
			
		||||
							char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
 | 
			
		||||
							hex_string = std::string(digit_str) + hex_string;
 | 
			
		||||
						}
 | 
			
		||||
						*f << stringf("\n            (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
 | 
			
		||||
					}
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				for (auto &p : cell->parameters)
 | 
			
		||||
					add_prop(p.first, p.second);
 | 
			
		||||
				if (attr_properties)
 | 
			
		||||
| 
						 | 
				
			
			@ -404,6 +412,8 @@ struct EdifBackend : public Backend {
 | 
			
		|||
						for (auto &ref : it.second)
 | 
			
		||||
							log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
 | 
			
		||||
						sig = RTLIL::State::S0;
 | 
			
		||||
					} else if (sig == RTLIL::State::Sz) {
 | 
			
		||||
						continue;
 | 
			
		||||
					} else {
 | 
			
		||||
						for (auto &ref : it.second)
 | 
			
		||||
							log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
 | 
			
		||||
| 
						 | 
				
			
			@ -431,8 +441,12 @@ struct EdifBackend : public Backend {
 | 
			
		|||
						*f << stringf("            (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
 | 
			
		||||
					if (sig == RTLIL::State::S1)
 | 
			
		||||
						*f << stringf("            (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
 | 
			
		||||
				}
 | 
			
		||||
				*f << stringf("          ))\n");
 | 
			
		||||
				}				
 | 
			
		||||
				*f << stringf("            )");
 | 
			
		||||
				if (attr_properties && sig.wire != NULL)
 | 
			
		||||
					for (auto &p : sig.wire->attributes)
 | 
			
		||||
						add_prop(p.first, p.second);
 | 
			
		||||
				*f << stringf("\n          )\n");
 | 
			
		||||
			}
 | 
			
		||||
			*f << stringf("        )\n");
 | 
			
		||||
			*f << stringf("      )\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -206,7 +206,7 @@ eval_end:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports)
 | 
			
		||||
	: design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports)
 | 
			
		||||
	: design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports), aiger_autoidx(autoidx++)
 | 
			
		||||
{
 | 
			
		||||
	module = new RTLIL::Module;
 | 
			
		||||
	module->name = module_name;
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +255,7 @@ end_of_header:
 | 
			
		|||
	else
 | 
			
		||||
		log_abort();
 | 
			
		||||
 | 
			
		||||
	RTLIL::Wire* n0 = module->wire("\\__0__");
 | 
			
		||||
	RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
 | 
			
		||||
	if (n0)
 | 
			
		||||
		module->connect(n0, State::S0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -271,14 +271,24 @@ end_of_header:
 | 
			
		|||
			if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
 | 
			
		||||
				log_error("Line %u has invalid symbol position!\n", line_count);
 | 
			
		||||
 | 
			
		||||
			RTLIL::IdString escaped_s = stringf("\\%s", s.c_str());
 | 
			
		||||
			RTLIL::Wire* wire;
 | 
			
		||||
			if (c == 'i') wire = inputs[l1];
 | 
			
		||||
			else if (c == 'l') wire = latches[l1];
 | 
			
		||||
			else if (c == 'o') wire = outputs[l1];
 | 
			
		||||
			else if (c == 'o') {
 | 
			
		||||
				wire = module->wire(escaped_s);
 | 
			
		||||
				if (wire) {
 | 
			
		||||
					// Could have been renamed by a latch
 | 
			
		||||
					module->swap_names(wire, outputs[l1]);
 | 
			
		||||
					module->connect(outputs[l1], wire);
 | 
			
		||||
					goto next;
 | 
			
		||||
				}
 | 
			
		||||
				wire = outputs[l1];
 | 
			
		||||
			}
 | 
			
		||||
			else if (c == 'b') wire = bad_properties[l1];
 | 
			
		||||
			else log_abort();
 | 
			
		||||
 | 
			
		||||
			module->rename(wire, stringf("\\%s", s.c_str()));
 | 
			
		||||
			module->rename(wire, escaped_s);
 | 
			
		||||
		}
 | 
			
		||||
		else if (c == 'j' || c == 'f') {
 | 
			
		||||
			// TODO
 | 
			
		||||
| 
						 | 
				
			
			@ -293,6 +303,7 @@ end_of_header:
 | 
			
		|||
		}
 | 
			
		||||
		else
 | 
			
		||||
			log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
 | 
			
		||||
next:
 | 
			
		||||
		std::getline(f, line); // Ignore up to start of next line
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -312,18 +323,18 @@ static uint32_t parse_xaiger_literal(std::istream &f)
 | 
			
		|||
	return from_big_endian(l);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
 | 
			
		||||
RTLIL::Wire* AigerReader::createWireIfNotExists(RTLIL::Module *module, unsigned literal)
 | 
			
		||||
{
 | 
			
		||||
	const unsigned variable = literal >> 1;
 | 
			
		||||
	const bool invert = literal & 1;
 | 
			
		||||
	RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : ""));
 | 
			
		||||
	RTLIL::IdString wire_name(stringf("$aiger%d$%d%s", aiger_autoidx, variable, invert ? "b" : ""));
 | 
			
		||||
	RTLIL::Wire *wire = module->wire(wire_name);
 | 
			
		||||
	if (wire) return wire;
 | 
			
		||||
	log_debug2("Creating %s\n", wire_name.c_str());
 | 
			
		||||
	wire = module->addWire(wire_name);
 | 
			
		||||
	wire->port_input = wire->port_output = false;
 | 
			
		||||
	if (!invert) return wire;
 | 
			
		||||
	RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable));
 | 
			
		||||
	RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable));
 | 
			
		||||
	RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
 | 
			
		||||
	if (wire_inv) {
 | 
			
		||||
		if (module->cell(wire_inv_name)) return wire;
 | 
			
		||||
| 
						 | 
				
			
			@ -335,12 +346,12 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
 | 
			
		||||
	module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire);
 | 
			
		||||
	module->addNotGate(stringf("$not$aiger%d$%d", aiger_autoidx, variable), wire_inv, wire);
 | 
			
		||||
 | 
			
		||||
	return wire;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
 | 
			
		||||
void AigerReader::parse_xaiger()
 | 
			
		||||
{
 | 
			
		||||
	std::string header;
 | 
			
		||||
	f >> header;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,108 +383,118 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
 | 
			
		|||
	else
 | 
			
		||||
		log_abort();
 | 
			
		||||
 | 
			
		||||
	RTLIL::Wire* n0 = module->wire("\\__0__");
 | 
			
		||||
	RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
 | 
			
		||||
	if (n0)
 | 
			
		||||
		module->connect(n0, State::S0);
 | 
			
		||||
 | 
			
		||||
	int c = f.get();
 | 
			
		||||
	if (c != 'c')
 | 
			
		||||
		log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
 | 
			
		||||
	if (f.peek() == '\n')
 | 
			
		||||
		f.get();
 | 
			
		||||
 | 
			
		||||
	dict<int,IdString> box_lookup;
 | 
			
		||||
	for (auto m : design->modules()) {
 | 
			
		||||
		auto it = m->attributes.find(ID(abc9_box_id));
 | 
			
		||||
		if (it == m->attributes.end())
 | 
			
		||||
			continue;
 | 
			
		||||
		if (m->name.begins_with("$paramod"))
 | 
			
		||||
			continue;
 | 
			
		||||
		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 abc9_box_id = %d value as '%s'.\n",
 | 
			
		||||
					log_id(m), id, log_id(r.first->second));
 | 
			
		||||
		log_assert(r.second);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Parse footer (symbol table, comments, etc.)
 | 
			
		||||
	std::string s;
 | 
			
		||||
	bool comment_seen = false;
 | 
			
		||||
	for (int c = f.peek(); c != EOF; c = f.peek()) {
 | 
			
		||||
		if (comment_seen || c == 'c') {
 | 
			
		||||
			if (!comment_seen) {
 | 
			
		||||
				f.ignore(1);
 | 
			
		||||
				c = f.peek();
 | 
			
		||||
				comment_seen = true;
 | 
			
		||||
			}
 | 
			
		||||
			if (c == '\n')
 | 
			
		||||
				break;
 | 
			
		||||
			f.ignore(1);
 | 
			
		||||
			// XAIGER extensions
 | 
			
		||||
			if (c == 'm') {
 | 
			
		||||
				uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				uint32_t lutNum = parse_xaiger_literal(f);
 | 
			
		||||
				uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
 | 
			
		||||
				ConstEvalAig ce(module);
 | 
			
		||||
				for (unsigned i = 0; i < lutNum; ++i) {
 | 
			
		||||
					uint32_t rootNodeID = parse_xaiger_literal(f);
 | 
			
		||||
					uint32_t cutLeavesM = parse_xaiger_literal(f);
 | 
			
		||||
					log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
 | 
			
		||||
					RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID));
 | 
			
		||||
					uint32_t nodeID;
 | 
			
		||||
					RTLIL::SigSpec input_sig;
 | 
			
		||||
					for (unsigned j = 0; j < cutLeavesM; ++j) {
 | 
			
		||||
						nodeID = parse_xaiger_literal(f);
 | 
			
		||||
						log_debug2("\t%u\n", nodeID);
 | 
			
		||||
						RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID));
 | 
			
		||||
						log_assert(wire);
 | 
			
		||||
						input_sig.append(wire);
 | 
			
		||||
					}
 | 
			
		||||
					// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
 | 
			
		||||
					ce.clear();
 | 
			
		||||
					ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
 | 
			
		||||
					RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size());
 | 
			
		||||
					for (int j = 0; j < (1 << cutLeavesM); ++j) {
 | 
			
		||||
						int gray = j ^ (j >> 1);
 | 
			
		||||
						ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
 | 
			
		||||
						RTLIL::SigBit o(output_sig);
 | 
			
		||||
						bool success YS_ATTRIBUTE(unused) = ce.eval(o);
 | 
			
		||||
						log_assert(success);
 | 
			
		||||
						log_assert(o.wire == nullptr);
 | 
			
		||||
						lut_mask[gray] = o.data;
 | 
			
		||||
					}
 | 
			
		||||
					RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID));
 | 
			
		||||
					log_assert(output_cell);
 | 
			
		||||
					module->remove(output_cell);
 | 
			
		||||
					module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask));
 | 
			
		||||
	for (int c = f.get(); c != EOF; c = f.get()) {
 | 
			
		||||
		// XAIGER extensions
 | 
			
		||||
		if (c == 'm') {
 | 
			
		||||
			uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			uint32_t lutNum = parse_xaiger_literal(f);
 | 
			
		||||
			uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
 | 
			
		||||
			ConstEvalAig ce(module);
 | 
			
		||||
			for (unsigned i = 0; i < lutNum; ++i) {
 | 
			
		||||
				uint32_t rootNodeID = parse_xaiger_literal(f);
 | 
			
		||||
				uint32_t cutLeavesM = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
 | 
			
		||||
				RTLIL::Wire *output_sig = module->wire(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID));
 | 
			
		||||
				log_assert(output_sig);
 | 
			
		||||
				uint32_t nodeID;
 | 
			
		||||
				RTLIL::SigSpec input_sig;
 | 
			
		||||
				for (unsigned j = 0; j < cutLeavesM; ++j) {
 | 
			
		||||
					nodeID = parse_xaiger_literal(f);
 | 
			
		||||
					log_debug2("\t%u\n", nodeID);
 | 
			
		||||
					RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID));
 | 
			
		||||
					log_assert(wire);
 | 
			
		||||
					input_sig.append(wire);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			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));
 | 
			
		||||
			}
 | 
			
		||||
			else if (c == 'n') {
 | 
			
		||||
				parse_xaiger_literal(f);
 | 
			
		||||
				f >> s;
 | 
			
		||||
				log_debug("n: '%s'\n", s.c_str());
 | 
			
		||||
			}
 | 
			
		||||
			else if (c == 'h') {
 | 
			
		||||
				f.ignore(sizeof(uint32_t));
 | 
			
		||||
				uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				log_assert(version == 1);
 | 
			
		||||
				uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("ciNum = %u\n", ciNum);
 | 
			
		||||
				uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("coNum = %u\n", coNum);
 | 
			
		||||
				piNum = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("piNum = %u\n", piNum);
 | 
			
		||||
				uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("poNum = %u\n", poNum);
 | 
			
		||||
				uint32_t boxNum = parse_xaiger_literal(f);
 | 
			
		||||
				log_debug("boxNum = %u\n", boxNum);
 | 
			
		||||
				for (unsigned i = 0; i < boxNum; i++) {
 | 
			
		||||
					f.ignore(2*sizeof(uint32_t));
 | 
			
		||||
					uint32_t boxUniqueId = parse_xaiger_literal(f);
 | 
			
		||||
					log_assert(boxUniqueId > 0);
 | 
			
		||||
					uint32_t oldBoxNum = parse_xaiger_literal(f);
 | 
			
		||||
					RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId));
 | 
			
		||||
					boxes.emplace_back(cell);
 | 
			
		||||
				// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
 | 
			
		||||
				ce.clear();
 | 
			
		||||
				ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
 | 
			
		||||
				RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size());
 | 
			
		||||
				for (int j = 0; j < (1 << cutLeavesM); ++j) {
 | 
			
		||||
					int gray = j ^ (j >> 1);
 | 
			
		||||
					ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
 | 
			
		||||
					RTLIL::SigBit o(output_sig);
 | 
			
		||||
					bool success YS_ATTRIBUTE(unused) = ce.eval(o);
 | 
			
		||||
					log_assert(success);
 | 
			
		||||
					log_assert(o.wire == nullptr);
 | 
			
		||||
					lut_mask[gray] = o.data;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (c == 'a' || c == 'i' || c == 'o') {
 | 
			
		||||
				uint32_t dataSize = parse_xaiger_literal(f);
 | 
			
		||||
				f.ignore(dataSize);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				break;
 | 
			
		||||
				RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
 | 
			
		||||
				log_assert(output_cell);
 | 
			
		||||
				module->remove(output_cell);
 | 
			
		||||
				module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
 | 
			
		||||
		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));
 | 
			
		||||
		}
 | 
			
		||||
		else if (c == 'n') {
 | 
			
		||||
			parse_xaiger_literal(f);
 | 
			
		||||
			f >> s;
 | 
			
		||||
			log_debug("n: '%s'\n", s.c_str());
 | 
			
		||||
		}
 | 
			
		||||
		else if (c == 'h') {
 | 
			
		||||
			f.ignore(sizeof(uint32_t));
 | 
			
		||||
			uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			log_assert(version == 1);
 | 
			
		||||
			uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("ciNum = %u\n", ciNum);
 | 
			
		||||
			uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("coNum = %u\n", coNum);
 | 
			
		||||
			piNum = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("piNum = %u\n", piNum);
 | 
			
		||||
			uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("poNum = %u\n", poNum);
 | 
			
		||||
			uint32_t boxNum = parse_xaiger_literal(f);
 | 
			
		||||
			log_debug("boxNum = %u\n", boxNum);
 | 
			
		||||
			for (unsigned i = 0; i < boxNum; i++) {
 | 
			
		||||
				f.ignore(2*sizeof(uint32_t));
 | 
			
		||||
				uint32_t boxUniqueId = parse_xaiger_literal(f);
 | 
			
		||||
				log_assert(boxUniqueId > 0);
 | 
			
		||||
				uint32_t oldBoxNum = parse_xaiger_literal(f);
 | 
			
		||||
				RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), box_lookup.at(boxUniqueId));
 | 
			
		||||
				boxes.emplace_back(cell);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (c == 'a' || c == 'i' || c == 'o' || c == 's') {
 | 
			
		||||
			uint32_t dataSize = parse_xaiger_literal(f);
 | 
			
		||||
			f.ignore(dataSize);
 | 
			
		||||
			log_debug("ignoring '%c'\n", c);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	post_process();
 | 
			
		||||
| 
						 | 
				
			
			@ -487,13 +508,15 @@ void AigerReader::parse_aiger_ascii()
 | 
			
		|||
	unsigned l1, l2, l3;
 | 
			
		||||
 | 
			
		||||
	// Parse inputs
 | 
			
		||||
	int digits = ceil(log10(I));
 | 
			
		||||
	for (unsigned i = 1; i <= I; ++i, ++line_count) {
 | 
			
		||||
		if (!(f >> l1))
 | 
			
		||||
			log_error("Line %u cannot be interpreted as an input!\n", line_count);
 | 
			
		||||
		log_debug2("%d is an input\n", l1);
 | 
			
		||||
		log_assert(!(l1 & 1)); // Inputs can't be inverted
 | 
			
		||||
		RTLIL::Wire *wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, l1 >> 1));
 | 
			
		||||
		wire->port_input = true;
 | 
			
		||||
		module->connect(createWireIfNotExists(module, l1), wire);
 | 
			
		||||
		inputs.push_back(wire);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -507,12 +530,14 @@ void AigerReader::parse_aiger_ascii()
 | 
			
		|||
		clk_wire->port_input = true;
 | 
			
		||||
		clk_wire->port_output = false;
 | 
			
		||||
	}
 | 
			
		||||
	digits = ceil(log10(L));
 | 
			
		||||
	for (unsigned i = 0; i < L; ++i, ++line_count) {
 | 
			
		||||
		if (!(f >> l1 >> l2))
 | 
			
		||||
			log_error("Line %u cannot be interpreted as a latch!\n", line_count);
 | 
			
		||||
		log_debug2("%d %d is a latch\n", l1, l2);
 | 
			
		||||
		log_assert(!(l1 & 1));
 | 
			
		||||
		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
 | 
			
		||||
		module->connect(createWireIfNotExists(module, l1), q_wire);
 | 
			
		||||
		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
 | 
			
		||||
 | 
			
		||||
		if (clk_wire)
 | 
			
		||||
| 
						 | 
				
			
			@ -550,7 +575,7 @@ void AigerReader::parse_aiger_ascii()
 | 
			
		|||
		log_debug2("%d is an output\n", l1);
 | 
			
		||||
		const unsigned variable = l1 >> 1;
 | 
			
		||||
		const bool invert = l1 & 1;
 | 
			
		||||
		RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix?
 | 
			
		||||
		RTLIL::IdString wire_name(stringf("$%d%s", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix?
 | 
			
		||||
		RTLIL::Wire *wire = module->wire(wire_name);
 | 
			
		||||
		if (!wire)
 | 
			
		||||
			wire = createWireIfNotExists(module, l1);
 | 
			
		||||
| 
						 | 
				
			
			@ -596,7 +621,7 @@ void AigerReader::parse_aiger_ascii()
 | 
			
		|||
		RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
 | 
			
		||||
		RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
 | 
			
		||||
		module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
 | 
			
		||||
		module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire);
 | 
			
		||||
	}
 | 
			
		||||
	std::getline(f, line); // Ignore up to start of next line
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -616,11 +641,12 @@ void AigerReader::parse_aiger_binary()
 | 
			
		|||
	std::string line;
 | 
			
		||||
 | 
			
		||||
	// Parse inputs
 | 
			
		||||
	int digits = ceil(log10(I));
 | 
			
		||||
	for (unsigned i = 1; i <= I; ++i) {
 | 
			
		||||
		log_debug2("%d is an input\n", i);
 | 
			
		||||
		RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
 | 
			
		||||
		RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i));
 | 
			
		||||
		wire->port_input = true;
 | 
			
		||||
		log_assert(!wire->port_output);
 | 
			
		||||
		module->connect(createWireIfNotExists(module, i << 1), wire);
 | 
			
		||||
		inputs.push_back(wire);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -634,12 +660,14 @@ void AigerReader::parse_aiger_binary()
 | 
			
		|||
		clk_wire->port_input = true;
 | 
			
		||||
		clk_wire->port_output = false;
 | 
			
		||||
	}
 | 
			
		||||
	digits = ceil(log10(L));
 | 
			
		||||
	l1 = (I+1) * 2;
 | 
			
		||||
	for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
 | 
			
		||||
		if (!(f >> l2))
 | 
			
		||||
			log_error("Line %u cannot be interpreted as a latch!\n", line_count);
 | 
			
		||||
		log_debug("%d %d is a latch\n", l1, l2);
 | 
			
		||||
		RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
 | 
			
		||||
		module->connect(createWireIfNotExists(module, l1), q_wire);
 | 
			
		||||
		RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
 | 
			
		||||
 | 
			
		||||
		if (clk_wire)
 | 
			
		||||
| 
						 | 
				
			
			@ -670,23 +698,15 @@ void AigerReader::parse_aiger_binary()
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Parse outputs
 | 
			
		||||
	digits = ceil(log10(O));
 | 
			
		||||
	for (unsigned i = 0; i < O; ++i, ++line_count) {
 | 
			
		||||
		if (!(f >> l1))
 | 
			
		||||
			log_error("Line %u cannot be interpreted as an output!\n", line_count);
 | 
			
		||||
 | 
			
		||||
		log_debug2("%d is an output\n", l1);
 | 
			
		||||
		const unsigned variable = l1 >> 1;
 | 
			
		||||
		const bool invert = l1 & 1;
 | 
			
		||||
		RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_b" the right suffix?
 | 
			
		||||
		RTLIL::Wire *wire = module->wire(wire_name);
 | 
			
		||||
		if (!wire)
 | 
			
		||||
			wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		else if (wire->port_input || wire->port_output) {
 | 
			
		||||
			RTLIL::Wire *new_wire = module->addWire(NEW_ID);
 | 
			
		||||
			module->connect(new_wire, wire);
 | 
			
		||||
			wire = new_wire;
 | 
			
		||||
		}
 | 
			
		||||
		RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
 | 
			
		||||
		wire->port_output = true;
 | 
			
		||||
		module->connect(wire, createWireIfNotExists(module, l1));
 | 
			
		||||
		outputs.push_back(wire);
 | 
			
		||||
	}
 | 
			
		||||
	std::getline(f, line); // Ignore up to start of next line
 | 
			
		||||
| 
						 | 
				
			
			@ -727,62 +747,43 @@ void AigerReader::parse_aiger_binary()
 | 
			
		|||
		RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
 | 
			
		||||
		RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
 | 
			
		||||
		RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
 | 
			
		||||
		module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
 | 
			
		||||
		module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AigerReader::post_process()
 | 
			
		||||
{
 | 
			
		||||
	pool<IdString> seen_boxes;
 | 
			
		||||
	unsigned ci_count = 0, co_count = 0;
 | 
			
		||||
	dict<IdString, std::vector<IdString>> box_ports;
 | 
			
		||||
	unsigned ci_count = 0, co_count = 0, flop_count = 0;
 | 
			
		||||
	for (auto cell : boxes) {
 | 
			
		||||
		RTLIL::Module* box_module = design->module(cell->type);
 | 
			
		||||
		log_assert(box_module);
 | 
			
		||||
 | 
			
		||||
		if (seen_boxes.insert(cell->type).second) {
 | 
			
		||||
			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("'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("'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("'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(); ) {
 | 
			
		||||
					RTLIL::Wire* w = box_module->wire(*jt);
 | 
			
		||||
					log_assert(w);
 | 
			
		||||
					if (w == carry_in || w == carry_out) {
 | 
			
		||||
						jt = ports.erase(jt);
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					if (w->port_id > carry_in->port_id)
 | 
			
		||||
						--w->port_id;
 | 
			
		||||
					if (w->port_id > carry_out->port_id)
 | 
			
		||||
						--w->port_id;
 | 
			
		||||
					log_assert(w->port_input || w->port_output);
 | 
			
		||||
					log_assert(ports[w->port_id-1] == w->name);
 | 
			
		||||
					++jt;
 | 
			
		||||
		auto r = box_ports.insert(cell->type);
 | 
			
		||||
		if (r.second) {
 | 
			
		||||
			// Make carry in the last PI, and carry out the last PO
 | 
			
		||||
			//   since ABC requires it this way
 | 
			
		||||
			IdString carry_in, carry_out;
 | 
			
		||||
			for (const auto &port_name : box_module->ports) {
 | 
			
		||||
				auto w = box_module->wire(port_name);
 | 
			
		||||
				log_assert(w);
 | 
			
		||||
				if (w->get_bool_attribute("\\abc9_carry")) {
 | 
			
		||||
					if (w->port_input)
 | 
			
		||||
						carry_in = port_name;
 | 
			
		||||
					if (w->port_output)
 | 
			
		||||
						carry_out = port_name;
 | 
			
		||||
				}
 | 
			
		||||
				ports.push_back(carry_in->name);
 | 
			
		||||
				carry_in->port_id = ports.size();
 | 
			
		||||
				ports.push_back(carry_out->name);
 | 
			
		||||
				carry_out->port_id = ports.size();
 | 
			
		||||
				else
 | 
			
		||||
					r.first->second.push_back(port_name);
 | 
			
		||||
			}
 | 
			
		||||
			if (carry_in != IdString()) {
 | 
			
		||||
				log_assert(carry_out != IdString());
 | 
			
		||||
				r.first->second.push_back(carry_in);
 | 
			
		||||
				r.first->second.push_back(carry_out);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// NB: Assume box_module->ports are sorted alphabetically
 | 
			
		||||
		//     (as RTLIL::Module::fixup_ports() would do)
 | 
			
		||||
		for (auto port_name : box_module->ports) {
 | 
			
		||||
		for (auto port_name : box_ports.at(cell->type)) {
 | 
			
		||||
			RTLIL::Wire* port = box_module->wire(port_name);
 | 
			
		||||
			log_assert(port);
 | 
			
		||||
			RTLIL::SigSpec rhs;
 | 
			
		||||
| 
						 | 
				
			
			@ -804,9 +805,32 @@ void AigerReader::post_process()
 | 
			
		|||
				}
 | 
			
		||||
				rhs.append(wire);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cell->setPort(port_name, rhs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (box_module->attributes.count("\\abc9_flop")) {
 | 
			
		||||
			log_assert(co_count < outputs.size());
 | 
			
		||||
			Wire *wire = outputs[co_count++];
 | 
			
		||||
			log_assert(wire);
 | 
			
		||||
			log_assert(wire->port_output);
 | 
			
		||||
			wire->port_output = false;
 | 
			
		||||
 | 
			
		||||
			RTLIL::Wire *d = outputs[outputs.size() - flopNum + flop_count];
 | 
			
		||||
			log_assert(d);
 | 
			
		||||
			log_assert(d->port_output);
 | 
			
		||||
			d->port_output = false;
 | 
			
		||||
 | 
			
		||||
			RTLIL::Wire *q = inputs[piNum - flopNum + flop_count];
 | 
			
		||||
			log_assert(q);
 | 
			
		||||
			log_assert(q->port_input);
 | 
			
		||||
			q->port_input = false;
 | 
			
		||||
 | 
			
		||||
			auto ff = module->addCell(NEW_ID, "$__ABC9_FF_");
 | 
			
		||||
			ff->setPort("\\D", d);
 | 
			
		||||
			ff->setPort("\\Q", q);
 | 
			
		||||
			flop_count++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dict<RTLIL::IdString, int> wideports_cache;
 | 
			
		||||
| 
						 | 
				
			
			@ -868,16 +892,7 @@ void AigerReader::post_process()
 | 
			
		|||
					// simply connect the latter to the former
 | 
			
		||||
					RTLIL::Wire* existing = module->wire(escaped_s);
 | 
			
		||||
					if (!existing) {
 | 
			
		||||
						if (escaped_s.ends_with("$inout.out")) {
 | 
			
		||||
							wire->port_output = false;
 | 
			
		||||
							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;
 | 
			
		||||
							module->connect(in_wire, wire);
 | 
			
		||||
						}
 | 
			
		||||
						else
 | 
			
		||||
							module->rename(wire, escaped_s);
 | 
			
		||||
						module->rename(wire, escaped_s);
 | 
			
		||||
					}
 | 
			
		||||
					else {
 | 
			
		||||
						wire->port_output = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -889,19 +904,9 @@ void AigerReader::post_process()
 | 
			
		|||
					std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
 | 
			
		||||
					RTLIL::Wire* existing = module->wire(indexed_name);
 | 
			
		||||
					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(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;
 | 
			
		||||
							module->connect(in_wire, wire);
 | 
			
		||||
						}
 | 
			
		||||
						else {
 | 
			
		||||
							module->rename(wire, indexed_name);
 | 
			
		||||
							if (wideports)
 | 
			
		||||
								wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
 | 
			
		||||
						}
 | 
			
		||||
						module->rename(wire, indexed_name);
 | 
			
		||||
						if (wideports)
 | 
			
		||||
							wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
 | 
			
		||||
					}
 | 
			
		||||
					else {
 | 
			
		||||
						module->connect(wire, existing);
 | 
			
		||||
| 
						 | 
				
			
			@ -909,9 +914,13 @@ void AigerReader::post_process()
 | 
			
		|||
					}
 | 
			
		||||
				}
 | 
			
		||||
				log_debug(" -> %s\n", log_id(wire));
 | 
			
		||||
				int init;
 | 
			
		||||
				mf >> init;
 | 
			
		||||
				if (init < 2)
 | 
			
		||||
					wire->attributes["\\init"] = init;
 | 
			
		||||
			}
 | 
			
		||||
			else if (type == "box") {
 | 
			
		||||
				RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
 | 
			
		||||
				RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
 | 
			
		||||
				if (cell) { // ABC could have optimised this box away
 | 
			
		||||
					module->rename(cell, escaped_s);
 | 
			
		||||
					for (const auto &i : cell->connections()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -968,15 +977,10 @@ void AigerReader::post_process()
 | 
			
		|||
			if (other_wire) {
 | 
			
		||||
				other_wire->port_input = false;
 | 
			
		||||
				other_wire->port_output = false;
 | 
			
		||||
			}
 | 
			
		||||
			if (wire->port_input) {
 | 
			
		||||
				if (other_wire)
 | 
			
		||||
				if (wire->port_input)
 | 
			
		||||
					module->connect(other_wire, SigSpec(wire, i));
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
								  // Since we skip POs that are connected to Sx,
 | 
			
		||||
								  // re-connect them here
 | 
			
		||||
				module->connect(SigSpec(wire, i), other_wire ? other_wire : SigSpec(RTLIL::Sx));
 | 
			
		||||
				else
 | 
			
		||||
					module->connect(SigSpec(wire, i), other_wire);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -997,9 +1001,9 @@ void AigerReader::post_process()
 | 
			
		|||
		if (cell->type != "$lut") continue;
 | 
			
		||||
		auto y_port = cell->getPort("\\Y").as_bit();
 | 
			
		||||
		if (y_port.wire->width == 1)
 | 
			
		||||
			module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str()));
 | 
			
		||||
			module->rename(cell, stringf("$lut%s", y_port.wire->name.c_str()));
 | 
			
		||||
		else
 | 
			
		||||
			module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset));
 | 
			
		||||
			module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name.c_str(), y_port.offset));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1032,7 +1036,7 @@ struct AigerFrontend : public Frontend {
 | 
			
		|||
	{
 | 
			
		||||
		log_header(design, "Executing AIGER frontend.\n");
 | 
			
		||||
 | 
			
		||||
		RTLIL::IdString clk_name = "\\clk";
 | 
			
		||||
		RTLIL::IdString clk_name;
 | 
			
		||||
		RTLIL::IdString module_name;
 | 
			
		||||
		std::string map_filename;
 | 
			
		||||
		bool wideports = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ struct AigerReader
 | 
			
		|||
    RTLIL::Module *module;
 | 
			
		||||
    std::string map_filename;
 | 
			
		||||
    bool wideports;
 | 
			
		||||
    const int aiger_autoidx;
 | 
			
		||||
 | 
			
		||||
    unsigned M, I, L, O, A;
 | 
			
		||||
    unsigned B, C, J, F; // Optional in AIGER 1.9
 | 
			
		||||
| 
						 | 
				
			
			@ -47,10 +48,12 @@ struct AigerReader
 | 
			
		|||
 | 
			
		||||
    AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);
 | 
			
		||||
    void parse_aiger();
 | 
			
		||||
    void parse_xaiger(const dict<int,IdString> &box_lookup);
 | 
			
		||||
    void parse_xaiger();
 | 
			
		||||
    void parse_aiger_ascii();
 | 
			
		||||
    void parse_aiger_binary();
 | 
			
		||||
    void post_process();
 | 
			
		||||
 | 
			
		||||
    RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
YOSYS_NAMESPACE_END
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1198,6 +1198,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 | 
			
		|||
		varbuf = new AstNode(AST_LOCALPARAM, varbuf);
 | 
			
		||||
		varbuf->str = init_ast->children[0]->str;
 | 
			
		||||
 | 
			
		||||
		auto resolved = current_scope.at(init_ast->children[0]->str);
 | 
			
		||||
		if (resolved->range_valid) {
 | 
			
		||||
			varbuf->range_left = resolved->range_left;
 | 
			
		||||
			varbuf->range_right = resolved->range_right;
 | 
			
		||||
			varbuf->range_swapped = resolved->range_swapped;
 | 
			
		||||
			varbuf->range_valid = resolved->range_valid;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		AstNode *backup_scope_varbuf = current_scope[varbuf->str];
 | 
			
		||||
		current_scope[varbuf->str] = varbuf;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2998,6 +3006,14 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
 | 
			
		|||
			current_ast_mod->children.push_back(p);
 | 
			
		||||
			str = p->str;
 | 
			
		||||
			id2ast = p;
 | 
			
		||||
 | 
			
		||||
			auto resolved = current_scope.at(index_var);
 | 
			
		||||
			if (resolved->range_valid) {
 | 
			
		||||
				p->range_left = resolved->range_left;
 | 
			
		||||
				p->range_right = resolved->range_right;
 | 
			
		||||
				p->range_swapped = resolved->range_swapped;
 | 
			
		||||
				p->range_valid = resolved->range_valid;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -430,10 +430,14 @@ sigspec:
 | 
			
		|||
		free($1);
 | 
			
		||||
	} |
 | 
			
		||||
	sigspec '[' TOK_INT ']' {
 | 
			
		||||
		if ($3 >= $1->size() || $3 < 0)
 | 
			
		||||
			rtlil_frontend_ilang_yyerror("bit index out of range");
 | 
			
		||||
		$$ = new RTLIL::SigSpec($1->extract($3));
 | 
			
		||||
		delete $1;
 | 
			
		||||
	} |
 | 
			
		||||
	sigspec '[' TOK_INT ':' TOK_INT ']' {
 | 
			
		||||
		if ($3 >= $1->size() || $3 < 0 || $3 < $5)
 | 
			
		||||
			rtlil_frontend_ilang_yyerror("invalid slice");
 | 
			
		||||
		$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
 | 
			
		||||
		delete $1;
 | 
			
		||||
	} |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,11 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
This directory contains Verific bindings for Yosys.
 | 
			
		||||
See http://www.verific.com/ for details.
 | 
			
		||||
 | 
			
		||||
Use Symbiotic EDA Suite if you need Yosys+Verifc.
 | 
			
		||||
https://www.symbioticeda.com/seda-suite
 | 
			
		||||
 | 
			
		||||
Contact office@symbioticeda.com for free evaluation
 | 
			
		||||
binaries of Symbiotic EDA Suite.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Verific Features that should be enabled in your Verific library
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ RTLIL::SigBit VerificImporter::net_map_at(Net *net)
 | 
			
		|||
 | 
			
		||||
bool is_blackbox(Netlist *nl)
 | 
			
		||||
{
 | 
			
		||||
	if (nl->IsBlackBox())
 | 
			
		||||
	if (nl->IsBlackBox() || nl->IsEmptyBox())
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	const char *attr = nl->GetAttValue("blackbox");
 | 
			
		||||
| 
						 | 
				
			
			@ -784,15 +784,15 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
 | 
			
		|||
		merge_past_ffs_clock(it.second, it.first.first, it.first.second);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo)
 | 
			
		||||
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool norename)
 | 
			
		||||
{
 | 
			
		||||
	std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
 | 
			
		||||
	std::string module_name = netlist_name;
 | 
			
		||||
 | 
			
		||||
	if (nl->IsOperator()) {
 | 
			
		||||
	if (nl->IsOperator() || nl->IsPrimitive()) {
 | 
			
		||||
		module_name = "$verific$" + module_name;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (*nl->Name()) {
 | 
			
		||||
		if (!norename && *nl->Name()) {
 | 
			
		||||
			module_name += "(";
 | 
			
		||||
			module_name += nl->Name();
 | 
			
		||||
			module_name += ")";
 | 
			
		||||
| 
						 | 
				
			
			@ -1409,7 +1409,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
 | 
			
		|||
 | 
			
		||||
		std::string inst_type = inst->View()->Owner()->Name();
 | 
			
		||||
 | 
			
		||||
		if (inst->View()->IsOperator()) {
 | 
			
		||||
		if (inst->View()->IsOperator() || inst->View()->IsPrimitive()) {
 | 
			
		||||
			inst_type = "$verific$" + inst_type;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (*inst->View()->Name()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1899,7 +1899,7 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
 | 
			
		|||
		Netlist *nl = *nl_todo.begin();
 | 
			
		||||
		if (nl_done.count(nl) == 0) {
 | 
			
		||||
			VerificImporter importer(false, false, false, false, false, false, false);
 | 
			
		||||
			importer.import_netlist(design, nl, nl_todo);
 | 
			
		||||
			importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == top);
 | 
			
		||||
		}
 | 
			
		||||
		nl_todo.erase(nl);
 | 
			
		||||
		nl_done.insert(nl);
 | 
			
		||||
| 
						 | 
				
			
			@ -2065,7 +2065,12 @@ struct VerificPass : public Pass {
 | 
			
		|||
		log("  -d <dump_file>\n");
 | 
			
		||||
		log("    Dump the Verific netlist as a verilog file.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Visit http://verific.com/ for more information on Verific.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n");
 | 
			
		||||
		log("https://www.symbioticeda.com/seda-suite\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Contact office@symbioticeda.com for free evaluation\n");
 | 
			
		||||
		log("binaries of Symbiotic EDA Suite.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
#ifdef YOSYS_ENABLE_VERIFIC
 | 
			
		||||
| 
						 | 
				
			
			@ -2074,7 +2079,13 @@ struct VerificPass : public Pass {
 | 
			
		|||
		static bool set_verific_global_flags = true;
 | 
			
		||||
 | 
			
		||||
		if (check_noverific_env())
 | 
			
		||||
			log_cmd_error("This version of Yosys is built without Verific support.\n");
 | 
			
		||||
			log_cmd_error("This version of Yosys is built without Verific support.\n"
 | 
			
		||||
					"\n"
 | 
			
		||||
					"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"
 | 
			
		||||
					"https://www.symbioticeda.com/seda-suite\n"
 | 
			
		||||
					"\n"
 | 
			
		||||
					"Contact office@symbioticeda.com for free evaluation\n"
 | 
			
		||||
					"binaries of Symbiotic EDA Suite.\n");
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2373,6 +2384,8 @@ struct VerificPass : public Pass {
 | 
			
		|||
			if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0)
 | 
			
		||||
				cmd_error(args, argidx, "unknown option");
 | 
			
		||||
 | 
			
		||||
			std::set<std::string> top_mod_names;
 | 
			
		||||
 | 
			
		||||
			if (mode_all)
 | 
			
		||||
			{
 | 
			
		||||
				log("Running hier_tree::ElaborateAll().\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -2401,6 +2414,7 @@ struct VerificPass : public Pass {
 | 
			
		|||
				for (; argidx < GetSize(args); argidx++)
 | 
			
		||||
				{
 | 
			
		||||
					const char *name = args[argidx].c_str();
 | 
			
		||||
					top_mod_names.insert(name);
 | 
			
		||||
					VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
 | 
			
		||||
 | 
			
		||||
					if (veri_lib) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2466,7 +2480,7 @@ struct VerificPass : public Pass {
 | 
			
		|||
				if (nl_done.count(nl) == 0) {
 | 
			
		||||
					VerificImporter importer(mode_gates, mode_keep, mode_nosva,
 | 
			
		||||
							mode_names, mode_verific, mode_autocover, mode_fullinit);
 | 
			
		||||
					importer.import_netlist(design, nl, nl_todo);
 | 
			
		||||
					importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->Owner()->Name()));
 | 
			
		||||
				}
 | 
			
		||||
				nl_todo.erase(nl);
 | 
			
		||||
				nl_done.insert(nl);
 | 
			
		||||
| 
						 | 
				
			
			@ -2490,7 +2504,13 @@ struct VerificPass : public Pass {
 | 
			
		|||
	}
 | 
			
		||||
#else /* YOSYS_ENABLE_VERIFIC */
 | 
			
		||||
	void execute(std::vector<std::string>, RTLIL::Design *) YS_OVERRIDE {
 | 
			
		||||
		log_cmd_error("This version of Yosys is built without Verific support.\n");
 | 
			
		||||
		log_cmd_error("This version of Yosys is built without Verific support.\n"
 | 
			
		||||
				"\n"
 | 
			
		||||
				"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"
 | 
			
		||||
				"https://www.symbioticeda.com/seda-suite\n"
 | 
			
		||||
				"\n"
 | 
			
		||||
				"Contact office@symbioticeda.com for free evaluation\n"
 | 
			
		||||
				"binaries of Symbiotic EDA Suite.\n");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
} VerificPass;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ struct VerificImporter
 | 
			
		|||
	void merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol);
 | 
			
		||||
	void merge_past_ffs(pool<RTLIL::Cell*> &candidates);
 | 
			
		||||
 | 
			
		||||
	void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo);
 | 
			
		||||
	void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo, bool norename = false);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,8 @@
 | 
			
		|||
// basic_property:
 | 
			
		||||
//   sequence
 | 
			
		||||
//   not basic_property
 | 
			
		||||
//   nexttime basic_property
 | 
			
		||||
//   nexttime[N] basic_property
 | 
			
		||||
//   sequence #-# basic_property
 | 
			
		||||
//   sequence #=# basic_property
 | 
			
		||||
//   basic_property or basic_property           (cover only)
 | 
			
		||||
| 
						 | 
				
			
			@ -1264,6 +1266,26 @@ struct VerificSvaImporter
 | 
			
		|||
			return node;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (inst->Type() == PRIM_SVA_NEXTTIME || inst->Type() == PRIM_SVA_S_NEXTTIME)
 | 
			
		||||
		{
 | 
			
		||||
			const char *sva_low_s = inst->GetAttValue("sva:low");
 | 
			
		||||
			const char *sva_high_s = inst->GetAttValue("sva:high");
 | 
			
		||||
 | 
			
		||||
			int sva_low = atoi(sva_low_s);
 | 
			
		||||
			int sva_high = atoi(sva_high_s);
 | 
			
		||||
			log_assert(sva_low == sva_high);
 | 
			
		||||
 | 
			
		||||
			int node = start_node;
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < sva_low; i++) {
 | 
			
		||||
				int next_node = fsm.createNode();
 | 
			
		||||
				fsm.createEdge(node, next_node);
 | 
			
		||||
				node = next_node;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return parse_sequence(fsm, node, inst->GetInput());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
 | 
			
		||||
		{
 | 
			
		||||
			const char *sva_low_s = inst->GetAttValue("sva:low");
 | 
			
		||||
| 
						 | 
				
			
			@ -1590,15 +1612,25 @@ struct VerificSvaImporter
 | 
			
		|||
			Instance *consequent_inst = net_to_ast_driver(consequent_net);
 | 
			
		||||
 | 
			
		||||
			if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL ||
 | 
			
		||||
					consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH))
 | 
			
		||||
					consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH ||
 | 
			
		||||
					consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS))
 | 
			
		||||
			{
 | 
			
		||||
				bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH;
 | 
			
		||||
 | 
			
		||||
				Net *until_net = consequent_inst->GetInput2();
 | 
			
		||||
				consequent_net = consequent_inst->GetInput1();
 | 
			
		||||
				consequent_inst = net_to_ast_driver(consequent_net);
 | 
			
		||||
				Net *until_net = nullptr;
 | 
			
		||||
				if (consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS)
 | 
			
		||||
				{
 | 
			
		||||
					consequent_net = consequent_inst->GetInput();
 | 
			
		||||
					consequent_inst = net_to_ast_driver(consequent_net);
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					until_net = consequent_inst->GetInput2();
 | 
			
		||||
					consequent_net = consequent_inst->GetInput1();
 | 
			
		||||
					consequent_inst = net_to_ast_driver(consequent_net);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				SigBit until_sig = parse_expression(until_net);
 | 
			
		||||
				SigBit until_sig = until_net ? parse_expression(until_net) : RTLIL::S0;
 | 
			
		||||
				SigBit not_until_sig = module->Not(NEW_ID, until_sig);
 | 
			
		||||
				antecedent_fsm.createEdge(node, node, not_until_sig);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@
 | 
			
		|||
 *
 | 
			
		||||
 *  Ad-hoc implementation of a Verilog preprocessor. The directives `define,
 | 
			
		||||
 *  `include, `ifdef, `ifndef, `else and `endif are handled here. All other
 | 
			
		||||
 *  directives are handled by the lexer (see lexer.l).
 | 
			
		||||
 *  directives are handled by the lexer (see verilog_lexer.l).
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@
 | 
			
		|||
 *
 | 
			
		||||
 *  A simple lexer for Verilog code. Non-preprocessor compiler directives are
 | 
			
		||||
 *  handled here. The preprocessor stuff is handled in preproc.cc. Everything
 | 
			
		||||
 *  else is left to the bison parser (see parser.y).
 | 
			
		||||
 *  else is left to the bison parser (see verilog_parser.y).
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -188,9 +188,9 @@ YOSYS_NAMESPACE_END
 | 
			
		|||
"unique0"      { SV_KEYWORD(TOK_UNIQUE); }
 | 
			
		||||
"priority"     { SV_KEYWORD(TOK_PRIORITY); }
 | 
			
		||||
 | 
			
		||||
"always_comb"  { SV_KEYWORD(TOK_ALWAYS); }
 | 
			
		||||
"always_ff"    { SV_KEYWORD(TOK_ALWAYS); }
 | 
			
		||||
"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
 | 
			
		||||
"always_comb"  { SV_KEYWORD(TOK_ALWAYS_COMB); }
 | 
			
		||||
"always_ff"    { SV_KEYWORD(TOK_ALWAYS_FF); }
 | 
			
		||||
"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); }
 | 
			
		||||
 | 
			
		||||
 /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
 | 
			
		||||
    to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,6 +141,7 @@ struct specify_rise_fall {
 | 
			
		|||
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
 | 
			
		||||
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
 | 
			
		||||
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
 | 
			
		||||
%token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH
 | 
			
		||||
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
 | 
			
		||||
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
 | 
			
		||||
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +157,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 hierarchical_type_id
 | 
			
		||||
%type <boolean> opt_signed opt_property unique_case_attr
 | 
			
		||||
%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
 | 
			
		||||
%type <al> attr case_attr
 | 
			
		||||
 | 
			
		||||
%type <specify_target_ptr> specify_target
 | 
			
		||||
| 
						 | 
				
			
			@ -1581,10 +1582,28 @@ cell_port:
 | 
			
		|||
		free_attr($1);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
always_comb_or_latch:
 | 
			
		||||
	TOK_ALWAYS_COMB {
 | 
			
		||||
		$$ = false;
 | 
			
		||||
	} |
 | 
			
		||||
	TOK_ALWAYS_LATCH {
 | 
			
		||||
		$$ = true;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
always_or_always_ff:
 | 
			
		||||
	TOK_ALWAYS {
 | 
			
		||||
		$$ = false;
 | 
			
		||||
	} |
 | 
			
		||||
	TOK_ALWAYS_FF {
 | 
			
		||||
		$$ = true;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
always_stmt:
 | 
			
		||||
	attr TOK_ALWAYS {
 | 
			
		||||
	attr always_or_always_ff {
 | 
			
		||||
		AstNode *node = new AstNode(AST_ALWAYS);
 | 
			
		||||
		append_attr(node, $1);
 | 
			
		||||
		if ($2)
 | 
			
		||||
			node->attributes[ID(always_ff)] = AstNode::mkconst_int(1, false);
 | 
			
		||||
		ast_stack.back()->children.push_back(node);
 | 
			
		||||
		ast_stack.push_back(node);
 | 
			
		||||
	} always_cond {
 | 
			
		||||
| 
						 | 
				
			
			@ -1595,6 +1614,22 @@ always_stmt:
 | 
			
		|||
		ast_stack.pop_back();
 | 
			
		||||
		ast_stack.pop_back();
 | 
			
		||||
	} |
 | 
			
		||||
	attr always_comb_or_latch {
 | 
			
		||||
		AstNode *node = new AstNode(AST_ALWAYS);
 | 
			
		||||
		append_attr(node, $1);
 | 
			
		||||
		if ($2)
 | 
			
		||||
			node->attributes[ID(always_latch)] = AstNode::mkconst_int(1, false);
 | 
			
		||||
		else
 | 
			
		||||
			node->attributes[ID(always_comb)] = AstNode::mkconst_int(1, false);
 | 
			
		||||
		ast_stack.back()->children.push_back(node);
 | 
			
		||||
		ast_stack.push_back(node);
 | 
			
		||||
		AstNode *block = new AstNode(AST_BLOCK);
 | 
			
		||||
		ast_stack.back()->children.push_back(block);
 | 
			
		||||
		ast_stack.push_back(block);
 | 
			
		||||
	} behavioral_stmt {
 | 
			
		||||
		ast_stack.pop_back();
 | 
			
		||||
		ast_stack.pop_back();
 | 
			
		||||
	} |
 | 
			
		||||
	attr TOK_INITIAL {
 | 
			
		||||
		AstNode *node = new AstNode(AST_INITIAL);
 | 
			
		||||
		append_attr(node, $1);
 | 
			
		||||
| 
						 | 
				
			
			@ -2207,7 +2242,7 @@ gen_stmt:
 | 
			
		|||
		ast_stack.back()->children.push_back(node);
 | 
			
		||||
		ast_stack.push_back(node);
 | 
			
		||||
	} opt_arg_list ';'{
 | 
			
		||||
		ast_stack.pop_back();		
 | 
			
		||||
		ast_stack.pop_back();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
gen_stmt_block:
 | 
			
		||||
| 
						 | 
				
			
			@ -2378,19 +2413,19 @@ basic_expr:
 | 
			
		|||
		append_attr($$, $2);
 | 
			
		||||
	} |
 | 
			
		||||
	basic_expr OP_SHL attr basic_expr {
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_LEFT, $1, $4);
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_LEFT, $1, new AstNode(AST_TO_UNSIGNED, $4));
 | 
			
		||||
		append_attr($$, $3);
 | 
			
		||||
	} |
 | 
			
		||||
	basic_expr OP_SHR attr basic_expr {
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_RIGHT, $1, $4);
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_RIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4));
 | 
			
		||||
		append_attr($$, $3);
 | 
			
		||||
	} |
 | 
			
		||||
	basic_expr OP_SSHL attr basic_expr {
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_SLEFT, $1, $4);
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_SLEFT, $1, new AstNode(AST_TO_UNSIGNED, $4));
 | 
			
		||||
		append_attr($$, $3);
 | 
			
		||||
	} |
 | 
			
		||||
	basic_expr OP_SSHR attr basic_expr {
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4);
 | 
			
		||||
		$$ = new AstNode(AST_SHIFT_SRIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4));
 | 
			
		||||
		append_attr($$, $3);
 | 
			
		||||
	} |
 | 
			
		||||
	basic_expr '<' attr basic_expr {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -566,34 +566,22 @@ int main(int argc, char **argv)
 | 
			
		|||
#else
 | 
			
		||||
		std::string meminfo;
 | 
			
		||||
		std::string stats_divider = ", ";
 | 
			
		||||
#  if defined(__linux__)
 | 
			
		||||
		std::ifstream statm;
 | 
			
		||||
		statm.open(stringf("/proc/%lld/statm", (long long)getpid()));
 | 
			
		||||
		if (statm.is_open()) {
 | 
			
		||||
			int sz_total, sz_resident;
 | 
			
		||||
			statm >> sz_total >> sz_resident;
 | 
			
		||||
			meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident",
 | 
			
		||||
					sz_total * (getpagesize() / 1024.0 / 1024.0),
 | 
			
		||||
					sz_resident * (getpagesize() / 1024.0 / 1024.0));
 | 
			
		||||
			stats_divider = "\n";
 | 
			
		||||
		}
 | 
			
		||||
#  elif defined(__FreeBSD__)
 | 
			
		||||
		pid_t pid = getpid();
 | 
			
		||||
		int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid};
 | 
			
		||||
		struct kinfo_proc kip;
 | 
			
		||||
		size_t kip_len = sizeof(kip);
 | 
			
		||||
		if (sysctl(mib, 4, &kip, &kip_len, NULL, 0) == 0) {
 | 
			
		||||
			vm_size_t sz_total = kip.ki_size;
 | 
			
		||||
			segsz_t sz_resident = kip.ki_rssize;
 | 
			
		||||
			meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident",
 | 
			
		||||
				(int)sz_total / 1024.0 / 1024.0,
 | 
			
		||||
				(int)sz_resident * (getpagesize() / 1024.0 / 1024.0));
 | 
			
		||||
			stats_divider = "\n";
 | 
			
		||||
		}
 | 
			
		||||
#  endif
 | 
			
		||||
 | 
			
		||||
		struct rusage ru_buffer;
 | 
			
		||||
		getrusage(RUSAGE_SELF, &ru_buffer);
 | 
			
		||||
		if (yosys_design->scratchpad_get_bool("print_stats.include_children")) {
 | 
			
		||||
			struct rusage ru_buffer_children;
 | 
			
		||||
			getrusage(RUSAGE_CHILDREN, &ru_buffer_children);
 | 
			
		||||
			ru_buffer.ru_utime.tv_sec += ru_buffer_children.ru_utime.tv_sec;
 | 
			
		||||
			ru_buffer.ru_utime.tv_usec += ru_buffer_children.ru_utime.tv_usec;
 | 
			
		||||
			ru_buffer.ru_stime.tv_sec += ru_buffer_children.ru_stime.tv_sec;
 | 
			
		||||
			ru_buffer.ru_stime.tv_usec += ru_buffer_children.ru_stime.tv_usec;
 | 
			
		||||
			ru_buffer.ru_maxrss = std::max(ru_buffer.ru_maxrss, ru_buffer_children.ru_maxrss);
 | 
			
		||||
		}
 | 
			
		||||
#  if defined(__linux__) || defined(__FreeBSD__)
 | 
			
		||||
		meminfo = stringf(", MEM: %.2f MB peak",
 | 
			
		||||
				ru_buffer.ru_maxrss / 1024.0);
 | 
			
		||||
#endif
 | 
			
		||||
		log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash.c_str(),
 | 
			
		||||
				stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
 | 
			
		||||
				ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,20 +114,35 @@ void Pass::run_register()
 | 
			
		|||
 | 
			
		||||
void Pass::init_register()
 | 
			
		||||
{
 | 
			
		||||
	vector<Pass*> added_passes;
 | 
			
		||||
	while (first_queued_pass) {
 | 
			
		||||
		added_passes.push_back(first_queued_pass);
 | 
			
		||||
		first_queued_pass->run_register();
 | 
			
		||||
		first_queued_pass = first_queued_pass->next_queued_pass;
 | 
			
		||||
	}
 | 
			
		||||
	for (auto added_pass : added_passes)
 | 
			
		||||
		added_pass->on_register();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Pass::done_register()
 | 
			
		||||
{
 | 
			
		||||
	for (auto &it : pass_register)
 | 
			
		||||
		it.second->on_shutdown();
 | 
			
		||||
 | 
			
		||||
	frontend_register.clear();
 | 
			
		||||
	pass_register.clear();
 | 
			
		||||
	backend_register.clear();
 | 
			
		||||
	log_assert(first_queued_pass == NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Pass::on_register()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Pass::on_shutdown()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Pass::~Pass()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,6 +62,9 @@ struct Pass
 | 
			
		|||
	virtual void run_register();
 | 
			
		||||
	static void init_register();
 | 
			
		||||
	static void done_register();
 | 
			
		||||
 | 
			
		||||
	virtual void on_register();
 | 
			
		||||
	virtual void on_shutdown();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ScriptPass : Pass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ IdString RTLIL::ID::Y;
 | 
			
		|||
IdString RTLIL::ID::keep;
 | 
			
		||||
IdString RTLIL::ID::whitebox;
 | 
			
		||||
IdString RTLIL::ID::blackbox;
 | 
			
		||||
dict<std::string, std::string> RTLIL::constpad;
 | 
			
		||||
 | 
			
		||||
RTLIL::Const::Const()
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -783,6 +784,14 @@ namespace {
 | 
			
		|||
			return v;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int param_bool(RTLIL::IdString name, bool expected)
 | 
			
		||||
		{
 | 
			
		||||
			int v = param_bool(name);
 | 
			
		||||
			if (v != expected)
 | 
			
		||||
				error(__LINE__);
 | 
			
		||||
			return v;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void param_bits(RTLIL::IdString name, int width)
 | 
			
		||||
		{
 | 
			
		||||
			param(name);
 | 
			
		||||
| 
						 | 
				
			
			@ -869,13 +878,23 @@ namespace {
 | 
			
		|||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) {
 | 
			
		||||
			if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr))) {
 | 
			
		||||
				param_bool(ID(A_SIGNED));
 | 
			
		||||
				param_bool(ID(B_SIGNED), /*expected=*/false);
 | 
			
		||||
				port(ID::A, param(ID(A_WIDTH)));
 | 
			
		||||
				port(ID::B, param(ID(B_WIDTH)));
 | 
			
		||||
				port(ID::Y, param(ID(Y_WIDTH)));
 | 
			
		||||
				check_expected(/*check_matched_sign=*/false);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cell->type.in(ID($shift), ID($shiftx))) {
 | 
			
		||||
				param_bool(ID(A_SIGNED));
 | 
			
		||||
				param_bool(ID(B_SIGNED));
 | 
			
		||||
				port(ID::A, param(ID(A_WIDTH)));
 | 
			
		||||
				port(ID::B, param(ID(B_WIDTH)));
 | 
			
		||||
				port(ID::Y, param(ID(Y_WIDTH)));
 | 
			
		||||
				check_expected(false);
 | 
			
		||||
				check_expected(/*check_matched_sign=*/false);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -957,7 +976,7 @@ namespace {
 | 
			
		|||
				port(ID::A, param(ID(A_WIDTH)));
 | 
			
		||||
				port(ID::B, param(ID(B_WIDTH)));
 | 
			
		||||
				port(ID::Y, param(ID(Y_WIDTH)));
 | 
			
		||||
				check_expected(false);
 | 
			
		||||
				check_expected(/*check_matched_sign=*/false);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1875,10 +1894,6 @@ DEF_METHOD(And,      max(sig_a.size(), sig_b.size()), ID($and))
 | 
			
		|||
DEF_METHOD(Or,       max(sig_a.size(), sig_b.size()), ID($or))
 | 
			
		||||
DEF_METHOD(Xor,      max(sig_a.size(), sig_b.size()), ID($xor))
 | 
			
		||||
DEF_METHOD(Xnor,     max(sig_a.size(), sig_b.size()), ID($xnor))
 | 
			
		||||
DEF_METHOD(Shl,      sig_a.size(), ID($shl))
 | 
			
		||||
DEF_METHOD(Shr,      sig_a.size(), ID($shr))
 | 
			
		||||
DEF_METHOD(Sshl,     sig_a.size(), ID($sshl))
 | 
			
		||||
DEF_METHOD(Sshr,     sig_a.size(), ID($sshr))
 | 
			
		||||
DEF_METHOD(Shift,    sig_a.size(), ID($shift))
 | 
			
		||||
DEF_METHOD(Shiftx,   sig_a.size(), ID($shiftx))
 | 
			
		||||
DEF_METHOD(Lt,       1, ID($lt))
 | 
			
		||||
| 
						 | 
				
			
			@ -1898,6 +1913,31 @@ DEF_METHOD(LogicAnd, 1, ID($logic_and))
 | 
			
		|||
DEF_METHOD(LogicOr,  1, ID($logic_or))
 | 
			
		||||
#undef DEF_METHOD
 | 
			
		||||
 | 
			
		||||
#define DEF_METHOD(_func, _y_size, _type) \
 | 
			
		||||
	RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed, const std::string &src) { \
 | 
			
		||||
		RTLIL::Cell *cell = addCell(name, _type);           \
 | 
			
		||||
		cell->parameters[ID(A_SIGNED)] = is_signed;         \
 | 
			
		||||
		cell->parameters[ID(B_SIGNED)] = false;             \
 | 
			
		||||
		cell->parameters[ID(A_WIDTH)] = sig_a.size();       \
 | 
			
		||||
		cell->parameters[ID(B_WIDTH)] = sig_b.size();       \
 | 
			
		||||
		cell->parameters[ID(Y_WIDTH)] = sig_y.size();       \
 | 
			
		||||
		cell->setPort(ID::A, sig_a);                        \
 | 
			
		||||
		cell->setPort(ID::B, sig_b);                        \
 | 
			
		||||
		cell->setPort(ID::Y, sig_y);                        \
 | 
			
		||||
		cell->set_src_attribute(src);                       \
 | 
			
		||||
		return cell;                                        \
 | 
			
		||||
	} \
 | 
			
		||||
	RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed, const std::string &src) { \
 | 
			
		||||
		RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size);         \
 | 
			
		||||
		add ## _func(name, sig_a, sig_b, sig_y, is_signed, src); \
 | 
			
		||||
		return sig_y;                                            \
 | 
			
		||||
	}
 | 
			
		||||
DEF_METHOD(Shl,      sig_a.size(), ID($shl))
 | 
			
		||||
DEF_METHOD(Shr,      sig_a.size(), ID($shr))
 | 
			
		||||
DEF_METHOD(Sshl,     sig_a.size(), ID($sshl))
 | 
			
		||||
DEF_METHOD(Sshr,     sig_a.size(), ID($sshr))
 | 
			
		||||
#undef DEF_METHOD
 | 
			
		||||
 | 
			
		||||
#define DEF_METHOD(_func, _type, _pmux) \
 | 
			
		||||
	RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y, const std::string &src) { \
 | 
			
		||||
		RTLIL::Cell *cell = addCell(name, _type);                 \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -377,6 +377,8 @@ namespace RTLIL
 | 
			
		|||
		extern IdString blackbox;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	extern dict<std::string, std::string> constpad;
 | 
			
		||||
 | 
			
		||||
	static inline std::string escape_id(std::string str) {
 | 
			
		||||
		if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
 | 
			
		||||
			return "\\" + str;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -544,6 +544,8 @@ void yosys_shutdown()
 | 
			
		|||
	already_shutdown = true;
 | 
			
		||||
	log_pop();
 | 
			
		||||
 | 
			
		||||
	Pass::done_register();
 | 
			
		||||
 | 
			
		||||
	delete yosys_design;
 | 
			
		||||
	yosys_design = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -553,7 +555,6 @@ void yosys_shutdown()
 | 
			
		|||
	log_errfile = NULL;
 | 
			
		||||
	log_files.clear();
 | 
			
		||||
 | 
			
		||||
	Pass::done_register();
 | 
			
		||||
	yosys_celltypes.clear();
 | 
			
		||||
 | 
			
		||||
#ifdef YOSYS_ENABLE_TCL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,11 @@ Verilog & Cell Type \\
 | 
			
		|||
\label{tab:CellLib_unary}
 | 
			
		||||
\end{table}
 | 
			
		||||
 | 
			
		||||
For the unary cells that output a logical value ({\tt \$reduce\_and}, {\tt \$reduce\_or},
 | 
			
		||||
{\tt \$reduce\_xor}, {\tt \$reduce\_xnor}, {\tt \$reduce\_bool}, {\tt \$logic\_not}),
 | 
			
		||||
when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
 | 
			
		||||
and only the least significant bit varies.
 | 
			
		||||
 | 
			
		||||
Note that {\tt \$reduce\_or} and {\tt \$reduce\_bool} actually represent the same
 | 
			
		||||
logic function. But the HDL frontends generate them in different situations. A
 | 
			
		||||
{\tt \$reduce\_or} cell is generated when the prefix {\tt |} operator is being used. A
 | 
			
		||||
| 
						 | 
				
			
			@ -97,41 +102,6 @@ The width of the output port \B{Y}.
 | 
			
		|||
 | 
			
		||||
Table~\ref{tab:CellLib_binary} lists all cells for binary RTL operators.
 | 
			
		||||
 | 
			
		||||
\subsection{Multiplexers}
 | 
			
		||||
 | 
			
		||||
Multiplexers are generated by the Verilog HDL frontend for {\tt
 | 
			
		||||
?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees
 | 
			
		||||
from RTLIL::Process objects to logic.
 | 
			
		||||
 | 
			
		||||
The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter
 | 
			
		||||
and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also
 | 
			
		||||
has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to
 | 
			
		||||
the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux}
 | 
			
		||||
cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;.
 | 
			
		||||
 | 
			
		||||
The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells
 | 
			
		||||
of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and
 | 
			
		||||
an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both
 | 
			
		||||
\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of
 | 
			
		||||
\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is
 | 
			
		||||
set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more
 | 
			
		||||
than one bit from \B{S} is set the output is undefined. Cells of this type are used to model
 | 
			
		||||
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
 | 
			
		||||
an optimization).
 | 
			
		||||
 | 
			
		||||
The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
 | 
			
		||||
parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
 | 
			
		||||
\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
 | 
			
		||||
is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
 | 
			
		||||
the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
 | 
			
		||||
 | 
			
		||||
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
 | 
			
		||||
usually results in trees of multiplexer cells. Many passes (from various
 | 
			
		||||
optimizations to FSM extraction) heavily depend on these multiplexer trees to
 | 
			
		||||
understand dependencies between signals. Therefore optimizations should not
 | 
			
		||||
break these multiplexer trees (e.g.~by replacing a multiplexer between a
 | 
			
		||||
calculated signal and a constant zero with an {\tt \$and} gate).
 | 
			
		||||
 | 
			
		||||
\begin{table}[t!]
 | 
			
		||||
\hfil
 | 
			
		||||
\begin{tabular}[t]{ll}
 | 
			
		||||
| 
						 | 
				
			
			@ -175,6 +145,57 @@ Verilog & Cell Type \\
 | 
			
		|||
\label{tab:CellLib_binary}
 | 
			
		||||
\end{table}
 | 
			
		||||
 | 
			
		||||
The {\tt \$shl} and {\tt \$shr} cells implement logical shifts, whereas the {\tt \$sshl} and
 | 
			
		||||
{\tt \$sshr} cells implement arithmetic shifts. The {\tt \$shl} and {\tt \$sshl} cells implement
 | 
			
		||||
the same operation. All four of these cells interpret the second operand as unsigned, and require
 | 
			
		||||
\B{B\_SIGNED} to be zero.
 | 
			
		||||
 | 
			
		||||
Two additional shift operator cells are available that do not directly correspond to any operator
 | 
			
		||||
in Verilog, {\tt \$shift} and {\tt \$shiftx}. The {\tt \$shift} cell performs a right logical shift
 | 
			
		||||
if the second operand is positive (or unsigned), and a left logical shift if it is negative.
 | 
			
		||||
The {\tt \$shiftx} cell performs the same operation as the {\tt \$shift} cell, but the vacated bit
 | 
			
		||||
positions are filled with undef (x) bits, and corresponds to the Verilog indexed part-select expression.
 | 
			
		||||
 | 
			
		||||
For the binary cells that output a logical value ({\tt \$logic\_and}, {\tt \$logic\_or},
 | 
			
		||||
{\tt \$eqx}, {\tt \$nex}, {\tt \$lt}, {\tt \$le}, {\tt \$eq}, {\tt \$ne}, {\tt \$ge},
 | 
			
		||||
{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
 | 
			
		||||
and only the least significant bit varies.
 | 
			
		||||
 | 
			
		||||
\subsection{Multiplexers}
 | 
			
		||||
 | 
			
		||||
Multiplexers are generated by the Verilog HDL frontend for {\tt
 | 
			
		||||
?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees
 | 
			
		||||
from RTLIL::Process objects to logic.
 | 
			
		||||
 | 
			
		||||
The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter
 | 
			
		||||
and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also
 | 
			
		||||
has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to
 | 
			
		||||
the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux}
 | 
			
		||||
cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;.
 | 
			
		||||
 | 
			
		||||
The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells
 | 
			
		||||
of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and
 | 
			
		||||
an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both
 | 
			
		||||
\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of
 | 
			
		||||
\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is
 | 
			
		||||
set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more
 | 
			
		||||
than one bit from \B{S} is set the output is undefined. Cells of this type are used to model
 | 
			
		||||
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
 | 
			
		||||
an optimization).
 | 
			
		||||
 | 
			
		||||
The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
 | 
			
		||||
parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
 | 
			
		||||
\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
 | 
			
		||||
is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
 | 
			
		||||
the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
 | 
			
		||||
 | 
			
		||||
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
 | 
			
		||||
usually results in trees of multiplexer cells. Many passes (from various
 | 
			
		||||
optimizations to FSM extraction) heavily depend on these multiplexer trees to
 | 
			
		||||
understand dependencies between signals. Therefore optimizations should not
 | 
			
		||||
break these multiplexer trees (e.g.~by replacing a multiplexer between a
 | 
			
		||||
calculated signal and a constant zero with an {\tt \$and} gate).
 | 
			
		||||
 | 
			
		||||
\subsection{Registers}
 | 
			
		||||
 | 
			
		||||
D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ frontends/verilog/preproc.cc} in the Yosys source tree.
 | 
			
		|||
 | 
			
		||||
\begin{sloppypar}
 | 
			
		||||
The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
 | 
			
		||||
can be found in {\tt frontends/verilog/lexer.l} in the Yosys source tree.
 | 
			
		||||
can be found in {\tt frontends/verilog/verilog\_lexer.l} in the Yosys source tree.
 | 
			
		||||
The lexer does little more than identifying all keywords and literals
 | 
			
		||||
recognised by the Yosys Verilog frontend.
 | 
			
		||||
\end{sloppypar}
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ whenever possible.)
 | 
			
		|||
\subsection{The Verilog Parser}
 | 
			
		||||
 | 
			
		||||
The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
 | 
			
		||||
can be found in {\tt frontends/verilog/parser.y} in the Yosys source tree.
 | 
			
		||||
can be found in {\tt frontends/verilog/verilog\_parser.y} in the Yosys source tree.
 | 
			
		||||
 | 
			
		||||
It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure
 | 
			
		||||
defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ with the help of HDL synthesis tools.
 | 
			
		|||
 | 
			
		||||
In special cases such as synthesis for coarse-grain cell libraries or when
 | 
			
		||||
testing new synthesis algorithms it might be necessary to write a custom HDL
 | 
			
		||||
synthesis tool or add new features to an existing one. It this cases the
 | 
			
		||||
synthesis tool or add new features to an existing one. In these cases the
 | 
			
		||||
availability of a Free and Open Source (FOSS) synthesis tool that can be used
 | 
			
		||||
as basis for custom tools would be helpful.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,3 +32,4 @@ OBJS += passes/cmds/chtype.o
 | 
			
		|||
OBJS += passes/cmds/blackbox.o
 | 
			
		||||
OBJS += passes/cmds/ltp.o
 | 
			
		||||
OBJS += passes/cmds/bugpoint.o
 | 
			
		||||
OBJS += passes/cmds/scratchpad.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ int autoname_worker(Module *module)
 | 
			
		|||
			for (auto &conn : cell->connections()) {
 | 
			
		||||
				string suffix = stringf("_%s", log_id(conn.first));
 | 
			
		||||
				for (auto bit : conn.second)
 | 
			
		||||
					if (bit.wire != nullptr && bit.wire->name[0] == '$') {
 | 
			
		||||
					if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
 | 
			
		||||
						IdString new_name(cell->name.str() + suffix);
 | 
			
		||||
						int score = wire_score.at(bit.wire);
 | 
			
		||||
						if (cell->output(conn.first)) score = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -301,41 +301,40 @@ struct SccPass : public Pass {
 | 
			
		|||
		RTLIL::Selection newSelection(false);
 | 
			
		||||
		int scc_counter = 0;
 | 
			
		||||
 | 
			
		||||
		for (auto &mod_it : design->modules_)
 | 
			
		||||
			if (design->selected(mod_it.second))
 | 
			
		||||
		for (auto mod : design->selected_modules())
 | 
			
		||||
		{
 | 
			
		||||
			SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);
 | 
			
		||||
 | 
			
		||||
			if (!setAttr.empty())
 | 
			
		||||
			{
 | 
			
		||||
				SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
 | 
			
		||||
 | 
			
		||||
				if (!setAttr.empty())
 | 
			
		||||
				for (const auto &cells : worker.sccList)
 | 
			
		||||
				{
 | 
			
		||||
					for (const auto &cells : worker.sccList)
 | 
			
		||||
					for (auto attr : setAttr)
 | 
			
		||||
					{
 | 
			
		||||
						for (auto attr : setAttr)
 | 
			
		||||
						{
 | 
			
		||||
							IdString attr_name(RTLIL::escape_id(attr.first));
 | 
			
		||||
							string attr_valstr = attr.second;
 | 
			
		||||
							string index = stringf("%d", scc_counter);
 | 
			
		||||
						IdString attr_name(RTLIL::escape_id(attr.first));
 | 
			
		||||
						string attr_valstr = attr.second;
 | 
			
		||||
						string index = stringf("%d", scc_counter);
 | 
			
		||||
 | 
			
		||||
							for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
 | 
			
		||||
								attr_valstr.replace(pos, 2, index);
 | 
			
		||||
						for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
 | 
			
		||||
							attr_valstr.replace(pos, 2, index);
 | 
			
		||||
 | 
			
		||||
							Const attr_value(attr_valstr);
 | 
			
		||||
						Const attr_value(attr_valstr);
 | 
			
		||||
 | 
			
		||||
							for (auto cell : cells)
 | 
			
		||||
								cell->attributes[attr_name] = attr_value;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						scc_counter++;
 | 
			
		||||
						for (auto cell : cells)
 | 
			
		||||
							cell->attributes[attr_name] = attr_value;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					scc_counter += GetSize(worker.sccList);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (selectMode)
 | 
			
		||||
					worker.select(newSelection);
 | 
			
		||||
					scc_counter++;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				scc_counter += GetSize(worker.sccList);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (selectMode)
 | 
			
		||||
				worker.select(newSelection);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (expect >= 0) {
 | 
			
		||||
			if (scc_counter == expect)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										141
									
								
								passes/cmds/scratchpad.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								passes/cmds/scratchpad.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,141 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  yosys -- Yosys Open SYnthesis Suite
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 | 
			
		||||
 *                2019  Nina Engelhardt <nak@symbioticeda.com>
 | 
			
		||||
 *
 | 
			
		||||
 *  Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 *  purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 *  copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "kernel/register.h"
 | 
			
		||||
#include "kernel/rtlil.h"
 | 
			
		||||
#include "kernel/log.h"
 | 
			
		||||
 | 
			
		||||
USING_YOSYS_NAMESPACE
 | 
			
		||||
PRIVATE_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
struct ScratchpadPass : public Pass {
 | 
			
		||||
	ScratchpadPass() : Pass("scratchpad", "get/set values in the scratchpad") { }
 | 
			
		||||
	void help() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    scratchpad [options]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("This pass allows to read and modify values from the scratchpad of the current\n");
 | 
			
		||||
		log("design. Options:\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -get <identifier>\n");
 | 
			
		||||
		log("        print the value saved in the scratchpad under the given identifier.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -set <identifier> <value>\n");
 | 
			
		||||
		log("        save the given value in the scratchpad under the given identifier.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -unset <identifier>\n");
 | 
			
		||||
		log("        remove the entry for the given identifier from the scratchpad.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -copy <identifier_from> <identifier_to>\n");
 | 
			
		||||
		log("        copy the value of the first identifier to the second identifier.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -assert <identifier> <value>\n");
 | 
			
		||||
		log("        assert that the entry for the given identifier is set to the given value.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -assert-set <identifier>\n");
 | 
			
		||||
		log("        assert that the entry for the given identifier exists.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -assert-unset <identifier>\n");
 | 
			
		||||
		log("        assert that the entry for the given identifier does not exist.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The identifier may not contain whitespace. By convention, it is usually prefixed\n");
 | 
			
		||||
		log("by the name of the pass that uses it, e.g. 'opt.did_something'. If the value\n");
 | 
			
		||||
		log("contains whitespace, it must be enclosed in double quotes.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			if (args[argidx] == "-get" && argidx+1 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				if (design->scratchpad.count(identifier)) {
 | 
			
		||||
					log("%s\n", design->scratchpad_get_string(identifier).c_str());
 | 
			
		||||
				} else if (RTLIL::constpad.count(identifier)) {
 | 
			
		||||
					log("%s\n", RTLIL::constpad.at(identifier).c_str());
 | 
			
		||||
				} else {
 | 
			
		||||
					log("\"%s\" not set\n", identifier.c_str());
 | 
			
		||||
				}
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-set" && argidx+2 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				if (RTLIL::constpad.count(identifier))
 | 
			
		||||
					log_error("scratchpad entry \"%s\" is a global constant\n", identifier.c_str());
 | 
			
		||||
				string value = args[++argidx];
 | 
			
		||||
				if (value.front() == '\"' && value.back() == '\"') value = value.substr(1, value.size() - 2);
 | 
			
		||||
				design->scratchpad_set_string(identifier, value);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-unset" && argidx+1 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				design->scratchpad_unset(identifier);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-copy" && argidx+2 < args.size()) {
 | 
			
		||||
				string identifier_from = args[++argidx];
 | 
			
		||||
				string identifier_to = args[++argidx];
 | 
			
		||||
				string value;
 | 
			
		||||
				if (design->scratchpad.count(identifier_from))
 | 
			
		||||
					value = design->scratchpad_get_string(identifier_from);
 | 
			
		||||
				else if (RTLIL::constpad.count(identifier_from))
 | 
			
		||||
					value = RTLIL::constpad.at(identifier_from);
 | 
			
		||||
				else
 | 
			
		||||
					log_error("\"%s\" not set\n", identifier_from.c_str());
 | 
			
		||||
				if (RTLIL::constpad.count(identifier_to))
 | 
			
		||||
					log_error("scratchpad entry \"%s\" is a global constant\n", identifier_to.c_str());
 | 
			
		||||
				design->scratchpad_set_string(identifier_to, value);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-assert" && argidx+2 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				string expected = args[++argidx];
 | 
			
		||||
				if (expected.front() == '\"' && expected.back() == '\"') expected = expected.substr(1, expected.size() - 2);
 | 
			
		||||
				if (design->scratchpad.count(identifier) == 0)
 | 
			
		||||
					log_error("scratchpad entry '%s' is not defined\n", identifier.c_str());
 | 
			
		||||
				string value = design->scratchpad_get_string(identifier);
 | 
			
		||||
				if (value != expected) {
 | 
			
		||||
					log_error("scratchpad entry '%s' is set to '%s' instead of the asserted '%s'\n",
 | 
			
		||||
					           identifier.c_str(), value.c_str(), expected.c_str());
 | 
			
		||||
				}
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-assert-set" && argidx+1 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				if (design->scratchpad.count(identifier) == 0)
 | 
			
		||||
					log_error("scratchpad entry '%s' is not defined\n", identifier.c_str());
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-assert-unset" && argidx+1 < args.size()) {
 | 
			
		||||
				string identifier = args[++argidx];
 | 
			
		||||
				if (design->scratchpad.count(identifier) > 0)
 | 
			
		||||
					log_error("scratchpad entry '%s' is defined\n", identifier.c_str());
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design, false);
 | 
			
		||||
	}
 | 
			
		||||
} ScratchpadPass;
 | 
			
		||||
PRIVATE_NAMESPACE_END
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +44,10 @@ struct EquivOptPass:public ScriptPass
 | 
			
		|||
		log("        expand the modules in this file before proving equivalence. this is\n");
 | 
			
		||||
		log("        useful for handling architecture-specific primitives.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -blacklist <file>\n");
 | 
			
		||||
		log("        Do not match cells or signals that match the names in the file\n");
 | 
			
		||||
		log("        (passed to equiv_make).\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -assert\n");
 | 
			
		||||
		log("        produce an error if the circuits are not equivalent.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -61,13 +65,14 @@ struct EquivOptPass:public ScriptPass
 | 
			
		|||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string command, techmap_opts;
 | 
			
		||||
	std::string command, techmap_opts, make_opts;
 | 
			
		||||
	bool assert, undef, multiclock, async2sync;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		command = "";
 | 
			
		||||
		techmap_opts = "";
 | 
			
		||||
		make_opts = "";
 | 
			
		||||
		assert = false;
 | 
			
		||||
		undef = false;
 | 
			
		||||
		multiclock = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -93,6 +98,10 @@ struct EquivOptPass:public ScriptPass
 | 
			
		|||
				techmap_opts += " -map " + args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-blacklist" && argidx + 1 < args.size()) {
 | 
			
		||||
				make_opts += " -blacklist " + args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-assert") {
 | 
			
		||||
				assert = true;
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +179,12 @@ struct EquivOptPass:public ScriptPass
 | 
			
		|||
				run("clk2fflogic", "(only with -multiclock)");
 | 
			
		||||
			if (async2sync || help_mode)
 | 
			
		||||
				run("async2sync", " (only with -async2sync)");
 | 
			
		||||
			run("equiv_make gold gate equiv");
 | 
			
		||||
			string opts;
 | 
			
		||||
			if (help_mode)
 | 
			
		||||
				opts = " -blacklist <filename> ...";
 | 
			
		||||
			else
 | 
			
		||||
				opts = make_opts;
 | 
			
		||||
			run("equiv_make" + opts + " gold gate equiv");
 | 
			
		||||
			if (help_mode)
 | 
			
		||||
				run("equiv_induct [-undef] equiv");
 | 
			
		||||
			else if (undef)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
 | 
			
		|||
static std::set<RTLIL::Cell*> muxtree_cells;
 | 
			
		||||
static SigPool sig_at_port;
 | 
			
		||||
 | 
			
		||||
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
 | 
			
		||||
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache)
 | 
			
		||||
{
 | 
			
		||||
	if (mux_tree_cache.find(sig) != mux_tree_cache.end())
 | 
			
		||||
		return mux_tree_cache.at(sig);
 | 
			
		||||
 | 
			
		||||
	if (sig.is_fully_const() || old_sig == sig) {
 | 
			
		||||
ret_true:
 | 
			
		||||
		mux_tree_cache[sig] = true;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sig_at_port.check_any(assign_map(sig))) {
 | 
			
		||||
ret_false:
 | 
			
		||||
		mux_tree_cache[sig] = false;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
 | 
			
		|||
	for (auto &cellport : cellport_list)
 | 
			
		||||
	{
 | 
			
		||||
		if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
 | 
			
		||||
			return false;
 | 
			
		||||
			goto ret_false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (recursion_monitor.count(cellport.first)) {
 | 
			
		||||
			log_warning("logic loop in mux tree at signal %s in module %s.\n",
 | 
			
		||||
					log_signal(sig), RTLIL::id2cstr(module->name));
 | 
			
		||||
			return false;
 | 
			
		||||
			goto ret_false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		recursion_monitor.insert(cellport.first);
 | 
			
		||||
| 
						 | 
				
			
			@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
 | 
			
		|||
		RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
 | 
			
		||||
		RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
 | 
			
		||||
 | 
			
		||||
		if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
 | 
			
		||||
		if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) {
 | 
			
		||||
			recursion_monitor.erase(cellport.first);
 | 
			
		||||
			return false;
 | 
			
		||||
			goto ret_false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < sig_b.size(); i += sig_a.size())
 | 
			
		||||
			if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
 | 
			
		||||
			if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) {
 | 
			
		||||
				recursion_monitor.erase(cellport.first);
 | 
			
		||||
				return false;
 | 
			
		||||
				goto ret_false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		recursion_monitor.erase(cellport.first);
 | 
			
		||||
		muxtree_cells.insert(cellport.first);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	goto ret_true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool check_state_users(RTLIL::SigSpec sig)
 | 
			
		||||
| 
						 | 
				
			
			@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
 | 
			
		|||
		pool<Cell*> recursion_monitor;
 | 
			
		||||
		RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
 | 
			
		||||
		RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
 | 
			
		||||
		dict<RTLIL::SigSpec, bool> mux_tree_cache;
 | 
			
		||||
 | 
			
		||||
		if (sig_q != assign_map(wire))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor);
 | 
			
		||||
		looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache);
 | 
			
		||||
		looks_like_good_state_reg = check_state_users(sig_q);
 | 
			
		||||
 | 
			
		||||
		if (!looks_like_state_reg)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ struct MemoryPass : public Pass {
 | 
			
		|||
		log("\n");
 | 
			
		||||
		log("This pass calls all the other memory_* passes in a useful order:\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    opt_mem\n");
 | 
			
		||||
		log("    memory_dff [-nordff]                (-memx implies -nordff)\n");
 | 
			
		||||
		log("    opt_clean\n");
 | 
			
		||||
		log("    memory_share\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +82,7 @@ struct MemoryPass : public Pass {
 | 
			
		|||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		Pass::call(design, "opt_mem");
 | 
			
		||||
		Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
 | 
			
		||||
		Pass::call(design, "opt_clean");
 | 
			
		||||
		Pass::call(design, "memory_share");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,6 +134,7 @@ struct rules_t
 | 
			
		|||
		dict<string, int> min_limits, max_limits;
 | 
			
		||||
		bool or_next_if_better, make_transp, make_outreg;
 | 
			
		||||
		char shuffle_enable;
 | 
			
		||||
		vector<vector<std::tuple<bool,IdString,Const>>> attributes;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	dict<IdString, vector<bram_t>> brams;
 | 
			
		||||
| 
						 | 
				
			
			@ -327,6 +328,20 @@ struct rules_t
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
 | 
			
		||||
				data.attributes.emplace_back();
 | 
			
		||||
				for (int idx = 1; idx < GetSize(tokens); idx++) {
 | 
			
		||||
					size_t c1 = tokens[idx][0] == '!' ? 1 : 0;
 | 
			
		||||
					size_t c2 = tokens[idx].find("=");
 | 
			
		||||
					bool exists = (c1 == 0);
 | 
			
		||||
					IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
 | 
			
		||||
					Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
 | 
			
		||||
 | 
			
		||||
					data.attributes.back().emplace_back(exists, key, val);
 | 
			
		||||
				}
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			syntax_error();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -724,7 +739,7 @@ grow_read_ports:;
 | 
			
		|||
					if (match.make_transp && wr_ports <= 1) {
 | 
			
		||||
						pi.make_transp = true;
 | 
			
		||||
						if (pi.clocks != 0) {
 | 
			
		||||
							if (wr_ports == 1 && wr_clkdom != clkdom) {								
 | 
			
		||||
							if (wr_ports == 1 && wr_clkdom != clkdom) {
 | 
			
		||||
								log("        Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
 | 
			
		||||
								goto skip_bram_rport;
 | 
			
		||||
							}
 | 
			
		||||
| 
						 | 
				
			
			@ -813,6 +828,43 @@ grow_read_ports:;
 | 
			
		|||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (const auto &sums : match.attributes) {
 | 
			
		||||
			bool found = false;
 | 
			
		||||
			for (const auto &term : sums) {
 | 
			
		||||
				bool exists = std::get<0>(term);
 | 
			
		||||
				IdString key = std::get<1>(term);
 | 
			
		||||
				const Const &value = std::get<2>(term);
 | 
			
		||||
				auto it = cell->attributes.find(key);
 | 
			
		||||
				if (it == cell->attributes.end()) {
 | 
			
		||||
					if (exists)
 | 
			
		||||
						continue;
 | 
			
		||||
					found = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				else if (!exists)
 | 
			
		||||
					continue;
 | 
			
		||||
				if (it->second != value)
 | 
			
		||||
					continue;
 | 
			
		||||
				found = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (!found) {
 | 
			
		||||
				std::stringstream ss;
 | 
			
		||||
				bool exists = std::get<0>(sums.front());
 | 
			
		||||
				if (!exists)
 | 
			
		||||
					ss << "!";
 | 
			
		||||
				IdString key = std::get<1>(sums.front());
 | 
			
		||||
				ss << log_id(key);
 | 
			
		||||
				const Const &value = std::get<2>(sums.front());
 | 
			
		||||
				if (exists && value != Const(1))
 | 
			
		||||
					ss << "=\"" << value.decode_string() << "\"";
 | 
			
		||||
 | 
			
		||||
				log("    Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
 | 
			
		||||
						log_id(match.name), ss.str().c_str());
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (mode == 1)
 | 
			
		||||
			return true;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,6 +1152,43 @@ void handle_cell(Cell *cell, const rules_t &rules)
 | 
			
		|||
				goto next_match_rule;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (const auto &sums : match.attributes) {
 | 
			
		||||
				bool found = false;
 | 
			
		||||
				for (const auto &term : sums) {
 | 
			
		||||
					bool exists = std::get<0>(term);
 | 
			
		||||
					IdString key = std::get<1>(term);
 | 
			
		||||
					const Const &value = std::get<2>(term);
 | 
			
		||||
					auto it = cell->attributes.find(key);
 | 
			
		||||
					if (it == cell->attributes.end()) {
 | 
			
		||||
						if (exists)
 | 
			
		||||
							continue;
 | 
			
		||||
						found = true;
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					else if (!exists)
 | 
			
		||||
						continue;
 | 
			
		||||
					if (it->second != value)
 | 
			
		||||
						continue;
 | 
			
		||||
					found = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				if (!found) {
 | 
			
		||||
					std::stringstream ss;
 | 
			
		||||
					bool exists = std::get<0>(sums.front());
 | 
			
		||||
					if (!exists)
 | 
			
		||||
						ss << "!";
 | 
			
		||||
					IdString key = std::get<1>(sums.front());
 | 
			
		||||
					ss << log_id(key);
 | 
			
		||||
					const Const &value = std::get<2>(sums.front());
 | 
			
		||||
					if (exists && value != Const(1))
 | 
			
		||||
						ss << "=\"" << value.decode_string() << "\"";
 | 
			
		||||
 | 
			
		||||
					log("    Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
 | 
			
		||||
							log_id(bram.name), bram.variant, ss.str().c_str());
 | 
			
		||||
					goto next_match_rule;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			log("    Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
 | 
			
		||||
 | 
			
		||||
			if (or_next_if_better || !best_rule_cache.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -1225,6 +1314,13 @@ struct MemoryBramPass : public Pass {
 | 
			
		|||
		log("    dcells  .......  number of cells in 'data-direction'\n");
 | 
			
		||||
		log("    cells  ........  total number of cells (acells*dcells*dups)\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("A match containing the command 'attribute' followed by a list of space\n");
 | 
			
		||||
		log("separated 'name[=string_value]' values requires that the memory contains any\n");
 | 
			
		||||
		log("one of the given attribute name and string values (where specified), or name\n");
 | 
			
		||||
		log("and integer 1 value (if no string_value given, since Verilog will interpret\n");
 | 
			
		||||
		log("'(* attr *)' as '(* attr=1 *)').\n");
 | 
			
		||||
		log("A name prefixed with '!' indicates that the attribute must not exist.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The interface for the created bram instances is derived from the bram\n");
 | 
			
		||||
		log("description. Use 'techmap' to convert the created bram instances into\n");
 | 
			
		||||
		log("instances of the actual bram cells of your target architecture.\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -218,6 +218,10 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
 | 
			
		|||
	mem->setPort("\\RD_DATA", sig_rd_data);
 | 
			
		||||
	mem->setPort("\\RD_EN", sig_rd_en);
 | 
			
		||||
 | 
			
		||||
	// Copy attributes from RTLIL memory to $mem
 | 
			
		||||
	for (auto attr : memory->attributes)
 | 
			
		||||
		mem->attributes[attr.first] = attr.second;
 | 
			
		||||
 | 
			
		||||
	for (auto c : memcells)
 | 
			
		||||
		module->remove(c);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
 | 
			
		||||
OBJS += passes/opt/opt.o
 | 
			
		||||
OBJS += passes/opt/opt_merge.o
 | 
			
		||||
OBJS += passes/opt/opt_mem.o
 | 
			
		||||
OBJS += passes/opt/opt_muxtree.o
 | 
			
		||||
OBJS += passes/opt/opt_reduce.o
 | 
			
		||||
OBJS += passes/opt/opt_rmdff.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -978,7 +978,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 | 
			
		|||
		{
 | 
			
		||||
			cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
 | 
			
		||||
			log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
 | 
			
		||||
					log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
 | 
			
		||||
					log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
 | 
			
		||||
			cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
 | 
			
		||||
			if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
 | 
			
		||||
				cell->setPort(ID::A, cell->getPort(ID::B));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										143
									
								
								passes/opt/opt_mem.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								passes/opt/opt_mem.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  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 OptMemWorker
 | 
			
		||||
{
 | 
			
		||||
	RTLIL::Design *design;
 | 
			
		||||
	RTLIL::Module *module;
 | 
			
		||||
	SigMap sigmap;
 | 
			
		||||
	bool restart;
 | 
			
		||||
 | 
			
		||||
	dict<IdString, vector<IdString>> memrd, memwr, meminit;
 | 
			
		||||
	pool<IdString> remove_mem, remove_cells;
 | 
			
		||||
 | 
			
		||||
	OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto &it : module->memories)
 | 
			
		||||
		{
 | 
			
		||||
			memrd[it.first];
 | 
			
		||||
			memwr[it.first];
 | 
			
		||||
			meminit[it.first];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto cell : module->cells())
 | 
			
		||||
		{
 | 
			
		||||
			if (cell->type == ID($memrd)) {
 | 
			
		||||
				IdString id = cell->getParam(ID(MEMID)).decode_string();
 | 
			
		||||
				memrd.at(id).push_back(cell->name);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cell->type == ID($memwr)) {
 | 
			
		||||
				IdString id = cell->getParam(ID(MEMID)).decode_string();
 | 
			
		||||
				memwr.at(id).push_back(cell->name);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cell->type == ID($meminit)) {
 | 
			
		||||
				IdString id = cell->getParam(ID(MEMID)).decode_string();
 | 
			
		||||
				meminit.at(id).push_back(cell->name);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~OptMemWorker()
 | 
			
		||||
	{
 | 
			
		||||
		for (auto it : remove_mem)
 | 
			
		||||
		{
 | 
			
		||||
			for (auto cell_name : memrd[it])
 | 
			
		||||
				module->remove(module->cell(cell_name));
 | 
			
		||||
			for (auto cell_name : memwr[it])
 | 
			
		||||
				module->remove(module->cell(cell_name));
 | 
			
		||||
			for (auto cell_name : meminit[it])
 | 
			
		||||
				module->remove(module->cell(cell_name));
 | 
			
		||||
 | 
			
		||||
			delete module->memories.at(it);
 | 
			
		||||
			module->memories.erase(it);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto cell_name : remove_cells)
 | 
			
		||||
			module->remove(module->cell(cell_name));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int run(RTLIL::Memory *mem)
 | 
			
		||||
	{
 | 
			
		||||
		if (restart || remove_mem.count(mem->name))
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) {
 | 
			
		||||
			log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem));
 | 
			
		||||
			remove_mem.insert(mem->name);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct OptMemPass : public Pass {
 | 
			
		||||
	OptMemPass() : Pass("opt_mem", "optimize memories") { }
 | 
			
		||||
	void help() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    opt_mem [options] [selection]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("This pass performs various optimizations on memories in the design.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		log_header(design, "Executing OPT_MEM pass (optimize memories).\n");
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++) {
 | 
			
		||||
			// if (args[argidx] == "-nomux") {
 | 
			
		||||
			// 	mode_nomux = true;
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		int total_count = 0;
 | 
			
		||||
		for (auto module : design->selected_modules()) {
 | 
			
		||||
			while (1) {
 | 
			
		||||
				int cnt = 0;
 | 
			
		||||
				OptMemWorker worker(module);
 | 
			
		||||
				for (auto &it : module->memories)
 | 
			
		||||
					if (module->selected(it.second))
 | 
			
		||||
						cnt += worker.run(it.second);
 | 
			
		||||
				if (!cnt && !worker.restart)
 | 
			
		||||
					break;
 | 
			
		||||
				total_count += cnt;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (total_count)
 | 
			
		||||
			design->scratchpad_set_bool("opt.did_something", true);
 | 
			
		||||
		log("Performed a total of %d transformations.\n", total_count);
 | 
			
		||||
	}
 | 
			
		||||
} OptMemPass;
 | 
			
		||||
 | 
			
		||||
PRIVATE_NAMESPACE_END
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,9 @@ struct ExtSigSpec {
 | 
			
		|||
	bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig && semantics == other.semantics; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define BITWISE_OPS ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($and), ID($or), ID($xor), ID($xnor)
 | 
			
		||||
#define FINE_BITWISE_OPS ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)
 | 
			
		||||
 | 
			
		||||
#define BITWISE_OPS FINE_BITWISE_OPS, ID($and), ID($or), ID($xor), ID($xnor)
 | 
			
		||||
 | 
			
		||||
#define REDUCTION_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($reduce_nand)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -250,14 +252,19 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 | 
			
		|||
		shared_op->setPort(ID(CO), alu_co.extract(0, conn_width));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shared_op->setParam(ID(Y_WIDTH), conn_width);
 | 
			
		||||
	bool is_fine = shared_op->type.in(FINE_BITWISE_OPS);
 | 
			
		||||
 | 
			
		||||
	if (!is_fine)
 | 
			
		||||
		shared_op->setParam(ID(Y_WIDTH), conn_width);
 | 
			
		||||
 | 
			
		||||
	if (decode_port(shared_op, ID::A, &assign_map) == operand) {
 | 
			
		||||
		shared_op->setPort(ID::B, mux_to_oper);
 | 
			
		||||
		shared_op->setParam(ID(B_WIDTH), max_width);
 | 
			
		||||
		if (!is_fine)
 | 
			
		||||
			shared_op->setParam(ID(B_WIDTH), max_width);
 | 
			
		||||
	} else {
 | 
			
		||||
		shared_op->setPort(ID::A, mux_to_oper);
 | 
			
		||||
		shared_op->setParam(ID(A_WIDTH), max_width);
 | 
			
		||||
		if (!is_fine)
 | 
			
		||||
			shared_op->setParam(ID(A_WIDTH), max_width);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,9 @@ $(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
 | 
			
		||||
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_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_dsp48a_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))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 | 
			
		|||
 | 
			
		||||
	// SB_MAC16 Input Interface
 | 
			
		||||
	SigSpec A = st.sigA;
 | 
			
		||||
	A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool());
 | 
			
		||||
	A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
 | 
			
		||||
	log_assert(GetSize(A) == 16);
 | 
			
		||||
 | 
			
		||||
	SigSpec B = st.sigB;
 | 
			
		||||
	B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool());
 | 
			
		||||
	B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
 | 
			
		||||
	log_assert(GetSize(B) == 16);
 | 
			
		||||
 | 
			
		||||
	SigSpec CD = st.sigCD;
 | 
			
		||||
| 
						 | 
				
			
			@ -248,8 +248,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 | 
			
		|||
	cell->setParam(ID(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(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
 | 
			
		||||
	cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
 | 
			
		||||
 | 
			
		||||
	if (st.ffO) {
 | 
			
		||||
		if (st.o_lo)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,11 +56,16 @@ code sigA sigB sigH
 | 
			
		|||
			break;
 | 
			
		||||
		sigH.append(O[i]);
 | 
			
		||||
	}
 | 
			
		||||
	// This sigM could have no users if downstream sinks (e.g. $add) is
 | 
			
		||||
	//   narrower than $mul result, for example
 | 
			
		||||
	if (i == 0)
 | 
			
		||||
		reject;
 | 
			
		||||
 | 
			
		||||
	log_assert(nusers(O.extract_end(i)) <= 1);
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
 | 
			
		||||
	if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
 | 
			
		||||
	if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) {
 | 
			
		||||
		argQ = sigA;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +86,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
 | 
			
		|||
endcode
 | 
			
		||||
 | 
			
		||||
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
 | 
			
		||||
	if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
 | 
			
		||||
	if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) {
 | 
			
		||||
		argQ = sigB;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +109,7 @@ endcode
 | 
			
		|||
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()))) {
 | 
			
		||||
			 (!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) {
 | 
			
		||||
		argD = sigH;
 | 
			
		||||
		subpattern(out_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +148,7 @@ endcode
 | 
			
		|||
 | 
			
		||||
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())) {
 | 
			
		||||
			(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) {
 | 
			
		||||
		argD = sigH;
 | 
			
		||||
		subpattern(out_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +179,7 @@ reject_ffH:		;
 | 
			
		|||
endcode
 | 
			
		||||
 | 
			
		||||
match add
 | 
			
		||||
	if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
 | 
			
		||||
	if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3)
 | 
			
		||||
 | 
			
		||||
	select add->type.in($add)
 | 
			
		||||
	choice <IdString> AB {\A, \B}
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +205,7 @@ code sigCD sigO cd_signed
 | 
			
		|||
		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
 | 
			
		||||
			reject;
 | 
			
		||||
		// 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()))
 | 
			
		||||
		if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool()))
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		sigO = port(add, \Y);
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +280,7 @@ 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()))) {
 | 
			
		||||
			(mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) {
 | 
			
		||||
		argQ = sigCD;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -328,6 +333,8 @@ arg argD argQ clock clock_pol
 | 
			
		|||
 | 
			
		||||
code
 | 
			
		||||
	dff = nullptr;
 | 
			
		||||
	if (argQ.empty())
 | 
			
		||||
		reject;
 | 
			
		||||
	for (auto c : argQ.chunks()) {
 | 
			
		||||
		if (!c.wire)
 | 
			
		||||
			reject;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,14 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
 | 
			
		|||
	cell->setPort("\\O", st.lut->getPort("\\O"));
 | 
			
		||||
	cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT"));
 | 
			
		||||
 | 
			
		||||
	for (const auto &a : st.carry->attributes)
 | 
			
		||||
		cell->attributes[stringf("\\SB_CARRY.%s", a.first.c_str())] = a.second;
 | 
			
		||||
	for (const auto &a : st.lut->attributes)
 | 
			
		||||
		cell->attributes[stringf("\\SB_LUT4.%s", a.first.c_str())] = a.second;
 | 
			
		||||
	cell->attributes[ID(SB_LUT4.name)] = Const(st.lut->name.str());
 | 
			
		||||
	if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
 | 
			
		||||
		cell->attributes[ID::keep] = true;
 | 
			
		||||
 | 
			
		||||
	pm.autoremove(st.carry);
 | 
			
		||||
	pm.autoremove(st.lut);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -62,28 +70,79 @@ struct Ice40WrapCarryPass : public Pass {
 | 
			
		|||
		log("\n");
 | 
			
		||||
		log("    ice40_wrapcarry [selection]\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUTs,\n");
 | 
			
		||||
		log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUT4s,\n");
 | 
			
		||||
		log("into an internal $__ICE40_CARRY_WRAPPER cell for preservation across technology\n");
 | 
			
		||||
		log("mapping.");
 | 
			
		||||
		log("mapping.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("Attributes on both cells will have their names prefixed with 'SB_CARRY.' or\n");
 | 
			
		||||
		log("'SB_LUT4.' and attached to the wrapping cell.\n");
 | 
			
		||||
		log("A (* keep *) attribute on either cell will be logically OR-ed together.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -unwrap\n");
 | 
			
		||||
		log("        unwrap $__ICE40_CARRY_WRAPPER cells back into SB_CARRYs and SB_LUT4s,\n");
 | 
			
		||||
		log("        including restoring their attributes.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		bool unwrap = false;
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			// if (args[argidx] == "-singleton") {
 | 
			
		||||
			// 	singleton_mode = true;
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			if (args[argidx] == "-unwrap") {
 | 
			
		||||
				unwrap = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		for (auto module : design->selected_modules())
 | 
			
		||||
			ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
 | 
			
		||||
		for (auto module : design->selected_modules()) {
 | 
			
		||||
			if (!unwrap)
 | 
			
		||||
				ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
 | 
			
		||||
			else {
 | 
			
		||||
				for (auto cell : module->selected_cells()) {
 | 
			
		||||
					if (cell->type != ID($__ICE40_CARRY_WRAPPER))
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					auto carry = module->addCell(NEW_ID, ID(SB_CARRY));
 | 
			
		||||
					carry->setPort(ID(I0), cell->getPort(ID(A)));
 | 
			
		||||
					carry->setPort(ID(I1), cell->getPort(ID(B)));
 | 
			
		||||
					carry->setPort(ID(CI), cell->getPort(ID(CI)));
 | 
			
		||||
					carry->setPort(ID(CO), cell->getPort(ID(CO)));
 | 
			
		||||
					module->swap_names(carry, cell);
 | 
			
		||||
					auto lut_name = cell->attributes.at(ID(SB_LUT4.name), Const(NEW_ID.str())).decode_string();
 | 
			
		||||
					auto lut = module->addCell(lut_name, ID($lut));
 | 
			
		||||
					lut->setParam(ID(WIDTH), 4);
 | 
			
		||||
					lut->setParam(ID(LUT), cell->getParam(ID(LUT)));
 | 
			
		||||
					lut->setPort(ID(A), {cell->getPort(ID(I0)), cell->getPort(ID(A)), cell->getPort(ID(B)), cell->getPort(ID(I3)) });
 | 
			
		||||
					lut->setPort(ID(Y), cell->getPort(ID(O)));
 | 
			
		||||
 | 
			
		||||
					Const src;
 | 
			
		||||
					for (const auto &a : cell->attributes)
 | 
			
		||||
						if (a.first.begins_with("\\SB_CARRY.\\"))
 | 
			
		||||
							carry->attributes[a.first.c_str() + strlen("\\SB_CARRY.")] = a.second;
 | 
			
		||||
						else if (a.first.begins_with("\\SB_LUT4.\\"))
 | 
			
		||||
							lut->attributes[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
 | 
			
		||||
						else if (a.first == ID(src))
 | 
			
		||||
							src = a.second;
 | 
			
		||||
						else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived)))
 | 
			
		||||
							continue;
 | 
			
		||||
						else
 | 
			
		||||
							log_abort();
 | 
			
		||||
 | 
			
		||||
					if (!src.empty()) {
 | 
			
		||||
						carry->attributes.insert(std::make_pair(ID(src), src));
 | 
			
		||||
						lut->attributes.insert(std::make_pair(ID(src), src));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					module->remove(cell);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
} Ice40WrapCarryPass;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE
 | 
			
		|||
PRIVATE_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
#include "passes/pmgen/xilinx_dsp_pm.h"
 | 
			
		||||
#include "passes/pmgen/xilinx_dsp48a_pm.h"
 | 
			
		||||
#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
 | 
			
		||||
#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -487,6 +488,190 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
 | 
			
		|||
	pm.blacklist(cell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
 | 
			
		||||
{
 | 
			
		||||
	auto &st = pm.st_xilinx_dsp48a_pack;
 | 
			
		||||
 | 
			
		||||
	log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
 | 
			
		||||
 | 
			
		||||
	log_debug("preAdd:     %s\n", log_id(st.preAdd, "--"));
 | 
			
		||||
	log_debug("ffA1:       %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
 | 
			
		||||
	log_debug("ffA0:       %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
 | 
			
		||||
	log_debug("ffB1:       %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
 | 
			
		||||
	log_debug("ffB0:       %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
 | 
			
		||||
	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, "--"));
 | 
			
		||||
 | 
			
		||||
	Cell *cell = st.dsp;
 | 
			
		||||
	SigSpec &opmode = cell->connections_.at(ID(OPMODE));
 | 
			
		||||
 | 
			
		||||
	if (st.preAdd) {
 | 
			
		||||
		log("  preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
 | 
			
		||||
		bool D_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
 | 
			
		||||
		bool B_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
 | 
			
		||||
		st.sigB.extend_u0(18, B_SIGNED);
 | 
			
		||||
		st.sigD.extend_u0(18, D_SIGNED);
 | 
			
		||||
		cell->setPort(ID(B), st.sigB);
 | 
			
		||||
		cell->setPort(ID(D), st.sigD);
 | 
			
		||||
		opmode[4] = State::S1;
 | 
			
		||||
		if (st.preAdd->type == ID($add))
 | 
			
		||||
			opmode[6] = State::S0;
 | 
			
		||||
		else if (st.preAdd->type == ID($sub))
 | 
			
		||||
			opmode[6] = State::S1;
 | 
			
		||||
		else
 | 
			
		||||
			log_assert(!"strange pre-adder type");
 | 
			
		||||
 | 
			
		||||
		pm.autoremove(st.preAdd);
 | 
			
		||||
	}
 | 
			
		||||
	if (st.postAdd) {
 | 
			
		||||
		log("  postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
 | 
			
		||||
 | 
			
		||||
		if (st.postAddMux) {
 | 
			
		||||
			log_assert(st.ffP);
 | 
			
		||||
			opmode[2] = st.postAddMux->getPort(ID(S));
 | 
			
		||||
			pm.autoremove(st.postAddMux);
 | 
			
		||||
		}
 | 
			
		||||
		else if (st.ffP && st.sigC == st.sigP)
 | 
			
		||||
			opmode[2] = State::S0;
 | 
			
		||||
		else
 | 
			
		||||
			opmode[2] = State::S1;
 | 
			
		||||
		opmode[3] = State::S1;
 | 
			
		||||
 | 
			
		||||
		if (opmode[2] != 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.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.ffA0 || st.ffA1) {
 | 
			
		||||
			SigSpec A = cell->getPort(ID(A));
 | 
			
		||||
			if (st.ffA1) {
 | 
			
		||||
				f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
 | 
			
		||||
				cell->setParam(ID(A1REG), 1);
 | 
			
		||||
			}
 | 
			
		||||
			if (st.ffA0) {
 | 
			
		||||
				f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
 | 
			
		||||
				cell->setParam(ID(A0REG), 1);
 | 
			
		||||
			}
 | 
			
		||||
			pm.add_siguser(A, cell);
 | 
			
		||||
			cell->setPort(ID(A), A);
 | 
			
		||||
		}
 | 
			
		||||
		if (st.ffB0 || st.ffB1) {
 | 
			
		||||
			SigSpec B = cell->getPort(ID(B));
 | 
			
		||||
			if (st.ffB1) {
 | 
			
		||||
				f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
 | 
			
		||||
				cell->setParam(ID(B1REG), 1);
 | 
			
		||||
			}
 | 
			
		||||
			if (st.ffB0) {
 | 
			
		||||
				f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
 | 
			
		||||
				cell->setParam(ID(B0REG), 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.ffA0)
 | 
			
		||||
			log(" ffA0:%s", log_id(st.ffA0));
 | 
			
		||||
		if (st.ffA1)
 | 
			
		||||
			log(" ffA1:%s", log_id(st.ffA1));
 | 
			
		||||
 | 
			
		||||
		if (st.ffB0)
 | 
			
		||||
			log(" ffB0:%s", log_id(st.ffB0));
 | 
			
		||||
		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;
 | 
			
		||||
| 
						 | 
				
			
			@ -592,33 +777,48 @@ struct XilinxDspPass : public Pass {
 | 
			
		|||
		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");
 | 
			
		||||
		log("    -family {xcup|xcu|xc7|xc6v|xc5v|xc4v|xc6s|xc3sda}\n");
 | 
			
		||||
		log("        select the family to target\n");
 | 
			
		||||
		log("        default: xc7\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");
 | 
			
		||||
 | 
			
		||||
		std::string family = "xc7";
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			// if (args[argidx] == "-singleton") {
 | 
			
		||||
			// 	singleton_mode = true;
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) {
 | 
			
		||||
				family = args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		// Don't bother distinguishing between those.
 | 
			
		||||
		if (family == "xc6v")
 | 
			
		||||
			family = "xc7";
 | 
			
		||||
		if (family == "xcup")
 | 
			
		||||
			family = "xcu";
 | 
			
		||||
 | 
			
		||||
		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());
 | 
			
		||||
			if (family == "xc7")
 | 
			
		||||
				xilinx_simd_pack(module, module->selected_cells());
 | 
			
		||||
 | 
			
		||||
			// Match for all features ([ABDMP][12]?REG, pre-adder,
 | 
			
		||||
			// post-adder, pattern detector, etc.) except for CREG
 | 
			
		||||
			{
 | 
			
		||||
			if (family == "xc7") {
 | 
			
		||||
				xilinx_dsp_pm pm(module, module->selected_cells());
 | 
			
		||||
				pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
 | 
			
		||||
			} else if (family == "xc6s" || family == "xc3sda") {
 | 
			
		||||
				xilinx_dsp48a_pm pm(module, module->selected_cells());
 | 
			
		||||
				pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack);
 | 
			
		||||
			}
 | 
			
		||||
			// Separating out CREG packing is necessary since there
 | 
			
		||||
			//   is no guarantee that the cell ordering corresponds
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,7 +120,7 @@ endcode
 | 
			
		|||
//      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) {
 | 
			
		||||
	if (param(dsp, \ADREG, 1).as_int() == 0) {
 | 
			
		||||
		argQ = sigA;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +176,7 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
 | 
			
		|||
	// 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) {
 | 
			
		||||
		if (param(dsp, \AREG, 1).as_int() == 0) {
 | 
			
		||||
			argQ = sigA;
 | 
			
		||||
			subpattern(in_dffe);
 | 
			
		||||
			if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -237,7 +237,7 @@ 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) {
 | 
			
		||||
	if (param(dsp, \BREG, 1).as_int() == 0) {
 | 
			
		||||
		argQ = sigB;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +287,7 @@ endcode
 | 
			
		|||
 | 
			
		||||
// (6) Match 'D' input for DREG
 | 
			
		||||
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
 | 
			
		||||
	if (param(dsp, \DREG).as_int() == 0) {
 | 
			
		||||
	if (param(dsp, \DREG, 1).as_int() == 0) {
 | 
			
		||||
		argQ = sigD;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +308,7 @@ 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) {
 | 
			
		||||
	if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) {
 | 
			
		||||
		argD = sigM;
 | 
			
		||||
		subpattern(out_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
| 
						 | 
				
			
			@ -335,7 +335,7 @@ endcode
 | 
			
		|||
//      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()
 | 
			
		||||
	if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero()
 | 
			
		||||
 | 
			
		||||
	select postAdd->type.in($add)
 | 
			
		||||
	select GetSize(port(postAdd, \Y)) <= 48
 | 
			
		||||
| 
						 | 
				
			
			@ -347,9 +347,9 @@ match postAdd
 | 
			
		|||
	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))
 | 
			
		||||
	// Check that remainder of AB is a sign- or zero-extension
 | 
			
		||||
	filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
 | 
			
		||||
 | 
			
		||||
	set postAddAB AB
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
| 
						 | 
				
			
			@ -363,7 +363,7 @@ 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) {
 | 
			
		||||
	if (param(dsp, \PREG, 1).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++;
 | 
			
		||||
| 
						 | 
				
			
			@ -460,7 +460,7 @@ arg argD argQ clock
 | 
			
		|||
 | 
			
		||||
code
 | 
			
		||||
	dff = nullptr;
 | 
			
		||||
	if (GetSize(argQ) == 0)
 | 
			
		||||
	if (argQ.empty())
 | 
			
		||||
		reject;
 | 
			
		||||
	for (const auto &c : argQ.chunks()) {
 | 
			
		||||
		// Abandon matches when 'Q' is a constant
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										673
									
								
								passes/pmgen/xilinx_dsp48a.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										673
									
								
								passes/pmgen/xilinx_dsp48a.pmg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,673 @@
 | 
			
		|||
// This file describes the main pattern matcher setup (of three total) that
 | 
			
		||||
//   forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
 | 
			
		||||
//   DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
 | 
			
		||||
// At a high level, it works as follows:
 | 
			
		||||
//   ( 1) Starting from a DSP48A/DSP48A1 cell
 | 
			
		||||
//   ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
 | 
			
		||||
//        (attached to at most two $mux cells that implement clock-enable or
 | 
			
		||||
//         reset functionality, using a subpattern discussed below)
 | 
			
		||||
//        If B1REG matched, treat 'B' input as input of B1REG
 | 
			
		||||
//   ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
 | 
			
		||||
//       (pre-adder)
 | 
			
		||||
//   ( 4) Match 'B' input for B0REG
 | 
			
		||||
//   ( 5) Match 'A' input for A1REG
 | 
			
		||||
//        If A1REG, then match 'A' input for A0REG
 | 
			
		||||
//   ( 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 |   +----+
 | 
			
		||||
//            +----+       \-------/
 | 
			
		||||
// Notes: see the notes in xilinx_dsp.pmg
 | 
			
		||||
 | 
			
		||||
pattern xilinx_dsp48a_pack
 | 
			
		||||
 | 
			
		||||
state <SigBit> clock
 | 
			
		||||
state <SigSpec> sigA sigB sigC sigD sigM sigP
 | 
			
		||||
state <IdString> postAddAB postAddMuxAB
 | 
			
		||||
state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
 | 
			
		||||
state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
 | 
			
		||||
state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
 | 
			
		||||
state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
 | 
			
		||||
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 DSP48A/DSP48A1 cell
 | 
			
		||||
match dsp
 | 
			
		||||
	select dsp->type.in(\DSP48A, \DSP48A1)
 | 
			
		||||
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);
 | 
			
		||||
	// Only care about those bits that are used
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = GetSize(P)-1; i >= 0; i--)
 | 
			
		||||
		if (nusers(P[i]) > 1)
 | 
			
		||||
			break;
 | 
			
		||||
	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 (i == 0)
 | 
			
		||||
		reject;
 | 
			
		||||
	sigM = P.extract(0, i);
 | 
			
		||||
 | 
			
		||||
	clock = port(dsp, \CLK, SigBit());
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
// (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
 | 
			
		||||
//     (attached to at most two $mux cells that implement clock-enable or
 | 
			
		||||
//      reset functionality, using a subpattern discussed above)
 | 
			
		||||
//     If matched, treat 'B' input as input of B1REG
 | 
			
		||||
code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
 | 
			
		||||
	if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
 | 
			
		||||
		argQ = sigB;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
			ffB1 = dff;
 | 
			
		||||
			clock = dffclock;
 | 
			
		||||
			if (dffrstmux) {
 | 
			
		||||
				ffB1rstmux = dffrstmux;
 | 
			
		||||
				ffBrstpol = dffrstpol;
 | 
			
		||||
			}
 | 
			
		||||
			if (dffcemux) {
 | 
			
		||||
				ffB1cemux = dffcemux;
 | 
			
		||||
				ffBcepol = dffcepol;
 | 
			
		||||
			}
 | 
			
		||||
			sigB = dffD;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
// (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
 | 
			
		||||
//     (pre-adder)
 | 
			
		||||
match preAdd
 | 
			
		||||
	if sigD.empty() || sigD.is_fully_zero()
 | 
			
		||||
	if param(dsp, \B0REG).as_int() == 0
 | 
			
		||||
	// Ensure that preAdder not already used
 | 
			
		||||
	if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
 | 
			
		||||
 | 
			
		||||
	select preAdd->type.in($add, $sub)
 | 
			
		||||
	// Output has to be 18 bits or less
 | 
			
		||||
	select GetSize(port(preAdd, \Y)) <= 18
 | 
			
		||||
	select nusers(port(preAdd, \Y)) == 2
 | 
			
		||||
	// D port has to be 18 bits or less
 | 
			
		||||
	select GetSize(port(preAdd, \A)) <= 18
 | 
			
		||||
	// B port has to be 18 bits or less
 | 
			
		||||
	select GetSize(port(preAdd, \B)) <= 18
 | 
			
		||||
	index <SigSpec> port(preAdd, \Y) === sigB
 | 
			
		||||
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code sigB sigD
 | 
			
		||||
	if (preAdd) {
 | 
			
		||||
		sigD = port(preAdd, \A);
 | 
			
		||||
		sigB = port(preAdd, \B);
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
// (4) Match 'B' input for B0REG
 | 
			
		||||
code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
 | 
			
		||||
	if (param(dsp, \B0REG).as_int() == 0) {
 | 
			
		||||
		argQ = sigB;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
			if (ffB1) {
 | 
			
		||||
				if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
 | 
			
		||||
					goto ffB0_end;
 | 
			
		||||
				if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
 | 
			
		||||
					goto ffB0_end;
 | 
			
		||||
				if (dffrstmux) {
 | 
			
		||||
					if (ffBrstpol != dffrstpol)
 | 
			
		||||
						goto ffB0_end;
 | 
			
		||||
					if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
 | 
			
		||||
						goto ffB0_end;
 | 
			
		||||
					ffB0rstmux = dffrstmux;
 | 
			
		||||
				}
 | 
			
		||||
				if (dffcemux) {
 | 
			
		||||
					if (ffBcepol != dffcepol)
 | 
			
		||||
						goto ffB0_end;
 | 
			
		||||
					if (port(ffB1cemux, \S) != port(dffcemux, \S))
 | 
			
		||||
						goto ffB0_end;
 | 
			
		||||
					ffB0cemux = dffcemux;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ffB0 = dff;
 | 
			
		||||
			clock = dffclock;
 | 
			
		||||
			if (dffrstmux) {
 | 
			
		||||
				ffB0rstmux = dffrstmux;
 | 
			
		||||
				ffBrstpol = dffrstpol;
 | 
			
		||||
			}
 | 
			
		||||
			if (dffcemux) {
 | 
			
		||||
				ffB0cemux = dffcemux;
 | 
			
		||||
				ffBcepol = dffcepol;
 | 
			
		||||
			}
 | 
			
		||||
			sigB = dffD;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
ffB0_end:
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
// (5) Match 'A' input for A1REG
 | 
			
		||||
//     If A1REG, then match 'A' input for A0REG
 | 
			
		||||
code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
 | 
			
		||||
	if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
 | 
			
		||||
		argQ = sigA;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
			ffA1 = dff;
 | 
			
		||||
			clock = dffclock;
 | 
			
		||||
			if (dffrstmux) {
 | 
			
		||||
				ffA1rstmux = dffrstmux;
 | 
			
		||||
				ffArstpol = dffrstpol;
 | 
			
		||||
			}
 | 
			
		||||
			if (dffcemux) {
 | 
			
		||||
				ffA1cemux = dffcemux;
 | 
			
		||||
				ffAcepol = dffcepol;
 | 
			
		||||
			}
 | 
			
		||||
			sigA = dffD;
 | 
			
		||||
 | 
			
		||||
			// Now attempt to match A0
 | 
			
		||||
			if (ffA1) {
 | 
			
		||||
				argQ = sigA;
 | 
			
		||||
				subpattern(in_dffe);
 | 
			
		||||
				if (dff) {
 | 
			
		||||
					if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
 | 
			
		||||
						goto ffA0_end;
 | 
			
		||||
					if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
 | 
			
		||||
						goto ffA0_end;
 | 
			
		||||
					if (dffrstmux) {
 | 
			
		||||
						if (ffArstpol != dffrstpol)
 | 
			
		||||
							goto ffA0_end;
 | 
			
		||||
						if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
 | 
			
		||||
							goto ffA0_end;
 | 
			
		||||
						ffA0rstmux = dffrstmux;
 | 
			
		||||
					}
 | 
			
		||||
					if (dffcemux) {
 | 
			
		||||
						if (ffAcepol != dffcepol)
 | 
			
		||||
							goto ffA0_end;
 | 
			
		||||
						if (port(ffA1cemux, \S) != port(dffcemux, \S))
 | 
			
		||||
							goto ffA0_end;
 | 
			
		||||
						ffA0cemux = dffcemux;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					ffA0 = dff;
 | 
			
		||||
					clock = dffclock;
 | 
			
		||||
 | 
			
		||||
					if (dffcemux) {
 | 
			
		||||
						ffA0cemux = dffcemux;
 | 
			
		||||
						ffAcepol = dffcepol;
 | 
			
		||||
					}
 | 
			
		||||
					sigA = dffD;
 | 
			
		||||
 | 
			
		||||
ffA0_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(2,2).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- or zero-extension
 | 
			
		||||
	filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(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
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	if (GetSize(argQ) == 0)
 | 
			
		||||
		reject;
 | 
			
		||||
	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
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
// 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,
 | 
			
		||||
//   (1) Starting from a DSP48* 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
 | 
			
		||||
| 
						 | 
				
			
			@ -38,10 +38,10 @@ 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,
 | 
			
		||||
// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
 | 
			
		||||
//     and (b) uses the 'C' port
 | 
			
		||||
match dsp
 | 
			
		||||
	select dsp->type.in(\DSP48E1)
 | 
			
		||||
	select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
 | 
			
		||||
	select param(dsp, \CREG, 1).as_int() == 0
 | 
			
		||||
	select nusers(port(dsp, \C, SigSpec())) > 1
 | 
			
		||||
endmatch
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +60,8 @@ code sigC sigP clock
 | 
			
		|||
	sigC = unextend(port(dsp, \C, SigSpec()));
 | 
			
		||||
 | 
			
		||||
	SigSpec P = port(dsp, \P);
 | 
			
		||||
	if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
 | 
			
		||||
	if (!dsp->type.in(\DSP48E1) ||
 | 
			
		||||
 param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
 | 
			
		||||
		// Only care about those bits that are used
 | 
			
		||||
		int i;
 | 
			
		||||
		for (i = GetSize(P)-1; i >= 0; i--)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,12 +62,11 @@ 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
 | 
			
		||||
// (1) Starting from a DSP48* cell that (a) has the Z multiplexer
 | 
			
		||||
//     (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1)
 | 
			
		||||
//     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 (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000"))
 | 
			
		||||
	select nusers(port(first, \PCOUT, SigSpec())) <= 1
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,14 +99,21 @@ finally
 | 
			
		|||
					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();
 | 
			
		||||
					if (dsp->type.in(\DSP48A, \DSP48A1)) {
 | 
			
		||||
						log_assert(P == 0);
 | 
			
		||||
						opmode[3] = State::S0;
 | 
			
		||||
						opmode[2] = State::S1;
 | 
			
		||||
					}
 | 
			
		||||
					else if (dsp->type.in(\DSP48E1)) {
 | 
			
		||||
						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;
 | 
			
		||||
						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));
 | 
			
		||||
| 
						 | 
				
			
			@ -120,21 +126,42 @@ finally
 | 
			
		|||
					add_siguser(cascade, dsp_pcin);
 | 
			
		||||
					add_siguser(cascade, dsp);
 | 
			
		||||
 | 
			
		||||
					dsp->setParam(ID(ACASCREG), AREG);
 | 
			
		||||
					if (dsp->type.in(\DSP48E1))
 | 
			
		||||
						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);
 | 
			
		||||
					if (dsp->type.in(\DSP48A, \DSP48A1)) {
 | 
			
		||||
						// According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
 | 
			
		||||
						// "The DSP48A1 component uses this input when cascading
 | 
			
		||||
						//   BCOUT from an adjacent DSP48A1 slice. The tools then
 | 
			
		||||
						//   translate BCOUT cascading to the dedicated BCIN input
 | 
			
		||||
						//   and set the B_INPUT attribute for implementation."
 | 
			
		||||
						dsp_pcin->setPort(ID(B), cascade);
 | 
			
		||||
					}
 | 
			
		||||
					else {
 | 
			
		||||
						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"));
 | 
			
		||||
					if (dsp->type.in(\DSP48E1)) {
 | 
			
		||||
						dsp->setParam(ID(BCASCREG), BREG);
 | 
			
		||||
						// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
 | 
			
		||||
						// "The attribute is only used by place and route tools and
 | 
			
		||||
						//   is not necessary for the users to set for synthesis. The
 | 
			
		||||
						//   attribute is determined by the connection to the B port
 | 
			
		||||
						//   of the DSP48A1 slice. If the B port is connected to the
 | 
			
		||||
						//   BCOUT of another DSP48A1 slice, then the tools automatically
 | 
			
		||||
						//   set the attribute to 'CASCADE', otherwise it is set to
 | 
			
		||||
						//   'DIRECT'".
 | 
			
		||||
						dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -156,22 +183,21 @@ subpattern tail
 | 
			
		|||
arg first
 | 
			
		||||
arg next
 | 
			
		||||
 | 
			
		||||
// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled,
 | 
			
		||||
// (2.1) Match another DSP48* 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 (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && 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
 | 
			
		||||
// (2.2) For DSP48E1 only, 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)
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +214,8 @@ code next
 | 
			
		|||
	if (!nextP)
 | 
			
		||||
		next = nextP_shift17;
 | 
			
		||||
	if (next) {
 | 
			
		||||
		if (next->type != first->type)
 | 
			
		||||
			reject;
 | 
			
		||||
		unextend = [](const SigSpec &sig) {
 | 
			
		||||
			int i;
 | 
			
		||||
			for (i = GetSize(sig)-1; i > 0; i--)
 | 
			
		||||
| 
						 | 
				
			
			@ -202,38 +230,50 @@ code next
 | 
			
		|||
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
 | 
			
		||||
//     if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade,
 | 
			
		||||
//     (b) the previous DSP does  not already use its ACOUT port, then
 | 
			
		||||
//     examine if an ACOUT -> ACIN cascade  opportunity exists if
 | 
			
		||||
//     (i) A ports are identical, or (ii) separated by 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
 | 
			
		||||
//     TODO: Check for two levels of flops, instead of just one
 | 
			
		||||
code argQ clock AREG
 | 
			
		||||
	AREG = -1;
 | 
			
		||||
	if (next) {
 | 
			
		||||
	if (next && next->type.in(\DSP48E1)) {
 | 
			
		||||
		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" &&
 | 
			
		||||
 | 
			
		||||
		if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
 | 
			
		||||
				port(next, \ACIN, SigSpec()).is_fully_zero() &&
 | 
			
		||||
				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:			;
 | 
			
		||||
			if (param(prev, \AREG, 2) == 0) {
 | 
			
		||||
				if (port(prev, \A) == port(next, \A))
 | 
			
		||||
					AREG = 0;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				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;
 | 
			
		||||
					IdString CEA;
 | 
			
		||||
					if (param(prev, \AREG, 2) == 1)
 | 
			
		||||
						CEA = \CEA2;
 | 
			
		||||
					else if (param(prev, \AREG, 2) == 2)
 | 
			
		||||
						CEA = \CEA1;
 | 
			
		||||
					else log_abort();
 | 
			
		||||
					if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
 | 
			
		||||
						goto reject_AREG;
 | 
			
		||||
					if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
 | 
			
		||||
						goto reject_AREG;
 | 
			
		||||
					if (dffD == unextend(port(prev, \A)))
 | 
			
		||||
						AREG = 1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
reject_AREG:	;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -242,28 +282,47 @@ 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" &&
 | 
			
		||||
		if (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:			;
 | 
			
		||||
			if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) ||
 | 
			
		||||
				(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) {
 | 
			
		||||
				if (port(prev, \B) == port(next, \B))
 | 
			
		||||
					BREG = 0;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				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;
 | 
			
		||||
					IdString CEB;
 | 
			
		||||
					if (next->type.in(\DSP48A, \DSP48A1))
 | 
			
		||||
						CEB = \CEB;
 | 
			
		||||
					else if (next->type.in(\DSP48E1)) {
 | 
			
		||||
						if (param(prev, \BREG, 2) == 1)
 | 
			
		||||
							CEB = \CEB2;
 | 
			
		||||
						else if (param(prev, \BREG, 2) == 2)
 | 
			
		||||
							CEB = \CEB1;
 | 
			
		||||
						else log_abort();
 | 
			
		||||
					}
 | 
			
		||||
					else log_abort();
 | 
			
		||||
					if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
 | 
			
		||||
						goto reject_BREG;
 | 
			
		||||
					if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
 | 
			
		||||
						goto reject_BREG;
 | 
			
		||||
					if (dffD == unextend(port(prev, \B))) {
 | 
			
		||||
						if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0)
 | 
			
		||||
							goto reject_BREG;
 | 
			
		||||
						BREG = 1;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
reject_BREG:	;
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -349,6 +349,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
 | 
			
		|||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (proc->get_bool_attribute(ID(always_ff)))
 | 
			
		||||
			log_error("Found non edge/level sensitive event in always_ff process `%s.%s'.\n",
 | 
			
		||||
					db.module->name.c_str(), proc->name.c_str());
 | 
			
		||||
 | 
			
		||||
		for (auto ss : sr->actions)
 | 
			
		||||
		{
 | 
			
		||||
			db.sigmap.apply(ss.first);
 | 
			
		||||
| 
						 | 
				
			
			@ -383,8 +387,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
 | 
			
		|||
	int offset = 0;
 | 
			
		||||
	for (auto chunk : nolatches_bits.first.chunks()) {
 | 
			
		||||
		SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
 | 
			
		||||
		log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
 | 
			
		||||
				db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
 | 
			
		||||
		if (proc->get_bool_attribute(ID(always_latch)))
 | 
			
		||||
			log_error("No latch inferred for signal `%s.%s' from always_latch process `%s.%s'.\n",
 | 
			
		||||
					db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
 | 
			
		||||
		else
 | 
			
		||||
			log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
 | 
			
		||||
					db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
 | 
			
		||||
		db.module->connect(lhs, rhs);
 | 
			
		||||
		offset += chunk.width;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -410,8 +418,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
 | 
			
		|||
			cell->set_src_attribute(src);
 | 
			
		||||
			db.generated_dlatches.insert(cell);
 | 
			
		||||
 | 
			
		||||
			log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
 | 
			
		||||
					db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
 | 
			
		||||
			if (proc->get_bool_attribute(ID(always_comb)))
 | 
			
		||||
				log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n",
 | 
			
		||||
						db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
 | 
			
		||||
			else
 | 
			
		||||
				log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
 | 
			
		||||
						db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		offset += width;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o
 | 
			
		|||
OBJS += passes/sat/fmcombine.o
 | 
			
		||||
OBJS += passes/sat/mutate.o
 | 
			
		||||
OBJS += passes/sat/cutpoint.o
 | 
			
		||||
OBJS += passes/sat/fminit.o
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										197
									
								
								passes/sat/fminit.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								passes/sat/fminit.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,197 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  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 FminitPass : public Pass {
 | 
			
		||||
	FminitPass() : Pass("fminit", "set init values/sequences for formal") { }
 | 
			
		||||
	void help() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    fminit [options] <selection>\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("This pass creates init constraints (for example for reset sequences) in a formal\n");
 | 
			
		||||
		log("model.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -seq <signal> <sequence>\n");
 | 
			
		||||
		log("        Set sequence using comma-separated list of values, use 'z for\n");
 | 
			
		||||
		log("        unconstrained bits. The last value is used for the remainder of the\n");
 | 
			
		||||
		log("        trace.\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -set <signal> <value>\n");
 | 
			
		||||
		log("        Add constant value constraint\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -posedge <signal>\n");
 | 
			
		||||
		log("    -negedge <signal>\n");
 | 
			
		||||
		log("        Set clock for init sequences\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
		vector<pair<string, vector<string>>> initdata;
 | 
			
		||||
		vector<pair<string, string>> setdata;
 | 
			
		||||
		string clocksignal;
 | 
			
		||||
		bool clockedge;
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing FMINIT pass.\n");
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		for (argidx = 1; argidx < args.size(); argidx++)
 | 
			
		||||
		{
 | 
			
		||||
			// if (args[argidx] == "-o" && argidx+1 < args.size()) {
 | 
			
		||||
			// 	filename = args[++argidx];
 | 
			
		||||
			// 	continue;
 | 
			
		||||
			// }
 | 
			
		||||
			if (args[argidx] == "-seq" && argidx+2 < args.size()) {
 | 
			
		||||
				string lhs = args[++argidx];
 | 
			
		||||
				string rhs = args[++argidx];
 | 
			
		||||
				initdata.push_back(make_pair(lhs, split_tokens(rhs, ",")));
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-set" && argidx+2 < args.size()) {
 | 
			
		||||
				string lhs = args[++argidx];
 | 
			
		||||
				string rhs = args[++argidx];
 | 
			
		||||
				setdata.push_back(make_pair(lhs, rhs));
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-posedge" && argidx+1 < args.size()) {
 | 
			
		||||
				clocksignal = args[++argidx];
 | 
			
		||||
				clockedge = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-negedge" && argidx+1 < args.size()) {
 | 
			
		||||
				clocksignal = args[++argidx];
 | 
			
		||||
				clockedge = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		Module *module = nullptr;
 | 
			
		||||
 | 
			
		||||
		for (auto mod : design->selected_modules()) {
 | 
			
		||||
			if (module != nullptr)
 | 
			
		||||
				log_error("'fminit' requires exactly one module to be selected.\n");
 | 
			
		||||
			module = mod;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (module == nullptr)
 | 
			
		||||
			log_error("'fminit' requires exactly one module to be selected.\n");
 | 
			
		||||
 | 
			
		||||
		SigSpec clksig;
 | 
			
		||||
		if (!clocksignal.empty()) {
 | 
			
		||||
			if (!SigSpec::parse(clksig, module, clocksignal))
 | 
			
		||||
				log_error("Error parsing expression '%s'.\n", clocksignal.c_str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &it : setdata)
 | 
			
		||||
		{
 | 
			
		||||
			SigSpec lhs, rhs;
 | 
			
		||||
 | 
			
		||||
			if (!SigSpec::parse(lhs, module, it.first))
 | 
			
		||||
				log_error("Error parsing expression '%s'.\n", it.first.c_str());
 | 
			
		||||
 | 
			
		||||
			if (!SigSpec::parse_rhs(lhs, rhs, module, it.second))
 | 
			
		||||
				log_error("Error parsing expression '%s'.\n", it.second.c_str());
 | 
			
		||||
 | 
			
		||||
			SigSpec final_lhs, final_rhs;
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < GetSize(rhs); i++)
 | 
			
		||||
				if (rhs[i] != State::Sz) {
 | 
			
		||||
					final_lhs.append(lhs[i]);
 | 
			
		||||
					final_rhs.append(rhs[i]);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			if (!final_lhs.empty()) {
 | 
			
		||||
				SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
 | 
			
		||||
				module->addAssume(NEW_ID, eq, State::S1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		vector<SigSpec> ctrlsig;
 | 
			
		||||
		vector<SigSpec> ctrlsig_latched;
 | 
			
		||||
 | 
			
		||||
		for (auto &it : initdata)
 | 
			
		||||
		{
 | 
			
		||||
			SigSpec lhs, rhs;
 | 
			
		||||
 | 
			
		||||
			if (!SigSpec::parse(lhs, module, it.first))
 | 
			
		||||
				log_error("Error parsing expression '%s'.\n", it.first.c_str());
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < GetSize(it.second); i++)
 | 
			
		||||
			{
 | 
			
		||||
				if (i >= GetSize(ctrlsig))
 | 
			
		||||
				{
 | 
			
		||||
					SigSpec insig = i > 0 ? ctrlsig.at(i-1) : State::S0;
 | 
			
		||||
 | 
			
		||||
					Wire *outwire = module->addWire(NEW_ID);
 | 
			
		||||
					outwire->attributes[ID(init)] = i > 0 ? State::S0 : State::S1;
 | 
			
		||||
 | 
			
		||||
					if (clksig.empty())
 | 
			
		||||
						module->addFf(NEW_ID, insig, outwire);
 | 
			
		||||
					else
 | 
			
		||||
						module->addDff(NEW_ID, clksig, insig, outwire, clockedge);
 | 
			
		||||
 | 
			
		||||
					ctrlsig.push_back(outwire);
 | 
			
		||||
					ctrlsig_latched.push_back(SigSpec());
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (i+1 == GetSize(it.second) && ctrlsig_latched[i].empty())
 | 
			
		||||
				{
 | 
			
		||||
					Wire *ffwire = module->addWire(NEW_ID);
 | 
			
		||||
					ffwire->attributes[ID(init)] = State::S0;
 | 
			
		||||
					SigSpec outsig = module->Or(NEW_ID, ffwire, ctrlsig[i]);
 | 
			
		||||
 | 
			
		||||
					if (clksig.empty())
 | 
			
		||||
						module->addFf(NEW_ID, outsig, ffwire);
 | 
			
		||||
					else
 | 
			
		||||
						module->addDff(NEW_ID, clksig, outsig, ffwire, clockedge);
 | 
			
		||||
 | 
			
		||||
					ctrlsig_latched[i] = outsig;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				SigSpec ctrl = i+1 == GetSize(it.second) ? ctrlsig_latched[i] : ctrlsig[i];
 | 
			
		||||
 | 
			
		||||
				SigSpec final_lhs, final_rhs;
 | 
			
		||||
 | 
			
		||||
				if (!SigSpec::parse_rhs(lhs, rhs, module, it.second[i]))
 | 
			
		||||
					log_error("Error parsing expression '%s'.\n", it.second[i].c_str());
 | 
			
		||||
 | 
			
		||||
				for (int i = 0; i < GetSize(rhs); i++)
 | 
			
		||||
					if (rhs[i] != State::Sz) {
 | 
			
		||||
						final_lhs.append(lhs[i]);
 | 
			
		||||
						final_rhs.append(rhs[i]);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				if (!final_lhs.empty()) {
 | 
			
		||||
					SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
 | 
			
		||||
					module->addAssume(NEW_ID, eq, ctrl);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
} FminitPass;
 | 
			
		||||
 | 
			
		||||
PRIVATE_NAMESPACE_END
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +230,7 @@ struct SimInstance
 | 
			
		|||
		bool did_something = false;
 | 
			
		||||
 | 
			
		||||
		sig = sigmap(sig);
 | 
			
		||||
		log_assert(GetSize(sig) == GetSize(value));
 | 
			
		||||
		log_assert(GetSize(sig) <= GetSize(value));
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < GetSize(sig); i++)
 | 
			
		||||
			if (state_nets.at(sig[i]) != value[i]) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,17 +29,17 @@
 | 
			
		|||
// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
 | 
			
		||||
// http://en.wikipedia.org/wiki/Topological_sorting
 | 
			
		||||
 | 
			
		||||
#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
 | 
			
		||||
#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
 | 
			
		||||
#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; if; mfs2"
 | 
			
		||||
#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; cover {I} {P}"
 | 
			
		||||
#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
 | 
			
		||||
#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
 | 
			
		||||
#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
 | 
			
		||||
#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2"
 | 
			
		||||
#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}"
 | 
			
		||||
#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
 | 
			
		||||
 | 
			
		||||
#define ABC_FAST_COMMAND_LIB "strash; dretime; retime {D}; map {D}"
 | 
			
		||||
#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
 | 
			
		||||
#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if"
 | 
			
		||||
#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}"
 | 
			
		||||
#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map"
 | 
			
		||||
#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}"
 | 
			
		||||
#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
 | 
			
		||||
#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
 | 
			
		||||
#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
 | 
			
		||||
#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
 | 
			
		||||
 | 
			
		||||
#include "kernel/register.h"
 | 
			
		||||
#include "kernel/sigtools.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -747,6 +747,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
 | 
			
		|||
	else
 | 
			
		||||
		abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
 | 
			
		||||
 | 
			
		||||
	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 = 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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1510,7 +1514,47 @@ struct AbcPass : public Pass {
 | 
			
		|||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		size_t argidx;
 | 
			
		||||
		// get arguments from scratchpad first, then override by command arguments
 | 
			
		||||
		std::string lut_arg, luts_arg, g_arg;
 | 
			
		||||
		exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */);
 | 
			
		||||
		script_file = design->scratchpad_get_string("abc.script", script_file);
 | 
			
		||||
		liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file);
 | 
			
		||||
		constr_file = design->scratchpad_get_string("abc.constr", constr_file);
 | 
			
		||||
		if (design->scratchpad.count("abc.D")) {
 | 
			
		||||
			delay_target = "-D " + design->scratchpad_get_string("abc.D");
 | 
			
		||||
		}
 | 
			
		||||
		if (design->scratchpad.count("abc.I")) {
 | 
			
		||||
			sop_inputs = "-I " + design->scratchpad_get_string("abc.I");
 | 
			
		||||
		}
 | 
			
		||||
		if (design->scratchpad.count("abc.P")) {
 | 
			
		||||
			sop_products = "-P " + design->scratchpad_get_string("abc.P");
 | 
			
		||||
		}
 | 
			
		||||
		if (design->scratchpad.count("abc.S")) {
 | 
			
		||||
			lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
 | 
			
		||||
		}
 | 
			
		||||
		lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
 | 
			
		||||
		luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
 | 
			
		||||
		sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode);
 | 
			
		||||
		map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4);
 | 
			
		||||
		map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8);
 | 
			
		||||
		map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16);
 | 
			
		||||
		abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress);
 | 
			
		||||
		g_arg = design->scratchpad_get_string("abc.g", g_arg);
 | 
			
		||||
 | 
			
		||||
		fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode);
 | 
			
		||||
		dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode);
 | 
			
		||||
		if (design->scratchpad.count("abc.clk")) {
 | 
			
		||||
			clk_str = design->scratchpad_get_string("abc.clk");
 | 
			
		||||
			dff_mode = true;
 | 
			
		||||
		}
 | 
			
		||||
		keepff = design->scratchpad_get_bool("abc.keepff", keepff);
 | 
			
		||||
		cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup);
 | 
			
		||||
		keepff = design->scratchpad_get_bool("abc.keepff", keepff);
 | 
			
		||||
		show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir);
 | 
			
		||||
		markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups);
 | 
			
		||||
 | 
			
		||||
		size_t argidx, g_argidx;
 | 
			
		||||
		bool g_arg_from_cmd = false;
 | 
			
		||||
		char pwd [PATH_MAX];
 | 
			
		||||
		if (!getcwd(pwd, sizeof(pwd))) {
 | 
			
		||||
			log_cmd_error("getcwd failed: %s\n", strerror(errno));
 | 
			
		||||
| 
						 | 
				
			
			@ -1524,23 +1568,14 @@ struct AbcPass : public Pass {
 | 
			
		|||
			}
 | 
			
		||||
			if (arg == "-script" && argidx+1 < args.size()) {
 | 
			
		||||
				script_file = args[++argidx];
 | 
			
		||||
				rewrite_filename(script_file);
 | 
			
		||||
				if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
 | 
			
		||||
					script_file = std::string(pwd) + "/" + script_file;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-liberty" && argidx+1 < args.size()) {
 | 
			
		||||
				liberty_file = args[++argidx];
 | 
			
		||||
				rewrite_filename(liberty_file);
 | 
			
		||||
				if (!liberty_file.empty() && !is_absolute_path(liberty_file))
 | 
			
		||||
					liberty_file = std::string(pwd) + "/" + liberty_file;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-constr" && argidx+1 < args.size()) {
 | 
			
		||||
				rewrite_filename(constr_file);
 | 
			
		||||
				constr_file = args[++argidx];
 | 
			
		||||
				if (!constr_file.empty() && !is_absolute_path(constr_file))
 | 
			
		||||
					constr_file = std::string(pwd) + "/" + constr_file;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-D" && argidx+1 < args.size()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1560,37 +1595,11 @@ struct AbcPass : public Pass {
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-lut" && argidx+1 < args.size()) {
 | 
			
		||||
				string arg = args[++argidx];
 | 
			
		||||
				size_t pos = arg.find_first_of(':');
 | 
			
		||||
				int lut_mode = 0, lut_mode2 = 0;
 | 
			
		||||
				if (pos != string::npos) {
 | 
			
		||||
					lut_mode = atoi(arg.substr(0, pos).c_str());
 | 
			
		||||
					lut_mode2 = atoi(arg.substr(pos+1).c_str());
 | 
			
		||||
				} else {
 | 
			
		||||
					lut_mode = atoi(arg.c_str());
 | 
			
		||||
					lut_mode2 = lut_mode;
 | 
			
		||||
				}
 | 
			
		||||
				lut_costs.clear();
 | 
			
		||||
				for (int i = 0; i < lut_mode; i++)
 | 
			
		||||
					lut_costs.push_back(1);
 | 
			
		||||
				for (int i = lut_mode; i < lut_mode2; i++)
 | 
			
		||||
					lut_costs.push_back(2 << (i - lut_mode));
 | 
			
		||||
				lut_arg = args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-luts" && argidx+1 < args.size()) {
 | 
			
		||||
				lut_costs.clear();
 | 
			
		||||
				for (auto &tok : split_tokens(args[++argidx], ",")) {
 | 
			
		||||
					auto parts = split_tokens(tok, ":");
 | 
			
		||||
					if (GetSize(parts) == 0 && !lut_costs.empty())
 | 
			
		||||
						lut_costs.push_back(lut_costs.back());
 | 
			
		||||
					else if (GetSize(parts) == 1)
 | 
			
		||||
						lut_costs.push_back(atoi(parts.at(0).c_str()));
 | 
			
		||||
					else if (GetSize(parts) == 2)
 | 
			
		||||
						while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
 | 
			
		||||
							lut_costs.push_back(atoi(parts.at(1).c_str()));
 | 
			
		||||
					else
 | 
			
		||||
						log_cmd_error("Invalid -luts syntax.\n");
 | 
			
		||||
				}
 | 
			
		||||
				luts_arg = args[++argidx];
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-sop") {
 | 
			
		||||
| 
						 | 
				
			
			@ -1614,123 +1623,11 @@ struct AbcPass : public Pass {
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-g" && argidx+1 < args.size()) {
 | 
			
		||||
				for (auto g : split_tokens(args[++argidx], ",")) {
 | 
			
		||||
					vector<string> gate_list;
 | 
			
		||||
					bool remove_gates = false;
 | 
			
		||||
					if (GetSize(g) > 0 && g[0] == '-') {
 | 
			
		||||
						remove_gates = true;
 | 
			
		||||
						g = g.substr(1);
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "AND") goto ok_gate;
 | 
			
		||||
					if (g == "NAND") goto ok_gate;
 | 
			
		||||
					if (g == "OR") goto ok_gate;
 | 
			
		||||
					if (g == "NOR") goto ok_gate;
 | 
			
		||||
					if (g == "XOR") goto ok_gate;
 | 
			
		||||
					if (g == "XNOR") goto ok_gate;
 | 
			
		||||
					if (g == "ANDNOT") goto ok_gate;
 | 
			
		||||
					if (g == "ORNOT") goto ok_gate;
 | 
			
		||||
					if (g == "MUX") goto ok_gate;
 | 
			
		||||
					if (g == "NMUX") goto ok_gate;
 | 
			
		||||
					if (g == "AOI3") goto ok_gate;
 | 
			
		||||
					if (g == "OAI3") goto ok_gate;
 | 
			
		||||
					if (g == "AOI4") goto ok_gate;
 | 
			
		||||
					if (g == "OAI4") goto ok_gate;
 | 
			
		||||
					if (g == "simple") {
 | 
			
		||||
						gate_list.push_back("AND");
 | 
			
		||||
						gate_list.push_back("OR");
 | 
			
		||||
						gate_list.push_back("XOR");
 | 
			
		||||
						gate_list.push_back("MUX");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "cmos2") {
 | 
			
		||||
						if (!remove_gates)
 | 
			
		||||
							cmos_cost = true;
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "cmos3") {
 | 
			
		||||
						if (!remove_gates)
 | 
			
		||||
							cmos_cost = true;
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("AOI3");
 | 
			
		||||
						gate_list.push_back("OAI3");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "cmos4") {
 | 
			
		||||
						if (!remove_gates)
 | 
			
		||||
							cmos_cost = true;
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("AOI3");
 | 
			
		||||
						gate_list.push_back("OAI3");
 | 
			
		||||
						gate_list.push_back("AOI4");
 | 
			
		||||
						gate_list.push_back("OAI4");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "cmos") {
 | 
			
		||||
						if (!remove_gates)
 | 
			
		||||
							cmos_cost = true;
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("AOI3");
 | 
			
		||||
						gate_list.push_back("OAI3");
 | 
			
		||||
						gate_list.push_back("AOI4");
 | 
			
		||||
						gate_list.push_back("OAI4");
 | 
			
		||||
						gate_list.push_back("NMUX");
 | 
			
		||||
						gate_list.push_back("MUX");
 | 
			
		||||
						gate_list.push_back("XOR");
 | 
			
		||||
						gate_list.push_back("XNOR");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "gates") {
 | 
			
		||||
						gate_list.push_back("AND");
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("OR");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("XOR");
 | 
			
		||||
						gate_list.push_back("XNOR");
 | 
			
		||||
						gate_list.push_back("ANDNOT");
 | 
			
		||||
						gate_list.push_back("ORNOT");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "aig") {
 | 
			
		||||
						gate_list.push_back("AND");
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("OR");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("ANDNOT");
 | 
			
		||||
						gate_list.push_back("ORNOT");
 | 
			
		||||
						goto ok_alias;
 | 
			
		||||
					}
 | 
			
		||||
					if (g == "all") {
 | 
			
		||||
						gate_list.push_back("AND");
 | 
			
		||||
						gate_list.push_back("NAND");
 | 
			
		||||
						gate_list.push_back("OR");
 | 
			
		||||
						gate_list.push_back("NOR");
 | 
			
		||||
						gate_list.push_back("XOR");
 | 
			
		||||
						gate_list.push_back("XNOR");
 | 
			
		||||
						gate_list.push_back("ANDNOT");
 | 
			
		||||
						gate_list.push_back("ORNOT");
 | 
			
		||||
						gate_list.push_back("AOI3");
 | 
			
		||||
						gate_list.push_back("OAI3");
 | 
			
		||||
						gate_list.push_back("AOI4");
 | 
			
		||||
						gate_list.push_back("OAI4");
 | 
			
		||||
						gate_list.push_back("MUX");
 | 
			
		||||
						gate_list.push_back("NMUX");
 | 
			
		||||
					}
 | 
			
		||||
					cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str()));
 | 
			
		||||
				ok_gate:
 | 
			
		||||
					gate_list.push_back(g);
 | 
			
		||||
				ok_alias:
 | 
			
		||||
					for (auto gate : gate_list) {
 | 
			
		||||
						if (remove_gates)
 | 
			
		||||
							enabled_gates.erase(gate);
 | 
			
		||||
						else
 | 
			
		||||
							enabled_gates.insert(gate);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if (g_arg_from_cmd)
 | 
			
		||||
					log_cmd_error("Can only use -g once. Please combine.");
 | 
			
		||||
				g_arg = args[++argidx];
 | 
			
		||||
				g_argidx = argidx;
 | 
			
		||||
				g_arg_from_cmd = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-fast") {
 | 
			
		||||
| 
						 | 
				
			
			@ -1766,8 +1663,176 @@ struct AbcPass : public Pass {
 | 
			
		|||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		rewrite_filename(script_file);
 | 
			
		||||
		if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
 | 
			
		||||
			script_file = std::string(pwd) + "/" + script_file;
 | 
			
		||||
		rewrite_filename(liberty_file);
 | 
			
		||||
		if (!liberty_file.empty() && !is_absolute_path(liberty_file))
 | 
			
		||||
			liberty_file = std::string(pwd) + "/" + liberty_file;
 | 
			
		||||
		rewrite_filename(constr_file);
 | 
			
		||||
		if (!constr_file.empty() && !is_absolute_path(constr_file))
 | 
			
		||||
			constr_file = std::string(pwd) + "/" + constr_file;
 | 
			
		||||
 | 
			
		||||
		// handle -lut argument
 | 
			
		||||
		if (!lut_arg.empty()) {
 | 
			
		||||
			size_t pos = lut_arg.find_first_of(':');
 | 
			
		||||
			int lut_mode = 0, lut_mode2 = 0;
 | 
			
		||||
			if (pos != string::npos) {
 | 
			
		||||
				lut_mode = atoi(lut_arg.substr(0, pos).c_str());
 | 
			
		||||
				lut_mode2 = atoi(lut_arg.substr(pos+1).c_str());
 | 
			
		||||
			} else {
 | 
			
		||||
				lut_mode = atoi(lut_arg.c_str());
 | 
			
		||||
				lut_mode2 = lut_mode;
 | 
			
		||||
			}
 | 
			
		||||
			lut_costs.clear();
 | 
			
		||||
			for (int i = 0; i < lut_mode; i++)
 | 
			
		||||
				lut_costs.push_back(1);
 | 
			
		||||
			for (int i = lut_mode; i < lut_mode2; i++)
 | 
			
		||||
				lut_costs.push_back(2 << (i - lut_mode));
 | 
			
		||||
		}
 | 
			
		||||
		//handle -luts argument
 | 
			
		||||
		if (!luts_arg.empty()){
 | 
			
		||||
			lut_costs.clear();
 | 
			
		||||
			for (auto &tok : split_tokens(luts_arg, ",")) {
 | 
			
		||||
				auto parts = split_tokens(tok, ":");
 | 
			
		||||
				if (GetSize(parts) == 0 && !lut_costs.empty())
 | 
			
		||||
					lut_costs.push_back(lut_costs.back());
 | 
			
		||||
				else if (GetSize(parts) == 1)
 | 
			
		||||
					lut_costs.push_back(atoi(parts.at(0).c_str()));
 | 
			
		||||
				else if (GetSize(parts) == 2)
 | 
			
		||||
					while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
 | 
			
		||||
						lut_costs.push_back(atoi(parts.at(1).c_str()));
 | 
			
		||||
				else
 | 
			
		||||
					log_cmd_error("Invalid -luts syntax.\n");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// handle -g argument
 | 
			
		||||
		if (!g_arg.empty()){
 | 
			
		||||
			for (auto g : split_tokens(g_arg, ",")) {
 | 
			
		||||
				vector<string> gate_list;
 | 
			
		||||
				bool remove_gates = false;
 | 
			
		||||
				if (GetSize(g) > 0 && g[0] == '-') {
 | 
			
		||||
					remove_gates = true;
 | 
			
		||||
					g = g.substr(1);
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "AND") goto ok_gate;
 | 
			
		||||
				if (g == "NAND") goto ok_gate;
 | 
			
		||||
				if (g == "OR") goto ok_gate;
 | 
			
		||||
				if (g == "NOR") goto ok_gate;
 | 
			
		||||
				if (g == "XOR") goto ok_gate;
 | 
			
		||||
				if (g == "XNOR") goto ok_gate;
 | 
			
		||||
				if (g == "ANDNOT") goto ok_gate;
 | 
			
		||||
				if (g == "ORNOT") goto ok_gate;
 | 
			
		||||
				if (g == "MUX") goto ok_gate;
 | 
			
		||||
				if (g == "NMUX") goto ok_gate;
 | 
			
		||||
				if (g == "AOI3") goto ok_gate;
 | 
			
		||||
				if (g == "OAI3") goto ok_gate;
 | 
			
		||||
				if (g == "AOI4") goto ok_gate;
 | 
			
		||||
				if (g == "OAI4") goto ok_gate;
 | 
			
		||||
				if (g == "simple") {
 | 
			
		||||
					gate_list.push_back("AND");
 | 
			
		||||
					gate_list.push_back("OR");
 | 
			
		||||
					gate_list.push_back("XOR");
 | 
			
		||||
					gate_list.push_back("MUX");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "cmos2") {
 | 
			
		||||
					if (!remove_gates)
 | 
			
		||||
						cmos_cost = true;
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "cmos3") {
 | 
			
		||||
					if (!remove_gates)
 | 
			
		||||
						cmos_cost = true;
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("AOI3");
 | 
			
		||||
					gate_list.push_back("OAI3");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "cmos4") {
 | 
			
		||||
					if (!remove_gates)
 | 
			
		||||
						cmos_cost = true;
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("AOI3");
 | 
			
		||||
					gate_list.push_back("OAI3");
 | 
			
		||||
					gate_list.push_back("AOI4");
 | 
			
		||||
					gate_list.push_back("OAI4");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "cmos") {
 | 
			
		||||
					if (!remove_gates)
 | 
			
		||||
						cmos_cost = true;
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("AOI3");
 | 
			
		||||
					gate_list.push_back("OAI3");
 | 
			
		||||
					gate_list.push_back("AOI4");
 | 
			
		||||
					gate_list.push_back("OAI4");
 | 
			
		||||
					gate_list.push_back("NMUX");
 | 
			
		||||
					gate_list.push_back("MUX");
 | 
			
		||||
					gate_list.push_back("XOR");
 | 
			
		||||
					gate_list.push_back("XNOR");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "gates") {
 | 
			
		||||
					gate_list.push_back("AND");
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("OR");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("XOR");
 | 
			
		||||
					gate_list.push_back("XNOR");
 | 
			
		||||
					gate_list.push_back("ANDNOT");
 | 
			
		||||
					gate_list.push_back("ORNOT");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "aig") {
 | 
			
		||||
					gate_list.push_back("AND");
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("OR");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("ANDNOT");
 | 
			
		||||
					gate_list.push_back("ORNOT");
 | 
			
		||||
					goto ok_alias;
 | 
			
		||||
				}
 | 
			
		||||
				if (g == "all") {
 | 
			
		||||
					gate_list.push_back("AND");
 | 
			
		||||
					gate_list.push_back("NAND");
 | 
			
		||||
					gate_list.push_back("OR");
 | 
			
		||||
					gate_list.push_back("NOR");
 | 
			
		||||
					gate_list.push_back("XOR");
 | 
			
		||||
					gate_list.push_back("XNOR");
 | 
			
		||||
					gate_list.push_back("ANDNOT");
 | 
			
		||||
					gate_list.push_back("ORNOT");
 | 
			
		||||
					gate_list.push_back("AOI3");
 | 
			
		||||
					gate_list.push_back("OAI3");
 | 
			
		||||
					gate_list.push_back("AOI4");
 | 
			
		||||
					gate_list.push_back("OAI4");
 | 
			
		||||
					gate_list.push_back("MUX");
 | 
			
		||||
					gate_list.push_back("NMUX");
 | 
			
		||||
				}
 | 
			
		||||
				if (g_arg_from_cmd)
 | 
			
		||||
					cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str()));
 | 
			
		||||
				else
 | 
			
		||||
					log_cmd_error("Unsupported gate type: %s", g.c_str());
 | 
			
		||||
			ok_gate:
 | 
			
		||||
				gate_list.push_back(g);
 | 
			
		||||
			ok_alias:
 | 
			
		||||
				for (auto gate : gate_list) {
 | 
			
		||||
					if (remove_gates)
 | 
			
		||||
						enabled_gates.erase(gate);
 | 
			
		||||
					else
 | 
			
		||||
						enabled_gates.insert(gate);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!lut_costs.empty() && !liberty_file.empty())
 | 
			
		||||
			log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n");
 | 
			
		||||
			log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n");
 | 
			
		||||
		if (!constr_file.empty() && liberty_file.empty())
 | 
			
		||||
			log_cmd_error("Got -constr but no -liberty!\n");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -115,6 +115,8 @@ struct ClkbufmapPass : public Pass {
 | 
			
		|||
		// Cell type, port name, bit index.
 | 
			
		||||
		pool<pair<IdString, pair<IdString, int>>> sink_ports;
 | 
			
		||||
		pool<pair<IdString, pair<IdString, int>>> buf_ports;
 | 
			
		||||
		dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_out;
 | 
			
		||||
		dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_in;
 | 
			
		||||
 | 
			
		||||
		// Process submodules before module using them.
 | 
			
		||||
		std::vector<Module *> modules_sorted;
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +135,14 @@ struct ClkbufmapPass : public Pass {
 | 
			
		|||
					if (wire->get_bool_attribute("\\clkbuf_sink"))
 | 
			
		||||
						for (int i = 0; i < GetSize(wire); i++)
 | 
			
		||||
							sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
 | 
			
		||||
					auto it = wire->attributes.find("\\clkbuf_inv");
 | 
			
		||||
					if (it != wire->attributes.end()) {
 | 
			
		||||
						IdString in_name = RTLIL::escape_id(it->second.decode_string());
 | 
			
		||||
						for (int i = 0; i < GetSize(wire); i++) {
 | 
			
		||||
							inv_ports_out[make_pair(module->name, make_pair(wire->name, i))] = make_pair(in_name, i);
 | 
			
		||||
							inv_ports_in[make_pair(module->name, make_pair(in_name, i))] = make_pair(wire->name, i);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +167,37 @@ struct ClkbufmapPass : public Pass {
 | 
			
		|||
				if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
 | 
			
		||||
					buf_wire_bits.insert(sigmap(port.second[i]));
 | 
			
		||||
 | 
			
		||||
			// Third, propagate tags through inverters.
 | 
			
		||||
			bool retry = true;
 | 
			
		||||
			while (retry) {
 | 
			
		||||
				retry = false;
 | 
			
		||||
				for (auto cell : module->cells())
 | 
			
		||||
				for (auto port : cell->connections())
 | 
			
		||||
				for (int i = 0; i < port.second.size(); i++) {
 | 
			
		||||
					auto it = inv_ports_out.find(make_pair(cell->type, make_pair(port.first, i)));
 | 
			
		||||
					auto bit = sigmap(port.second[i]);
 | 
			
		||||
					// If output of an inverter is connected to a sink, mark it as buffered,
 | 
			
		||||
					// and request a buffer on the inverter's input instead.
 | 
			
		||||
					if (it != inv_ports_out.end() && !buf_wire_bits.count(bit) && sink_wire_bits.count(bit)) {
 | 
			
		||||
						buf_wire_bits.insert(bit);
 | 
			
		||||
						auto other_bit = sigmap(cell->getPort(it->second.first)[it->second.second]);
 | 
			
		||||
						sink_wire_bits.insert(other_bit);
 | 
			
		||||
						retry = true;
 | 
			
		||||
					}
 | 
			
		||||
					// If input of an inverter is marked as already-buffered,
 | 
			
		||||
					// mark its output already-buffered as well.
 | 
			
		||||
					auto it2 = inv_ports_in.find(make_pair(cell->type, make_pair(port.first, i)));
 | 
			
		||||
					if (it2 != inv_ports_in.end() && buf_wire_bits.count(bit)) {
 | 
			
		||||
						auto other_bit = sigmap(cell->getPort(it2->second.first)[it2->second.second]);
 | 
			
		||||
						if (!buf_wire_bits.count(other_bit)) {
 | 
			
		||||
							buf_wire_bits.insert(other_bit);
 | 
			
		||||
							retry = true;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			// Collect all driven bits.
 | 
			
		||||
			for (auto cell : module->cells())
 | 
			
		||||
			for (auto port : cell->connections())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,11 +87,11 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
	{
 | 
			
		||||
		log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
 | 
			
		||||
 | 
			
		||||
		std::string inpad_celltype, inpad_portname, inpad_portname2;
 | 
			
		||||
		std::string outpad_celltype, outpad_portname, outpad_portname2;
 | 
			
		||||
		std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
 | 
			
		||||
		std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
 | 
			
		||||
		std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
 | 
			
		||||
		std::string inpad_celltype, inpad_portname_o, inpad_portname_pad;
 | 
			
		||||
		std::string outpad_celltype, outpad_portname_i, outpad_portname_pad;
 | 
			
		||||
		std::string inoutpad_celltype, inoutpad_portname_io, inoutpad_portname_pad;
 | 
			
		||||
		std::string toutpad_celltype, toutpad_portname_oe, toutpad_portname_i, toutpad_portname_pad;
 | 
			
		||||
		std::string tinoutpad_celltype, tinoutpad_portname_oe, tinoutpad_portname_o, tinoutpad_portname_i, tinoutpad_portname_pad;
 | 
			
		||||
		std::string widthparam, nameparam;
 | 
			
		||||
		pool<pair<IdString, IdString>> ignore;
 | 
			
		||||
		bool flag_bits = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,35 +102,35 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
			std::string arg = args[argidx];
 | 
			
		||||
			if (arg == "-inpad" && argidx+2 < args.size()) {
 | 
			
		||||
				inpad_celltype = args[++argidx];
 | 
			
		||||
				inpad_portname = args[++argidx];
 | 
			
		||||
				split_portname_pair(inpad_portname, inpad_portname2);
 | 
			
		||||
				inpad_portname_o = args[++argidx];
 | 
			
		||||
				split_portname_pair(inpad_portname_o, inpad_portname_pad);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-outpad" && argidx+2 < args.size()) {
 | 
			
		||||
				outpad_celltype = args[++argidx];
 | 
			
		||||
				outpad_portname = args[++argidx];
 | 
			
		||||
				split_portname_pair(outpad_portname, outpad_portname2);
 | 
			
		||||
				outpad_portname_i = args[++argidx];
 | 
			
		||||
				split_portname_pair(outpad_portname_i, outpad_portname_pad);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-inoutpad" && argidx+2 < args.size()) {
 | 
			
		||||
				inoutpad_celltype = args[++argidx];
 | 
			
		||||
				inoutpad_portname = args[++argidx];
 | 
			
		||||
				split_portname_pair(inoutpad_portname, inoutpad_portname2);
 | 
			
		||||
				inoutpad_portname_io = args[++argidx];
 | 
			
		||||
				split_portname_pair(inoutpad_portname_io, inoutpad_portname_pad);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-toutpad" && argidx+2 < args.size()) {
 | 
			
		||||
				toutpad_celltype = args[++argidx];
 | 
			
		||||
				toutpad_portname = args[++argidx];
 | 
			
		||||
				split_portname_pair(toutpad_portname, toutpad_portname2);
 | 
			
		||||
				split_portname_pair(toutpad_portname2, toutpad_portname3);
 | 
			
		||||
				toutpad_portname_oe = args[++argidx];
 | 
			
		||||
				split_portname_pair(toutpad_portname_oe, toutpad_portname_i);
 | 
			
		||||
				split_portname_pair(toutpad_portname_i, toutpad_portname_pad);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-tinoutpad" && argidx+2 < args.size()) {
 | 
			
		||||
				tinoutpad_celltype = args[++argidx];
 | 
			
		||||
				tinoutpad_portname = args[++argidx];
 | 
			
		||||
				split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
 | 
			
		||||
				split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
 | 
			
		||||
				split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
 | 
			
		||||
				tinoutpad_portname_oe = args[++argidx];
 | 
			
		||||
				split_portname_pair(tinoutpad_portname_oe, tinoutpad_portname_o);
 | 
			
		||||
				split_portname_pair(tinoutpad_portname_o, tinoutpad_portname_i);
 | 
			
		||||
				split_portname_pair(tinoutpad_portname_i, tinoutpad_portname_pad);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (arg == "-ignore" && argidx+2 < args.size()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -161,16 +161,16 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
 | 
			
		||||
		if (!inpad_portname2.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
 | 
			
		||||
		if (!outpad_portname2.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
 | 
			
		||||
		if (!inoutpad_portname2.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
 | 
			
		||||
		if (!toutpad_portname3.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
 | 
			
		||||
		if (!tinoutpad_portname4.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
 | 
			
		||||
		if (!inpad_portname_pad.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname_pad)));
 | 
			
		||||
		if (!outpad_portname_pad.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname_pad)));
 | 
			
		||||
		if (!inoutpad_portname_pad.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname_pad)));
 | 
			
		||||
		if (!toutpad_portname_pad.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname_pad)));
 | 
			
		||||
		if (!tinoutpad_portname_pad.empty())
 | 
			
		||||
			ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname_pad)));
 | 
			
		||||
 | 
			
		||||
		for (auto module : design->modules())
 | 
			
		||||
			if (module->get_blackbox_attribute())
 | 
			
		||||
| 
						 | 
				
			
			@ -180,148 +180,130 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
 | 
			
		||||
		for (auto module : design->selected_modules())
 | 
			
		||||
		{
 | 
			
		||||
			dict<IdString, pool<int>> skip_wires;
 | 
			
		||||
			pool<SigBit> skip_wire_bits;
 | 
			
		||||
			SigMap sigmap(module);
 | 
			
		||||
			dict<Wire *, dict<int, pair<Cell *, IdString>>> rewrite_bits;
 | 
			
		||||
 | 
			
		||||
			for (auto cell : module->cells())
 | 
			
		||||
			for (auto port : cell->connections())
 | 
			
		||||
				if (ignore.count(make_pair(cell->type, port.first)))
 | 
			
		||||
					for (auto bit : sigmap(port.second))
 | 
			
		||||
					for (auto bit : port.second)
 | 
			
		||||
						skip_wire_bits.insert(bit);
 | 
			
		||||
 | 
			
		||||
			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
 | 
			
		||||
			{
 | 
			
		||||
				dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
 | 
			
		||||
				pool<pair<IdString, IdString>> norewrites;
 | 
			
		||||
				SigMap rewrites;
 | 
			
		||||
				dict<SigBit, Cell *> tbuf_bits;
 | 
			
		||||
				pool<SigBit> driven_bits;
 | 
			
		||||
 | 
			
		||||
				// Gather tristate buffers and always-on drivers.
 | 
			
		||||
				for (auto cell : module->cells())
 | 
			
		||||
					if (cell->type == ID($_TBUF_)) {
 | 
			
		||||
						SigBit bit = sigmap(cell->getPort(ID::Y).as_bit());
 | 
			
		||||
						tbuf_bits[bit].first = cell->name;
 | 
			
		||||
						SigBit bit = cell->getPort(ID::Y).as_bit();
 | 
			
		||||
						tbuf_bits[bit] = cell;
 | 
			
		||||
					} else {
 | 
			
		||||
						for (auto port : cell->connections())
 | 
			
		||||
							if (!cell->known() || cell->output(port.first))
 | 
			
		||||
								for (auto bit : port.second)
 | 
			
		||||
									driven_bits.insert(bit);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				for (auto cell : module->cells())
 | 
			
		||||
				for (auto port : cell->connections())
 | 
			
		||||
				for (auto bit : sigmap(port.second))
 | 
			
		||||
					if (tbuf_bits.count(bit))
 | 
			
		||||
						tbuf_bits.at(bit).second.insert(cell->name);
 | 
			
		||||
				// If a wire is a target of an assignment, it is driven, unless the source is 'z.
 | 
			
		||||
				for (auto &conn : module->connections())
 | 
			
		||||
					for (int i = 0; i < GetSize(conn.first); i++) {
 | 
			
		||||
						SigBit dstbit = conn.first[i];
 | 
			
		||||
						SigBit srcbit = conn.second[i];
 | 
			
		||||
						if (!srcbit.wire && srcbit.data == State::Sz)
 | 
			
		||||
							continue;
 | 
			
		||||
						driven_bits.insert(dstbit);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				for (auto wire : module->selected_wires())
 | 
			
		||||
				{
 | 
			
		||||
					if (!wire->port_output)
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					// Don't handle inout ports if we have no suitable buffer type.
 | 
			
		||||
					if (wire->port_input && tinoutpad_celltype.empty())
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					// likewise for output ports.
 | 
			
		||||
					if (!wire->port_input && toutpad_celltype.empty())
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					for (int i = 0; i < GetSize(wire); i++)
 | 
			
		||||
					{
 | 
			
		||||
						SigBit wire_bit(wire, i);
 | 
			
		||||
						SigBit mapped_wire_bit = sigmap(wire_bit);
 | 
			
		||||
						Cell *tbuf_cell = nullptr;
 | 
			
		||||
 | 
			
		||||
						if (tbuf_bits.count(mapped_wire_bit) == 0)
 | 
			
		||||
						if (skip_wire_bits.count(wire_bit))
 | 
			
		||||
							continue;
 | 
			
		||||
 | 
			
		||||
						if (skip_wire_bits.count(mapped_wire_bit))
 | 
			
		||||
							continue;
 | 
			
		||||
						if (tbuf_bits.count(wire_bit))
 | 
			
		||||
							tbuf_cell = tbuf_bits.at(wire_bit);
 | 
			
		||||
 | 
			
		||||
						auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
 | 
			
		||||
						Cell *tbuf_cell = module->cell(tbuf_cache.first);
 | 
			
		||||
						SigBit en_sig;
 | 
			
		||||
						SigBit data_sig;
 | 
			
		||||
						bool is_driven = driven_bits.count(wire_bit);
 | 
			
		||||
 | 
			
		||||
						if (tbuf_cell == nullptr)
 | 
			
		||||
							continue;
 | 
			
		||||
						if (tbuf_cell != nullptr) {
 | 
			
		||||
							// Found a tristate buffer — use it.
 | 
			
		||||
							en_sig = tbuf_cell->getPort(ID(E)).as_bit();
 | 
			
		||||
							data_sig = tbuf_cell->getPort(ID::A).as_bit();
 | 
			
		||||
						} else if (is_driven) {
 | 
			
		||||
							// No tristate buffer, but an always-on driver is present.
 | 
			
		||||
							// If this is an inout port, we're creating a tinoutpad
 | 
			
		||||
							// anyway, just with a constant 1 as enable.
 | 
			
		||||
							if (!wire->port_input)
 | 
			
		||||
								continue;
 | 
			
		||||
							en_sig = SigBit(State::S1);
 | 
			
		||||
							data_sig = wire_bit;
 | 
			
		||||
						} else {
 | 
			
		||||
							// No driver on a wire.  Create a tristate pad with always-0
 | 
			
		||||
							// enable.
 | 
			
		||||
							en_sig = SigBit(State::S0);
 | 
			
		||||
							data_sig = SigBit(State::Sx);
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						SigBit en_sig = tbuf_cell->getPort(ID(E)).as_bit();
 | 
			
		||||
						SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit();
 | 
			
		||||
 | 
			
		||||
						if (wire->port_input && !tinoutpad_celltype.empty())
 | 
			
		||||
						if (wire->port_input)
 | 
			
		||||
						{
 | 
			
		||||
							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
 | 
			
		||||
 | 
			
		||||
							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
 | 
			
		||||
							Wire *owire = module->addWire(NEW_ID);
 | 
			
		||||
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig);
 | 
			
		||||
							cell->attributes[ID::keep] = RTLIL::Const(1);
 | 
			
		||||
 | 
			
		||||
							for (auto cn : tbuf_cache.second) {
 | 
			
		||||
								auto c = module->cell(cn);
 | 
			
		||||
								if (c == nullptr)
 | 
			
		||||
									continue;
 | 
			
		||||
								for (auto port : c->connections()) {
 | 
			
		||||
									SigSpec sig = port.second;
 | 
			
		||||
									bool newsig = false;
 | 
			
		||||
									for (auto &bit : sig)
 | 
			
		||||
										if (sigmap(bit) == mapped_wire_bit) {
 | 
			
		||||
											bit = owire;
 | 
			
		||||
											newsig = true;
 | 
			
		||||
										}
 | 
			
		||||
									if (newsig)
 | 
			
		||||
										c->setPort(port.first, sig);
 | 
			
		||||
								}
 | 
			
		||||
							if (tbuf_cell) {
 | 
			
		||||
								module->remove(tbuf_cell);
 | 
			
		||||
								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
 | 
			
		||||
								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
 | 
			
		||||
							} else if (is_driven) {
 | 
			
		||||
								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit);
 | 
			
		||||
							} else {
 | 
			
		||||
								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
 | 
			
		||||
								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							module->remove(tbuf_cell);
 | 
			
		||||
							skip_wires[wire->name].insert(i);
 | 
			
		||||
 | 
			
		||||
							norewrites.insert(make_pair(cell->name, RTLIL::escape_id(tinoutpad_portname4)));
 | 
			
		||||
							rewrites.add(sigmap(wire_bit), owire);
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						if (!wire->port_input && !toutpad_celltype.empty())
 | 
			
		||||
						{
 | 
			
		||||
							skip_wire_bits.insert(wire_bit);
 | 
			
		||||
							if (!tinoutpad_portname_pad.empty())
 | 
			
		||||
								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
 | 
			
		||||
						} else {
 | 
			
		||||
							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
 | 
			
		||||
 | 
			
		||||
							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
 | 
			
		||||
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(toutpad_portname_oe), en_sig);
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);
 | 
			
		||||
							cell->attributes[ID::keep] = RTLIL::Const(1);
 | 
			
		||||
 | 
			
		||||
							for (auto cn : tbuf_cache.second) {
 | 
			
		||||
								auto c = module->cell(cn);
 | 
			
		||||
								if (c == nullptr)
 | 
			
		||||
									continue;
 | 
			
		||||
								for (auto port : c->connections()) {
 | 
			
		||||
									SigSpec sig = port.second;
 | 
			
		||||
									bool newsig = false;
 | 
			
		||||
									for (auto &bit : sig)
 | 
			
		||||
										if (sigmap(bit) == mapped_wire_bit) {
 | 
			
		||||
											bit = data_sig;
 | 
			
		||||
											newsig = true;
 | 
			
		||||
										}
 | 
			
		||||
									if (newsig)
 | 
			
		||||
										c->setPort(port.first, sig);
 | 
			
		||||
								}
 | 
			
		||||
							if (tbuf_cell) {
 | 
			
		||||
								module->remove(tbuf_cell);
 | 
			
		||||
								module->connect(wire_bit, data_sig);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							module->remove(tbuf_cell);
 | 
			
		||||
							skip_wires[wire->name].insert(i);
 | 
			
		||||
							continue;
 | 
			
		||||
							skip_wire_bits.insert(wire_bit);
 | 
			
		||||
							if (!toutpad_portname_pad.empty())
 | 
			
		||||
								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad));
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (GetSize(norewrites))
 | 
			
		||||
				{
 | 
			
		||||
					for (auto cell : module->cells())
 | 
			
		||||
					for (auto port : cell->connections())
 | 
			
		||||
					{
 | 
			
		||||
						if (norewrites.count(make_pair(cell->name, port.first)))
 | 
			
		||||
							continue;
 | 
			
		||||
 | 
			
		||||
						SigSpec orig_sig = sigmap(port.second);
 | 
			
		||||
						SigSpec new_sig = rewrites(orig_sig);
 | 
			
		||||
 | 
			
		||||
						if (orig_sig != new_sig)
 | 
			
		||||
							cell->setPort(port.first, new_sig);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (auto wire : module->selected_wires())
 | 
			
		||||
| 
						 | 
				
			
			@ -329,17 +311,11 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
				if (!wire->port_id)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				std::string celltype, portname, portname2;
 | 
			
		||||
				std::string celltype, portname_int, portname_pad;
 | 
			
		||||
				pool<int> skip_bit_indices;
 | 
			
		||||
 | 
			
		||||
				if (skip_wires.count(wire->name)) {
 | 
			
		||||
					if (!flag_bits)
 | 
			
		||||
						continue;
 | 
			
		||||
					skip_bit_indices = skip_wires.at(wire->name);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				for (int i = 0; i < GetSize(wire); i++)
 | 
			
		||||
					if (skip_wire_bits.count(sigmap(SigBit(wire, i))))
 | 
			
		||||
					if (skip_wire_bits.count(SigBit(wire, i)))
 | 
			
		||||
						skip_bit_indices.insert(i);
 | 
			
		||||
 | 
			
		||||
				if (GetSize(wire) == GetSize(skip_bit_indices))
 | 
			
		||||
| 
						 | 
				
			
			@ -351,8 +327,8 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					celltype = inpad_celltype;
 | 
			
		||||
					portname = inpad_portname;
 | 
			
		||||
					portname2 = inpad_portname2;
 | 
			
		||||
					portname_int = inpad_portname_o;
 | 
			
		||||
					portname_pad = inpad_portname_pad;
 | 
			
		||||
				} else
 | 
			
		||||
				if (!wire->port_input && wire->port_output) {
 | 
			
		||||
					if (outpad_celltype.empty()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -360,8 +336,8 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					celltype = outpad_celltype;
 | 
			
		||||
					portname = outpad_portname;
 | 
			
		||||
					portname2 = outpad_portname2;
 | 
			
		||||
					portname_int = outpad_portname_i;
 | 
			
		||||
					portname_pad = outpad_portname_pad;
 | 
			
		||||
				} else
 | 
			
		||||
				if (wire->port_input && wire->port_output) {
 | 
			
		||||
					if (inoutpad_celltype.empty()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -369,8 +345,8 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					celltype = inoutpad_celltype;
 | 
			
		||||
					portname = inoutpad_portname;
 | 
			
		||||
					portname2 = inoutpad_portname2;
 | 
			
		||||
					portname_int = inoutpad_portname_io;
 | 
			
		||||
					portname_pad = inoutpad_portname_pad;
 | 
			
		||||
				} else
 | 
			
		||||
					log_abort();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -381,29 +357,20 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
 | 
			
		||||
				log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str());
 | 
			
		||||
 | 
			
		||||
				RTLIL::Wire *new_wire = NULL;
 | 
			
		||||
				if (!portname2.empty()) {
 | 
			
		||||
					new_wire = module->addWire(NEW_ID, wire);
 | 
			
		||||
					module->swap_names(new_wire, wire);
 | 
			
		||||
					wire->attributes.clear();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (flag_bits)
 | 
			
		||||
				{
 | 
			
		||||
					for (int i = 0; i < wire->width; i++)
 | 
			
		||||
					{
 | 
			
		||||
						if (skip_bit_indices.count(i)) {
 | 
			
		||||
							if (wire->port_output)
 | 
			
		||||
								module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
 | 
			
		||||
							else
 | 
			
		||||
								module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
 | 
			
		||||
						if (skip_bit_indices.count(i))
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						SigBit wire_bit(wire, i);
 | 
			
		||||
 | 
			
		||||
						RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
 | 
			
		||||
						cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
 | 
			
		||||
						if (!portname2.empty())
 | 
			
		||||
							cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i));
 | 
			
		||||
						cell->setPort(RTLIL::escape_id(portname_int), wire_bit);
 | 
			
		||||
 | 
			
		||||
						if (!portname_pad.empty())
 | 
			
		||||
							rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(portname_pad));
 | 
			
		||||
						if (!widthparam.empty())
 | 
			
		||||
							cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
 | 
			
		||||
						if (!nameparam.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -414,9 +381,15 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
				else
 | 
			
		||||
				{
 | 
			
		||||
					RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
 | 
			
		||||
					cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire));
 | 
			
		||||
					if (!portname2.empty())
 | 
			
		||||
						cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire));
 | 
			
		||||
					cell->setPort(RTLIL::escape_id(portname_int), RTLIL::SigSpec(wire));
 | 
			
		||||
 | 
			
		||||
					if (!portname_pad.empty()) {
 | 
			
		||||
						RTLIL::Wire *new_wire = NULL;
 | 
			
		||||
						new_wire = module->addWire(NEW_ID, wire);
 | 
			
		||||
						module->swap_names(new_wire, wire);
 | 
			
		||||
						wire->attributes.clear();
 | 
			
		||||
						cell->setPort(RTLIL::escape_id(portname_pad), RTLIL::SigSpec(new_wire));
 | 
			
		||||
					}
 | 
			
		||||
					if (!widthparam.empty())
 | 
			
		||||
						cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
 | 
			
		||||
					if (!nameparam.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -424,6 +397,32 @@ struct IopadmapPass : public Pass {
 | 
			
		|||
					cell->attributes[ID::keep] = RTLIL::Const(1);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (!rewrite_bits.count(wire)) {
 | 
			
		||||
					wire->port_id = 0;
 | 
			
		||||
					wire->port_input = false;
 | 
			
		||||
					wire->port_output = false;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (auto &it : rewrite_bits) {
 | 
			
		||||
				RTLIL::Wire *wire = it.first;
 | 
			
		||||
				RTLIL::Wire *new_wire = module->addWire(NEW_ID, wire);
 | 
			
		||||
				module->swap_names(new_wire, wire);
 | 
			
		||||
				wire->attributes.clear();
 | 
			
		||||
				for (int i = 0; i < wire->width; i++)
 | 
			
		||||
				{
 | 
			
		||||
					SigBit wire_bit(wire, i);
 | 
			
		||||
					if (!it.second.count(i)) {
 | 
			
		||||
						if (wire->port_output)
 | 
			
		||||
							module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
 | 
			
		||||
						else
 | 
			
		||||
							module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
 | 
			
		||||
					} else {
 | 
			
		||||
						auto &new_conn = it.second.at(i);
 | 
			
		||||
						new_conn.first->setPort(new_conn.second, RTLIL::SigSpec(new_wire, i));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				wire->port_id = 0;
 | 
			
		||||
				wire->port_input = false;
 | 
			
		||||
				wire->port_output = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ struct SynthAchronixPass : public ScriptPass {
 | 
			
		|||
    log("        do not flatten design before synthesis\n");
 | 
			
		||||
    log("\n");
 | 
			
		||||
    log("    -retime\n");
 | 
			
		||||
    log("        run 'abc' with -dff option\n");
 | 
			
		||||
    log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
    log("\n");
 | 
			
		||||
    log("\n");
 | 
			
		||||
    log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -152,12 +152,12 @@ struct SynthAchronixPass : public ScriptPass {
 | 
			
		|||
        run("clean -purge");
 | 
			
		||||
        run("setundef -undriven -zero");
 | 
			
		||||
        if (retime || help_mode)
 | 
			
		||||
          run("abc -markgroups -dff", "(only if -retime)");
 | 
			
		||||
          run("abc -markgroups -dff -D 1", "(only if -retime)");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    if (check_label("map_luts"))
 | 
			
		||||
      {
 | 
			
		||||
        run("abc -lut 4" + string(retime ? " -dff" : ""));
 | 
			
		||||
        run("abc -lut 4" + string(retime ? " -dff -D 1" : ""));
 | 
			
		||||
        run("clean");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,6 @@ $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_map.v))
 | 
			
		|||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/arith_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.v))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/eagle_bb.v))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/dram_init_16x4.vh))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutram_init_16x4.vh))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,14 +6,14 @@ module  \$_DFFE_NP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REG
 | 
			
		|||
module  \$_DFFE_PN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(E), .sr(1'b0)); endmodule
 | 
			
		||||
module  \$_DFFE_PP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(E), .sr(1'b0)); endmodule
 | 
			
		||||
 | 
			
		||||
module  \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"),  .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"),  .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"),  .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"),  .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"),  .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"),  .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
module  \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C),  .ce(1'b1), .sr(R)); endmodule
 | 
			
		||||
 | 
			
		||||
module \$_DLATCH_N_ (E, D, Q);
 | 
			
		||||
  wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
 | 
			
		|||
	input B1EN;
 | 
			
		||||
 | 
			
		||||
	EG_LOGIC_DRAM16X4 #(
 | 
			
		||||
		`include "dram_init_16x4.vh"
 | 
			
		||||
		`include "lutram_init_16x4.vh"
 | 
			
		||||
	) _TECHMAP_REPLACE_ (
 | 
			
		||||
		.di(B1DATA),
 | 
			
		||||
		.waddr(B1ADDR),
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,10 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -nolutram\n");
 | 
			
		||||
		log("        do not use EG_LOGIC_DRAM16X4 cells in output netlist\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +70,7 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, edif_file, json_file;
 | 
			
		||||
	bool flatten, retime;
 | 
			
		||||
	bool flatten, retime, nolutram;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +79,7 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
		json_file = "";
 | 
			
		||||
		flatten = true;
 | 
			
		||||
		retime = false;
 | 
			
		||||
		nolutram = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +114,10 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
				flatten = false;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-nolutram") {
 | 
			
		||||
				nolutram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-retime") {
 | 
			
		||||
				retime = true;
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -150,21 +158,25 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
			run("synth -run coarse");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("dram"))
 | 
			
		||||
		if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/anlogic/drams.txt");
 | 
			
		||||
			run("techmap -map +/anlogic/drams_map.v");
 | 
			
		||||
			run("memory_bram -rules +/anlogic/lutrams.txt");
 | 
			
		||||
			run("techmap -map +/anlogic/lutrams_map.v");
 | 
			
		||||
			run("setundef -zero -params t:EG_LOGIC_DRAM16X4");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("fine"))
 | 
			
		||||
		if (check_label("map_ffram"))
 | 
			
		||||
		{
 | 
			
		||||
			run("opt -fast -mux_undef -undriven -fine");
 | 
			
		||||
			run("memory_map");
 | 
			
		||||
			run("opt -undriven -fine");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_gates"))
 | 
			
		||||
		{
 | 
			
		||||
			run("techmap -map +/techmap.v -map +/anlogic/arith_map.v");
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run("abc -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_ffs"))
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +199,7 @@ struct SynthAnlogicPass : public ScriptPass
 | 
			
		|||
			run("techmap -map +/anlogic/cells_map.v");
 | 
			
		||||
			run("clean");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_anlogic"))
 | 
			
		||||
		{
 | 
			
		||||
			run("anlogic_fixcarry");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
 | 
			
		|||
 | 
			
		||||
		if (check_label("map_pla"))
 | 
			
		||||
		{
 | 
			
		||||
			run("abc -sop -I 40 -P 56");
 | 
			
		||||
			run("abc -sop -I 40 -P 56" + string(retime ? " -dff -D 1" : ""));
 | 
			
		||||
			run("clean");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,8 +194,6 @@ struct SynthCoolrunner2Pass : public ScriptPass
 | 
			
		|||
			if (!json_file.empty() || help_mode)
 | 
			
		||||
				run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log_pop();
 | 
			
		||||
	}
 | 
			
		||||
} SynthCoolrunner2Pass;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ struct SynthEasicPass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +158,7 @@ struct SynthEasicPass : public ScriptPass
 | 
			
		|||
			run("techmap");
 | 
			
		||||
			run("opt -fast");
 | 
			
		||||
			if (retime || help_mode) {
 | 
			
		||||
				run("abc -dff", " (only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", " (only if -retime)");
 | 
			
		||||
				run("opt_clean", "(only if -retime)");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,9 +8,9 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_map.v))
 | 
			
		|||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.v))
 | 
			
		||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_bb.v))
 | 
			
		||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutram.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams.txt))
 | 
			
		||||
$(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/brams.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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,43 +1,36 @@
 | 
			
		|||
# NB: Inputs/Outputs must be ordered alphabetically
 | 
			
		||||
#     (with exceptions for carry in/out)
 | 
			
		||||
# NB: Box inputs/outputs must each be in the same order
 | 
			
		||||
#     as their corresponding module definition
 | 
			
		||||
#     (with exceptions detailed below)
 | 
			
		||||
 | 
			
		||||
# Box 1 : CCU2C (2xCARRY + 2xLUT4)
 | 
			
		||||
# Outputs: S0, S1, COUT
 | 
			
		||||
#   (NB: carry chain input/output must be last
 | 
			
		||||
#        input/output and bus has been moved
 | 
			
		||||
#        there overriding the otherwise
 | 
			
		||||
#   (Exception: carry chain input/output must be the
 | 
			
		||||
#        last input and output and the entire bus has been
 | 
			
		||||
#        moved there overriding the otherwise
 | 
			
		||||
#        alphabetical ordering)
 | 
			
		||||
# name  ID   w/b   ins    outs
 | 
			
		||||
CCU2C   1      1   9      3
 | 
			
		||||
 | 
			
		||||
#A0   A1   B0   B1   C0    C1  D0   D1   CIN
 | 
			
		||||
379  -    379  -    275   -    141  -    257
 | 
			
		||||
630  379  630  379  526   275  392  141  273
 | 
			
		||||
516  516  516  516  412   412  278  278  43
 | 
			
		||||
#A0  B0   C0    D0   A1   B1   C1   D1   CIN
 | 
			
		||||
379  379  275   141  -    -    -    -    257 # S0
 | 
			
		||||
630  630  526   392  379  379  275  141  273 # S1
 | 
			
		||||
516  516  412   278  516  516  412  278   43 # COUT
 | 
			
		||||
 | 
			
		||||
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
 | 
			
		||||
# Outputs: DO0, DO1, DO2, DO3
 | 
			
		||||
# name               ID  w/b   ins   outs
 | 
			
		||||
$__ABC9_DPR16X4_COMB  2     0   8    4
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
#$DO0 $DO1 $DO2 $DO3 RAD0   RAD1   RAD2   RAD3
 | 
			
		||||
0     0    0    0    141    379    275    379 # DO0
 | 
			
		||||
0     0    0    0    141    379    275    379 # DO1
 | 
			
		||||
0     0    0    0    141    379    275    379 # DO2
 | 
			
		||||
0     0    0    0    141    379    275    379 # DO3
 | 
			
		||||
 | 
			
		||||
# Box 3 : PFUMX (MUX2)
 | 
			
		||||
# Outputs: Z
 | 
			
		||||
# name  ID   w/b   ins    outs
 | 
			
		||||
PFUMX   3    1     3      1
 | 
			
		||||
 | 
			
		||||
#ALUT  BLUT  C0
 | 
			
		||||
98     98    151
 | 
			
		||||
98     98    151 # Z
 | 
			
		||||
 | 
			
		||||
# Box 4 : L6MUX21 (MUX2)
 | 
			
		||||
# Outputs: Z
 | 
			
		||||
# name   ID   w/b   ins    outs
 | 
			
		||||
L6MUX21  4    1     3      1
 | 
			
		||||
 | 
			
		||||
#D0    D1    SD
 | 
			
		||||
140    141   148
 | 
			
		||||
140    141   148 # Z
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +1,27 @@
 | 
			
		|||
// ---------------------------------------
 | 
			
		||||
 | 
			
		||||
// Attach a (combinatorial) black-box onto the output
 | 
			
		||||
//   of this LUTRAM primitive to capture its
 | 
			
		||||
//   asynchronous read behaviour
 | 
			
		||||
module TRELLIS_DPR16X4 (
 | 
			
		||||
	input  [3:0] DI,
 | 
			
		||||
	input  [3:0] WAD,
 | 
			
		||||
	input        WRE,
 | 
			
		||||
	input        WCK,
 | 
			
		||||
	input  [3:0] RAD,
 | 
			
		||||
	(* techmap_autopurge *) input  [3:0] DI,
 | 
			
		||||
	(* techmap_autopurge *) input  [3:0] WAD,
 | 
			
		||||
	(* techmap_autopurge *) input        WRE,
 | 
			
		||||
	(* techmap_autopurge *) input        WCK,
 | 
			
		||||
	(* techmap_autopurge *) input  [3:0] RAD,
 | 
			
		||||
	output [3:0] DO
 | 
			
		||||
);
 | 
			
		||||
	parameter WCKMUX = "WCK";
 | 
			
		||||
	parameter WREMUX = "WRE";
 | 
			
		||||
	parameter [63:0] INITVAL = 64'h0000000000000000;
 | 
			
		||||
    wire [3:0] \$DO ;
 | 
			
		||||
    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 )
 | 
			
		||||
      .RAD(RAD), .DO($DO)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    \$__ABC9_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
 | 
			
		||||
    $__ABC9_DPR16X4_COMB do (.$DO($DO), .RAD(RAD), .DO(DO));
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
// ---------------------------------------
 | 
			
		||||
 | 
			
		||||
(* abc9_box_id=2 *)
 | 
			
		||||
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
 | 
			
		||||
module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
// ---------------------------------------
 | 
			
		||||
 | 
			
		||||
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
 | 
			
		||||
    assign Y = A;
 | 
			
		||||
module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
 | 
			
		||||
    assign DO = $DO;
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,12 @@
 | 
			
		|||
// Diamond flip-flops
 | 
			
		||||
module FD1P3AX(input     D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0),  .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3AY(input     D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0),  .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3AX(input     D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3AY(input     D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3BX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3DX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3IX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE"))  _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1P3JX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("LSR_OVER_CE"))  _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3AX(input     D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0),           .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3AY(input     D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0),           .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3AX(input     D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0),          .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3AY(input     D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0),          .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3BX(input PD, D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"),   .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD),          .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3DX(input CD, D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))        _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD),          .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module FD1S3IX(input CD, D,     CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"),  .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE"))  _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD),          .DI(D), .Q(Q)); endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,6 +47,21 @@ module  \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .
 | 
			
		|||
module  \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE"))  _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
module  \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE"))  _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
 | 
			
		||||
 | 
			
		||||
`ifdef ASYNC_PRLD
 | 
			
		||||
module  \$_DLATCH_N_ (input E, input D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.LSR(!E), .DI(1'b0), .M(D), .Q(Q)); endmodule
 | 
			
		||||
module  \$_DLATCH_P_ (input E, input D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.LSR(E), .DI(1'b0), .M(D), .Q(Q)); endmodule
 | 
			
		||||
 | 
			
		||||
module \$_DFFSR_NNN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || !R), .DI(D), .M(R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_NNP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || R), .DI(D), .M(!R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_NPN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || !R), .DI(D), .M(R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_NPP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || R), .DI(D), .M(!R), .Q(Q)); endmodule
 | 
			
		||||
 | 
			
		||||
module \$_DFFSR_PNN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || !R), .DI(D), .M(R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_PNP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || R), .DI(D), .M(!R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_PPN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || !R), .DI(D), .M(R), .Q(Q)); endmodule
 | 
			
		||||
module \$_DFFSR_PPP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC"))  _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || R), .DI(D), .M(!R), .Q(Q)); endmodule
 | 
			
		||||
`endif
 | 
			
		||||
 | 
			
		||||
`include "cells_ff.vh"
 | 
			
		||||
`include "cells_io.vh"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
// ---------------------------------------
 | 
			
		||||
 | 
			
		||||
(* lib_whitebox *)
 | 
			
		||||
module LUT4(input A, B, C, D, output Z);
 | 
			
		||||
    parameter [15:0] INIT = 16'h0000;
 | 
			
		||||
    wire [7:0] s3 = D ?     INIT[15:8] :     INIT[7:0];
 | 
			
		||||
| 
						 | 
				
			
			@ -31,13 +32,8 @@ module CCU2C(
 | 
			
		|||
 | 
			
		||||
	// First half
 | 
			
		||||
	wire LUT4_0, LUT2_0;
 | 
			
		||||
`ifdef _ABC
 | 
			
		||||
	assign LUT4_0 = INIT0[{D0, C0, B0, A0}];
 | 
			
		||||
	assign LUT2_0 = INIT0[{2'b00, B0, A0}];
 | 
			
		||||
`else
 | 
			
		||||
	LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0));
 | 
			
		||||
	LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0));
 | 
			
		||||
`endif
 | 
			
		||||
	wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
 | 
			
		||||
	assign S0 = LUT4_0 ^ gated_cin_0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,13 +42,8 @@ module CCU2C(
 | 
			
		|||
 | 
			
		||||
	// Second half
 | 
			
		||||
	wire LUT4_1, LUT2_1;
 | 
			
		||||
`ifdef _ABC
 | 
			
		||||
	assign LUT4_1 = INIT1[{D1, C1, B1, A1}];
 | 
			
		||||
	assign LUT2_1 = INIT1[{2'b00, B1, A1}];
 | 
			
		||||
`else
 | 
			
		||||
	LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1));
 | 
			
		||||
	LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1));
 | 
			
		||||
`endif
 | 
			
		||||
	wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
 | 
			
		||||
	assign S1 = LUT4_1 ^ gated_cin_1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -209,6 +200,7 @@ endmodule
 | 
			
		|||
 | 
			
		||||
// ---------------------------------------
 | 
			
		||||
 | 
			
		||||
(* lib_whitebox *)
 | 
			
		||||
module LUT2(input A, B, output Z);
 | 
			
		||||
    parameter [3:0] INIT = 4'h0;
 | 
			
		||||
    wire [1:0] s1 = B ?     INIT[ 3:2] :     INIT[1:0];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -noccu2\n");
 | 
			
		||||
		log("        do not use CCU2 cells in output netlist\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +79,9 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
		log("    -nowidelut\n");
 | 
			
		||||
		log("        do not use PFU muxes to implement LUTs larger than LUT4s\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -asyncprld\n");
 | 
			
		||||
		log("        use async PRLD mode to implement DLATCH and DFFSR (EXPERIMENTAL)\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -abc2\n");
 | 
			
		||||
		log("        run two passes of 'abc' for slightly improved logic density\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +102,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, blif_file, edif_file, json_file;
 | 
			
		||||
	bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;
 | 
			
		||||
	bool noccu2, nodffe, nobram, nolutram, nowidelut, asyncprld, flatten, retime, abc2, abc9, nodsp, vpr;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +115,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
		nobram = false;
 | 
			
		||||
		nolutram = false;
 | 
			
		||||
		nowidelut = false;
 | 
			
		||||
		asyncprld = false;
 | 
			
		||||
		flatten = true;
 | 
			
		||||
		retime = false;
 | 
			
		||||
		abc2 = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +180,10 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
				nobram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-asyncprld") {
 | 
			
		||||
				asyncprld = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
 | 
			
		||||
				nolutram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
	{
 | 
			
		||||
		if (check_label("begin"))
 | 
			
		||||
		{
 | 
			
		||||
			run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
 | 
			
		||||
			run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
 | 
			
		||||
			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -258,13 +266,13 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
 | 
			
		||||
		if (!nobram && check_label("map_bram", "(skip if -nobram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/ecp5/bram.txt");
 | 
			
		||||
			run("memory_bram -rules +/ecp5/brams.txt");
 | 
			
		||||
			run("techmap -map +/ecp5/brams_map.v");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/ecp5/lutram.txt");
 | 
			
		||||
			run("memory_bram -rules +/ecp5/lutrams.txt");
 | 
			
		||||
			run("techmap -map +/ecp5/lutrams_map.v");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +290,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
			else
 | 
			
		||||
				run("techmap -map +/techmap.v -map +/ecp5/arith_map.v");
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run("abc -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_ffs"))
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +300,7 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
			run("opt_clean");
 | 
			
		||||
			if (!nodffe)
 | 
			
		||||
				run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
 | 
			
		||||
			run("techmap -D NO_LUT -map +/ecp5/cells_map.v");
 | 
			
		||||
			run(stringf("techmap -D NO_LUT %s -map +/ecp5/cells_map.v", help_mode ? "[-D ASYNC_PRLD]" : (asyncprld ? "-D ASYNC_PRLD" : "")));
 | 
			
		||||
			run("opt_expr -undriven -mux_undef");
 | 
			
		||||
			run("simplemap");
 | 
			
		||||
			run("ecp5_ffinit");
 | 
			
		||||
| 
						 | 
				
			
			@ -306,17 +314,18 @@ struct SynthEcp5Pass : public ScriptPass
 | 
			
		|||
			if (abc2 || help_mode) {
 | 
			
		||||
				run("abc", "      (only if -abc2)");
 | 
			
		||||
			}
 | 
			
		||||
			std::string techmap_args = "-map +/ecp5/latches_map.v";
 | 
			
		||||
			std::string techmap_args = asyncprld ? "" : "-map +/ecp5/latches_map.v";
 | 
			
		||||
			if (abc9)
 | 
			
		||||
				techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1";
 | 
			
		||||
			run("techmap " + techmap_args);
 | 
			
		||||
			if (!asyncprld || abc9)
 | 
			
		||||
				run("techmap " + techmap_args);
 | 
			
		||||
 | 
			
		||||
			if (abc9) {
 | 
			
		||||
				run("read_verilog -icells -lib +/ecp5/abc9_model.v");
 | 
			
		||||
				if (nowidelut)
 | 
			
		||||
					run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
 | 
			
		||||
					run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200");
 | 
			
		||||
				else
 | 
			
		||||
					run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
 | 
			
		||||
					run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200");
 | 
			
		||||
				run("techmap -map +/ecp5/abc9_unmap.v");
 | 
			
		||||
			} else {
 | 
			
		||||
				if (nowidelut)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,4 @@ $(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_map.v))
 | 
			
		|||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/arith_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_sim.v))
 | 
			
		||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/bram.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams.txt))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,7 +58,10 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -nobram\n");
 | 
			
		||||
		log("        do not use EFX_RAM_5K cells in output netlist\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +70,7 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, edif_file, json_file;
 | 
			
		||||
	bool flatten, retime;
 | 
			
		||||
	bool flatten, retime, nobram;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +79,7 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
		json_file = "";
 | 
			
		||||
		flatten = true;
 | 
			
		||||
		retime = false;
 | 
			
		||||
		nobram = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +118,10 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
				retime = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-nobram") {
 | 
			
		||||
				nobram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		extra_args(args, argidx, design);
 | 
			
		||||
| 
						 | 
				
			
			@ -150,21 +158,25 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
			run("synth -run coarse");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_bram", "(skip if -nobram)"))
 | 
			
		||||
		if (!nobram || check_label("map_bram", "(skip if -nobram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/efinix/bram.txt");
 | 
			
		||||
			run("memory_bram -rules +/efinix/brams.txt");
 | 
			
		||||
			run("techmap -map +/efinix/brams_map.v");
 | 
			
		||||
			run("setundef -zero -params t:EFX_RAM_5K");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("fine"))
 | 
			
		||||
		if (check_label("map_ffram"))
 | 
			
		||||
		{
 | 
			
		||||
			run("opt -fast -mux_undef -undriven -fine");
 | 
			
		||||
			run("memory_map");
 | 
			
		||||
			run("opt -undriven -fine");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_gates"))
 | 
			
		||||
		{
 | 
			
		||||
			run("techmap -map +/techmap.v -map +/efinix/arith_map.v");
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run("abc -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_ffs"))
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +206,7 @@ struct SynthEfinixPass : public ScriptPass
 | 
			
		|||
			run("efinix_fixcarry");
 | 
			
		||||
			run("clean");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		if (check_label("check"))
 | 
			
		||||
		{
 | 
			
		||||
			run("hierarchy -check");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								techlibs/gowin/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								techlibs/gowin/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
brams_init.mk
 | 
			
		||||
bram_init_*.vh
 | 
			
		||||
| 
						 | 
				
			
			@ -7,9 +7,9 @@ $(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
 | 
			
		|||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/bram.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/drams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams.txt))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams_map.v))
 | 
			
		||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams.txt))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,133 +1,282 @@
 | 
			
		|||
`default_nettype none
 | 
			
		||||
//All DFF* have INIT, but the hardware is always initialised to the reset
 | 
			
		||||
//value regardless. The parameter is ignored.
 | 
			
		||||
 | 
			
		||||
// DFFN      D Flip-Flop with Negative-Edge Clock
 | 
			
		||||
module  \$_DFF_N_ (input D, C, output Q); DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
 | 
			
		||||
// DFF       D Flip-Flop
 | 
			
		||||
module  \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
 | 
			
		||||
// DFFN			 D Flip-Flop with Negative-Edge Clock
 | 
			
		||||
module	\$_DFF_N_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFE      D Flip-Flop with Clock Enable
 | 
			
		||||
module  \$_DFFE_PP_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E)); endmodule
 | 
			
		||||
module  \$_DFFE_PN_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
 | 
			
		||||
// DFF			 D Flip-Flop
 | 
			
		||||
module	\$_DFF_P_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNE     D Flip-Flop with Negative-Edge Clock and Clock Enable
 | 
			
		||||
module  \$_DFFE_NP_ (input D, C, E, output Q); DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E)); endmodule
 | 
			
		||||
module  \$_DFFE_NN_ (input D, C, E, output Q); DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
 | 
			
		||||
// DFFE			 D Flip-Flop with Clock Enable
 | 
			
		||||
module	\$_DFFE_PP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFR      D Flip-Flop with Synchronous Reset
 | 
			
		||||
module  \$__DFFS_PN0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
 | 
			
		||||
module  \$__DFFS_PP0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
 | 
			
		||||
module	\$_DFFE_PN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNR     D Flip-Flop with Negative-Edge Clock and Synchronous Reset
 | 
			
		||||
module  \$__DFFS_NN0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
 | 
			
		||||
module  \$__DFFS_NP0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
 | 
			
		||||
// DFFNE		 D Flip-Flop with Negative-Edge Clock and Clock Enable
 | 
			
		||||
module	\$_DFFE_NP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFRE     D Flip-Flop with Clock Enable and Synchronous Reset
 | 
			
		||||
module  \$__DFFSE_PN0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFSE_PP0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E)); endmodule
 | 
			
		||||
module	\$_DFFE_NN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
 | 
			
		||||
	generate
 | 
			
		||||
		if (_TECHMAP_WIREINIT_Q_ === 1'b1)
 | 
			
		||||
			DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E), .SET(1'b0));
 | 
			
		||||
		else
 | 
			
		||||
			DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E));
 | 
			
		||||
	endgenerate
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = 1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNRE    D Flip-Flop with Negative-Edge Clock,Clock Enable, and Synchronous Reset
 | 
			
		||||
module  \$__DFFNSE_PN0 (input D, C, R, E, output Q); DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFNSE_PP0 (input D, C, R, E, output Q); DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E)); endmodule
 | 
			
		||||
// DFFR			 D Flip-Flop with Synchronous Reset
 | 
			
		||||
module	\$__DFFS_PN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFS      D Flip-Flop with Synchronous Set
 | 
			
		||||
module  \$__DFFS_PN1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
 | 
			
		||||
module  \$__DFFS_PP1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
 | 
			
		||||
module	\$__DFFS_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNS     D Flip-Flop with Negative-Edge Clock and Synchronous Set
 | 
			
		||||
module  \$__DFFS_NN1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
 | 
			
		||||
module  \$__DFFS_NP1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
 | 
			
		||||
// DFFNR		 D Flip-Flop with Negative-Edge Clock and Synchronous Reset
 | 
			
		||||
module	\$__DFFS_NN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFS_NP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFSE     D Flip-Flop with Clock Enable and Synchronous Set
 | 
			
		||||
module  \$__DFFSE_PN1 (input D, C, R, E, output Q); DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFSE_PP1 (input D, C, R, E, output Q); DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E)); endmodule
 | 
			
		||||
// DFFRE		 D Flip-Flop with Clock Enable and Synchronous Reset
 | 
			
		||||
module	\$__DFFSE_PN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFSE_PP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNSE    D Flip-Flop with Negative-Edge Clock,Clock Enable,and Synchronous Set
 | 
			
		||||
module  \$__DFFSE_NN1 (input D, C, R, E, output Q); DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFSE_NP1 (input D, C, R, E, output Q); DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E)); endmodule
 | 
			
		||||
// DFFNRE		 D Flip-Flop with Negative-Edge Clock,Clock Enable, and Synchronous Reset
 | 
			
		||||
module	\$__DFFSE_NN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFSE_NP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFP      D Flip-Flop with Asynchronous Preset
 | 
			
		||||
module  \$_DFF_PP1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
 | 
			
		||||
// DFFS			 D Flip-Flop with Synchronous Set
 | 
			
		||||
module	\$__DFFS_PN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFS_PP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNP     D Flip-Flop with Negative-Edge Clock and Asynchronous Preset
 | 
			
		||||
module  \$_DFF_NP1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
 | 
			
		||||
module  \$_DFF_NN1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
 | 
			
		||||
// DFFNS		 D Flip-Flop with Negative-Edge Clock and Synchronous Set
 | 
			
		||||
module	\$__DFFS_NN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFS_NP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFC      D Flip-Flop with Asynchronous Clear
 | 
			
		||||
module  \$_DFF_PP0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
 | 
			
		||||
module  \$_DFF_PN0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
 | 
			
		||||
// DFFSE		 D Flip-Flop with Clock Enable and Synchronous Set
 | 
			
		||||
module	\$__DFFSE_PN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFSE_PP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNC     D Flip-Flop with Negative-Edge Clock and Asynchronous Clear
 | 
			
		||||
module  \$_DFF_NP0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
 | 
			
		||||
module  \$_DFF_NN0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
 | 
			
		||||
// DFFNSE		 D Flip-Flop with Negative-Edge Clock,Clock Enable,and Synchronous Set
 | 
			
		||||
module	\$__DFFSE_NN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFSE_NP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFPE     D Flip-Flop with Clock Enable and Asynchronous Preset
 | 
			
		||||
module  \$__DFFE_PP1 (input D, C, R, E, output Q); DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFE_PN1 (input D, C, R, E, output Q); DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E)); endmodule
 | 
			
		||||
// DFFP			 D Flip-Flop with Asynchronous Preset
 | 
			
		||||
module	\$_DFF_PP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$_DFF_PN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNPE    D Flip-Flop with Negative-Edge Clock,Clock Enable, and Asynchronous Preset
 | 
			
		||||
module  \$__DFFE_NP1 (input D, C, R, E, output Q); DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFE_NN1 (input D, C, R, E, output Q); DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E)); endmodule
 | 
			
		||||
// DFFNP		 D Flip-Flop with Negative-Edge Clock and Asynchronous Preset
 | 
			
		||||
module	\$_DFF_NP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$_DFF_NN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFCE     D Flip-Flop with Clock Enable and Asynchronous Clear
 | 
			
		||||
module  \$__DFFE_PP0 (input D, C, R, E, output Q); DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFE_PN0 (input D, C, R, E, output Q); DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E)); endmodule
 | 
			
		||||
// DFFC			 D Flip-Flop with Asynchronous Clear
 | 
			
		||||
module	\$_DFF_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$_DFF_PN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNCE    D Flip-Flop with Negative-Edge Clock,Clock Enable and Asynchronous Clear
 | 
			
		||||
module  \$__DFFE_NP0 (input D, C, R, E, output Q); DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E)); endmodule
 | 
			
		||||
module  \$__DFFE_NN0 (input D, C, R, E, output Q); DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E)); endmodule
 | 
			
		||||
// DFFNC		 D Flip-Flop with Negative-Edge Clock and Asynchronous Clear
 | 
			
		||||
module	\$_DFF_NP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$_DFF_NN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
 | 
			
		||||
	DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFPE		 D Flip-Flop with Clock Enable and Asynchronous Preset
 | 
			
		||||
module	\$__DFFE_PP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFE_PN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNPE		 D Flip-Flop with Negative-Edge Clock,Clock Enable, and Asynchronous Preset
 | 
			
		||||
module	\$__DFFE_NP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFE_NN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFCE		 D Flip-Flop with Clock Enable and Asynchronous Clear
 | 
			
		||||
module	\$__DFFE_PP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFE_PN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
// DFFNCE		 D Flip-Flop with Negative-Edge Clock,Clock Enable and Asynchronous Clear
 | 
			
		||||
module	\$__DFFE_NP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
module	\$__DFFE_NN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
 | 
			
		||||
	DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E));
 | 
			
		||||
	wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
 | 
			
		||||
endmodule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
module \$lut (A, Y);
 | 
			
		||||
  parameter WIDTH = 0;
 | 
			
		||||
  parameter LUT = 0;
 | 
			
		||||
	parameter WIDTH = 0;
 | 
			
		||||
	parameter LUT = 0;
 | 
			
		||||
 | 
			
		||||
  input [WIDTH-1:0] A;
 | 
			
		||||
  output Y;
 | 
			
		||||
	input [WIDTH-1:0] A;
 | 
			
		||||
	output Y;
 | 
			
		||||
 | 
			
		||||
  generate
 | 
			
		||||
    if (WIDTH == 1) begin
 | 
			
		||||
      LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
        .I0(A[0]));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 2) begin
 | 
			
		||||
      LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
        .I0(A[0]), .I1(A[1]));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 3) begin
 | 
			
		||||
      LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
        .I0(A[0]), .I1(A[1]), .I2(A[2]));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 4) begin
 | 
			
		||||
      LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
        .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 5) begin
 | 
			
		||||
      wire f0, f1;
 | 
			
		||||
      \$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0));
 | 
			
		||||
      \$lut #(.LUT(LUT[31:16]), .WIDTH(4)) lut1 (.A(A[3:0]), .Y(f1));
 | 
			
		||||
      MUX2_LUT5 mux5(.I0(f0), .I1(f1), .S0(A[4]), .O(Y));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 6) begin
 | 
			
		||||
      wire f0, f1;
 | 
			
		||||
      \$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0));
 | 
			
		||||
      \$lut #(.LUT(LUT[63:32]), .WIDTH(5)) lut1 (.A(A[4:0]), .Y(f1));
 | 
			
		||||
      MUX2_LUT6 mux6(.I0(f0), .I1(f1), .S0(A[5]), .O(Y));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 7) begin
 | 
			
		||||
      wire f0, f1;
 | 
			
		||||
      \$lut #(.LUT(LUT[63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
 | 
			
		||||
      \$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1));
 | 
			
		||||
      MUX2_LUT7 mux7(.I0(f0), .I1(f1), .S0(A[6]), .O(Y));
 | 
			
		||||
    end else
 | 
			
		||||
    if (WIDTH == 8) begin
 | 
			
		||||
      wire f0, f1;
 | 
			
		||||
      \$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
 | 
			
		||||
      \$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1));
 | 
			
		||||
      MUX2_LUT8 mux8(.I0(f0), .I1(f1), .S0(A[7]), .O(Y));
 | 
			
		||||
    end else begin
 | 
			
		||||
      wire _TECHMAP_FAIL_ = 1;
 | 
			
		||||
    end
 | 
			
		||||
  endgenerate
 | 
			
		||||
	generate
 | 
			
		||||
		if (WIDTH == 1) begin
 | 
			
		||||
			LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
				.I0(A[0]));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 2) begin
 | 
			
		||||
			LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
				.I0(A[0]), .I1(A[1]));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 3) begin
 | 
			
		||||
			LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
				.I0(A[0]), .I1(A[1]), .I2(A[2]));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 4) begin
 | 
			
		||||
			LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
 | 
			
		||||
				.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 5) begin
 | 
			
		||||
			wire f0, f1;
 | 
			
		||||
			\$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0));
 | 
			
		||||
			\$lut #(.LUT(LUT[31:16]), .WIDTH(4)) lut1 (.A(A[3:0]), .Y(f1));
 | 
			
		||||
			MUX2_LUT5 mux5(.I0(f0), .I1(f1), .S0(A[4]), .O(Y));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 6) begin
 | 
			
		||||
			wire f0, f1;
 | 
			
		||||
			\$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0));
 | 
			
		||||
			\$lut #(.LUT(LUT[63:32]), .WIDTH(5)) lut1 (.A(A[4:0]), .Y(f1));
 | 
			
		||||
			MUX2_LUT6 mux6(.I0(f0), .I1(f1), .S0(A[5]), .O(Y));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 7) begin
 | 
			
		||||
			wire f0, f1;
 | 
			
		||||
			\$lut #(.LUT(LUT[63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
 | 
			
		||||
			\$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1));
 | 
			
		||||
			MUX2_LUT7 mux7(.I0(f0), .I1(f1), .S0(A[6]), .O(Y));
 | 
			
		||||
		end else
 | 
			
		||||
		if (WIDTH == 8) begin
 | 
			
		||||
			wire f0, f1;
 | 
			
		||||
			\$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
 | 
			
		||||
			\$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1));
 | 
			
		||||
			MUX2_LUT8 mux8(.I0(f0), .I1(f1), .S0(A[7]), .O(Y));
 | 
			
		||||
		end else begin
 | 
			
		||||
			wire _TECHMAP_FAIL_ = 1;
 | 
			
		||||
		end
 | 
			
		||||
	endgenerate
 | 
			
		||||
endmodule
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,20 +55,23 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
		log("    -nobram\n");
 | 
			
		||||
		log("        do not use BRAM cells in output netlist\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -nodram\n");
 | 
			
		||||
		log("    -nolutram\n");
 | 
			
		||||
		log("        do not use distributed RAM cells in output netlist\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -noflatten\n");
 | 
			
		||||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -nowidelut\n");
 | 
			
		||||
		log("        do not use muxes to implement LUTs larger than LUT4s\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -abc9\n");
 | 
			
		||||
		log("        use new ABC9 flow (EXPERIMENTAL)\n");
 | 
			
		||||
		log("    -noiopads\n");
 | 
			
		||||
		log("        do not emit IOB at top level ports\n");
 | 
			
		||||
		//log("\n");
 | 
			
		||||
		//log("    -abc9\n");
 | 
			
		||||
		//log("        use new ABC9 flow (EXPERIMENTAL)\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +80,7 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, vout_file;
 | 
			
		||||
	bool retime, nobram, nodram, flatten, nodffe, nowidelut, abc9;
 | 
			
		||||
	bool retime, nobram, nolutram, flatten, nodffe, nowidelut, abc9, noiopads;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -87,9 +90,10 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
		flatten = true;
 | 
			
		||||
		nobram = false;
 | 
			
		||||
		nodffe = false;
 | 
			
		||||
		nodram = false;
 | 
			
		||||
		nolutram = false;
 | 
			
		||||
		nowidelut = false;
 | 
			
		||||
		abc9 = false;
 | 
			
		||||
		noiopads = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 | 
			
		||||
| 
						 | 
				
			
			@ -124,8 +128,8 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
				nobram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-nodram") {
 | 
			
		||||
				nodram = true;
 | 
			
		||||
			if (args[argidx] == "-nolutram" || /*deprecated*/args[argidx] == "-nodram") {
 | 
			
		||||
				nolutram = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-nodffe") {
 | 
			
		||||
| 
						 | 
				
			
			@ -140,8 +144,12 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
				nowidelut = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-abc9") {
 | 
			
		||||
				abc9 = true;
 | 
			
		||||
			//if (args[argidx] == "-abc9") {
 | 
			
		||||
			//	abc9 = true;
 | 
			
		||||
			//	continue;
 | 
			
		||||
			//}
 | 
			
		||||
			if (args[argidx] == "-noiopads") {
 | 
			
		||||
				noiopads = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -180,35 +188,39 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
			run("synth -run coarse");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!nobram && check_label("bram", "(skip if -nobram)"))
 | 
			
		||||
		if (!nobram && check_label("map_bram", "(skip if -nobram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/gowin/bram.txt");
 | 
			
		||||
			run("memory_bram -rules +/gowin/brams.txt");
 | 
			
		||||
			run("techmap -map +/gowin/brams_map.v -map +/gowin/cells_sim.v");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!nodram && check_label("dram", "(skip if -nodram)"))
 | 
			
		||||
		if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
 | 
			
		||||
		{
 | 
			
		||||
			run("memory_bram -rules +/gowin/dram.txt");
 | 
			
		||||
			run("techmap -map +/gowin/drams_map.v");
 | 
			
		||||
			run("memory_bram -rules +/gowin/lutrams.txt");
 | 
			
		||||
			run("techmap -map +/gowin/lutrams_map.v");
 | 
			
		||||
			run("determine_init");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("fine"))
 | 
			
		||||
		if (check_label("map_ffram"))
 | 
			
		||||
		{
 | 
			
		||||
			run("opt -fast -mux_undef -undriven -fine");
 | 
			
		||||
			run("memory_map");
 | 
			
		||||
			run("opt -undriven -fine");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_gates"))
 | 
			
		||||
		{
 | 
			
		||||
			run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
 | 
			
		||||
			run("techmap -map +/techmap.v");
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run("abc -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
			run("splitnets");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_ffs"))
 | 
			
		||||
		{
 | 
			
		||||
			run("dffsr2dff");
 | 
			
		||||
			run("dff2dffs");
 | 
			
		||||
			run("dff2dffs -match-init");
 | 
			
		||||
			run("opt_clean");
 | 
			
		||||
			if (!nodffe)
 | 
			
		||||
				run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
 | 
			
		||||
| 
						 | 
				
			
			@ -219,13 +231,13 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
 | 
			
		||||
		if (check_label("map_luts"))
 | 
			
		||||
		{
 | 
			
		||||
			if (nowidelut && abc9) {
 | 
			
		||||
			/*if (nowidelut && abc9) {
 | 
			
		||||
				run("abc9 -lut 4");
 | 
			
		||||
			} else if (nowidelut && !abc9) {
 | 
			
		||||
			} else*/ if (nowidelut && !abc9) {
 | 
			
		||||
				run("abc -lut 4");
 | 
			
		||||
			} else if (!nowidelut && abc9) {
 | 
			
		||||
			} else /*if (!nowidelut && abc9) {
 | 
			
		||||
				run("abc9 -lut 4:8");
 | 
			
		||||
			} else if (!nowidelut && !abc9) {
 | 
			
		||||
			} else*/ if (!nowidelut && !abc9) {
 | 
			
		||||
				run("abc -lut 4:8");
 | 
			
		||||
			}
 | 
			
		||||
			run("clean");
 | 
			
		||||
| 
						 | 
				
			
			@ -236,10 +248,10 @@ struct SynthGowinPass : public ScriptPass
 | 
			
		|||
			run("techmap -map +/gowin/cells_map.v");
 | 
			
		||||
			run("setundef -undriven -params -zero");
 | 
			
		||||
			run("hilomap -singleton -hicell VCC V -locell GND G");
 | 
			
		||||
			run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O "
 | 
			
		||||
				"-toutpad TBUF OEN:I:O -tinoutpad IOBUF OEN:O:I:IO", "(unless -noiopads)");
 | 
			
		||||
			if (!noiopads || help_mode)
 | 
			
		||||
				run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O "
 | 
			
		||||
					"-toutpad TBUF OEN:I:O -tinoutpad IOBUF OEN:O:I:IO", "(unless -noiopads)");
 | 
			
		||||
			run("clean");
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("check"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("The following commands are executed by this synthesis command:\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +165,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
 | 
			
		|||
			run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
 | 
			
		||||
			run("opt -fast");
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run("abc -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (check_label("map_luts"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,17 @@
 | 
			
		|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
 | 
			
		||||
 | 
			
		||||
# NB: Inputs/Outputs must be ordered alphabetically
 | 
			
		||||
#     (with exceptions for carry in/out)
 | 
			
		||||
# NB: Box inputs/outputs must each be in the same order
 | 
			
		||||
#     as their corresponding module definition
 | 
			
		||||
#     (with exceptions detailed below)
 | 
			
		||||
 | 
			
		||||
# Inputs: A B I0 I3 CI
 | 
			
		||||
# Outputs: O CO
 | 
			
		||||
#   (NB: carry chain input/output must be last
 | 
			
		||||
#        input/output and have been moved there
 | 
			
		||||
#        overriding the alphabetical ordering)
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
 | 
			
		||||
400 379 449 316 316
 | 
			
		||||
259 231 -   -   126
 | 
			
		||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
 | 
			
		||||
#                                 SB_LUT4+SB_CARRY)
 | 
			
		||||
#   (Exception: carry chain input/output must be the
 | 
			
		||||
#        last input and output and the entire bus has been
 | 
			
		||||
#        moved there overriding the otherwise
 | 
			
		||||
#        alphabetical ordering)
 | 
			
		||||
# name                 ID  w/b ins outs
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1   1   5   2
 | 
			
		||||
#A  B   I0  I3  CI
 | 
			
		||||
400 379 449 316 316 # O
 | 
			
		||||
259 231 -   -   126 # CO
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,17 @@
 | 
			
		|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
 | 
			
		||||
 | 
			
		||||
# NB: Inputs/Outputs must be ordered alphabetically
 | 
			
		||||
#     (with exceptions for carry in/out)
 | 
			
		||||
# NB: Box inputs/outputs must each be in the same order
 | 
			
		||||
#     as their corresponding module definition
 | 
			
		||||
#     (with exceptions detailed below)
 | 
			
		||||
 | 
			
		||||
# Inputs: A B I0 I3 CI
 | 
			
		||||
# Outputs: O CO
 | 
			
		||||
#   (NB: carry chain input/output must be last
 | 
			
		||||
#        input/output and have been moved there
 | 
			
		||||
#        overriding the alphabetical ordering)
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
 | 
			
		||||
589 558 661 465 465
 | 
			
		||||
675 609 -   -   186
 | 
			
		||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
 | 
			
		||||
#                                 SB_LUT4+SB_CARRY)
 | 
			
		||||
#   (Exception: carry chain input/output must be the
 | 
			
		||||
#        last input and output and the entire bus has been
 | 
			
		||||
#        moved there overriding the otherwise
 | 
			
		||||
#        alphabetical ordering)
 | 
			
		||||
# name                 ID  w/b ins outs
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1   1   5   2
 | 
			
		||||
#A  B   I0  I3  CI
 | 
			
		||||
589 558 661 465 465 # O
 | 
			
		||||
675 609 -   -   186 # CO
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,18 @@
 | 
			
		|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
 | 
			
		||||
 | 
			
		||||
# NB: Inputs/Outputs must be ordered alphabetically
 | 
			
		||||
#     (with exceptions for carry in/out)
 | 
			
		||||
# NB: Box inputs/outputs must each be in the same order
 | 
			
		||||
#     as their corresponding module definition
 | 
			
		||||
#     (with exceptions detailed below)
 | 
			
		||||
 | 
			
		||||
# Inputs: A B I0 I3 CI
 | 
			
		||||
# Outputs: O CO
 | 
			
		||||
#   (NB: carry chain input/output must be last
 | 
			
		||||
#        input/output and have been moved there
 | 
			
		||||
#        overriding the alphabetical ordering)
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
 | 
			
		||||
1231 1205 1285 874 874
 | 
			
		||||
675  609  -    -   278
 | 
			
		||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
 | 
			
		||||
#                                 SB_LUT4+SB_CARRY)
 | 
			
		||||
# Outputs: O, CO
 | 
			
		||||
#   (Exception: carry chain input/output must be the
 | 
			
		||||
#        last input and output and the entire bus has been
 | 
			
		||||
#        moved there overriding the otherwise
 | 
			
		||||
#        alphabetical ordering)
 | 
			
		||||
# name                 ID  w/b ins outs
 | 
			
		||||
$__ICE40_CARRY_WRAPPER 1   1   5   2
 | 
			
		||||
#A  B   I0  I3  CI
 | 
			
		||||
1231 1205 1285 874 874 # O
 | 
			
		||||
675  609  -    -   278 # CO
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
 | 
			
		|||
			//    A[2]: 1111 0000 1111 0000
 | 
			
		||||
			//    A[3]: 1111 1111 0000 0000
 | 
			
		||||
			.LUT(16'b 0110_1001_1001_0110)
 | 
			
		||||
		) fadd (
 | 
			
		||||
		) carry (
 | 
			
		||||
			.A(AA[i]),
 | 
			
		||||
			.B(BB[i]),
 | 
			
		||||
			.CI(C[i]),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,22 +61,3 @@ module \$lut (A, Y);
 | 
			
		|||
  endgenerate
 | 
			
		||||
endmodule
 | 
			
		||||
`endif
 | 
			
		||||
 | 
			
		||||
`ifndef NO_ADDER
 | 
			
		||||
module \$__ICE40_CARRY_WRAPPER (output CO, O, input A, B, CI, I0, I3);
 | 
			
		||||
  parameter LUT = 0;
 | 
			
		||||
  SB_CARRY carry (
 | 
			
		||||
    .I0(A),
 | 
			
		||||
    .I1(B),
 | 
			
		||||
    .CI(CI),
 | 
			
		||||
    .CO(CO)
 | 
			
		||||
  );
 | 
			
		||||
  \$lut #(
 | 
			
		||||
    .WIDTH(4),
 | 
			
		||||
    .LUT(LUT)
 | 
			
		||||
  ) lut (
 | 
			
		||||
    .A({I0,A,B,I3}),
 | 
			
		||||
    .Y(O)
 | 
			
		||||
  );
 | 
			
		||||
endmodule
 | 
			
		||||
`endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass {
 | 
			
		|||
						continue;
 | 
			
		||||
 | 
			
		||||
					if (initbits.count(bit)) {
 | 
			
		||||
						if (initbits.at(bit) != val)
 | 
			
		||||
							log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
 | 
			
		||||
						if (initbits.at(bit) != val) {
 | 
			
		||||
							log_warning("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
 | 
			
		||||
									log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val),
 | 
			
		||||
									log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
 | 
			
		||||
							initbits.at(bit) = State::Sx;
 | 
			
		||||
						}
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass {
 | 
			
		|||
					continue;
 | 
			
		||||
 | 
			
		||||
				State val = initbits.at(bit_q);
 | 
			
		||||
 | 
			
		||||
				if (val == State::Sx)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				handled_initbits.insert(bit_q);
 | 
			
		||||
 | 
			
		||||
				log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,11 @@ static void run_ice40_opts(Module *module)
 | 
			
		|||
 | 
			
		||||
	for (auto cell : module->selected_cells())
 | 
			
		||||
	{
 | 
			
		||||
		if (!cell->type.in("\\SB_LUT4", "\\SB_CARRY", "$__ICE40_CARRY_WRAPPER"))
 | 
			
		||||
			continue;
 | 
			
		||||
		if (cell->has_keep_attr())
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (cell->type == "\\SB_LUT4")
 | 
			
		||||
		{
 | 
			
		||||
			sb_lut_cells.push_back(cell);
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +117,23 @@ static void run_ice40_opts(Module *module)
 | 
			
		|||
 | 
			
		||||
			if (GetSize(replacement_output)) {
 | 
			
		||||
				optimized_co.insert(sigmap(cell->getPort("\\CO")[0]));
 | 
			
		||||
				auto it = cell->attributes.find(ID(SB_LUT4.name));
 | 
			
		||||
				if (it != cell->attributes.end()) {
 | 
			
		||||
					module->rename(cell, it->second.decode_string());
 | 
			
		||||
					decltype(Cell::attributes) new_attr;
 | 
			
		||||
					for (const auto &a : cell->attributes)
 | 
			
		||||
						if (a.first.begins_with("\\SB_LUT4.\\"))
 | 
			
		||||
							new_attr[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
 | 
			
		||||
						else if (a.first == ID(src))
 | 
			
		||||
							new_attr.insert(std::make_pair(a.first, a.second));
 | 
			
		||||
						else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived)))
 | 
			
		||||
							continue;
 | 
			
		||||
						else if (a.first.begins_with("\\SB_CARRY.\\"))
 | 
			
		||||
							continue;
 | 
			
		||||
						else
 | 
			
		||||
							log_abort();
 | 
			
		||||
					cell->attributes = std::move(new_attr);
 | 
			
		||||
				}
 | 
			
		||||
				module->connect(cell->getPort("\\CO")[0], replacement_output);
 | 
			
		||||
				module->design->scratchpad_set_bool("opt.did_something", true);
 | 
			
		||||
				log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		log("        do not flatten design before synthesis\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -retime\n");
 | 
			
		||||
		log("        run 'abc' with -dff option\n");
 | 
			
		||||
		log("        run 'abc' with '-dff -D 1' options\n");
 | 
			
		||||
		log("\n");
 | 
			
		||||
		log("    -nocarry\n");
 | 
			
		||||
		log("        do not use SB_CARRY cells in output netlist\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -102,8 +102,8 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		log("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	string top_opt, blif_file, edif_file, json_file, abc, device_opt;
 | 
			
		||||
	bool nocarry, nodffe, nobram, dsp, flatten, retime, noabc, abc2, vpr;
 | 
			
		||||
	string top_opt, blif_file, edif_file, json_file, device_opt;
 | 
			
		||||
	bool nocarry, nodffe, nobram, dsp, flatten, retime, noabc, abc2, vpr, abc9;
 | 
			
		||||
	int min_ce_use;
 | 
			
		||||
 | 
			
		||||
	void clear_flags() YS_OVERRIDE
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +122,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		noabc = false;
 | 
			
		||||
		abc2 = false;
 | 
			
		||||
		vpr = false;
 | 
			
		||||
		abc = "abc";
 | 
			
		||||
		abc9 = false;
 | 
			
		||||
		device_opt = "hx";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -207,7 +207,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-abc9") {
 | 
			
		||||
				abc = "abc9";
 | 
			
		||||
				abc9 = true;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (args[argidx] == "-device" && argidx+1 < args.size()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		if (device_opt != "hx" && device_opt != "lp" && device_opt !="u")
 | 
			
		||||
			log_cmd_error("Invalid or no device specified: '%s'\n", device_opt.c_str());
 | 
			
		||||
 | 
			
		||||
		if (abc == "abc9" && retime)
 | 
			
		||||
		if (abc9 && retime)
 | 
			
		||||
			log_cmd_error("-retime option not currently compatible with -abc9!\n");
 | 
			
		||||
 | 
			
		||||
		log_header(design, "Executing SYNTH_ICE40 pass.\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -273,7 +273,8 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
			run("opt_expr");
 | 
			
		||||
			run("opt_clean");
 | 
			
		||||
			if (help_mode || dsp) {
 | 
			
		||||
				run("memory_dff");
 | 
			
		||||
				run("memory_dff"); // ice40_dsp will merge registers, reserve memory port registers first
 | 
			
		||||
				run("wreduce t:$mul");
 | 
			
		||||
				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)");
 | 
			
		||||
| 
						 | 
				
			
			@ -316,7 +317,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
				run("techmap -map +/techmap.v -map +/ice40/arith_map.v");
 | 
			
		||||
			}
 | 
			
		||||
			if (retime || help_mode)
 | 
			
		||||
				run(abc + " -dff", "(only if -retime)");
 | 
			
		||||
				run("abc -dff -D 1", "(only if -retime)");
 | 
			
		||||
			run("ice40_opt");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,7 +341,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
		if (check_label("map_luts"))
 | 
			
		||||
		{
 | 
			
		||||
			if (abc2 || help_mode) {
 | 
			
		||||
				run(abc, "      (only if -abc2)");
 | 
			
		||||
				run("abc", "      (only if -abc2)");
 | 
			
		||||
				run("ice40_opt", "(only if -abc2)");
 | 
			
		||||
			}
 | 
			
		||||
			run("techmap -map +/ice40/latches_map.v");
 | 
			
		||||
| 
						 | 
				
			
			@ -349,7 +350,7 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
				run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)");
 | 
			
		||||
			}
 | 
			
		||||
			if (!noabc) {
 | 
			
		||||
				if (abc == "abc9") {
 | 
			
		||||
				if (abc9) {
 | 
			
		||||
					run("read_verilog -icells -lib +/ice40/abc9_model.v");
 | 
			
		||||
					int wire_delay;
 | 
			
		||||
					if (device_opt == "lp")
 | 
			
		||||
| 
						 | 
				
			
			@ -358,11 +359,12 @@ struct SynthIce40Pass : public ScriptPass
 | 
			
		|||
						wire_delay = 750;
 | 
			
		||||
					else
 | 
			
		||||
						wire_delay = 250;
 | 
			
		||||
					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)");
 | 
			
		||||
					run(stringf("abc9 -W %d -lut +/ice40/abc9_%s.lut -box +/ice40/abc9_%s.box", wire_delay, device_opt.c_str(), device_opt.c_str()));
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
					run(abc + " -dress -lut 4", "(skip if -noabc)");
 | 
			
		||||
					run("abc -dress -lut 4", "(skip if -noabc)");
 | 
			
		||||
			}
 | 
			
		||||
			run("ice40_wrapcarry -unwrap");
 | 
			
		||||
			run("techmap -D NO_LUT -map +/ice40/cells_map.v");
 | 
			
		||||
			run("clean");
 | 
			
		||||
			run("opt_lut -dlogic SB_CARRY:I0=2:I1=1:CI=0");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ $(eval $(call add_share_file,share/intel/common,techlibs/intel/common/brams_m9k.
 | 
			
		|||
$(eval $(call add_share_file,share/intel/common,techlibs/intel/common/brams_map_m9k.v))
 | 
			
		||||
 | 
			
		||||
# Add the cell models and mappings for the VQM backend
 | 
			
		||||
families := max10 a10gx cyclonev cyclone10 cycloneiv cycloneive
 | 
			
		||||
families := max10 arria10gx cyclonev cyclone10lp cycloneiv cycloneive
 | 
			
		||||
$(foreach family,$(families), $(eval $(call add_share_file,share/intel/$(family),techlibs/intel/$(family)/cells_sim.v)))
 | 
			
		||||
$(foreach family,$(families), $(eval $(call add_share_file,share/intel/$(family),techlibs/intel/$(family)/cells_map.v)))
 | 
			
		||||
#$(eval $(call add_share_file,share/intel/cycloneive,techlibs/intel/cycloneive/arith_map.v))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
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