mirror of https://github.com/YosysHQ/yosys.git
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_ice40 -dsp" to infer DSP blocks
|
||||
- Added latch support to synth_xilinx
|
||||
- Added support for flip-flops with synchronous reset to synth_xilinx
|
||||
- Added support for flip-flops with reset and enable to synth_xilinx
|
||||
- Added "check -mapped"
|
||||
- Added checking of SystemVerilog always block types (always_comb,
|
||||
always_latch and always_ff)
|
||||
- Added "xilinx_dffopt" pass
|
||||
- Added "scratchpad" pass
|
||||
- Added "abc9 -dff"
|
||||
- Added "synth_xilinx -dff"
|
||||
|
||||
Yosys 0.8 .. Yosys 0.9
|
||||
----------------------
|
||||
|
|
46
Makefile
46
Makefile
|
@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
|
|||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.9+932
|
||||
YOSYS_VER := 0.9+1706
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
@ -128,7 +128,7 @@ bumpversion:
|
|||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||
# will remove the 'abc' directory and you do not want to accidentally
|
||||
# delete your work on ABC..
|
||||
ABCREV = 623b5e8
|
||||
ABCREV = 71f2b40
|
||||
ABCPULL = 1
|
||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||
|
@ -152,7 +152,15 @@ ifeq ($(ENABLE_PYOSYS),1)
|
|||
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
|
||||
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
|
||||
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
||||
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
|
||||
|
||||
ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1)
|
||||
ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1)
|
||||
PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed
|
||||
else
|
||||
PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config
|
||||
endif
|
||||
|
||||
PYTHON_PREFIX := $(shell $(PYTHON_CONFIG) --prefix)
|
||||
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
|
||||
|
||||
# Reload Makefile.conf to override python specific variables if defined
|
||||
|
@ -305,17 +313,17 @@ ifeq ($(ENABLE_PYOSYS),1)
|
|||
#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
|
||||
ifeq ($(OS), Darwin)
|
||||
BOOST_PYTHON_LIB ?= $(shell \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
echo ""; fi; fi; fi; fi;)
|
||||
else
|
||||
BOOST_PYTHON_LIB ?= $(shell \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
echo ""; fi; fi; fi; fi;)
|
||||
endif
|
||||
|
||||
|
@ -325,19 +333,19 @@ endif
|
|||
|
||||
ifeq ($(OS), Darwin)
|
||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
||||
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
else
|
||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
||||
LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
endif
|
||||
else
|
||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
||||
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
else
|
||||
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
|
||||
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
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
|
||||
request clock buffer insertion by the ``clkbufmap`` pass.
|
||||
|
||||
- The ``clkbuf_inv`` attribute can be set on an output port of a module
|
||||
with the value set to the name of an input port of that module. When
|
||||
the ``clkbufmap`` would otherwise insert a clock buffer on this output,
|
||||
it will instead try inserting the clock buffer on the input port (this
|
||||
is used to implement clock inverter cells that clock buffer insertion
|
||||
will "see through").
|
||||
|
||||
- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
|
||||
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
|
||||
overridden by providing a custom selection to ``clkbufmap``.
|
||||
|
@ -357,19 +364,23 @@ Verilog Attributes and non-standard features
|
|||
it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
|
||||
from inserting another pad cell on it.
|
||||
|
||||
- The module attribute ``abc_box_id`` specifies a positive integer linking a
|
||||
- The module attribute ``abc9_box_id`` specifies a positive integer linking a
|
||||
blackbox or whitebox definition to a corresponding entry in a `abc9`
|
||||
box-file.
|
||||
|
||||
- The port attribute ``abc_carry`` marks the carry-in (if an input port) and
|
||||
- The port attribute ``abc9_carry`` marks the carry-in (if an input port) and
|
||||
carry-out (if output port) ports of a box. This information is necessary for
|
||||
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
|
||||
onto a bus port will affect only its most significant bit.
|
||||
|
||||
- The port attribute ``abc_arrival`` specifies an integer (for output ports
|
||||
- The port attribute ``abc9_arrival`` specifies an integer (for output ports
|
||||
only) to be used as the arrival time of this sequential port. It can be used,
|
||||
for example, to specify the clk-to-Q delay of a flip-flop for consideration
|
||||
during techmapping.
|
||||
during `abc9` techmapping.
|
||||
|
||||
- The module attribute ``abc9_flop`` is a boolean marking the module as a
|
||||
flip-flop. This allows `abc9` to analyse its contents in order to perform
|
||||
sequential synthesis.
|
||||
|
||||
- The frontend sets attributes ``always_comb``, ``always_latch`` and
|
||||
``always_ff`` on processes derived from SystemVerilog style always blocks
|
||||
|
@ -447,10 +458,10 @@ Verilog Attributes and non-standard features
|
|||
expressions over parameters and constant values are allowed). The intended
|
||||
use for this is synthesis-time DRC.
|
||||
|
||||
- There is limited support for converting specify .. endspecify statements to
|
||||
special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in
|
||||
blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this
|
||||
functionality. (By default specify .. endspecify blocks are ignored.)
|
||||
- There is limited support for converting ``specify`` .. ``endspecify``
|
||||
statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells,
|
||||
for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to
|
||||
enable this functionality. (By default these blocks are ignored.)
|
||||
|
||||
|
||||
Non-standard or SystemVerilog features for formal verification
|
||||
|
|
|
@ -787,6 +787,14 @@ struct AigerBackend : public Backend {
|
|||
if (top_module == nullptr)
|
||||
log_error("Can't find top module in current design!\n");
|
||||
|
||||
if (!design->selected_whole_module(top_module))
|
||||
log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
|
||||
|
||||
if (!top_module->processes.empty())
|
||||
log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module));
|
||||
if (!top_module->memories.empty())
|
||||
log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module));
|
||||
|
||||
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
|
||||
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@ struct XAigerWriter
|
|||
pool<SigBit> input_bits, output_bits;
|
||||
dict<SigBit, SigBit> not_map, alias_map;
|
||||
dict<SigBit, pair<SigBit, SigBit>> and_map;
|
||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
|
||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
|
||||
vector<SigBit> ci_bits, co_bits;
|
||||
dict<SigBit, Cell*> ff_bits;
|
||||
dict<SigBit, float> arrival_times;
|
||||
|
||||
vector<pair<int, int>> aig_gates;
|
||||
|
@ -93,7 +93,6 @@ struct XAigerWriter
|
|||
dict<SigBit, int> ordered_outputs;
|
||||
|
||||
vector<Cell*> box_list;
|
||||
bool omode = false;
|
||||
|
||||
int mkgate(int a0, int a1)
|
||||
{
|
||||
|
@ -141,7 +140,6 @@ struct XAigerWriter
|
|||
{
|
||||
pool<SigBit> undriven_bits;
|
||||
pool<SigBit> unused_bits;
|
||||
pool<SigBit> keep_bits;
|
||||
|
||||
// promote public wires
|
||||
for (auto wire : module->wires())
|
||||
|
@ -153,367 +151,333 @@ struct XAigerWriter
|
|||
if (wire->port_input)
|
||||
sigmap.add(wire);
|
||||
|
||||
// promote output wires
|
||||
// promote keep wires
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
if (wire->get_bool_attribute(ID::keep))
|
||||
sigmap.add(wire);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
bool keep = wire->attributes.count("\\keep");
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
SigBit wirebit(wire, i);
|
||||
SigBit bit = sigmap(wirebit);
|
||||
|
||||
if (bit.wire) {
|
||||
undriven_bits.insert(bit);
|
||||
unused_bits.insert(bit);
|
||||
}
|
||||
|
||||
if (keep)
|
||||
keep_bits.insert(bit);
|
||||
|
||||
if (wire->port_input || keep) {
|
||||
if (bit != wirebit)
|
||||
alias_map[bit] = wirebit;
|
||||
input_bits.insert(wirebit);
|
||||
}
|
||||
|
||||
if (wire->port_output || keep) {
|
||||
if (bit != RTLIL::Sx) {
|
||||
if (bit != wirebit)
|
||||
alias_map[wirebit] = bit;
|
||||
if (bit.wire == nullptr) {
|
||||
if (wire->port_output) {
|
||||
aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
|
||||
output_bits.insert(wirebit);
|
||||
}
|
||||
else
|
||||
log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit));
|
||||
continue;
|
||||
}
|
||||
|
||||
undriven_bits.insert(bit);
|
||||
unused_bits.insert(bit);
|
||||
|
||||
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);
|
||||
if (inst_module && inst_module->attributes.count("\\abc9_box_id")) {
|
||||
abc9_box_seen = true;
|
||||
if (!cell->has_keep_attr()) {
|
||||
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) {
|
||||
toposort.node(cell->name);
|
||||
for (const auto &conn : cell->connections()) {
|
||||
auto port_wire = inst_module->wire(conn.first);
|
||||
if (port_wire->port_input) {
|
||||
// Ignore inout for the sake of topographical ordering
|
||||
if (port_wire->port_output) continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_users[bit].insert(cell->name);
|
||||
}
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port_wire->port_output)
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_drivers[bit].insert(cell->name);
|
||||
if (cell->type == "$__ABC9_FF_" &&
|
||||
// The presence of an abc9_mergeability attribute indicates
|
||||
// that we do want to pass this flop to ABC
|
||||
cell->attributes.count("\\abc9_mergeability"))
|
||||
{
|
||||
SigBit D = sigmap(cell->getPort("\\D").as_bit());
|
||||
SigBit Q = sigmap(cell->getPort("\\Q").as_bit());
|
||||
unused_bits.erase(D);
|
||||
undriven_bits.erase(Q);
|
||||
alias_map[Q] = D;
|
||||
auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell));
|
||||
log_assert(r.second);
|
||||
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) {
|
||||
for (auto b : c.second) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
if (!w->port_output || !cell_known) {
|
||||
SigBit I = sigmap(b);
|
||||
if (I != b)
|
||||
alias_map[b] = I;
|
||||
output_bits.insert(b);
|
||||
unused_bits.erase(b);
|
||||
bool cell_known = inst_module || cell->known();
|
||||
for (const auto &c : cell->connections()) {
|
||||
if (c.second.is_fully_const()) continue;
|
||||
auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
|
||||
auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
|
||||
auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
|
||||
if (!is_input && !is_output)
|
||||
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
|
||||
|
||||
if (!cell_known)
|
||||
keep_bits.insert(b);
|
||||
}
|
||||
if (is_input)
|
||||
for (auto b : c.second) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
// 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));
|
||||
}
|
||||
|
||||
if (abc9_box_seen) {
|
||||
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);
|
||||
dict<IdString, std::vector<IdString>> box_ports;
|
||||
for (auto cell : box_list) {
|
||||
log_assert(cell);
|
||||
|
||||
#if 0
|
||||
toposort.analyze_loops = true;
|
||||
#endif
|
||||
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);
|
||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||
log_assert(box_module);
|
||||
log_assert(box_module->attributes.count("\\abc9_box_id"));
|
||||
|
||||
for (auto cell_name : toposort.sorted) {
|
||||
RTLIL::Cell *cell = module->cell(cell_name);
|
||||
log_assert(cell);
|
||||
|
||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||
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)
|
||||
auto r = box_ports.insert(cell->type);
|
||||
if (r.second) {
|
||||
// Make carry in the last PI, and carry out the last PO
|
||||
// since ABC requires it this way
|
||||
IdString carry_in, carry_out;
|
||||
for (const auto &port_name : box_module->ports) {
|
||||
RTLIL::Wire* w = box_module->wire(port_name);
|
||||
auto w = box_module->wire(port_name);
|
||||
log_assert(w);
|
||||
auto it = cell->connections_.find(port_name);
|
||||
if (w->port_input) {
|
||||
RTLIL::SigSpec rhs;
|
||||
if (it != cell->connections_.end()) {
|
||||
if (GetSize(it->second) < GetSize(w))
|
||||
it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second)));
|
||||
rhs = it->second;
|
||||
if (w->get_bool_attribute("\\abc9_carry")) {
|
||||
if (w->port_input) {
|
||||
if (carry_in != IdString())
|
||||
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module));
|
||||
carry_in = port_name;
|
||||
}
|
||||
else {
|
||||
rhs = RTLIL::SigSpec(State::S0, GetSize(w));
|
||||
cell->setPort(port_name, rhs);
|
||||
}
|
||||
|
||||
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);
|
||||
if (w->port_output) {
|
||||
if (carry_out != IdString())
|
||||
log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module));
|
||||
carry_out = port_name;
|
||||
}
|
||||
}
|
||||
else
|
||||
r.first->second.push_back(port_name);
|
||||
}
|
||||
|
||||
if (carry_in != IdString() && carry_out == IdString())
|
||||
log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module));
|
||||
if (carry_in == IdString() && carry_out != IdString())
|
||||
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module));
|
||||
if (carry_in != IdString()) {
|
||||
r.first->second.push_back(carry_in);
|
||||
r.first->second.push_back(carry_out);
|
||||
}
|
||||
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) {
|
||||
if (!output_bits.count(bit))
|
||||
continue;
|
||||
RTLIL::Wire *wire = bit.wire;
|
||||
// If encountering an inout port, or a keep-ed wire, then create a new wire
|
||||
// with $inout.out suffix, make it a PO driven by the existing inout, and
|
||||
// inherit existing inout's drivers
|
||||
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|
||||
|| keep_bits.count(bit)) {
|
||||
RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str());
|
||||
RTLIL::Wire *new_wire = module->wire(wire_name);
|
||||
if (!new_wire)
|
||||
new_wire = module->addWire(wire_name, GetSize(wire));
|
||||
SigBit new_bit(new_wire, bit.offset);
|
||||
module->connect(new_bit, bit);
|
||||
if (not_map.count(bit)) {
|
||||
auto a = not_map.at(bit);
|
||||
not_map[new_bit] = a;
|
||||
// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box
|
||||
if (box_module->get_bool_attribute("\\abc9_flop")) {
|
||||
SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
|
||||
if (rhs.empty())
|
||||
log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module));
|
||||
|
||||
for (auto b : rhs) {
|
||||
SigBit I = sigmap(b);
|
||||
if (b == RTLIL::Sx)
|
||||
b = State::S0;
|
||||
else if (I != b) {
|
||||
if (I == RTLIL::Sx)
|
||||
alias_map[b] = State::S0;
|
||||
else
|
||||
alias_map[b] = I;
|
||||
}
|
||||
co_bits.emplace_back(b);
|
||||
unused_bits.erase(I);
|
||||
}
|
||||
else if (and_map.count(bit)) {
|
||||
auto a = and_map.at(bit);
|
||||
and_map[new_bit] = a;
|
||||
}
|
||||
else if (alias_map.count(bit)) {
|
||||
auto a = alias_map.at(bit);
|
||||
alias_map[new_bit] = a;
|
||||
}
|
||||
else
|
||||
alias_map[new_bit] = bit;
|
||||
output_bits.erase(bit);
|
||||
output_bits.insert(new_bit);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto bit : input_bits)
|
||||
undriven_bits.erase(bit);
|
||||
for (auto bit : output_bits)
|
||||
unused_bits.erase(sigmap(bit));
|
||||
for (auto bit : unused_bits)
|
||||
undriven_bits.erase(bit);
|
||||
|
||||
if (!undriven_bits.empty() && !holes_mode) {
|
||||
undriven_bits.sort();
|
||||
for (auto bit : undriven_bits) {
|
||||
log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
|
||||
input_bits.insert(bit);
|
||||
}
|
||||
log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
|
||||
// Make all undriven bits a primary input
|
||||
for (auto bit : undriven_bits) {
|
||||
input_bits.insert(bit);
|
||||
undriven_bits.erase(bit);
|
||||
}
|
||||
|
||||
if (holes_mode) {
|
||||
struct sort_by_port_id {
|
||||
bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
|
||||
return a.wire->port_id < b.wire->port_id;
|
||||
return a.wire->port_id < b.wire->port_id ||
|
||||
(a.wire->port_id == b.wire->port_id && a.offset < b.offset);
|
||||
}
|
||||
};
|
||||
input_bits.sort(sort_by_port_id());
|
||||
output_bits.sort(sort_by_port_id());
|
||||
}
|
||||
else {
|
||||
input_bits.sort();
|
||||
output_bits.sort();
|
||||
}
|
||||
|
||||
not_map.sort();
|
||||
and_map.sort();
|
||||
|
||||
aig_map[State::S0] = 0;
|
||||
aig_map[State::S1] = 1;
|
||||
|
||||
for (auto bit : input_bits) {
|
||||
for (const auto &bit : input_bits) {
|
||||
aig_m++, aig_i++;
|
||||
log_assert(!aig_map.count(bit));
|
||||
aig_map[bit] = 2*aig_m;
|
||||
}
|
||||
|
||||
for (auto &c : ci_bits) {
|
||||
RTLIL::SigBit bit = std::get<0>(c);
|
||||
for (const auto &i : ff_bits) {
|
||||
const Cell *cell = i.second;
|
||||
const SigBit &q = sigmap(cell->getPort("\\Q"));
|
||||
aig_m++, aig_i++;
|
||||
log_assert(!aig_map.count(q));
|
||||
aig_map[q] = 2*aig_m;
|
||||
}
|
||||
|
||||
for (auto &bit : ci_bits) {
|
||||
aig_m++, aig_i++;
|
||||
// 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;
|
||||
}
|
||||
|
||||
for (auto &c : co_bits) {
|
||||
RTLIL::SigBit bit = std::get<0>(c);
|
||||
std::get<4>(c) = ordered_outputs[bit] = aig_o++;
|
||||
aig_outputs.push_back(bit2aig(bit));
|
||||
}
|
||||
|
||||
if (output_bits.empty()) {
|
||||
output_bits.insert(State::S0);
|
||||
omode = true;
|
||||
}
|
||||
|
||||
for (auto bit : output_bits) {
|
||||
for (auto bit : co_bits) {
|
||||
ordered_outputs[bit] = aig_o++;
|
||||
aig_outputs.push_back(bit2aig(bit));
|
||||
}
|
||||
|
||||
for (const auto &bit : output_bits) {
|
||||
ordered_outputs[bit] = aig_o++;
|
||||
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)
|
||||
|
@ -575,7 +539,6 @@ struct XAigerWriter
|
|||
|
||||
f << "c";
|
||||
|
||||
log_assert(!output_bits.empty());
|
||||
auto write_buffer = [](std::stringstream &buffer, int i32) {
|
||||
int32_t i32_be = to_big_endian(i32);
|
||||
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
|
||||
|
@ -583,14 +546,14 @@ struct XAigerWriter
|
|||
std::stringstream h_buffer;
|
||||
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
|
||||
write_h_buffer(1);
|
||||
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits));
|
||||
write_h_buffer(input_bits.size() + ci_bits.size());
|
||||
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits));
|
||||
write_h_buffer(output_bits.size() + GetSize(co_bits));
|
||||
log_debug("piNum = %d\n", GetSize(input_bits));
|
||||
write_h_buffer(input_bits.size());
|
||||
log_debug("poNum = %d\n", GetSize(output_bits));
|
||||
write_h_buffer(output_bits.size());
|
||||
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
|
||||
write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
|
||||
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
|
||||
write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
|
||||
log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
|
||||
write_h_buffer(input_bits.size() + ff_bits.size());
|
||||
log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
|
||||
write_h_buffer(output_bits.size() + ff_bits.size());
|
||||
log_debug("boxNum = %d\n", GetSize(box_list));
|
||||
write_h_buffer(box_list.size());
|
||||
|
||||
|
@ -606,126 +569,100 @@ struct XAigerWriter
|
|||
//for (auto bit : output_bits)
|
||||
// write_o_buffer(0);
|
||||
|
||||
if (!box_list.empty()) {
|
||||
RTLIL::Module *holes_module = module->design->addModule("$__holes__");
|
||||
log_assert(holes_module);
|
||||
if (!box_list.empty() || !ff_bits.empty()) {
|
||||
dict<IdString, std::tuple<int,int,int>> cell_cache;
|
||||
|
||||
int port_id = 1;
|
||||
int box_count = 0;
|
||||
for (auto cell : box_list) {
|
||||
log_assert(cell);
|
||||
|
||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||
int box_inputs = 0, box_outputs = 0;
|
||||
Cell *holes_cell = nullptr;
|
||||
if (box_module->get_bool_attribute("\\whitebox")) {
|
||||
holes_cell = holes_module->addCell(cell->name, cell->type);
|
||||
holes_cell->parameters = cell->parameters;
|
||||
log_assert(box_module);
|
||||
|
||||
auto r = cell_cache.insert(cell->type);
|
||||
auto &v = r.first->second;
|
||||
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
|
||||
// (as RTLIL::Module::fixup_ports() would do)
|
||||
for (const auto &port_name : box_module->ports) {
|
||||
RTLIL::Wire *w = box_module->wire(port_name);
|
||||
log_assert(w);
|
||||
RTLIL::Wire *holes_wire;
|
||||
RTLIL::SigSpec port_wire;
|
||||
if (w->port_input) {
|
||||
for (int i = 0; i < GetSize(w); i++) {
|
||||
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(std::get<0>(v));
|
||||
write_h_buffer(std::get<1>(v));
|
||||
write_h_buffer(std::get<2>(v));
|
||||
write_h_buffer(box_count++);
|
||||
}
|
||||
|
||||
std::stringstream r_buffer;
|
||||
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
|
||||
write_r_buffer(0);
|
||||
log_debug("flopNum = %d\n", GetSize(ff_bits));
|
||||
write_r_buffer(ff_bits.size());
|
||||
|
||||
std::stringstream s_buffer;
|
||||
auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
|
||||
write_s_buffer(ff_bits.size());
|
||||
|
||||
for (const auto &i : ff_bits) {
|
||||
const SigBit &d = i.first;
|
||||
const Cell *cell = i.second;
|
||||
|
||||
int mergeability = cell->attributes.at(ID(abc9_mergeability)).as_int();
|
||||
log_assert(mergeability > 0);
|
||||
write_r_buffer(mergeability);
|
||||
|
||||
Const init = cell->attributes.at(ID(abc9_init));
|
||||
log_assert(GetSize(init) == 1);
|
||||
if (init == State::S1)
|
||||
write_s_buffer(1);
|
||||
else if (init == State::S0)
|
||||
write_s_buffer(0);
|
||||
else {
|
||||
log_assert(init == State::Sx);
|
||||
write_s_buffer(0);
|
||||
}
|
||||
|
||||
write_i_buffer(arrival_times.at(d, 0));
|
||||
//write_o_buffer(0);
|
||||
}
|
||||
|
||||
f << "r";
|
||||
std::string buffer_str = r_buffer.str();
|
||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
f << "s";
|
||||
buffer_str = s_buffer.str();
|
||||
buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str()));
|
||||
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;
|
||||
XAigerWriter writer(holes_module, true /* holes_mode */);
|
||||
writer.write_aiger(a_buffer, false /*ascii_mode*/);
|
||||
|
||||
delete holes_design;
|
||||
|
||||
f << "a";
|
||||
std::string buffer_str = a_buffer.str();
|
||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
log_pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -747,19 +684,20 @@ struct XAigerWriter
|
|||
//f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
f << stringf("Generated by %s\n", yosys_version_str);
|
||||
|
||||
module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
|
||||
module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
|
||||
module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size());
|
||||
module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());
|
||||
}
|
||||
|
||||
void write_map(std::ostream &f, bool verbose_map)
|
||||
void write_map(std::ostream &f)
|
||||
{
|
||||
dict<int, string> input_lines;
|
||||
dict<int, string> output_lines;
|
||||
dict<int, string> wire_lines;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
//if (!verbose_map && wire->name[0] == '$')
|
||||
// continue;
|
||||
|
||||
SigSpec sig = sigmap(wire);
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
|
@ -773,17 +711,10 @@ struct XAigerWriter
|
|||
|
||||
if (output_bits.count(b)) {
|
||||
int o = ordered_outputs.at(b);
|
||||
output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), i, log_id(wire));
|
||||
int init = 2;
|
||||
output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose_map) {
|
||||
if (aig_map.count(sig[i]) == 0)
|
||||
continue;
|
||||
|
||||
int a = aig_map.at(sig[i]);
|
||||
wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -797,15 +728,9 @@ struct XAigerWriter
|
|||
f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
|
||||
|
||||
output_lines.sort();
|
||||
if (omode)
|
||||
output_lines[State::S0] = "output 0 0 $__dummy__\n";
|
||||
for (auto &it : output_lines)
|
||||
f << it.second;
|
||||
log_assert(output_lines.size() == output_bits.size());
|
||||
|
||||
wire_lines.sort();
|
||||
for (auto &it : wire_lines)
|
||||
f << it.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -817,23 +742,22 @@ struct XAigerBackend : public Backend {
|
|||
log("\n");
|
||||
log(" write_xaiger [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an XAIGER file. The design must be flattened and\n");
|
||||
log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\n");
|
||||
log("Write the top module (according to the (* top *) attribute or if only one module\n");
|
||||
log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or");
|
||||
log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n");
|
||||
log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n");
|
||||
log("module, if it exists.\n");
|
||||
log("\n");
|
||||
log(" -ascii\n");
|
||||
log(" write ASCII version of AIGER format\n");
|
||||
log("\n");
|
||||
log(" -map <filename>\n");
|
||||
log(" write an extra file with port and latch symbols\n");
|
||||
log("\n");
|
||||
log(" -vmap <filename>\n");
|
||||
log(" like -map, but more verbose\n");
|
||||
log(" write an extra file with port and box symbols\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool ascii_mode = false;
|
||||
bool verbose_map = false;
|
||||
std::string map_filename;
|
||||
|
||||
log_header(design, "Executing XAIGER backend.\n");
|
||||
|
@ -849,11 +773,6 @@ struct XAigerBackend : public Backend {
|
|||
map_filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
|
||||
map_filename = args[++argidx];
|
||||
verbose_map = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||
|
@ -863,6 +782,14 @@ struct XAigerBackend : public Backend {
|
|||
if (top_module == nullptr)
|
||||
log_error("Can't find top module in current design!\n");
|
||||
|
||||
if (!design->selected_whole_module(top_module))
|
||||
log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
|
||||
|
||||
if (!top_module->processes.empty())
|
||||
log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module));
|
||||
if (!top_module->memories.empty())
|
||||
log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module));
|
||||
|
||||
XAigerWriter writer(top_module);
|
||||
writer.write_aiger(*f, ascii_mode);
|
||||
|
||||
|
@ -871,7 +798,7 @@ struct XAigerBackend : public Backend {
|
|||
mapf.open(map_filename.c_str(), std::ofstream::trunc);
|
||||
if (mapf.fail())
|
||||
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
|
||||
writer.write_map(mapf, verbose_map);
|
||||
writer.write_map(mapf);
|
||||
}
|
||||
}
|
||||
} XAigerBackend;
|
||||
|
|
|
@ -300,13 +300,33 @@ struct EdifBackend : public Backend {
|
|||
*f << stringf(" (library DESIGN\n");
|
||||
*f << stringf(" (edifLevel 0)\n");
|
||||
*f << stringf(" (technology (numberDefinition))\n");
|
||||
|
||||
auto add_prop = [&](IdString name, Const val) {
|
||||
if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
|
||||
else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
|
||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
|
||||
else {
|
||||
std::string hex_string = "";
|
||||
for (size_t i = 0; i < val.bits.size(); i += 4) {
|
||||
int digit_value = 0;
|
||||
if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
|
||||
if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
|
||||
if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
|
||||
if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
||||
hex_string = std::string(digit_str) + hex_string;
|
||||
}
|
||||
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
|
||||
}
|
||||
};
|
||||
for (auto module : sorted_modules)
|
||||
{
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
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(" (cellType GENERIC)\n");
|
||||
|
@ -323,17 +343,26 @@ struct EdifBackend : public Backend {
|
|||
else if (!wire->port_input)
|
||||
dir = "OUTPUT";
|
||||
if (wire->width == 1) {
|
||||
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(wire->name), dir);
|
||||
*f << stringf(" (port %s (direction %s)", EDIF_DEF(wire->name), dir);
|
||||
if (attr_properties)
|
||||
for (auto &p : wire->attributes)
|
||||
add_prop(p.first, p.second);
|
||||
*f << ")\n";
|
||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
||||
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
||||
net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input));
|
||||
} else {
|
||||
int b[2];
|
||||
b[wire->upto ? 0 : 1] = wire->start_offset;
|
||||
b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1;
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
|
||||
*f << stringf(" (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
|
||||
if (attr_properties)
|
||||
for (auto &p : wire->attributes)
|
||||
add_prop(p.first, p.second);
|
||||
|
||||
*f << ")\n";
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
|
||||
net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
|
||||
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(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
||||
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
||||
|
||||
auto add_prop = [&](IdString name, Const val) {
|
||||
if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
|
||||
else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
|
||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
|
||||
else {
|
||||
std::string hex_string = "";
|
||||
for (size_t i = 0; i < val.bits.size(); i += 4) {
|
||||
int digit_value = 0;
|
||||
if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
|
||||
if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
|
||||
if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
|
||||
if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
||||
hex_string = std::string(digit_str) + hex_string;
|
||||
}
|
||||
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto &p : cell->parameters)
|
||||
add_prop(p.first, p.second);
|
||||
if (attr_properties)
|
||||
|
@ -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",
|
||||
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
||||
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 {
|
||||
int member_idx = GetSize(sig)-i-1;
|
||||
auto m = design->module(cell->type);
|
||||
|
@ -392,8 +400,8 @@ struct EdifBackend : public Backend {
|
|||
if (w)
|
||||
member_idx = GetSize(w)-i-1;
|
||||
}
|
||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))",
|
||||
EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)));
|
||||
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)), 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 == RTLIL::State::Sx) {
|
||||
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;
|
||||
} else if (sig == RTLIL::State::Sz) {
|
||||
continue;
|
||||
} else {
|
||||
for (auto &ref : it.second)
|
||||
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
|
||||
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str());
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +433,7 @@ struct EdifBackend : public Backend {
|
|||
}
|
||||
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||
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 (nogndvcc)
|
||||
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');
|
||||
if (sig == RTLIL::State::S1)
|
||||
*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");
|
||||
|
|
|
@ -304,7 +304,11 @@ class SmtIo:
|
|||
|
||||
def p_open(self):
|
||||
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
|
||||
self.p_running = True
|
||||
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)
|
||||
: design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports)
|
||||
: design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports), aiger_autoidx(autoidx++)
|
||||
{
|
||||
module = new RTLIL::Module;
|
||||
module->name = module_name;
|
||||
|
@ -255,7 +255,7 @@ end_of_header:
|
|||
else
|
||||
log_abort();
|
||||
|
||||
RTLIL::Wire* n0 = module->wire("\\__0__");
|
||||
RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
|
||||
if (n0)
|
||||
module->connect(n0, State::S0);
|
||||
|
||||
|
@ -271,14 +271,24 @@ end_of_header:
|
|||
if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
|
||||
log_error("Line %u has invalid symbol position!\n", line_count);
|
||||
|
||||
RTLIL::IdString escaped_s = stringf("\\%s", s.c_str());
|
||||
RTLIL::Wire* wire;
|
||||
if (c == 'i') wire = inputs[l1];
|
||||
else if (c == 'l') wire = latches[l1];
|
||||
else if (c == 'o') wire = outputs[l1];
|
||||
else if (c == 'o') {
|
||||
wire = module->wire(escaped_s);
|
||||
if (wire) {
|
||||
// Could have been renamed by a latch
|
||||
module->swap_names(wire, outputs[l1]);
|
||||
module->connect(outputs[l1], wire);
|
||||
goto next;
|
||||
}
|
||||
wire = outputs[l1];
|
||||
}
|
||||
else if (c == 'b') wire = bad_properties[l1];
|
||||
else log_abort();
|
||||
|
||||
module->rename(wire, stringf("\\%s", s.c_str()));
|
||||
module->rename(wire, escaped_s);
|
||||
}
|
||||
else if (c == 'j' || c == 'f') {
|
||||
// TODO
|
||||
|
@ -293,6 +303,7 @@ end_of_header:
|
|||
}
|
||||
else
|
||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
||||
next:
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
}
|
||||
|
||||
|
@ -312,18 +323,18 @@ static uint32_t parse_xaiger_literal(std::istream &f)
|
|||
return from_big_endian(l);
|
||||
}
|
||||
|
||||
static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
|
||||
RTLIL::Wire* AigerReader::createWireIfNotExists(RTLIL::Module *module, unsigned literal)
|
||||
{
|
||||
const unsigned variable = literal >> 1;
|
||||
const bool invert = literal & 1;
|
||||
RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : ""));
|
||||
RTLIL::IdString wire_name(stringf("$aiger%d$%d%s", aiger_autoidx, variable, invert ? "b" : ""));
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire) return wire;
|
||||
log_debug2("Creating %s\n", wire_name.c_str());
|
||||
wire = module->addWire(wire_name);
|
||||
wire->port_input = wire->port_output = false;
|
||||
if (!invert) return wire;
|
||||
RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable));
|
||||
RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable));
|
||||
RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
|
||||
if (wire_inv) {
|
||||
if (module->cell(wire_inv_name)) return wire;
|
||||
|
@ -335,12 +346,12 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera
|
|||
}
|
||||
|
||||
log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
|
||||
module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire);
|
||||
module->addNotGate(stringf("$not$aiger%d$%d", aiger_autoidx, variable), wire_inv, wire);
|
||||
|
||||
return wire;
|
||||
}
|
||||
|
||||
void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
|
||||
void AigerReader::parse_xaiger()
|
||||
{
|
||||
std::string header;
|
||||
f >> header;
|
||||
|
@ -372,108 +383,115 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
|
|||
else
|
||||
log_abort();
|
||||
|
||||
RTLIL::Wire* n0 = module->wire("\\__0__");
|
||||
RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx));
|
||||
if (n0)
|
||||
module->connect(n0, State::S0);
|
||||
|
||||
int c = f.get();
|
||||
if (c != 'c')
|
||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
||||
if (f.peek() == '\n')
|
||||
f.get();
|
||||
|
||||
// Parse footer (symbol table, comments, etc.)
|
||||
std::string s;
|
||||
bool comment_seen = false;
|
||||
for (int c = f.peek(); c != EOF; c = f.peek()) {
|
||||
if (comment_seen || c == 'c') {
|
||||
if (!comment_seen) {
|
||||
f.ignore(1);
|
||||
c = f.peek();
|
||||
comment_seen = true;
|
||||
}
|
||||
if (c == '\n')
|
||||
break;
|
||||
f.ignore(1);
|
||||
// XAIGER extensions
|
||||
if (c == 'm') {
|
||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
uint32_t lutNum = parse_xaiger_literal(f);
|
||||
uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
|
||||
ConstEvalAig ce(module);
|
||||
for (unsigned i = 0; i < lutNum; ++i) {
|
||||
uint32_t rootNodeID = parse_xaiger_literal(f);
|
||||
uint32_t cutLeavesM = parse_xaiger_literal(f);
|
||||
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
|
||||
RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID));
|
||||
uint32_t nodeID;
|
||||
RTLIL::SigSpec input_sig;
|
||||
for (unsigned j = 0; j < cutLeavesM; ++j) {
|
||||
nodeID = parse_xaiger_literal(f);
|
||||
log_debug2("\t%u\n", nodeID);
|
||||
RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID));
|
||||
log_assert(wire);
|
||||
input_sig.append(wire);
|
||||
for (int c = f.get(); c != EOF; c = f.get()) {
|
||||
// XAIGER extensions
|
||||
if (c == 'm') {
|
||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
uint32_t lutNum = parse_xaiger_literal(f);
|
||||
uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
|
||||
ConstEvalAig ce(module);
|
||||
for (unsigned i = 0; i < lutNum; ++i) {
|
||||
uint32_t rootNodeID = parse_xaiger_literal(f);
|
||||
uint32_t cutLeavesM = parse_xaiger_literal(f);
|
||||
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
|
||||
RTLIL::Wire *output_sig = module->wire(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||
log_assert(output_sig);
|
||||
uint32_t nodeID;
|
||||
RTLIL::SigSpec input_sig;
|
||||
for (unsigned j = 0; j < cutLeavesM; ++j) {
|
||||
nodeID = parse_xaiger_literal(f);
|
||||
log_debug2("\t%u\n", nodeID);
|
||||
if (nodeID == 0) {
|
||||
log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM);
|
||||
continue;
|
||||
}
|
||||
// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
|
||||
ce.clear();
|
||||
ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
|
||||
RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size());
|
||||
for (int j = 0; j < (1 << cutLeavesM); ++j) {
|
||||
int gray = j ^ (j >> 1);
|
||||
ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
|
||||
RTLIL::SigBit o(output_sig);
|
||||
bool success YS_ATTRIBUTE(unused) = ce.eval(o);
|
||||
log_assert(success);
|
||||
log_assert(o.wire == nullptr);
|
||||
lut_mask[gray] = o.data;
|
||||
}
|
||||
RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID));
|
||||
log_assert(output_cell);
|
||||
module->remove(output_cell);
|
||||
module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask));
|
||||
RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID));
|
||||
log_assert(wire);
|
||||
input_sig.append(wire);
|
||||
}
|
||||
}
|
||||
else if (c == 'r') {
|
||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
flopNum = parse_xaiger_literal(f);
|
||||
log_debug("flopNum: %u\n", flopNum);
|
||||
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
|
||||
f.ignore(flopNum * sizeof(uint32_t));
|
||||
}
|
||||
else if (c == 'n') {
|
||||
parse_xaiger_literal(f);
|
||||
f >> s;
|
||||
log_debug("n: '%s'\n", s.c_str());
|
||||
}
|
||||
else if (c == 'h') {
|
||||
f.ignore(sizeof(uint32_t));
|
||||
uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_assert(version == 1);
|
||||
uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_debug("ciNum = %u\n", ciNum);
|
||||
uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_debug("coNum = %u\n", coNum);
|
||||
piNum = parse_xaiger_literal(f);
|
||||
log_debug("piNum = %u\n", piNum);
|
||||
uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
log_debug("poNum = %u\n", poNum);
|
||||
uint32_t boxNum = parse_xaiger_literal(f);
|
||||
log_debug("boxNum = %u\n", boxNum);
|
||||
for (unsigned i = 0; i < boxNum; i++) {
|
||||
f.ignore(2*sizeof(uint32_t));
|
||||
uint32_t boxUniqueId = parse_xaiger_literal(f);
|
||||
log_assert(boxUniqueId > 0);
|
||||
uint32_t oldBoxNum = parse_xaiger_literal(f);
|
||||
RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId));
|
||||
boxes.emplace_back(cell);
|
||||
// Reverse input order as fastest input is returned first
|
||||
input_sig.reverse();
|
||||
// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
|
||||
ce.clear();
|
||||
ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
|
||||
RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << GetSize(input_sig));
|
||||
for (int j = 0; j < GetSize(lut_mask); ++j) {
|
||||
int gray = j ^ (j >> 1);
|
||||
ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)});
|
||||
RTLIL::SigBit o(output_sig);
|
||||
bool success YS_ATTRIBUTE(unused) = ce.eval(o);
|
||||
log_assert(success);
|
||||
log_assert(o.wire == nullptr);
|
||||
lut_mask[gray] = o.data;
|
||||
}
|
||||
}
|
||||
else if (c == 'a' || c == 'i' || c == 'o') {
|
||||
uint32_t dataSize = parse_xaiger_literal(f);
|
||||
f.ignore(dataSize);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||
log_assert(output_cell);
|
||||
module->remove(output_cell);
|
||||
module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask));
|
||||
}
|
||||
}
|
||||
else
|
||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
||||
else if (c == 'r') {
|
||||
uint32_t dataSize = 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();
|
||||
|
@ -487,13 +505,15 @@ void AigerReader::parse_aiger_ascii()
|
|||
unsigned l1, l2, l3;
|
||||
|
||||
// Parse inputs
|
||||
int digits = ceil(log10(I));
|
||||
for (unsigned i = 1; i <= I; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an input!\n", line_count);
|
||||
log_debug2("%d is an input\n", l1);
|
||||
log_assert(!(l1 & 1)); // Inputs can't be inverted
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, l1 >> 1));
|
||||
wire->port_input = true;
|
||||
module->connect(createWireIfNotExists(module, l1), wire);
|
||||
inputs.push_back(wire);
|
||||
}
|
||||
|
||||
|
@ -507,12 +527,14 @@ void AigerReader::parse_aiger_ascii()
|
|||
clk_wire->port_input = true;
|
||||
clk_wire->port_output = false;
|
||||
}
|
||||
digits = ceil(log10(L));
|
||||
for (unsigned i = 0; i < L; ++i, ++line_count) {
|
||||
if (!(f >> l1 >> l2))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
log_debug2("%d %d is a latch\n", l1, l2);
|
||||
log_assert(!(l1 & 1));
|
||||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
|
||||
module->connect(createWireIfNotExists(module, l1), q_wire);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
if (clk_wire)
|
||||
|
@ -543,25 +565,18 @@ void AigerReader::parse_aiger_ascii()
|
|||
}
|
||||
|
||||
// Parse outputs
|
||||
digits = ceil(log10(O));
|
||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
|
||||
log_debug2("%d is an output\n", l1);
|
||||
const unsigned variable = l1 >> 1;
|
||||
const bool invert = l1 & 1;
|
||||
RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix?
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (!wire)
|
||||
wire = createWireIfNotExists(module, l1);
|
||||
else if (wire->port_input || wire->port_output) {
|
||||
RTLIL::Wire *new_wire = module->addWire(NEW_ID);
|
||||
module->connect(new_wire, wire);
|
||||
wire = new_wire;
|
||||
}
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||
wire->port_output = true;
|
||||
module->connect(wire, createWireIfNotExists(module, l1));
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
//std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// Parse bad properties
|
||||
for (unsigned i = 0; i < B; ++i, ++line_count) {
|
||||
|
@ -573,6 +588,8 @@ void AigerReader::parse_aiger_ascii()
|
|||
wire->port_output = true;
|
||||
bad_properties.push_back(wire);
|
||||
}
|
||||
//if (B > 0)
|
||||
// std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse invariant constraints
|
||||
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 *i1_wire = createWireIfNotExists(module, l2);
|
||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
||||
module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
|
||||
module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
}
|
||||
|
@ -616,11 +633,12 @@ void AigerReader::parse_aiger_binary()
|
|||
std::string line;
|
||||
|
||||
// Parse inputs
|
||||
int digits = ceil(log10(I));
|
||||
for (unsigned i = 1; i <= I; ++i) {
|
||||
log_debug2("%d is an input\n", i);
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i));
|
||||
wire->port_input = true;
|
||||
log_assert(!wire->port_output);
|
||||
module->connect(createWireIfNotExists(module, i << 1), wire);
|
||||
inputs.push_back(wire);
|
||||
}
|
||||
|
||||
|
@ -634,12 +652,14 @@ void AigerReader::parse_aiger_binary()
|
|||
clk_wire->port_input = true;
|
||||
clk_wire->port_output = false;
|
||||
}
|
||||
digits = ceil(log10(L));
|
||||
l1 = (I+1) * 2;
|
||||
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
|
||||
if (!(f >> l2))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
log_debug("%d %d is a latch\n", l1, l2);
|
||||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
|
||||
module->connect(createWireIfNotExists(module, l1), q_wire);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
if (clk_wire)
|
||||
|
@ -670,23 +690,15 @@ void AigerReader::parse_aiger_binary()
|
|||
}
|
||||
|
||||
// Parse outputs
|
||||
digits = ceil(log10(O));
|
||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
|
||||
log_debug2("%d is an output\n", l1);
|
||||
const unsigned variable = l1 >> 1;
|
||||
const bool invert = l1 & 1;
|
||||
RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_b" the right suffix?
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (!wire)
|
||||
wire = createWireIfNotExists(module, l1);
|
||||
else if (wire->port_input || wire->port_output) {
|
||||
RTLIL::Wire *new_wire = module->addWire(NEW_ID);
|
||||
module->connect(new_wire, wire);
|
||||
wire = new_wire;
|
||||
}
|
||||
RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
|
||||
wire->port_output = true;
|
||||
module->connect(wire, createWireIfNotExists(module, l1));
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
@ -727,88 +739,49 @@ void AigerReader::parse_aiger_binary()
|
|||
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
||||
module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
|
||||
module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire);
|
||||
}
|
||||
}
|
||||
|
||||
void AigerReader::post_process()
|
||||
{
|
||||
pool<IdString> seen_boxes;
|
||||
unsigned ci_count = 0, co_count = 0;
|
||||
for (auto cell : boxes) {
|
||||
RTLIL::Module* box_module = design->module(cell->type);
|
||||
log_assert(box_module);
|
||||
|
||||
if (seen_boxes.insert(cell->type).second) {
|
||||
auto it = box_module->attributes.find("\\abc9_carry");
|
||||
if (it != box_module->attributes.end()) {
|
||||
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
|
||||
auto carry_in_out = it->second.decode_string();
|
||||
auto pos = carry_in_out.find(',');
|
||||
if (pos == std::string::npos)
|
||||
log_error("'abc9_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
|
||||
auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos));
|
||||
carry_in = box_module->wire(carry_in_name);
|
||||
if (!carry_in || !carry_in->port_input)
|
||||
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
|
||||
|
||||
auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
|
||||
carry_out = box_module->wire(carry_out_name);
|
||||
if (!carry_out || !carry_out->port_output)
|
||||
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
|
||||
|
||||
auto &ports = box_module->ports;
|
||||
for (auto jt = ports.begin(); jt != ports.end(); ) {
|
||||
RTLIL::Wire* w = box_module->wire(*jt);
|
||||
log_assert(w);
|
||||
if (w == carry_in || w == carry_out) {
|
||||
jt = ports.erase(jt);
|
||||
continue;
|
||||
}
|
||||
if (w->port_id > carry_in->port_id)
|
||||
--w->port_id;
|
||||
if (w->port_id > carry_out->port_id)
|
||||
--w->port_id;
|
||||
log_assert(w->port_input || w->port_output);
|
||||
log_assert(ports[w->port_id-1] == w->name);
|
||||
++jt;
|
||||
}
|
||||
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("\\i")) {
|
||||
log_assert(bit == State::S0);
|
||||
log_assert(co_count < outputs.size());
|
||||
bit = outputs[co_count++];
|
||||
log_assert(bit.wire && GetSize(bit.wire) == 1);
|
||||
log_assert(bit.wire->port_output);
|
||||
bit.wire->port_output = false;
|
||||
}
|
||||
|
||||
// NB: Assume box_module->ports are sorted alphabetically
|
||||
// (as RTLIL::Module::fixup_ports() would do)
|
||||
for (auto port_name : box_module->ports) {
|
||||
RTLIL::Wire* port = box_module->wire(port_name);
|
||||
log_assert(port);
|
||||
RTLIL::SigSpec rhs;
|
||||
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 (auto &bit : cell->connections_.at("\\o")) {
|
||||
log_assert(bit == State::S0);
|
||||
log_assert((piNum + ci_count) < inputs.size());
|
||||
bit = inputs[piNum + ci_count++];
|
||||
log_assert(bit.wire && GetSize(bit.wire) == 1);
|
||||
log_assert(bit.wire->port_input);
|
||||
bit.wire->port_input = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!map_filename.empty()) {
|
||||
|
@ -835,6 +808,7 @@ void AigerReader::post_process()
|
|||
wire->port_input = false;
|
||||
module->connect(wire, existing);
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
}
|
||||
else if (index > 0) {
|
||||
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||
|
@ -848,18 +822,14 @@ void AigerReader::post_process()
|
|||
module->connect(wire, existing);
|
||||
wire->port_input = false;
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(indexed_name));
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(wire));
|
||||
}
|
||||
else if (type == "output") {
|
||||
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
|
||||
RTLIL::Wire* wire = outputs[variable + co_count];
|
||||
log_assert(wire);
|
||||
log_assert(wire->port_output);
|
||||
if (escaped_s == "$__dummy__") {
|
||||
wire->port_output = false;
|
||||
continue;
|
||||
}
|
||||
log_debug("Renaming output %s", log_id(wire));
|
||||
|
||||
if (index == 0) {
|
||||
|
@ -868,70 +838,40 @@ void AigerReader::post_process()
|
|||
// simply connect the latter to the former
|
||||
RTLIL::Wire* existing = module->wire(escaped_s);
|
||||
if (!existing) {
|
||||
if (escaped_s.ends_with("$inout.out")) {
|
||||
wire->port_output = false;
|
||||
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11));
|
||||
log_assert(in_wire);
|
||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||
in_wire->port_output = true;
|
||||
module->connect(in_wire, wire);
|
||||
}
|
||||
else
|
||||
module->rename(wire, escaped_s);
|
||||
module->rename(wire, escaped_s);
|
||||
}
|
||||
else {
|
||||
wire->port_output = false;
|
||||
existing->port_output = true;
|
||||
module->connect(wire, existing);
|
||||
wire = existing;
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(escaped_s));
|
||||
}
|
||||
else if (index > 0) {
|
||||
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
|
||||
RTLIL::Wire* existing = module->wire(indexed_name);
|
||||
if (!existing) {
|
||||
if (escaped_s.ends_with("$inout.out")) {
|
||||
wire->port_output = false;
|
||||
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index));
|
||||
log_assert(in_wire);
|
||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||
in_wire->port_output = true;
|
||||
module->connect(in_wire, wire);
|
||||
}
|
||||
else {
|
||||
module->rename(wire, indexed_name);
|
||||
if (wideports)
|
||||
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
|
||||
}
|
||||
module->rename(wire, indexed_name);
|
||||
if (wideports)
|
||||
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
|
||||
}
|
||||
else {
|
||||
module->connect(wire, existing);
|
||||
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") {
|
||||
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
|
||||
if (cell) { // ABC could have optimised this box away
|
||||
RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
|
||||
if (cell) // ABC could have optimised this box away
|
||||
module->rename(cell, escaped_s);
|
||||
for (const auto &i : cell->connections()) {
|
||||
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
|
||||
log_error("Symbol type '%s' not recognised.\n", type.c_str());
|
||||
|
@ -968,15 +908,10 @@ void AigerReader::post_process()
|
|||
if (other_wire) {
|
||||
other_wire->port_input = false;
|
||||
other_wire->port_output = false;
|
||||
}
|
||||
if (wire->port_input) {
|
||||
if (other_wire)
|
||||
if (wire->port_input)
|
||||
module->connect(other_wire, SigSpec(wire, i));
|
||||
}
|
||||
else {
|
||||
// Since we skip POs that are connected to Sx,
|
||||
// re-connect them here
|
||||
module->connect(SigSpec(wire, i), other_wire ? other_wire : SigSpec(RTLIL::Sx));
|
||||
else
|
||||
module->connect(SigSpec(wire, i), other_wire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -997,9 +932,9 @@ void AigerReader::post_process()
|
|||
if (cell->type != "$lut") continue;
|
||||
auto y_port = cell->getPort("\\Y").as_bit();
|
||||
if (y_port.wire->width == 1)
|
||||
module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str()));
|
||||
module->rename(cell, stringf("$lut%s", y_port.wire->name.c_str()));
|
||||
else
|
||||
module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset));
|
||||
module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name.c_str(), y_port.offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1014,28 +949,31 @@ struct AigerFrontend : public Frontend {
|
|||
log("Load module from an AIGER file into the current design.\n");
|
||||
log("\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(" -clk_name <wire_name>\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(" if specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
|
||||
log(" clocked by wire of this name. otherwise, $_FF_ cells will be used\n");
|
||||
log("\n");
|
||||
log(" -map <filename>\n");
|
||||
log(" read file with port and latch symbols\n");
|
||||
log("\n");
|
||||
log(" -wideports\n");
|
||||
log(" Merge ports that match the pattern 'name[int]' into a single\n");
|
||||
log(" multi-bit port 'name'.\n");
|
||||
log(" merge ports that match the pattern 'name[int]' into a single\n");
|
||||
log(" multi-bit port 'name'\n");
|
||||
log("\n");
|
||||
log(" -xaiger\n");
|
||||
log(" read XAIGER extensions\n");
|
||||
log("\n");
|
||||
}
|
||||
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");
|
||||
|
||||
RTLIL::IdString clk_name = "\\clk";
|
||||
RTLIL::IdString clk_name;
|
||||
RTLIL::IdString module_name;
|
||||
std::string map_filename;
|
||||
bool wideports = false;
|
||||
bool wideports = false, xaiger = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
|
@ -1056,6 +994,10 @@ struct AigerFrontend : public Frontend {
|
|||
wideports = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-xaiger") {
|
||||
xaiger = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
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);
|
||||
reader.parse_aiger();
|
||||
if (xaiger)
|
||||
reader.parse_xaiger();
|
||||
else
|
||||
reader.parse_aiger();
|
||||
}
|
||||
} AigerFrontend;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ struct AigerReader
|
|||
RTLIL::Module *module;
|
||||
std::string map_filename;
|
||||
bool wideports;
|
||||
const int aiger_autoidx;
|
||||
|
||||
unsigned M, I, L, O, A;
|
||||
unsigned B, C, J, F; // Optional in AIGER 1.9
|
||||
|
@ -44,13 +45,16 @@ struct AigerReader
|
|||
std::vector<RTLIL::Wire*> outputs;
|
||||
std::vector<RTLIL::Wire*> bad_properties;
|
||||
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);
|
||||
void parse_aiger();
|
||||
void parse_xaiger(const dict<int,IdString> &box_lookup);
|
||||
void parse_xaiger();
|
||||
void parse_aiger_ascii();
|
||||
void parse_aiger_binary();
|
||||
void post_process();
|
||||
|
||||
RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal);
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
@ -1198,6 +1198,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
varbuf = new AstNode(AST_LOCALPARAM, varbuf);
|
||||
varbuf->str = init_ast->children[0]->str;
|
||||
|
||||
auto resolved = current_scope.at(init_ast->children[0]->str);
|
||||
if (resolved->range_valid) {
|
||||
varbuf->range_left = resolved->range_left;
|
||||
varbuf->range_right = resolved->range_right;
|
||||
varbuf->range_swapped = resolved->range_swapped;
|
||||
varbuf->range_valid = resolved->range_valid;
|
||||
}
|
||||
|
||||
AstNode *backup_scope_varbuf = current_scope[varbuf->str];
|
||||
current_scope[varbuf->str] = varbuf;
|
||||
|
||||
|
@ -2998,6 +3006,14 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
|||
current_ast_mod->children.push_back(p);
|
||||
str = p->str;
|
||||
id2ast = p;
|
||||
|
||||
auto resolved = current_scope.at(index_var);
|
||||
if (resolved->range_valid) {
|
||||
p->range_left = resolved->range_left;
|
||||
p->range_right = resolved->range_right;
|
||||
p->range_swapped = resolved->range_swapped;
|
||||
p->range_valid = resolved->range_valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -430,10 +430,14 @@ sigspec:
|
|||
free($1);
|
||||
} |
|
||||
sigspec '[' TOK_INT ']' {
|
||||
if ($3 >= $1->size() || $3 < 0)
|
||||
rtlil_frontend_ilang_yyerror("bit index out of range");
|
||||
$$ = new RTLIL::SigSpec($1->extract($3));
|
||||
delete $1;
|
||||
} |
|
||||
sigspec '[' TOK_INT ':' TOK_INT ']' {
|
||||
if ($3 >= $1->size() || $3 < 0 || $3 < $5)
|
||||
rtlil_frontend_ilang_yyerror("invalid slice");
|
||||
$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
|
||||
delete $1;
|
||||
} |
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
|
||||
|
||||
This directory contains Verific bindings for Yosys.
|
||||
See http://www.verific.com/ for details.
|
||||
|
||||
Use Symbiotic EDA Suite if you need Yosys+Verifc.
|
||||
https://www.symbioticeda.com/seda-suite
|
||||
|
||||
Contact office@symbioticeda.com for free evaluation
|
||||
binaries of Symbiotic EDA Suite.
|
||||
|
||||
|
||||
Verific Features that should be enabled in your Verific library
|
||||
|
|
|
@ -539,6 +539,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
|
|||
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) {
|
||||
cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
|
||||
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())
|
||||
log_error("%s\n", verific_error_msg.c_str());
|
||||
|
||||
for (auto nl : nl_todo)
|
||||
nl->ChangePortBusStructures(1 /* hierarchical */);
|
||||
|
||||
VerificExtNets worker;
|
||||
for (auto nl : nl_todo)
|
||||
worker.run(nl);
|
||||
|
@ -2065,7 +2076,12 @@ struct VerificPass : public Pass {
|
|||
log(" -d <dump_file>\n");
|
||||
log(" Dump the Verific netlist as a verilog file.\n");
|
||||
log("\n");
|
||||
log("Visit http://verific.com/ for more information on Verific.\n");
|
||||
log("\n");
|
||||
log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n");
|
||||
log("https://www.symbioticeda.com/seda-suite\n");
|
||||
log("\n");
|
||||
log("Contact office@symbioticeda.com for free evaluation\n");
|
||||
log("binaries of Symbiotic EDA Suite.\n");
|
||||
log("\n");
|
||||
}
|
||||
#ifdef YOSYS_ENABLE_VERIFIC
|
||||
|
@ -2074,7 +2090,13 @@ struct VerificPass : public Pass {
|
|||
static bool set_verific_global_flags = true;
|
||||
|
||||
if (check_noverific_env())
|
||||
log_cmd_error("This version of Yosys is built without Verific support.\n");
|
||||
log_cmd_error("This version of Yosys is built without Verific support.\n"
|
||||
"\n"
|
||||
"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"
|
||||
"https://www.symbioticeda.com/seda-suite\n"
|
||||
"\n"
|
||||
"Contact office@symbioticeda.com for free evaluation\n"
|
||||
"binaries of Symbiotic EDA Suite.\n");
|
||||
|
||||
log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n");
|
||||
|
||||
|
@ -2397,7 +2419,7 @@ struct VerificPass : public Pass {
|
|||
else
|
||||
{
|
||||
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;
|
||||
for (; argidx < GetSize(args); argidx++)
|
||||
|
@ -2459,6 +2481,9 @@ struct VerificPass : public Pass {
|
|||
worker.run(nl);
|
||||
}
|
||||
|
||||
for (auto nl : nl_todo)
|
||||
nl->ChangePortBusStructures(1 /* hierarchical */);
|
||||
|
||||
if (!dumpfile.empty()) {
|
||||
VeriWrite veri_writer;
|
||||
veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign());
|
||||
|
@ -2484,7 +2509,7 @@ struct VerificPass : public Pass {
|
|||
goto check_error;
|
||||
}
|
||||
|
||||
log_cmd_error("Missing or unsupported mode parameter.\n");
|
||||
cmd_error(args, argidx, "Missing or unsupported mode parameter.\n");
|
||||
|
||||
check_error:
|
||||
if (!verific_error_msg.empty())
|
||||
|
@ -2493,7 +2518,13 @@ struct VerificPass : public Pass {
|
|||
}
|
||||
#else /* YOSYS_ENABLE_VERIFIC */
|
||||
void execute(std::vector<std::string>, RTLIL::Design *) YS_OVERRIDE {
|
||||
log_cmd_error("This version of Yosys is built without Verific support.\n");
|
||||
log_cmd_error("This version of Yosys is built without Verific support.\n"
|
||||
"\n"
|
||||
"Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"
|
||||
"https://www.symbioticeda.com/seda-suite\n"
|
||||
"\n"
|
||||
"Contact office@symbioticeda.com for free evaluation\n"
|
||||
"binaries of Symbiotic EDA Suite.\n");
|
||||
}
|
||||
#endif
|
||||
} VerificPass;
|
||||
|
@ -2551,14 +2582,14 @@ struct ReadPass : public Pass {
|
|||
static bool use_verific = verific_available;
|
||||
|
||||
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.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 (!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;
|
||||
} else {
|
||||
use_verific = false;
|
||||
|
@ -2567,7 +2598,7 @@ struct ReadPass : public Pass {
|
|||
}
|
||||
|
||||
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 (use_verific) {
|
||||
|
@ -2599,7 +2630,7 @@ struct ReadPass : public Pass {
|
|||
args[0] = "verific";
|
||||
Pass::call(design, args);
|
||||
} 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;
|
||||
}
|
||||
|
@ -2646,7 +2677,7 @@ struct ReadPass : public Pass {
|
|||
return;
|
||||
}
|
||||
|
||||
log_cmd_error("Missing or unsupported mode parameter.\n");
|
||||
cmd_error(args, 1, "Missing or unsupported mode parameter.\n");
|
||||
}
|
||||
} ReadPass;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
*
|
||||
* Ad-hoc implementation of a Verilog preprocessor. The directives `define,
|
||||
* `include, `ifdef, `ifndef, `else and `endif are handled here. All other
|
||||
* directives are handled by the lexer (see lexer.l).
|
||||
* directives are handled by the lexer (see verilog_lexer.l).
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
*
|
||||
* A simple lexer for Verilog code. Non-preprocessor compiler directives are
|
||||
* handled here. The preprocessor stuff is handled in preproc.cc. Everything
|
||||
* else is left to the bison parser (see parser.y).
|
||||
* else is left to the bison parser (see verilog_parser.y).
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -2242,7 +2242,7 @@ gen_stmt:
|
|||
ast_stack.back()->children.push_back(node);
|
||||
ast_stack.push_back(node);
|
||||
} opt_arg_list ';'{
|
||||
ast_stack.pop_back();
|
||||
ast_stack.pop_back();
|
||||
};
|
||||
|
||||
gen_stmt_block:
|
||||
|
@ -2413,19 +2413,19 @@ basic_expr:
|
|||
append_attr($$, $2);
|
||||
} |
|
||||
basic_expr OP_SHL attr basic_expr {
|
||||
$$ = new AstNode(AST_SHIFT_LEFT, $1, $4);
|
||||
$$ = new AstNode(AST_SHIFT_LEFT, $1, new AstNode(AST_TO_UNSIGNED, $4));
|
||||
append_attr($$, $3);
|
||||
} |
|
||||
basic_expr OP_SHR attr basic_expr {
|
||||
$$ = new AstNode(AST_SHIFT_RIGHT, $1, $4);
|
||||
$$ = new AstNode(AST_SHIFT_RIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4));
|
||||
append_attr($$, $3);
|
||||
} |
|
||||
basic_expr OP_SSHL attr basic_expr {
|
||||
$$ = new AstNode(AST_SHIFT_SLEFT, $1, $4);
|
||||
$$ = new AstNode(AST_SHIFT_SLEFT, $1, new AstNode(AST_TO_UNSIGNED, $4));
|
||||
append_attr($$, $3);
|
||||
} |
|
||||
basic_expr OP_SSHR attr basic_expr {
|
||||
$$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4);
|
||||
$$ = new AstNode(AST_SHIFT_SRIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4));
|
||||
append_attr($$, $3);
|
||||
} |
|
||||
basic_expr '<' attr basic_expr {
|
||||
|
|
|
@ -295,6 +295,9 @@ int main(int argc, char **argv)
|
|||
printf(" -E <depsfile>\n");
|
||||
printf(" write a Makefile dependencies file with in- and output file names\n");
|
||||
printf("\n");
|
||||
printf(" -x <feature>\n");
|
||||
printf(" do not print warnings for the specified experimental feature\n");
|
||||
printf("\n");
|
||||
printf(" -g\n");
|
||||
printf(" globally enable debug log messages\n");
|
||||
printf("\n");
|
||||
|
@ -317,8 +320,14 @@ int main(int argc, char **argv)
|
|||
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;
|
||||
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)
|
||||
{
|
||||
|
@ -449,6 +458,9 @@ int main(int argc, char **argv)
|
|||
case 'E':
|
||||
depsfile = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
log_experimentals_ignored.insert(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
|
||||
exit(1);
|
||||
|
@ -561,39 +573,31 @@ int main(int argc, char **argv)
|
|||
|
||||
if (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
|
||||
log("End of script. Logfile hash: %s\n", hash.c_str());
|
||||
#else
|
||||
std::string meminfo;
|
||||
std::string stats_divider = ", ";
|
||||
# if defined(__linux__)
|
||||
std::ifstream statm;
|
||||
statm.open(stringf("/proc/%lld/statm", (long long)getpid()));
|
||||
if (statm.is_open()) {
|
||||
int sz_total, sz_resident;
|
||||
statm >> sz_total >> sz_resident;
|
||||
meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident",
|
||||
sz_total * (getpagesize() / 1024.0 / 1024.0),
|
||||
sz_resident * (getpagesize() / 1024.0 / 1024.0));
|
||||
stats_divider = "\n";
|
||||
}
|
||||
# elif defined(__FreeBSD__)
|
||||
pid_t pid = getpid();
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid};
|
||||
struct kinfo_proc kip;
|
||||
size_t kip_len = sizeof(kip);
|
||||
if (sysctl(mib, 4, &kip, &kip_len, NULL, 0) == 0) {
|
||||
vm_size_t sz_total = kip.ki_size;
|
||||
segsz_t sz_resident = kip.ki_rssize;
|
||||
meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident",
|
||||
(int)sz_total / 1024.0 / 1024.0,
|
||||
(int)sz_resident * (getpagesize() / 1024.0 / 1024.0));
|
||||
stats_divider = "\n";
|
||||
}
|
||||
# endif
|
||||
|
||||
struct rusage ru_buffer;
|
||||
getrusage(RUSAGE_SELF, &ru_buffer);
|
||||
if (yosys_design->scratchpad_get_bool("print_stats.include_children")) {
|
||||
struct rusage ru_buffer_children;
|
||||
getrusage(RUSAGE_CHILDREN, &ru_buffer_children);
|
||||
ru_buffer.ru_utime.tv_sec += ru_buffer_children.ru_utime.tv_sec;
|
||||
ru_buffer.ru_utime.tv_usec += ru_buffer_children.ru_utime.tv_usec;
|
||||
ru_buffer.ru_stime.tv_sec += ru_buffer_children.ru_stime.tv_sec;
|
||||
ru_buffer.ru_stime.tv_usec += ru_buffer_children.ru_stime.tv_usec;
|
||||
ru_buffer.ru_maxrss = std::max(ru_buffer.ru_maxrss, ru_buffer_children.ru_maxrss);
|
||||
}
|
||||
# if defined(__linux__) || defined(__FreeBSD__)
|
||||
meminfo = stringf(", MEM: %.2f MB peak",
|
||||
ru_buffer.ru_maxrss / 1024.0);
|
||||
#endif
|
||||
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash.c_str(),
|
||||
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
|
||||
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
|
||||
|
|
|
@ -42,7 +42,7 @@ std::vector<FILE*> log_files;
|
|||
std::vector<std::ostream*> log_streams;
|
||||
std::map<std::string, std::set<std::string>> log_hdump;
|
||||
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;
|
||||
bool log_hdump_all = false;
|
||||
FILE *log_errfile = NULL;
|
||||
|
@ -377,6 +377,19 @@ void log_warning(const char *format, ...)
|
|||
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, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
|
|
@ -50,7 +50,7 @@ extern std::vector<FILE*> log_files;
|
|||
extern std::vector<std::ostream*> log_streams;
|
||||
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::set<std::string> log_warnings;
|
||||
extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
|
||||
extern int log_warnings_count;
|
||||
extern bool log_hdump_all;
|
||||
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_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_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||
|
||||
// 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));
|
||||
|
|
|
@ -114,20 +114,35 @@ void Pass::run_register()
|
|||
|
||||
void Pass::init_register()
|
||||
{
|
||||
vector<Pass*> added_passes;
|
||||
while (first_queued_pass) {
|
||||
added_passes.push_back(first_queued_pass);
|
||||
first_queued_pass->run_register();
|
||||
first_queued_pass = first_queued_pass->next_queued_pass;
|
||||
}
|
||||
for (auto added_pass : added_passes)
|
||||
added_pass->on_register();
|
||||
}
|
||||
|
||||
void Pass::done_register()
|
||||
{
|
||||
for (auto &it : pass_register)
|
||||
it.second->on_shutdown();
|
||||
|
||||
frontend_register.clear();
|
||||
pass_register.clear();
|
||||
backend_register.clear();
|
||||
log_assert(first_queued_pass == NULL);
|
||||
}
|
||||
|
||||
void Pass::on_register()
|
||||
{
|
||||
}
|
||||
|
||||
void Pass::on_shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
Pass::~Pass()
|
||||
{
|
||||
}
|
||||
|
@ -289,6 +304,9 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
|
|||
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());
|
||||
|
||||
if (pass_register[args[0]]->experimental_flag)
|
||||
log_experimental("%s", args[0].c_str());
|
||||
|
||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||
auto state = pass_register[args[0]]->pre_execute();
|
||||
pass_register[args[0]]->execute(args, design);
|
||||
|
@ -809,6 +827,11 @@ struct HelpPass : public Pass {
|
|||
log("=");
|
||||
log("\n");
|
||||
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") {
|
||||
|
@ -831,6 +854,11 @@ struct HelpPass : public Pass {
|
|||
std::ostringstream buf;
|
||||
log_streams.push_back(&buf);
|
||||
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();
|
||||
write_tex(f, it.first, it.second->short_help, buf.str());
|
||||
}
|
||||
|
@ -843,6 +871,11 @@ struct HelpPass : public Pass {
|
|||
std::ostringstream buf;
|
||||
log_streams.push_back(&buf);
|
||||
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();
|
||||
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])) {
|
||||
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])) {
|
||||
log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
|
||||
|
|
|
@ -36,6 +36,11 @@ struct Pass
|
|||
|
||||
int call_counter;
|
||||
int64_t runtime_ns;
|
||||
bool experimental_flag = false;
|
||||
|
||||
void experimental() {
|
||||
experimental_flag = true;
|
||||
}
|
||||
|
||||
struct pre_post_exec_state_t {
|
||||
Pass *parent_pass;
|
||||
|
@ -62,6 +67,9 @@ struct Pass
|
|||
virtual void run_register();
|
||||
static void init_register();
|
||||
static void done_register();
|
||||
|
||||
virtual void on_register();
|
||||
virtual void on_shutdown();
|
||||
};
|
||||
|
||||
struct ScriptPass : Pass
|
||||
|
|
|
@ -46,6 +46,7 @@ IdString RTLIL::ID::Y;
|
|||
IdString RTLIL::ID::keep;
|
||||
IdString RTLIL::ID::whitebox;
|
||||
IdString RTLIL::ID::blackbox;
|
||||
dict<std::string, std::string> RTLIL::constpad;
|
||||
|
||||
RTLIL::Const::Const()
|
||||
{
|
||||
|
@ -783,6 +784,14 @@ namespace {
|
|||
return v;
|
||||
}
|
||||
|
||||
int param_bool(RTLIL::IdString name, bool expected)
|
||||
{
|
||||
int v = param_bool(name);
|
||||
if (v != expected)
|
||||
error(__LINE__);
|
||||
return v;
|
||||
}
|
||||
|
||||
void param_bits(RTLIL::IdString name, int width)
|
||||
{
|
||||
param(name);
|
||||
|
@ -869,13 +878,23 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) {
|
||||
if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr))) {
|
||||
param_bool(ID(A_SIGNED));
|
||||
param_bool(ID(B_SIGNED), /*expected=*/false);
|
||||
port(ID::A, param(ID(A_WIDTH)));
|
||||
port(ID::B, param(ID(B_WIDTH)));
|
||||
port(ID::Y, param(ID(Y_WIDTH)));
|
||||
check_expected(/*check_matched_sign=*/false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in(ID($shift), ID($shiftx))) {
|
||||
param_bool(ID(A_SIGNED));
|
||||
param_bool(ID(B_SIGNED));
|
||||
port(ID::A, param(ID(A_WIDTH)));
|
||||
port(ID::B, param(ID(B_WIDTH)));
|
||||
port(ID::Y, param(ID(Y_WIDTH)));
|
||||
check_expected(false);
|
||||
check_expected(/*check_matched_sign=*/false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -957,7 +976,7 @@ namespace {
|
|||
port(ID::A, param(ID(A_WIDTH)));
|
||||
port(ID::B, param(ID(B_WIDTH)));
|
||||
port(ID::Y, param(ID(Y_WIDTH)));
|
||||
check_expected(false);
|
||||
check_expected(/*check_matched_sign=*/false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1875,10 +1894,6 @@ DEF_METHOD(And, max(sig_a.size(), sig_b.size()), ID($and))
|
|||
DEF_METHOD(Or, max(sig_a.size(), sig_b.size()), ID($or))
|
||||
DEF_METHOD(Xor, max(sig_a.size(), sig_b.size()), ID($xor))
|
||||
DEF_METHOD(Xnor, max(sig_a.size(), sig_b.size()), ID($xnor))
|
||||
DEF_METHOD(Shl, sig_a.size(), ID($shl))
|
||||
DEF_METHOD(Shr, sig_a.size(), ID($shr))
|
||||
DEF_METHOD(Sshl, sig_a.size(), ID($sshl))
|
||||
DEF_METHOD(Sshr, sig_a.size(), ID($sshr))
|
||||
DEF_METHOD(Shift, sig_a.size(), ID($shift))
|
||||
DEF_METHOD(Shiftx, sig_a.size(), ID($shiftx))
|
||||
DEF_METHOD(Lt, 1, ID($lt))
|
||||
|
@ -1898,6 +1913,31 @@ DEF_METHOD(LogicAnd, 1, ID($logic_and))
|
|||
DEF_METHOD(LogicOr, 1, ID($logic_or))
|
||||
#undef DEF_METHOD
|
||||
|
||||
#define DEF_METHOD(_func, _y_size, _type) \
|
||||
RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed, const std::string &src) { \
|
||||
RTLIL::Cell *cell = addCell(name, _type); \
|
||||
cell->parameters[ID(A_SIGNED)] = is_signed; \
|
||||
cell->parameters[ID(B_SIGNED)] = false; \
|
||||
cell->parameters[ID(A_WIDTH)] = sig_a.size(); \
|
||||
cell->parameters[ID(B_WIDTH)] = sig_b.size(); \
|
||||
cell->parameters[ID(Y_WIDTH)] = sig_y.size(); \
|
||||
cell->setPort(ID::A, sig_a); \
|
||||
cell->setPort(ID::B, sig_b); \
|
||||
cell->setPort(ID::Y, sig_y); \
|
||||
cell->set_src_attribute(src); \
|
||||
return cell; \
|
||||
} \
|
||||
RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed, const std::string &src) { \
|
||||
RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size); \
|
||||
add ## _func(name, sig_a, sig_b, sig_y, is_signed, src); \
|
||||
return sig_y; \
|
||||
}
|
||||
DEF_METHOD(Shl, sig_a.size(), ID($shl))
|
||||
DEF_METHOD(Shr, sig_a.size(), ID($shr))
|
||||
DEF_METHOD(Sshl, sig_a.size(), ID($sshl))
|
||||
DEF_METHOD(Sshr, sig_a.size(), ID($sshr))
|
||||
#undef DEF_METHOD
|
||||
|
||||
#define DEF_METHOD(_func, _type, _pmux) \
|
||||
RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y, const std::string &src) { \
|
||||
RTLIL::Cell *cell = addCell(name, _type); \
|
||||
|
|
|
@ -377,6 +377,8 @@ namespace RTLIL
|
|||
extern IdString blackbox;
|
||||
};
|
||||
|
||||
extern dict<std::string, std::string> constpad;
|
||||
|
||||
static inline std::string escape_id(std::string str) {
|
||||
if (str.size() > 0 && str[0] != '\\' && str[0] != '$')
|
||||
return "\\" + str;
|
||||
|
@ -849,6 +851,8 @@ public:
|
|||
|
||||
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;
|
||||
inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); }
|
||||
|
|
|
@ -544,6 +544,8 @@ void yosys_shutdown()
|
|||
already_shutdown = true;
|
||||
log_pop();
|
||||
|
||||
Pass::done_register();
|
||||
|
||||
delete yosys_design;
|
||||
yosys_design = NULL;
|
||||
|
||||
|
@ -553,7 +555,6 @@ void yosys_shutdown()
|
|||
log_errfile = NULL;
|
||||
log_files.clear();
|
||||
|
||||
Pass::done_register();
|
||||
yosys_celltypes.clear();
|
||||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
|
|
|
@ -65,6 +65,11 @@ Verilog & Cell Type \\
|
|||
\label{tab:CellLib_unary}
|
||||
\end{table}
|
||||
|
||||
For the unary cells that output a logical value ({\tt \$reduce\_and}, {\tt \$reduce\_or},
|
||||
{\tt \$reduce\_xor}, {\tt \$reduce\_xnor}, {\tt \$reduce\_bool}, {\tt \$logic\_not}),
|
||||
when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
|
||||
and only the least significant bit varies.
|
||||
|
||||
Note that {\tt \$reduce\_or} and {\tt \$reduce\_bool} actually represent the same
|
||||
logic function. But the HDL frontends generate them in different situations. A
|
||||
{\tt \$reduce\_or} cell is generated when the prefix {\tt |} operator is being used. A
|
||||
|
@ -97,41 +102,6 @@ The width of the output port \B{Y}.
|
|||
|
||||
Table~\ref{tab:CellLib_binary} lists all cells for binary RTL operators.
|
||||
|
||||
\subsection{Multiplexers}
|
||||
|
||||
Multiplexers are generated by the Verilog HDL frontend for {\tt
|
||||
?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees
|
||||
from RTLIL::Process objects to logic.
|
||||
|
||||
The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter
|
||||
and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also
|
||||
has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to
|
||||
the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux}
|
||||
cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;.
|
||||
|
||||
The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells
|
||||
of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and
|
||||
an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both
|
||||
\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of
|
||||
\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is
|
||||
set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more
|
||||
than one bit from \B{S} is set the output is undefined. Cells of this type are used to model
|
||||
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
|
||||
an optimization).
|
||||
|
||||
The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
|
||||
parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
|
||||
\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
|
||||
is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
|
||||
the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
|
||||
|
||||
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
|
||||
usually results in trees of multiplexer cells. Many passes (from various
|
||||
optimizations to FSM extraction) heavily depend on these multiplexer trees to
|
||||
understand dependencies between signals. Therefore optimizations should not
|
||||
break these multiplexer trees (e.g.~by replacing a multiplexer between a
|
||||
calculated signal and a constant zero with an {\tt \$and} gate).
|
||||
|
||||
\begin{table}[t!]
|
||||
\hfil
|
||||
\begin{tabular}[t]{ll}
|
||||
|
@ -175,10 +145,61 @@ Verilog & Cell Type \\
|
|||
\label{tab:CellLib_binary}
|
||||
\end{table}
|
||||
|
||||
The {\tt \$shl} and {\tt \$shr} cells implement logical shifts, whereas the {\tt \$sshl} and
|
||||
{\tt \$sshr} cells implement arithmetic shifts. The {\tt \$shl} and {\tt \$sshl} cells implement
|
||||
the same operation. All four of these cells interpret the second operand as unsigned, and require
|
||||
\B{B\_SIGNED} to be zero.
|
||||
|
||||
Two additional shift operator cells are available that do not directly correspond to any operator
|
||||
in Verilog, {\tt \$shift} and {\tt \$shiftx}. The {\tt \$shift} cell performs a right logical shift
|
||||
if the second operand is positive (or unsigned), and a left logical shift if it is negative.
|
||||
The {\tt \$shiftx} cell performs the same operation as the {\tt \$shift} cell, but the vacated bit
|
||||
positions are filled with undef (x) bits, and corresponds to the Verilog indexed part-select expression.
|
||||
|
||||
For the binary cells that output a logical value ({\tt \$logic\_and}, {\tt \$logic\_or},
|
||||
{\tt \$eqx}, {\tt \$nex}, {\tt \$lt}, {\tt \$le}, {\tt \$eq}, {\tt \$ne}, {\tt \$ge},
|
||||
{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
|
||||
and only the least significant bit varies.
|
||||
|
||||
\subsection{Multiplexers}
|
||||
|
||||
Multiplexers are generated by the Verilog HDL frontend for {\tt
|
||||
?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees
|
||||
from RTLIL::Process objects to logic.
|
||||
|
||||
The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter
|
||||
and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also
|
||||
has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to
|
||||
the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux}
|
||||
cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;.
|
||||
|
||||
The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells
|
||||
of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and
|
||||
an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both
|
||||
\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of
|
||||
\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is
|
||||
set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more
|
||||
than one bit from \B{S} is set the output is undefined. Cells of this type are used to model
|
||||
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
|
||||
an optimization).
|
||||
|
||||
The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
|
||||
parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
|
||||
\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
|
||||
is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
|
||||
the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
|
||||
|
||||
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
|
||||
usually results in trees of multiplexer cells. Many passes (from various
|
||||
optimizations to FSM extraction) heavily depend on these multiplexer trees to
|
||||
understand dependencies between signals. Therefore optimizations should not
|
||||
break these multiplexer trees (e.g.~by replacing a multiplexer between a
|
||||
calculated signal and a constant zero with an {\tt \$and} gate).
|
||||
|
||||
\subsection{Registers}
|
||||
|
||||
D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
|
||||
an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff
|
||||
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 {\tt \$dff}
|
||||
cells:
|
||||
|
||||
\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}.
|
||||
\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}
|
||||
input port for the reset pin and the following additional two parameters:
|
||||
|
||||
\begin{itemize}
|
||||
\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}.
|
||||
|
||||
\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.
|
||||
\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}
|
||||
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}
|
||||
|
||||
\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];1; & {\tt \$\_DFF\_PP1\_} \\
|
||||
\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}
|
||||
\label{tab:CellLib_gates}
|
||||
\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 \$\_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 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\_},
|
||||
{\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;
|
||||
if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge;
|
||||
otherwise.
|
||||
|
@ -455,6 +540,25 @@ otherwise.
|
|||
Q <= D;
|
||||
\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
|
||||
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}
|
||||
|
@ -486,11 +590,7 @@ Add information about {\tt \$ff} and {\tt \$\_FF\_} cells.
|
|||
\end{fixme}
|
||||
|
||||
\begin{fixme}
|
||||
Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells.
|
||||
\end{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}
|
||||
|
|
|
@ -93,7 +93,7 @@ frontends/verilog/preproc.cc} in the Yosys source tree.
|
|||
|
||||
\begin{sloppypar}
|
||||
The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code
|
||||
can be found in {\tt frontends/verilog/lexer.l} in the Yosys source tree.
|
||||
can be found in {\tt frontends/verilog/verilog\_lexer.l} in the Yosys source tree.
|
||||
The lexer does little more than identifying all keywords and literals
|
||||
recognised by the Yosys Verilog frontend.
|
||||
\end{sloppypar}
|
||||
|
@ -115,7 +115,7 @@ whenever possible.)
|
|||
\subsection{The Verilog Parser}
|
||||
|
||||
The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code
|
||||
can be found in {\tt frontends/verilog/parser.y} in the Yosys source tree.
|
||||
can be found in {\tt frontends/verilog/verilog\_parser.y} in the Yosys source tree.
|
||||
|
||||
It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure
|
||||
defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has
|
||||
|
|
|
@ -146,7 +146,7 @@ with the help of HDL synthesis tools.
|
|||
|
||||
In special cases such as synthesis for coarse-grain cell libraries or when
|
||||
testing new synthesis algorithms it might be necessary to write a custom HDL
|
||||
synthesis tool or add new features to an existing one. It this cases the
|
||||
synthesis tool or add new features to an existing one. In these cases the
|
||||
availability of a Free and Open Source (FOSS) synthesis tool that can be used
|
||||
as basis for custom tools would be helpful.
|
||||
|
||||
|
|
|
@ -1935,6 +1935,19 @@ def parse_header(source):
|
|||
line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
|
||||
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:
|
||||
namespace_name = ugly_line[10:].replace("{","").strip()
|
||||
namespaces.append((namespace_name, ugly_line.count("{")))
|
||||
|
|
|
@ -32,3 +32,4 @@ OBJS += passes/cmds/chtype.o
|
|||
OBJS += passes/cmds/blackbox.o
|
||||
OBJS += passes/cmds/ltp.o
|
||||
OBJS += passes/cmds/bugpoint.o
|
||||
OBJS += passes/cmds/scratchpad.o
|
||||
|
|
|
@ -56,7 +56,7 @@ int autoname_worker(Module *module)
|
|||
for (auto &conn : cell->connections()) {
|
||||
string suffix = stringf("_%s", log_id(conn.first));
|
||||
for (auto bit : conn.second)
|
||||
if (bit.wire != nullptr && bit.wire->name[0] == '$') {
|
||||
if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
|
||||
IdString new_name(cell->name.str() + suffix);
|
||||
int score = wire_score.at(bit.wire);
|
||||
if (cell->output(conn.first)) score = 0;
|
||||
|
|
|
@ -301,41 +301,40 @@ struct SccPass : public Pass {
|
|||
RTLIL::Selection newSelection(false);
|
||||
int scc_counter = 0;
|
||||
|
||||
for (auto &mod_it : design->modules_)
|
||||
if (design->selected(mod_it.second))
|
||||
for (auto mod : design->selected_modules())
|
||||
{
|
||||
SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);
|
||||
|
||||
if (!setAttr.empty())
|
||||
{
|
||||
SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
|
||||
|
||||
if (!setAttr.empty())
|
||||
for (const auto &cells : worker.sccList)
|
||||
{
|
||||
for (const auto &cells : worker.sccList)
|
||||
for (auto attr : setAttr)
|
||||
{
|
||||
for (auto attr : setAttr)
|
||||
{
|
||||
IdString attr_name(RTLIL::escape_id(attr.first));
|
||||
string attr_valstr = attr.second;
|
||||
string index = stringf("%d", scc_counter);
|
||||
IdString attr_name(RTLIL::escape_id(attr.first));
|
||||
string attr_valstr = attr.second;
|
||||
string index = stringf("%d", scc_counter);
|
||||
|
||||
for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
|
||||
attr_valstr.replace(pos, 2, index);
|
||||
for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
|
||||
attr_valstr.replace(pos, 2, index);
|
||||
|
||||
Const attr_value(attr_valstr);
|
||||
Const attr_value(attr_valstr);
|
||||
|
||||
for (auto cell : cells)
|
||||
cell->attributes[attr_name] = attr_value;
|
||||
}
|
||||
|
||||
scc_counter++;
|
||||
for (auto cell : cells)
|
||||
cell->attributes[attr_name] = attr_value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scc_counter += GetSize(worker.sccList);
|
||||
}
|
||||
|
||||
if (selectMode)
|
||||
worker.select(newSelection);
|
||||
scc_counter++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scc_counter += GetSize(worker.sccList);
|
||||
}
|
||||
|
||||
if (selectMode)
|
||||
worker.select(newSelection);
|
||||
}
|
||||
|
||||
if (expect >= 0) {
|
||||
if (scc_counter == expect)
|
||||
|
|
|
@ -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__
|
||||
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
|
||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 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
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
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(" useful for handling architecture-specific primitives.\n");
|
||||
log("\n");
|
||||
log(" -blacklist <file>\n");
|
||||
log(" Do not match cells or signals that match the names in the file\n");
|
||||
log(" (passed to equiv_make).\n");
|
||||
log("\n");
|
||||
log(" -assert\n");
|
||||
log(" produce an error if the circuits are not equivalent.\n");
|
||||
log("\n");
|
||||
|
@ -61,13 +65,14 @@ struct EquivOptPass:public ScriptPass
|
|||
log("\n");
|
||||
}
|
||||
|
||||
std::string command, techmap_opts;
|
||||
std::string command, techmap_opts, make_opts;
|
||||
bool assert, undef, multiclock, async2sync;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
command = "";
|
||||
techmap_opts = "";
|
||||
make_opts = "";
|
||||
assert = false;
|
||||
undef = false;
|
||||
multiclock = false;
|
||||
|
@ -93,6 +98,10 @@ struct EquivOptPass:public ScriptPass
|
|||
techmap_opts += " -map " + args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-blacklist" && argidx + 1 < args.size()) {
|
||||
make_opts += " -blacklist " + args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-assert") {
|
||||
assert = true;
|
||||
continue;
|
||||
|
@ -170,7 +179,12 @@ struct EquivOptPass:public ScriptPass
|
|||
run("clk2fflogic", "(only with -multiclock)");
|
||||
if (async2sync || help_mode)
|
||||
run("async2sync", " (only with -async2sync)");
|
||||
run("equiv_make gold gate equiv");
|
||||
string opts;
|
||||
if (help_mode)
|
||||
opts = " -blacklist <filename> ...";
|
||||
else
|
||||
opts = make_opts;
|
||||
run("equiv_make" + opts + " gold gate equiv");
|
||||
if (help_mode)
|
||||
run("equiv_induct [-undef] equiv");
|
||||
else if (undef)
|
||||
|
|
|
@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
|
|||
static std::set<RTLIL::Cell*> muxtree_cells;
|
||||
static SigPool sig_at_port;
|
||||
|
||||
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
|
||||
static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache)
|
||||
{
|
||||
if (mux_tree_cache.find(sig) != mux_tree_cache.end())
|
||||
return mux_tree_cache.at(sig);
|
||||
|
||||
if (sig.is_fully_const() || old_sig == sig) {
|
||||
ret_true:
|
||||
mux_tree_cache[sig] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sig_at_port.check_any(assign_map(sig))) {
|
||||
ret_false:
|
||||
mux_tree_cache[sig] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
|
|||
for (auto &cellport : cellport_list)
|
||||
{
|
||||
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
|
||||
return false;
|
||||
goto ret_false;
|
||||
}
|
||||
|
||||
if (recursion_monitor.count(cellport.first)) {
|
||||
log_warning("logic loop in mux tree at signal %s in module %s.\n",
|
||||
log_signal(sig), RTLIL::id2cstr(module->name));
|
||||
return false;
|
||||
goto ret_false;
|
||||
}
|
||||
|
||||
recursion_monitor.insert(cellport.first);
|
||||
|
@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
|
|||
RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
|
||||
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
|
||||
|
||||
if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
|
||||
if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) {
|
||||
recursion_monitor.erase(cellport.first);
|
||||
return false;
|
||||
goto ret_false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sig_b.size(); i += sig_a.size())
|
||||
if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
|
||||
if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) {
|
||||
recursion_monitor.erase(cellport.first);
|
||||
return false;
|
||||
goto ret_false;
|
||||
}
|
||||
|
||||
recursion_monitor.erase(cellport.first);
|
||||
muxtree_cells.insert(cellport.first);
|
||||
}
|
||||
|
||||
return true;
|
||||
goto ret_true;
|
||||
}
|
||||
|
||||
static bool check_state_users(RTLIL::SigSpec sig)
|
||||
|
@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
|
|||
pool<Cell*> recursion_monitor;
|
||||
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
|
||||
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
|
||||
dict<RTLIL::SigSpec, bool> mux_tree_cache;
|
||||
|
||||
if (sig_q != assign_map(wire))
|
||||
continue;
|
||||
|
||||
looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor);
|
||||
looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache);
|
||||
looks_like_good_state_reg = check_state_users(sig_q);
|
||||
|
||||
if (!looks_like_state_reg)
|
||||
|
|
|
@ -134,6 +134,7 @@ struct rules_t
|
|||
dict<string, int> min_limits, max_limits;
|
||||
bool or_next_if_better, make_transp, make_outreg;
|
||||
char shuffle_enable;
|
||||
vector<vector<std::tuple<bool,IdString,Const>>> attributes;
|
||||
};
|
||||
|
||||
dict<IdString, vector<bram_t>> brams;
|
||||
|
@ -327,6 +328,20 @@ struct rules_t
|
|||
continue;
|
||||
}
|
||||
|
||||
if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
|
||||
data.attributes.emplace_back();
|
||||
for (int idx = 1; idx < GetSize(tokens); idx++) {
|
||||
size_t c1 = tokens[idx][0] == '!' ? 1 : 0;
|
||||
size_t c2 = tokens[idx].find("=");
|
||||
bool exists = (c1 == 0);
|
||||
IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
|
||||
Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
|
||||
|
||||
data.attributes.back().emplace_back(exists, key, val);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
syntax_error();
|
||||
}
|
||||
}
|
||||
|
@ -724,7 +739,7 @@ grow_read_ports:;
|
|||
if (match.make_transp && wr_ports <= 1) {
|
||||
pi.make_transp = true;
|
||||
if (pi.clocks != 0) {
|
||||
if (wr_ports == 1 && wr_clkdom != clkdom) {
|
||||
if (wr_ports == 1 && wr_clkdom != clkdom) {
|
||||
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
}
|
||||
|
@ -813,6 +828,43 @@ grow_read_ports:;
|
|||
return false;
|
||||
}
|
||||
|
||||
for (const auto &sums : match.attributes) {
|
||||
bool found = false;
|
||||
for (const auto &term : sums) {
|
||||
bool exists = std::get<0>(term);
|
||||
IdString key = std::get<1>(term);
|
||||
const Const &value = std::get<2>(term);
|
||||
auto it = cell->attributes.find(key);
|
||||
if (it == cell->attributes.end()) {
|
||||
if (exists)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else if (!exists)
|
||||
continue;
|
||||
if (it->second != value)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
std::stringstream ss;
|
||||
bool exists = std::get<0>(sums.front());
|
||||
if (!exists)
|
||||
ss << "!";
|
||||
IdString key = std::get<1>(sums.front());
|
||||
ss << log_id(key);
|
||||
const Const &value = std::get<2>(sums.front());
|
||||
if (exists && value != Const(1))
|
||||
ss << "=\"" << value.decode_string() << "\"";
|
||||
|
||||
log(" Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
|
||||
log_id(match.name), ss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == 1)
|
||||
return true;
|
||||
}
|
||||
|
@ -1100,6 +1152,43 @@ void handle_cell(Cell *cell, const rules_t &rules)
|
|||
goto next_match_rule;
|
||||
}
|
||||
|
||||
for (const auto &sums : match.attributes) {
|
||||
bool found = false;
|
||||
for (const auto &term : sums) {
|
||||
bool exists = std::get<0>(term);
|
||||
IdString key = std::get<1>(term);
|
||||
const Const &value = std::get<2>(term);
|
||||
auto it = cell->attributes.find(key);
|
||||
if (it == cell->attributes.end()) {
|
||||
if (exists)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else if (!exists)
|
||||
continue;
|
||||
if (it->second != value)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
std::stringstream ss;
|
||||
bool exists = std::get<0>(sums.front());
|
||||
if (!exists)
|
||||
ss << "!";
|
||||
IdString key = std::get<1>(sums.front());
|
||||
ss << log_id(key);
|
||||
const Const &value = std::get<2>(sums.front());
|
||||
if (exists && value != Const(1))
|
||||
ss << "=\"" << value.decode_string() << "\"";
|
||||
|
||||
log(" Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
|
||||
log_id(bram.name), bram.variant, ss.str().c_str());
|
||||
goto next_match_rule;
|
||||
}
|
||||
}
|
||||
|
||||
log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
|
||||
|
||||
if (or_next_if_better || !best_rule_cache.empty())
|
||||
|
@ -1225,6 +1314,13 @@ struct MemoryBramPass : public Pass {
|
|||
log(" dcells ....... number of cells in 'data-direction'\n");
|
||||
log(" cells ........ total number of cells (acells*dcells*dups)\n");
|
||||
log("\n");
|
||||
log("A match containing the command 'attribute' followed by a list of space\n");
|
||||
log("separated 'name[=string_value]' values requires that the memory contains any\n");
|
||||
log("one of the given attribute name and string values (where specified), or name\n");
|
||||
log("and integer 1 value (if no string_value given, since Verilog will interpret\n");
|
||||
log("'(* attr *)' as '(* attr=1 *)').\n");
|
||||
log("A name prefixed with '!' indicates that the attribute must not exist.\n");
|
||||
log("\n");
|
||||
log("The interface for the created bram instances is derived from the bram\n");
|
||||
log("description. Use 'techmap' to convert the created bram instances into\n");
|
||||
log("instances of the actual bram cells of your target architecture.\n");
|
||||
|
|
|
@ -218,6 +218,10 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
|
|||
mem->setPort("\\RD_DATA", sig_rd_data);
|
||||
mem->setPort("\\RD_EN", sig_rd_en);
|
||||
|
||||
// Copy attributes from RTLIL memory to $mem
|
||||
for (auto attr : memory->attributes)
|
||||
mem->attributes[attr.first] = attr.second;
|
||||
|
||||
for (auto c : memcells)
|
||||
module->remove(c);
|
||||
|
||||
|
|
|
@ -978,7 +978,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
|
||||
log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
|
||||
log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
|
||||
cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
|
||||
if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
|
||||
cell->setPort(ID::A, cell->getPort(ID::B));
|
||||
|
|
|
@ -44,9 +44,10 @@ struct OptReduceWorker
|
|||
cells.erase(cell);
|
||||
|
||||
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
||||
sig_a.sort_and_unify();
|
||||
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 (cell->type == ID($reduce_and)) {
|
||||
|
@ -86,6 +87,7 @@ struct OptReduceWorker
|
|||
}
|
||||
|
||||
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()) {
|
||||
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));
|
||||
|
||||
module->connect(old_sig_conn);
|
||||
module->check();
|
||||
|
||||
did_something = true;
|
||||
total_count++;
|
||||
|
@ -324,6 +325,8 @@ struct OptReduceWorker
|
|||
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; }
|
||||
};
|
||||
|
||||
#define BITWISE_OPS ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($and), ID($or), ID($xor), ID($xnor)
|
||||
#define FINE_BITWISE_OPS ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)
|
||||
|
||||
#define BITWISE_OPS FINE_BITWISE_OPS, ID($and), ID($or), ID($xor), ID($xnor)
|
||||
|
||||
#define REDUCTION_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($reduce_nand)
|
||||
|
||||
|
@ -250,14 +252,19 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
|
|||
shared_op->setPort(ID(CO), alu_co.extract(0, conn_width));
|
||||
}
|
||||
|
||||
shared_op->setParam(ID(Y_WIDTH), conn_width);
|
||||
bool is_fine = shared_op->type.in(FINE_BITWISE_OPS);
|
||||
|
||||
if (!is_fine)
|
||||
shared_op->setParam(ID(Y_WIDTH), conn_width);
|
||||
|
||||
if (decode_port(shared_op, ID::A, &assign_map) == operand) {
|
||||
shared_op->setPort(ID::B, mux_to_oper);
|
||||
shared_op->setParam(ID(B_WIDTH), max_width);
|
||||
if (!is_fine)
|
||||
shared_op->setParam(ID(B_WIDTH), max_width);
|
||||
} else {
|
||||
shared_op->setPort(ID::A, mux_to_oper);
|
||||
shared_op->setParam(ID(A_WIDTH), max_width);
|
||||
if (!is_fine)
|
||||
shared_op->setParam(ID(A_WIDTH), max_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,9 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
|
|||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/xilinx_dsp.o
|
||||
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
|
||||
|
||||
|
|
|
@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
|||
|
||||
// SB_MAC16 Input Interface
|
||||
SigSpec A = st.sigA;
|
||||
A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||
A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
|
||||
log_assert(GetSize(A) == 16);
|
||||
|
||||
SigSpec B = st.sigB;
|
||||
B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
|
||||
log_assert(GetSize(B) == 16);
|
||||
|
||||
SigSpec CD = st.sigCD;
|
||||
|
@ -248,8 +248,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
|||
cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
|
||||
|
||||
cell->setParam(ID(MODE_8x8), State::S0);
|
||||
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||
cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
cell->setParam(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
|
||||
cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
|
||||
|
||||
if (st.ffO) {
|
||||
if (st.o_lo)
|
||||
|
|
|
@ -56,11 +56,16 @@ code sigA sigB sigH
|
|||
break;
|
||||
sigH.append(O[i]);
|
||||
}
|
||||
// This sigM could have no users if downstream sinks (e.g. $add) is
|
||||
// narrower than $mul result, for example
|
||||
if (i == 0)
|
||||
reject;
|
||||
|
||||
log_assert(nusers(O.extract_end(i)) <= 1);
|
||||
endcode
|
||||
|
||||
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -81,7 +86,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
|||
endcode
|
||||
|
||||
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) {
|
||||
argQ = sigB;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -104,7 +109,7 @@ endcode
|
|||
code argD ffFJKG sigH clock clock_pol
|
||||
if (nusers(sigH) == 2 &&
|
||||
(mul->type != \SB_MAC16 ||
|
||||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
|
||||
(!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) {
|
||||
argD = sigH;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
|
@ -143,7 +148,7 @@ endcode
|
|||
|
||||
code argD ffH sigH sigO clock clock_pol
|
||||
if (ffFJKG && nusers(sigH) == 2 &&
|
||||
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
|
||||
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) {
|
||||
argD = sigH;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
|
@ -174,7 +179,7 @@ reject_ffH: ;
|
|||
endcode
|
||||
|
||||
match add
|
||||
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
|
||||
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3)
|
||||
|
||||
select add->type.in($add)
|
||||
choice <IdString> AB {\A, \B}
|
||||
|
@ -200,7 +205,7 @@ code sigCD sigO cd_signed
|
|||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||
reject;
|
||||
// If accumulator, check adder width and signedness
|
||||
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
|
||||
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool()))
|
||||
reject;
|
||||
|
||||
sigO = port(add, \Y);
|
||||
|
@ -275,7 +280,7 @@ endcode
|
|||
|
||||
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
|
||||
if (!sigCD.empty() && sigCD != sigO &&
|
||||
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
|
||||
(mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) {
|
||||
argQ = sigCD;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -328,6 +333,8 @@ arg argD argQ clock clock_pol
|
|||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (argQ.empty())
|
||||
reject;
|
||||
for (auto c : argQ.chunks()) {
|
||||
if (!c.wire)
|
||||
reject;
|
||||
|
|
|
@ -42,14 +42,30 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
|
|||
|
||||
cell->setPort("\\A", st.carry->getPort("\\I0"));
|
||||
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("\\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->setParam("\\LUT", st.lut->getParam("\\LUT_INIT"));
|
||||
|
||||
for (const auto &a : st.carry->attributes)
|
||||
cell->attributes[stringf("\\SB_CARRY.%s", a.first.c_str())] = a.second;
|
||||
for (const auto &a : st.lut->attributes)
|
||||
cell->attributes[stringf("\\SB_LUT4.%s", a.first.c_str())] = a.second;
|
||||
cell->attributes[ID(SB_LUT4.name)] = Const(st.lut->name.str());
|
||||
if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
|
||||
cell->attributes[ID::keep] = true;
|
||||
|
||||
pm.autoremove(st.carry);
|
||||
pm.autoremove(st.lut);
|
||||
}
|
||||
|
@ -62,28 +78,80 @@ struct Ice40WrapCarryPass : public Pass {
|
|||
log("\n");
|
||||
log(" ice40_wrapcarry [selection]\n");
|
||||
log("\n");
|
||||
log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUTs,\n");
|
||||
log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUT4s,\n");
|
||||
log("into an internal $__ICE40_CARRY_WRAPPER cell for preservation across technology\n");
|
||||
log("mapping.");
|
||||
log("mapping.\n");
|
||||
log("\n");
|
||||
log("Attributes on both cells will have their names prefixed with 'SB_CARRY.' or\n");
|
||||
log("'SB_LUT4.' and attached to the wrapping cell.\n");
|
||||
log("A (* keep *) attribute on either cell will be logically OR-ed together.\n");
|
||||
log("\n");
|
||||
log(" -unwrap\n");
|
||||
log(" unwrap $__ICE40_CARRY_WRAPPER cells back into SB_CARRYs and SB_LUT4s,\n");
|
||||
log(" including restoring their attributes.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool unwrap = false;
|
||||
|
||||
log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-singleton") {
|
||||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-unwrap") {
|
||||
unwrap = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
|
||||
for (auto module : design->selected_modules()) {
|
||||
if (!unwrap)
|
||||
ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
|
||||
else {
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type != ID($__ICE40_CARRY_WRAPPER))
|
||||
continue;
|
||||
|
||||
auto carry = module->addCell(NEW_ID, ID(SB_CARRY));
|
||||
carry->setPort(ID(I0), cell->getPort(ID(A)));
|
||||
carry->setPort(ID(I1), cell->getPort(ID(B)));
|
||||
carry->setPort(ID(CI), cell->getPort(ID(CI)));
|
||||
carry->setPort(ID(CO), cell->getPort(ID(CO)));
|
||||
module->swap_names(carry, cell);
|
||||
auto lut_name = cell->attributes.at(ID(SB_LUT4.name), Const(NEW_ID.str())).decode_string();
|
||||
auto lut = module->addCell(lut_name, ID($lut));
|
||||
lut->setParam(ID(WIDTH), 4);
|
||||
lut->setParam(ID(LUT), cell->getParam(ID(LUT)));
|
||||
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;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE
|
|||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/pmgen/xilinx_dsp_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp48a_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
|
||||
#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
|
||||
|
||||
|
@ -487,6 +488,190 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
|
|||
pm.blacklist(cell);
|
||||
}
|
||||
|
||||
void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_xilinx_dsp48a_pack;
|
||||
|
||||
log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
|
||||
|
||||
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
|
||||
log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
|
||||
log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
|
||||
log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
|
||||
log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
|
||||
log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
|
||||
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
|
||||
log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
|
||||
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
|
||||
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
|
||||
log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
|
||||
|
||||
Cell *cell = st.dsp;
|
||||
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
|
||||
|
||||
if (st.preAdd) {
|
||||
log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
|
||||
bool D_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
|
||||
bool B_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigB.extend_u0(18, B_SIGNED);
|
||||
st.sigD.extend_u0(18, D_SIGNED);
|
||||
cell->setPort(ID(B), st.sigB);
|
||||
cell->setPort(ID(D), st.sigD);
|
||||
opmode[4] = State::S1;
|
||||
if (st.preAdd->type == ID($add))
|
||||
opmode[6] = State::S0;
|
||||
else if (st.preAdd->type == ID($sub))
|
||||
opmode[6] = State::S1;
|
||||
else
|
||||
log_assert(!"strange pre-adder type");
|
||||
|
||||
pm.autoremove(st.preAdd);
|
||||
}
|
||||
if (st.postAdd) {
|
||||
log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
|
||||
|
||||
if (st.postAddMux) {
|
||||
log_assert(st.ffP);
|
||||
opmode[2] = st.postAddMux->getPort(ID(S));
|
||||
pm.autoremove(st.postAddMux);
|
||||
}
|
||||
else if (st.ffP && st.sigC == st.sigP)
|
||||
opmode[2] = State::S0;
|
||||
else
|
||||
opmode[2] = State::S1;
|
||||
opmode[3] = State::S1;
|
||||
|
||||
if (opmode[2] != State::S0) {
|
||||
if (st.postAddMuxAB == ID(A))
|
||||
st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
|
||||
else
|
||||
st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
|
||||
cell->setPort(ID(C), st.sigC);
|
||||
}
|
||||
|
||||
pm.autoremove(st.postAdd);
|
||||
}
|
||||
|
||||
if (st.clock != SigBit())
|
||||
{
|
||||
cell->setPort(ID(CLK), st.clock);
|
||||
|
||||
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
|
||||
SigSpec D = ff->getPort(ID(D));
|
||||
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
|
||||
if (!A.empty())
|
||||
A.replace(Q, D);
|
||||
if (rstmux) {
|
||||
SigSpec Y = rstmux->getPort(ID(Y));
|
||||
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
|
||||
if (!A.empty())
|
||||
A.replace(Y, AB);
|
||||
if (rstport != IdString()) {
|
||||
SigSpec S = rstmux->getPort(ID(S));
|
||||
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
|
||||
}
|
||||
}
|
||||
else if (rstport != IdString())
|
||||
cell->setPort(rstport, State::S0);
|
||||
if (cemux) {
|
||||
SigSpec Y = cemux->getPort(ID(Y));
|
||||
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
|
||||
SigSpec S = cemux->getPort(ID(S));
|
||||
if (!A.empty())
|
||||
A.replace(Y, BA);
|
||||
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
|
||||
}
|
||||
else
|
||||
cell->setPort(ceport, State::S1);
|
||||
|
||||
for (auto c : Q.chunks()) {
|
||||
auto it = c.wire->attributes.find(ID(init));
|
||||
if (it == c.wire->attributes.end())
|
||||
continue;
|
||||
for (int i = c.offset; i < c.offset+c.width; i++) {
|
||||
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
|
||||
it->second[i] = State::Sx;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (st.ffA0 || st.ffA1) {
|
||||
SigSpec A = cell->getPort(ID(A));
|
||||
if (st.ffA1) {
|
||||
f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
|
||||
cell->setParam(ID(A1REG), 1);
|
||||
}
|
||||
if (st.ffA0) {
|
||||
f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
|
||||
cell->setParam(ID(A0REG), 1);
|
||||
}
|
||||
pm.add_siguser(A, cell);
|
||||
cell->setPort(ID(A), A);
|
||||
}
|
||||
if (st.ffB0 || st.ffB1) {
|
||||
SigSpec B = cell->getPort(ID(B));
|
||||
if (st.ffB1) {
|
||||
f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
|
||||
cell->setParam(ID(B1REG), 1);
|
||||
}
|
||||
if (st.ffB0) {
|
||||
f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
|
||||
cell->setParam(ID(B0REG), 1);
|
||||
}
|
||||
pm.add_siguser(B, cell);
|
||||
cell->setPort(ID(B), B);
|
||||
}
|
||||
if (st.ffD) {
|
||||
SigSpec D = cell->getPort(ID(D));
|
||||
f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
|
||||
pm.add_siguser(D, cell);
|
||||
cell->setPort(ID(D), D);
|
||||
cell->setParam(ID(DREG), 1);
|
||||
}
|
||||
if (st.ffM) {
|
||||
SigSpec M; // unused
|
||||
f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
|
||||
st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
|
||||
cell->setParam(ID(MREG), State::S1);
|
||||
}
|
||||
if (st.ffP) {
|
||||
SigSpec P; // unused
|
||||
f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
|
||||
st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
|
||||
cell->setParam(ID(PREG), State::S1);
|
||||
}
|
||||
|
||||
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
|
||||
|
||||
if (st.ffA0)
|
||||
log(" ffA0:%s", log_id(st.ffA0));
|
||||
if (st.ffA1)
|
||||
log(" ffA1:%s", log_id(st.ffA1));
|
||||
|
||||
if (st.ffB0)
|
||||
log(" ffB0:%s", log_id(st.ffB0));
|
||||
if (st.ffB1)
|
||||
log(" ffB1:%s", log_id(st.ffB1));
|
||||
|
||||
if (st.ffD)
|
||||
log(" ffD:%s", log_id(st.ffD));
|
||||
|
||||
if (st.ffM)
|
||||
log(" ffM:%s", log_id(st.ffM));
|
||||
|
||||
if (st.ffP)
|
||||
log(" ffP:%s", log_id(st.ffP));
|
||||
}
|
||||
log("\n");
|
||||
|
||||
SigSpec P = st.sigP;
|
||||
if (GetSize(P) < 48)
|
||||
P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
|
||||
cell->setPort(ID(P), P);
|
||||
|
||||
pm.blacklist(cell);
|
||||
}
|
||||
|
||||
void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_xilinx_dsp_packC;
|
||||
|
@ -592,33 +777,48 @@ struct XilinxDspPass : public Pass {
|
|||
log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n");
|
||||
log("into using the DSP48E1's pattern detector feature for overflow detection.\n");
|
||||
log("\n");
|
||||
log(" -family {xcup|xcu|xc7|xc6v|xc5v|xc4v|xc6s|xc3sda}\n");
|
||||
log(" select the family to target\n");
|
||||
log(" default: xc7\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
|
||||
|
||||
std::string family = "xc7";
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-singleton") {
|
||||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) {
|
||||
family = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
// Don't bother distinguishing between those.
|
||||
if (family == "xc6v")
|
||||
family = "xc7";
|
||||
if (family == "xcup")
|
||||
family = "xcu";
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
// Experimental feature: pack $add/$sub cells with
|
||||
// (* use_dsp48="simd" *) into DSP48E1's using its
|
||||
// SIMD feature
|
||||
xilinx_simd_pack(module, module->selected_cells());
|
||||
if (family == "xc7")
|
||||
xilinx_simd_pack(module, module->selected_cells());
|
||||
|
||||
// Match for all features ([ABDMP][12]?REG, pre-adder,
|
||||
// post-adder, pattern detector, etc.) except for CREG
|
||||
{
|
||||
if (family == "xc7") {
|
||||
xilinx_dsp_pm pm(module, module->selected_cells());
|
||||
pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
|
||||
} else if (family == "xc6s" || family == "xc3sda") {
|
||||
xilinx_dsp48a_pm pm(module, module->selected_cells());
|
||||
pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack);
|
||||
}
|
||||
// Separating out CREG packing is necessary since there
|
||||
// is no guarantee that the cell ordering corresponds
|
||||
|
|
|
@ -120,7 +120,7 @@ endcode
|
|||
// reset functionality, using a subpattern discussed above)
|
||||
// If matched, treat 'A' input as input of ADREG
|
||||
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
|
||||
if (param(dsp, \ADREG).as_int() == 0) {
|
||||
if (param(dsp, \ADREG, 1).as_int() == 0) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -176,7 +176,7 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
|
|||
// Only search for ffA2 if there was a pre-adder
|
||||
// (otherwise ffA2 would have been matched as ffAD)
|
||||
if (preAdd) {
|
||||
if (param(dsp, \AREG).as_int() == 0) {
|
||||
if (param(dsp, \AREG, 1).as_int() == 0) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -237,7 +237,7 @@ endcode
|
|||
// (5) Match 'B' input for B2REG
|
||||
// If B2REG, then match 'B' input for B1REG
|
||||
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
|
||||
if (param(dsp, \BREG).as_int() == 0) {
|
||||
if (param(dsp, \BREG, 1).as_int() == 0) {
|
||||
argQ = sigB;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -287,7 +287,7 @@ endcode
|
|||
|
||||
// (6) Match 'D' input for DREG
|
||||
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
|
||||
if (param(dsp, \DREG).as_int() == 0) {
|
||||
if (param(dsp, \DREG, 1).as_int() == 0) {
|
||||
argQ = sigD;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
|
@ -308,7 +308,7 @@ endcode
|
|||
|
||||
// (7) Match 'P' output that exclusively drives an MREG
|
||||
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
|
||||
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
|
||||
if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) {
|
||||
argD = sigM;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
|
@ -335,7 +335,7 @@ endcode
|
|||
// recognised in xilinx_dsp.cc).
|
||||
match postAdd
|
||||
// Ensure that Z mux is not already used
|
||||
if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero()
|
||||
if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero()
|
||||
|
||||
select postAdd->type.in($add)
|
||||
select GetSize(port(postAdd, \Y)) <= 48
|
||||
|
@ -347,9 +347,9 @@ match postAdd
|
|||
index <SigBit> port(postAdd, AB)[0] === sigP[0]
|
||||
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
|
||||
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
|
||||
// Check that remainder of AB is a sign-extension
|
||||
define <bool> AB_SIGNED (param(postAdd, AB == \A ? \A_SIGNED : \B_SIGNED).as_bool())
|
||||
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(AB_SIGNED ? sigP[GetSize(sigP)-1] : State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
|
||||
// Check that remainder of AB is a sign- or zero-extension
|
||||
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
|
||||
|
||||
set postAddAB AB
|
||||
optional
|
||||
endmatch
|
||||
|
@ -363,7 +363,7 @@ endcode
|
|||
|
||||
// (9) Match 'P' output that exclusively drives a PREG
|
||||
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
|
||||
if (param(dsp, \PREG).as_int() == 0) {
|
||||
if (param(dsp, \PREG, 1).as_int() == 0) {
|
||||
int users = 2;
|
||||
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
|
||||
if (ffMcemux && !postAdd) users++;
|
||||
|
@ -460,7 +460,7 @@ arg argD argQ clock
|
|||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (GetSize(argQ) == 0)
|
||||
if (argQ.empty())
|
||||
reject;
|
||||
for (const auto &c : argQ.chunks()) {
|
||||
// Abandon matches when 'Q' is a constant
|
||||
|
|
|
@ -0,0 +1,673 @@
|
|||
// This file describes the main pattern matcher setup (of three total) that
|
||||
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
|
||||
// DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
|
||||
// At a high level, it works as follows:
|
||||
// ( 1) Starting from a DSP48A/DSP48A1 cell
|
||||
// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
|
||||
// (attached to at most two $mux cells that implement clock-enable or
|
||||
// reset functionality, using a subpattern discussed below)
|
||||
// If B1REG matched, treat 'B' input as input of B1REG
|
||||
// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
|
||||
// (pre-adder)
|
||||
// ( 4) Match 'B' input for B0REG
|
||||
// ( 5) Match 'A' input for A1REG
|
||||
// If A1REG, then match 'A' input for A0REG
|
||||
// ( 6) Match 'D' input for DREG
|
||||
// ( 7) Match 'P' output that exclusively drives an MREG
|
||||
// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
|
||||
// cell (post-adder).
|
||||
// The other input to the adder is assumed to come in from the 'C' input
|
||||
// (note: 'P' -> 'C' connections that exist for accumulators are
|
||||
// recognised in xilinx_dsp.cc).
|
||||
// ( 9) Match 'P' output that exclusively drives a PREG
|
||||
// (10) If post-adder and PREG both present, match for a $mux cell driving
|
||||
// the 'C' input, where one of the $mux's inputs is the PREG output.
|
||||
// This indicates an accumulator situation, and one where a $mux exists
|
||||
// to override the accumulated value:
|
||||
// +--------------------------------+
|
||||
// | ____ |
|
||||
// +--| \ |
|
||||
// |$mux|-+ |
|
||||
// 'C' ---|____/ | |
|
||||
// | /-------\ +----+ |
|
||||
// +----+ +-| post- |___|PREG|---+ 'P'
|
||||
// |MREG|------ | adder | +----+
|
||||
// +----+ \-------/
|
||||
// Notes: see the notes in xilinx_dsp.pmg
|
||||
|
||||
pattern xilinx_dsp48a_pack
|
||||
|
||||
state <SigBit> clock
|
||||
state <SigSpec> sigA sigB sigC sigD sigM sigP
|
||||
state <IdString> postAddAB postAddMuxAB
|
||||
state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
|
||||
state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
|
||||
state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
|
||||
state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
|
||||
state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
|
||||
|
||||
// Variables used for subpatterns
|
||||
state <SigSpec> argQ argD
|
||||
state <bool> ffcepol ffrstpol
|
||||
state <int> ffoffset
|
||||
udata <SigSpec> dffD dffQ
|
||||
udata <SigBit> dffclock
|
||||
udata <Cell*> dff dffcemux dffrstmux
|
||||
udata <bool> dffcepol dffrstpol
|
||||
|
||||
// (1) Starting from a DSP48A/DSP48A1 cell
|
||||
match dsp
|
||||
select dsp->type.in(\DSP48A, \DSP48A1)
|
||||
endmatch
|
||||
|
||||
code sigA sigB sigC sigD sigM clock
|
||||
auto unextend = [](const SigSpec &sig) {
|
||||
int i;
|
||||
for (i = GetSize(sig)-1; i > 0; i--)
|
||||
if (sig[i] != sig[i-1])
|
||||
break;
|
||||
// Do not remove non-const sign bit
|
||||
if (sig[i].wire)
|
||||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
sigA = unextend(port(dsp, \A));
|
||||
sigB = unextend(port(dsp, \B));
|
||||
|
||||
sigC = port(dsp, \C, SigSpec());
|
||||
sigD = port(dsp, \D, SigSpec());
|
||||
|
||||
SigSpec P = port(dsp, \P);
|
||||
// Only care about those bits that are used
|
||||
int i;
|
||||
for (i = GetSize(P)-1; i >= 0; i--)
|
||||
if (nusers(P[i]) > 1)
|
||||
break;
|
||||
i++;
|
||||
log_assert(nusers(P.extract_end(i)) <= 1);
|
||||
// This sigM could have no users if downstream sinks (e.g. $add) is
|
||||
// narrower than $mul result, for example
|
||||
if (i == 0)
|
||||
reject;
|
||||
sigM = P.extract(0, i);
|
||||
|
||||
clock = port(dsp, \CLK, SigBit());
|
||||
endcode
|
||||
|
||||
// (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
|
||||
// (attached to at most two $mux cells that implement clock-enable or
|
||||
// reset functionality, using a subpattern discussed above)
|
||||
// If matched, treat 'B' input as input of B1REG
|
||||
code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
|
||||
if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
|
||||
argQ = sigB;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB1 = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffB1rstmux = dffrstmux;
|
||||
ffBrstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffB1cemux = dffcemux;
|
||||
ffBcepol = dffcepol;
|
||||
}
|
||||
sigB = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
|
||||
// (pre-adder)
|
||||
match preAdd
|
||||
if sigD.empty() || sigD.is_fully_zero()
|
||||
if param(dsp, \B0REG).as_int() == 0
|
||||
// Ensure that preAdder not already used
|
||||
if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
|
||||
|
||||
select preAdd->type.in($add, $sub)
|
||||
// Output has to be 18 bits or less
|
||||
select GetSize(port(preAdd, \Y)) <= 18
|
||||
select nusers(port(preAdd, \Y)) == 2
|
||||
// D port has to be 18 bits or less
|
||||
select GetSize(port(preAdd, \A)) <= 18
|
||||
// B port has to be 18 bits or less
|
||||
select GetSize(port(preAdd, \B)) <= 18
|
||||
index <SigSpec> port(preAdd, \Y) === sigB
|
||||
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigB sigD
|
||||
if (preAdd) {
|
||||
sigD = port(preAdd, \A);
|
||||
sigB = port(preAdd, \B);
|
||||
}
|
||||
endcode
|
||||
|
||||
// (4) Match 'B' input for B0REG
|
||||
code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
|
||||
if (param(dsp, \B0REG).as_int() == 0) {
|
||||
argQ = sigB;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (ffB1) {
|
||||
if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
|
||||
goto ffB0_end;
|
||||
if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
|
||||
goto ffB0_end;
|
||||
if (dffrstmux) {
|
||||
if (ffBrstpol != dffrstpol)
|
||||
goto ffB0_end;
|
||||
if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
|
||||
goto ffB0_end;
|
||||
ffB0rstmux = dffrstmux;
|
||||
}
|
||||
if (dffcemux) {
|
||||
if (ffBcepol != dffcepol)
|
||||
goto ffB0_end;
|
||||
if (port(ffB1cemux, \S) != port(dffcemux, \S))
|
||||
goto ffB0_end;
|
||||
ffB0cemux = dffcemux;
|
||||
}
|
||||
}
|
||||
ffB0 = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffB0rstmux = dffrstmux;
|
||||
ffBrstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffB0cemux = dffcemux;
|
||||
ffBcepol = dffcepol;
|
||||
}
|
||||
sigB = dffD;
|
||||
}
|
||||
}
|
||||
ffB0_end:
|
||||
endcode
|
||||
|
||||
// (5) Match 'A' input for A1REG
|
||||
// If A1REG, then match 'A' input for A0REG
|
||||
code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
|
||||
if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA1 = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffA1rstmux = dffrstmux;
|
||||
ffArstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffA1cemux = dffcemux;
|
||||
ffAcepol = dffcepol;
|
||||
}
|
||||
sigA = dffD;
|
||||
|
||||
// Now attempt to match A0
|
||||
if (ffA1) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
|
||||
goto ffA0_end;
|
||||
if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
|
||||
goto ffA0_end;
|
||||
if (dffrstmux) {
|
||||
if (ffArstpol != dffrstpol)
|
||||
goto ffA0_end;
|
||||
if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
|
||||
goto ffA0_end;
|
||||
ffA0rstmux = dffrstmux;
|
||||
}
|
||||
if (dffcemux) {
|
||||
if (ffAcepol != dffcepol)
|
||||
goto ffA0_end;
|
||||
if (port(ffA1cemux, \S) != port(dffcemux, \S))
|
||||
goto ffA0_end;
|
||||
ffA0cemux = dffcemux;
|
||||
}
|
||||
|
||||
ffA0 = dff;
|
||||
clock = dffclock;
|
||||
|
||||
if (dffcemux) {
|
||||
ffA0cemux = dffcemux;
|
||||
ffAcepol = dffcepol;
|
||||
}
|
||||
sigA = dffD;
|
||||
|
||||
ffA0_end: ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// (6) Match 'D' input for DREG
|
||||
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
|
||||
if (param(dsp, \DREG).as_int() == 0) {
|
||||
argQ = sigD;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffD = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffDrstmux = dffrstmux;
|
||||
ffDrstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffDcemux = dffcemux;
|
||||
ffDcepol = dffcepol;
|
||||
}
|
||||
sigD = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// (7) Match 'P' output that exclusively drives an MREG
|
||||
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
|
||||
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
|
||||
argD = sigM;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffM = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffMrstmux = dffrstmux;
|
||||
ffMrstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffMcemux = dffcemux;
|
||||
ffMcepol = dffcepol;
|
||||
}
|
||||
sigM = dffQ;
|
||||
}
|
||||
}
|
||||
sigP = sigM;
|
||||
endcode
|
||||
|
||||
// (8) Match 'P' output that exclusively drives one of two inputs to an $add
|
||||
// cell (post-adder).
|
||||
// The other input to the adder is assumed to come in from the 'C' input
|
||||
// (note: 'P' -> 'C' connections that exist for accumulators are
|
||||
// recognised in xilinx_dsp.cc).
|
||||
match postAdd
|
||||
// Ensure that Z mux is not already used
|
||||
if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero()
|
||||
|
||||
select postAdd->type.in($add)
|
||||
select GetSize(port(postAdd, \Y)) <= 48
|
||||
choice <IdString> AB {\A, \B}
|
||||
select nusers(port(postAdd, AB)) <= 3
|
||||
filter ffMcemux || nusers(port(postAdd, AB)) == 2
|
||||
filter !ffMcemux || nusers(port(postAdd, AB)) == 3
|
||||
|
||||
index <SigBit> port(postAdd, AB)[0] === sigP[0]
|
||||
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
|
||||
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
|
||||
// Check that remainder of AB is a sign- or zero-extension
|
||||
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
|
||||
|
||||
set postAddAB AB
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigC sigP
|
||||
if (postAdd) {
|
||||
sigC = port(postAdd, postAddAB == \A ? \B : \A);
|
||||
sigP = port(postAdd, \Y);
|
||||
}
|
||||
endcode
|
||||
|
||||
// (9) Match 'P' output that exclusively drives a PREG
|
||||
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
|
||||
if (param(dsp, \PREG).as_int() == 0) {
|
||||
int users = 2;
|
||||
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
|
||||
if (ffMcemux && !postAdd) users++;
|
||||
if (nusers(sigP) == users) {
|
||||
argD = sigP;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffP = dff;
|
||||
clock = dffclock;
|
||||
if (dffrstmux) {
|
||||
ffPrstmux = dffrstmux;
|
||||
ffPrstpol = dffrstpol;
|
||||
}
|
||||
if (dffcemux) {
|
||||
ffPcemux = dffcemux;
|
||||
ffPcepol = dffcepol;
|
||||
}
|
||||
sigP = dffQ;
|
||||
}
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// (10) If post-adder and PREG both present, match for a $mux cell driving
|
||||
// the 'C' input, where one of the $mux's inputs is the PREG output.
|
||||
// This indicates an accumulator situation, and one where a $mux exists
|
||||
// to override the accumulated value:
|
||||
// +--------------------------------+
|
||||
// | ____ |
|
||||
// +--| \ |
|
||||
// |$mux|-+ |
|
||||
// 'C' ---|____/ | |
|
||||
// | /-------\ +----+ |
|
||||
// +----+ +-| post- |___|PREG|---+ 'P'
|
||||
// |MREG|------ | adder | +----+
|
||||
// +----+ \-------/
|
||||
match postAddMux
|
||||
if postAdd
|
||||
if ffP
|
||||
select postAddMux->type.in($mux)
|
||||
select nusers(port(postAddMux, \Y)) == 2
|
||||
choice <IdString> AB {\A, \B}
|
||||
index <SigSpec> port(postAddMux, AB) === sigP
|
||||
index <SigSpec> port(postAddMux, \Y) === sigC
|
||||
set postAddMuxAB AB
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigC
|
||||
if (postAddMux)
|
||||
sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
|
||||
endcode
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching against input registers, based on knowledge of the
|
||||
// 'Q' input. Typically, identifying registers with clock-enable and reset
|
||||
// capability would be a task would be handled by other Yosys passes such as
|
||||
// dff2dffe, but since DSP inference happens much before this, these patterns
|
||||
// have to be manually identified.
|
||||
// At a high level:
|
||||
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||
// 'Q' argument
|
||||
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||
// one that exclusively drives the 'D' input of the $dff, with one of its
|
||||
// $mux inputs being fully zero
|
||||
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||
// the 'Q' output of the $dff
|
||||
subpattern in_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (GetSize(argQ) == 0)
|
||||
reject;
|
||||
for (const auto &c : argQ.chunks()) {
|
||||
// Abandon matches when 'Q' is a constant
|
||||
if (!c.wire)
|
||||
reject;
|
||||
// Abandon matches when 'Q' has the keep attribute set
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
// Abandon matches when 'Q' has a non-zero init attribute set
|
||||
// (not supported by DSP48E1)
|
||||
Const init = c.wire->attributes.at(\init, Const());
|
||||
if (!init.empty())
|
||||
for (auto b : init.extract(c.offset, c.width))
|
||||
if (b != State::Sx && b != State::S0)
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||
// 'Q' argument
|
||||
match ff
|
||||
select ff->type.in($dff)
|
||||
// DSP48E1 does not support clock inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||
|
||||
// Check that the rest of argQ is present
|
||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||
|
||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||
|
||||
set ffoffset offset
|
||||
endmatch
|
||||
|
||||
code argQ argD
|
||||
SigSpec Q = port(ff, \Q);
|
||||
dff = ff;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffD = argQ;
|
||||
argD = port(ff, \D);
|
||||
argQ = Q;
|
||||
dffD.replace(argQ, argD);
|
||||
// Only search for ffrstmux if dffD only
|
||||
// has two (ff, ffrstmux) users
|
||||
if (nusers(dffD) > 2)
|
||||
argD = SigSpec();
|
||||
endcode
|
||||
|
||||
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||
// exclusively drives the 'D' input of the $dff, with one of the $mux
|
||||
// inputs being fully zero
|
||||
match ffrstmux
|
||||
if !argD.empty()
|
||||
select ffrstmux->type.in($mux)
|
||||
index <SigSpec> port(ffrstmux, \Y) === argD
|
||||
|
||||
choice <IdString> BA {\B, \A}
|
||||
// DSP48E1 only supports reset to zero
|
||||
select port(ffrstmux, BA).is_fully_zero()
|
||||
|
||||
define <bool> pol (BA == \B)
|
||||
set ffrstpol pol
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
if (ffrstmux) {
|
||||
dffrstmux = ffrstmux;
|
||||
dffrstpol = ffrstpol;
|
||||
argD = port(ffrstmux, ffrstpol ? \A : \B);
|
||||
dffD.replace(port(ffrstmux, \Y), argD);
|
||||
|
||||
// Only search for ffcemux if argQ has at
|
||||
// least 3 users (ff, <upstream>, ffrstmux) and
|
||||
// dffD only has two (ff, ffrstmux)
|
||||
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
|
||||
argD = SigSpec();
|
||||
}
|
||||
else
|
||||
dffrstmux = nullptr;
|
||||
endcode
|
||||
|
||||
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||
// the 'Q' output of the $dff
|
||||
match ffcemux
|
||||
if !argD.empty()
|
||||
select ffcemux->type.in($mux)
|
||||
index <SigSpec> port(ffcemux, \Y) === argD
|
||||
choice <IdString> AB {\A, \B}
|
||||
index <SigSpec> port(ffcemux, AB) === argQ
|
||||
define <bool> pol (AB == \A)
|
||||
set ffcepol pol
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
if (ffcemux) {
|
||||
dffcemux = ffcemux;
|
||||
dffcepol = ffcepol;
|
||||
argD = port(ffcemux, ffcepol ? \B : \A);
|
||||
dffD.replace(port(ffcemux, \Y), argD);
|
||||
}
|
||||
else
|
||||
dffcemux = nullptr;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching against output registers, based on knowledge of the
|
||||
// 'D' input.
|
||||
// At a high level:
|
||||
// (1) Starting from an optional $mux cell that implements clock enable
|
||||
// semantics --- one where the given 'D' argument (partially or fully)
|
||||
// drives one of its two inputs
|
||||
// (2) Starting from, or continuing onto, another optional $mux cell that
|
||||
// implements synchronous reset semantics --- one where the given 'D'
|
||||
// argument (or the clock enable $mux output) drives one of its two inputs
|
||||
// and where the other input is fully zero
|
||||
// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
|
||||
// output of the previous clock enable or reset $mux cells)
|
||||
subpattern out_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argD.chunks())
|
||||
// Abandon matches when 'D' has the keep attribute set
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
endcode
|
||||
|
||||
// (1) Starting from an optional $mux cell that implements clock enable
|
||||
// semantics --- one where the given 'D' argument (partially or fully)
|
||||
// drives one of its two inputs
|
||||
match ffcemux
|
||||
select ffcemux->type.in($mux)
|
||||
// ffcemux output must have two users: ffcemux and ff.D
|
||||
select nusers(port(ffcemux, \Y)) == 2
|
||||
|
||||
choice <IdString> AB {\A, \B}
|
||||
// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
|
||||
select nusers(port(ffcemux, AB)) >= 3
|
||||
|
||||
slice offset GetSize(port(ffcemux, \Y))
|
||||
define <IdString> BA (AB == \A ? \B : \A)
|
||||
index <SigBit> port(ffcemux, BA)[offset] === argD[0]
|
||||
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
|
||||
filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
|
||||
|
||||
set ffoffset offset
|
||||
define <bool> pol (AB == \A)
|
||||
set ffcepol pol
|
||||
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD argQ
|
||||
dffcemux = ffcemux;
|
||||
if (ffcemux) {
|
||||
SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
|
||||
SigSpec Y = port(ffcemux, \Y);
|
||||
argQ = argD;
|
||||
argD.replace(BA, Y);
|
||||
argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
|
||||
|
||||
dffcemux = ffcemux;
|
||||
dffcepol = ffcepol;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (2) Starting from, or continuing onto, another optional $mux cell that
|
||||
// implements synchronous reset semantics --- one where the given 'D'
|
||||
// argument (or the clock enable $mux output) drives one of its two inputs
|
||||
// and where the other input is fully zero
|
||||
match ffrstmux
|
||||
select ffrstmux->type.in($mux)
|
||||
// ffrstmux output must have two users: ffrstmux and ff.D
|
||||
select nusers(port(ffrstmux, \Y)) == 2
|
||||
|
||||
choice <IdString> BA {\B, \A}
|
||||
// DSP48E1 only supports reset to zero
|
||||
select port(ffrstmux, BA).is_fully_zero()
|
||||
|
||||
slice offset GetSize(port(ffrstmux, \Y))
|
||||
define <IdString> AB (BA == \B ? \A : \B)
|
||||
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
|
||||
|
||||
// Check that offset is consistent
|
||||
filter !ffcemux || ffoffset == offset
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
|
||||
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
|
||||
|
||||
set ffoffset offset
|
||||
define <bool> pol (AB == \A)
|
||||
set ffrstpol pol
|
||||
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD argQ
|
||||
dffrstmux = ffrstmux;
|
||||
if (ffrstmux) {
|
||||
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
|
||||
SigSpec Y = port(ffrstmux, \Y);
|
||||
argD.replace(AB, Y);
|
||||
|
||||
dffrstmux = ffrstmux;
|
||||
dffrstpol = ffrstpol;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
|
||||
// output of the previous clock enable or reset $mux cells)
|
||||
match ff
|
||||
select ff->type.in($dff)
|
||||
// DSP48E1 does not support clock inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \D)[offset] === argD[0]
|
||||
|
||||
// Check that offset is consistent
|
||||
filter (!ffcemux && !ffrstmux) || ffoffset == offset
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||
// Check that FF.Q is connected to CE-mux
|
||||
filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||
|
||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||
|
||||
set ffoffset offset
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
if (!ffcemux) {
|
||||
argQ = argD;
|
||||
argQ.replace(D, Q);
|
||||
}
|
||||
|
||||
// Abandon matches when 'Q' has a non-zero init attribute set
|
||||
// (not supported by DSP48E1)
|
||||
for (auto c : argQ.chunks()) {
|
||||
Const init = c.wire->attributes.at(\init, Const());
|
||||
if (!init.empty())
|
||||
for (auto b : init.extract(c.offset, c.width))
|
||||
if (b != State::Sx && b != State::S0)
|
||||
reject;
|
||||
}
|
||||
|
||||
dff = ff;
|
||||
dffQ = argQ;
|
||||
dffclock = port(ff, \CLK);
|
||||
endcode
|
|
@ -1,7 +1,7 @@
|
|||
// This file describes the second of three pattern matcher setups that
|
||||
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
||||
// At a high level, it works as follows:
|
||||
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
|
||||
// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
|
||||
// and (b) uses the 'C' port
|
||||
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
|
||||
// (attached to at most two $mux cells that implement clock-enable or
|
||||
|
@ -38,10 +38,10 @@ udata <SigBit> dffclock
|
|||
udata <Cell*> dff dffcemux dffrstmux
|
||||
udata <bool> dffcepol dffrstpol
|
||||
|
||||
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
|
||||
// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
|
||||
// and (b) uses the 'C' port
|
||||
match dsp
|
||||
select dsp->type.in(\DSP48E1)
|
||||
select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
|
||||
select param(dsp, \CREG, 1).as_int() == 0
|
||||
select nusers(port(dsp, \C, SigSpec())) > 1
|
||||
endmatch
|
||||
|
@ -60,7 +60,8 @@ code sigC sigP clock
|
|||
sigC = unextend(port(dsp, \C, SigSpec()));
|
||||
|
||||
SigSpec P = port(dsp, \P);
|
||||
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||
if (!dsp->type.in(\DSP48E1) ||
|
||||
param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||
// Only care about those bits that are used
|
||||
int i;
|
||||
for (i = GetSize(P)-1; i >= 0; i--)
|
||||
|
|
|
@ -62,12 +62,11 @@ code
|
|||
#define MAX_DSP_CASCADE 20
|
||||
endcode
|
||||
|
||||
// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer
|
||||
// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already
|
||||
// use the 'PCOUT' port
|
||||
// (1) Starting from a DSP48* cell that (a) has the Z multiplexer
|
||||
// (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1)
|
||||
// set to zero and (b) doesn't already use the 'PCOUT' port
|
||||
match first
|
||||
select first->type.in(\DSP48E1)
|
||||
select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
|
||||
select (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000"))
|
||||
select nusers(port(first, \PCOUT, SigSpec())) <= 1
|
||||
endmatch
|
||||
|
||||
|
@ -100,14 +99,21 @@ finally
|
|||
add_siguser(cascade, dsp);
|
||||
|
||||
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
|
||||
if (P == 17)
|
||||
opmode[6] = State::S1;
|
||||
else if (P == 0)
|
||||
opmode[6] = State::S0;
|
||||
else log_abort();
|
||||
if (dsp->type.in(\DSP48A, \DSP48A1)) {
|
||||
log_assert(P == 0);
|
||||
opmode[3] = State::S0;
|
||||
opmode[2] = State::S1;
|
||||
}
|
||||
else if (dsp->type.in(\DSP48E1)) {
|
||||
if (P == 17)
|
||||
opmode[6] = State::S1;
|
||||
else if (P == 0)
|
||||
opmode[6] = State::S0;
|
||||
else log_abort();
|
||||
|
||||
opmode[5] = State::S0;
|
||||
opmode[4] = State::S1;
|
||||
opmode[5] = State::S0;
|
||||
opmode[4] = State::S1;
|
||||
}
|
||||
dsp_pcin->setPort(\OPMODE, opmode);
|
||||
|
||||
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||
|
@ -120,21 +126,42 @@ finally
|
|||
add_siguser(cascade, dsp_pcin);
|
||||
add_siguser(cascade, dsp);
|
||||
|
||||
dsp->setParam(ID(ACASCREG), AREG);
|
||||
if (dsp->type.in(\DSP48E1))
|
||||
dsp->setParam(ID(ACASCREG), AREG);
|
||||
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
|
||||
|
||||
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||
}
|
||||
if (BREG >= 0) {
|
||||
Wire *cascade = module->addWire(NEW_ID, 18);
|
||||
dsp_pcin->setPort(ID(B), Const(0, 18));
|
||||
dsp_pcin->setPort(ID(BCIN), cascade);
|
||||
if (dsp->type.in(\DSP48A, \DSP48A1)) {
|
||||
// According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
|
||||
// "The DSP48A1 component uses this input when cascading
|
||||
// BCOUT from an adjacent DSP48A1 slice. The tools then
|
||||
// translate BCOUT cascading to the dedicated BCIN input
|
||||
// and set the B_INPUT attribute for implementation."
|
||||
dsp_pcin->setPort(ID(B), cascade);
|
||||
}
|
||||
else {
|
||||
dsp_pcin->setPort(ID(B), Const(0, 18));
|
||||
dsp_pcin->setPort(ID(BCIN), cascade);
|
||||
}
|
||||
dsp->setPort(ID(BCOUT), cascade);
|
||||
add_siguser(cascade, dsp_pcin);
|
||||
add_siguser(cascade, dsp);
|
||||
|
||||
dsp->setParam(ID(BCASCREG), BREG);
|
||||
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
|
||||
if (dsp->type.in(\DSP48E1)) {
|
||||
dsp->setParam(ID(BCASCREG), BREG);
|
||||
// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
|
||||
// "The attribute is only used by place and route tools and
|
||||
// is not necessary for the users to set for synthesis. The
|
||||
// attribute is determined by the connection to the B port
|
||||
// of the DSP48A1 slice. If the B port is connected to the
|
||||
// BCOUT of another DSP48A1 slice, then the tools automatically
|
||||
// set the attribute to 'CASCADE', otherwise it is set to
|
||||
// 'DIRECT'".
|
||||
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
|
||||
}
|
||||
|
||||
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||
}
|
||||
|
@ -156,22 +183,21 @@ subpattern tail
|
|||
arg first
|
||||
arg next
|
||||
|
||||
// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled,
|
||||
// (2.1) Match another DSP48* cell that (a) does not have the CREG enabled,
|
||||
// (b) has its Z multiplexer output set to the 'C' port, which is
|
||||
// driven by the 'P' output of the previous DSP cell, and (c) has its
|
||||
// 'PCIN' port unused
|
||||
match nextP
|
||||
select nextP->type.in(\DSP48E1)
|
||||
select !param(nextP, \CREG, State::S1).as_bool()
|
||||
select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
|
||||
select (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011"))
|
||||
select nusers(port(nextP, \C, SigSpec())) > 1
|
||||
select nusers(port(nextP, \PCIN, SigSpec())) == 0
|
||||
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the
|
||||
// previous DSP cell right-shifted by 17 bits
|
||||
// (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven
|
||||
// by the 'P' output of the previous DSP cell right-shifted by 17 bits
|
||||
match nextP_shift17
|
||||
if !nextP
|
||||
select nextP_shift17->type.in(\DSP48E1)
|
||||
|
@ -188,6 +214,8 @@ code next
|
|||
if (!nextP)
|
||||
next = nextP_shift17;
|
||||
if (next) {
|
||||
if (next->type != first->type)
|
||||
reject;
|
||||
unextend = [](const SigSpec &sig) {
|
||||
int i;
|
||||
for (i = GetSize(sig)-1; i > 0; i--)
|
||||
|
@ -202,38 +230,50 @@ code next
|
|||
endcode
|
||||
|
||||
// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
|
||||
// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this
|
||||
// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already
|
||||
// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already
|
||||
// use its ACOUT port, then examine if an ACOUT -> ACIN cascade
|
||||
// opportunity exists by matching for a $dff-with-optional-clock-enable-
|
||||
// or-reset and checking that the 'D' input of this register is the same
|
||||
// as the 'A' input of the previous DSP
|
||||
// if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade,
|
||||
// (b) the previous DSP does not already use its ACOUT port, then
|
||||
// examine if an ACOUT -> ACIN cascade opportunity exists if
|
||||
// (i) A ports are identical, or (ii) separated by a
|
||||
// $dff-with-optional-clock-enable-or-reset and checking that the 'D' input
|
||||
// of this register is the same as the 'A' input of the previous DSP
|
||||
// TODO: Check for two levels of flops, instead of just one
|
||||
code argQ clock AREG
|
||||
AREG = -1;
|
||||
if (next) {
|
||||
if (next && next->type.in(\DSP48E1)) {
|
||||
Cell *prev = std::get<0>(chain.back());
|
||||
if (param(prev, \AREG, 2).as_int() > 0 &&
|
||||
param(next, \AREG, 2).as_int() > 0 &&
|
||||
param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||
|
||||
if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||
port(next, \ACIN, SigSpec()).is_fully_zero() &&
|
||||
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
|
||||
argQ = unextend(port(next, \A));
|
||||
clock = port(prev, \CLK);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
||||
goto reject_AREG;
|
||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
||||
goto reject_AREG;
|
||||
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0)
|
||||
goto reject_AREG;
|
||||
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0))
|
||||
goto reject_AREG;
|
||||
if (dffD == unextend(port(prev, \A)))
|
||||
AREG = 1;
|
||||
reject_AREG: ;
|
||||
if (param(prev, \AREG, 2) == 0) {
|
||||
if (port(prev, \A) == port(next, \A))
|
||||
AREG = 0;
|
||||
}
|
||||
else {
|
||||
argQ = unextend(port(next, \A));
|
||||
clock = port(prev, \CLK);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
||||
goto reject_AREG;
|
||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
||||
goto reject_AREG;
|
||||
IdString CEA;
|
||||
if (param(prev, \AREG, 2) == 1)
|
||||
CEA = \CEA2;
|
||||
else if (param(prev, \AREG, 2) == 2)
|
||||
CEA = \CEA1;
|
||||
else log_abort();
|
||||
if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
|
||||
goto reject_AREG;
|
||||
if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
|
||||
goto reject_AREG;
|
||||
if (dffD == unextend(port(prev, \A)))
|
||||
AREG = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
reject_AREG: ;
|
||||
}
|
||||
endcode
|
||||
|
||||
|
@ -242,28 +282,47 @@ code argQ clock BREG
|
|||
BREG = -1;
|
||||
if (next) {
|
||||
Cell *prev = std::get<0>(chain.back());
|
||||
if (param(prev, \BREG, 2).as_int() > 0 &&
|
||||
param(next, \BREG, 2).as_int() > 0 &&
|
||||
param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||
if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||
port(next, \BCIN, SigSpec()).is_fully_zero() &&
|
||||
nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
|
||||
argQ = unextend(port(next, \B));
|
||||
clock = port(prev, \CLK);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
|
||||
goto reject_BREG;
|
||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
|
||||
goto reject_BREG;
|
||||
if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0)
|
||||
goto reject_BREG;
|
||||
if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0))
|
||||
goto reject_BREG;
|
||||
if (dffD == unextend(port(prev, \B)))
|
||||
BREG = 1;
|
||||
reject_BREG: ;
|
||||
if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) ||
|
||||
(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) {
|
||||
if (port(prev, \B) == port(next, \B))
|
||||
BREG = 0;
|
||||
}
|
||||
else {
|
||||
argQ = unextend(port(next, \B));
|
||||
clock = port(prev, \CLK);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
|
||||
goto reject_BREG;
|
||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
|
||||
goto reject_BREG;
|
||||
IdString CEB;
|
||||
if (next->type.in(\DSP48A, \DSP48A1))
|
||||
CEB = \CEB;
|
||||
else if (next->type.in(\DSP48E1)) {
|
||||
if (param(prev, \BREG, 2) == 1)
|
||||
CEB = \CEB2;
|
||||
else if (param(prev, \BREG, 2) == 2)
|
||||
CEB = \CEB1;
|
||||
else log_abort();
|
||||
}
|
||||
else log_abort();
|
||||
if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
|
||||
goto reject_BREG;
|
||||
if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
|
||||
goto reject_BREG;
|
||||
if (dffD == unextend(port(prev, \B))) {
|
||||
if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0)
|
||||
goto reject_BREG;
|
||||
BREG = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reject_BREG: ;
|
||||
}
|
||||
endcode
|
||||
|
||||
|
|
|
@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o
|
|||
OBJS += passes/sat/fmcombine.o
|
||||
OBJS += passes/sat/mutate.o
|
||||
OBJS += passes/sat/cutpoint.o
|
||||
OBJS += passes/sat/fminit.o
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
RTLIL::SigSpec bit = lhs.extract(i, 1);
|
||||
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);
|
||||
rhs.remove(i, 1);
|
||||
i--;
|
||||
|
|
|
@ -230,7 +230,7 @@ struct SimInstance
|
|||
bool did_something = false;
|
||||
|
||||
sig = sigmap(sig);
|
||||
log_assert(GetSize(sig) == GetSize(value));
|
||||
log_assert(GetSize(sig) <= GetSize(value));
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (state_nets.at(sig[i]) != value[i]) {
|
||||
|
|
|
@ -8,9 +8,12 @@ OBJS += passes/techmap/libparse.o
|
|||
ifeq ($(ENABLE_ABC),1)
|
||||
OBJS += passes/techmap/abc.o
|
||||
OBJS += passes/techmap/abc9.o
|
||||
OBJS += passes/techmap/abc9_exe.o
|
||||
OBJS += passes/techmap/abc9_ops.o
|
||||
ifneq ($(ABCEXTERNAL),)
|
||||
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||
passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||
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
|
||||
// http://en.wikipedia.org/wiki/Topological_sorting
|
||||
|
||||
#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
|
||||
#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
|
||||
#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; if; mfs2"
|
||||
#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; cover {I} {P}"
|
||||
#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
|
||||
#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
|
||||
#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
|
||||
#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2"
|
||||
#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}"
|
||||
#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
|
||||
|
||||
#define ABC_FAST_COMMAND_LIB "strash; dretime; retime {D}; map {D}"
|
||||
#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
|
||||
#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if"
|
||||
#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}"
|
||||
#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map"
|
||||
#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}"
|
||||
#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
|
||||
#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
|
||||
#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
|
||||
#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
|
||||
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
@ -747,6 +747,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
else
|
||||
abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
|
||||
|
||||
if (script_file.empty() && !delay_target.empty())
|
||||
for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
|
||||
abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
|
||||
|
||||
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
|
||||
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
|
||||
|
||||
|
@ -1510,7 +1514,47 @@ struct AbcPass : public Pass {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
size_t argidx;
|
||||
// get arguments from scratchpad first, then override by command arguments
|
||||
std::string lut_arg, luts_arg, g_arg;
|
||||
exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */);
|
||||
script_file = design->scratchpad_get_string("abc.script", script_file);
|
||||
liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file);
|
||||
constr_file = design->scratchpad_get_string("abc.constr", constr_file);
|
||||
if (design->scratchpad.count("abc.D")) {
|
||||
delay_target = "-D " + design->scratchpad_get_string("abc.D");
|
||||
}
|
||||
if (design->scratchpad.count("abc.I")) {
|
||||
sop_inputs = "-I " + design->scratchpad_get_string("abc.I");
|
||||
}
|
||||
if (design->scratchpad.count("abc.P")) {
|
||||
sop_products = "-P " + design->scratchpad_get_string("abc.P");
|
||||
}
|
||||
if (design->scratchpad.count("abc.S")) {
|
||||
lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
|
||||
}
|
||||
lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
|
||||
luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
|
||||
sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode);
|
||||
map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4);
|
||||
map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8);
|
||||
map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16);
|
||||
abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress);
|
||||
g_arg = design->scratchpad_get_string("abc.g", g_arg);
|
||||
|
||||
fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode);
|
||||
dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode);
|
||||
if (design->scratchpad.count("abc.clk")) {
|
||||
clk_str = design->scratchpad_get_string("abc.clk");
|
||||
dff_mode = true;
|
||||
}
|
||||
keepff = design->scratchpad_get_bool("abc.keepff", keepff);
|
||||
cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup);
|
||||
keepff = design->scratchpad_get_bool("abc.keepff", keepff);
|
||||
show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir);
|
||||
markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups);
|
||||
|
||||
size_t argidx, g_argidx;
|
||||
bool g_arg_from_cmd = false;
|
||||
char pwd [PATH_MAX];
|
||||
if (!getcwd(pwd, sizeof(pwd))) {
|
||||
log_cmd_error("getcwd failed: %s\n", strerror(errno));
|
||||
|
@ -1524,23 +1568,14 @@ struct AbcPass : public Pass {
|
|||
}
|
||||
if (arg == "-script" && argidx+1 < args.size()) {
|
||||
script_file = args[++argidx];
|
||||
rewrite_filename(script_file);
|
||||
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
|
||||
script_file = std::string(pwd) + "/" + script_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-liberty" && argidx+1 < args.size()) {
|
||||
liberty_file = args[++argidx];
|
||||
rewrite_filename(liberty_file);
|
||||
if (!liberty_file.empty() && !is_absolute_path(liberty_file))
|
||||
liberty_file = std::string(pwd) + "/" + liberty_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-constr" && argidx+1 < args.size()) {
|
||||
rewrite_filename(constr_file);
|
||||
constr_file = args[++argidx];
|
||||
if (!constr_file.empty() && !is_absolute_path(constr_file))
|
||||
constr_file = std::string(pwd) + "/" + constr_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-D" && argidx+1 < args.size()) {
|
||||
|
@ -1560,37 +1595,11 @@ struct AbcPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-lut" && argidx+1 < args.size()) {
|
||||
string arg = args[++argidx];
|
||||
size_t pos = arg.find_first_of(':');
|
||||
int lut_mode = 0, lut_mode2 = 0;
|
||||
if (pos != string::npos) {
|
||||
lut_mode = atoi(arg.substr(0, pos).c_str());
|
||||
lut_mode2 = atoi(arg.substr(pos+1).c_str());
|
||||
} else {
|
||||
lut_mode = atoi(arg.c_str());
|
||||
lut_mode2 = lut_mode;
|
||||
}
|
||||
lut_costs.clear();
|
||||
for (int i = 0; i < lut_mode; i++)
|
||||
lut_costs.push_back(1);
|
||||
for (int i = lut_mode; i < lut_mode2; i++)
|
||||
lut_costs.push_back(2 << (i - lut_mode));
|
||||
lut_arg = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (arg == "-luts" && argidx+1 < args.size()) {
|
||||
lut_costs.clear();
|
||||
for (auto &tok : split_tokens(args[++argidx], ",")) {
|
||||
auto parts = split_tokens(tok, ":");
|
||||
if (GetSize(parts) == 0 && !lut_costs.empty())
|
||||
lut_costs.push_back(lut_costs.back());
|
||||
else if (GetSize(parts) == 1)
|
||||
lut_costs.push_back(atoi(parts.at(0).c_str()));
|
||||
else if (GetSize(parts) == 2)
|
||||
while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
|
||||
lut_costs.push_back(atoi(parts.at(1).c_str()));
|
||||
else
|
||||
log_cmd_error("Invalid -luts syntax.\n");
|
||||
}
|
||||
luts_arg = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (arg == "-sop") {
|
||||
|
@ -1614,123 +1623,11 @@ struct AbcPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
if (arg == "-g" && argidx+1 < args.size()) {
|
||||
for (auto g : split_tokens(args[++argidx], ",")) {
|
||||
vector<string> gate_list;
|
||||
bool remove_gates = false;
|
||||
if (GetSize(g) > 0 && g[0] == '-') {
|
||||
remove_gates = true;
|
||||
g = g.substr(1);
|
||||
}
|
||||
if (g == "AND") goto ok_gate;
|
||||
if (g == "NAND") goto ok_gate;
|
||||
if (g == "OR") goto ok_gate;
|
||||
if (g == "NOR") goto ok_gate;
|
||||
if (g == "XOR") goto ok_gate;
|
||||
if (g == "XNOR") goto ok_gate;
|
||||
if (g == "ANDNOT") goto ok_gate;
|
||||
if (g == "ORNOT") goto ok_gate;
|
||||
if (g == "MUX") goto ok_gate;
|
||||
if (g == "NMUX") goto ok_gate;
|
||||
if (g == "AOI3") goto ok_gate;
|
||||
if (g == "OAI3") goto ok_gate;
|
||||
if (g == "AOI4") goto ok_gate;
|
||||
if (g == "OAI4") goto ok_gate;
|
||||
if (g == "simple") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("MUX");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos2") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos3") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos4") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
gate_list.push_back("NMUX");
|
||||
gate_list.push_back("MUX");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "gates") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "aig") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "all") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
gate_list.push_back("MUX");
|
||||
gate_list.push_back("NMUX");
|
||||
}
|
||||
cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str()));
|
||||
ok_gate:
|
||||
gate_list.push_back(g);
|
||||
ok_alias:
|
||||
for (auto gate : gate_list) {
|
||||
if (remove_gates)
|
||||
enabled_gates.erase(gate);
|
||||
else
|
||||
enabled_gates.insert(gate);
|
||||
}
|
||||
}
|
||||
if (g_arg_from_cmd)
|
||||
log_cmd_error("Can only use -g once. Please combine.");
|
||||
g_arg = args[++argidx];
|
||||
g_argidx = argidx;
|
||||
g_arg_from_cmd = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-fast") {
|
||||
|
@ -1766,8 +1663,176 @@ struct AbcPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
rewrite_filename(script_file);
|
||||
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
|
||||
script_file = std::string(pwd) + "/" + script_file;
|
||||
rewrite_filename(liberty_file);
|
||||
if (!liberty_file.empty() && !is_absolute_path(liberty_file))
|
||||
liberty_file = std::string(pwd) + "/" + liberty_file;
|
||||
rewrite_filename(constr_file);
|
||||
if (!constr_file.empty() && !is_absolute_path(constr_file))
|
||||
constr_file = std::string(pwd) + "/" + constr_file;
|
||||
|
||||
// handle -lut argument
|
||||
if (!lut_arg.empty()) {
|
||||
size_t pos = lut_arg.find_first_of(':');
|
||||
int lut_mode = 0, lut_mode2 = 0;
|
||||
if (pos != string::npos) {
|
||||
lut_mode = atoi(lut_arg.substr(0, pos).c_str());
|
||||
lut_mode2 = atoi(lut_arg.substr(pos+1).c_str());
|
||||
} else {
|
||||
lut_mode = atoi(lut_arg.c_str());
|
||||
lut_mode2 = lut_mode;
|
||||
}
|
||||
lut_costs.clear();
|
||||
for (int i = 0; i < lut_mode; i++)
|
||||
lut_costs.push_back(1);
|
||||
for (int i = lut_mode; i < lut_mode2; i++)
|
||||
lut_costs.push_back(2 << (i - lut_mode));
|
||||
}
|
||||
//handle -luts argument
|
||||
if (!luts_arg.empty()){
|
||||
lut_costs.clear();
|
||||
for (auto &tok : split_tokens(luts_arg, ",")) {
|
||||
auto parts = split_tokens(tok, ":");
|
||||
if (GetSize(parts) == 0 && !lut_costs.empty())
|
||||
lut_costs.push_back(lut_costs.back());
|
||||
else if (GetSize(parts) == 1)
|
||||
lut_costs.push_back(atoi(parts.at(0).c_str()));
|
||||
else if (GetSize(parts) == 2)
|
||||
while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
|
||||
lut_costs.push_back(atoi(parts.at(1).c_str()));
|
||||
else
|
||||
log_cmd_error("Invalid -luts syntax.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// handle -g argument
|
||||
if (!g_arg.empty()){
|
||||
for (auto g : split_tokens(g_arg, ",")) {
|
||||
vector<string> gate_list;
|
||||
bool remove_gates = false;
|
||||
if (GetSize(g) > 0 && g[0] == '-') {
|
||||
remove_gates = true;
|
||||
g = g.substr(1);
|
||||
}
|
||||
if (g == "AND") goto ok_gate;
|
||||
if (g == "NAND") goto ok_gate;
|
||||
if (g == "OR") goto ok_gate;
|
||||
if (g == "NOR") goto ok_gate;
|
||||
if (g == "XOR") goto ok_gate;
|
||||
if (g == "XNOR") goto ok_gate;
|
||||
if (g == "ANDNOT") goto ok_gate;
|
||||
if (g == "ORNOT") goto ok_gate;
|
||||
if (g == "MUX") goto ok_gate;
|
||||
if (g == "NMUX") goto ok_gate;
|
||||
if (g == "AOI3") goto ok_gate;
|
||||
if (g == "OAI3") goto ok_gate;
|
||||
if (g == "AOI4") goto ok_gate;
|
||||
if (g == "OAI4") goto ok_gate;
|
||||
if (g == "simple") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("MUX");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos2") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos3") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos4") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "cmos") {
|
||||
if (!remove_gates)
|
||||
cmos_cost = true;
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
gate_list.push_back("NMUX");
|
||||
gate_list.push_back("MUX");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "gates") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "aig") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
goto ok_alias;
|
||||
}
|
||||
if (g == "all") {
|
||||
gate_list.push_back("AND");
|
||||
gate_list.push_back("NAND");
|
||||
gate_list.push_back("OR");
|
||||
gate_list.push_back("NOR");
|
||||
gate_list.push_back("XOR");
|
||||
gate_list.push_back("XNOR");
|
||||
gate_list.push_back("ANDNOT");
|
||||
gate_list.push_back("ORNOT");
|
||||
gate_list.push_back("AOI3");
|
||||
gate_list.push_back("OAI3");
|
||||
gate_list.push_back("AOI4");
|
||||
gate_list.push_back("OAI4");
|
||||
gate_list.push_back("MUX");
|
||||
gate_list.push_back("NMUX");
|
||||
}
|
||||
if (g_arg_from_cmd)
|
||||
cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str()));
|
||||
else
|
||||
log_cmd_error("Unsupported gate type: %s", g.c_str());
|
||||
ok_gate:
|
||||
gate_list.push_back(g);
|
||||
ok_alias:
|
||||
for (auto gate : gate_list) {
|
||||
if (remove_gates)
|
||||
enabled_gates.erase(gate);
|
||||
else
|
||||
enabled_gates.insert(gate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lut_costs.empty() && !liberty_file.empty())
|
||||
log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n");
|
||||
log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n");
|
||||
if (!constr_file.empty() && liberty_file.empty())
|
||||
log_cmd_error("Got -constr but no -liberty!\n");
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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.
|
||||
pool<pair<IdString, pair<IdString, int>>> sink_ports;
|
||||
pool<pair<IdString, pair<IdString, int>>> buf_ports;
|
||||
dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_out;
|
||||
dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_in;
|
||||
|
||||
// Process submodules before module using them.
|
||||
std::vector<Module *> modules_sorted;
|
||||
|
@ -133,6 +135,14 @@ struct ClkbufmapPass : public Pass {
|
|||
if (wire->get_bool_attribute("\\clkbuf_sink"))
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
auto it = wire->attributes.find("\\clkbuf_inv");
|
||||
if (it != wire->attributes.end()) {
|
||||
IdString in_name = RTLIL::escape_id(it->second.decode_string());
|
||||
for (int i = 0; i < GetSize(wire); i++) {
|
||||
inv_ports_out[make_pair(module->name, make_pair(wire->name, i))] = make_pair(in_name, i);
|
||||
inv_ports_in[make_pair(module->name, make_pair(in_name, i))] = make_pair(wire->name, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -157,6 +167,37 @@ struct ClkbufmapPass : public Pass {
|
|||
if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
|
||||
buf_wire_bits.insert(sigmap(port.second[i]));
|
||||
|
||||
// Third, propagate tags through inverters.
|
||||
bool retry = true;
|
||||
while (retry) {
|
||||
retry = false;
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
for (int i = 0; i < port.second.size(); i++) {
|
||||
auto it = inv_ports_out.find(make_pair(cell->type, make_pair(port.first, i)));
|
||||
auto bit = sigmap(port.second[i]);
|
||||
// If output of an inverter is connected to a sink, mark it as buffered,
|
||||
// and request a buffer on the inverter's input instead.
|
||||
if (it != inv_ports_out.end() && !buf_wire_bits.count(bit) && sink_wire_bits.count(bit)) {
|
||||
buf_wire_bits.insert(bit);
|
||||
auto other_bit = sigmap(cell->getPort(it->second.first)[it->second.second]);
|
||||
sink_wire_bits.insert(other_bit);
|
||||
retry = true;
|
||||
}
|
||||
// If input of an inverter is marked as already-buffered,
|
||||
// mark its output already-buffered as well.
|
||||
auto it2 = inv_ports_in.find(make_pair(cell->type, make_pair(port.first, i)));
|
||||
if (it2 != inv_ports_in.end() && buf_wire_bits.count(bit)) {
|
||||
auto other_bit = sigmap(cell->getPort(it2->second.first)[it2->second.second]);
|
||||
if (!buf_wire_bits.count(other_bit)) {
|
||||
buf_wire_bits.insert(other_bit);
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Collect all driven bits.
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
|
|
|
@ -87,11 +87,11 @@ struct IopadmapPass : public Pass {
|
|||
{
|
||||
log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
|
||||
|
||||
std::string inpad_celltype, inpad_portname, inpad_portname2;
|
||||
std::string outpad_celltype, outpad_portname, outpad_portname2;
|
||||
std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
|
||||
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
|
||||
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
|
||||
std::string inpad_celltype, inpad_portname_o, inpad_portname_pad;
|
||||
std::string outpad_celltype, outpad_portname_i, outpad_portname_pad;
|
||||
std::string inoutpad_celltype, inoutpad_portname_io, inoutpad_portname_pad;
|
||||
std::string toutpad_celltype, toutpad_portname_oe, toutpad_portname_i, toutpad_portname_pad;
|
||||
std::string tinoutpad_celltype, tinoutpad_portname_oe, tinoutpad_portname_o, tinoutpad_portname_i, tinoutpad_portname_pad;
|
||||
std::string widthparam, nameparam;
|
||||
pool<pair<IdString, IdString>> ignore;
|
||||
bool flag_bits = false;
|
||||
|
@ -102,35 +102,35 @@ struct IopadmapPass : public Pass {
|
|||
std::string arg = args[argidx];
|
||||
if (arg == "-inpad" && argidx+2 < args.size()) {
|
||||
inpad_celltype = args[++argidx];
|
||||
inpad_portname = args[++argidx];
|
||||
split_portname_pair(inpad_portname, inpad_portname2);
|
||||
inpad_portname_o = args[++argidx];
|
||||
split_portname_pair(inpad_portname_o, inpad_portname_pad);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-outpad" && argidx+2 < args.size()) {
|
||||
outpad_celltype = args[++argidx];
|
||||
outpad_portname = args[++argidx];
|
||||
split_portname_pair(outpad_portname, outpad_portname2);
|
||||
outpad_portname_i = args[++argidx];
|
||||
split_portname_pair(outpad_portname_i, outpad_portname_pad);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-inoutpad" && argidx+2 < args.size()) {
|
||||
inoutpad_celltype = args[++argidx];
|
||||
inoutpad_portname = args[++argidx];
|
||||
split_portname_pair(inoutpad_portname, inoutpad_portname2);
|
||||
inoutpad_portname_io = args[++argidx];
|
||||
split_portname_pair(inoutpad_portname_io, inoutpad_portname_pad);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-toutpad" && argidx+2 < args.size()) {
|
||||
toutpad_celltype = args[++argidx];
|
||||
toutpad_portname = args[++argidx];
|
||||
split_portname_pair(toutpad_portname, toutpad_portname2);
|
||||
split_portname_pair(toutpad_portname2, toutpad_portname3);
|
||||
toutpad_portname_oe = args[++argidx];
|
||||
split_portname_pair(toutpad_portname_oe, toutpad_portname_i);
|
||||
split_portname_pair(toutpad_portname_i, toutpad_portname_pad);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-tinoutpad" && argidx+2 < args.size()) {
|
||||
tinoutpad_celltype = args[++argidx];
|
||||
tinoutpad_portname = args[++argidx];
|
||||
split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
|
||||
split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
|
||||
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
|
||||
tinoutpad_portname_oe = args[++argidx];
|
||||
split_portname_pair(tinoutpad_portname_oe, tinoutpad_portname_o);
|
||||
split_portname_pair(tinoutpad_portname_o, tinoutpad_portname_i);
|
||||
split_portname_pair(tinoutpad_portname_i, tinoutpad_portname_pad);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore" && argidx+2 < args.size()) {
|
||||
|
@ -161,16 +161,16 @@ struct IopadmapPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (!inpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
|
||||
if (!outpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
|
||||
if (!inoutpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
|
||||
if (!toutpad_portname3.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
|
||||
if (!tinoutpad_portname4.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
|
||||
if (!inpad_portname_pad.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname_pad)));
|
||||
if (!outpad_portname_pad.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname_pad)));
|
||||
if (!inoutpad_portname_pad.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname_pad)));
|
||||
if (!toutpad_portname_pad.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname_pad)));
|
||||
if (!tinoutpad_portname_pad.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname_pad)));
|
||||
|
||||
for (auto module : design->modules())
|
||||
if (module->get_blackbox_attribute())
|
||||
|
@ -180,148 +180,130 @@ struct IopadmapPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
dict<IdString, pool<int>> skip_wires;
|
||||
pool<SigBit> skip_wire_bits;
|
||||
SigMap sigmap(module);
|
||||
dict<Wire *, dict<int, pair<Cell *, IdString>>> rewrite_bits;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
if (ignore.count(make_pair(cell->type, port.first)))
|
||||
for (auto bit : sigmap(port.second))
|
||||
for (auto bit : port.second)
|
||||
skip_wire_bits.insert(bit);
|
||||
|
||||
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
|
||||
{
|
||||
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
|
||||
pool<pair<IdString, IdString>> norewrites;
|
||||
SigMap rewrites;
|
||||
dict<SigBit, Cell *> tbuf_bits;
|
||||
pool<SigBit> driven_bits;
|
||||
|
||||
// Gather tristate buffers and always-on drivers.
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type == ID($_TBUF_)) {
|
||||
SigBit bit = sigmap(cell->getPort(ID::Y).as_bit());
|
||||
tbuf_bits[bit].first = cell->name;
|
||||
SigBit bit = cell->getPort(ID::Y).as_bit();
|
||||
tbuf_bits[bit] = cell;
|
||||
} else {
|
||||
for (auto port : cell->connections())
|
||||
if (!cell->known() || cell->output(port.first))
|
||||
for (auto bit : port.second)
|
||||
driven_bits.insert(bit);
|
||||
}
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
for (auto bit : sigmap(port.second))
|
||||
if (tbuf_bits.count(bit))
|
||||
tbuf_bits.at(bit).second.insert(cell->name);
|
||||
// If a wire is a target of an assignment, it is driven, unless the source is 'z.
|
||||
for (auto &conn : module->connections())
|
||||
for (int i = 0; i < GetSize(conn.first); i++) {
|
||||
SigBit dstbit = conn.first[i];
|
||||
SigBit srcbit = conn.second[i];
|
||||
if (!srcbit.wire && srcbit.data == State::Sz)
|
||||
continue;
|
||||
driven_bits.insert(dstbit);
|
||||
}
|
||||
|
||||
for (auto wire : module->selected_wires())
|
||||
{
|
||||
if (!wire->port_output)
|
||||
continue;
|
||||
|
||||
// Don't handle inout ports if we have no suitable buffer type.
|
||||
if (wire->port_input && tinoutpad_celltype.empty())
|
||||
continue;
|
||||
|
||||
// likewise for output ports.
|
||||
if (!wire->port_input && toutpad_celltype.empty())
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
SigBit wire_bit(wire, i);
|
||||
SigBit mapped_wire_bit = sigmap(wire_bit);
|
||||
Cell *tbuf_cell = nullptr;
|
||||
|
||||
if (tbuf_bits.count(mapped_wire_bit) == 0)
|
||||
if (skip_wire_bits.count(wire_bit))
|
||||
continue;
|
||||
|
||||
if (skip_wire_bits.count(mapped_wire_bit))
|
||||
continue;
|
||||
if (tbuf_bits.count(wire_bit))
|
||||
tbuf_cell = tbuf_bits.at(wire_bit);
|
||||
|
||||
auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
|
||||
Cell *tbuf_cell = module->cell(tbuf_cache.first);
|
||||
SigBit en_sig;
|
||||
SigBit data_sig;
|
||||
bool is_driven = driven_bits.count(wire_bit);
|
||||
|
||||
if (tbuf_cell == nullptr)
|
||||
continue;
|
||||
if (tbuf_cell != nullptr) {
|
||||
// Found a tristate buffer — use it.
|
||||
en_sig = tbuf_cell->getPort(ID(E)).as_bit();
|
||||
data_sig = tbuf_cell->getPort(ID::A).as_bit();
|
||||
} else if (is_driven) {
|
||||
// No tristate buffer, but an always-on driver is present.
|
||||
// If this is an inout port, we're creating a tinoutpad
|
||||
// anyway, just with a constant 1 as enable.
|
||||
if (!wire->port_input)
|
||||
continue;
|
||||
en_sig = SigBit(State::S1);
|
||||
data_sig = wire_bit;
|
||||
} else {
|
||||
// No driver on a wire. Create a tristate pad with always-0
|
||||
// enable.
|
||||
en_sig = SigBit(State::S0);
|
||||
data_sig = SigBit(State::Sx);
|
||||
}
|
||||
|
||||
SigBit en_sig = tbuf_cell->getPort(ID(E)).as_bit();
|
||||
SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit();
|
||||
|
||||
if (wire->port_input && !tinoutpad_celltype.empty())
|
||||
if (wire->port_input)
|
||||
{
|
||||
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
|
||||
|
||||
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
|
||||
Wire *owire = module->addWire(NEW_ID);
|
||||
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig);
|
||||
cell->attributes[ID::keep] = RTLIL::Const(1);
|
||||
|
||||
for (auto cn : tbuf_cache.second) {
|
||||
auto c = module->cell(cn);
|
||||
if (c == nullptr)
|
||||
continue;
|
||||
for (auto port : c->connections()) {
|
||||
SigSpec sig = port.second;
|
||||
bool newsig = false;
|
||||
for (auto &bit : sig)
|
||||
if (sigmap(bit) == mapped_wire_bit) {
|
||||
bit = owire;
|
||||
newsig = true;
|
||||
}
|
||||
if (newsig)
|
||||
c->setPort(port.first, sig);
|
||||
}
|
||||
if (tbuf_cell) {
|
||||
module->remove(tbuf_cell);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
|
||||
} else if (is_driven) {
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit);
|
||||
} else {
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
|
||||
cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
|
||||
}
|
||||
|
||||
|
||||
module->remove(tbuf_cell);
|
||||
skip_wires[wire->name].insert(i);
|
||||
|
||||
norewrites.insert(make_pair(cell->name, RTLIL::escape_id(tinoutpad_portname4)));
|
||||
rewrites.add(sigmap(wire_bit), owire);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!wire->port_input && !toutpad_celltype.empty())
|
||||
{
|
||||
skip_wire_bits.insert(wire_bit);
|
||||
if (!tinoutpad_portname_pad.empty())
|
||||
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
|
||||
} else {
|
||||
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
|
||||
|
||||
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
|
||||
|
||||
cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig);
|
||||
cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
|
||||
cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
|
||||
cell->setPort(RTLIL::escape_id(toutpad_portname_oe), en_sig);
|
||||
cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);
|
||||
cell->attributes[ID::keep] = RTLIL::Const(1);
|
||||
|
||||
for (auto cn : tbuf_cache.second) {
|
||||
auto c = module->cell(cn);
|
||||
if (c == nullptr)
|
||||
continue;
|
||||
for (auto port : c->connections()) {
|
||||
SigSpec sig = port.second;
|
||||
bool newsig = false;
|
||||
for (auto &bit : sig)
|
||||
if (sigmap(bit) == mapped_wire_bit) {
|
||||
bit = data_sig;
|
||||
newsig = true;
|
||||
}
|
||||
if (newsig)
|
||||
c->setPort(port.first, sig);
|
||||
}
|
||||
if (tbuf_cell) {
|
||||
module->remove(tbuf_cell);
|
||||
module->connect(wire_bit, data_sig);
|
||||
}
|
||||
|
||||
module->remove(tbuf_cell);
|
||||
skip_wires[wire->name].insert(i);
|
||||
continue;
|
||||
skip_wire_bits.insert(wire_bit);
|
||||
if (!toutpad_portname_pad.empty())
|
||||
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GetSize(norewrites))
|
||||
{
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
{
|
||||
if (norewrites.count(make_pair(cell->name, port.first)))
|
||||
continue;
|
||||
|
||||
SigSpec orig_sig = sigmap(port.second);
|
||||
SigSpec new_sig = rewrites(orig_sig);
|
||||
|
||||
if (orig_sig != new_sig)
|
||||
cell->setPort(port.first, new_sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : module->selected_wires())
|
||||
|
@ -329,17 +311,11 @@ struct IopadmapPass : public Pass {
|
|||
if (!wire->port_id)
|
||||
continue;
|
||||
|
||||
std::string celltype, portname, portname2;
|
||||
std::string celltype, portname_int, portname_pad;
|
||||
pool<int> skip_bit_indices;
|
||||
|
||||
if (skip_wires.count(wire->name)) {
|
||||
if (!flag_bits)
|
||||
continue;
|
||||
skip_bit_indices = skip_wires.at(wire->name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
if (skip_wire_bits.count(sigmap(SigBit(wire, i))))
|
||||
if (skip_wire_bits.count(SigBit(wire, i)))
|
||||
skip_bit_indices.insert(i);
|
||||
|
||||
if (GetSize(wire) == GetSize(skip_bit_indices))
|
||||
|
@ -351,8 +327,8 @@ struct IopadmapPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
celltype = inpad_celltype;
|
||||
portname = inpad_portname;
|
||||
portname2 = inpad_portname2;
|
||||
portname_int = inpad_portname_o;
|
||||
portname_pad = inpad_portname_pad;
|
||||
} else
|
||||
if (!wire->port_input && wire->port_output) {
|
||||
if (outpad_celltype.empty()) {
|
||||
|
@ -360,8 +336,8 @@ struct IopadmapPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
celltype = outpad_celltype;
|
||||
portname = outpad_portname;
|
||||
portname2 = outpad_portname2;
|
||||
portname_int = outpad_portname_i;
|
||||
portname_pad = outpad_portname_pad;
|
||||
} else
|
||||
if (wire->port_input && wire->port_output) {
|
||||
if (inoutpad_celltype.empty()) {
|
||||
|
@ -369,8 +345,8 @@ struct IopadmapPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
celltype = inoutpad_celltype;
|
||||
portname = inoutpad_portname;
|
||||
portname2 = inoutpad_portname2;
|
||||
portname_int = inoutpad_portname_io;
|
||||
portname_pad = inoutpad_portname_pad;
|
||||
} else
|
||||
log_abort();
|
||||
|
||||
|
@ -381,29 +357,20 @@ struct IopadmapPass : public Pass {
|
|||
|
||||
log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str());
|
||||
|
||||
RTLIL::Wire *new_wire = NULL;
|
||||
if (!portname2.empty()) {
|
||||
new_wire = module->addWire(NEW_ID, wire);
|
||||
module->swap_names(new_wire, wire);
|
||||
wire->attributes.clear();
|
||||
}
|
||||
|
||||
if (flag_bits)
|
||||
{
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
{
|
||||
if (skip_bit_indices.count(i)) {
|
||||
if (wire->port_output)
|
||||
module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
|
||||
else
|
||||
module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
|
||||
if (skip_bit_indices.count(i))
|
||||
continue;
|
||||
}
|
||||
|
||||
SigBit wire_bit(wire, i);
|
||||
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
||||
cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
|
||||
if (!portname2.empty())
|
||||
cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i));
|
||||
cell->setPort(RTLIL::escape_id(portname_int), wire_bit);
|
||||
|
||||
if (!portname_pad.empty())
|
||||
rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(portname_pad));
|
||||
if (!widthparam.empty())
|
||||
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
|
||||
if (!nameparam.empty())
|
||||
|
@ -414,9 +381,15 @@ struct IopadmapPass : public Pass {
|
|||
else
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
|
||||
cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire));
|
||||
if (!portname2.empty())
|
||||
cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire));
|
||||
cell->setPort(RTLIL::escape_id(portname_int), RTLIL::SigSpec(wire));
|
||||
|
||||
if (!portname_pad.empty()) {
|
||||
RTLIL::Wire *new_wire = NULL;
|
||||
new_wire = module->addWire(NEW_ID, wire);
|
||||
module->swap_names(new_wire, wire);
|
||||
wire->attributes.clear();
|
||||
cell->setPort(RTLIL::escape_id(portname_pad), RTLIL::SigSpec(new_wire));
|
||||
}
|
||||
if (!widthparam.empty())
|
||||
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
|
||||
if (!nameparam.empty())
|
||||
|
@ -424,6 +397,32 @@ struct IopadmapPass : public Pass {
|
|||
cell->attributes[ID::keep] = RTLIL::Const(1);
|
||||
}
|
||||
|
||||
if (!rewrite_bits.count(wire)) {
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : rewrite_bits) {
|
||||
RTLIL::Wire *wire = it.first;
|
||||
RTLIL::Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
module->swap_names(new_wire, wire);
|
||||
wire->attributes.clear();
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
{
|
||||
SigBit wire_bit(wire, i);
|
||||
if (!it.second.count(i)) {
|
||||
if (wire->port_output)
|
||||
module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
|
||||
else
|
||||
module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
|
||||
} else {
|
||||
auto &new_conn = it.second.at(i);
|
||||
new_conn.first->setPort(new_conn.second, RTLIL::SigSpec(new_wire, i));
|
||||
}
|
||||
}
|
||||
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
|
|
|
@ -86,6 +86,7 @@ struct TribufWorker {
|
|||
cell->unsetPort(ID(S));
|
||||
cell->type = tri_type;
|
||||
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
||||
module->design->scratchpad_set_bool("tribuf.added_something", true);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -95,6 +96,7 @@ struct TribufWorker {
|
|||
cell->unsetPort(ID(S));
|
||||
cell->type = tri_type;
|
||||
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
|
||||
module->design->scratchpad_set_bool("tribuf.added_something", true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -130,8 +132,10 @@ struct TribufWorker {
|
|||
|
||||
if (no_tribuf)
|
||||
module->connect(it.first, muxout);
|
||||
else
|
||||
else {
|
||||
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("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -152,12 +152,12 @@ struct SynthAchronixPass : public ScriptPass {
|
|||
run("clean -purge");
|
||||
run("setundef -undriven -zero");
|
||||
if (retime || help_mode)
|
||||
run("abc -markgroups -dff", "(only if -retime)");
|
||||
run("abc -markgroups -dff -D 1", "(only if -retime)");
|
||||
}
|
||||
|
||||
if (check_label("map_luts"))
|
||||
{
|
||||
run("abc -lut 4" + string(retime ? " -dff" : ""));
|
||||
run("abc -lut 4" + string(retime ? " -dff -D 1" : ""));
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,6 @@ $(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_map.v))
|
|||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/arith_map.v))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/eagle_bb.v))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/dram_init_16x4.vh))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams.txt))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutrams_map.v))
|
||||
$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/lutram_init_16x4.vh))
|
||||
|
|
|
@ -6,14 +6,14 @@ module \$_DFFE_NP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REG
|
|||
module \$_DFFE_PN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule
|
||||
|
||||
module \$_DLATCH_N_ (E, D, Q);
|
||||
wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
|
||||
|
|
|
@ -10,7 +10,7 @@ module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
|
|||
input B1EN;
|
||||
|
||||
EG_LOGIC_DRAM16X4 #(
|
||||
`include "dram_init_16x4.vh"
|
||||
`include "lutram_init_16x4.vh"
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.di(B1DATA),
|
||||
.waddr(B1ADDR),
|
|
@ -58,7 +58,10 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log(" -nolutram\n");
|
||||
log(" do not use EG_LOGIC_DRAM16X4 cells in output netlist\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -67,7 +70,7 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, edif_file, json_file;
|
||||
bool flatten, retime;
|
||||
bool flatten, retime, nolutram;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -76,6 +79,7 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
json_file = "";
|
||||
flatten = true;
|
||||
retime = false;
|
||||
nolutram = false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -110,6 +114,10 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
flatten = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nolutram") {
|
||||
nolutram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-retime") {
|
||||
retime = true;
|
||||
continue;
|
||||
|
@ -150,21 +158,25 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
run("synth -run coarse");
|
||||
}
|
||||
|
||||
if (check_label("dram"))
|
||||
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
|
||||
{
|
||||
run("memory_bram -rules +/anlogic/drams.txt");
|
||||
run("techmap -map +/anlogic/drams_map.v");
|
||||
run("memory_bram -rules +/anlogic/lutrams.txt");
|
||||
run("techmap -map +/anlogic/lutrams_map.v");
|
||||
run("setundef -zero -params t:EG_LOGIC_DRAM16X4");
|
||||
}
|
||||
|
||||
if (check_label("fine"))
|
||||
if (check_label("map_ffram"))
|
||||
{
|
||||
run("opt -fast -mux_undef -undriven -fine");
|
||||
run("memory_map");
|
||||
run("opt -undriven -fine");
|
||||
}
|
||||
|
||||
if (check_label("map_gates"))
|
||||
{
|
||||
run("techmap -map +/techmap.v -map +/anlogic/arith_map.v");
|
||||
if (retime || help_mode)
|
||||
run("abc -dff", "(only if -retime)");
|
||||
run("abc -dff -D 1", "(only if -retime)");
|
||||
}
|
||||
|
||||
if (check_label("map_ffs"))
|
||||
|
@ -187,7 +199,7 @@ struct SynthAnlogicPass : public ScriptPass
|
|||
run("techmap -map +/anlogic/cells_map.v");
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
||||
if (check_label("map_anlogic"))
|
||||
{
|
||||
run("anlogic_fixcarry");
|
||||
|
|
|
@ -55,7 +55,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -161,7 +161,7 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
|||
|
||||
if (check_label("map_pla"))
|
||||
{
|
||||
run("abc -sop -I 40 -P 56");
|
||||
run("abc -sop -I 40 -P 56" + string(retime ? " -dff -D 1" : ""));
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
@ -194,8 +194,6 @@ struct SynthCoolrunner2Pass : public ScriptPass
|
|||
if (!json_file.empty() || help_mode)
|
||||
run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
|
||||
}
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} SynthCoolrunner2Pass;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ struct SynthEasicPass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -158,7 +158,7 @@ struct SynthEasicPass : public ScriptPass
|
|||
run("techmap");
|
||||
run("opt -fast");
|
||||
if (retime || help_mode) {
|
||||
run("abc -dff", " (only if -retime)");
|
||||
run("abc -dff -D 1", " (only if -retime)");
|
||||
run("opt_clean", "(only if -retime)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_map.v))
|
|||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_bb.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutram.txt))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/lutrams.txt))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams.txt))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))
|
||||
|
|
|
@ -1,43 +1,36 @@
|
|||
# NB: Inputs/Outputs must be ordered alphabetically
|
||||
# (with exceptions for carry in/out)
|
||||
# NB: Box inputs/outputs must each be in the same order
|
||||
# as their corresponding module definition
|
||||
# (with exceptions detailed below)
|
||||
|
||||
# Box 1 : CCU2C (2xCARRY + 2xLUT4)
|
||||
# Outputs: S0, S1, COUT
|
||||
# (NB: carry chain input/output must be last
|
||||
# input/output and bus has been moved
|
||||
# there overriding the otherwise
|
||||
# (Exception: carry chain input/output must be the
|
||||
# last input and output and the entire bus has been
|
||||
# moved there overriding the otherwise
|
||||
# alphabetical ordering)
|
||||
# name ID w/b ins outs
|
||||
CCU2C 1 1 9 3
|
||||
|
||||
#A0 A1 B0 B1 C0 C1 D0 D1 CIN
|
||||
379 - 379 - 275 - 141 - 257
|
||||
630 379 630 379 526 275 392 141 273
|
||||
516 516 516 516 412 412 278 278 43
|
||||
#A0 B0 C0 D0 A1 B1 C1 D1 CIN
|
||||
379 379 275 141 - - - - 257 # S0
|
||||
630 630 526 392 379 379 275 141 273 # S1
|
||||
516 516 412 278 516 516 412 278 43 # COUT
|
||||
|
||||
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
||||
# Outputs: DO0, DO1, DO2, DO3
|
||||
# name ID w/b ins outs
|
||||
$__ABC9_DPR16X4_COMB 2 0 8 4
|
||||
|
||||
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
#$DO0 $DO1 $DO2 $DO3 RAD0 RAD1 RAD2 RAD3
|
||||
0 0 0 0 141 379 275 379 # DO0
|
||||
0 0 0 0 141 379 275 379 # DO1
|
||||
0 0 0 0 141 379 275 379 # DO2
|
||||
0 0 0 0 141 379 275 379 # DO3
|
||||
|
||||
# Box 3 : PFUMX (MUX2)
|
||||
# Outputs: Z
|
||||
# name ID w/b ins outs
|
||||
PFUMX 3 1 3 1
|
||||
|
||||
#ALUT BLUT C0
|
||||
98 98 151
|
||||
98 98 151 # Z
|
||||
|
||||
# Box 4 : L6MUX21 (MUX2)
|
||||
# Outputs: Z
|
||||
# name ID w/b ins outs
|
||||
L6MUX21 4 1 3 1
|
||||
|
||||
#D0 D1 SD
|
||||
140 141 148
|
||||
140 141 148 # Z
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
// ---------------------------------------
|
||||
|
||||
// Attach a (combinatorial) black-box onto the output
|
||||
// of this LUTRAM primitive to capture its
|
||||
// asynchronous read behaviour
|
||||
module TRELLIS_DPR16X4 (
|
||||
input [3:0] DI,
|
||||
input [3:0] WAD,
|
||||
input WRE,
|
||||
input WCK,
|
||||
input [3:0] RAD,
|
||||
(* techmap_autopurge *) input [3:0] DI,
|
||||
(* techmap_autopurge *) input [3:0] WAD,
|
||||
(* techmap_autopurge *) input WRE,
|
||||
(* techmap_autopurge *) input WCK,
|
||||
(* techmap_autopurge *) input [3:0] RAD,
|
||||
output [3:0] DO
|
||||
);
|
||||
parameter WCKMUX = "WCK";
|
||||
parameter WREMUX = "WRE";
|
||||
parameter [63:0] INITVAL = 64'h0000000000000000;
|
||||
wire [3:0] \$DO ;
|
||||
wire [3:0] $DO;
|
||||
|
||||
TRELLIS_DPR16X4 #(
|
||||
.WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK),
|
||||
.RAD(RAD), .DO(\$DO )
|
||||
.RAD(RAD), .DO($DO)
|
||||
);
|
||||
|
||||
\$__ABC9_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
|
||||
$__ABC9_DPR16X4_COMB do (.$DO($DO), .RAD(RAD), .DO(DO));
|
||||
endmodule
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// ---------------------------------------
|
||||
|
||||
(* abc9_box_id=2 *)
|
||||
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||
module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
|
||||
endmodule
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// ---------------------------------------
|
||||
|
||||
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||
assign Y = A;
|
||||
module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
|
||||
assign DO = $DO;
|
||||
endmodule
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// Diamond flip-flops
|
||||
module FD1P3AX(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3AY(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3AX(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3AY(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3BX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3DX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3IX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1P3JX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3AX(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3AY(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3AX(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3AY(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(|0), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3BX(input PD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3DX(input CD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
|
||||
module FD1S3IX(input CD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
|
||||
|
|
|
@ -47,6 +47,21 @@ module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .
|
|||
module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
|
||||
module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("AUTO"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
|
||||
|
||||
`ifdef ASYNC_PRLD
|
||||
module \$_DLATCH_N_ (input E, input D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.LSR(!E), .DI(1'b0), .M(D), .Q(Q)); endmodule
|
||||
module \$_DLATCH_P_ (input E, input D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.LSR(E), .DI(1'b0), .M(D), .Q(Q)); endmodule
|
||||
|
||||
module \$_DFFSR_NNN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || !R), .DI(D), .M(R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_NNP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || R), .DI(D), .M(!R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_NPN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || !R), .DI(D), .M(R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_NPP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || R), .DI(D), .M(!R), .Q(Q)); endmodule
|
||||
|
||||
module \$_DFFSR_PNN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || !R), .DI(D), .M(R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_PNP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!S || R), .DI(D), .M(!R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_PPN_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || !R), .DI(D), .M(R), .Q(Q)); endmodule
|
||||
module \$_DFFSR_PPP_ (input C, S, R, D, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMODE("PRLD"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(S || R), .DI(D), .M(!R), .Q(Q)); endmodule
|
||||
`endif
|
||||
|
||||
`include "cells_ff.vh"
|
||||
`include "cells_io.vh"
|
||||
|
||||
|
@ -58,102 +73,80 @@ module \$lut (A, Y);
|
|||
input [WIDTH-1:0] A;
|
||||
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
|
||||
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]));
|
||||
end else
|
||||
if (WIDTH == 2) begin
|
||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(1'b0), .B(1'b0), .C(A[1]), .D(A[0]));
|
||||
localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}};
|
||||
LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(1'b0), .B(1'b0), .C(A[0]), .D(A[1]));
|
||||
end else
|
||||
if (WIDTH == 3) begin
|
||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(1'b0), .B(A[2]), .C(A[1]), .D(A[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]}}};
|
||||
LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(1'b0), .B(A[0]), .C(A[1]), .D(A[2]));
|
||||
end else
|
||||
if (WIDTH == 4) begin
|
||||
LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(A[3]), .B(A[2]), .C(A[1]), .D(A[0]));
|
||||
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
`ifndef NO_PFUMUX
|
||||
end else
|
||||
if (WIDTH == 5) begin
|
||||
wire f0, f1;
|
||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
|
||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
|
||||
PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y));
|
||||
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y));
|
||||
end else
|
||||
if (WIDTH == 6) begin
|
||||
wire f0, f1, f2, f3, g0, g1;
|
||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
||||
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
|
||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
||||
LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
|
||||
.A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
|
||||
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0));
|
||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1));
|
||||
L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y));
|
||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
|
||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
|
||||
L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y));
|
||||
end else
|
||||
if (WIDTH == 7) begin
|
||||
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
|
||||
LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7),
|
||||
.A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7),
|
||||
.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
|
||||
|
||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0));
|
||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1));
|
||||
PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2));
|
||||
PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3));
|
||||
L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0));
|
||||
L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1));
|
||||
L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y));
|
||||
PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
|
||||
PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
|
||||
PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2));
|
||||
PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3));
|
||||
L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0));
|
||||
L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1));
|
||||
L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y));
|
||||
`endif
|
||||
end else begin
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// ---------------------------------------
|
||||
|
||||
(* lib_whitebox *)
|
||||
module LUT4(input A, B, C, D, output Z);
|
||||
parameter [15:0] INIT = 16'h0000;
|
||||
wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0];
|
||||
|
@ -31,13 +32,8 @@ module CCU2C(
|
|||
|
||||
// First half
|
||||
wire LUT4_0, LUT2_0;
|
||||
`ifdef _ABC
|
||||
assign LUT4_0 = INIT0[{D0, C0, B0, A0}];
|
||||
assign LUT2_0 = INIT0[{2'b00, B0, A0}];
|
||||
`else
|
||||
LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0));
|
||||
LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0));
|
||||
`endif
|
||||
wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
|
||||
assign S0 = LUT4_0 ^ gated_cin_0;
|
||||
|
||||
|
@ -46,13 +42,8 @@ module CCU2C(
|
|||
|
||||
// Second half
|
||||
wire LUT4_1, LUT2_1;
|
||||
`ifdef _ABC
|
||||
assign LUT4_1 = INIT1[{D1, C1, B1, A1}];
|
||||
assign LUT2_1 = INIT1[{2'b00, B1, A1}];
|
||||
`else
|
||||
LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1));
|
||||
LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1));
|
||||
`endif
|
||||
wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
|
||||
assign S1 = LUT4_1 ^ gated_cin_1;
|
||||
|
||||
|
@ -209,6 +200,7 @@ endmodule
|
|||
|
||||
// ---------------------------------------
|
||||
|
||||
(* lib_whitebox *)
|
||||
module LUT2(input A, B, output Z);
|
||||
parameter [3:0] INIT = 4'h0;
|
||||
wire [1:0] s1 = B ? INIT[ 3:2] : INIT[1:0];
|
||||
|
|
|
@ -62,7 +62,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log(" -noccu2\n");
|
||||
log(" do not use CCU2 cells in output netlist\n");
|
||||
|
@ -79,6 +79,9 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
log(" -nowidelut\n");
|
||||
log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
|
||||
log("\n");
|
||||
log(" -asyncprld\n");
|
||||
log(" use async PRLD mode to implement DLATCH and DFFSR (EXPERIMENTAL)\n");
|
||||
log("\n");
|
||||
log(" -abc2\n");
|
||||
log(" run two passes of 'abc' for slightly improved logic density\n");
|
||||
log("\n");
|
||||
|
@ -99,7 +102,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, json_file;
|
||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;
|
||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, asyncprld, flatten, retime, abc2, abc9, nodsp, vpr;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -112,6 +115,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
nobram = false;
|
||||
nolutram = false;
|
||||
nowidelut = false;
|
||||
asyncprld = false;
|
||||
flatten = true;
|
||||
retime = false;
|
||||
abc2 = false;
|
||||
|
@ -176,6 +180,10 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
nobram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-asyncprld") {
|
||||
asyncprld = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
|
||||
nolutram = true;
|
||||
continue;
|
||||
|
@ -222,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
{
|
||||
if (check_label("begin"))
|
||||
{
|
||||
run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
|
||||
run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
|
||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
}
|
||||
|
||||
|
@ -258,13 +266,13 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
|
||||
if (!nobram && check_label("map_bram", "(skip if -nobram)"))
|
||||
{
|
||||
run("memory_bram -rules +/ecp5/bram.txt");
|
||||
run("memory_bram -rules +/ecp5/brams.txt");
|
||||
run("techmap -map +/ecp5/brams_map.v");
|
||||
}
|
||||
|
||||
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
|
||||
{
|
||||
run("memory_bram -rules +/ecp5/lutram.txt");
|
||||
run("memory_bram -rules +/ecp5/lutrams.txt");
|
||||
run("techmap -map +/ecp5/lutrams_map.v");
|
||||
}
|
||||
|
||||
|
@ -282,7 +290,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
else
|
||||
run("techmap -map +/techmap.v -map +/ecp5/arith_map.v");
|
||||
if (retime || help_mode)
|
||||
run("abc -dff", "(only if -retime)");
|
||||
run("abc -dff -D 1", "(only if -retime)");
|
||||
}
|
||||
|
||||
if (check_label("map_ffs"))
|
||||
|
@ -292,7 +300,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
run("opt_clean");
|
||||
if (!nodffe)
|
||||
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
|
||||
run("techmap -D NO_LUT -map +/ecp5/cells_map.v");
|
||||
run(stringf("techmap -D NO_LUT %s -map +/ecp5/cells_map.v", help_mode ? "[-D ASYNC_PRLD]" : (asyncprld ? "-D ASYNC_PRLD" : "")));
|
||||
run("opt_expr -undriven -mux_undef");
|
||||
run("simplemap");
|
||||
run("ecp5_ffinit");
|
||||
|
@ -306,17 +314,18 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
if (abc2 || help_mode) {
|
||||
run("abc", " (only if -abc2)");
|
||||
}
|
||||
std::string techmap_args = "-map +/ecp5/latches_map.v";
|
||||
std::string techmap_args = asyncprld ? "" : "-map +/ecp5/latches_map.v";
|
||||
if (abc9)
|
||||
techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1";
|
||||
run("techmap " + techmap_args);
|
||||
if (!asyncprld || abc9)
|
||||
run("techmap " + techmap_args);
|
||||
|
||||
if (abc9) {
|
||||
run("read_verilog -icells -lib +/ecp5/abc9_model.v");
|
||||
if (nowidelut)
|
||||
run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
|
||||
run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200");
|
||||
else
|
||||
run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
|
||||
run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200");
|
||||
run("techmap -map +/ecp5/abc9_unmap.v");
|
||||
} else {
|
||||
if (nowidelut)
|
||||
|
|
|
@ -7,4 +7,4 @@ $(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_map.v))
|
|||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/arith_map.v))
|
||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams_map.v))
|
||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/bram.txt))
|
||||
$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams.txt))
|
||||
|
|
|
@ -58,7 +58,10 @@ struct SynthEfinixPass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log(" -nobram\n");
|
||||
log(" do not use EFX_RAM_5K cells in output netlist\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -67,7 +70,7 @@ struct SynthEfinixPass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, edif_file, json_file;
|
||||
bool flatten, retime;
|
||||
bool flatten, retime, nobram;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -76,6 +79,7 @@ struct SynthEfinixPass : public ScriptPass
|
|||
json_file = "";
|
||||
flatten = true;
|
||||
retime = false;
|
||||
nobram = false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -114,6 +118,10 @@ struct SynthEfinixPass : public ScriptPass
|
|||
retime = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nobram") {
|
||||
nobram = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -150,21 +158,25 @@ struct SynthEfinixPass : public ScriptPass
|
|||
run("synth -run coarse");
|
||||
}
|
||||
|
||||
if (check_label("map_bram", "(skip if -nobram)"))
|
||||
if (!nobram || check_label("map_bram", "(skip if -nobram)"))
|
||||
{
|
||||
run("memory_bram -rules +/efinix/bram.txt");
|
||||
run("memory_bram -rules +/efinix/brams.txt");
|
||||
run("techmap -map +/efinix/brams_map.v");
|
||||
run("setundef -zero -params t:EFX_RAM_5K");
|
||||
}
|
||||
|
||||
if (check_label("fine"))
|
||||
if (check_label("map_ffram"))
|
||||
{
|
||||
run("opt -fast -mux_undef -undriven -fine");
|
||||
run("memory_map");
|
||||
run("opt -undriven -fine");
|
||||
}
|
||||
|
||||
if (check_label("map_gates"))
|
||||
{
|
||||
run("techmap -map +/techmap.v -map +/efinix/arith_map.v");
|
||||
if (retime || help_mode)
|
||||
run("abc -dff", "(only if -retime)");
|
||||
run("abc -dff -D 1", "(only if -retime)");
|
||||
}
|
||||
|
||||
if (check_label("map_ffs"))
|
||||
|
@ -194,7 +206,7 @@ struct SynthEfinixPass : public ScriptPass
|
|||
run("efinix_fixcarry");
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
||||
if (check_label("check"))
|
||||
{
|
||||
run("hierarchy -check");
|
||||
|
|
|
@ -7,9 +7,9 @@ $(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
|
|||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.v))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_map.v))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/bram.txt))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/drams_map.v))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams.txt))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams_map.v))
|
||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams.txt))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,133 +1,282 @@
|
|||
`default_nettype none
|
||||
//All DFF* have INIT, but the hardware is always initialised to the reset
|
||||
//value regardless. The parameter is ignored.
|
||||
|
||||
// DFFN D Flip-Flop with Negative-Edge Clock
|
||||
module \$_DFF_N_ (input D, C, output Q); DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
|
||||
// DFF D Flip-Flop
|
||||
module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
|
||||
// DFFN D Flip-Flop with Negative-Edge Clock
|
||||
module \$_DFF_N_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(1'b0));
|
||||
else
|
||||
DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFE D Flip-Flop with Clock Enable
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E)); endmodule
|
||||
module \$_DFFE_PN_ (input D, C, E, output Q); DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
|
||||
// DFF D Flip-Flop
|
||||
module \$_DFF_P_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(1'b0));
|
||||
else
|
||||
DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFNE D Flip-Flop with Negative-Edge Clock and Clock Enable
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q); DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E)); endmodule
|
||||
module \$_DFFE_NN_ (input D, C, E, output Q); DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E)); endmodule
|
||||
// DFFE D Flip-Flop with Clock Enable
|
||||
module \$_DFFE_PP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E), .SET(1'b0));
|
||||
else
|
||||
DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFR D Flip-Flop with Synchronous Reset
|
||||
module \$__DFFS_PN0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
|
||||
module \$__DFFS_PP0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
|
||||
module \$_DFFE_PN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E), .SET(1'b0));
|
||||
else
|
||||
DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFNR D Flip-Flop with Negative-Edge Clock and Synchronous Reset
|
||||
module \$__DFFS_NN0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
|
||||
module \$__DFFS_NP0_ (input D, C, R, output Q); DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
|
||||
// DFFNE D Flip-Flop with Negative-Edge Clock and Clock Enable
|
||||
module \$_DFFE_NP_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E), .SET(1'b0));
|
||||
else
|
||||
DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(E));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFRE D Flip-Flop with Clock Enable and Synchronous Reset
|
||||
module \$__DFFSE_PN0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E)); endmodule
|
||||
module \$__DFFSE_PP0 (input D, C, R, E, output Q); DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E)); endmodule
|
||||
module \$_DFFE_NN_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, E, output Q);
|
||||
generate
|
||||
if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E), .SET(1'b0));
|
||||
else
|
||||
DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CE(!E));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
// DFFNRE D Flip-Flop with Negative-Edge Clock,Clock Enable, and Synchronous Reset
|
||||
module \$__DFFNSE_PN0 (input D, C, R, E, output Q); DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E)); endmodule
|
||||
module \$__DFFNSE_PP0 (input D, C, R, E, output Q); DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E)); endmodule
|
||||
// DFFR D Flip-Flop with Synchronous Reset
|
||||
module \$__DFFS_PN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFS D Flip-Flop with Synchronous Set
|
||||
module \$__DFFS_PN1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
|
||||
module \$__DFFS_PP1_ (input D, C, R, output Q); DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
|
||||
module \$__DFFS_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFNS D Flip-Flop with Negative-Edge Clock and Synchronous Set
|
||||
module \$__DFFS_NN1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R)); endmodule
|
||||
module \$__DFFS_NP1_ (input D, C, R, output Q); DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R)); endmodule
|
||||
// DFFNR D Flip-Flop with Negative-Edge Clock and Synchronous Reset
|
||||
module \$__DFFS_NN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$__DFFS_NP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFSE D Flip-Flop with Clock Enable and Synchronous Set
|
||||
module \$__DFFSE_PN1 (input D, C, R, E, output Q); DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E)); endmodule
|
||||
module \$__DFFSE_PP1 (input D, C, R, E, output Q); DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E)); endmodule
|
||||
// DFFRE D Flip-Flop with Clock Enable and Synchronous Reset
|
||||
module \$__DFFSE_PN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$__DFFSE_PP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFNSE D Flip-Flop with Negative-Edge Clock,Clock Enable,and Synchronous Set
|
||||
module \$__DFFSE_NN1 (input D, C, R, E, output Q); DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E)); endmodule
|
||||
module \$__DFFSE_NP1 (input D, C, R, E, output Q); DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E)); endmodule
|
||||
// DFFNRE D Flip-Flop with Negative-Edge Clock,Clock Enable, and Synchronous Reset
|
||||
module \$__DFFSE_NN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$__DFFSE_NP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNRE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFP D Flip-Flop with Asynchronous Preset
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q); DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
|
||||
// DFFS D Flip-Flop with Synchronous Set
|
||||
module \$__DFFS_PN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFS_PP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFNP D Flip-Flop with Negative-Edge Clock and Asynchronous Preset
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R)); endmodule
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q); DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R)); endmodule
|
||||
// DFFNS D Flip-Flop with Negative-Edge Clock and Synchronous Set
|
||||
module \$__DFFS_NN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFS_NP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFC D Flip-Flop with Asynchronous Clear
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q); DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
|
||||
// DFFSE D Flip-Flop with Clock Enable and Synchronous Set
|
||||
module \$__DFFSE_PN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFSE_PP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFNC D Flip-Flop with Negative-Edge Clock and Asynchronous Clear
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R)); endmodule
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q); DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R)); endmodule
|
||||
// DFFNSE D Flip-Flop with Negative-Edge Clock,Clock Enable,and Synchronous Set
|
||||
module \$__DFFSE_NN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFSE_NP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNSE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .SET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFPE D Flip-Flop with Clock Enable and Asynchronous Preset
|
||||
module \$__DFFE_PP1 (input D, C, R, E, output Q); DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E)); endmodule
|
||||
module \$__DFFE_PN1 (input D, C, R, E, output Q); DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E)); endmodule
|
||||
// DFFP D Flip-Flop with Asynchronous Preset
|
||||
module \$_DFF_PP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$_DFF_PN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFNPE D Flip-Flop with Negative-Edge Clock,Clock Enable, and Asynchronous Preset
|
||||
module \$__DFFE_NP1 (input D, C, R, E, output Q); DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E)); endmodule
|
||||
module \$__DFFE_NN1 (input D, C, R, E, output Q); DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E)); endmodule
|
||||
// DFFNP D Flip-Flop with Negative-Edge Clock and Asynchronous Preset
|
||||
module \$_DFF_NP1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$_DFF_NN1_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNP _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFCE D Flip-Flop with Clock Enable and Asynchronous Clear
|
||||
module \$__DFFE_PP0 (input D, C, R, E, output Q); DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E)); endmodule
|
||||
module \$__DFFE_PN0 (input D, C, R, E, output Q); DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E)); endmodule
|
||||
// DFFC D Flip-Flop with Asynchronous Clear
|
||||
module \$_DFF_PP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$_DFF_PN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFNCE D Flip-Flop with Negative-Edge Clock,Clock Enable and Asynchronous Clear
|
||||
module \$__DFFE_NP0 (input D, C, R, E, output Q); DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E)); endmodule
|
||||
module \$__DFFE_NN0 (input D, C, R, E, output Q); DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E)); endmodule
|
||||
// DFFNC D Flip-Flop with Negative-Edge Clock and Asynchronous Clear
|
||||
module \$_DFF_NP0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$_DFF_NN0_ #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, output Q);
|
||||
DFFNC _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFPE D Flip-Flop with Clock Enable and Asynchronous Preset
|
||||
module \$__DFFE_PP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFE_PN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFNPE D Flip-Flop with Negative-Edge Clock,Clock Enable, and Asynchronous Preset
|
||||
module \$__DFFE_NP1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
module \$__DFFE_NN1 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNPE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .PRESET(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b0;
|
||||
endmodule
|
||||
|
||||
// DFFCE D Flip-Flop with Clock Enable and Asynchronous Clear
|
||||
module \$__DFFE_PP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$__DFFE_PN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
// DFFNCE D Flip-Flop with Negative-Edge Clock,Clock Enable and Asynchronous Clear
|
||||
module \$__DFFE_NP0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
module \$__DFFE_NN0 #(parameter _TECHMAP_WIREINIT_Q_ = 1'bx) (input D, C, R, E, output Q);
|
||||
DFFNCE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .CLEAR(!R), .CE(E));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = _TECHMAP_WIREINIT_Q_ !== 1'b1;
|
||||
endmodule
|
||||
|
||||
|
||||
module \$lut (A, Y);
|
||||
parameter WIDTH = 0;
|
||||
parameter LUT = 0;
|
||||
parameter WIDTH = 0;
|
||||
parameter LUT = 0;
|
||||
|
||||
input [WIDTH-1:0] A;
|
||||
output Y;
|
||||
input [WIDTH-1:0] A;
|
||||
output Y;
|
||||
|
||||
generate
|
||||
if (WIDTH == 1) begin
|
||||
LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]));
|
||||
end else
|
||||
if (WIDTH == 2) begin
|
||||
LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]));
|
||||
end else
|
||||
if (WIDTH == 3) begin
|
||||
LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]), .I2(A[2]));
|
||||
end else
|
||||
if (WIDTH == 4) begin
|
||||
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
||||
end else
|
||||
if (WIDTH == 5) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[31:16]), .WIDTH(4)) lut1 (.A(A[3:0]), .Y(f1));
|
||||
MUX2_LUT5 mux5(.I0(f0), .I1(f1), .S0(A[4]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 6) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[63:32]), .WIDTH(5)) lut1 (.A(A[4:0]), .Y(f1));
|
||||
MUX2_LUT6 mux6(.I0(f0), .I1(f1), .S0(A[5]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 7) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1));
|
||||
MUX2_LUT7 mux7(.I0(f0), .I1(f1), .S0(A[6]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 8) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1));
|
||||
MUX2_LUT8 mux8(.I0(f0), .I1(f1), .S0(A[7]), .O(Y));
|
||||
end else begin
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
end
|
||||
endgenerate
|
||||
generate
|
||||
if (WIDTH == 1) begin
|
||||
LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]));
|
||||
end else
|
||||
if (WIDTH == 2) begin
|
||||
LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]));
|
||||
end else
|
||||
if (WIDTH == 3) begin
|
||||
LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]), .I2(A[2]));
|
||||
end else
|
||||
if (WIDTH == 4) begin
|
||||
LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.F(Y),
|
||||
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
||||
end else
|
||||
if (WIDTH == 5) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[31:16]), .WIDTH(4)) lut1 (.A(A[3:0]), .Y(f1));
|
||||
MUX2_LUT5 mux5(.I0(f0), .I1(f1), .S0(A[4]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 6) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[63:32]), .WIDTH(5)) lut1 (.A(A[4:0]), .Y(f1));
|
||||
MUX2_LUT6 mux6(.I0(f0), .I1(f1), .S0(A[5]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 7) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1));
|
||||
MUX2_LUT7 mux7(.I0(f0), .I1(f1), .S0(A[6]), .O(Y));
|
||||
end else
|
||||
if (WIDTH == 8) begin
|
||||
wire f0, f1;
|
||||
\$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0));
|
||||
\$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1));
|
||||
MUX2_LUT8 mux8(.I0(f0), .I1(f1), .S0(A[7]), .O(Y));
|
||||
end else begin
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
|
|
@ -55,20 +55,23 @@ struct SynthGowinPass : public ScriptPass
|
|||
log(" -nobram\n");
|
||||
log(" do not use BRAM cells in output netlist\n");
|
||||
log("\n");
|
||||
log(" -nodram\n");
|
||||
log(" -nolutram\n");
|
||||
log(" do not use distributed RAM cells in output netlist\n");
|
||||
log("\n");
|
||||
log(" -noflatten\n");
|
||||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log(" -nowidelut\n");
|
||||
log(" do not use muxes to implement LUTs larger than LUT4s\n");
|
||||
log("\n");
|
||||
log(" -abc9\n");
|
||||
log(" use new ABC9 flow (EXPERIMENTAL)\n");
|
||||
log(" -noiopads\n");
|
||||
log(" do not emit IOB at top level ports\n");
|
||||
//log("\n");
|
||||
//log(" -abc9\n");
|
||||
//log(" use new ABC9 flow (EXPERIMENTAL)\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -77,7 +80,7 @@ struct SynthGowinPass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, vout_file;
|
||||
bool retime, nobram, nodram, flatten, nodffe, nowidelut, abc9;
|
||||
bool retime, nobram, nolutram, flatten, nodffe, nowidelut, abc9, noiopads;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -87,9 +90,10 @@ struct SynthGowinPass : public ScriptPass
|
|||
flatten = true;
|
||||
nobram = false;
|
||||
nodffe = false;
|
||||
nodram = false;
|
||||
nolutram = false;
|
||||
nowidelut = false;
|
||||
abc9 = false;
|
||||
noiopads = false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -124,8 +128,8 @@ struct SynthGowinPass : public ScriptPass
|
|||
nobram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodram") {
|
||||
nodram = true;
|
||||
if (args[argidx] == "-nolutram" || /*deprecated*/args[argidx] == "-nodram") {
|
||||
nolutram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodffe") {
|
||||
|
@ -140,8 +144,12 @@ struct SynthGowinPass : public ScriptPass
|
|||
nowidelut = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-abc9") {
|
||||
abc9 = true;
|
||||
//if (args[argidx] == "-abc9") {
|
||||
// abc9 = true;
|
||||
// continue;
|
||||
//}
|
||||
if (args[argidx] == "-noiopads") {
|
||||
noiopads = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -180,35 +188,39 @@ struct SynthGowinPass : public ScriptPass
|
|||
run("synth -run coarse");
|
||||
}
|
||||
|
||||
if (!nobram && check_label("bram", "(skip if -nobram)"))
|
||||
if (!nobram && check_label("map_bram", "(skip if -nobram)"))
|
||||
{
|
||||
run("memory_bram -rules +/gowin/bram.txt");
|
||||
run("memory_bram -rules +/gowin/brams.txt");
|
||||
run("techmap -map +/gowin/brams_map.v -map +/gowin/cells_sim.v");
|
||||
}
|
||||
|
||||
if (!nodram && check_label("dram", "(skip if -nodram)"))
|
||||
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)"))
|
||||
{
|
||||
run("memory_bram -rules +/gowin/dram.txt");
|
||||
run("techmap -map +/gowin/drams_map.v");
|
||||
run("memory_bram -rules +/gowin/lutrams.txt");
|
||||
run("techmap -map +/gowin/lutrams_map.v");
|
||||
run("determine_init");
|
||||
}
|
||||
|
||||
if (check_label("fine"))
|
||||
if (check_label("map_ffram"))
|
||||
{
|
||||
run("opt -fast -mux_undef -undriven -fine");
|
||||
run("memory_map");
|
||||
run("opt -undriven -fine");
|
||||
}
|
||||
|
||||
if (check_label("map_gates"))
|
||||
{
|
||||
run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
|
||||
run("techmap -map +/techmap.v");
|
||||
if (retime || help_mode)
|
||||
run("abc -dff", "(only if -retime)");
|
||||
run("abc -dff -D 1", "(only if -retime)");
|
||||
run("splitnets");
|
||||
}
|
||||
|
||||
if (check_label("map_ffs"))
|
||||
{
|
||||
run("dffsr2dff");
|
||||
run("dff2dffs");
|
||||
run("dff2dffs -match-init");
|
||||
run("opt_clean");
|
||||
if (!nodffe)
|
||||
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
|
||||
|
@ -219,13 +231,13 @@ struct SynthGowinPass : public ScriptPass
|
|||
|
||||
if (check_label("map_luts"))
|
||||
{
|
||||
if (nowidelut && abc9) {
|
||||
/*if (nowidelut && abc9) {
|
||||
run("abc9 -lut 4");
|
||||
} else if (nowidelut && !abc9) {
|
||||
} else*/ if (nowidelut && !abc9) {
|
||||
run("abc -lut 4");
|
||||
} else if (!nowidelut && abc9) {
|
||||
} else /*if (!nowidelut && abc9) {
|
||||
run("abc9 -lut 4:8");
|
||||
} else if (!nowidelut && !abc9) {
|
||||
} else*/ if (!nowidelut && !abc9) {
|
||||
run("abc -lut 4:8");
|
||||
}
|
||||
run("clean");
|
||||
|
@ -236,10 +248,10 @@ struct SynthGowinPass : public ScriptPass
|
|||
run("techmap -map +/gowin/cells_map.v");
|
||||
run("setundef -undriven -params -zero");
|
||||
run("hilomap -singleton -hicell VCC V -locell GND G");
|
||||
run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O "
|
||||
"-toutpad TBUF OEN:I:O -tinoutpad IOBUF OEN:O:I:IO", "(unless -noiopads)");
|
||||
if (!noiopads || help_mode)
|
||||
run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O "
|
||||
"-toutpad TBUF OEN:I:O -tinoutpad IOBUF OEN:O:I:IO", "(unless -noiopads)");
|
||||
run("clean");
|
||||
|
||||
}
|
||||
|
||||
if (check_label("check"))
|
||||
|
|
|
@ -59,7 +59,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
|
|||
log(" do not flatten design before synthesis\n");
|
||||
log("\n");
|
||||
log(" -retime\n");
|
||||
log(" run 'abc' with -dff option\n");
|
||||
log(" run 'abc' with '-dff -D 1' options\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
|
@ -165,7 +165,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
|
|||
run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
|
||||
run("opt -fast");
|
||||
if (retime || help_mode)
|
||||
run("abc -dff", "(only if -retime)");
|
||||
run("abc -dff -D 1", "(only if -retime)");
|
||||
}
|
||||
|
||||
if (check_label("map_luts"))
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
|
||||
|
||||
# NB: Inputs/Outputs must be ordered alphabetically
|
||||
# (with exceptions for carry in/out)
|
||||
# NB: Box inputs/outputs must each be in the same order
|
||||
# as their corresponding module definition
|
||||
# (with exceptions detailed below)
|
||||
|
||||
# Inputs: A B I0 I3 CI
|
||||
# Outputs: O CO
|
||||
# (NB: carry chain input/output must be last
|
||||
# input/output and have been moved there
|
||||
# overriding the alphabetical ordering)
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
400 379 449 316 316
|
||||
259 231 - - 126
|
||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||
# SB_LUT4+SB_CARRY)
|
||||
# (Exception: carry chain input/output must be the
|
||||
# last input and output and the entire bus has been
|
||||
# moved there overriding the otherwise
|
||||
# alphabetical ordering)
|
||||
# name ID w/b ins outs
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
#A B I0 I3 CI
|
||||
400 379 449 316 316 # O
|
||||
259 231 - - 126 # CO
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
|
||||
|
||||
# NB: Inputs/Outputs must be ordered alphabetically
|
||||
# (with exceptions for carry in/out)
|
||||
# NB: Box inputs/outputs must each be in the same order
|
||||
# as their corresponding module definition
|
||||
# (with exceptions detailed below)
|
||||
|
||||
# Inputs: A B I0 I3 CI
|
||||
# Outputs: O CO
|
||||
# (NB: carry chain input/output must be last
|
||||
# input/output and have been moved there
|
||||
# overriding the alphabetical ordering)
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
589 558 661 465 465
|
||||
675 609 - - 186
|
||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||
# SB_LUT4+SB_CARRY)
|
||||
# (Exception: carry chain input/output must be the
|
||||
# last input and output and the entire bus has been
|
||||
# moved there overriding the otherwise
|
||||
# alphabetical ordering)
|
||||
# name ID w/b ins outs
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
#A B I0 I3 CI
|
||||
589 558 661 465 465 # O
|
||||
675 609 - - 186 # CO
|
||||
|
|
|
@ -9,6 +9,8 @@ module \$__ICE40_CARRY_WRAPPER (
|
|||
input I0, I3
|
||||
);
|
||||
parameter LUT = 0;
|
||||
parameter I3_IS_CI = 0;
|
||||
wire I3_OR_CI = I3_IS_CI ? CI : I3;
|
||||
SB_CARRY carry (
|
||||
.I0(A),
|
||||
.I1(B),
|
||||
|
@ -21,7 +23,7 @@ module \$__ICE40_CARRY_WRAPPER (
|
|||
.I0(I0),
|
||||
.I1(A),
|
||||
.I2(B),
|
||||
.I3(I3),
|
||||
.I3(I3_OR_CI),
|
||||
.O(O)
|
||||
);
|
||||
endmodule
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
|
||||
|
||||
# NB: Inputs/Outputs must be ordered alphabetically
|
||||
# (with exceptions for carry in/out)
|
||||
# NB: Box inputs/outputs must each be in the same order
|
||||
# as their corresponding module definition
|
||||
# (with exceptions detailed below)
|
||||
|
||||
# Inputs: A B I0 I3 CI
|
||||
# Outputs: O CO
|
||||
# (NB: carry chain input/output must be last
|
||||
# input/output and have been moved there
|
||||
# overriding the alphabetical ordering)
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
1231 1205 1285 874 874
|
||||
675 609 - - 278
|
||||
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
|
||||
# SB_LUT4+SB_CARRY)
|
||||
# (Exception: carry chain input/output must be the
|
||||
# last input and output and the entire bus has been
|
||||
# moved there overriding the otherwise
|
||||
# alphabetical ordering)
|
||||
# name ID w/b ins outs
|
||||
$__ICE40_CARRY_WRAPPER 1 1 5 2
|
||||
#A B I0 I3 CI
|
||||
1231 1205 1285 874 874 # O
|
||||
675 609 - - 278 # CO
|
||||
|
|
|
@ -49,13 +49,14 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
|
|||
// A[1]: 1100 1100 1100 1100
|
||||
// A[2]: 1111 0000 1111 0000
|
||||
// A[3]: 1111 1111 0000 0000
|
||||
.LUT(16'b 0110_1001_1001_0110)
|
||||
) fadd (
|
||||
.LUT(16'b 0110_1001_1001_0110),
|
||||
.I3_IS_CI(1'b1)
|
||||
) carry (
|
||||
.A(AA[i]),
|
||||
.B(BB[i]),
|
||||
.CI(C[i]),
|
||||
.I0(1'b0),
|
||||
.I3(C[i]),
|
||||
.I3(1'bx),
|
||||
.CO(CO[i]),
|
||||
.O(Y[i])
|
||||
);
|
||||
|
|
|
@ -42,41 +42,21 @@ module \$lut (A, Y);
|
|||
.I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
|
||||
end else
|
||||
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),
|
||||
.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
|
||||
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),
|
||||
.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
|
||||
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(INIT)) _TECHMAP_REPLACE_ (.O(Y),
|
||||
.I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0]));
|
||||
SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
|
||||
.I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
|
||||
end else begin
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
`endif
|
||||
|
||||
`ifndef NO_ADDER
|
||||
module \$__ICE40_CARRY_WRAPPER (output CO, O, input A, B, CI, I0, I3);
|
||||
parameter LUT = 0;
|
||||
SB_CARRY carry (
|
||||
.I0(A),
|
||||
.I1(B),
|
||||
.CI(CI),
|
||||
.CO(CO)
|
||||
);
|
||||
\$lut #(
|
||||
.WIDTH(4),
|
||||
.LUT(LUT)
|
||||
) lut (
|
||||
.A({I0,A,B,I3}),
|
||||
.Y(O)
|
||||
);
|
||||
endmodule
|
||||
`endif
|
||||
|
|
|
@ -1126,6 +1126,7 @@ module SB_SPRAM256KA (
|
|||
input [15:0] DATAIN,
|
||||
input [3:0] MASKWREN,
|
||||
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
|
||||
);
|
||||
`ifndef BLACKBOX
|
||||
|
|
|
@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass {
|
|||
continue;
|
||||
|
||||
if (initbits.count(bit)) {
|
||||
if (initbits.at(bit) != val)
|
||||
log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
|
||||
if (initbits.at(bit) != val) {
|
||||
log_warning("Conflicting init values for signal %s (%s = %s, %s = %s).\n",
|
||||
log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val),
|
||||
log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit)));
|
||||
initbits.at(bit) = State::Sx;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass {
|
|||
continue;
|
||||
|
||||
State val = initbits.at(bit_q);
|
||||
|
||||
if (val == State::Sx)
|
||||
continue;
|
||||
|
||||
handled_initbits.insert(bit_q);
|
||||
|
||||
log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type),
|
||||
|
|
|
@ -41,6 +41,11 @@ static void run_ice40_opts(Module *module)
|
|||
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (!cell->type.in("\\SB_LUT4", "\\SB_CARRY", "$__ICE40_CARRY_WRAPPER"))
|
||||
continue;
|
||||
if (cell->has_keep_attr())
|
||||
continue;
|
||||
|
||||
if (cell->type == "\\SB_LUT4")
|
||||
{
|
||||
sb_lut_cells.push_back(cell);
|
||||
|
@ -112,12 +117,30 @@ static void run_ice40_opts(Module *module)
|
|||
|
||||
if (GetSize(replacement_output)) {
|
||||
optimized_co.insert(sigmap(cell->getPort("\\CO")[0]));
|
||||
auto it = cell->attributes.find(ID(SB_LUT4.name));
|
||||
if (it != cell->attributes.end()) {
|
||||
module->rename(cell, it->second.decode_string());
|
||||
decltype(Cell::attributes) new_attr;
|
||||
for (const auto &a : cell->attributes)
|
||||
if (a.first.begins_with("\\SB_LUT4.\\"))
|
||||
new_attr[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
||||
else if (a.first == ID(src))
|
||||
new_attr.insert(std::make_pair(a.first, a.second));
|
||||
else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived)))
|
||||
continue;
|
||||
else if (a.first.begins_with("\\SB_CARRY.\\"))
|
||||
continue;
|
||||
else
|
||||
log_abort();
|
||||
cell->attributes = std::move(new_attr);
|
||||
}
|
||||
module->connect(cell->getPort("\\CO")[0], replacement_output);
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
|
||||
log_id(module), log_id(cell), log_signal(replacement_output));
|
||||
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->unsetPort("\\B");
|
||||
cell->unsetPort("\\CI");
|
||||
|
@ -126,6 +149,7 @@ static void run_ice40_opts(Module *module)
|
|||
cell->unsetPort("\\CO");
|
||||
cell->unsetPort("\\O");
|
||||
cell->setParam("\\WIDTH", 4);
|
||||
cell->unsetParam("\\I3_IS_CI");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue