mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-13 04:28:18 +00:00
Merge branch 'master' into eddie/submod_po
This commit is contained in:
commit
136842b1ef
|
@ -50,9 +50,15 @@ Yosys 0.9 .. Yosys 0.9-dev
|
||||||
- "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental)
|
- "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental)
|
||||||
- "synth_ice40 -dsp" to infer DSP blocks
|
- "synth_ice40 -dsp" to infer DSP blocks
|
||||||
- Added latch support to synth_xilinx
|
- 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 "check -mapped"
|
||||||
- Added checking of SystemVerilog always block types (always_comb,
|
- Added checking of SystemVerilog always block types (always_comb,
|
||||||
always_latch and always_ff)
|
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
|
Yosys 0.8 .. Yosys 0.9
|
||||||
----------------------
|
----------------------
|
||||||
|
|
46
Makefile
46
Makefile
|
@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
|
||||||
LDLIBS += -lrt
|
LDLIBS += -lrt
|
||||||
endif
|
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)
|
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ bumpversion:
|
||||||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||||
# will remove the 'abc' directory and you do not want to accidentally
|
# will remove the 'abc' directory and you do not want to accidentally
|
||||||
# delete your work on ABC..
|
# delete your work on ABC..
|
||||||
ABCREV = 623b5e8
|
ABCREV = 71f2b40
|
||||||
ABCPULL = 1
|
ABCPULL = 1
|
||||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||||
|
@ -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_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_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
|
||||||
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
||||||
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
|
|
||||||
|
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
|
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
|
||||||
|
|
||||||
# Reload Makefile.conf to override python specific variables if defined
|
# 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
|
#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)
|
ifeq ($(OS), Darwin)
|
||||||
BOOST_PYTHON_LIB ?= $(shell \
|
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_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_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_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$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||||
echo ""; fi; fi; fi; fi;)
|
echo ""; fi; fi; fi; fi;)
|
||||||
else
|
else
|
||||||
BOOST_PYTHON_LIB ?= $(shell \
|
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_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_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_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$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||||
echo ""; fi; fi; fi; fi;)
|
echo ""; fi; fi; fi; fi;)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -325,19 +333,19 @@ endif
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
||||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||||
else
|
else
|
||||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
||||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||||
else
|
else
|
||||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
27
README.md
27
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
|
- The ``clkbuf_sink`` attribute can be set on an input port of a module to
|
||||||
request clock buffer insertion by the ``clkbufmap`` pass.
|
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
|
- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
|
||||||
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
|
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
|
||||||
overridden by providing a custom selection to ``clkbufmap``.
|
overridden by providing a custom selection to ``clkbufmap``.
|
||||||
|
@ -357,19 +364,23 @@ Verilog Attributes and non-standard features
|
||||||
it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
|
it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
|
||||||
from inserting another pad cell on it.
|
from inserting another pad cell on it.
|
||||||
|
|
||||||
- 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`
|
blackbox or whitebox definition to a corresponding entry in a `abc9`
|
||||||
box-file.
|
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
|
carry-out (if output port) ports of a box. This information is necessary for
|
||||||
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
|
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
|
||||||
onto a bus port will affect only its most significant bit.
|
onto a bus port will affect only its most significant bit.
|
||||||
|
|
||||||
- The port attribute ``abc_arrival`` specifies an integer (for output ports
|
- 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,
|
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
|
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
|
- The frontend sets attributes ``always_comb``, ``always_latch`` and
|
||||||
``always_ff`` on processes derived from SystemVerilog style always blocks
|
``always_ff`` on processes derived from SystemVerilog style always blocks
|
||||||
|
@ -447,10 +458,10 @@ Verilog Attributes and non-standard features
|
||||||
expressions over parameters and constant values are allowed). The intended
|
expressions over parameters and constant values are allowed). The intended
|
||||||
use for this is synthesis-time DRC.
|
use for this is synthesis-time DRC.
|
||||||
|
|
||||||
- There is limited support for converting specify .. endspecify statements to
|
- There is limited support for converting ``specify`` .. ``endspecify``
|
||||||
special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in
|
statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells,
|
||||||
blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this
|
for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to
|
||||||
functionality. (By default specify .. endspecify blocks are ignored.)
|
enable this functionality. (By default these blocks are ignored.)
|
||||||
|
|
||||||
|
|
||||||
Non-standard or SystemVerilog features for formal verification
|
Non-standard or SystemVerilog features for formal verification
|
||||||
|
|
|
@ -787,6 +787,14 @@ struct AigerBackend : public Backend {
|
||||||
if (top_module == nullptr)
|
if (top_module == nullptr)
|
||||||
log_error("Can't find top module in current design!\n");
|
log_error("Can't find top module in current design!\n");
|
||||||
|
|
||||||
|
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);
|
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
|
||||||
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
|
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
|
||||||
|
|
||||||
|
|
|
@ -81,8 +81,8 @@ struct XAigerWriter
|
||||||
pool<SigBit> input_bits, output_bits;
|
pool<SigBit> input_bits, output_bits;
|
||||||
dict<SigBit, SigBit> not_map, alias_map;
|
dict<SigBit, SigBit> not_map, alias_map;
|
||||||
dict<SigBit, pair<SigBit, SigBit>> and_map;
|
dict<SigBit, pair<SigBit, SigBit>> and_map;
|
||||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
|
vector<SigBit> ci_bits, co_bits;
|
||||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
|
dict<SigBit, Cell*> ff_bits;
|
||||||
dict<SigBit, float> arrival_times;
|
dict<SigBit, float> arrival_times;
|
||||||
|
|
||||||
vector<pair<int, int>> aig_gates;
|
vector<pair<int, int>> aig_gates;
|
||||||
|
@ -93,7 +93,6 @@ struct XAigerWriter
|
||||||
dict<SigBit, int> ordered_outputs;
|
dict<SigBit, int> ordered_outputs;
|
||||||
|
|
||||||
vector<Cell*> box_list;
|
vector<Cell*> box_list;
|
||||||
bool omode = false;
|
|
||||||
|
|
||||||
int mkgate(int a0, int a1)
|
int mkgate(int a0, int a1)
|
||||||
{
|
{
|
||||||
|
@ -141,7 +140,6 @@ struct XAigerWriter
|
||||||
{
|
{
|
||||||
pool<SigBit> undriven_bits;
|
pool<SigBit> undriven_bits;
|
||||||
pool<SigBit> unused_bits;
|
pool<SigBit> unused_bits;
|
||||||
pool<SigBit> keep_bits;
|
|
||||||
|
|
||||||
// promote public wires
|
// promote public wires
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
|
@ -153,367 +151,333 @@ struct XAigerWriter
|
||||||
if (wire->port_input)
|
if (wire->port_input)
|
||||||
sigmap.add(wire);
|
sigmap.add(wire);
|
||||||
|
|
||||||
// promote output wires
|
// promote keep wires
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
if (wire->port_output)
|
if (wire->get_bool_attribute(ID::keep))
|
||||||
sigmap.add(wire);
|
sigmap.add(wire);
|
||||||
|
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
{
|
|
||||||
bool keep = wire->attributes.count("\\keep");
|
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(wire); i++)
|
for (int i = 0; i < GetSize(wire); i++)
|
||||||
{
|
{
|
||||||
SigBit wirebit(wire, i);
|
SigBit wirebit(wire, i);
|
||||||
SigBit bit = sigmap(wirebit);
|
SigBit bit = sigmap(wirebit);
|
||||||
|
|
||||||
if (bit.wire) {
|
if (bit.wire == nullptr) {
|
||||||
undriven_bits.insert(bit);
|
if (wire->port_output) {
|
||||||
unused_bits.insert(bit);
|
aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
output_bits.insert(wirebit);
|
output_bits.insert(wirebit);
|
||||||
}
|
}
|
||||||
else
|
continue;
|
||||||
log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit));
|
}
|
||||||
|
|
||||||
|
undriven_bits.insert(bit);
|
||||||
|
unused_bits.insert(bit);
|
||||||
|
|
||||||
|
bool keep = wire->get_bool_attribute(ID::keep);
|
||||||
|
if (wire->port_input || keep)
|
||||||
|
input_bits.insert(bit);
|
||||||
|
|
||||||
|
if (wire->port_output || keep) {
|
||||||
|
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
|
|
||||||
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
|
||||||
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
|
||||||
bool abc9_box_seen = false;
|
|
||||||
|
|
||||||
for (auto cell : module->selected_cells()) {
|
|
||||||
if (cell->type == "$_NOT_")
|
|
||||||
{
|
|
||||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
|
||||||
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cell->type == "$_AND_")
|
|
||||||
{
|
|
||||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
|
||||||
SigBit B = sigmap(cell->getPort("\\B").as_bit());
|
|
||||||
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
|
||||||
unused_bits.erase(A);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_assert(!holes_mode);
|
|
||||||
|
|
||||||
|
dict<IdString,dict<IdString,int>> arrival_cache;
|
||||||
|
for (auto cell : module->cells()) {
|
||||||
RTLIL::Module* inst_module = module->design->module(cell->type);
|
RTLIL::Module* inst_module = module->design->module(cell->type);
|
||||||
if (inst_module && inst_module->attributes.count("\\abc9_box_id")) {
|
if (!cell->has_keep_attr()) {
|
||||||
abc9_box_seen = true;
|
if (cell->type == "$_NOT_")
|
||||||
|
{
|
||||||
|
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||||
|
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
||||||
|
unused_bits.erase(A);
|
||||||
|
undriven_bits.erase(Y);
|
||||||
|
not_map[Y] = A;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!holes_mode) {
|
if (cell->type == "$_AND_")
|
||||||
toposort.node(cell->name);
|
{
|
||||||
for (const auto &conn : cell->connections()) {
|
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||||
auto port_wire = inst_module->wire(conn.first);
|
SigBit B = sigmap(cell->getPort("\\B").as_bit());
|
||||||
if (port_wire->port_input) {
|
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
||||||
// Ignore inout for the sake of topographical ordering
|
unused_bits.erase(A);
|
||||||
if (port_wire->port_output) continue;
|
unused_bits.erase(B);
|
||||||
for (auto bit : sigmap(conn.second))
|
undriven_bits.erase(Y);
|
||||||
bit_users[bit].insert(cell->name);
|
and_map[Y] = make_pair(A, B);
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (port_wire->port_output)
|
if (cell->type == "$__ABC9_FF_" &&
|
||||||
for (auto bit : sigmap(conn.second))
|
// The presence of an abc9_mergeability attribute indicates
|
||||||
bit_drivers[bit].insert(cell->name);
|
// 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);
|
||||||
|
if (input_bits.erase(Q))
|
||||||
|
log_assert(Q.wire->attributes.count(ID::keep));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst_module) {
|
||||||
|
bool abc9_flop = false;
|
||||||
|
auto it = cell->attributes.find("\\abc9_box_seq");
|
||||||
|
if (it != cell->attributes.end()) {
|
||||||
|
int abc9_box_seq = it->second.as_int();
|
||||||
|
if (GetSize(box_list) <= abc9_box_seq)
|
||||||
|
box_list.resize(abc9_box_seq+1);
|
||||||
|
box_list[abc9_box_seq] = cell;
|
||||||
|
// Only flop boxes may have arrival times
|
||||||
|
abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
|
||||||
|
if (!abc9_flop)
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto &cell_arrivals = arrival_cache[cell->type];
|
||||||
|
for (const auto &conn : cell->connections()) {
|
||||||
|
auto r = cell_arrivals.insert(conn.first);
|
||||||
|
auto &arrival = r.first->second;
|
||||||
|
if (r.second) {
|
||||||
|
auto port_wire = inst_module->wire(conn.first);
|
||||||
|
if (port_wire->port_output) {
|
||||||
|
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_flop)
|
||||||
|
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) {
|
bool cell_known = inst_module || cell->known();
|
||||||
for (auto b : c.second) {
|
for (const auto &c : cell->connections()) {
|
||||||
Wire *w = b.wire;
|
if (c.second.is_fully_const()) continue;
|
||||||
if (!w) continue;
|
auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
|
||||||
if (!w->port_output || !cell_known) {
|
auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
|
||||||
SigBit I = sigmap(b);
|
auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
|
||||||
if (I != b)
|
if (!is_input && !is_output)
|
||||||
alias_map[b] = I;
|
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
|
||||||
output_bits.insert(b);
|
|
||||||
unused_bits.erase(b);
|
|
||||||
|
|
||||||
if (!cell_known)
|
if (is_input)
|
||||||
keep_bits.insert(b);
|
for (auto b : c.second) {
|
||||||
}
|
Wire *w = b.wire;
|
||||||
|
if (!w) continue;
|
||||||
|
// Do not add as PO if bit is already a PI
|
||||||
|
if (input_bits.count(b))
|
||||||
|
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));
|
//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abc9_box_seen) {
|
dict<IdString, std::vector<IdString>> box_ports;
|
||||||
for (auto &it : bit_users)
|
for (auto cell : box_list) {
|
||||||
if (bit_drivers.count(it.first))
|
log_assert(cell);
|
||||||
for (auto driver_cell : bit_drivers.at(it.first))
|
|
||||||
for (auto user_cell : it.second)
|
|
||||||
toposort.edge(driver_cell, user_cell);
|
|
||||||
|
|
||||||
#if 0
|
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||||
toposort.analyze_loops = true;
|
log_assert(box_module);
|
||||||
#endif
|
log_assert(box_module->attributes.count("\\abc9_box_id"));
|
||||||
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
|
|
||||||
#if 0
|
|
||||||
unsigned i = 0;
|
|
||||||
for (auto &it : toposort.loops) {
|
|
||||||
log(" loop %d\n", i++);
|
|
||||||
for (auto cell_name : it) {
|
|
||||||
auto cell = module->cell(cell_name);
|
|
||||||
log_assert(cell);
|
|
||||||
log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
log_assert(no_loops);
|
|
||||||
|
|
||||||
for (auto cell_name : toposort.sorted) {
|
auto r = box_ports.insert(cell->type);
|
||||||
RTLIL::Cell *cell = module->cell(cell_name);
|
if (r.second) {
|
||||||
log_assert(cell);
|
// Make carry in the last PI, and carry out the last PO
|
||||||
|
// since ABC requires it this way
|
||||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
IdString carry_in, carry_out;
|
||||||
if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
|
|
||||||
|
|
||||||
// Fully pad all unused input connections of this box cell with S0
|
|
||||||
// Fully pad all undriven output connections of this box cell with anonymous wires
|
|
||||||
// NB: Assume box_module->ports are sorted alphabetically
|
|
||||||
// (as RTLIL::Module::fixup_ports() would do)
|
|
||||||
for (const auto &port_name : box_module->ports) {
|
for (const auto &port_name : box_module->ports) {
|
||||||
RTLIL::Wire* w = box_module->wire(port_name);
|
auto w = box_module->wire(port_name);
|
||||||
log_assert(w);
|
log_assert(w);
|
||||||
auto it = cell->connections_.find(port_name);
|
if (w->get_bool_attribute("\\abc9_carry")) {
|
||||||
if (w->port_input) {
|
if (w->port_input) {
|
||||||
RTLIL::SigSpec rhs;
|
if (carry_in != IdString())
|
||||||
if (it != cell->connections_.end()) {
|
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module));
|
||||||
if (GetSize(it->second) < GetSize(w))
|
carry_in = port_name;
|
||||||
it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second)));
|
|
||||||
rhs = it->second;
|
|
||||||
}
|
}
|
||||||
else {
|
if (w->port_output) {
|
||||||
rhs = RTLIL::SigSpec(State::S0, GetSize(w));
|
if (carry_out != IdString())
|
||||||
cell->setPort(port_name, rhs);
|
log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module));
|
||||||
}
|
carry_out = port_name;
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (auto b : rhs.bits()) {
|
|
||||||
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, cell, port_name, offset++, 0);
|
|
||||||
unused_bits.erase(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (w->port_output) {
|
|
||||||
RTLIL::SigSpec rhs;
|
|
||||||
auto it = cell->connections_.find(w->name);
|
|
||||||
if (it != cell->connections_.end()) {
|
|
||||||
if (GetSize(it->second) < GetSize(w))
|
|
||||||
it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second)));
|
|
||||||
rhs = it->second;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Wire *wire = module->addWire(NEW_ID, GetSize(w));
|
|
||||||
if (blackbox)
|
|
||||||
wire->set_bool_attribute(ID(abc9_padding));
|
|
||||||
rhs = wire;
|
|
||||||
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;
|
|
||||||
undriven_bits.erase(O);
|
|
||||||
input_bits.erase(b);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
box_list.emplace_back(cell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Free memory from toposort, bit_drivers, bit_users
|
for (auto port_name : r.first->second) {
|
||||||
}
|
auto w = box_module->wire(port_name);
|
||||||
|
log_assert(w);
|
||||||
|
auto rhs = cell->connections_.at(port_name, SigSpec());
|
||||||
|
rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs)));
|
||||||
|
if (w->port_input)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (w->port_output)
|
||||||
|
for (const auto &b : rhs) {
|
||||||
|
SigBit O = sigmap(b);
|
||||||
|
if (O != b)
|
||||||
|
alias_map[O] = b;
|
||||||
|
ci_bits.emplace_back(b);
|
||||||
|
undriven_bits.erase(O);
|
||||||
|
// If PI and CI, then must be a (* keep *) wire
|
||||||
|
if (input_bits.erase(O)) {
|
||||||
|
log_assert(output_bits.count(O));
|
||||||
|
log_assert(O.wire->get_bool_attribute(ID::keep));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto bit : input_bits) {
|
// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box
|
||||||
if (!output_bits.count(bit))
|
if (box_module->get_bool_attribute("\\abc9_flop")) {
|
||||||
continue;
|
SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
|
||||||
RTLIL::Wire *wire = bit.wire;
|
if (rhs.empty())
|
||||||
// If encountering an inout port, or a keep-ed wire, then create a new wire
|
log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module));
|
||||||
// with $inout.out suffix, make it a PO driven by the existing inout, and
|
|
||||||
// inherit existing inout's drivers
|
for (auto b : rhs) {
|
||||||
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|
SigBit I = sigmap(b);
|
||||||
|| keep_bits.count(bit)) {
|
if (b == RTLIL::Sx)
|
||||||
RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str());
|
b = State::S0;
|
||||||
RTLIL::Wire *new_wire = module->wire(wire_name);
|
else if (I != b) {
|
||||||
if (!new_wire)
|
if (I == RTLIL::Sx)
|
||||||
new_wire = module->addWire(wire_name, GetSize(wire));
|
alias_map[b] = State::S0;
|
||||||
SigBit new_bit(new_wire, bit.offset);
|
else
|
||||||
module->connect(new_bit, bit);
|
alias_map[b] = I;
|
||||||
if (not_map.count(bit)) {
|
}
|
||||||
auto a = not_map.at(bit);
|
co_bits.emplace_back(b);
|
||||||
not_map[new_bit] = a;
|
unused_bits.erase(I);
|
||||||
}
|
}
|
||||||
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)
|
for (auto bit : unused_bits)
|
||||||
undriven_bits.erase(bit);
|
undriven_bits.erase(bit);
|
||||||
|
|
||||||
if (!undriven_bits.empty() && !holes_mode) {
|
// Make all undriven bits a primary input
|
||||||
undriven_bits.sort();
|
for (auto bit : undriven_bits) {
|
||||||
for (auto bit : undriven_bits) {
|
input_bits.insert(bit);
|
||||||
log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
|
undriven_bits.erase(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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (holes_mode) {
|
if (holes_mode) {
|
||||||
struct sort_by_port_id {
|
struct sort_by_port_id {
|
||||||
bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
|
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());
|
input_bits.sort(sort_by_port_id());
|
||||||
output_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::S0] = 0;
|
||||||
aig_map[State::S1] = 1;
|
aig_map[State::S1] = 1;
|
||||||
|
|
||||||
for (auto bit : input_bits) {
|
for (const auto &bit : input_bits) {
|
||||||
aig_m++, aig_i++;
|
aig_m++, aig_i++;
|
||||||
log_assert(!aig_map.count(bit));
|
log_assert(!aig_map.count(bit));
|
||||||
aig_map[bit] = 2*aig_m;
|
aig_map[bit] = 2*aig_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &c : ci_bits) {
|
for (const auto &i : ff_bits) {
|
||||||
RTLIL::SigBit bit = std::get<0>(c);
|
const Cell *cell = i.second;
|
||||||
|
const SigBit &q = sigmap(cell->getPort("\\Q"));
|
||||||
aig_m++, aig_i++;
|
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++;
|
||||||
|
// 1'bx may exist here due to a box output
|
||||||
|
// that has been padded to its full width
|
||||||
|
if (bit == State::Sx)
|
||||||
|
continue;
|
||||||
|
log_assert(!aig_map.count(bit));
|
||||||
aig_map[bit] = 2*aig_m;
|
aig_map[bit] = 2*aig_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &c : co_bits) {
|
for (auto bit : 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) {
|
|
||||||
ordered_outputs[bit] = aig_o++;
|
ordered_outputs[bit] = aig_o++;
|
||||||
aig_outputs.push_back(bit2aig(bit));
|
aig_outputs.push_back(bit2aig(bit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto &bit : output_bits) {
|
||||||
|
ordered_outputs[bit] = aig_o++;
|
||||||
|
int aig;
|
||||||
|
// Unlike bit2aig() which checks aig_map first, for
|
||||||
|
// inout/keep bits, since aig_map will point to
|
||||||
|
// the PI, first attempt to find the NOT/AND driver
|
||||||
|
// before resorting to an aig_map lookup (which
|
||||||
|
// could be another PO)
|
||||||
|
if (input_bits.count(bit)) {
|
||||||
|
if (not_map.count(bit)) {
|
||||||
|
aig = bit2aig(not_map.at(bit)) ^ 1;
|
||||||
|
} else if (and_map.count(bit)) {
|
||||||
|
auto args = and_map.at(bit);
|
||||||
|
int a0 = bit2aig(args.first);
|
||||||
|
int a1 = bit2aig(args.second);
|
||||||
|
aig = mkgate(a0, a1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
aig = aig_map.at(bit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
aig = bit2aig(bit);
|
||||||
|
aig_outputs.push_back(aig);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
void write_aiger(std::ostream &f, bool ascii_mode)
|
||||||
|
@ -575,7 +539,6 @@ struct XAigerWriter
|
||||||
|
|
||||||
f << "c";
|
f << "c";
|
||||||
|
|
||||||
log_assert(!output_bits.empty());
|
|
||||||
auto write_buffer = [](std::stringstream &buffer, int i32) {
|
auto write_buffer = [](std::stringstream &buffer, int i32) {
|
||||||
int32_t i32_be = to_big_endian(i32);
|
int32_t i32_be = to_big_endian(i32);
|
||||||
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
|
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
|
||||||
|
@ -583,14 +546,14 @@ struct XAigerWriter
|
||||||
std::stringstream h_buffer;
|
std::stringstream h_buffer;
|
||||||
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
|
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
|
||||||
write_h_buffer(1);
|
write_h_buffer(1);
|
||||||
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits));
|
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
|
||||||
write_h_buffer(input_bits.size() + ci_bits.size());
|
write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
|
||||||
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits));
|
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
|
||||||
write_h_buffer(output_bits.size() + GetSize(co_bits));
|
write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
|
||||||
log_debug("piNum = %d\n", GetSize(input_bits));
|
log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
|
||||||
write_h_buffer(input_bits.size());
|
write_h_buffer(input_bits.size() + ff_bits.size());
|
||||||
log_debug("poNum = %d\n", GetSize(output_bits));
|
log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
|
||||||
write_h_buffer(output_bits.size());
|
write_h_buffer(output_bits.size() + ff_bits.size());
|
||||||
log_debug("boxNum = %d\n", GetSize(box_list));
|
log_debug("boxNum = %d\n", GetSize(box_list));
|
||||||
write_h_buffer(box_list.size());
|
write_h_buffer(box_list.size());
|
||||||
|
|
||||||
|
@ -606,126 +569,100 @@ struct XAigerWriter
|
||||||
//for (auto bit : output_bits)
|
//for (auto bit : output_bits)
|
||||||
// write_o_buffer(0);
|
// write_o_buffer(0);
|
||||||
|
|
||||||
if (!box_list.empty()) {
|
if (!box_list.empty() || !ff_bits.empty()) {
|
||||||
RTLIL::Module *holes_module = module->design->addModule("$__holes__");
|
dict<IdString, std::tuple<int,int,int>> cell_cache;
|
||||||
log_assert(holes_module);
|
|
||||||
|
|
||||||
int port_id = 1;
|
|
||||||
int box_count = 0;
|
int box_count = 0;
|
||||||
for (auto cell : box_list) {
|
for (auto cell : box_list) {
|
||||||
|
log_assert(cell);
|
||||||
|
|
||||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||||
int box_inputs = 0, box_outputs = 0;
|
log_assert(box_module);
|
||||||
Cell *holes_cell = nullptr;
|
|
||||||
if (box_module->get_bool_attribute("\\whitebox")) {
|
auto r = cell_cache.insert(cell->type);
|
||||||
holes_cell = holes_module->addCell(cell->name, cell->type);
|
auto &v = r.first->second;
|
||||||
holes_cell->parameters = cell->parameters;
|
if (r.second) {
|
||||||
|
int box_inputs = 0, box_outputs = 0;
|
||||||
|
for (auto port_name : box_module->ports) {
|
||||||
|
RTLIL::Wire *w = box_module->wire(port_name);
|
||||||
|
log_assert(w);
|
||||||
|
if (w->port_input)
|
||||||
|
box_inputs += GetSize(w);
|
||||||
|
if (w->port_output)
|
||||||
|
box_outputs += 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++;
|
||||||
|
|
||||||
|
std::get<0>(v) = box_inputs;
|
||||||
|
std::get<1>(v) = box_outputs;
|
||||||
|
std::get<2>(v) = box_module->attributes.at("\\abc9_box_id").as_int();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Assume box_module->ports are sorted alphabetically
|
write_h_buffer(std::get<0>(v));
|
||||||
// (as RTLIL::Module::fixup_ports() would do)
|
write_h_buffer(std::get<1>(v));
|
||||||
for (const auto &port_name : box_module->ports) {
|
write_h_buffer(std::get<2>(v));
|
||||||
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++) {
|
|
||||||
box_inputs++;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (!port_wire.empty())
|
|
||||||
holes_cell->setPort(w->name, port_wire);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if (!port_wire.empty())
|
|
||||||
holes_cell->setPort(w->name, port_wire);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_h_buffer(box_inputs);
|
|
||||||
write_h_buffer(box_outputs);
|
|
||||||
write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int());
|
|
||||||
write_h_buffer(box_count++);
|
write_h_buffer(box_count++);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream r_buffer;
|
std::stringstream r_buffer;
|
||||||
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
|
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
|
||||||
write_r_buffer(0);
|
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";
|
f << "r";
|
||||||
std::string buffer_str = r_buffer.str();
|
std::string buffer_str = r_buffer.str();
|
||||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||||
f.write(buffer_str.data(), buffer_str.size());
|
f.write(buffer_str.data(), buffer_str.size());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str()));
|
||||||
if (holes_module) {
|
if (holes_module) {
|
||||||
log_push();
|
|
||||||
|
|
||||||
// NB: fixup_ports() will sort ports by name
|
|
||||||
//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);
|
|
||||||
|
|
||||||
// TODO: Should not need to opt_merge if we only instantiate
|
|
||||||
// each box type once...
|
|
||||||
Pass::call(holes_module->design, "opt_merge -share_all");
|
|
||||||
|
|
||||||
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_"))
|
|
||||||
log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n");
|
|
||||||
|
|
||||||
holes_module->design->selection_stack.pop_back();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
holes_design->add(holes_module);
|
|
||||||
Pass::call(holes_design, "clean -purge");
|
|
||||||
|
|
||||||
std::stringstream a_buffer;
|
std::stringstream a_buffer;
|
||||||
XAigerWriter writer(holes_module, true /* holes_mode */);
|
XAigerWriter writer(holes_module, true /* holes_mode */);
|
||||||
writer.write_aiger(a_buffer, false /*ascii_mode*/);
|
writer.write_aiger(a_buffer, false /*ascii_mode*/);
|
||||||
|
|
||||||
delete holes_design;
|
|
||||||
|
|
||||||
f << "a";
|
f << "a";
|
||||||
std::string buffer_str = a_buffer.str();
|
std::string buffer_str = a_buffer.str();
|
||||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||||
f.write(buffer_str.data(), buffer_str.size());
|
f.write(buffer_str.data(), buffer_str.size());
|
||||||
|
|
||||||
log_pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -747,19 +684,20 @@ struct XAigerWriter
|
||||||
//f.write(buffer_str.data(), buffer_str.size());
|
//f.write(buffer_str.data(), buffer_str.size());
|
||||||
|
|
||||||
f << stringf("Generated by %s\n", yosys_version_str);
|
f << stringf("Generated by %s\n", yosys_version_str);
|
||||||
|
|
||||||
|
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> input_lines;
|
||||||
dict<int, string> output_lines;
|
dict<int, string> output_lines;
|
||||||
dict<int, string> wire_lines;
|
|
||||||
|
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
{
|
{
|
||||||
//if (!verbose_map && wire->name[0] == '$')
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
SigSpec sig = sigmap(wire);
|
SigSpec sig = sigmap(wire);
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(wire); i++)
|
for (int i = 0; i < GetSize(wire); i++)
|
||||||
|
@ -773,17 +711,10 @@ struct XAigerWriter
|
||||||
|
|
||||||
if (output_bits.count(b)) {
|
if (output_bits.count(b)) {
|
||||||
int o = ordered_outputs.at(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;
|
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 +728,9 @@ struct XAigerWriter
|
||||||
f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
|
f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
|
||||||
|
|
||||||
output_lines.sort();
|
output_lines.sort();
|
||||||
if (omode)
|
|
||||||
output_lines[State::S0] = "output 0 0 $__dummy__\n";
|
|
||||||
for (auto &it : output_lines)
|
for (auto &it : output_lines)
|
||||||
f << it.second;
|
f << it.second;
|
||||||
log_assert(output_lines.size() == output_bits.size());
|
log_assert(output_lines.size() == output_bits.size());
|
||||||
|
|
||||||
wire_lines.sort();
|
|
||||||
for (auto &it : wire_lines)
|
|
||||||
f << it.second;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -817,23 +742,22 @@ struct XAigerBackend : public Backend {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" write_xaiger [options] [filename]\n");
|
log(" write_xaiger [options] [filename]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Write the current design to an XAIGER file. The design must be flattened and\n");
|
log("Write the top module (according to the (* top *) attribute or if only one module\n");
|
||||||
log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\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. Whitebox contents will be taken from the '<module-name>$holes'\n");
|
||||||
|
log("module, if it exists.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -ascii\n");
|
log(" -ascii\n");
|
||||||
log(" write ASCII version of AIGER format\n");
|
log(" write ASCII version of AIGER format\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -map <filename>\n");
|
log(" -map <filename>\n");
|
||||||
log(" write an extra file with port and latch symbols\n");
|
log(" write an extra file with port and box symbols\n");
|
||||||
log("\n");
|
|
||||||
log(" -vmap <filename>\n");
|
|
||||||
log(" like -map, but more verbose\n");
|
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
bool ascii_mode = false;
|
bool ascii_mode = false;
|
||||||
bool verbose_map = false;
|
|
||||||
std::string map_filename;
|
std::string map_filename;
|
||||||
|
|
||||||
log_header(design, "Executing XAIGER backend.\n");
|
log_header(design, "Executing XAIGER backend.\n");
|
||||||
|
@ -849,11 +773,6 @@ struct XAigerBackend : public Backend {
|
||||||
map_filename = args[++argidx];
|
map_filename = args[++argidx];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
|
|
||||||
map_filename = args[++argidx];
|
|
||||||
verbose_map = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx, !ascii_mode);
|
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||||
|
@ -863,6 +782,14 @@ struct XAigerBackend : public Backend {
|
||||||
if (top_module == nullptr)
|
if (top_module == nullptr)
|
||||||
log_error("Can't find top module in current design!\n");
|
log_error("Can't find top module in current design!\n");
|
||||||
|
|
||||||
|
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);
|
XAigerWriter writer(top_module);
|
||||||
writer.write_aiger(*f, ascii_mode);
|
writer.write_aiger(*f, ascii_mode);
|
||||||
|
|
||||||
|
@ -871,7 +798,7 @@ struct XAigerBackend : public Backend {
|
||||||
mapf.open(map_filename.c_str(), std::ofstream::trunc);
|
mapf.open(map_filename.c_str(), std::ofstream::trunc);
|
||||||
if (mapf.fail())
|
if (mapf.fail())
|
||||||
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
|
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;
|
} XAigerBackend;
|
||||||
|
|
|
@ -300,13 +300,33 @@ struct EdifBackend : public Backend {
|
||||||
*f << stringf(" (library DESIGN\n");
|
*f << stringf(" (library DESIGN\n");
|
||||||
*f << stringf(" (edifLevel 0)\n");
|
*f << stringf(" (edifLevel 0)\n");
|
||||||
*f << stringf(" (technology (numberDefinition))\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)
|
for (auto module : sorted_modules)
|
||||||
{
|
{
|
||||||
if (module->get_blackbox_attribute())
|
if (module->get_blackbox_attribute())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SigMap sigmap(module);
|
SigMap sigmap(module);
|
||||||
std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db;
|
std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db;
|
||||||
|
|
||||||
*f << stringf(" (cell %s\n", EDIF_DEF(module->name));
|
*f << stringf(" (cell %s\n", EDIF_DEF(module->name));
|
||||||
*f << stringf(" (cellType GENERIC)\n");
|
*f << stringf(" (cellType GENERIC)\n");
|
||||||
|
@ -323,17 +343,26 @@ struct EdifBackend : public Backend {
|
||||||
else if (!wire->port_input)
|
else if (!wire->port_input)
|
||||||
dir = "OUTPUT";
|
dir = "OUTPUT";
|
||||||
if (wire->width == 1) {
|
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));
|
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
||||||
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input));
|
||||||
} else {
|
} else {
|
||||||
int b[2];
|
int b[2];
|
||||||
b[wire->upto ? 0 : 1] = wire->start_offset;
|
b[wire->upto ? 0 : 1] = wire->start_offset;
|
||||||
b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1;
|
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++) {
|
for (int i = 0; i < wire->width; i++) {
|
||||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, 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));
|
net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,27 +377,6 @@ struct EdifBackend : public Backend {
|
||||||
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
|
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
|
||||||
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
||||||
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
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)
|
for (auto &p : cell->parameters)
|
||||||
add_prop(p.first, p.second);
|
add_prop(p.first, p.second);
|
||||||
if (attr_properties)
|
if (attr_properties)
|
||||||
|
@ -383,7 +391,7 @@ struct EdifBackend : public Backend {
|
||||||
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
|
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
|
||||||
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
||||||
else if (sig.size() == 1)
|
else if (sig.size() == 1)
|
||||||
net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
|
net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first)));
|
||||||
else {
|
else {
|
||||||
int member_idx = GetSize(sig)-i-1;
|
int member_idx = GetSize(sig)-i-1;
|
||||||
auto m = design->module(cell->type);
|
auto m = design->module(cell->type);
|
||||||
|
@ -392,8 +400,8 @@ struct EdifBackend : public Backend {
|
||||||
if (w)
|
if (w)
|
||||||
member_idx = GetSize(w)-i-1;
|
member_idx = GetSize(w)-i-1;
|
||||||
}
|
}
|
||||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))",
|
net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))",
|
||||||
EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)));
|
EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,11 +410,13 @@ struct EdifBackend : public Backend {
|
||||||
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
|
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
|
||||||
if (sig == RTLIL::State::Sx) {
|
if (sig == RTLIL::State::Sx) {
|
||||||
for (auto &ref : it.second)
|
for (auto &ref : it.second)
|
||||||
log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
|
log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str());
|
||||||
sig = RTLIL::State::S0;
|
sig = RTLIL::State::S0;
|
||||||
|
} else if (sig == RTLIL::State::Sz) {
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
for (auto &ref : it.second)
|
for (auto &ref : it.second)
|
||||||
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
|
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str());
|
||||||
log_abort();
|
log_abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,7 +433,7 @@ struct EdifBackend : public Backend {
|
||||||
}
|
}
|
||||||
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||||
for (auto &ref : it.second)
|
for (auto &ref : it.second)
|
||||||
*f << stringf(" %s\n", ref.c_str());
|
*f << stringf(" %s\n", ref.first.c_str());
|
||||||
if (sig.wire == NULL) {
|
if (sig.wire == NULL) {
|
||||||
if (nogndvcc)
|
if (nogndvcc)
|
||||||
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
|
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
|
||||||
|
@ -431,8 +441,37 @@ struct EdifBackend : public Backend {
|
||||||
*f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
|
*f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
|
||||||
if (sig == RTLIL::State::S1)
|
if (sig == RTLIL::State::S1)
|
||||||
*f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
|
*f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
|
||||||
|
}
|
||||||
|
*f << stringf(" )");
|
||||||
|
if (attr_properties && sig.wire != NULL)
|
||||||
|
for (auto &p : sig.wire->attributes)
|
||||||
|
add_prop(p.first, p.second);
|
||||||
|
*f << stringf("\n )\n");
|
||||||
|
}
|
||||||
|
for (auto &wire_it : module->wires_) {
|
||||||
|
RTLIL::Wire *wire = wire_it.second;
|
||||||
|
if (!wire->get_bool_attribute(ID::keep))
|
||||||
|
continue;
|
||||||
|
for(int i = 0; i < wire->width; i++) {
|
||||||
|
SigBit raw_sig = RTLIL::SigSpec(wire, i);
|
||||||
|
SigBit mapped_sig = sigmap(raw_sig);
|
||||||
|
if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0)
|
||||||
|
continue;
|
||||||
|
std::string netname = log_signal(raw_sig);
|
||||||
|
for (size_t i = 0; i < netname.size(); i++)
|
||||||
|
if (netname[i] == ' ' || netname[i] == '\\')
|
||||||
|
netname.erase(netname.begin() + i--);
|
||||||
|
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||||
|
auto &refs = net_join_db.at(mapped_sig);
|
||||||
|
for (auto &ref : refs)
|
||||||
|
if (ref.second)
|
||||||
|
*f << stringf(" %s\n", ref.first.c_str());
|
||||||
|
*f << stringf(" )");
|
||||||
|
if (attr_properties && raw_sig.wire != NULL)
|
||||||
|
for (auto &p : raw_sig.wire->attributes)
|
||||||
|
add_prop(p.first, p.second);
|
||||||
|
*f << stringf("\n )\n");
|
||||||
}
|
}
|
||||||
*f << stringf(" ))\n");
|
|
||||||
}
|
}
|
||||||
*f << stringf(" )\n");
|
*f << stringf(" )\n");
|
||||||
*f << stringf(" )\n");
|
*f << stringf(" )\n");
|
||||||
|
|
|
@ -304,7 +304,11 @@ class SmtIo:
|
||||||
|
|
||||||
def p_open(self):
|
def p_open(self):
|
||||||
assert self.p is None
|
assert self.p is None
|
||||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
try:
|
||||||
|
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True)
|
||||||
|
sys.exit(1)
|
||||||
running_solvers[self.p_index] = self.p
|
running_solvers[self.p_index] = self.p
|
||||||
self.p_running = True
|
self.p_running = True
|
||||||
self.p_next = None
|
self.p_next = None
|
||||||
|
|
|
@ -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)
|
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 = new RTLIL::Module;
|
||||||
module->name = module_name;
|
module->name = module_name;
|
||||||
|
@ -255,7 +255,7 @@ end_of_header:
|
||||||
else
|
else
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
||||||
RTLIL::Wire* n0 = module->wire("\\__0__");
|
RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
|
||||||
if (n0)
|
if (n0)
|
||||||
module->connect(n0, State::S0);
|
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()))
|
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);
|
log_error("Line %u has invalid symbol position!\n", line_count);
|
||||||
|
|
||||||
|
RTLIL::IdString escaped_s = stringf("\\%s", s.c_str());
|
||||||
RTLIL::Wire* wire;
|
RTLIL::Wire* wire;
|
||||||
if (c == 'i') wire = inputs[l1];
|
if (c == 'i') wire = inputs[l1];
|
||||||
else if (c == 'l') wire = latches[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 if (c == 'b') wire = bad_properties[l1];
|
||||||
else log_abort();
|
else log_abort();
|
||||||
|
|
||||||
module->rename(wire, stringf("\\%s", s.c_str()));
|
module->rename(wire, escaped_s);
|
||||||
}
|
}
|
||||||
else if (c == 'j' || c == 'f') {
|
else if (c == 'j' || c == 'f') {
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -293,6 +303,7 @@ end_of_header:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
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
|
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);
|
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 unsigned variable = literal >> 1;
|
||||||
const bool invert = 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);
|
RTLIL::Wire *wire = module->wire(wire_name);
|
||||||
if (wire) return wire;
|
if (wire) return wire;
|
||||||
log_debug2("Creating %s\n", wire_name.c_str());
|
log_debug2("Creating %s\n", wire_name.c_str());
|
||||||
wire = module->addWire(wire_name);
|
wire = module->addWire(wire_name);
|
||||||
wire->port_input = wire->port_output = false;
|
wire->port_input = wire->port_output = false;
|
||||||
if (!invert) return wire;
|
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);
|
RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
|
||||||
if (wire_inv) {
|
if (wire_inv) {
|
||||||
if (module->cell(wire_inv_name)) return wire;
|
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());
|
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;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
|
void AigerReader::parse_xaiger()
|
||||||
{
|
{
|
||||||
std::string header;
|
std::string header;
|
||||||
f >> header;
|
f >> header;
|
||||||
|
@ -372,108 +383,115 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
|
||||||
else
|
else
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
||||||
RTLIL::Wire* n0 = module->wire("\\__0__");
|
RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
|
||||||
if (n0)
|
if (n0)
|
||||||
module->connect(n0, State::S0);
|
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();
|
||||||
|
|
||||||
// Parse footer (symbol table, comments, etc.)
|
// Parse footer (symbol table, comments, etc.)
|
||||||
std::string s;
|
std::string s;
|
||||||
bool comment_seen = false;
|
for (int c = f.get(); c != EOF; c = f.get()) {
|
||||||
for (int c = f.peek(); c != EOF; c = f.peek()) {
|
// XAIGER extensions
|
||||||
if (comment_seen || c == 'c') {
|
if (c == 'm') {
|
||||||
if (!comment_seen) {
|
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||||
f.ignore(1);
|
uint32_t lutNum = parse_xaiger_literal(f);
|
||||||
c = f.peek();
|
uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||||
comment_seen = true;
|
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
|
||||||
}
|
ConstEvalAig ce(module);
|
||||||
if (c == '\n')
|
for (unsigned i = 0; i < lutNum; ++i) {
|
||||||
break;
|
uint32_t rootNodeID = parse_xaiger_literal(f);
|
||||||
f.ignore(1);
|
uint32_t cutLeavesM = parse_xaiger_literal(f);
|
||||||
// XAIGER extensions
|
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
|
||||||
if (c == 'm') {
|
RTLIL::Wire *output_sig = module->wire(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
log_assert(output_sig);
|
||||||
uint32_t lutNum = parse_xaiger_literal(f);
|
uint32_t nodeID;
|
||||||
uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
RTLIL::SigSpec input_sig;
|
||||||
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
|
for (unsigned j = 0; j < cutLeavesM; ++j) {
|
||||||
ConstEvalAig ce(module);
|
nodeID = parse_xaiger_literal(f);
|
||||||
for (unsigned i = 0; i < lutNum; ++i) {
|
log_debug2("\t%u\n", nodeID);
|
||||||
uint32_t rootNodeID = parse_xaiger_literal(f);
|
if (nodeID == 0) {
|
||||||
uint32_t cutLeavesM = parse_xaiger_literal(f);
|
log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM);
|
||||||
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
|
continue;
|
||||||
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())
|
RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID));
|
||||||
ce.clear();
|
log_assert(wire);
|
||||||
ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
|
input_sig.append(wire);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
// Reverse input order as fastest input is returned first
|
||||||
else if (c == 'r') {
|
input_sig.reverse();
|
||||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
|
||||||
flopNum = parse_xaiger_literal(f);
|
ce.clear();
|
||||||
log_debug("flopNum: %u\n", flopNum);
|
ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
|
||||||
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
|
RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << GetSize(input_sig));
|
||||||
f.ignore(flopNum * sizeof(uint32_t));
|
for (int j = 0; j < GetSize(lut_mask); ++j) {
|
||||||
}
|
int gray = j ^ (j >> 1);
|
||||||
else if (c == 'n') {
|
ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)});
|
||||||
parse_xaiger_literal(f);
|
RTLIL::SigBit o(output_sig);
|
||||||
f >> s;
|
bool success YS_ATTRIBUTE(unused) = ce.eval(o);
|
||||||
log_debug("n: '%s'\n", s.c_str());
|
log_assert(success);
|
||||||
}
|
log_assert(o.wire == nullptr);
|
||||||
else if (c == 'h') {
|
lut_mask[gray] = o.data;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||||
else if (c == 'a' || c == 'i' || c == 'o') {
|
log_assert(output_cell);
|
||||||
uint32_t dataSize = parse_xaiger_literal(f);
|
module->remove(output_cell);
|
||||||
f.ignore(dataSize);
|
module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask));
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (c == 'r') {
|
||||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
uint32_t dataSize = parse_xaiger_literal(f);
|
||||||
|
flopNum = parse_xaiger_literal(f);
|
||||||
|
log_debug("flopNum = %u\n", flopNum);
|
||||||
|
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
|
||||||
|
mergeability.reserve(flopNum);
|
||||||
|
for (unsigned i = 0; i < flopNum; i++)
|
||||||
|
mergeability.emplace_back(parse_xaiger_literal(f));
|
||||||
|
}
|
||||||
|
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++) {
|
||||||
|
uint32_t boxInputs = parse_xaiger_literal(f);
|
||||||
|
uint32_t boxOutputs = parse_xaiger_literal(f);
|
||||||
|
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), stringf("$__boxid%u", boxUniqueId));
|
||||||
|
cell->setPort("\\i", SigSpec(State::S0, boxInputs));
|
||||||
|
cell->setPort("\\o", SigSpec(State::S0, boxOutputs));
|
||||||
|
cell->attributes["\\abc9_box_seq"] = oldBoxNum;
|
||||||
|
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();
|
post_process();
|
||||||
|
@ -487,13 +505,15 @@ void AigerReader::parse_aiger_ascii()
|
||||||
unsigned l1, l2, l3;
|
unsigned l1, l2, l3;
|
||||||
|
|
||||||
// Parse inputs
|
// Parse inputs
|
||||||
|
int digits = ceil(log10(I));
|
||||||
for (unsigned i = 1; i <= I; ++i, ++line_count) {
|
for (unsigned i = 1; i <= I; ++i, ++line_count) {
|
||||||
if (!(f >> l1))
|
if (!(f >> l1))
|
||||||
log_error("Line %u cannot be interpreted as an input!\n", line_count);
|
log_error("Line %u cannot be interpreted as an input!\n", line_count);
|
||||||
log_debug2("%d is an input\n", l1);
|
log_debug2("%d is an input\n", l1);
|
||||||
log_assert(!(l1 & 1)); // Inputs can't be inverted
|
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;
|
wire->port_input = true;
|
||||||
|
module->connect(createWireIfNotExists(module, l1), wire);
|
||||||
inputs.push_back(wire);
|
inputs.push_back(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,12 +527,14 @@ void AigerReader::parse_aiger_ascii()
|
||||||
clk_wire->port_input = true;
|
clk_wire->port_input = true;
|
||||||
clk_wire->port_output = false;
|
clk_wire->port_output = false;
|
||||||
}
|
}
|
||||||
|
digits = ceil(log10(L));
|
||||||
for (unsigned i = 0; i < L; ++i, ++line_count) {
|
for (unsigned i = 0; i < L; ++i, ++line_count) {
|
||||||
if (!(f >> l1 >> l2))
|
if (!(f >> l1 >> l2))
|
||||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||||
log_debug2("%d %d is a latch\n", l1, l2);
|
log_debug2("%d %d is a latch\n", l1, l2);
|
||||||
log_assert(!(l1 & 1));
|
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);
|
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||||
|
|
||||||
if (clk_wire)
|
if (clk_wire)
|
||||||
|
@ -543,25 +565,18 @@ void AigerReader::parse_aiger_ascii()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse outputs
|
// Parse outputs
|
||||||
|
digits = ceil(log10(O));
|
||||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||||
if (!(f >> l1))
|
if (!(f >> l1))
|
||||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||||
|
|
||||||
log_debug2("%d is an output\n", l1);
|
log_debug2("%d is an output\n", l1);
|
||||||
const unsigned variable = l1 >> 1;
|
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||||
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;
|
|
||||||
}
|
|
||||||
wire->port_output = true;
|
wire->port_output = true;
|
||||||
|
module->connect(wire, createWireIfNotExists(module, l1));
|
||||||
outputs.push_back(wire);
|
outputs.push_back(wire);
|
||||||
}
|
}
|
||||||
|
//std::getline(f, line); // Ignore up to start of next line
|
||||||
|
|
||||||
// Parse bad properties
|
// Parse bad properties
|
||||||
for (unsigned i = 0; i < B; ++i, ++line_count) {
|
for (unsigned i = 0; i < B; ++i, ++line_count) {
|
||||||
|
@ -573,6 +588,8 @@ void AigerReader::parse_aiger_ascii()
|
||||||
wire->port_output = true;
|
wire->port_output = true;
|
||||||
bad_properties.push_back(wire);
|
bad_properties.push_back(wire);
|
||||||
}
|
}
|
||||||
|
//if (B > 0)
|
||||||
|
// std::getline(f, line); // Ignore up to start of next line
|
||||||
|
|
||||||
// TODO: Parse invariant constraints
|
// TODO: Parse invariant constraints
|
||||||
for (unsigned i = 0; i < C; ++i, ++line_count)
|
for (unsigned i = 0; i < C; ++i, ++line_count)
|
||||||
|
@ -596,7 +613,7 @@ void AigerReader::parse_aiger_ascii()
|
||||||
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
||||||
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
||||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
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
|
std::getline(f, line); // Ignore up to start of next line
|
||||||
}
|
}
|
||||||
|
@ -616,11 +633,12 @@ void AigerReader::parse_aiger_binary()
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|
||||||
// Parse inputs
|
// Parse inputs
|
||||||
|
int digits = ceil(log10(I));
|
||||||
for (unsigned i = 1; i <= I; ++i) {
|
for (unsigned i = 1; i <= I; ++i) {
|
||||||
log_debug2("%d is an input\n", 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;
|
wire->port_input = true;
|
||||||
log_assert(!wire->port_output);
|
module->connect(createWireIfNotExists(module, i << 1), wire);
|
||||||
inputs.push_back(wire);
|
inputs.push_back(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,12 +652,14 @@ void AigerReader::parse_aiger_binary()
|
||||||
clk_wire->port_input = true;
|
clk_wire->port_input = true;
|
||||||
clk_wire->port_output = false;
|
clk_wire->port_output = false;
|
||||||
}
|
}
|
||||||
|
digits = ceil(log10(L));
|
||||||
l1 = (I+1) * 2;
|
l1 = (I+1) * 2;
|
||||||
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
|
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
|
||||||
if (!(f >> l2))
|
if (!(f >> l2))
|
||||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||||
log_debug("%d %d is a latch\n", l1, l2);
|
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);
|
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||||
|
|
||||||
if (clk_wire)
|
if (clk_wire)
|
||||||
|
@ -670,23 +690,15 @@ void AigerReader::parse_aiger_binary()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse outputs
|
// Parse outputs
|
||||||
|
digits = ceil(log10(O));
|
||||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||||
if (!(f >> l1))
|
if (!(f >> l1))
|
||||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||||
|
|
||||||
log_debug2("%d is an output\n", l1);
|
log_debug2("%d is an output\n", l1);
|
||||||
const unsigned variable = l1 >> 1;
|
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||||
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;
|
|
||||||
}
|
|
||||||
wire->port_output = true;
|
wire->port_output = true;
|
||||||
|
module->connect(wire, createWireIfNotExists(module, l1));
|
||||||
outputs.push_back(wire);
|
outputs.push_back(wire);
|
||||||
}
|
}
|
||||||
std::getline(f, line); // Ignore up to start of next line
|
std::getline(f, line); // Ignore up to start of next line
|
||||||
|
@ -727,88 +739,49 @@ void AigerReader::parse_aiger_binary()
|
||||||
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
||||||
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
||||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
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()
|
void AigerReader::post_process()
|
||||||
{
|
{
|
||||||
pool<IdString> seen_boxes;
|
|
||||||
unsigned ci_count = 0, co_count = 0;
|
unsigned ci_count = 0, co_count = 0;
|
||||||
for (auto cell : boxes) {
|
for (auto cell : boxes) {
|
||||||
RTLIL::Module* box_module = design->module(cell->type);
|
for (auto &bit : cell->connections_.at("\\i")) {
|
||||||
log_assert(box_module);
|
log_assert(bit == State::S0);
|
||||||
|
log_assert(co_count < outputs.size());
|
||||||
if (seen_boxes.insert(cell->type).second) {
|
bit = outputs[co_count++];
|
||||||
auto it = box_module->attributes.find("\\abc9_carry");
|
log_assert(bit.wire && GetSize(bit.wire) == 1);
|
||||||
if (it != box_module->attributes.end()) {
|
log_assert(bit.wire->port_output);
|
||||||
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
|
bit.wire->port_output = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
ports.push_back(carry_in->name);
|
|
||||||
carry_in->port_id = ports.size();
|
|
||||||
ports.push_back(carry_out->name);
|
|
||||||
carry_out->port_id = ports.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for (auto &bit : cell->connections_.at("\\o")) {
|
||||||
// NB: Assume box_module->ports are sorted alphabetically
|
log_assert(bit == State::S0);
|
||||||
// (as RTLIL::Module::fixup_ports() would do)
|
log_assert((piNum + ci_count) < inputs.size());
|
||||||
for (auto port_name : box_module->ports) {
|
bit = inputs[piNum + ci_count++];
|
||||||
RTLIL::Wire* port = box_module->wire(port_name);
|
log_assert(bit.wire && GetSize(bit.wire) == 1);
|
||||||
log_assert(port);
|
log_assert(bit.wire->port_input);
|
||||||
RTLIL::SigSpec rhs;
|
bit.wire->port_input = false;
|
||||||
for (int i = 0; i < GetSize(port); i++) {
|
|
||||||
RTLIL::Wire* wire = nullptr;
|
|
||||||
if (port->port_input) {
|
|
||||||
log_assert(co_count < outputs.size());
|
|
||||||
wire = outputs[co_count++];
|
|
||||||
log_assert(wire);
|
|
||||||
log_assert(wire->port_output);
|
|
||||||
wire->port_output = false;
|
|
||||||
}
|
|
||||||
if (port->port_output) {
|
|
||||||
log_assert((piNum + ci_count) < inputs.size());
|
|
||||||
wire = inputs[piNum + ci_count++];
|
|
||||||
log_assert(wire);
|
|
||||||
log_assert(wire->port_input);
|
|
||||||
wire->port_input = false;
|
|
||||||
}
|
|
||||||
rhs.append(wire);
|
|
||||||
}
|
|
||||||
|
|
||||||
cell->setPort(port_name, rhs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < flopNum; i++) {
|
||||||
|
RTLIL::Wire *d = outputs[outputs.size() - flopNum + i];
|
||||||
|
log_assert(d);
|
||||||
|
log_assert(d->port_output);
|
||||||
|
d->port_output = false;
|
||||||
|
|
||||||
|
RTLIL::Wire *q = inputs[piNum - flopNum + i];
|
||||||
|
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);
|
||||||
|
ff->attributes["\\abc9_mergeability"] = mergeability[i];
|
||||||
|
}
|
||||||
|
|
||||||
dict<RTLIL::IdString, int> wideports_cache;
|
dict<RTLIL::IdString, int> wideports_cache;
|
||||||
|
|
||||||
if (!map_filename.empty()) {
|
if (!map_filename.empty()) {
|
||||||
|
@ -835,6 +808,7 @@ void AigerReader::post_process()
|
||||||
wire->port_input = false;
|
wire->port_input = false;
|
||||||
module->connect(wire, existing);
|
module->connect(wire, existing);
|
||||||
}
|
}
|
||||||
|
log_debug(" -> %s\n", log_id(escaped_s));
|
||||||
}
|
}
|
||||||
else if (index > 0) {
|
else if (index > 0) {
|
||||||
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||||
|
@ -848,18 +822,14 @@ void AigerReader::post_process()
|
||||||
module->connect(wire, existing);
|
module->connect(wire, existing);
|
||||||
wire->port_input = false;
|
wire->port_input = false;
|
||||||
}
|
}
|
||||||
|
log_debug(" -> %s\n", log_id(indexed_name));
|
||||||
}
|
}
|
||||||
log_debug(" -> %s\n", log_id(wire));
|
|
||||||
}
|
}
|
||||||
else if (type == "output") {
|
else if (type == "output") {
|
||||||
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
|
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
|
||||||
RTLIL::Wire* wire = outputs[variable + co_count];
|
RTLIL::Wire* wire = outputs[variable + co_count];
|
||||||
log_assert(wire);
|
log_assert(wire);
|
||||||
log_assert(wire->port_output);
|
log_assert(wire->port_output);
|
||||||
if (escaped_s == "$__dummy__") {
|
|
||||||
wire->port_output = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
log_debug("Renaming output %s", log_id(wire));
|
log_debug("Renaming output %s", log_id(wire));
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
@ -868,70 +838,40 @@ void AigerReader::post_process()
|
||||||
// simply connect the latter to the former
|
// simply connect the latter to the former
|
||||||
RTLIL::Wire* existing = module->wire(escaped_s);
|
RTLIL::Wire* existing = module->wire(escaped_s);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
if (escaped_s.ends_with("$inout.out")) {
|
module->rename(wire, escaped_s);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
wire->port_output = false;
|
wire->port_output = false;
|
||||||
|
existing->port_output = true;
|
||||||
module->connect(wire, existing);
|
module->connect(wire, existing);
|
||||||
wire = existing;
|
wire = existing;
|
||||||
}
|
}
|
||||||
|
log_debug(" -> %s\n", log_id(escaped_s));
|
||||||
}
|
}
|
||||||
else if (index > 0) {
|
else if (index > 0) {
|
||||||
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||||
RTLIL::Wire* existing = module->wire(indexed_name);
|
RTLIL::Wire* existing = module->wire(indexed_name);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
if (escaped_s.ends_with("$inout.out")) {
|
module->rename(wire, indexed_name);
|
||||||
wire->port_output = false;
|
if (wideports)
|
||||||
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index));
|
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
module->connect(wire, existing);
|
|
||||||
wire->port_output = false;
|
wire->port_output = false;
|
||||||
|
existing->port_output = true;
|
||||||
|
module->connect(wire, existing);
|
||||||
}
|
}
|
||||||
|
log_debug(" -> %s\n", log_id(indexed_name));
|
||||||
}
|
}
|
||||||
log_debug(" -> %s\n", log_id(wire));
|
int init;
|
||||||
|
mf >> init;
|
||||||
|
if (init < 2)
|
||||||
|
wire->attributes["\\init"] = init;
|
||||||
}
|
}
|
||||||
else if (type == "box") {
|
else if (type == "box") {
|
||||||
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
|
RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
|
||||||
if (cell) { // ABC could have optimised this box away
|
if (cell) // ABC could have optimised this box away
|
||||||
module->rename(cell, escaped_s);
|
module->rename(cell, escaped_s);
|
||||||
for (const auto &i : cell->connections()) {
|
|
||||||
RTLIL::IdString port_name = i.first;
|
|
||||||
RTLIL::SigSpec rhs = i.second;
|
|
||||||
int index = 0;
|
|
||||||
for (auto bit : rhs.bits()) {
|
|
||||||
RTLIL::Wire* wire = bit.wire;
|
|
||||||
RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name)));
|
|
||||||
if (index == 0)
|
|
||||||
module->rename(wire, escaped_s);
|
|
||||||
else if (index > 0) {
|
|
||||||
module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index));
|
|
||||||
if (wideports)
|
|
||||||
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log_error("Symbol type '%s' not recognised.\n", type.c_str());
|
log_error("Symbol type '%s' not recognised.\n", type.c_str());
|
||||||
|
@ -968,15 +908,10 @@ void AigerReader::post_process()
|
||||||
if (other_wire) {
|
if (other_wire) {
|
||||||
other_wire->port_input = false;
|
other_wire->port_input = false;
|
||||||
other_wire->port_output = false;
|
other_wire->port_output = false;
|
||||||
}
|
if (wire->port_input)
|
||||||
if (wire->port_input) {
|
|
||||||
if (other_wire)
|
|
||||||
module->connect(other_wire, SigSpec(wire, i));
|
module->connect(other_wire, SigSpec(wire, i));
|
||||||
}
|
else
|
||||||
else {
|
module->connect(SigSpec(wire, i), other_wire);
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -997,9 +932,9 @@ void AigerReader::post_process()
|
||||||
if (cell->type != "$lut") continue;
|
if (cell->type != "$lut") continue;
|
||||||
auto y_port = cell->getPort("\\Y").as_bit();
|
auto y_port = cell->getPort("\\Y").as_bit();
|
||||||
if (y_port.wire->width == 1)
|
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
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,28 +949,31 @@ struct AigerFrontend : public Frontend {
|
||||||
log("Load module from an AIGER file into the current design.\n");
|
log("Load module from an AIGER file into the current design.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -module_name <module_name>\n");
|
log(" -module_name <module_name>\n");
|
||||||
log(" Name of module to be created (default: <filename>)\n");
|
log(" name of module to be created (default: <filename>)\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -clk_name <wire_name>\n");
|
log(" -clk_name <wire_name>\n");
|
||||||
log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
|
log(" if specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
|
||||||
log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n");
|
log(" clocked by wire of this name. otherwise, $_FF_ cells will be used\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -map <filename>\n");
|
log(" -map <filename>\n");
|
||||||
log(" read file with port and latch symbols\n");
|
log(" read file with port and latch symbols\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -wideports\n");
|
log(" -wideports\n");
|
||||||
log(" Merge ports that match the pattern 'name[int]' into a single\n");
|
log(" merge ports that match the pattern 'name[int]' into a single\n");
|
||||||
log(" multi-bit port 'name'.\n");
|
log(" multi-bit port 'name'\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -xaiger\n");
|
||||||
|
log(" read XAIGER extensions\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
log_header(design, "Executing AIGER frontend.\n");
|
log_header(design, "Executing AIGER frontend.\n");
|
||||||
|
|
||||||
RTLIL::IdString clk_name = "\\clk";
|
RTLIL::IdString clk_name;
|
||||||
RTLIL::IdString module_name;
|
RTLIL::IdString module_name;
|
||||||
std::string map_filename;
|
std::string map_filename;
|
||||||
bool wideports = false;
|
bool wideports = false, xaiger = false;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
@ -1056,6 +994,10 @@ struct AigerFrontend : public Frontend {
|
||||||
wideports = true;
|
wideports = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-xaiger") {
|
||||||
|
xaiger = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx, true);
|
extra_args(f, filename, args, argidx, true);
|
||||||
|
@ -1075,7 +1017,10 @@ struct AigerFrontend : public Frontend {
|
||||||
}
|
}
|
||||||
|
|
||||||
AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports);
|
AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports);
|
||||||
reader.parse_aiger();
|
if (xaiger)
|
||||||
|
reader.parse_xaiger();
|
||||||
|
else
|
||||||
|
reader.parse_aiger();
|
||||||
}
|
}
|
||||||
} AigerFrontend;
|
} AigerFrontend;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ struct AigerReader
|
||||||
RTLIL::Module *module;
|
RTLIL::Module *module;
|
||||||
std::string map_filename;
|
std::string map_filename;
|
||||||
bool wideports;
|
bool wideports;
|
||||||
|
const int aiger_autoidx;
|
||||||
|
|
||||||
unsigned M, I, L, O, A;
|
unsigned M, I, L, O, A;
|
||||||
unsigned B, C, J, F; // Optional in AIGER 1.9
|
unsigned B, C, J, F; // Optional in AIGER 1.9
|
||||||
|
@ -44,13 +45,16 @@ struct AigerReader
|
||||||
std::vector<RTLIL::Wire*> outputs;
|
std::vector<RTLIL::Wire*> outputs;
|
||||||
std::vector<RTLIL::Wire*> bad_properties;
|
std::vector<RTLIL::Wire*> bad_properties;
|
||||||
std::vector<RTLIL::Cell*> boxes;
|
std::vector<RTLIL::Cell*> boxes;
|
||||||
|
std::vector<int> mergeability;
|
||||||
|
|
||||||
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);
|
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_aiger();
|
||||||
void parse_xaiger(const dict<int,IdString> &box_lookup);
|
void parse_xaiger();
|
||||||
void parse_aiger_ascii();
|
void parse_aiger_ascii();
|
||||||
void parse_aiger_binary();
|
void parse_aiger_binary();
|
||||||
void post_process();
|
void post_process();
|
||||||
|
|
||||||
|
RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal);
|
||||||
};
|
};
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
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 = new AstNode(AST_LOCALPARAM, varbuf);
|
||||||
varbuf->str = init_ast->children[0]->str;
|
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];
|
AstNode *backup_scope_varbuf = current_scope[varbuf->str];
|
||||||
current_scope[varbuf->str] = varbuf;
|
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);
|
current_ast_mod->children.push_back(p);
|
||||||
str = p->str;
|
str = p->str;
|
||||||
id2ast = p;
|
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);
|
free($1);
|
||||||
} |
|
} |
|
||||||
sigspec '[' TOK_INT ']' {
|
sigspec '[' TOK_INT ']' {
|
||||||
|
if ($3 >= $1->size() || $3 < 0)
|
||||||
|
rtlil_frontend_ilang_yyerror("bit index out of range");
|
||||||
$$ = new RTLIL::SigSpec($1->extract($3));
|
$$ = new RTLIL::SigSpec($1->extract($3));
|
||||||
delete $1;
|
delete $1;
|
||||||
} |
|
} |
|
||||||
sigspec '[' TOK_INT ':' TOK_INT ']' {
|
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));
|
$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
|
||||||
delete $1;
|
delete $1;
|
||||||
} |
|
} |
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
|
||||||
|
|
||||||
This directory contains Verific bindings for Yosys.
|
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
|
Verific Features that should be enabled in your Verific library
|
||||||
|
|
|
@ -539,6 +539,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inst->Type() == OPER_REDUCE_NAND) {
|
||||||
|
Wire *tmp = module->addWire(NEW_ID);
|
||||||
|
cell = module->addReduceAnd(inst_name, IN, tmp, SIGNED);
|
||||||
|
module->addNot(NEW_ID, tmp, net_map_at(inst->GetOutput()));
|
||||||
|
import_attributes(cell->attributes, inst);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (inst->Type() == OPER_REDUCE_OR) {
|
if (inst->Type() == OPER_REDUCE_OR) {
|
||||||
cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
|
cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
|
||||||
import_attributes(cell->attributes, inst);
|
import_attributes(cell->attributes, inst);
|
||||||
|
@ -1891,6 +1899,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
|
||||||
if (!verific_error_msg.empty())
|
if (!verific_error_msg.empty())
|
||||||
log_error("%s\n", verific_error_msg.c_str());
|
log_error("%s\n", verific_error_msg.c_str());
|
||||||
|
|
||||||
|
for (auto nl : nl_todo)
|
||||||
|
nl->ChangePortBusStructures(1 /* hierarchical */);
|
||||||
|
|
||||||
VerificExtNets worker;
|
VerificExtNets worker;
|
||||||
for (auto nl : nl_todo)
|
for (auto nl : nl_todo)
|
||||||
worker.run(nl);
|
worker.run(nl);
|
||||||
|
@ -2065,7 +2076,12 @@ struct VerificPass : public Pass {
|
||||||
log(" -d <dump_file>\n");
|
log(" -d <dump_file>\n");
|
||||||
log(" Dump the Verific netlist as a verilog file.\n");
|
log(" Dump the Verific netlist as a verilog file.\n");
|
||||||
log("\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");
|
log("\n");
|
||||||
}
|
}
|
||||||
#ifdef YOSYS_ENABLE_VERIFIC
|
#ifdef YOSYS_ENABLE_VERIFIC
|
||||||
|
@ -2074,7 +2090,13 @@ struct VerificPass : public Pass {
|
||||||
static bool set_verific_global_flags = true;
|
static bool set_verific_global_flags = true;
|
||||||
|
|
||||||
if (check_noverific_env())
|
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");
|
log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n");
|
||||||
|
|
||||||
|
@ -2397,7 +2419,7 @@ struct VerificPass : public Pass {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (argidx == GetSize(args))
|
if (argidx == GetSize(args))
|
||||||
log_cmd_error("No top module specified.\n");
|
cmd_error(args, argidx, "No top module specified.\n");
|
||||||
|
|
||||||
Array veri_modules, vhdl_units;
|
Array veri_modules, vhdl_units;
|
||||||
for (; argidx < GetSize(args); argidx++)
|
for (; argidx < GetSize(args); argidx++)
|
||||||
|
@ -2459,6 +2481,9 @@ struct VerificPass : public Pass {
|
||||||
worker.run(nl);
|
worker.run(nl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto nl : nl_todo)
|
||||||
|
nl->ChangePortBusStructures(1 /* hierarchical */);
|
||||||
|
|
||||||
if (!dumpfile.empty()) {
|
if (!dumpfile.empty()) {
|
||||||
VeriWrite veri_writer;
|
VeriWrite veri_writer;
|
||||||
veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign());
|
veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign());
|
||||||
|
@ -2484,7 +2509,7 @@ struct VerificPass : public Pass {
|
||||||
goto check_error;
|
goto check_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_cmd_error("Missing or unsupported mode parameter.\n");
|
cmd_error(args, argidx, "Missing or unsupported mode parameter.\n");
|
||||||
|
|
||||||
check_error:
|
check_error:
|
||||||
if (!verific_error_msg.empty())
|
if (!verific_error_msg.empty())
|
||||||
|
@ -2493,7 +2518,13 @@ struct VerificPass : public Pass {
|
||||||
}
|
}
|
||||||
#else /* YOSYS_ENABLE_VERIFIC */
|
#else /* YOSYS_ENABLE_VERIFIC */
|
||||||
void execute(std::vector<std::string>, RTLIL::Design *) YS_OVERRIDE {
|
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
|
#endif
|
||||||
} VerificPass;
|
} VerificPass;
|
||||||
|
@ -2551,14 +2582,14 @@ struct ReadPass : public Pass {
|
||||||
static bool use_verific = verific_available;
|
static bool use_verific = verific_available;
|
||||||
|
|
||||||
if (args.size() < 2 || args[1][0] != '-')
|
if (args.size() < 2 || args[1][0] != '-')
|
||||||
log_cmd_error("Missing mode parameter.\n");
|
cmd_error(args, 1, "Missing mode parameter.\n");
|
||||||
|
|
||||||
if (args[1] == "-verific" || args[1] == "-noverific") {
|
if (args[1] == "-verific" || args[1] == "-noverific") {
|
||||||
if (args.size() != 2)
|
if (args.size() != 2)
|
||||||
log_cmd_error("Additional arguments to -verific/-noverific.\n");
|
cmd_error(args, 1, "Additional arguments to -verific/-noverific.\n");
|
||||||
if (args[1] == "-verific") {
|
if (args[1] == "-verific") {
|
||||||
if (!verific_available)
|
if (!verific_available)
|
||||||
log_cmd_error("This version of Yosys is built without Verific support.\n");
|
cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
|
||||||
use_verific = true;
|
use_verific = true;
|
||||||
} else {
|
} else {
|
||||||
use_verific = false;
|
use_verific = false;
|
||||||
|
@ -2567,7 +2598,7 @@ struct ReadPass : public Pass {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.size() < 3)
|
if (args.size() < 3)
|
||||||
log_cmd_error("Missing file name parameter.\n");
|
cmd_error(args, 3, "Missing file name parameter.\n");
|
||||||
|
|
||||||
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
|
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
|
||||||
if (use_verific) {
|
if (use_verific) {
|
||||||
|
@ -2599,7 +2630,7 @@ struct ReadPass : public Pass {
|
||||||
args[0] = "verific";
|
args[0] = "verific";
|
||||||
Pass::call(design, args);
|
Pass::call(design, args);
|
||||||
} else {
|
} else {
|
||||||
log_cmd_error("This version of Yosys is built without Verific support.\n");
|
cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2646,7 +2677,7 @@ struct ReadPass : public Pass {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_cmd_error("Missing or unsupported mode parameter.\n");
|
cmd_error(args, 1, "Missing or unsupported mode parameter.\n");
|
||||||
}
|
}
|
||||||
} ReadPass;
|
} ReadPass;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
*
|
*
|
||||||
* Ad-hoc implementation of a Verilog preprocessor. The directives `define,
|
* Ad-hoc implementation of a Verilog preprocessor. The directives `define,
|
||||||
* `include, `ifdef, `ifndef, `else and `endif are handled here. All other
|
* `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
|
* A simple lexer for Verilog code. Non-preprocessor compiler directives are
|
||||||
* handled here. The preprocessor stuff is handled in preproc.cc. Everything
|
* 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).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -2242,7 +2242,7 @@ gen_stmt:
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
ast_stack.push_back(node);
|
ast_stack.push_back(node);
|
||||||
} opt_arg_list ';'{
|
} opt_arg_list ';'{
|
||||||
ast_stack.pop_back();
|
ast_stack.pop_back();
|
||||||
};
|
};
|
||||||
|
|
||||||
gen_stmt_block:
|
gen_stmt_block:
|
||||||
|
@ -2413,19 +2413,19 @@ basic_expr:
|
||||||
append_attr($$, $2);
|
append_attr($$, $2);
|
||||||
} |
|
} |
|
||||||
basic_expr OP_SHL attr basic_expr {
|
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);
|
append_attr($$, $3);
|
||||||
} |
|
} |
|
||||||
basic_expr OP_SHR attr basic_expr {
|
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);
|
append_attr($$, $3);
|
||||||
} |
|
} |
|
||||||
basic_expr OP_SSHL attr basic_expr {
|
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);
|
append_attr($$, $3);
|
||||||
} |
|
} |
|
||||||
basic_expr OP_SSHR attr basic_expr {
|
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);
|
append_attr($$, $3);
|
||||||
} |
|
} |
|
||||||
basic_expr '<' attr basic_expr {
|
basic_expr '<' attr basic_expr {
|
||||||
|
|
|
@ -295,6 +295,9 @@ int main(int argc, char **argv)
|
||||||
printf(" -E <depsfile>\n");
|
printf(" -E <depsfile>\n");
|
||||||
printf(" write a Makefile dependencies file with in- and output file names\n");
|
printf(" write a Makefile dependencies file with in- and output file names\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
printf(" -x <feature>\n");
|
||||||
|
printf(" do not print warnings for the specified experimental feature\n");
|
||||||
|
printf("\n");
|
||||||
printf(" -g\n");
|
printf(" -g\n");
|
||||||
printf(" globally enable debug log messages\n");
|
printf(" globally enable debug log messages\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
@ -317,8 +320,14 @@ int main(int argc, char **argv)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")))
|
||||||
|
{
|
||||||
|
printf("%s\n", yosys_version_str);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
|
while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1)
|
||||||
{
|
{
|
||||||
switch (opt)
|
switch (opt)
|
||||||
{
|
{
|
||||||
|
@ -449,6 +458,9 @@ int main(int argc, char **argv)
|
||||||
case 'E':
|
case 'E':
|
||||||
depsfile = optarg;
|
depsfile = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'x':
|
||||||
|
log_experimentals_ignored.insert(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
|
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -561,39 +573,31 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
if (log_warnings_count)
|
if (log_warnings_count)
|
||||||
log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count);
|
log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count);
|
||||||
|
|
||||||
|
if (!log_experimentals.empty())
|
||||||
|
log("Warnings: %d experimental features used (not excluded with -x).\n", GetSize(log_experimentals));
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
log("End of script. Logfile hash: %s\n", hash.c_str());
|
log("End of script. Logfile hash: %s\n", hash.c_str());
|
||||||
#else
|
#else
|
||||||
std::string meminfo;
|
std::string meminfo;
|
||||||
std::string stats_divider = ", ";
|
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;
|
struct rusage ru_buffer;
|
||||||
getrusage(RUSAGE_SELF, &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(),
|
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,
|
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());
|
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
|
||||||
|
|
|
@ -42,7 +42,7 @@ std::vector<FILE*> log_files;
|
||||||
std::vector<std::ostream*> log_streams;
|
std::vector<std::ostream*> log_streams;
|
||||||
std::map<std::string, std::set<std::string>> log_hdump;
|
std::map<std::string, std::set<std::string>> log_hdump;
|
||||||
std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
|
std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
|
||||||
std::set<std::string> log_warnings;
|
std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
|
||||||
int log_warnings_count = 0;
|
int log_warnings_count = 0;
|
||||||
bool log_hdump_all = false;
|
bool log_hdump_all = false;
|
||||||
FILE *log_errfile = NULL;
|
FILE *log_errfile = NULL;
|
||||||
|
@ -377,6 +377,19 @@ void log_warning(const char *format, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void log_experimental(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
string s = vstringf(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) {
|
||||||
|
log_warning("Feature '%s' is experimental.\n", s.c_str());
|
||||||
|
log_experimentals.insert(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void log_warning_noprefix(const char *format, ...)
|
void log_warning_noprefix(const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
|
@ -50,7 +50,7 @@ extern std::vector<FILE*> log_files;
|
||||||
extern std::vector<std::ostream*> log_streams;
|
extern std::vector<std::ostream*> log_streams;
|
||||||
extern std::map<std::string, std::set<std::string>> log_hdump;
|
extern std::map<std::string, std::set<std::string>> log_hdump;
|
||||||
extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
|
extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
|
||||||
extern std::set<std::string> log_warnings;
|
extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
|
||||||
extern int log_warnings_count;
|
extern int log_warnings_count;
|
||||||
extern bool log_hdump_all;
|
extern bool log_hdump_all;
|
||||||
extern FILE *log_errfile;
|
extern FILE *log_errfile;
|
||||||
|
@ -77,6 +77,7 @@ YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noretur
|
||||||
void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||||
void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3));
|
void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3));
|
||||||
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||||
|
void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||||
|
|
||||||
// Log with filename to report a problem in a source file.
|
// Log with filename to report a problem in a source file.
|
||||||
void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
|
void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
|
||||||
|
|
|
@ -114,20 +114,35 @@ void Pass::run_register()
|
||||||
|
|
||||||
void Pass::init_register()
|
void Pass::init_register()
|
||||||
{
|
{
|
||||||
|
vector<Pass*> added_passes;
|
||||||
while (first_queued_pass) {
|
while (first_queued_pass) {
|
||||||
|
added_passes.push_back(first_queued_pass);
|
||||||
first_queued_pass->run_register();
|
first_queued_pass->run_register();
|
||||||
first_queued_pass = first_queued_pass->next_queued_pass;
|
first_queued_pass = first_queued_pass->next_queued_pass;
|
||||||
}
|
}
|
||||||
|
for (auto added_pass : added_passes)
|
||||||
|
added_pass->on_register();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pass::done_register()
|
void Pass::done_register()
|
||||||
{
|
{
|
||||||
|
for (auto &it : pass_register)
|
||||||
|
it.second->on_shutdown();
|
||||||
|
|
||||||
frontend_register.clear();
|
frontend_register.clear();
|
||||||
pass_register.clear();
|
pass_register.clear();
|
||||||
backend_register.clear();
|
backend_register.clear();
|
||||||
log_assert(first_queued_pass == NULL);
|
log_assert(first_queued_pass == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pass::on_register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pass::on_shutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Pass::~Pass()
|
Pass::~Pass()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -289,6 +304,9 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
|
||||||
if (pass_register.count(args[0]) == 0)
|
if (pass_register.count(args[0]) == 0)
|
||||||
log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
|
log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
|
||||||
|
|
||||||
|
if (pass_register[args[0]]->experimental_flag)
|
||||||
|
log_experimental("%s", args[0].c_str());
|
||||||
|
|
||||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||||
auto state = pass_register[args[0]]->pre_execute();
|
auto state = pass_register[args[0]]->pre_execute();
|
||||||
pass_register[args[0]]->execute(args, design);
|
pass_register[args[0]]->execute(args, design);
|
||||||
|
@ -809,6 +827,11 @@ struct HelpPass : public Pass {
|
||||||
log("=");
|
log("=");
|
||||||
log("\n");
|
log("\n");
|
||||||
it.second->help();
|
it.second->help();
|
||||||
|
if (it.second->experimental_flag) {
|
||||||
|
log("\n");
|
||||||
|
log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (args[1] == "-cells") {
|
else if (args[1] == "-cells") {
|
||||||
|
@ -831,6 +854,11 @@ struct HelpPass : public Pass {
|
||||||
std::ostringstream buf;
|
std::ostringstream buf;
|
||||||
log_streams.push_back(&buf);
|
log_streams.push_back(&buf);
|
||||||
it.second->help();
|
it.second->help();
|
||||||
|
if (it.second->experimental_flag) {
|
||||||
|
log("\n");
|
||||||
|
log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
log_streams.pop_back();
|
log_streams.pop_back();
|
||||||
write_tex(f, it.first, it.second->short_help, buf.str());
|
write_tex(f, it.first, it.second->short_help, buf.str());
|
||||||
}
|
}
|
||||||
|
@ -843,6 +871,11 @@ struct HelpPass : public Pass {
|
||||||
std::ostringstream buf;
|
std::ostringstream buf;
|
||||||
log_streams.push_back(&buf);
|
log_streams.push_back(&buf);
|
||||||
it.second->help();
|
it.second->help();
|
||||||
|
if (it.second->experimental_flag) {
|
||||||
|
log("\n");
|
||||||
|
log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
log_streams.pop_back();
|
log_streams.pop_back();
|
||||||
write_html(f, it.first, it.second->short_help, buf.str());
|
write_html(f, it.first, it.second->short_help, buf.str());
|
||||||
}
|
}
|
||||||
|
@ -850,6 +883,11 @@ struct HelpPass : public Pass {
|
||||||
}
|
}
|
||||||
else if (pass_register.count(args[1])) {
|
else if (pass_register.count(args[1])) {
|
||||||
pass_register.at(args[1])->help();
|
pass_register.at(args[1])->help();
|
||||||
|
if (pass_register.at(args[1])->experimental_flag) {
|
||||||
|
log("\n");
|
||||||
|
log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str());
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (cell_help_messages.cell_help.count(args[1])) {
|
else if (cell_help_messages.cell_help.count(args[1])) {
|
||||||
log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
|
log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
|
||||||
|
|
|
@ -36,6 +36,11 @@ struct Pass
|
||||||
|
|
||||||
int call_counter;
|
int call_counter;
|
||||||
int64_t runtime_ns;
|
int64_t runtime_ns;
|
||||||
|
bool experimental_flag = false;
|
||||||
|
|
||||||
|
void experimental() {
|
||||||
|
experimental_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
struct pre_post_exec_state_t {
|
struct pre_post_exec_state_t {
|
||||||
Pass *parent_pass;
|
Pass *parent_pass;
|
||||||
|
@ -62,6 +67,9 @@ struct Pass
|
||||||
virtual void run_register();
|
virtual void run_register();
|
||||||
static void init_register();
|
static void init_register();
|
||||||
static void done_register();
|
static void done_register();
|
||||||
|
|
||||||
|
virtual void on_register();
|
||||||
|
virtual void on_shutdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScriptPass : Pass
|
struct ScriptPass : Pass
|
||||||
|
|
|
@ -46,6 +46,7 @@ IdString RTLIL::ID::Y;
|
||||||
IdString RTLIL::ID::keep;
|
IdString RTLIL::ID::keep;
|
||||||
IdString RTLIL::ID::whitebox;
|
IdString RTLIL::ID::whitebox;
|
||||||
IdString RTLIL::ID::blackbox;
|
IdString RTLIL::ID::blackbox;
|
||||||
|
dict<std::string, std::string> RTLIL::constpad;
|
||||||
|
|
||||||
RTLIL::Const::Const()
|
RTLIL::Const::Const()
|
||||||
{
|
{
|
||||||
|
@ -783,6 +784,14 @@ namespace {
|
||||||
return v;
|
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)
|
void param_bits(RTLIL::IdString name, int width)
|
||||||
{
|
{
|
||||||
param(name);
|
param(name);
|
||||||
|
@ -869,13 +878,23 @@ namespace {
|
||||||
return;
|
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(A_SIGNED));
|
||||||
param_bool(ID(B_SIGNED));
|
param_bool(ID(B_SIGNED));
|
||||||
port(ID::A, param(ID(A_WIDTH)));
|
port(ID::A, param(ID(A_WIDTH)));
|
||||||
port(ID::B, param(ID(B_WIDTH)));
|
port(ID::B, param(ID(B_WIDTH)));
|
||||||
port(ID::Y, param(ID(Y_WIDTH)));
|
port(ID::Y, param(ID(Y_WIDTH)));
|
||||||
check_expected(false);
|
check_expected(/*check_matched_sign=*/false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,7 +976,7 @@ namespace {
|
||||||
port(ID::A, param(ID(A_WIDTH)));
|
port(ID::A, param(ID(A_WIDTH)));
|
||||||
port(ID::B, param(ID(B_WIDTH)));
|
port(ID::B, param(ID(B_WIDTH)));
|
||||||
port(ID::Y, param(ID(Y_WIDTH)));
|
port(ID::Y, param(ID(Y_WIDTH)));
|
||||||
check_expected(false);
|
check_expected(/*check_matched_sign=*/false);
|
||||||
return;
|
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(Or, max(sig_a.size(), sig_b.size()), ID($or))
|
||||||
DEF_METHOD(Xor, max(sig_a.size(), sig_b.size()), ID($xor))
|
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(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(Shift, sig_a.size(), ID($shift))
|
||||||
DEF_METHOD(Shiftx, sig_a.size(), ID($shiftx))
|
DEF_METHOD(Shiftx, sig_a.size(), ID($shiftx))
|
||||||
DEF_METHOD(Lt, 1, ID($lt))
|
DEF_METHOD(Lt, 1, ID($lt))
|
||||||
|
@ -1898,6 +1913,31 @@ DEF_METHOD(LogicAnd, 1, ID($logic_and))
|
||||||
DEF_METHOD(LogicOr, 1, ID($logic_or))
|
DEF_METHOD(LogicOr, 1, ID($logic_or))
|
||||||
#undef DEF_METHOD
|
#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) \
|
#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* 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); \
|
RTLIL::Cell *cell = addCell(name, _type); \
|
||||||
|
|
|
@ -377,6 +377,8 @@ namespace RTLIL
|
||||||
extern IdString blackbox;
|
extern IdString blackbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern dict<std::string, std::string> constpad;
|
||||||
|
|
||||||
static inline std::string escape_id(std::string str) {
|
static inline std::string escape_id(std::string str) {
|
||||||
if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
|
if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
|
||||||
return "\\" + str;
|
return "\\" + str;
|
||||||
|
@ -849,6 +851,8 @@ public:
|
||||||
|
|
||||||
RTLIL::SigSpec repeat(int num) const;
|
RTLIL::SigSpec repeat(int num) const;
|
||||||
|
|
||||||
|
void reverse() { inline_unpack(); std::reverse(bits_.begin(), bits_.end()); }
|
||||||
|
|
||||||
bool operator <(const RTLIL::SigSpec &other) const;
|
bool operator <(const RTLIL::SigSpec &other) const;
|
||||||
bool operator ==(const RTLIL::SigSpec &other) const;
|
bool operator ==(const RTLIL::SigSpec &other) const;
|
||||||
inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); }
|
inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); }
|
||||||
|
|
|
@ -544,6 +544,8 @@ void yosys_shutdown()
|
||||||
already_shutdown = true;
|
already_shutdown = true;
|
||||||
log_pop();
|
log_pop();
|
||||||
|
|
||||||
|
Pass::done_register();
|
||||||
|
|
||||||
delete yosys_design;
|
delete yosys_design;
|
||||||
yosys_design = NULL;
|
yosys_design = NULL;
|
||||||
|
|
||||||
|
@ -553,7 +555,6 @@ void yosys_shutdown()
|
||||||
log_errfile = NULL;
|
log_errfile = NULL;
|
||||||
log_files.clear();
|
log_files.clear();
|
||||||
|
|
||||||
Pass::done_register();
|
|
||||||
yosys_celltypes.clear();
|
yosys_celltypes.clear();
|
||||||
|
|
||||||
#ifdef YOSYS_ENABLE_TCL
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
|
|
|
@ -65,6 +65,11 @@ Verilog & Cell Type \\
|
||||||
\label{tab:CellLib_unary}
|
\label{tab:CellLib_unary}
|
||||||
\end{table}
|
\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
|
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
|
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
|
{\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.
|
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!]
|
\begin{table}[t!]
|
||||||
\hfil
|
\hfil
|
||||||
\begin{tabular}[t]{ll}
|
\begin{tabular}[t]{ll}
|
||||||
|
@ -175,10 +145,61 @@ Verilog & Cell Type \\
|
||||||
\label{tab:CellLib_binary}
|
\label{tab:CellLib_binary}
|
||||||
\end{table}
|
\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}
|
\subsection{Registers}
|
||||||
|
|
||||||
D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
|
D-type flip-flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
|
||||||
an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff
|
an input port \B{D} and an output port \B{Q}. The following parameters are available for {\tt \$dff}
|
||||||
cells:
|
cells:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
|
@ -190,13 +211,23 @@ Clock is active on the positive edge if this parameter has the value {\tt 1'b1}
|
||||||
edge if this parameter is {\tt 1'b0}.
|
edge if this parameter is {\tt 1'b0}.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
D-Type Flip-Flops with asynchronous resets are represented by {\tt \$adff} cells. As the {\tt \$dff}
|
D-type flip-flops with enable are represented by {\tt \$dffe} cells. As the {\tt \$dff}
|
||||||
|
cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{EN}
|
||||||
|
input port for the enable pin and the following parameter:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \B{EN\_POLARITY} \\
|
||||||
|
The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low
|
||||||
|
if this parameter is {\tt 1'b0}.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
D-type flip-flops with asynchronous reset are represented by {\tt \$adff} cells. As the {\tt \$dff}
|
||||||
cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST}
|
cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST}
|
||||||
input port for the reset pin and the following additional two parameters:
|
input port for the reset pin and the following additional two parameters:
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \B{ARST\_POLARITY} \\
|
\item \B{ARST\_POLARITY} \\
|
||||||
The asynchronous reset is high-active if this parameter has the value {\tt 1'b1} and low-active
|
The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low
|
||||||
if this parameter is {\tt 1'b0}.
|
if this parameter is {\tt 1'b0}.
|
||||||
|
|
||||||
\item \B{ARST\_VALUE} \\
|
\item \B{ARST\_VALUE} \\
|
||||||
|
@ -210,8 +241,27 @@ Usually these cells are generated by the {\tt proc} pass using the information
|
||||||
in the designs RTLIL::Process objects.
|
in the designs RTLIL::Process objects.
|
||||||
\end{sloppypar}
|
\end{sloppypar}
|
||||||
|
|
||||||
|
D-type flip-flops with asynchronous set and reset are represented by {\tt \$dffsr} cells.
|
||||||
|
As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have
|
||||||
|
a single-bit \B{SET} input port for the set pin, a single-bit \B{CLR} input port for the reset pin,
|
||||||
|
and the following two parameters:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \B{SET\_POLARITY} \\
|
||||||
|
The set input is active-high if this parameter has the value {\tt 1'b1} and active-low
|
||||||
|
if this parameter is {\tt 1'b0}.
|
||||||
|
|
||||||
|
\item \B{CLR\_POLARITY} \\
|
||||||
|
The reset input is active-high if this parameter has the value {\tt 1'b1} and active-low
|
||||||
|
if this parameter is {\tt 1'b0}.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
When both the set and reset inputs of a {\tt \$dffsr} cell are active, the reset input takes
|
||||||
|
precedence.
|
||||||
|
|
||||||
\begin{fixme}
|
\begin{fixme}
|
||||||
Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches.
|
Add information about {\tt \$sr} cells (set-reset flip-flops), {\tt \$dlatch} cells (d-type latches),
|
||||||
|
and {\tt \$dlatchsr} cells (d-type latches with set/reset).
|
||||||
\end{fixme}
|
\end{fixme}
|
||||||
|
|
||||||
\subsection{Memories}
|
\subsection{Memories}
|
||||||
|
@ -430,6 +480,30 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\
|
||||||
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\
|
||||||
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
|
% FIXME: the layout of this is broken and I have no idea how to fix it
|
||||||
|
\hfil
|
||||||
|
\begin{tabular}[t]{lll}
|
||||||
|
$ClkEdge$ & $EnLvl$ & Cell Type \\
|
||||||
|
\hline
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN\_} \\
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP\_} \\
|
||||||
|
\end{tabular}
|
||||||
|
% FIXME: the layout of this is broken too
|
||||||
|
\hfil
|
||||||
|
\begin{tabular}[t]{llll}
|
||||||
|
$ClkEdge$ & $SetLvl$ & $RstLvl$ & Cell Type \\
|
||||||
|
\hline
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NNN\_} \\
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NNP\_} \\
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NPN\_} \\
|
||||||
|
\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NPP\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PNN\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PNP\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PPN\_} \\
|
||||||
|
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PPP\_} \\
|
||||||
|
\end{tabular}
|
||||||
\caption{Cell types for gate level logic networks}
|
\caption{Cell types for gate level logic networks}
|
||||||
\label{tab:CellLib_gates}
|
\label{tab:CellLib_gates}
|
||||||
\end{table}
|
\end{table}
|
||||||
|
@ -438,11 +512,22 @@ Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. Th
|
||||||
{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_},
|
{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_},
|
||||||
{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic.
|
{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic.
|
||||||
The cell type {\tt \$\_TBUF\_} is used to model tristate logic.
|
The cell type {\tt \$\_TBUF\_} is used to model tristate logic.
|
||||||
|
|
||||||
The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops.
|
The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops.
|
||||||
|
|
||||||
|
The cell types {\tt \$\_DFFE\_NN\_}, {\tt \$\_DFFE\_NP\_}, {\tt \$\_DFFE\_PN\_} and {\tt \$\_DFFE\_PP\_}
|
||||||
|
implement d-type flip-flops with enable. The values in the table for these cell types relate to the
|
||||||
|
following Verilog code template.
|
||||||
|
|
||||||
|
\begin{lstlisting}[mathescape,language=Verilog]
|
||||||
|
always @($ClkEdge$ C)
|
||||||
|
if (EN == $EnLvl$)
|
||||||
|
Q <= D;
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_},
|
The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_},
|
||||||
{\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement
|
{\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement
|
||||||
d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the
|
d-type flip-flops with asynchronous reset. The values in the table for these cell types relate to the
|
||||||
following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
|
following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
|
||||||
if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge;
|
if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge;
|
||||||
otherwise.
|
otherwise.
|
||||||
|
@ -455,6 +540,25 @@ otherwise.
|
||||||
Q <= D;
|
Q <= D;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
|
The cell types {\tt \$\_DFFSR\_NNN\_}, {\tt \$\_DFFSR\_NNP\_}, {\tt \$\_DFFSR\_NPN\_}, {\tt \$\_DFFSR\_NPP\_},
|
||||||
|
{\tt \$\_DFFSR\_PNN\_}, {\tt \$\_DFFSR\_PNP\_}, {\tt \$\_DFFSR\_PPN\_} and {\tt \$\_DFFSR\_PPP\_} implement
|
||||||
|
d-type flip-flops with asynchronous set and reset. The values in the table for these cell types relate to the
|
||||||
|
following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
|
||||||
|
if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge;
|
||||||
|
otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge;
|
||||||
|
if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge;
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
\begin{lstlisting}[mathescape,language=Verilog]
|
||||||
|
always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S)
|
||||||
|
if (R == $RstLvl$)
|
||||||
|
Q <= 0;
|
||||||
|
else if (S == $SetLvl$)
|
||||||
|
Q <= 1;
|
||||||
|
else
|
||||||
|
Q <= D;
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells
|
In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells
|
||||||
from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap}
|
from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap}
|
||||||
pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC}
|
pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC}
|
||||||
|
@ -486,11 +590,7 @@ Add information about {\tt \$ff} and {\tt \$\_FF\_} cells.
|
||||||
\end{fixme}
|
\end{fixme}
|
||||||
|
|
||||||
\begin{fixme}
|
\begin{fixme}
|
||||||
Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells.
|
Add information about {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells.
|
||||||
\end{fixme}
|
|
||||||
|
|
||||||
\begin{fixme}
|
|
||||||
Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells.
|
|
||||||
\end{fixme}
|
\end{fixme}
|
||||||
|
|
||||||
\begin{fixme}
|
\begin{fixme}
|
||||||
|
|
|
@ -93,7 +93,7 @@ frontends/verilog/preproc.cc} in the Yosys source tree.
|
||||||
|
|
||||||
\begin{sloppypar}
|
\begin{sloppypar}
|
||||||
The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
|
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
|
The lexer does little more than identifying all keywords and literals
|
||||||
recognised by the Yosys Verilog frontend.
|
recognised by the Yosys Verilog frontend.
|
||||||
\end{sloppypar}
|
\end{sloppypar}
|
||||||
|
@ -115,7 +115,7 @@ whenever possible.)
|
||||||
\subsection{The Verilog Parser}
|
\subsection{The Verilog Parser}
|
||||||
|
|
||||||
The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
|
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
|
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
|
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
|
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
|
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
|
availability of a Free and Open Source (FOSS) synthesis tool that can be used
|
||||||
as basis for custom tools would be helpful.
|
as basis for custom tools would be helpful.
|
||||||
|
|
||||||
|
|
|
@ -1935,6 +1935,19 @@ def parse_header(source):
|
||||||
line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
|
line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
|
||||||
ugly_line = unpretty_string(line)
|
ugly_line = unpretty_string(line)
|
||||||
|
|
||||||
|
# for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line
|
||||||
|
if 'union {' in line:
|
||||||
|
j = i+1
|
||||||
|
while j < len(source_text):
|
||||||
|
union_line = source_text[j]
|
||||||
|
if '};' in union_line:
|
||||||
|
source_text[j] = '\n'
|
||||||
|
break
|
||||||
|
j += 1
|
||||||
|
if j != len(source_text):
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
|
if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
|
||||||
namespace_name = ugly_line[10:].replace("{","").strip()
|
namespace_name = ugly_line[10:].replace("{","").strip()
|
||||||
namespaces.append((namespace_name, ugly_line.count("{")))
|
namespaces.append((namespace_name, ugly_line.count("{")))
|
||||||
|
|
|
@ -32,3 +32,4 @@ OBJS += passes/cmds/chtype.o
|
||||||
OBJS += passes/cmds/blackbox.o
|
OBJS += passes/cmds/blackbox.o
|
||||||
OBJS += passes/cmds/ltp.o
|
OBJS += passes/cmds/ltp.o
|
||||||
OBJS += passes/cmds/bugpoint.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()) {
|
for (auto &conn : cell->connections()) {
|
||||||
string suffix = stringf("_%s", log_id(conn.first));
|
string suffix = stringf("_%s", log_id(conn.first));
|
||||||
for (auto bit : conn.second)
|
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);
|
IdString new_name(cell->name.str() + suffix);
|
||||||
int score = wire_score.at(bit.wire);
|
int score = wire_score.at(bit.wire);
|
||||||
if (cell->output(conn.first)) score = 0;
|
if (cell->output(conn.first)) score = 0;
|
||||||
|
|
|
@ -301,41 +301,40 @@ struct SccPass : public Pass {
|
||||||
RTLIL::Selection newSelection(false);
|
RTLIL::Selection newSelection(false);
|
||||||
int scc_counter = 0;
|
int scc_counter = 0;
|
||||||
|
|
||||||
for (auto &mod_it : design->modules_)
|
for (auto mod : design->selected_modules())
|
||||||
if (design->selected(mod_it.second))
|
{
|
||||||
|
SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);
|
||||||
|
|
||||||
|
if (!setAttr.empty())
|
||||||
{
|
{
|
||||||
SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
|
for (const auto &cells : worker.sccList)
|
||||||
|
|
||||||
if (!setAttr.empty())
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
IdString attr_name(RTLIL::escape_id(attr.first));
|
string index = stringf("%d", scc_counter);
|
||||||
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())
|
for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
|
||||||
attr_valstr.replace(pos, 2, index);
|
attr_valstr.replace(pos, 2, index);
|
||||||
|
|
||||||
Const attr_value(attr_valstr);
|
Const attr_value(attr_valstr);
|
||||||
|
|
||||||
for (auto cell : cells)
|
for (auto cell : cells)
|
||||||
cell->attributes[attr_name] = attr_value;
|
cell->attributes[attr_name] = attr_value;
|
||||||
}
|
|
||||||
|
|
||||||
scc_counter++;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
scc_counter += GetSize(worker.sccList);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectMode)
|
scc_counter++;
|
||||||
worker.select(newSelection);
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scc_counter += GetSize(worker.sccList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectMode)
|
||||||
|
worker.select(newSelection);
|
||||||
|
}
|
||||||
|
|
||||||
if (expect >= 0) {
|
if (expect >= 0) {
|
||||||
if (scc_counter == expect)
|
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
|
|
@ -873,7 +873,7 @@ struct ShowPass : public Pass {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
|
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
|
||||||
#else
|
#else
|
||||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
|
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
|
||||||
#endif
|
#endif
|
||||||
log("Exec: %s\n", cmd.c_str());
|
log("Exec: %s\n", cmd.c_str());
|
||||||
if (run_command(cmd) != 0)
|
if (run_command(cmd) != 0)
|
||||||
|
|
|
@ -44,6 +44,10 @@ struct EquivOptPass:public ScriptPass
|
||||||
log(" expand the modules in this file before proving equivalence. this is\n");
|
log(" expand the modules in this file before proving equivalence. this is\n");
|
||||||
log(" useful for handling architecture-specific primitives.\n");
|
log(" useful for handling architecture-specific primitives.\n");
|
||||||
log("\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(" -assert\n");
|
||||||
log(" produce an error if the circuits are not equivalent.\n");
|
log(" produce an error if the circuits are not equivalent.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -61,13 +65,14 @@ struct EquivOptPass:public ScriptPass
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string command, techmap_opts;
|
std::string command, techmap_opts, make_opts;
|
||||||
bool assert, undef, multiclock, async2sync;
|
bool assert, undef, multiclock, async2sync;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
command = "";
|
command = "";
|
||||||
techmap_opts = "";
|
techmap_opts = "";
|
||||||
|
make_opts = "";
|
||||||
assert = false;
|
assert = false;
|
||||||
undef = false;
|
undef = false;
|
||||||
multiclock = false;
|
multiclock = false;
|
||||||
|
@ -93,6 +98,10 @@ struct EquivOptPass:public ScriptPass
|
||||||
techmap_opts += " -map " + args[++argidx];
|
techmap_opts += " -map " + args[++argidx];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-blacklist" && argidx + 1 < args.size()) {
|
||||||
|
make_opts += " -blacklist " + args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-assert") {
|
if (args[argidx] == "-assert") {
|
||||||
assert = true;
|
assert = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -170,7 +179,12 @@ struct EquivOptPass:public ScriptPass
|
||||||
run("clk2fflogic", "(only with -multiclock)");
|
run("clk2fflogic", "(only with -multiclock)");
|
||||||
if (async2sync || help_mode)
|
if (async2sync || help_mode)
|
||||||
run("async2sync", " (only with -async2sync)");
|
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)
|
if (help_mode)
|
||||||
run("equiv_induct [-undef] equiv");
|
run("equiv_induct [-undef] equiv");
|
||||||
else if (undef)
|
else if (undef)
|
||||||
|
|
|
@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
|
||||||
static std::set<RTLIL::Cell*> muxtree_cells;
|
static std::set<RTLIL::Cell*> muxtree_cells;
|
||||||
static SigPool sig_at_port;
|
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) {
|
if (sig.is_fully_const() || old_sig == sig) {
|
||||||
|
ret_true:
|
||||||
|
mux_tree_cache[sig] = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sig_at_port.check_any(assign_map(sig))) {
|
if (sig_at_port.check_any(assign_map(sig))) {
|
||||||
|
ret_false:
|
||||||
|
mux_tree_cache[sig] = false;
|
||||||
return 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)
|
for (auto &cellport : cellport_list)
|
||||||
{
|
{
|
||||||
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
|
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
|
||||||
return false;
|
goto ret_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recursion_monitor.count(cellport.first)) {
|
if (recursion_monitor.count(cellport.first)) {
|
||||||
log_warning("logic loop in mux tree at signal %s in module %s.\n",
|
log_warning("logic loop in mux tree at signal %s in module %s.\n",
|
||||||
log_signal(sig), RTLIL::id2cstr(module->name));
|
log_signal(sig), RTLIL::id2cstr(module->name));
|
||||||
return false;
|
goto ret_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recursion_monitor.insert(cellport.first);
|
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_a = assign_map(cellport.first->getPort("\\A"));
|
||||||
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
|
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);
|
recursion_monitor.erase(cellport.first);
|
||||||
return false;
|
goto ret_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < sig_b.size(); i += sig_a.size())
|
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);
|
recursion_monitor.erase(cellport.first);
|
||||||
return false;
|
goto ret_false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recursion_monitor.erase(cellport.first);
|
recursion_monitor.erase(cellport.first);
|
||||||
muxtree_cells.insert(cellport.first);
|
muxtree_cells.insert(cellport.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
goto ret_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_state_users(RTLIL::SigSpec sig)
|
static bool check_state_users(RTLIL::SigSpec sig)
|
||||||
|
@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
|
||||||
pool<Cell*> recursion_monitor;
|
pool<Cell*> recursion_monitor;
|
||||||
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
|
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
|
||||||
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
|
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
|
||||||
|
dict<RTLIL::SigSpec, bool> mux_tree_cache;
|
||||||
|
|
||||||
if (sig_q != assign_map(wire))
|
if (sig_q != assign_map(wire))
|
||||||
continue;
|
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);
|
looks_like_good_state_reg = check_state_users(sig_q);
|
||||||
|
|
||||||
if (!looks_like_state_reg)
|
if (!looks_like_state_reg)
|
||||||
|
|
|
@ -134,6 +134,7 @@ struct rules_t
|
||||||
dict<string, int> min_limits, max_limits;
|
dict<string, int> min_limits, max_limits;
|
||||||
bool or_next_if_better, make_transp, make_outreg;
|
bool or_next_if_better, make_transp, make_outreg;
|
||||||
char shuffle_enable;
|
char shuffle_enable;
|
||||||
|
vector<vector<std::tuple<bool,IdString,Const>>> attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
dict<IdString, vector<bram_t>> brams;
|
dict<IdString, vector<bram_t>> brams;
|
||||||
|
@ -327,6 +328,20 @@ struct rules_t
|
||||||
continue;
|
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();
|
syntax_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -724,7 +739,7 @@ grow_read_ports:;
|
||||||
if (match.make_transp && wr_ports <= 1) {
|
if (match.make_transp && wr_ports <= 1) {
|
||||||
pi.make_transp = true;
|
pi.make_transp = true;
|
||||||
if (pi.clocks != 0) {
|
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);
|
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;
|
goto skip_bram_rport;
|
||||||
}
|
}
|
||||||
|
@ -813,6 +828,43 @@ grow_read_ports:;
|
||||||
return false;
|
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)
|
if (mode == 1)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1100,6 +1152,43 @@ void handle_cell(Cell *cell, const rules_t &rules)
|
||||||
goto next_match_rule;
|
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);
|
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())
|
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(" dcells ....... number of cells in 'data-direction'\n");
|
||||||
log(" cells ........ total number of cells (acells*dcells*dups)\n");
|
log(" cells ........ total number of cells (acells*dcells*dups)\n");
|
||||||
log("\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("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("description. Use 'techmap' to convert the created bram instances into\n");
|
||||||
log("instances of the actual bram cells of your target architecture.\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_DATA", sig_rd_data);
|
||||||
mem->setPort("\\RD_EN", sig_rd_en);
|
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)
|
for (auto c : memcells)
|
||||||
module->remove(c);
|
module->remove(c);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
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_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);
|
cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
|
||||||
if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
|
if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
|
||||||
cell->setPort(ID::A, cell->getPort(ID::B));
|
cell->setPort(ID::A, cell->getPort(ID::B));
|
||||||
|
|
|
@ -44,9 +44,10 @@ struct OptReduceWorker
|
||||||
cells.erase(cell);
|
cells.erase(cell);
|
||||||
|
|
||||||
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
||||||
|
sig_a.sort_and_unify();
|
||||||
pool<RTLIL::SigBit> new_sig_a_bits;
|
pool<RTLIL::SigBit> new_sig_a_bits;
|
||||||
|
|
||||||
for (auto &bit : sig_a.to_sigbit_set())
|
for (auto &bit : sig_a)
|
||||||
{
|
{
|
||||||
if (bit == RTLIL::State::S0) {
|
if (bit == RTLIL::State::S0) {
|
||||||
if (cell->type == ID($reduce_and)) {
|
if (cell->type == ID($reduce_and)) {
|
||||||
|
@ -86,6 +87,7 @@ struct OptReduceWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::SigSpec new_sig_a(new_sig_a_bits);
|
RTLIL::SigSpec new_sig_a(new_sig_a_bits);
|
||||||
|
new_sig_a.sort_and_unify();
|
||||||
|
|
||||||
if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) {
|
if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) {
|
||||||
log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
|
log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
|
||||||
|
@ -235,7 +237,6 @@ struct OptReduceWorker
|
||||||
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
|
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
|
||||||
|
|
||||||
module->connect(old_sig_conn);
|
module->connect(old_sig_conn);
|
||||||
module->check();
|
|
||||||
|
|
||||||
did_something = true;
|
did_something = true;
|
||||||
total_count++;
|
total_count++;
|
||||||
|
@ -324,6 +325,8 @@ struct OptReduceWorker
|
||||||
opt_mux(cell);
|
opt_mux(cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module->check();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
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)
|
#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->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) {
|
if (decode_port(shared_op, ID::A, &assign_map) == operand) {
|
||||||
shared_op->setPort(ID::B, mux_to_oper);
|
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 {
|
} else {
|
||||||
shared_op->setPort(ID::A, mux_to_oper);
|
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
|
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_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_CREG_pm.h))
|
||||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_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
|
// SB_MAC16 Input Interface
|
||||||
SigSpec A = st.sigA;
|
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);
|
log_assert(GetSize(A) == 16);
|
||||||
|
|
||||||
SigSpec B = st.sigB;
|
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);
|
log_assert(GetSize(B) == 16);
|
||||||
|
|
||||||
SigSpec CD = st.sigCD;
|
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(BOTADDSUB_CARRYSELECT), Const(0, 2));
|
||||||
|
|
||||||
cell->setParam(ID(MODE_8x8), State::S0);
|
cell->setParam(ID(MODE_8x8), State::S0);
|
||||||
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_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->getParam(ID(B_SIGNED)).as_bool());
|
cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
|
||||||
|
|
||||||
if (st.ffO) {
|
if (st.ffO) {
|
||||||
if (st.o_lo)
|
if (st.o_lo)
|
||||||
|
|
|
@ -56,11 +56,16 @@ code sigA sigB sigH
|
||||||
break;
|
break;
|
||||||
sigH.append(O[i]);
|
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);
|
log_assert(nusers(O.extract_end(i)) <= 1);
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
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;
|
argQ = sigA;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -81,7 +86,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
|
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;
|
argQ = sigB;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -104,7 +109,7 @@ endcode
|
||||||
code argD ffFJKG sigH clock clock_pol
|
code argD ffFJKG sigH clock clock_pol
|
||||||
if (nusers(sigH) == 2 &&
|
if (nusers(sigH) == 2 &&
|
||||||
(mul->type != \SB_MAC16 ||
|
(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;
|
argD = sigH;
|
||||||
subpattern(out_dffe);
|
subpattern(out_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -143,7 +148,7 @@ endcode
|
||||||
|
|
||||||
code argD ffH sigH sigO clock clock_pol
|
code argD ffH sigH sigO clock clock_pol
|
||||||
if (ffFJKG && nusers(sigH) == 2 &&
|
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;
|
argD = sigH;
|
||||||
subpattern(out_dffe);
|
subpattern(out_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -174,7 +179,7 @@ reject_ffH: ;
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match add
|
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)
|
select add->type.in($add)
|
||||||
choice <IdString> AB {\A, \B}
|
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))
|
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||||
reject;
|
reject;
|
||||||
// If accumulator, check adder width and signedness
|
// 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;
|
reject;
|
||||||
|
|
||||||
sigO = port(add, \Y);
|
sigO = port(add, \Y);
|
||||||
|
@ -275,7 +280,7 @@ endcode
|
||||||
|
|
||||||
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
|
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
|
||||||
if (!sigCD.empty() && sigCD != sigO &&
|
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;
|
argQ = sigCD;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -328,6 +333,8 @@ arg argD argQ clock clock_pol
|
||||||
|
|
||||||
code
|
code
|
||||||
dff = nullptr;
|
dff = nullptr;
|
||||||
|
if (argQ.empty())
|
||||||
|
reject;
|
||||||
for (auto c : argQ.chunks()) {
|
for (auto c : argQ.chunks()) {
|
||||||
if (!c.wire)
|
if (!c.wire)
|
||||||
reject;
|
reject;
|
||||||
|
|
|
@ -42,14 +42,30 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
|
||||||
|
|
||||||
cell->setPort("\\A", st.carry->getPort("\\I0"));
|
cell->setPort("\\A", st.carry->getPort("\\I0"));
|
||||||
cell->setPort("\\B", st.carry->getPort("\\I1"));
|
cell->setPort("\\B", st.carry->getPort("\\I1"));
|
||||||
cell->setPort("\\CI", st.carry->getPort("\\CI"));
|
auto CI = st.carry->getPort("\\CI");
|
||||||
|
cell->setPort("\\CI", CI);
|
||||||
cell->setPort("\\CO", st.carry->getPort("\\CO"));
|
cell->setPort("\\CO", st.carry->getPort("\\CO"));
|
||||||
|
|
||||||
cell->setPort("\\I0", st.lut->getPort("\\I0"));
|
cell->setPort("\\I0", st.lut->getPort("\\I0"));
|
||||||
cell->setPort("\\I3", st.lut->getPort("\\I3"));
|
auto I3 = st.lut->getPort("\\I3");
|
||||||
|
if (pm.sigmap(CI) == pm.sigmap(I3)) {
|
||||||
|
cell->setParam("\\I3_IS_CI", State::S1);
|
||||||
|
I3 = State::Sx;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cell->setParam("\\I3_IS_CI", State::S0);
|
||||||
|
cell->setPort("\\I3", I3);
|
||||||
cell->setPort("\\O", st.lut->getPort("\\O"));
|
cell->setPort("\\O", st.lut->getPort("\\O"));
|
||||||
cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT"));
|
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.carry);
|
||||||
pm.autoremove(st.lut);
|
pm.autoremove(st.lut);
|
||||||
}
|
}
|
||||||
|
@ -62,28 +78,80 @@ struct Ice40WrapCarryPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" ice40_wrapcarry [selection]\n");
|
log(" ice40_wrapcarry [selection]\n");
|
||||||
log("\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("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");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
bool unwrap = false;
|
||||||
|
|
||||||
log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
|
log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-singleton") {
|
if (args[argidx] == "-unwrap") {
|
||||||
// singleton_mode = true;
|
unwrap = true;
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules()) {
|
||||||
ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
|
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)));
|
||||||
|
auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3));
|
||||||
|
lut->setPort(ID(A), { I3, cell->getPort(ID(B)), cell->getPort(ID(A)), cell->getPort(ID(I0)) });
|
||||||
|
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;
|
} Ice40WrapCarryPass;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#include "passes/pmgen/xilinx_dsp_pm.h"
|
#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_CREG_pm.h"
|
||||||
#include "passes/pmgen/xilinx_dsp_cascade_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);
|
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)
|
void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
|
||||||
{
|
{
|
||||||
auto &st = pm.st_xilinx_dsp_packC;
|
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("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("into using the DSP48E1's pattern detector feature for overflow detection.\n");
|
||||||
log("\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
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
|
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
|
||||||
|
|
||||||
|
std::string family = "xc7";
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-singleton") {
|
if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) {
|
||||||
// singleton_mode = true;
|
family = args[++argidx];
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
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()) {
|
for (auto module : design->selected_modules()) {
|
||||||
// Experimental feature: pack $add/$sub cells with
|
// Experimental feature: pack $add/$sub cells with
|
||||||
// (* use_dsp48="simd" *) into DSP48E1's using its
|
// (* use_dsp48="simd" *) into DSP48E1's using its
|
||||||
// SIMD feature
|
// 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,
|
// Match for all features ([ABDMP][12]?REG, pre-adder,
|
||||||
// post-adder, pattern detector, etc.) except for CREG
|
// post-adder, pattern detector, etc.) except for CREG
|
||||||
{
|
if (family == "xc7") {
|
||||||
xilinx_dsp_pm pm(module, module->selected_cells());
|
xilinx_dsp_pm pm(module, module->selected_cells());
|
||||||
pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
|
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
|
// Separating out CREG packing is necessary since there
|
||||||
// is no guarantee that the cell ordering corresponds
|
// is no guarantee that the cell ordering corresponds
|
||||||
|
|
|
@ -120,7 +120,7 @@ endcode
|
||||||
// reset functionality, using a subpattern discussed above)
|
// reset functionality, using a subpattern discussed above)
|
||||||
// If matched, treat 'A' input as input of ADREG
|
// If matched, treat 'A' input as input of ADREG
|
||||||
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
|
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;
|
argQ = sigA;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
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
|
// Only search for ffA2 if there was a pre-adder
|
||||||
// (otherwise ffA2 would have been matched as ffAD)
|
// (otherwise ffA2 would have been matched as ffAD)
|
||||||
if (preAdd) {
|
if (preAdd) {
|
||||||
if (param(dsp, \AREG).as_int() == 0) {
|
if (param(dsp, \AREG, 1).as_int() == 0) {
|
||||||
argQ = sigA;
|
argQ = sigA;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -237,7 +237,7 @@ endcode
|
||||||
// (5) Match 'B' input for B2REG
|
// (5) Match 'B' input for B2REG
|
||||||
// If B2REG, then match 'B' input for B1REG
|
// If B2REG, then match 'B' input for B1REG
|
||||||
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
|
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;
|
argQ = sigB;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -287,7 +287,7 @@ endcode
|
||||||
|
|
||||||
// (6) Match 'D' input for DREG
|
// (6) Match 'D' input for DREG
|
||||||
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
|
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;
|
argQ = sigD;
|
||||||
subpattern(in_dffe);
|
subpattern(in_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -308,7 +308,7 @@ endcode
|
||||||
|
|
||||||
// (7) Match 'P' output that exclusively drives an MREG
|
// (7) Match 'P' output that exclusively drives an MREG
|
||||||
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
|
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;
|
argD = sigM;
|
||||||
subpattern(out_dffe);
|
subpattern(out_dffe);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
|
@ -335,7 +335,7 @@ endcode
|
||||||
// recognised in xilinx_dsp.cc).
|
// recognised in xilinx_dsp.cc).
|
||||||
match postAdd
|
match postAdd
|
||||||
// Ensure that Z mux is not already used
|
// 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 postAdd->type.in($add)
|
||||||
select GetSize(port(postAdd, \Y)) <= 48
|
select GetSize(port(postAdd, \Y)) <= 48
|
||||||
|
@ -347,9 +347,9 @@ match postAdd
|
||||||
index <SigBit> port(postAdd, AB)[0] === sigP[0]
|
index <SigBit> port(postAdd, AB)[0] === sigP[0]
|
||||||
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
|
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
|
||||||
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
|
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
|
||||||
// Check that remainder of AB is a sign-extension
|
// Check that remainder of AB is a sign- or zero-extension
|
||||||
define <bool> AB_SIGNED (param(postAdd, AB == \A ? \A_SIGNED : \B_SIGNED).as_bool())
|
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))
|
||||||
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(AB_SIGNED ? sigP[GetSize(sigP)-1] : State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
|
|
||||||
set postAddAB AB
|
set postAddAB AB
|
||||||
optional
|
optional
|
||||||
endmatch
|
endmatch
|
||||||
|
@ -363,7 +363,7 @@ endcode
|
||||||
|
|
||||||
// (9) Match 'P' output that exclusively drives a PREG
|
// (9) Match 'P' output that exclusively drives a PREG
|
||||||
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
|
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;
|
int users = 2;
|
||||||
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
|
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
|
||||||
if (ffMcemux && !postAdd) users++;
|
if (ffMcemux && !postAdd) users++;
|
||||||
|
@ -460,7 +460,7 @@ arg argD argQ clock
|
||||||
|
|
||||||
code
|
code
|
||||||
dff = nullptr;
|
dff = nullptr;
|
||||||
if (GetSize(argQ) == 0)
|
if (argQ.empty())
|
||||||
reject;
|
reject;
|
||||||
for (const auto &c : argQ.chunks()) {
|
for (const auto &c : argQ.chunks()) {
|
||||||
// Abandon matches when 'Q' is a constant
|
// 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
|
// This file describes the second of three pattern matcher setups that
|
||||||
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
||||||
// At a high level, it works as follows:
|
// 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
|
// and (b) uses the 'C' port
|
||||||
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
|
// (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
|
// (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 <Cell*> dff dffcemux dffrstmux
|
||||||
udata <bool> dffcepol dffrstpol
|
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
|
// and (b) uses the 'C' port
|
||||||
match dsp
|
match dsp
|
||||||
select dsp->type.in(\DSP48E1)
|
select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
|
||||||
select param(dsp, \CREG, 1).as_int() == 0
|
select param(dsp, \CREG, 1).as_int() == 0
|
||||||
select nusers(port(dsp, \C, SigSpec())) > 1
|
select nusers(port(dsp, \C, SigSpec())) > 1
|
||||||
endmatch
|
endmatch
|
||||||
|
@ -60,7 +60,8 @@ code sigC sigP clock
|
||||||
sigC = unextend(port(dsp, \C, SigSpec()));
|
sigC = unextend(port(dsp, \C, SigSpec()));
|
||||||
|
|
||||||
SigSpec P = port(dsp, \P);
|
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
|
// Only care about those bits that are used
|
||||||
int i;
|
int i;
|
||||||
for (i = GetSize(P)-1; i >= 0; i--)
|
for (i = GetSize(P)-1; i >= 0; i--)
|
||||||
|
|
|
@ -62,12 +62,11 @@ code
|
||||||
#define MAX_DSP_CASCADE 20
|
#define MAX_DSP_CASCADE 20
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer
|
// (1) Starting from a DSP48* cell that (a) has the Z multiplexer
|
||||||
// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already
|
// (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1)
|
||||||
// use the 'PCOUT' port
|
// set to zero and (b) doesn't already use the 'PCOUT' port
|
||||||
match first
|
match first
|
||||||
select first->type.in(\DSP48E1)
|
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 port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
|
|
||||||
select nusers(port(first, \PCOUT, SigSpec())) <= 1
|
select nusers(port(first, \PCOUT, SigSpec())) <= 1
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
|
@ -100,14 +99,21 @@ finally
|
||||||
add_siguser(cascade, dsp);
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
|
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
|
||||||
if (P == 17)
|
if (dsp->type.in(\DSP48A, \DSP48A1)) {
|
||||||
opmode[6] = State::S1;
|
log_assert(P == 0);
|
||||||
else if (P == 0)
|
opmode[3] = State::S0;
|
||||||
opmode[6] = State::S0;
|
opmode[2] = State::S1;
|
||||||
else log_abort();
|
}
|
||||||
|
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[5] = State::S0;
|
||||||
opmode[4] = State::S1;
|
opmode[4] = State::S1;
|
||||||
|
}
|
||||||
dsp_pcin->setPort(\OPMODE, opmode);
|
dsp_pcin->setPort(\OPMODE, opmode);
|
||||||
|
|
||||||
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
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_pcin);
|
||||||
add_siguser(cascade, dsp);
|
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"));
|
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
|
||||||
|
|
||||||
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
}
|
}
|
||||||
if (BREG >= 0) {
|
if (BREG >= 0) {
|
||||||
Wire *cascade = module->addWire(NEW_ID, 18);
|
Wire *cascade = module->addWire(NEW_ID, 18);
|
||||||
dsp_pcin->setPort(ID(B), Const(0, 18));
|
if (dsp->type.in(\DSP48A, \DSP48A1)) {
|
||||||
dsp_pcin->setPort(ID(BCIN), cascade);
|
// 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);
|
dsp->setPort(ID(BCOUT), cascade);
|
||||||
add_siguser(cascade, dsp_pcin);
|
add_siguser(cascade, dsp_pcin);
|
||||||
add_siguser(cascade, dsp);
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
dsp->setParam(ID(BCASCREG), BREG);
|
if (dsp->type.in(\DSP48E1)) {
|
||||||
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
|
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));
|
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 first
|
||||||
arg next
|
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
|
// (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
|
// driven by the 'P' output of the previous DSP cell, and (c) has its
|
||||||
// 'PCIN' port unused
|
// 'PCIN' port unused
|
||||||
match nextP
|
match nextP
|
||||||
select nextP->type.in(\DSP48E1)
|
|
||||||
select !param(nextP, \CREG, State::S1).as_bool()
|
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, \C, SigSpec())) > 1
|
||||||
select nusers(port(nextP, \PCIN, SigSpec())) == 0
|
select nusers(port(nextP, \PCIN, SigSpec())) == 0
|
||||||
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
|
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
|
||||||
semioptional
|
semioptional
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the
|
// (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven
|
||||||
// previous DSP cell right-shifted by 17 bits
|
// by the 'P' output of the previous DSP cell right-shifted by 17 bits
|
||||||
match nextP_shift17
|
match nextP_shift17
|
||||||
if !nextP
|
if !nextP
|
||||||
select nextP_shift17->type.in(\DSP48E1)
|
select nextP_shift17->type.in(\DSP48E1)
|
||||||
|
@ -188,6 +214,8 @@ code next
|
||||||
if (!nextP)
|
if (!nextP)
|
||||||
next = nextP_shift17;
|
next = nextP_shift17;
|
||||||
if (next) {
|
if (next) {
|
||||||
|
if (next->type != first->type)
|
||||||
|
reject;
|
||||||
unextend = [](const SigSpec &sig) {
|
unextend = [](const SigSpec &sig) {
|
||||||
int i;
|
int i;
|
||||||
for (i = GetSize(sig)-1; i > 0; i--)
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
|
@ -202,38 +230,50 @@ code next
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
|
// (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
|
// if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade,
|
||||||
// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already
|
// (b) the previous DSP does not already use its ACOUT port, then
|
||||||
// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already
|
// examine if an ACOUT -> ACIN cascade opportunity exists if
|
||||||
// use its ACOUT port, then examine if an ACOUT -> ACIN cascade
|
// (i) A ports are identical, or (ii) separated by a
|
||||||
// opportunity exists by matching for a $dff-with-optional-clock-enable-
|
// $dff-with-optional-clock-enable-or-reset and checking that the 'D' input
|
||||||
// or-reset and checking that the 'D' input of this register is the same
|
// of this register is the same as the 'A' input of the previous DSP
|
||||||
// as the 'A' input of the previous DSP
|
// TODO: Check for two levels of flops, instead of just one
|
||||||
code argQ clock AREG
|
code argQ clock AREG
|
||||||
AREG = -1;
|
AREG = -1;
|
||||||
if (next) {
|
if (next && next->type.in(\DSP48E1)) {
|
||||||
Cell *prev = std::get<0>(chain.back());
|
Cell *prev = std::get<0>(chain.back());
|
||||||
if (param(prev, \AREG, 2).as_int() > 0 &&
|
|
||||||
param(next, \AREG, 2).as_int() > 0 &&
|
if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||||
param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
port(next, \ACIN, SigSpec()).is_fully_zero() &&
|
||||||
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
|
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
|
||||||
argQ = unextend(port(next, \A));
|
if (param(prev, \AREG, 2) == 0) {
|
||||||
clock = port(prev, \CLK);
|
if (port(prev, \A) == port(next, \A))
|
||||||
subpattern(in_dffe);
|
AREG = 0;
|
||||||
if (dff) {
|
}
|
||||||
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
else {
|
||||||
goto reject_AREG;
|
argQ = unextend(port(next, \A));
|
||||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
clock = port(prev, \CLK);
|
||||||
goto reject_AREG;
|
subpattern(in_dffe);
|
||||||
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0)
|
if (dff) {
|
||||||
goto reject_AREG;
|
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
||||||
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0))
|
goto reject_AREG;
|
||||||
goto reject_AREG;
|
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
||||||
if (dffD == unextend(port(prev, \A)))
|
goto reject_AREG;
|
||||||
AREG = 1;
|
IdString CEA;
|
||||||
reject_AREG: ;
|
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
|
endcode
|
||||||
|
|
||||||
|
@ -242,28 +282,47 @@ code argQ clock BREG
|
||||||
BREG = -1;
|
BREG = -1;
|
||||||
if (next) {
|
if (next) {
|
||||||
Cell *prev = std::get<0>(chain.back());
|
Cell *prev = std::get<0>(chain.back());
|
||||||
if (param(prev, \BREG, 2).as_int() > 0 &&
|
if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||||
param(next, \BREG, 2).as_int() > 0 &&
|
|
||||||
param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
|
||||||
port(next, \BCIN, SigSpec()).is_fully_zero() &&
|
port(next, \BCIN, SigSpec()).is_fully_zero() &&
|
||||||
nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
|
nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
|
||||||
argQ = unextend(port(next, \B));
|
if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) ||
|
||||||
clock = port(prev, \CLK);
|
(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) {
|
||||||
subpattern(in_dffe);
|
if (port(prev, \B) == port(next, \B))
|
||||||
if (dff) {
|
BREG = 0;
|
||||||
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
|
}
|
||||||
goto reject_BREG;
|
else {
|
||||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
|
argQ = unextend(port(next, \B));
|
||||||
goto reject_BREG;
|
clock = port(prev, \CLK);
|
||||||
if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0)
|
subpattern(in_dffe);
|
||||||
goto reject_BREG;
|
if (dff) {
|
||||||
if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0))
|
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
|
||||||
goto reject_BREG;
|
goto reject_BREG;
|
||||||
if (dffD == unextend(port(prev, \B)))
|
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
|
||||||
BREG = 1;
|
goto reject_BREG;
|
||||||
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
|
endcode
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o
|
||||||
OBJS += passes/sat/fmcombine.o
|
OBJS += passes/sat/fmcombine.o
|
||||||
OBJS += passes/sat/mutate.o
|
OBJS += passes/sat/mutate.o
|
||||||
OBJS += passes/sat/cutpoint.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
|
|
@ -269,7 +269,8 @@ struct SatHelper
|
||||||
for (int i = 0; i < lhs.size(); i++) {
|
for (int i = 0; i < lhs.size(); i++) {
|
||||||
RTLIL::SigSpec bit = lhs.extract(i, 1);
|
RTLIL::SigSpec bit = lhs.extract(i, 1);
|
||||||
if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
|
if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
|
||||||
removed_bits.append(bit);
|
if (rhs[i] != State::Sx)
|
||||||
|
removed_bits.append(bit);
|
||||||
lhs.remove(i, 1);
|
lhs.remove(i, 1);
|
||||||
rhs.remove(i, 1);
|
rhs.remove(i, 1);
|
||||||
i--;
|
i--;
|
||||||
|
|
|
@ -230,7 +230,7 @@ struct SimInstance
|
||||||
bool did_something = false;
|
bool did_something = false;
|
||||||
|
|
||||||
sig = sigmap(sig);
|
sig = sigmap(sig);
|
||||||
log_assert(GetSize(sig) == GetSize(value));
|
log_assert(GetSize(sig) <= GetSize(value));
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(sig); i++)
|
for (int i = 0; i < GetSize(sig); i++)
|
||||||
if (state_nets.at(sig[i]) != value[i]) {
|
if (state_nets.at(sig[i]) != value[i]) {
|
||||||
|
|
|
@ -8,9 +8,12 @@ OBJS += passes/techmap/libparse.o
|
||||||
ifeq ($(ENABLE_ABC),1)
|
ifeq ($(ENABLE_ABC),1)
|
||||||
OBJS += passes/techmap/abc.o
|
OBJS += passes/techmap/abc.o
|
||||||
OBJS += passes/techmap/abc9.o
|
OBJS += passes/techmap/abc9.o
|
||||||
|
OBJS += passes/techmap/abc9_exe.o
|
||||||
|
OBJS += passes/techmap/abc9_ops.o
|
||||||
ifneq ($(ABCEXTERNAL),)
|
ifneq ($(ABCEXTERNAL),)
|
||||||
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||||
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||||
|
passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -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
|
// 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
|
// 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_LIB "strash; ifraig; scorr; dc2; dretime; 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_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; retime {D}; strash; dch -f; if; mfs2"
|
#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; 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_SOP "strash; ifraig; scorr; dc2; dretime; 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_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_LIB "strash; dretime; map {D}"
|
||||||
#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
|
#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
|
||||||
#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if"
|
#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
|
||||||
#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}"
|
#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
|
||||||
#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map"
|
#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
|
||||||
|
|
||||||
#include "kernel/register.h"
|
#include "kernel/register.h"
|
||||||
#include "kernel/sigtools.h"
|
#include "kernel/sigtools.h"
|
||||||
|
@ -747,6 +747,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
||||||
else
|
else
|
||||||
abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
|
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))
|
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);
|
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
|
||||||
|
|
||||||
|
@ -1510,7 +1514,47 @@ struct AbcPass : public Pass {
|
||||||
#endif
|
#endif
|
||||||
#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];
|
char pwd [PATH_MAX];
|
||||||
if (!getcwd(pwd, sizeof(pwd))) {
|
if (!getcwd(pwd, sizeof(pwd))) {
|
||||||
log_cmd_error("getcwd failed: %s\n", strerror(errno));
|
log_cmd_error("getcwd failed: %s\n", strerror(errno));
|
||||||
|
@ -1524,23 +1568,14 @@ struct AbcPass : public Pass {
|
||||||
}
|
}
|
||||||
if (arg == "-script" && argidx+1 < args.size()) {
|
if (arg == "-script" && argidx+1 < args.size()) {
|
||||||
script_file = args[++argidx];
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-liberty" && argidx+1 < args.size()) {
|
if (arg == "-liberty" && argidx+1 < args.size()) {
|
||||||
liberty_file = args[++argidx];
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-constr" && argidx+1 < args.size()) {
|
if (arg == "-constr" && argidx+1 < args.size()) {
|
||||||
rewrite_filename(constr_file);
|
|
||||||
constr_file = args[++argidx];
|
constr_file = args[++argidx];
|
||||||
if (!constr_file.empty() && !is_absolute_path(constr_file))
|
|
||||||
constr_file = std::string(pwd) + "/" + constr_file;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-D" && argidx+1 < args.size()) {
|
if (arg == "-D" && argidx+1 < args.size()) {
|
||||||
|
@ -1560,37 +1595,11 @@ struct AbcPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-lut" && argidx+1 < args.size()) {
|
if (arg == "-lut" && argidx+1 < args.size()) {
|
||||||
string arg = args[++argidx];
|
lut_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));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-luts" && argidx+1 < args.size()) {
|
if (arg == "-luts" && argidx+1 < args.size()) {
|
||||||
lut_costs.clear();
|
luts_arg = args[++argidx];
|
||||||
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");
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-sop") {
|
if (arg == "-sop") {
|
||||||
|
@ -1614,123 +1623,11 @@ struct AbcPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-g" && argidx+1 < args.size()) {
|
if (arg == "-g" && argidx+1 < args.size()) {
|
||||||
for (auto g : split_tokens(args[++argidx], ",")) {
|
if (g_arg_from_cmd)
|
||||||
vector<string> gate_list;
|
log_cmd_error("Can only use -g once. Please combine.");
|
||||||
bool remove_gates = false;
|
g_arg = args[++argidx];
|
||||||
if (GetSize(g) > 0 && g[0] == '-') {
|
g_argidx = argidx;
|
||||||
remove_gates = true;
|
g_arg_from_cmd = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-fast") {
|
if (arg == "-fast") {
|
||||||
|
@ -1766,8 +1663,176 @@ struct AbcPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
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())
|
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())
|
if (!constr_file.empty() && liberty_file.empty())
|
||||||
log_cmd_error("Got -constr but no -liberty!\n");
|
log_cmd_error("Got -constr but no -liberty!\n");
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
531
passes/techmap/abc9_exe.cc
Normal file
531
passes/techmap/abc9_exe.cc
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// [[CITE]] ABC
|
||||||
|
// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
|
||||||
|
// http://www.eecs.berkeley.edu/~alanmi/abc/
|
||||||
|
|
||||||
|
#include "kernel/register.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <dirent.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef YOSYS_LINK_ABC
|
||||||
|
extern "C" int Abc_RealMain(int argc, char *argv[]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string fold_abc9_cmd(std::string str)
|
||||||
|
{
|
||||||
|
std::string token, new_str = " ";
|
||||||
|
int char_counter = 10;
|
||||||
|
|
||||||
|
for (size_t i = 0; i <= str.size(); i++) {
|
||||||
|
if (i < str.size())
|
||||||
|
token += str[i];
|
||||||
|
if (i == str.size() || str[i] == ';') {
|
||||||
|
if (char_counter + token.size() > 75)
|
||||||
|
new_str += "\n ", char_counter = 14;
|
||||||
|
new_str += token, char_counter += token.size();
|
||||||
|
token.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
std::string add_echos_to_abc9_cmd(std::string str)
|
||||||
|
{
|
||||||
|
std::string new_str, token;
|
||||||
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
|
token += str[i];
|
||||||
|
if (str[i] == ';') {
|
||||||
|
while (i+1 < str.size() && str[i+1] == ' ')
|
||||||
|
i++;
|
||||||
|
new_str += "echo + " + token + " " + token + " ";
|
||||||
|
token.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.empty()) {
|
||||||
|
if (!new_str.empty())
|
||||||
|
new_str += "echo + " + token + "; ";
|
||||||
|
new_str += token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
|
||||||
|
{
|
||||||
|
if (show_tempdir)
|
||||||
|
return text;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
size_t pos = text.find(tempdir_name);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
break;
|
||||||
|
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string selfdir_name = proc_self_dirname();
|
||||||
|
if (selfdir_name != "/") {
|
||||||
|
while (1) {
|
||||||
|
size_t pos = text.find(selfdir_name);
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
break;
|
||||||
|
text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct abc9_output_filter
|
||||||
|
{
|
||||||
|
bool got_cr;
|
||||||
|
int escape_seq_state;
|
||||||
|
std::string linebuf;
|
||||||
|
std::string tempdir_name;
|
||||||
|
bool show_tempdir;
|
||||||
|
|
||||||
|
abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
|
||||||
|
{
|
||||||
|
got_cr = false;
|
||||||
|
escape_seq_state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void next_char(char ch)
|
||||||
|
{
|
||||||
|
if (escape_seq_state == 0 && ch == '\033') {
|
||||||
|
escape_seq_state = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (escape_seq_state == 1) {
|
||||||
|
escape_seq_state = ch == '[' ? 2 : 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (escape_seq_state == 2) {
|
||||||
|
if ((ch < '0' || '9' < ch) && ch != ';')
|
||||||
|
escape_seq_state = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
escape_seq_state = 0;
|
||||||
|
if (ch == '\r') {
|
||||||
|
got_cr = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ch == '\n') {
|
||||||
|
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
|
||||||
|
got_cr = false, linebuf.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (got_cr)
|
||||||
|
got_cr = false, linebuf.clear();
|
||||||
|
linebuf += ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void next_line(const std::string &line)
|
||||||
|
{
|
||||||
|
//int pi, po;
|
||||||
|
//if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) {
|
||||||
|
// log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n",
|
||||||
|
// pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???",
|
||||||
|
// po, po_map.count(po) ? po_map.at(po).c_str() : "???");
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
for (char ch : line)
|
||||||
|
next_char(ch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file,
|
||||||
|
vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
|
||||||
|
bool show_tempdir, std::string box_file, std::string lut_file,
|
||||||
|
std::string wire_delay, std::string tempdir_name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::string abc9_script;
|
||||||
|
|
||||||
|
if (!lut_costs.empty())
|
||||||
|
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
|
||||||
|
else if (!lut_file.empty())
|
||||||
|
abc9_script += stringf("read_lut %s; ", lut_file.c_str());
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
|
||||||
|
log_assert(!box_file.empty());
|
||||||
|
abc9_script += stringf("read_box %s; ", box_file.c_str());
|
||||||
|
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
|
||||||
|
|
||||||
|
if (!script_file.empty()) {
|
||||||
|
if (script_file[0] == '+') {
|
||||||
|
for (size_t i = 1; i < script_file.size(); i++)
|
||||||
|
if (script_file[i] == '\'')
|
||||||
|
abc9_script += "'\\''";
|
||||||
|
else if (script_file[i] == ',')
|
||||||
|
abc9_script += " ";
|
||||||
|
else
|
||||||
|
abc9_script += script_file[i];
|
||||||
|
} else
|
||||||
|
abc9_script += stringf("source %s", script_file.c_str());
|
||||||
|
} else if (!lut_costs.empty() || !lut_file.empty()) {
|
||||||
|
abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)
|
||||||
|
: RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
|
||||||
|
} else
|
||||||
|
log_abort();
|
||||||
|
|
||||||
|
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
|
||||||
|
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
|
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
|
||||||
|
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
|
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
|
||||||
|
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
|
std::string C;
|
||||||
|
if (design->scratchpad.count("abc9.if.C"))
|
||||||
|
C = "-C " + design->scratchpad_get_string("abc9.if.C");
|
||||||
|
for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos))
|
||||||
|
abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
|
std::string R;
|
||||||
|
if (design->scratchpad.count("abc9.if.R"))
|
||||||
|
R = "-R " + design->scratchpad_get_string("abc9.if.R");
|
||||||
|
for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos))
|
||||||
|
abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
|
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str());
|
||||||
|
if (design->scratchpad_get_bool("abc9.verify")) {
|
||||||
|
if (dff_mode)
|
||||||
|
abc9_script += "; verify -s";
|
||||||
|
else
|
||||||
|
abc9_script += "; verify";
|
||||||
|
}
|
||||||
|
abc9_script += "; time";
|
||||||
|
abc9_script = add_echos_to_abc9_cmd(abc9_script);
|
||||||
|
|
||||||
|
for (size_t i = 0; i+1 < abc9_script.size(); i++)
|
||||||
|
if (abc9_script[i] == ';' && abc9_script[i+1] == ' ')
|
||||||
|
abc9_script[i+1] = '\n';
|
||||||
|
|
||||||
|
FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
|
||||||
|
fprintf(f, "%s\n", abc9_script.c_str());
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
std::string buffer;
|
||||||
|
|
||||||
|
log_header(design, "Executing ABC9.\n");
|
||||||
|
|
||||||
|
if (!lut_costs.empty()) {
|
||||||
|
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
|
||||||
|
f = fopen(buffer.c_str(), "wt");
|
||||||
|
if (f == NULL)
|
||||||
|
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
|
||||||
|
for (int i = 0; i < GetSize(lut_costs); i++)
|
||||||
|
fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
|
||||||
|
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
|
||||||
|
|
||||||
|
#ifndef YOSYS_LINK_ABC
|
||||||
|
abc9_output_filter filt(tempdir_name, show_tempdir);
|
||||||
|
int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
|
||||||
|
#else
|
||||||
|
// These needs to be mutable, supposedly due to getopt
|
||||||
|
char *abc9_argv[5];
|
||||||
|
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
|
||||||
|
abc9_argv[0] = strdup(exe_file.c_str());
|
||||||
|
abc9_argv[1] = strdup("-s");
|
||||||
|
abc9_argv[2] = strdup("-f");
|
||||||
|
abc9_argv[3] = strdup(tmp_script_name.c_str());
|
||||||
|
abc9_argv[4] = 0;
|
||||||
|
int ret = Abc_RealMain(4, abc9_argv);
|
||||||
|
free(abc9_argv[0]);
|
||||||
|
free(abc9_argv[1]);
|
||||||
|
free(abc9_argv[2]);
|
||||||
|
free(abc9_argv[3]);
|
||||||
|
#endif
|
||||||
|
if (ret != 0)
|
||||||
|
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Abc9ExePass : public Pass {
|
||||||
|
Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" abc9_exe [options]\n");
|
||||||
|
log("\n");
|
||||||
|
log(" \n");
|
||||||
|
log("This pass uses the ABC tool [1] for technology mapping of the top module\n");
|
||||||
|
log("(according to the (* top *) attribute or if only one module is currently selected)\n");
|
||||||
|
log("to a target FPGA architecture.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -exe <command>\n");
|
||||||
|
#ifdef ABCEXTERNAL
|
||||||
|
log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
|
||||||
|
#else
|
||||||
|
log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
|
||||||
|
#endif
|
||||||
|
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -script <file>\n");
|
||||||
|
log(" use the specified ABC script file instead of the default script.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
|
||||||
|
log(" string is interpreted as the command string to be passed to ABC. The\n");
|
||||||
|
log(" leading plus sign is removed and all commas (,) in the string are\n");
|
||||||
|
log(" replaced with blanks before the string is passed to ABC.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" if no -script parameter is given, the following scripts are used:\n");
|
||||||
|
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());
|
||||||
|
log("\n");
|
||||||
|
log(" -fast\n");
|
||||||
|
log(" use different default scripts that are slightly faster (at the cost\n");
|
||||||
|
log(" of output quality):\n");
|
||||||
|
log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());
|
||||||
|
log("\n");
|
||||||
|
log(" -D <picoseconds>\n");
|
||||||
|
log(" set delay target. the string {D} in the default scripts above is\n");
|
||||||
|
log(" replaced by this option when used, and an empty string otherwise\n");
|
||||||
|
log(" (indicating best possible delay).\n");
|
||||||
|
log("\n");
|
||||||
|
// log(" -S <num>\n");
|
||||||
|
// log(" maximum number of LUT inputs shared.\n");
|
||||||
|
// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
|
||||||
|
// log("\n");
|
||||||
|
log(" -lut <width>\n");
|
||||||
|
log(" generate netlist using luts of (max) the specified width.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -lut <w1>:<w2>\n");
|
||||||
|
log(" generate netlist using luts of (max) the specified width <w2>. All\n");
|
||||||
|
log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
|
||||||
|
log(" the area cost doubles with each additional input bit. the delay cost\n");
|
||||||
|
log(" is still constant for all lut widths.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -lut <file>\n");
|
||||||
|
log(" pass this file with lut library to ABC.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
|
||||||
|
log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
|
||||||
|
log(" 2, 3, .. inputs.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -showtmp\n");
|
||||||
|
log(" print the temp dir name in log. usually this is suppressed so that the\n");
|
||||||
|
log(" command output is identical across runs.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -box <file>\n");
|
||||||
|
log(" pass this file with box library to ABC.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -cwd <dir>\n");
|
||||||
|
log(" use this as the current working directory, inside which the 'input.xaig'\n");
|
||||||
|
log(" file is expected. temporary files will be created in this directory, and\n");
|
||||||
|
log(" the mapped result will be written to 'output.aig'.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
|
||||||
|
log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
|
||||||
|
log("ABC on logic snippets extracted from your design. You will not get any useful\n");
|
||||||
|
log("output when passing an ABC script that writes a file. Instead write your full\n");
|
||||||
|
log("design as BLIF file with write_blif and then load that into ABC externally if\n");
|
||||||
|
log("you want to use ABC to convert your design into another format.\n");
|
||||||
|
log("\n");
|
||||||
|
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n");
|
||||||
|
|
||||||
|
#ifdef ABCEXTERNAL
|
||||||
|
std::string exe_file = ABCEXTERNAL;
|
||||||
|
#else
|
||||||
|
std::string exe_file = proc_self_dirname() + "yosys-abc";
|
||||||
|
#endif
|
||||||
|
std::string script_file, clk_str, box_file, lut_file;
|
||||||
|
std::string delay_target, lutin_shared = "-S 1", wire_delay;
|
||||||
|
std::string tempdir_name;
|
||||||
|
bool fast_mode = false, dff_mode = false;
|
||||||
|
bool show_tempdir = false;
|
||||||
|
vector<int> lut_costs;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
cleanup = false;
|
||||||
|
show_tempdir = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef ABCEXTERNAL
|
||||||
|
if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
|
||||||
|
exe_file = proc_self_dirname() + "..\\yosys-abc";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string lut_arg, luts_arg;
|
||||||
|
exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */);
|
||||||
|
script_file = design->scratchpad_get_string("abc9.script", script_file);
|
||||||
|
if (design->scratchpad.count("abc9.D")) {
|
||||||
|
delay_target = "-D " + design->scratchpad_get_string("abc9.D");
|
||||||
|
}
|
||||||
|
lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
|
||||||
|
luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
|
||||||
|
fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);
|
||||||
|
dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
|
||||||
|
show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
|
||||||
|
box_file = design->scratchpad_get_string("abc9.box", box_file);
|
||||||
|
if (design->scratchpad.count("abc9.W")) {
|
||||||
|
wire_delay = "-W " + design->scratchpad_get_string("abc9.W");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
char pwd [PATH_MAX];
|
||||||
|
if (!getcwd(pwd, sizeof(pwd))) {
|
||||||
|
log_cmd_error("getcwd failed: %s\n", strerror(errno));
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
std::string arg = args[argidx];
|
||||||
|
if (arg == "-exe" && argidx+1 < args.size()) {
|
||||||
|
exe_file = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-script" && argidx+1 < args.size()) {
|
||||||
|
script_file = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-D" && argidx+1 < args.size()) {
|
||||||
|
delay_target = "-D " + args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//if (arg == "-S" && argidx+1 < args.size()) {
|
||||||
|
// lutin_shared = "-S " + args[++argidx];
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
if (arg == "-lut" && argidx+1 < args.size()) {
|
||||||
|
lut_arg = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-luts" && argidx+1 < args.size()) {
|
||||||
|
lut_arg = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-fast") {
|
||||||
|
fast_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-dff") {
|
||||||
|
dff_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-showtmp") {
|
||||||
|
show_tempdir = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-box" && argidx+1 < args.size()) {
|
||||||
|
box_file = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-W" && argidx+1 < args.size()) {
|
||||||
|
wire_delay = "-W " + args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-cwd" && argidx+1 < args.size()) {
|
||||||
|
tempdir_name = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
// handle -lut / -luts args
|
||||||
|
if (!lut_arg.empty()) {
|
||||||
|
string arg = lut_arg;
|
||||||
|
if (arg.find_first_not_of("0123456789:") == std::string::npos) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lut_file = arg;
|
||||||
|
rewrite_filename(lut_file);
|
||||||
|
if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+')
|
||||||
|
lut_file = std::string(pwd) + "/" + lut_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) < atoi(parts.at(0).c_str()))
|
||||||
|
lut_costs.push_back(atoi(parts.at(1).c_str()));
|
||||||
|
else
|
||||||
|
log_cmd_error("Invalid -luts syntax.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABC expects a box file for XAIG
|
||||||
|
if (box_file.empty())
|
||||||
|
box_file = "+/dummy.box";
|
||||||
|
|
||||||
|
rewrite_filename(box_file);
|
||||||
|
if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
|
||||||
|
box_file = std::string(pwd) + "/" + box_file;
|
||||||
|
|
||||||
|
if (tempdir_name.empty())
|
||||||
|
log_cmd_error("abc9_exe '-cwd' option is mandatory.\n");
|
||||||
|
|
||||||
|
|
||||||
|
abc9_module(design, script_file, exe_file, lut_costs, dff_mode,
|
||||||
|
delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||||
|
box_file, lut_file, wire_delay, tempdir_name);
|
||||||
|
}
|
||||||
|
} Abc9ExePass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
825
passes/techmap/abc9_ops.cc
Normal file
825
passes/techmap/abc9_ops.cc
Normal file
|
@ -0,0 +1,825 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/register.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
#include "kernel/utils.h"
|
||||||
|
#include "kernel/celltypes.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
int map_autoidx;
|
||||||
|
|
||||||
|
inline std::string remap_name(RTLIL::IdString abc9_name)
|
||||||
|
{
|
||||||
|
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_scc(RTLIL::Module *module)
|
||||||
|
{
|
||||||
|
// For every unique SCC found, (arbitrarily) find the first
|
||||||
|
// cell in the component, and convert all wires driven by
|
||||||
|
// its output ports into a new PO, and drive its previous
|
||||||
|
// sinks with a new PI
|
||||||
|
pool<RTLIL::Const> ids_seen;
|
||||||
|
for (auto cell : module->cells()) {
|
||||||
|
auto it = cell->attributes.find(ID(abc9_scc_id));
|
||||||
|
if (it == cell->attributes.end())
|
||||||
|
continue;
|
||||||
|
auto id = it->second;
|
||||||
|
auto r = ids_seen.insert(id);
|
||||||
|
cell->attributes.erase(it);
|
||||||
|
if (!r.second)
|
||||||
|
continue;
|
||||||
|
for (auto &c : cell->connections_) {
|
||||||
|
if (c.second.is_fully_const()) continue;
|
||||||
|
if (cell->output(c.first)) {
|
||||||
|
SigBit b = c.second.as_bit();
|
||||||
|
Wire *w = b.wire;
|
||||||
|
w->set_bool_attribute(ID::keep);
|
||||||
|
w->attributes[ID(abc9_scc_id)] = id.as_int();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module->fixup_ports();
|
||||||
|
}
|
||||||
|
|
||||||
|
void prep_dff(RTLIL::Module *module)
|
||||||
|
{
|
||||||
|
auto design = module->design;
|
||||||
|
log_assert(design);
|
||||||
|
|
||||||
|
SigMap assign_map(module);
|
||||||
|
|
||||||
|
typedef SigSpec clkdomain_t;
|
||||||
|
dict<clkdomain_t, int> clk_to_mergeability;
|
||||||
|
|
||||||
|
for (auto cell : module->cells()) {
|
||||||
|
if (cell->type != "$__ABC9_FF_")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str()));
|
||||||
|
if (abc9_clock_wire == NULL)
|
||||||
|
log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
|
||||||
|
SigSpec abc9_clock = assign_map(abc9_clock_wire);
|
||||||
|
|
||||||
|
clkdomain_t key(abc9_clock);
|
||||||
|
|
||||||
|
auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1));
|
||||||
|
auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second));
|
||||||
|
log_assert(r2.second);
|
||||||
|
|
||||||
|
Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str()));
|
||||||
|
if (abc9_init_wire == NULL)
|
||||||
|
log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
|
||||||
|
log_assert(GetSize(abc9_init_wire) == 1);
|
||||||
|
SigSpec abc9_init = assign_map(abc9_init_wire);
|
||||||
|
if (!abc9_init.is_fully_const())
|
||||||
|
log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
|
||||||
|
if (abc9_init == State::S1)
|
||||||
|
log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module));
|
||||||
|
r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const()));
|
||||||
|
log_assert(r2.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str()));
|
||||||
|
if (holes_module) {
|
||||||
|
SigMap sigmap(holes_module);
|
||||||
|
|
||||||
|
dict<SigSpec, SigSpec> replace;
|
||||||
|
for (auto cell : holes_module->cells().to_vector()) {
|
||||||
|
if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
|
||||||
|
"$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_"))
|
||||||
|
continue;
|
||||||
|
SigBit D = cell->getPort("\\D");
|
||||||
|
SigBit Q = cell->getPort("\\Q");
|
||||||
|
// Emulate async control embedded inside $_DFF_* cell with mux in front of D
|
||||||
|
if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_"))
|
||||||
|
D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R"));
|
||||||
|
else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_"))
|
||||||
|
D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R"));
|
||||||
|
else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_"))
|
||||||
|
D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R"));
|
||||||
|
else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_"))
|
||||||
|
D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\R"));
|
||||||
|
// Remove the $_DFF_* cell from what needs to be a combinatorial box
|
||||||
|
holes_module->remove(cell);
|
||||||
|
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_currQ"
|
||||||
|
// wire (which itself is driven an by 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &conn : holes_module->connections_)
|
||||||
|
conn.second = replace.at(sigmap(conn.second), conn.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void prep_xaiger(RTLIL::Module *module, bool dff)
|
||||||
|
{
|
||||||
|
auto design = module->design;
|
||||||
|
log_assert(design);
|
||||||
|
|
||||||
|
SigMap sigmap(module);
|
||||||
|
|
||||||
|
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||||
|
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
||||||
|
dict<IdString, std::vector<IdString>> box_ports;
|
||||||
|
|
||||||
|
for (auto cell : module->cells()) {
|
||||||
|
if (cell->type == "$__ABC9_FF_")
|
||||||
|
continue;
|
||||||
|
if (cell->has_keep_attr())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto inst_module = module->design->module(cell->type);
|
||||||
|
bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id");
|
||||||
|
bool abc9_flop = false;
|
||||||
|
if (abc9_box) {
|
||||||
|
abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
|
||||||
|
if (abc9_flop && !dff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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 : inst_module->ports) {
|
||||||
|
auto w = inst_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(inst_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(inst_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(inst_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(inst_module));
|
||||||
|
if (carry_in != IdString()) {
|
||||||
|
r.first->second.push_back(carry_in);
|
||||||
|
r.first->second.push_back(carry_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!yosys_celltypes.cell_known(cell->type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO: Speed up toposort -- we care about box ordering only
|
||||||
|
for (auto conn : cell->connections()) {
|
||||||
|
if (cell->input(conn.first))
|
||||||
|
for (auto bit : sigmap(conn.second))
|
||||||
|
bit_users[bit].insert(cell->name);
|
||||||
|
|
||||||
|
if (cell->output(conn.first) && !abc9_flop)
|
||||||
|
for (auto bit : sigmap(conn.second))
|
||||||
|
bit_drivers[bit].insert(cell->name);
|
||||||
|
}
|
||||||
|
toposort.node(cell->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (box_ports.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto &it : bit_users)
|
||||||
|
if (bit_drivers.count(it.first))
|
||||||
|
for (auto driver_cell : bit_drivers.at(it.first))
|
||||||
|
for (auto user_cell : it.second)
|
||||||
|
toposort.edge(driver_cell, user_cell);
|
||||||
|
|
||||||
|
if (ys_debug(1))
|
||||||
|
toposort.analyze_loops = true;
|
||||||
|
|
||||||
|
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
|
||||||
|
|
||||||
|
if (ys_debug(1)) {
|
||||||
|
unsigned i = 0;
|
||||||
|
for (auto &it : toposort.loops) {
|
||||||
|
log(" loop %d\n", i++);
|
||||||
|
for (auto cell_name : it) {
|
||||||
|
auto cell = module->cell(cell_name);
|
||||||
|
log_assert(cell);
|
||||||
|
log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_assert(no_loops);
|
||||||
|
|
||||||
|
RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str()));
|
||||||
|
log_assert(holes_module);
|
||||||
|
holes_module->set_bool_attribute("\\abc9_holes");
|
||||||
|
|
||||||
|
dict<IdString, Cell*> cell_cache;
|
||||||
|
|
||||||
|
int port_id = 1, box_count = 0;
|
||||||
|
for (auto cell_name : toposort.sorted) {
|
||||||
|
RTLIL::Cell *cell = module->cell(cell_name);
|
||||||
|
log_assert(cell);
|
||||||
|
|
||||||
|
RTLIL::Module* box_module = design->module(cell->type);
|
||||||
|
if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cell->attributes["\\abc9_box_seq"] = box_count++;
|
||||||
|
|
||||||
|
IdString derived_name = box_module->derive(design, cell->parameters);
|
||||||
|
box_module = design->module(derived_name);
|
||||||
|
|
||||||
|
auto r = cell_cache.insert(derived_name);
|
||||||
|
auto &holes_cell = r.first->second;
|
||||||
|
if (r.second) {
|
||||||
|
if (box_module->has_processes())
|
||||||
|
Pass::call_on_module(design, box_module, "proc");
|
||||||
|
|
||||||
|
if (box_module->get_bool_attribute("\\whitebox")) {
|
||||||
|
holes_cell = holes_module->addCell(cell->name, derived_name);
|
||||||
|
|
||||||
|
int box_inputs = 0;
|
||||||
|
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)
|
||||||
|
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++;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
|
||||||
|
holes_module->connect(Q, holes_wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // box_module is a blackbox
|
||||||
|
log_assert(holes_cell == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reintegrate(RTLIL::Module *module)
|
||||||
|
{
|
||||||
|
auto design = module->design;
|
||||||
|
log_assert(design);
|
||||||
|
|
||||||
|
map_autoidx = autoidx++;
|
||||||
|
|
||||||
|
RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str()));
|
||||||
|
if (mapped_mod == NULL)
|
||||||
|
log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module));
|
||||||
|
|
||||||
|
for (auto w : mapped_mod->wires())
|
||||||
|
module->addWire(remap_name(w->name), GetSize(w));
|
||||||
|
|
||||||
|
dict<IdString,std::vector<IdString>> box_ports;
|
||||||
|
|
||||||
|
for (auto m : design->modules()) {
|
||||||
|
if (!m->attributes.count(ID(abc9_box_id)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto r = box_ports.insert(m->name);
|
||||||
|
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 : m->ports) {
|
||||||
|
auto w = m->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(m));
|
||||||
|
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(m));
|
||||||
|
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(m));
|
||||||
|
if (carry_in == IdString() && carry_out != IdString())
|
||||||
|
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
|
||||||
|
if (carry_in != IdString()) {
|
||||||
|
r.first->second.push_back(carry_in);
|
||||||
|
r.first->second.push_back(carry_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Cell*> boxes;
|
||||||
|
for (auto cell : module->cells().to_vector()) {
|
||||||
|
if (cell->has_keep_attr())
|
||||||
|
continue;
|
||||||
|
if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_)))
|
||||||
|
module->remove(cell);
|
||||||
|
else if (cell->attributes.erase("\\abc9_box_seq"))
|
||||||
|
boxes.emplace_back(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||||
|
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
||||||
|
dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers;
|
||||||
|
dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
|
||||||
|
|
||||||
|
std::map<IdString, int> cell_stats;
|
||||||
|
for (auto mapped_cell : mapped_mod->cells())
|
||||||
|
{
|
||||||
|
// TODO: Speed up toposort -- we care about NOT ordering only
|
||||||
|
toposort.node(mapped_cell->name);
|
||||||
|
|
||||||
|
if (mapped_cell->type == ID($_NOT_)) {
|
||||||
|
RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
|
||||||
|
RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
|
||||||
|
bit_users[a_bit].insert(mapped_cell->name);
|
||||||
|
// Ignore inouts for topo ordering
|
||||||
|
if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output))
|
||||||
|
bit_drivers[y_bit].insert(mapped_cell->name);
|
||||||
|
|
||||||
|
if (!a_bit.wire) {
|
||||||
|
mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||||
|
RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
|
||||||
|
log_assert(wire);
|
||||||
|
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RTLIL::Cell* driver_lut = nullptr;
|
||||||
|
// ABC can return NOT gates that drive POs
|
||||||
|
if (!a_bit.wire->port_input) {
|
||||||
|
// If it's not a NOT gate that that comes from a PI directly,
|
||||||
|
// find the driver LUT and clone that to guarantee that we won't
|
||||||
|
// increase the max logic depth
|
||||||
|
// (TODO: Optimise by not cloning unless will increase depth)
|
||||||
|
RTLIL::IdString driver_name;
|
||||||
|
if (GetSize(a_bit.wire) == 1)
|
||||||
|
driver_name = stringf("$lut%s", a_bit.wire->name.c_str());
|
||||||
|
else
|
||||||
|
driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset);
|
||||||
|
driver_lut = mapped_mod->cell(driver_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!driver_lut) {
|
||||||
|
// If a driver couldn't be found (could be from PI or box CI)
|
||||||
|
// then implement using a LUT
|
||||||
|
RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())),
|
||||||
|
RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset),
|
||||||
|
RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset),
|
||||||
|
RTLIL::Const::from_string("01"));
|
||||||
|
bit2sinks[cell->getPort(ID::A)].push_back(cell);
|
||||||
|
cell_stats[ID($lut)]++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
not2drivers[mapped_cell] = driver_lut;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
|
||||||
|
// Convert buffer into direct connection
|
||||||
|
if (mapped_cell->type == ID($lut) &&
|
||||||
|
GetSize(mapped_cell->getPort(ID::A)) == 1 &&
|
||||||
|
mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) {
|
||||||
|
SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name));
|
||||||
|
SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name));
|
||||||
|
module->connect(my_y, my_a);
|
||||||
|
log_abort();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
|
||||||
|
cell->parameters = mapped_cell->parameters;
|
||||||
|
cell->attributes = mapped_cell->attributes;
|
||||||
|
|
||||||
|
for (auto &mapped_conn : mapped_cell->connections()) {
|
||||||
|
RTLIL::SigSpec newsig;
|
||||||
|
for (auto c : mapped_conn.second.chunks()) {
|
||||||
|
if (c.width == 0)
|
||||||
|
continue;
|
||||||
|
//log_assert(c.width == 1);
|
||||||
|
if (c.wire)
|
||||||
|
c.wire = module->wires_.at(remap_name(c.wire->name));
|
||||||
|
newsig.append(c);
|
||||||
|
}
|
||||||
|
cell->setPort(mapped_conn.first, newsig);
|
||||||
|
|
||||||
|
if (cell->input(mapped_conn.first)) {
|
||||||
|
for (auto i : newsig)
|
||||||
|
bit2sinks[i].push_back(cell);
|
||||||
|
for (auto i : mapped_conn.second)
|
||||||
|
bit_users[i].insert(mapped_cell->name);
|
||||||
|
}
|
||||||
|
if (cell->output(mapped_conn.first))
|
||||||
|
for (auto i : mapped_conn.second)
|
||||||
|
// Ignore inouts for topo ordering
|
||||||
|
if (i.wire && !(i.wire->port_input && i.wire->port_output))
|
||||||
|
bit_drivers[i].insert(mapped_cell->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RTLIL::Cell *existing_cell = module->cell(mapped_cell->name);
|
||||||
|
log_assert(existing_cell);
|
||||||
|
|
||||||
|
RTLIL::Module* box_module = design->module(existing_cell->type);
|
||||||
|
auto it = box_module->attributes.find(ID(abc9_box_id));
|
||||||
|
log_assert(it != box_module->attributes.end());
|
||||||
|
log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int()));
|
||||||
|
mapped_cell->type = existing_cell->type;
|
||||||
|
|
||||||
|
RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
|
||||||
|
cell->parameters = existing_cell->parameters;
|
||||||
|
cell->attributes = existing_cell->attributes;
|
||||||
|
module->swap_names(cell, existing_cell);
|
||||||
|
|
||||||
|
auto jt = mapped_cell->connections_.find("\\i");
|
||||||
|
log_assert(jt != mapped_cell->connections_.end());
|
||||||
|
SigSpec inputs = std::move(jt->second);
|
||||||
|
mapped_cell->connections_.erase(jt);
|
||||||
|
jt = mapped_cell->connections_.find("\\o");
|
||||||
|
log_assert(jt != mapped_cell->connections_.end());
|
||||||
|
SigSpec outputs = std::move(jt->second);
|
||||||
|
mapped_cell->connections_.erase(jt);
|
||||||
|
|
||||||
|
auto abc9_flop = box_module->attributes.count("\\abc9_flop");
|
||||||
|
if (!abc9_flop) {
|
||||||
|
for (const auto &i : inputs)
|
||||||
|
bit_users[i].insert(mapped_cell->name);
|
||||||
|
for (const auto &i : outputs)
|
||||||
|
// Ignore inouts for topo ordering
|
||||||
|
if (i.wire && !(i.wire->port_input && i.wire->port_output))
|
||||||
|
bit_drivers[i].insert(mapped_cell->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_count = 0, output_count = 0;
|
||||||
|
for (const auto &port_name : box_ports.at(cell->type)) {
|
||||||
|
RTLIL::Wire *w = box_module->wire(port_name);
|
||||||
|
log_assert(w);
|
||||||
|
|
||||||
|
SigSpec sig;
|
||||||
|
if (w->port_input) {
|
||||||
|
sig = inputs.extract(input_count, GetSize(w));
|
||||||
|
input_count += GetSize(w);
|
||||||
|
}
|
||||||
|
if (w->port_output) {
|
||||||
|
sig = outputs.extract(output_count, GetSize(w));
|
||||||
|
output_count += GetSize(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
SigSpec newsig;
|
||||||
|
for (auto c : sig.chunks()) {
|
||||||
|
if (c.width == 0)
|
||||||
|
continue;
|
||||||
|
//log_assert(c.width == 1);
|
||||||
|
if (c.wire)
|
||||||
|
c.wire = module->wires_.at(remap_name(c.wire->name));
|
||||||
|
newsig.append(c);
|
||||||
|
}
|
||||||
|
cell->setPort(port_name, newsig);
|
||||||
|
|
||||||
|
if (w->port_input && !abc9_flop)
|
||||||
|
for (const auto &i : newsig)
|
||||||
|
bit2sinks[i].push_back(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_stats[mapped_cell->type]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell : boxes)
|
||||||
|
module->remove(cell);
|
||||||
|
|
||||||
|
// Copy connections (and rename) from mapped_mod to module
|
||||||
|
for (auto conn : mapped_mod->connections()) {
|
||||||
|
if (!conn.first.is_fully_const()) {
|
||||||
|
auto chunks = conn.first.chunks();
|
||||||
|
for (auto &c : chunks)
|
||||||
|
c.wire = module->wires_.at(remap_name(c.wire->name));
|
||||||
|
conn.first = std::move(chunks);
|
||||||
|
}
|
||||||
|
if (!conn.second.is_fully_const()) {
|
||||||
|
auto chunks = conn.second.chunks();
|
||||||
|
for (auto &c : chunks)
|
||||||
|
if (c.wire)
|
||||||
|
c.wire = module->wires_.at(remap_name(c.wire->name));
|
||||||
|
conn.second = std::move(chunks);
|
||||||
|
}
|
||||||
|
module->connect(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &it : cell_stats)
|
||||||
|
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
|
||||||
|
int in_wires = 0, out_wires = 0;
|
||||||
|
|
||||||
|
// Stitch in mapped_mod's inputs/outputs into module
|
||||||
|
for (auto port : mapped_mod->ports) {
|
||||||
|
RTLIL::Wire *mapped_wire = mapped_mod->wire(port);
|
||||||
|
RTLIL::Wire *wire = module->wire(port);
|
||||||
|
log_assert(wire);
|
||||||
|
if (wire->attributes.erase(ID(abc9_scc_id))) {
|
||||||
|
auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep);
|
||||||
|
log_assert(r);
|
||||||
|
}
|
||||||
|
RTLIL::Wire *remap_wire = module->wire(remap_name(port));
|
||||||
|
RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire));
|
||||||
|
log_assert(GetSize(signal) >= GetSize(remap_wire));
|
||||||
|
|
||||||
|
RTLIL::SigSig conn;
|
||||||
|
if (mapped_wire->port_output) {
|
||||||
|
conn.first = signal;
|
||||||
|
conn.second = remap_wire;
|
||||||
|
out_wires++;
|
||||||
|
module->connect(conn);
|
||||||
|
}
|
||||||
|
else if (mapped_wire->port_input) {
|
||||||
|
conn.first = remap_wire;
|
||||||
|
conn.second = signal;
|
||||||
|
in_wires++;
|
||||||
|
module->connect(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABC9 will return $_NOT_ gates in its mapping (since they are
|
||||||
|
// treated as being "free"), in particular driving primary
|
||||||
|
// outputs (real primary outputs, or cells treated as blackboxes)
|
||||||
|
// or driving box inputs.
|
||||||
|
// Instead of just mapping those $_NOT_ gates into 2-input $lut-s
|
||||||
|
// at an area and delay cost, see if it is possible to push
|
||||||
|
// this $_NOT_ into the driving LUT, or into all sink LUTs.
|
||||||
|
// When this is not possible, (i.e. this signal drives two primary
|
||||||
|
// outputs, only one of which is complemented) and when the driver
|
||||||
|
// is a LUT, then clone the LUT so that it can be inverted without
|
||||||
|
// increasing depth/delay.
|
||||||
|
for (auto &it : bit_users)
|
||||||
|
if (bit_drivers.count(it.first))
|
||||||
|
for (auto driver_cell : bit_drivers.at(it.first))
|
||||||
|
for (auto user_cell : it.second)
|
||||||
|
toposort.edge(driver_cell, user_cell);
|
||||||
|
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
|
||||||
|
log_assert(no_loops);
|
||||||
|
|
||||||
|
for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
|
||||||
|
RTLIL::Cell *not_cell = mapped_mod->cell(*ii);
|
||||||
|
log_assert(not_cell);
|
||||||
|
if (not_cell->type != ID($_NOT_))
|
||||||
|
continue;
|
||||||
|
auto it = not2drivers.find(not_cell);
|
||||||
|
if (it == not2drivers.end())
|
||||||
|
continue;
|
||||||
|
RTLIL::Cell *driver_lut = it->second;
|
||||||
|
RTLIL::SigBit a_bit = not_cell->getPort(ID::A);
|
||||||
|
RTLIL::SigBit y_bit = not_cell->getPort(ID::Y);
|
||||||
|
RTLIL::Const driver_mask;
|
||||||
|
|
||||||
|
a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name));
|
||||||
|
y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name));
|
||||||
|
|
||||||
|
auto jt = bit2sinks.find(a_bit);
|
||||||
|
if (jt == bit2sinks.end())
|
||||||
|
goto clone_lut;
|
||||||
|
|
||||||
|
for (auto sink_cell : jt->second)
|
||||||
|
if (sink_cell->type != ID($lut))
|
||||||
|
goto clone_lut;
|
||||||
|
|
||||||
|
// Push downstream LUTs past inverter
|
||||||
|
for (auto sink_cell : jt->second) {
|
||||||
|
SigSpec A = sink_cell->getPort(ID::A);
|
||||||
|
RTLIL::Const mask = sink_cell->getParam(ID(LUT));
|
||||||
|
int index = 0;
|
||||||
|
for (; index < GetSize(A); index++)
|
||||||
|
if (A[index] == a_bit)
|
||||||
|
break;
|
||||||
|
log_assert(index < GetSize(A));
|
||||||
|
int i = 0;
|
||||||
|
while (i < GetSize(mask)) {
|
||||||
|
for (int j = 0; j < (1 << index); j++)
|
||||||
|
std::swap(mask[i+j], mask[i+j+(1 << index)]);
|
||||||
|
i += 1 << (index+1);
|
||||||
|
}
|
||||||
|
A[index] = y_bit;
|
||||||
|
sink_cell->setPort(ID::A, A);
|
||||||
|
sink_cell->setParam(ID(LUT), mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we have rewritten all sinks (which we know
|
||||||
|
// to be only LUTs) to be after the inverter, we can
|
||||||
|
// go ahead and clone the LUT with the expectation
|
||||||
|
// that the original driving LUT will become dangling
|
||||||
|
// and get cleaned away
|
||||||
|
clone_lut:
|
||||||
|
driver_mask = driver_lut->getParam(ID(LUT));
|
||||||
|
for (auto &b : driver_mask.bits) {
|
||||||
|
if (b == RTLIL::State::S0) b = RTLIL::State::S1;
|
||||||
|
else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
|
||||||
|
}
|
||||||
|
auto cell = module->addLut(NEW_ID,
|
||||||
|
driver_lut->getPort(ID::A),
|
||||||
|
y_bit,
|
||||||
|
driver_mask);
|
||||||
|
for (auto &bit : cell->connections_.at(ID::A)) {
|
||||||
|
bit.wire = module->wires_.at(remap_name(bit.wire->name));
|
||||||
|
bit2sinks[bit].push_back(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
|
||||||
|
log("ABC RESULTS: input signals: %8d\n", in_wires);
|
||||||
|
log("ABC RESULTS: output signals: %8d\n", out_wires);
|
||||||
|
|
||||||
|
design->remove(mapped_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Abc9OpsPass : public Pass {
|
||||||
|
Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" abc9_ops [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("This pass contains a set of supporting operations for use during ABC technology\n");
|
||||||
|
log("mapping, and is expected to be called in conjunction with other operations from\n");
|
||||||
|
log("the `abc9' script pass. Only fully-selected modules are supported.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -mark_scc\n");
|
||||||
|
log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n");
|
||||||
|
log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n");
|
||||||
|
log(" wires driven by this cell's outputs with a (* keep *) attribute in order\n");
|
||||||
|
log(" to break the SCC. this temporary attribute will be removed on -reintegrate.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -prep_xaiger\n");
|
||||||
|
log(" prepare the design for XAIGER output. this includes computing the\n");
|
||||||
|
log(" topological ordering of ABC9 boxes, as well as preparing the\n");
|
||||||
|
log(" '<module-name>$holes' module that contains the logic behaviour of ABC9\n");
|
||||||
|
log(" whiteboxes.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -dff\n");
|
||||||
|
log(" consider flop cells (those instantiating modules marked with (* abc9_flop *)\n");
|
||||||
|
log(" during -prep_xaiger.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -prep_dff\n");
|
||||||
|
log(" compute the clock domain and initial value of each flop in the design.\n");
|
||||||
|
log(" process the '$holes' module to support clock-enable functionality.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -reintegrate\n");
|
||||||
|
log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n");
|
||||||
|
log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n");
|
||||||
|
log(" inputs and outputs.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n");
|
||||||
|
|
||||||
|
bool mark_scc_mode = false;
|
||||||
|
bool prep_dff_mode = false;
|
||||||
|
bool prep_xaiger_mode = false;
|
||||||
|
bool reintegrate_mode = false;
|
||||||
|
bool dff_mode = false;
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
std::string arg = args[argidx];
|
||||||
|
if (arg == "-mark_scc") {
|
||||||
|
mark_scc_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-prep_dff") {
|
||||||
|
prep_dff_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-prep_xaiger") {
|
||||||
|
prep_xaiger_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-reintegrate") {
|
||||||
|
reintegrate_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-dff") {
|
||||||
|
dff_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode))
|
||||||
|
log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n");
|
||||||
|
|
||||||
|
if (dff_mode && !prep_xaiger_mode)
|
||||||
|
log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n");
|
||||||
|
|
||||||
|
for (auto mod : design->selected_modules()) {
|
||||||
|
if (mod->get_bool_attribute("\\abc9_holes"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (mod->processes.size() > 0) {
|
||||||
|
log("Skipping module %s as it contains processes.\n", log_id(mod));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!design->selected_whole_module(mod))
|
||||||
|
log_error("Can't handle partially selected module %s!\n", log_id(mod));
|
||||||
|
|
||||||
|
if (mark_scc_mode)
|
||||||
|
mark_scc(mod);
|
||||||
|
if (prep_dff_mode)
|
||||||
|
prep_dff(mod);
|
||||||
|
if (prep_xaiger_mode)
|
||||||
|
prep_xaiger(mod, dff_mode);
|
||||||
|
if (reintegrate_mode)
|
||||||
|
reintegrate(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} Abc9OpsPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -115,6 +115,8 @@ struct ClkbufmapPass : public Pass {
|
||||||
// Cell type, port name, bit index.
|
// Cell type, port name, bit index.
|
||||||
pool<pair<IdString, pair<IdString, int>>> sink_ports;
|
pool<pair<IdString, pair<IdString, int>>> sink_ports;
|
||||||
pool<pair<IdString, pair<IdString, int>>> buf_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.
|
// Process submodules before module using them.
|
||||||
std::vector<Module *> modules_sorted;
|
std::vector<Module *> modules_sorted;
|
||||||
|
@ -133,6 +135,14 @@ struct ClkbufmapPass : public Pass {
|
||||||
if (wire->get_bool_attribute("\\clkbuf_sink"))
|
if (wire->get_bool_attribute("\\clkbuf_sink"))
|
||||||
for (int i = 0; i < GetSize(wire); i++)
|
for (int i = 0; i < GetSize(wire); i++)
|
||||||
sink_ports.insert(make_pair(module->name, make_pair(wire->name, 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -157,6 +167,37 @@ struct ClkbufmapPass : public Pass {
|
||||||
if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
|
if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
|
||||||
buf_wire_bits.insert(sigmap(port.second[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.
|
// Collect all driven bits.
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
for (auto port : cell->connections())
|
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");
|
log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
|
||||||
|
|
||||||
std::string inpad_celltype, inpad_portname, inpad_portname2;
|
std::string inpad_celltype, inpad_portname_o, inpad_portname_pad;
|
||||||
std::string outpad_celltype, outpad_portname, outpad_portname2;
|
std::string outpad_celltype, outpad_portname_i, outpad_portname_pad;
|
||||||
std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
|
std::string inoutpad_celltype, inoutpad_portname_io, inoutpad_portname_pad;
|
||||||
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
|
std::string toutpad_celltype, toutpad_portname_oe, toutpad_portname_i, toutpad_portname_pad;
|
||||||
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
|
std::string tinoutpad_celltype, tinoutpad_portname_oe, tinoutpad_portname_o, tinoutpad_portname_i, tinoutpad_portname_pad;
|
||||||
std::string widthparam, nameparam;
|
std::string widthparam, nameparam;
|
||||||
pool<pair<IdString, IdString>> ignore;
|
pool<pair<IdString, IdString>> ignore;
|
||||||
bool flag_bits = false;
|
bool flag_bits = false;
|
||||||
|
@ -102,35 +102,35 @@ struct IopadmapPass : public Pass {
|
||||||
std::string arg = args[argidx];
|
std::string arg = args[argidx];
|
||||||
if (arg == "-inpad" && argidx+2 < args.size()) {
|
if (arg == "-inpad" && argidx+2 < args.size()) {
|
||||||
inpad_celltype = args[++argidx];
|
inpad_celltype = args[++argidx];
|
||||||
inpad_portname = args[++argidx];
|
inpad_portname_o = args[++argidx];
|
||||||
split_portname_pair(inpad_portname, inpad_portname2);
|
split_portname_pair(inpad_portname_o, inpad_portname_pad);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-outpad" && argidx+2 < args.size()) {
|
if (arg == "-outpad" && argidx+2 < args.size()) {
|
||||||
outpad_celltype = args[++argidx];
|
outpad_celltype = args[++argidx];
|
||||||
outpad_portname = args[++argidx];
|
outpad_portname_i = args[++argidx];
|
||||||
split_portname_pair(outpad_portname, outpad_portname2);
|
split_portname_pair(outpad_portname_i, outpad_portname_pad);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-inoutpad" && argidx+2 < args.size()) {
|
if (arg == "-inoutpad" && argidx+2 < args.size()) {
|
||||||
inoutpad_celltype = args[++argidx];
|
inoutpad_celltype = args[++argidx];
|
||||||
inoutpad_portname = args[++argidx];
|
inoutpad_portname_io = args[++argidx];
|
||||||
split_portname_pair(inoutpad_portname, inoutpad_portname2);
|
split_portname_pair(inoutpad_portname_io, inoutpad_portname_pad);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-toutpad" && argidx+2 < args.size()) {
|
if (arg == "-toutpad" && argidx+2 < args.size()) {
|
||||||
toutpad_celltype = args[++argidx];
|
toutpad_celltype = args[++argidx];
|
||||||
toutpad_portname = args[++argidx];
|
toutpad_portname_oe = args[++argidx];
|
||||||
split_portname_pair(toutpad_portname, toutpad_portname2);
|
split_portname_pair(toutpad_portname_oe, toutpad_portname_i);
|
||||||
split_portname_pair(toutpad_portname2, toutpad_portname3);
|
split_portname_pair(toutpad_portname_i, toutpad_portname_pad);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-tinoutpad" && argidx+2 < args.size()) {
|
if (arg == "-tinoutpad" && argidx+2 < args.size()) {
|
||||||
tinoutpad_celltype = args[++argidx];
|
tinoutpad_celltype = args[++argidx];
|
||||||
tinoutpad_portname = args[++argidx];
|
tinoutpad_portname_oe = args[++argidx];
|
||||||
split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
|
split_portname_pair(tinoutpad_portname_oe, tinoutpad_portname_o);
|
||||||
split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
|
split_portname_pair(tinoutpad_portname_o, tinoutpad_portname_i);
|
||||||
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
|
split_portname_pair(tinoutpad_portname_i, tinoutpad_portname_pad);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (arg == "-ignore" && argidx+2 < args.size()) {
|
if (arg == "-ignore" && argidx+2 < args.size()) {
|
||||||
|
@ -161,16 +161,16 @@ struct IopadmapPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
if (!inpad_portname2.empty())
|
if (!inpad_portname_pad.empty())
|
||||||
ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
|
ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname_pad)));
|
||||||
if (!outpad_portname2.empty())
|
if (!outpad_portname_pad.empty())
|
||||||
ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
|
ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname_pad)));
|
||||||
if (!inoutpad_portname2.empty())
|
if (!inoutpad_portname_pad.empty())
|
||||||
ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
|
ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname_pad)));
|
||||||
if (!toutpad_portname3.empty())
|
if (!toutpad_portname_pad.empty())
|
||||||
ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
|
ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname_pad)));
|
||||||
if (!tinoutpad_portname4.empty())
|
if (!tinoutpad_portname_pad.empty())
|
||||||
ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
|
ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname_pad)));
|
||||||
|
|
||||||
for (auto module : design->modules())
|
for (auto module : design->modules())
|
||||||
if (module->get_blackbox_attribute())
|
if (module->get_blackbox_attribute())
|
||||||
|
@ -180,148 +180,130 @@ struct IopadmapPass : public Pass {
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules())
|
||||||
{
|
{
|
||||||
dict<IdString, pool<int>> skip_wires;
|
|
||||||
pool<SigBit> skip_wire_bits;
|
pool<SigBit> skip_wire_bits;
|
||||||
SigMap sigmap(module);
|
dict<Wire *, dict<int, pair<Cell *, IdString>>> rewrite_bits;
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
for (auto port : cell->connections())
|
for (auto port : cell->connections())
|
||||||
if (ignore.count(make_pair(cell->type, port.first)))
|
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);
|
skip_wire_bits.insert(bit);
|
||||||
|
|
||||||
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
|
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
|
||||||
{
|
{
|
||||||
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
|
dict<SigBit, Cell *> tbuf_bits;
|
||||||
pool<pair<IdString, IdString>> norewrites;
|
pool<SigBit> driven_bits;
|
||||||
SigMap rewrites;
|
|
||||||
|
|
||||||
|
// Gather tristate buffers and always-on drivers.
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
if (cell->type == ID($_TBUF_)) {
|
if (cell->type == ID($_TBUF_)) {
|
||||||
SigBit bit = sigmap(cell->getPort(ID::Y).as_bit());
|
SigBit bit = cell->getPort(ID::Y).as_bit();
|
||||||
tbuf_bits[bit].first = cell->name;
|
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())
|
// If a wire is a target of an assignment, it is driven, unless the source is 'z.
|
||||||
for (auto port : cell->connections())
|
for (auto &conn : module->connections())
|
||||||
for (auto bit : sigmap(port.second))
|
for (int i = 0; i < GetSize(conn.first); i++) {
|
||||||
if (tbuf_bits.count(bit))
|
SigBit dstbit = conn.first[i];
|
||||||
tbuf_bits.at(bit).second.insert(cell->name);
|
SigBit srcbit = conn.second[i];
|
||||||
|
if (!srcbit.wire && srcbit.data == State::Sz)
|
||||||
|
continue;
|
||||||
|
driven_bits.insert(dstbit);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto wire : module->selected_wires())
|
for (auto wire : module->selected_wires())
|
||||||
{
|
{
|
||||||
if (!wire->port_output)
|
if (!wire->port_output)
|
||||||
continue;
|
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++)
|
for (int i = 0; i < GetSize(wire); i++)
|
||||||
{
|
{
|
||||||
SigBit wire_bit(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;
|
continue;
|
||||||
|
|
||||||
if (skip_wire_bits.count(mapped_wire_bit))
|
if (tbuf_bits.count(wire_bit))
|
||||||
continue;
|
tbuf_cell = tbuf_bits.at(wire_bit);
|
||||||
|
|
||||||
auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
|
SigBit en_sig;
|
||||||
Cell *tbuf_cell = module->cell(tbuf_cache.first);
|
SigBit data_sig;
|
||||||
|
bool is_driven = driven_bits.count(wire_bit);
|
||||||
|
|
||||||
if (tbuf_cell == nullptr)
|
if (tbuf_cell != nullptr) {
|
||||||
continue;
|
// 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();
|
if (wire->port_input)
|
||||||
SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit();
|
|
||||||
|
|
||||||
if (wire->port_input && !tinoutpad_celltype.empty())
|
|
||||||
{
|
{
|
||||||
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
|
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));
|
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_portname_oe), 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->attributes[ID::keep] = RTLIL::Const(1);
|
cell->attributes[ID::keep] = RTLIL::Const(1);
|
||||||
|
|
||||||
for (auto cn : tbuf_cache.second) {
|
if (tbuf_cell) {
|
||||||
auto c = module->cell(cn);
|
module->remove(tbuf_cell);
|
||||||
if (c == nullptr)
|
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
|
||||||
continue;
|
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
|
||||||
for (auto port : c->connections()) {
|
} else if (is_driven) {
|
||||||
SigSpec sig = port.second;
|
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit);
|
||||||
bool newsig = false;
|
} else {
|
||||||
for (auto &bit : sig)
|
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
|
||||||
if (sigmap(bit) == mapped_wire_bit) {
|
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
|
||||||
bit = owire;
|
|
||||||
newsig = true;
|
|
||||||
}
|
|
||||||
if (newsig)
|
|
||||||
c->setPort(port.first, sig);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
skip_wire_bits.insert(wire_bit);
|
||||||
|
if (!tinoutpad_portname_pad.empty())
|
||||||
module->remove(tbuf_cell);
|
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
|
||||||
skip_wires[wire->name].insert(i);
|
} else {
|
||||||
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
|
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 *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_portname_oe), en_sig);
|
||||||
cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
|
cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);
|
||||||
cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
|
|
||||||
cell->attributes[ID::keep] = RTLIL::Const(1);
|
cell->attributes[ID::keep] = RTLIL::Const(1);
|
||||||
|
|
||||||
for (auto cn : tbuf_cache.second) {
|
if (tbuf_cell) {
|
||||||
auto c = module->cell(cn);
|
module->remove(tbuf_cell);
|
||||||
if (c == nullptr)
|
module->connect(wire_bit, data_sig);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
skip_wire_bits.insert(wire_bit);
|
||||||
module->remove(tbuf_cell);
|
if (!toutpad_portname_pad.empty())
|
||||||
skip_wires[wire->name].insert(i);
|
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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())
|
for (auto wire : module->selected_wires())
|
||||||
|
@ -329,17 +311,11 @@ struct IopadmapPass : public Pass {
|
||||||
if (!wire->port_id)
|
if (!wire->port_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string celltype, portname, portname2;
|
std::string celltype, portname_int, portname_pad;
|
||||||
pool<int> skip_bit_indices;
|
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++)
|
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);
|
skip_bit_indices.insert(i);
|
||||||
|
|
||||||
if (GetSize(wire) == GetSize(skip_bit_indices))
|
if (GetSize(wire) == GetSize(skip_bit_indices))
|
||||||
|
@ -351,8 +327,8 @@ struct IopadmapPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
celltype = inpad_celltype;
|
celltype = inpad_celltype;
|
||||||
portname = inpad_portname;
|
portname_int = inpad_portname_o;
|
||||||
portname2 = inpad_portname2;
|
portname_pad = inpad_portname_pad;
|
||||||
} else
|
} else
|
||||||
if (!wire->port_input && wire->port_output) {
|
if (!wire->port_input && wire->port_output) {
|
||||||
if (outpad_celltype.empty()) {
|
if (outpad_celltype.empty()) {
|
||||||
|
@ -360,8 +336,8 @@ struct IopadmapPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
celltype = outpad_celltype;
|
celltype = outpad_celltype;
|
||||||
portname = outpad_portname;
|
portname_int = outpad_portname_i;
|
||||||
portname2 = outpad_portname2;
|
portname_pad = outpad_portname_pad;
|
||||||
} else
|
} else
|
||||||
if (wire->port_input && wire->port_output) {
|
if (wire->port_input && wire->port_output) {
|
||||||
if (inoutpad_celltype.empty()) {
|
if (inoutpad_celltype.empty()) {
|
||||||
|
@ -369,8 +345,8 @@ struct IopadmapPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
celltype = inoutpad_celltype;
|
celltype = inoutpad_celltype;
|
||||||
portname = inoutpad_portname;
|
portname_int = inoutpad_portname_io;
|
||||||
portname2 = inoutpad_portname2;
|
portname_pad = inoutpad_portname_pad;
|
||||||
} else
|
} else
|
||||||
log_abort();
|
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());
|
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)
|
if (flag_bits)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < wire->width; i++)
|
for (int i = 0; i < wire->width; i++)
|
||||||
{
|
{
|
||||||
if (skip_bit_indices.count(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));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
SigBit wire_bit(wire, i);
|
||||||
|
|
||||||
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
||||||
cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
|
cell->setPort(RTLIL::escape_id(portname_int), wire_bit);
|
||||||
if (!portname2.empty())
|
|
||||||
cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i));
|
if (!portname_pad.empty())
|
||||||
|
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(portname_pad));
|
||||||
if (!widthparam.empty())
|
if (!widthparam.empty())
|
||||||
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
|
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
|
||||||
if (!nameparam.empty())
|
if (!nameparam.empty())
|
||||||
|
@ -414,9 +381,15 @@ struct IopadmapPass : public Pass {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
||||||
cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire));
|
cell->setPort(RTLIL::escape_id(portname_int), RTLIL::SigSpec(wire));
|
||||||
if (!portname2.empty())
|
|
||||||
cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_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())
|
if (!widthparam.empty())
|
||||||
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
|
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
|
||||||
if (!nameparam.empty())
|
if (!nameparam.empty())
|
||||||
|
@ -424,6 +397,32 @@ struct IopadmapPass : public Pass {
|
||||||
cell->attributes[ID::keep] = RTLIL::Const(1);
|
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_id = 0;
|
||||||
wire->port_input = false;
|
wire->port_input = false;
|
||||||
wire->port_output = false;
|
wire->port_output = false;
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct TribufWorker {
|
||||||
cell->unsetPort(ID(S));
|
cell->unsetPort(ID(S));
|
||||||
cell->type = tri_type;
|
cell->type = tri_type;
|
||||||
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
||||||
|
module->design->scratchpad_set_bool("tribuf.added_something", true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ struct TribufWorker {
|
||||||
cell->unsetPort(ID(S));
|
cell->unsetPort(ID(S));
|
||||||
cell->type = tri_type;
|
cell->type = tri_type;
|
||||||
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
||||||
|
module->design->scratchpad_set_bool("tribuf.added_something", true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,8 +132,10 @@ struct TribufWorker {
|
||||||
|
|
||||||
if (no_tribuf)
|
if (no_tribuf)
|
||||||
module->connect(it.first, muxout);
|
module->connect(it.first, muxout);
|
||||||
else
|
else {
|
||||||
module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
|
module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
|
||||||
|
module->design->scratchpad_set_bool("tribuf.added_something", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ struct SynthAchronixPass : public ScriptPass {
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -152,12 +152,12 @@ struct SynthAchronixPass : public ScriptPass {
|
||||||
run("clean -purge");
|
run("clean -purge");
|
||||||
run("setundef -undriven -zero");
|
run("setundef -undriven -zero");
|
||||||
if (retime || help_mode)
|
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"))
|
if (check_label("map_luts"))
|
||||||
{
|
{
|
||||||
run("abc -lut 4" + string(retime ? " -dff" : ""));
|
run("abc -lut 4" + string(retime ? " -dff -D 1" : ""));
|
||||||
run("clean");
|
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/arith_map.v))
|
||||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.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/eagle_bb.v))
|
||||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt))
|
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams.txt))
|
||||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v))
|
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams_map.v))
|
||||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/dram_init_16x4.vh))
|
$(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_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 \$_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_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("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("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("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("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("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("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("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("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("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("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("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("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("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("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||||
|
|
||||||
module \$_DLATCH_N_ (E, D, Q);
|
module \$_DLATCH_N_ (E, D, Q);
|
||||||
wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
|
wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
|
||||||
|
|
|
@ -10,7 +10,7 @@ module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
|
||||||
input B1EN;
|
input B1EN;
|
||||||
|
|
||||||
EG_LOGIC_DRAM16X4 #(
|
EG_LOGIC_DRAM16X4 #(
|
||||||
`include "dram_init_16x4.vh"
|
`include "lutram_init_16x4.vh"
|
||||||
) _TECHMAP_REPLACE_ (
|
) _TECHMAP_REPLACE_ (
|
||||||
.di(B1DATA),
|
.di(B1DATA),
|
||||||
.waddr(B1ADDR),
|
.waddr(B1ADDR),
|
|
@ -58,7 +58,10 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -67,7 +70,7 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, edif_file, json_file;
|
string top_opt, edif_file, json_file;
|
||||||
bool flatten, retime;
|
bool flatten, retime, nolutram;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -76,6 +79,7 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
json_file = "";
|
json_file = "";
|
||||||
flatten = true;
|
flatten = true;
|
||||||
retime = false;
|
retime = false;
|
||||||
|
nolutram = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -110,6 +114,10 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
flatten = false;
|
flatten = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-nolutram") {
|
||||||
|
nolutram = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-retime") {
|
if (args[argidx] == "-retime") {
|
||||||
retime = true;
|
retime = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -150,21 +158,25 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
run("synth -run coarse");
|
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("memory_bram -rules +/anlogic/lutrams.txt");
|
||||||
run("techmap -map +/anlogic/drams_map.v");
|
run("techmap -map +/anlogic/lutrams_map.v");
|
||||||
run("setundef -zero -params t:EG_LOGIC_DRAM16X4");
|
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("opt -fast -mux_undef -undriven -fine");
|
||||||
run("memory_map");
|
run("memory_map");
|
||||||
run("opt -undriven -fine");
|
run("opt -undriven -fine");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_label("map_gates"))
|
||||||
|
{
|
||||||
run("techmap -map +/techmap.v -map +/anlogic/arith_map.v");
|
run("techmap -map +/techmap.v -map +/anlogic/arith_map.v");
|
||||||
if (retime || help_mode)
|
if (retime || help_mode)
|
||||||
run("abc -dff", "(only if -retime)");
|
run("abc -dff -D 1", "(only if -retime)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_ffs"))
|
if (check_label("map_ffs"))
|
||||||
|
@ -187,7 +199,7 @@ struct SynthAnlogicPass : public ScriptPass
|
||||||
run("techmap -map +/anlogic/cells_map.v");
|
run("techmap -map +/anlogic/cells_map.v");
|
||||||
run("clean");
|
run("clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_anlogic"))
|
if (check_label("map_anlogic"))
|
||||||
{
|
{
|
||||||
run("anlogic_fixcarry");
|
run("anlogic_fixcarry");
|
||||||
|
|
|
@ -55,7 +55,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -161,7 +161,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
||||||
|
|
||||||
if (check_label("map_pla"))
|
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");
|
run("clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,8 +194,6 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
||||||
if (!json_file.empty() || help_mode)
|
if (!json_file.empty() || help_mode)
|
||||||
run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
|
run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
log_pop();
|
|
||||||
}
|
}
|
||||||
} SynthCoolrunner2Pass;
|
} SynthCoolrunner2Pass;
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ struct SynthEasicPass : public ScriptPass
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -158,7 +158,7 @@ struct SynthEasicPass : public ScriptPass
|
||||||
run("techmap");
|
run("techmap");
|
||||||
run("opt -fast");
|
run("opt -fast");
|
||||||
if (retime || help_mode) {
|
if (retime || help_mode) {
|
||||||
run("abc -dff", " (only if -retime)");
|
run("abc -dff -D 1", " (only if -retime)");
|
||||||
run("opt_clean", "(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_sim.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_bb.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/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/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/arith_map.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))
|
||||||
|
|
|
@ -1,43 +1,36 @@
|
||||||
# NB: Inputs/Outputs must be ordered alphabetically
|
# NB: Box inputs/outputs must each be in the same order
|
||||||
# (with exceptions for carry in/out)
|
# as their corresponding module definition
|
||||||
|
# (with exceptions detailed below)
|
||||||
|
|
||||||
# Box 1 : CCU2C (2xCARRY + 2xLUT4)
|
# Box 1 : CCU2C (2xCARRY + 2xLUT4)
|
||||||
# Outputs: S0, S1, COUT
|
# (Exception: carry chain input/output must be the
|
||||||
# (NB: carry chain input/output must be last
|
# last input and output and the entire bus has been
|
||||||
# input/output and bus has been moved
|
# moved there overriding the otherwise
|
||||||
# there overriding the otherwise
|
|
||||||
# alphabetical ordering)
|
# alphabetical ordering)
|
||||||
# name ID w/b ins outs
|
# name ID w/b ins outs
|
||||||
CCU2C 1 1 9 3
|
CCU2C 1 1 9 3
|
||||||
|
#A0 B0 C0 D0 A1 B1 C1 D1 CIN
|
||||||
#A0 A1 B0 B1 C0 C1 D0 D1 CIN
|
379 379 275 141 - - - - 257 # S0
|
||||||
379 - 379 - 275 - 141 - 257
|
630 630 526 392 379 379 275 141 273 # S1
|
||||||
630 379 630 379 526 275 392 141 273
|
516 516 412 278 516 516 412 278 43 # COUT
|
||||||
516 516 516 516 412 412 278 278 43
|
|
||||||
|
|
||||||
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
||||||
# Outputs: DO0, DO1, DO2, DO3
|
|
||||||
# name ID w/b ins outs
|
# name ID w/b ins outs
|
||||||
$__ABC9_DPR16X4_COMB 2 0 8 4
|
$__ABC9_DPR16X4_COMB 2 0 8 4
|
||||||
|
#$DO0 $DO1 $DO2 $DO3 RAD0 RAD1 RAD2 RAD3
|
||||||
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
|
0 0 0 0 141 379 275 379 # DO0
|
||||||
0 0 0 0 141 379 275 379
|
0 0 0 0 141 379 275 379 # DO1
|
||||||
0 0 0 0 141 379 275 379
|
0 0 0 0 141 379 275 379 # DO2
|
||||||
0 0 0 0 141 379 275 379
|
0 0 0 0 141 379 275 379 # DO3
|
||||||
0 0 0 0 141 379 275 379
|
|
||||||
|
|
||||||
# Box 3 : PFUMX (MUX2)
|
# Box 3 : PFUMX (MUX2)
|
||||||
# Outputs: Z
|
|
||||||
# name ID w/b ins outs
|
# name ID w/b ins outs
|
||||||
PFUMX 3 1 3 1
|
PFUMX 3 1 3 1
|
||||||
|
|
||||||
#ALUT BLUT C0
|
#ALUT BLUT C0
|
||||||
98 98 151
|
98 98 151 # Z
|
||||||
|
|
||||||
# Box 4 : L6MUX21 (MUX2)
|
# Box 4 : L6MUX21 (MUX2)
|
||||||
# Outputs: Z
|
|
||||||
# name ID w/b ins outs
|
# name ID w/b ins outs
|
||||||
L6MUX21 4 1 3 1
|
L6MUX21 4 1 3 1
|
||||||
|
|
||||||
#D0 D1 SD
|
#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 (
|
module TRELLIS_DPR16X4 (
|
||||||
input [3:0] DI,
|
(* techmap_autopurge *) input [3:0] DI,
|
||||||
input [3:0] WAD,
|
(* techmap_autopurge *) input [3:0] WAD,
|
||||||
input WRE,
|
(* techmap_autopurge *) input WRE,
|
||||||
input WCK,
|
(* techmap_autopurge *) input WCK,
|
||||||
input [3:0] RAD,
|
(* techmap_autopurge *) input [3:0] RAD,
|
||||||
output [3:0] DO
|
output [3:0] DO
|
||||||
);
|
);
|
||||||
parameter WCKMUX = "WCK";
|
parameter WCKMUX = "WCK";
|
||||||
parameter WREMUX = "WRE";
|
parameter WREMUX = "WRE";
|
||||||
parameter [63:0] INITVAL = 64'h0000000000000000;
|
parameter [63:0] INITVAL = 64'h0000000000000000;
|
||||||
wire [3:0] \$DO ;
|
wire [3:0] $DO;
|
||||||
|
|
||||||
TRELLIS_DPR16X4 #(
|
TRELLIS_DPR16X4 #(
|
||||||
.WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
|
.WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
|
||||||
) _TECHMAP_REPLACE_ (
|
) _TECHMAP_REPLACE_ (
|
||||||
.DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK),
|
.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
|
endmodule
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
(* abc9_box_id=2 *)
|
(* 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
|
endmodule
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
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);
|
||||||
assign Y = A;
|
assign DO = $DO;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// Diamond flip-flops
|
// 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 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 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 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 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 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 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 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 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 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 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
|
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_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
|
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_ff.vh"
|
||||||
`include "cells_io.vh"
|
`include "cells_io.vh"
|
||||||
|
|
||||||
|
@ -58,102 +73,80 @@ module \$lut (A, Y);
|
||||||
input [WIDTH-1:0] A;
|
input [WIDTH-1:0] A;
|
||||||
output Y;
|
output Y;
|
||||||
|
|
||||||
// Need to swap input ordering, and fix init accordingly,
|
|
||||||
// to match ABC's expectation of LUT inputs in non-decreasing
|
|
||||||
// delay order
|
|
||||||
localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH;
|
|
||||||
function [P_WIDTH-1:0] permute_index;
|
|
||||||
input [P_WIDTH-1:0] i;
|
|
||||||
integer j;
|
|
||||||
begin
|
|
||||||
permute_index = 0;
|
|
||||||
for (j = 0; j < P_WIDTH; j = j + 1)
|
|
||||||
permute_index[P_WIDTH-1 - j] = i[j];
|
|
||||||
end
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function [2**P_WIDTH-1:0] permute_init;
|
|
||||||
integer i;
|
|
||||||
begin
|
|
||||||
permute_init = 0;
|
|
||||||
for (i = 0; i < 2**P_WIDTH; i = i + 1)
|
|
||||||
permute_init[i] = LUT[permute_index(i)];
|
|
||||||
end
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
parameter [2**P_WIDTH-1:0] P_LUT = permute_init();
|
|
||||||
|
|
||||||
generate
|
generate
|
||||||
if (WIDTH == 1) begin
|
if (WIDTH == 1) begin
|
||||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}};
|
||||||
|
LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||||
.A(1'b0), .B(1'b0), .C(1'b0), .D(A[0]));
|
.A(1'b0), .B(1'b0), .C(1'b0), .D(A[0]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 2) begin
|
if (WIDTH == 2) begin
|
||||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}};
|
||||||
.A(1'b0), .B(1'b0), .C(A[1]), .D(A[0]));
|
LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||||
|
.A(1'b0), .B(1'b0), .C(A[0]), .D(A[1]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 3) begin
|
if (WIDTH == 3) begin
|
||||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}};
|
||||||
.A(1'b0), .B(A[2]), .C(A[1]), .D(A[0]));
|
LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||||
|
.A(1'b0), .B(A[0]), .C(A[1]), .D(A[2]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 4) begin
|
if (WIDTH == 4) begin
|
||||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||||
.A(A[3]), .B(A[2]), .C(A[1]), .D(A[0]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
`ifndef NO_PFUMUX
|
`ifndef NO_PFUMUX
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 5) begin
|
if (WIDTH == 5) begin
|
||||||
wire f0, f1;
|
wire f0, f1;
|
||||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||||
.A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||||
.A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y));
|
PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 6) begin
|
if (WIDTH == 6) begin
|
||||||
wire f0, f1, f2, f3, g0, g1;
|
wire f0, f1, f2, f3, g0, g1;
|
||||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
|
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
|
||||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
|
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
|
||||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0));
|
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
|
||||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1));
|
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
|
||||||
L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y));
|
L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 7) begin
|
if (WIDTH == 7) begin
|
||||||
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
|
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
|
||||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
|
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
|
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4),
|
LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5),
|
LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6),
|
LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7),
|
LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7),
|
||||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||||
|
|
||||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0));
|
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
|
||||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1));
|
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
|
||||||
PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2));
|
PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2));
|
||||||
PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3));
|
PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3));
|
||||||
L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0));
|
L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0));
|
||||||
L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1));
|
L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1));
|
||||||
L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y));
|
L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y));
|
||||||
`endif
|
`endif
|
||||||
end else begin
|
end else begin
|
||||||
wire _TECHMAP_FAIL_ = 1;
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
|
(* lib_whitebox *)
|
||||||
module LUT4(input A, B, C, D, output Z);
|
module LUT4(input A, B, C, D, output Z);
|
||||||
parameter [15:0] INIT = 16'h0000;
|
parameter [15:0] INIT = 16'h0000;
|
||||||
wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0];
|
wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0];
|
||||||
|
@ -31,13 +32,8 @@ module CCU2C(
|
||||||
|
|
||||||
// First half
|
// First half
|
||||||
wire LUT4_0, LUT2_0;
|
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));
|
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));
|
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;
|
wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
|
||||||
assign S0 = LUT4_0 ^ gated_cin_0;
|
assign S0 = LUT4_0 ^ gated_cin_0;
|
||||||
|
|
||||||
|
@ -46,13 +42,8 @@ module CCU2C(
|
||||||
|
|
||||||
// Second half
|
// Second half
|
||||||
wire LUT4_1, LUT2_1;
|
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));
|
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));
|
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;
|
wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
|
||||||
assign S1 = LUT4_1 ^ gated_cin_1;
|
assign S1 = LUT4_1 ^ gated_cin_1;
|
||||||
|
|
||||||
|
@ -209,6 +200,7 @@ endmodule
|
||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
|
(* lib_whitebox *)
|
||||||
module LUT2(input A, B, output Z);
|
module LUT2(input A, B, output Z);
|
||||||
parameter [3:0] INIT = 4'h0;
|
parameter [3:0] INIT = 4'h0;
|
||||||
wire [1:0] s1 = B ? INIT[ 3:2] : INIT[1:0];
|
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(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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(" -noccu2\n");
|
log(" -noccu2\n");
|
||||||
log(" do not use CCU2 cells in output netlist\n");
|
log(" do not use CCU2 cells in output netlist\n");
|
||||||
|
@ -79,6 +79,9 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
log(" -nowidelut\n");
|
log(" -nowidelut\n");
|
||||||
log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
|
log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -asyncprld\n");
|
||||||
|
log(" use async PRLD mode to implement DLATCH and DFFSR (EXPERIMENTAL)\n");
|
||||||
|
log("\n");
|
||||||
log(" -abc2\n");
|
log(" -abc2\n");
|
||||||
log(" run two passes of 'abc' for slightly improved logic density\n");
|
log(" run two passes of 'abc' for slightly improved logic density\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -99,7 +102,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, blif_file, edif_file, json_file;
|
string top_opt, blif_file, edif_file, json_file;
|
||||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;
|
bool noccu2, nodffe, nobram, nolutram, nowidelut, asyncprld, flatten, retime, abc2, abc9, nodsp, vpr;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -112,6 +115,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
nobram = false;
|
nobram = false;
|
||||||
nolutram = false;
|
nolutram = false;
|
||||||
nowidelut = false;
|
nowidelut = false;
|
||||||
|
asyncprld = false;
|
||||||
flatten = true;
|
flatten = true;
|
||||||
retime = false;
|
retime = false;
|
||||||
abc2 = false;
|
abc2 = false;
|
||||||
|
@ -176,6 +180,10 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
nobram = true;
|
nobram = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-asyncprld") {
|
||||||
|
asyncprld = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
|
if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
|
||||||
nolutram = true;
|
nolutram = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -222,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
{
|
{
|
||||||
if (check_label("begin"))
|
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()));
|
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)"))
|
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");
|
run("techmap -map +/ecp5/brams_map.v");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
|
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");
|
run("techmap -map +/ecp5/lutrams_map.v");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +290,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
else
|
else
|
||||||
run("techmap -map +/techmap.v -map +/ecp5/arith_map.v");
|
run("techmap -map +/techmap.v -map +/ecp5/arith_map.v");
|
||||||
if (retime || help_mode)
|
if (retime || help_mode)
|
||||||
run("abc -dff", "(only if -retime)");
|
run("abc -dff -D 1", "(only if -retime)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_ffs"))
|
if (check_label("map_ffs"))
|
||||||
|
@ -292,7 +300,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
if (!nodffe)
|
if (!nodffe)
|
||||||
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
|
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("opt_expr -undriven -mux_undef");
|
||||||
run("simplemap");
|
run("simplemap");
|
||||||
run("ecp5_ffinit");
|
run("ecp5_ffinit");
|
||||||
|
@ -306,17 +314,18 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
if (abc2 || help_mode) {
|
if (abc2 || help_mode) {
|
||||||
run("abc", " (only if -abc2)");
|
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)
|
if (abc9)
|
||||||
techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1";
|
techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1";
|
||||||
run("techmap " + techmap_args);
|
if (!asyncprld || abc9)
|
||||||
|
run("techmap " + techmap_args);
|
||||||
|
|
||||||
if (abc9) {
|
if (abc9) {
|
||||||
run("read_verilog -icells -lib +/ecp5/abc9_model.v");
|
run("read_verilog -icells -lib +/ecp5/abc9_model.v");
|
||||||
if (nowidelut)
|
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
|
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");
|
run("techmap -map +/ecp5/abc9_unmap.v");
|
||||||
} else {
|
} else {
|
||||||
if (nowidelut)
|
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/arith_map.v))
|
||||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_sim.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/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(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -67,7 +70,7 @@ struct SynthEfinixPass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, edif_file, json_file;
|
string top_opt, edif_file, json_file;
|
||||||
bool flatten, retime;
|
bool flatten, retime, nobram;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -76,6 +79,7 @@ struct SynthEfinixPass : public ScriptPass
|
||||||
json_file = "";
|
json_file = "";
|
||||||
flatten = true;
|
flatten = true;
|
||||||
retime = false;
|
retime = false;
|
||||||
|
nobram = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -114,6 +118,10 @@ struct SynthEfinixPass : public ScriptPass
|
||||||
retime = true;
|
retime = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-nobram") {
|
||||||
|
nobram = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -150,21 +158,25 @@ struct SynthEfinixPass : public ScriptPass
|
||||||
run("synth -run coarse");
|
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("techmap -map +/efinix/brams_map.v");
|
||||||
run("setundef -zero -params t:EFX_RAM_5K");
|
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("opt -fast -mux_undef -undriven -fine");
|
||||||
run("memory_map");
|
run("memory_map");
|
||||||
run("opt -undriven -fine");
|
run("opt -undriven -fine");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_label("map_gates"))
|
||||||
|
{
|
||||||
run("techmap -map +/techmap.v -map +/efinix/arith_map.v");
|
run("techmap -map +/techmap.v -map +/efinix/arith_map.v");
|
||||||
if (retime || help_mode)
|
if (retime || help_mode)
|
||||||
run("abc -dff", "(only if -retime)");
|
run("abc -dff -D 1", "(only if -retime)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_ffs"))
|
if (check_label("map_ffs"))
|
||||||
|
@ -194,7 +206,7 @@ struct SynthEfinixPass : public ScriptPass
|
||||||
run("efinix_fixcarry");
|
run("efinix_fixcarry");
|
||||||
run("clean");
|
run("clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("check"))
|
if (check_label("check"))
|
||||||
{
|
{
|
||||||
run("hierarchy -check");
|
run("hierarchy -check");
|
||||||
|
|
|
@ -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/cells_sim.v))
|
||||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.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/brams_map.v))
|
||||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/bram.txt))
|
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams.txt))
|
||||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/drams_map.v))
|
$(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams_map.v))
|
||||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
|
$(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
|
//All DFF* have INIT, but the hardware is always initialised to the reset
|
||||||
//value regardless. The parameter is ignored.
|
//value regardless. The parameter is ignored.
|
||||||
|
|
||||||
// DFFN D Flip-Flop with Negative-Edge Clock
|
// 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
|
module \$_DFF_N_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
|
||||||
// DFF D Flip-Flop
|
generate
|
||||||
module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
|
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
|
// DFF D Flip-Flop
|
||||||
module \$_DFFE_PP_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E)); endmodule
|
module \$_DFF_P_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
|
||||||
module \$_DFFE_PN_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
|
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
|
// DFFE D Flip-Flop with 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_PP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||||
module \$_DFFE_NN_ (input D, C, E, output Q); DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
|
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 \$_DFFE_PN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||||
module \$__DFFS_PN0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
|
generate
|
||||||
module \$__DFFS_PP0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
|
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
|
// DFFNE D Flip-Flop with Negative-Edge Clock and Clock Enable
|
||||||
module \$__DFFS_NN0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
|
module \$_DFFE_NP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||||
module \$__DFFS_NP0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
|
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 \$_DFFE_NN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||||
module \$__DFFSE_PN0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E)); endmodule
|
generate
|
||||||
module \$__DFFSE_PP0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E)); endmodule
|
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
|
// DFFR D Flip-Flop with 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 \$__DFFS_PN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
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 _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_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
module \$__DFFS_PN1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
|
DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R));
|
||||||
module \$__DFFS_PP1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
|
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
// DFFNS D Flip-Flop with Negative-Edge Clock and Synchronous Set
|
// DFFNR D Flip-Flop with Negative-Edge Clock and Synchronous Reset
|
||||||
module \$__DFFS_NN1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
|
module \$__DFFS_NN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
module \$__DFFS_NP1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
|
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
|
// DFFRE D Flip-Flop with Clock Enable and Synchronous Reset
|
||||||
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_PN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||||
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 _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
|
// DFFNRE D Flip-Flop with Negative-Edge Clock,Clock Enable, and Synchronous Reset
|
||||||
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_NN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||||
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 _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
|
// DFFS D Flip-Flop with Synchronous Set
|
||||||
module \$_DFF_PP1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
|
module \$__DFFS_PN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
module \$_DFF_PN1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
|
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
|
// DFFNS D Flip-Flop with Negative-Edge Clock and Synchronous Set
|
||||||
module \$_DFF_NP1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
|
module \$__DFFS_NN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
module \$_DFF_NN1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
|
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
|
// DFFSE D Flip-Flop with Clock Enable and Synchronous Set
|
||||||
module \$_DFF_PP0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
|
module \$__DFFSE_PN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||||
module \$_DFF_PN0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
|
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
|
// DFFNSE D Flip-Flop with Negative-Edge Clock,Clock Enable,and Synchronous Set
|
||||||
module \$_DFF_NP0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
|
module \$__DFFSE_NN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||||
module \$_DFF_NN0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
|
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
|
// DFFP D Flip-Flop with 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 \$_DFF_PP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
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 _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
|
// DFFNP D Flip-Flop with Negative-Edge Clock 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 \$_DFF_NP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
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 _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
|
// DFFC D Flip-Flop with 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 \$_DFF_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
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 _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
|
// DFFNC D Flip-Flop with Negative-Edge Clock 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 \$_DFF_NP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||||
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 _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);
|
module \$lut (A, Y);
|
||||||
parameter WIDTH = 0;
|
parameter WIDTH = 0;
|
||||||
parameter LUT = 0;
|
parameter LUT = 0;
|
||||||
|
|
||||||
input [WIDTH-1:0] A;
|
input [WIDTH-1:0] A;
|
||||||
output Y;
|
output Y;
|
||||||
|
|
||||||
generate
|
generate
|
||||||
if (WIDTH == 1) begin
|
if (WIDTH == 1) begin
|
||||||
LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||||
.I0(A[0]));
|
.I0(A[0]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 2) begin
|
if (WIDTH == 2) begin
|
||||||
LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||||
.I0(A[0]), .I1(A[1]));
|
.I0(A[0]), .I1(A[1]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 3) begin
|
if (WIDTH == 3) begin
|
||||||
LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||||
.I0(A[0]), .I1(A[1]), .I2(A[2]));
|
.I0(A[0]), .I1(A[1]), .I2(A[2]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 4) begin
|
if (WIDTH == 4) begin
|
||||||
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||||
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 5) begin
|
if (WIDTH == 5) begin
|
||||||
wire f0, f1;
|
wire f0, f1;
|
||||||
\$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0));
|
\$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));
|
\$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));
|
MUX2_LUT5 mux5(.I0(f0), .I1(f1), .S0(A[4]), .O(Y));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 6) begin
|
if (WIDTH == 6) begin
|
||||||
wire f0, f1;
|
wire f0, f1;
|
||||||
\$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0));
|
\$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));
|
\$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));
|
MUX2_LUT6 mux6(.I0(f0), .I1(f1), .S0(A[5]), .O(Y));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 7) begin
|
if (WIDTH == 7) begin
|
||||||
wire f0, f1;
|
wire f0, f1;
|
||||||
\$lut #(.LUT(LUT[63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
|
\$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));
|
\$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));
|
MUX2_LUT7 mux7(.I0(f0), .I1(f1), .S0(A[6]), .O(Y));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 8) begin
|
if (WIDTH == 8) begin
|
||||||
wire f0, f1;
|
wire f0, f1;
|
||||||
\$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
|
\$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));
|
\$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));
|
MUX2_LUT8 mux8(.I0(f0), .I1(f1), .S0(A[7]), .O(Y));
|
||||||
end else begin
|
end else begin
|
||||||
wire _TECHMAP_FAIL_ = 1;
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
end
|
end
|
||||||
endgenerate
|
endgenerate
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
@ -55,20 +55,23 @@ struct SynthGowinPass : public ScriptPass
|
||||||
log(" -nobram\n");
|
log(" -nobram\n");
|
||||||
log(" do not use BRAM cells in output netlist\n");
|
log(" do not use BRAM cells in output netlist\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -nodram\n");
|
log(" -nolutram\n");
|
||||||
log(" do not use distributed RAM cells in output netlist\n");
|
log(" do not use distributed RAM cells in output netlist\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -noflatten\n");
|
log(" -noflatten\n");
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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(" -nowidelut\n");
|
log(" -nowidelut\n");
|
||||||
log(" do not use muxes to implement LUTs larger than LUT4s\n");
|
log(" do not use muxes to implement LUTs larger than LUT4s\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -abc9\n");
|
log(" -noiopads\n");
|
||||||
log(" use new ABC9 flow (EXPERIMENTAL)\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -77,7 +80,7 @@ struct SynthGowinPass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, vout_file;
|
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
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -87,9 +90,10 @@ struct SynthGowinPass : public ScriptPass
|
||||||
flatten = true;
|
flatten = true;
|
||||||
nobram = false;
|
nobram = false;
|
||||||
nodffe = false;
|
nodffe = false;
|
||||||
nodram = false;
|
nolutram = false;
|
||||||
nowidelut = false;
|
nowidelut = false;
|
||||||
abc9 = false;
|
abc9 = false;
|
||||||
|
noiopads = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -124,8 +128,8 @@ struct SynthGowinPass : public ScriptPass
|
||||||
nobram = true;
|
nobram = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (args[argidx] == "-nodram") {
|
if (args[argidx] == "-nolutram" || /*deprecated*/args[argidx] == "-nodram") {
|
||||||
nodram = true;
|
nolutram = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (args[argidx] == "-nodffe") {
|
if (args[argidx] == "-nodffe") {
|
||||||
|
@ -140,8 +144,12 @@ struct SynthGowinPass : public ScriptPass
|
||||||
nowidelut = true;
|
nowidelut = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (args[argidx] == "-abc9") {
|
//if (args[argidx] == "-abc9") {
|
||||||
abc9 = true;
|
// abc9 = true;
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
if (args[argidx] == "-noiopads") {
|
||||||
|
noiopads = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -180,35 +188,39 @@ struct SynthGowinPass : public ScriptPass
|
||||||
run("synth -run coarse");
|
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");
|
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("memory_bram -rules +/gowin/lutrams.txt");
|
||||||
run("techmap -map +/gowin/drams_map.v");
|
run("techmap -map +/gowin/lutrams_map.v");
|
||||||
run("determine_init");
|
run("determine_init");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("fine"))
|
if (check_label("map_ffram"))
|
||||||
{
|
{
|
||||||
run("opt -fast -mux_undef -undriven -fine");
|
run("opt -fast -mux_undef -undriven -fine");
|
||||||
run("memory_map");
|
run("memory_map");
|
||||||
run("opt -undriven -fine");
|
run("opt -undriven -fine");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_label("map_gates"))
|
||||||
|
{
|
||||||
run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
|
run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
|
||||||
run("techmap -map +/techmap.v");
|
run("techmap -map +/techmap.v");
|
||||||
if (retime || help_mode)
|
if (retime || help_mode)
|
||||||
run("abc -dff", "(only if -retime)");
|
run("abc -dff -D 1", "(only if -retime)");
|
||||||
run("splitnets");
|
run("splitnets");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_ffs"))
|
if (check_label("map_ffs"))
|
||||||
{
|
{
|
||||||
run("dffsr2dff");
|
run("dffsr2dff");
|
||||||
run("dff2dffs");
|
run("dff2dffs -match-init");
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
if (!nodffe)
|
if (!nodffe)
|
||||||
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
|
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
|
||||||
|
@ -219,13 +231,13 @@ struct SynthGowinPass : public ScriptPass
|
||||||
|
|
||||||
if (check_label("map_luts"))
|
if (check_label("map_luts"))
|
||||||
{
|
{
|
||||||
if (nowidelut && abc9) {
|
/*if (nowidelut && abc9) {
|
||||||
run("abc9 -lut 4");
|
run("abc9 -lut 4");
|
||||||
} else if (nowidelut && !abc9) {
|
} else*/ if (nowidelut && !abc9) {
|
||||||
run("abc -lut 4");
|
run("abc -lut 4");
|
||||||
} else if (!nowidelut && abc9) {
|
} else /*if (!nowidelut && abc9) {
|
||||||
run("abc9 -lut 4:8");
|
run("abc9 -lut 4:8");
|
||||||
} else if (!nowidelut && !abc9) {
|
} else*/ if (!nowidelut && !abc9) {
|
||||||
run("abc -lut 4:8");
|
run("abc -lut 4:8");
|
||||||
}
|
}
|
||||||
run("clean");
|
run("clean");
|
||||||
|
@ -236,10 +248,10 @@ struct SynthGowinPass : public ScriptPass
|
||||||
run("techmap -map +/gowin/cells_map.v");
|
run("techmap -map +/gowin/cells_map.v");
|
||||||
run("setundef -undriven -params -zero");
|
run("setundef -undriven -params -zero");
|
||||||
run("hilomap -singleton -hicell VCC V -locell GND G");
|
run("hilomap -singleton -hicell VCC V -locell GND G");
|
||||||
run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O "
|
if (!noiopads || help_mode)
|
||||||
"-toutpad TBUF OEN:I:O -tinoutpad IOBUF OEN:O:I:IO", "(unless -noiopads)");
|
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");
|
run("clean");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("check"))
|
if (check_label("check"))
|
||||||
|
|
|
@ -59,7 +59,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
|
||||||
log(" do not flatten design before synthesis\n");
|
log(" do not flatten design before synthesis\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -retime\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("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
@ -165,7 +165,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
|
||||||
run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
|
run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
|
||||||
run("opt -fast");
|
run("opt -fast");
|
||||||
if (retime || help_mode)
|
if (retime || help_mode)
|
||||||
run("abc -dff", "(only if -retime)");
|
run("abc -dff -D 1", "(only if -retime)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_luts"))
|
if (check_label("map_luts"))
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
|
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
|
||||||
|
|
||||||
# NB: Inputs/Outputs must be ordered alphabetically
|
# NB: Box inputs/outputs must each be in the same order
|
||||||
# (with exceptions for carry in/out)
|
# as their corresponding module definition
|
||||||
|
# (with exceptions detailed below)
|
||||||
|
|
||||||
# Inputs: A B I0 I3 CI
|
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||||
# Outputs: O CO
|
# SB_LUT4+SB_CARRY)
|
||||||
# (NB: carry chain input/output must be last
|
# (Exception: carry chain input/output must be the
|
||||||
# input/output and have been moved there
|
# last input and output and the entire bus has been
|
||||||
# overriding the alphabetical ordering)
|
# moved there overriding the otherwise
|
||||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
# alphabetical ordering)
|
||||||
400 379 449 316 316
|
# name ID w/b ins outs
|
||||||
259 231 - - 126
|
$__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
|
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
|
||||||
|
|
||||||
# NB: Inputs/Outputs must be ordered alphabetically
|
# NB: Box inputs/outputs must each be in the same order
|
||||||
# (with exceptions for carry in/out)
|
# as their corresponding module definition
|
||||||
|
# (with exceptions detailed below)
|
||||||
|
|
||||||
# Inputs: A B I0 I3 CI
|
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||||
# Outputs: O CO
|
# SB_LUT4+SB_CARRY)
|
||||||
# (NB: carry chain input/output must be last
|
# (Exception: carry chain input/output must be the
|
||||||
# input/output and have been moved there
|
# last input and output and the entire bus has been
|
||||||
# overriding the alphabetical ordering)
|
# moved there overriding the otherwise
|
||||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
# alphabetical ordering)
|
||||||
589 558 661 465 465
|
# name ID w/b ins outs
|
||||||
675 609 - - 186
|
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||||
|
#A B I0 I3 CI
|
||||||
|
589 558 661 465 465 # O
|
||||||
|
675 609 - - 186 # CO
|
||||||
|
|
|
@ -9,6 +9,8 @@ module \$__ICE40_CARRY_WRAPPER (
|
||||||
input I0, I3
|
input I0, I3
|
||||||
);
|
);
|
||||||
parameter LUT = 0;
|
parameter LUT = 0;
|
||||||
|
parameter I3_IS_CI = 0;
|
||||||
|
wire I3_OR_CI = I3_IS_CI ? CI : I3;
|
||||||
SB_CARRY carry (
|
SB_CARRY carry (
|
||||||
.I0(A),
|
.I0(A),
|
||||||
.I1(B),
|
.I1(B),
|
||||||
|
@ -21,7 +23,7 @@ module \$__ICE40_CARRY_WRAPPER (
|
||||||
.I0(I0),
|
.I0(I0),
|
||||||
.I1(A),
|
.I1(A),
|
||||||
.I2(B),
|
.I2(B),
|
||||||
.I3(I3),
|
.I3(I3_OR_CI),
|
||||||
.O(O)
|
.O(O)
|
||||||
);
|
);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
|
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
|
||||||
|
|
||||||
# NB: Inputs/Outputs must be ordered alphabetically
|
# NB: Box inputs/outputs must each be in the same order
|
||||||
# (with exceptions for carry in/out)
|
# as their corresponding module definition
|
||||||
|
# (with exceptions detailed below)
|
||||||
|
|
||||||
# Inputs: A B I0 I3 CI
|
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||||
# Outputs: O CO
|
# SB_LUT4+SB_CARRY)
|
||||||
# (NB: carry chain input/output must be last
|
# (Exception: carry chain input/output must be the
|
||||||
# input/output and have been moved there
|
# last input and output and the entire bus has been
|
||||||
# overriding the alphabetical ordering)
|
# moved there overriding the otherwise
|
||||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
# alphabetical ordering)
|
||||||
1231 1205 1285 874 874
|
# name ID w/b ins outs
|
||||||
675 609 - - 278
|
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||||
|
#A B I0 I3 CI
|
||||||
|
1231 1205 1285 874 874 # O
|
||||||
|
675 609 - - 278 # CO
|
||||||
|
|
|
@ -49,13 +49,14 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
|
||||||
// A[1]: 1100 1100 1100 1100
|
// A[1]: 1100 1100 1100 1100
|
||||||
// A[2]: 1111 0000 1111 0000
|
// A[2]: 1111 0000 1111 0000
|
||||||
// A[3]: 1111 1111 0000 0000
|
// A[3]: 1111 1111 0000 0000
|
||||||
.LUT(16'b 0110_1001_1001_0110)
|
.LUT(16'b 0110_1001_1001_0110),
|
||||||
) fadd (
|
.I3_IS_CI(1'b1)
|
||||||
|
) carry (
|
||||||
.A(AA[i]),
|
.A(AA[i]),
|
||||||
.B(BB[i]),
|
.B(BB[i]),
|
||||||
.CI(C[i]),
|
.CI(C[i]),
|
||||||
.I0(1'b0),
|
.I0(1'b0),
|
||||||
.I3(C[i]),
|
.I3(1'bx),
|
||||||
.CO(CO[i]),
|
.CO(CO[i]),
|
||||||
.O(Y[i])
|
.O(Y[i])
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,41 +42,21 @@ module \$lut (A, Y);
|
||||||
.I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
|
.I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 2) begin
|
if (WIDTH == 2) begin
|
||||||
localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}};
|
localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}};
|
||||||
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
||||||
.I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0]));
|
.I0(1'b0), .I1(1'b0), .I2(A[0]), .I3(A[1]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 3) begin
|
if (WIDTH == 3) begin
|
||||||
localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}};
|
localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}};
|
||||||
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
||||||
.I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0]));
|
.I0(1'b0), .I1(A[0]), .I2(A[1]), .I3(A[2]));
|
||||||
end else
|
end else
|
||||||
if (WIDTH == 4) begin
|
if (WIDTH == 4) begin
|
||||||
localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]};
|
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
|
||||||
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
||||||
.I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0]));
|
|
||||||
end else begin
|
end else begin
|
||||||
wire _TECHMAP_FAIL_ = 1;
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
end
|
end
|
||||||
endgenerate
|
endgenerate
|
||||||
endmodule
|
endmodule
|
||||||
`endif
|
`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
|
|
||||||
|
|
|
@ -1126,6 +1126,7 @@ module SB_SPRAM256KA (
|
||||||
input [15:0] DATAIN,
|
input [15:0] DATAIN,
|
||||||
input [3:0] MASKWREN,
|
input [3:0] MASKWREN,
|
||||||
input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF,
|
input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF,
|
||||||
|
`ABC9_ARRIVAL_U(1821) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13207
|
||||||
output reg [15:0] DATAOUT
|
output reg [15:0] DATAOUT
|
||||||
);
|
);
|
||||||
`ifndef BLACKBOX
|
`ifndef BLACKBOX
|
||||||
|
|
|
@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (initbits.count(bit)) {
|
if (initbits.count(bit)) {
|
||||||
if (initbits.at(bit) != val)
|
if (initbits.at(bit) != val) {
|
||||||
log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
|
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(bit), log_signal(SigBit(wire, i)), log_signal(val),
|
||||||
log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
|
log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
|
||||||
|
initbits.at(bit) = State::Sx;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
State val = initbits.at(bit_q);
|
State val = initbits.at(bit_q);
|
||||||
|
|
||||||
|
if (val == State::Sx)
|
||||||
|
continue;
|
||||||
|
|
||||||
handled_initbits.insert(bit_q);
|
handled_initbits.insert(bit_q);
|
||||||
|
|
||||||
log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type),
|
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())
|
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")
|
if (cell->type == "\\SB_LUT4")
|
||||||
{
|
{
|
||||||
sb_lut_cells.push_back(cell);
|
sb_lut_cells.push_back(cell);
|
||||||
|
@ -112,12 +117,30 @@ static void run_ice40_opts(Module *module)
|
||||||
|
|
||||||
if (GetSize(replacement_output)) {
|
if (GetSize(replacement_output)) {
|
||||||
optimized_co.insert(sigmap(cell->getPort("\\CO")[0]));
|
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->connect(cell->getPort("\\CO")[0], replacement_output);
|
||||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
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",
|
log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
|
||||||
log_id(module), log_id(cell), log_signal(replacement_output));
|
log_id(module), log_id(cell), log_signal(replacement_output));
|
||||||
cell->type = "$lut";
|
cell->type = "$lut";
|
||||||
cell->setPort("\\A", { cell->getPort("\\I0"), inbit[0], inbit[1], cell->getPort("\\I3") });
|
auto I3 = get_bit_or_zero(cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3)));
|
||||||
|
cell->setPort("\\A", { I3, inbit[1], inbit[0], get_bit_or_zero(cell->getPort("\\I0")) });
|
||||||
cell->setPort("\\Y", cell->getPort("\\O"));
|
cell->setPort("\\Y", cell->getPort("\\O"));
|
||||||
cell->unsetPort("\\B");
|
cell->unsetPort("\\B");
|
||||||
cell->unsetPort("\\CI");
|
cell->unsetPort("\\CI");
|
||||||
|
@ -126,6 +149,7 @@ static void run_ice40_opts(Module *module)
|
||||||
cell->unsetPort("\\CO");
|
cell->unsetPort("\\CO");
|
||||||
cell->unsetPort("\\O");
|
cell->unsetPort("\\O");
|
||||||
cell->setParam("\\WIDTH", 4);
|
cell->setParam("\\WIDTH", 4);
|
||||||
|
cell->unsetParam("\\I3_IS_CI");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue