mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'master' of github.com:YosysHQ/yosys
This commit is contained in:
commit
2a54fa41c4
10
CHANGELOG
10
CHANGELOG
|
@ -43,6 +43,16 @@ Yosys 0.9 .. Yosys 0.9-dev
|
||||||
- Added "-match-init" option to "dff2dffs" pass
|
- Added "-match-init" option to "dff2dffs" pass
|
||||||
- Added "techmap_autopurge" support to techmap
|
- Added "techmap_autopurge" support to techmap
|
||||||
- Added "add -mod <modname[s]>"
|
- Added "add -mod <modname[s]>"
|
||||||
|
- Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones
|
||||||
|
- Added "ice40_dsp" for Lattice iCE40 DSP packing
|
||||||
|
- Added "xilinx_dsp" for Xilinx DSP packing
|
||||||
|
- "synth_xilinx" to now infer DSP blocks (-nodsp to disable)
|
||||||
|
- "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental)
|
||||||
|
- "synth_ice40 -dsp" to infer DSP blocks
|
||||||
|
- Added latch support to synth_xilinx
|
||||||
|
- Added "check -mapped"
|
||||||
|
- Added checking of SystemVerilog always block types (always_comb,
|
||||||
|
always_latch and always_ff)
|
||||||
|
|
||||||
Yosys 0.8 .. Yosys 0.9
|
Yosys 0.8 .. Yosys 0.9
|
||||||
----------------------
|
----------------------
|
||||||
|
|
46
CodingReadme
46
CodingReadme
|
@ -202,6 +202,52 @@ of how to use the Yosys API:
|
||||||
manual/PRESENTATION_Prog/my_cmd.cc
|
manual/PRESENTATION_Prog/my_cmd.cc
|
||||||
|
|
||||||
|
|
||||||
|
Script Passes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The ScriptPass base class can be used to implement passes that just call other passes,
|
||||||
|
like a script. Examples for such passes are:
|
||||||
|
|
||||||
|
techlibs/common/prep.cc
|
||||||
|
techlibs/common/synth.cc
|
||||||
|
|
||||||
|
In some cases it is easier to implement such a pass as regular pass, for example when
|
||||||
|
ScriptPass doesn't provide the type of flow control desired. (But many of the
|
||||||
|
script passes in Yosys that don't use ScriptPass simply predate the ScriptPass base
|
||||||
|
class.) Examples for such passes are:
|
||||||
|
|
||||||
|
passes/opt/opt.cc
|
||||||
|
passes/proc/proc.cc
|
||||||
|
|
||||||
|
Whether they use the ScriptPass base-class or not, a pass should always either
|
||||||
|
call other passes without doing any non-trivial work itself, or should implement
|
||||||
|
a non-trivial algorithm but not call any other passes. The reason for this is that
|
||||||
|
this helps containing complexity in individual passes and simplifies debugging the
|
||||||
|
entire system.
|
||||||
|
|
||||||
|
Exceptions to this rule should be rare and limited to cases where calling other
|
||||||
|
passes is optional and only happens when requested by the user (such as for
|
||||||
|
example `techmap -autoproc`), or where it is about commands that are "top-level
|
||||||
|
commands" in their own right, not components to be used in regular synthesis
|
||||||
|
flows (such as the `bugpoint` command).
|
||||||
|
|
||||||
|
A pass that would "naturally" call other passes and also do some work itself
|
||||||
|
should be re-written in one of two ways:
|
||||||
|
|
||||||
|
1) It could be re-written as script pass with the parts that are not calls
|
||||||
|
to other passes factored out into individual new passes. Usually in those
|
||||||
|
cases the new sub passes share the same prefix as the top-level script pass.
|
||||||
|
|
||||||
|
2) It could be re-written so that it already expects the design in a certain
|
||||||
|
state, expecting the calling script to set up this state before calling the
|
||||||
|
pass in questions.
|
||||||
|
|
||||||
|
Many back-ends are examples for the 2nd approach. For example, `write_aiger`
|
||||||
|
does not convert the design into AIG representation, but expects the design
|
||||||
|
to be already in this form, and prints an `Unsupported cell type` error
|
||||||
|
message otherwise.
|
||||||
|
|
||||||
|
|
||||||
Notes on the existing codebase
|
Notes on the existing codebase
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|
18
Makefile
18
Makefile
|
@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
|
||||||
LDLIBS += -lrt
|
LDLIBS += -lrt
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.9+431
|
YOSYS_VER := 0.9+932
|
||||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ bumpversion:
|
||||||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||||
# will remove the 'abc' directory and you do not want to accidentally
|
# will remove the 'abc' directory and you do not want to accidentally
|
||||||
# delete your work on ABC..
|
# delete your work on ABC..
|
||||||
ABCREV = 5776ad0
|
ABCREV = 623b5e8
|
||||||
ABCPULL = 1
|
ABCPULL = 1
|
||||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||||
|
@ -147,9 +147,9 @@ $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$
|
||||||
include Makefile.conf
|
include Makefile.conf
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
|
||||||
ifeq ($(ENABLE_PYOSYS),1)
|
ifeq ($(ENABLE_PYOSYS),1)
|
||||||
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
|
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
|
||||||
PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
|
|
||||||
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
|
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
|
||||||
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
||||||
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
|
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
|
||||||
|
@ -528,6 +528,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
|
||||||
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
||||||
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
||||||
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
||||||
|
$(eval $(call add_include_file,libs/json11/json11.hpp))
|
||||||
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
||||||
$(eval $(call add_include_file,frontends/ast/ast.h))
|
$(eval $(call add_include_file,frontends/ast/ast.h))
|
||||||
$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
|
$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
|
||||||
|
@ -545,6 +546,8 @@ OBJS += libs/sha1/sha1.o
|
||||||
|
|
||||||
ifneq ($(SMALL),1)
|
ifneq ($(SMALL),1)
|
||||||
|
|
||||||
|
OBJS += libs/json11/json11.o
|
||||||
|
|
||||||
OBJS += libs/subcircuit/subcircuit.o
|
OBJS += libs/subcircuit/subcircuit.o
|
||||||
|
|
||||||
OBJS += libs/ezsat/ezsat.o
|
OBJS += libs/ezsat/ezsat.o
|
||||||
|
@ -705,11 +708,18 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
+cd tests/various && bash run-test.sh
|
+cd tests/various && bash run-test.sh
|
||||||
+cd tests/sat && bash run-test.sh
|
+cd tests/sat && bash run-test.sh
|
||||||
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
|
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/svtypes && bash run-test.sh $(SEEDOPT)
|
||||||
+cd tests/proc && bash run-test.sh
|
+cd tests/proc && bash run-test.sh
|
||||||
+cd tests/opt && bash run-test.sh
|
+cd tests/opt && bash run-test.sh
|
||||||
+cd tests/aiger && bash run-test.sh $(ABCOPT)
|
+cd tests/aiger && bash run-test.sh $(ABCOPT)
|
||||||
+cd tests/arch && bash run-test.sh
|
+cd tests/arch && bash run-test.sh
|
||||||
+cd tests/ice40 && bash run-test.sh $(SEEDOPT)
|
+cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/arch/efinix && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/arch/gowin && bash run-test.sh $(SEEDOPT)
|
||||||
|
+cd tests/rpc && bash run-test.sh
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " Passed \"make test\"."
|
@echo " Passed \"make test\"."
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
|
@ -371,6 +371,11 @@ Verilog Attributes and non-standard features
|
||||||
for example, to specify the clk-to-Q delay of a flip-flop for consideration
|
for example, to specify the clk-to-Q delay of a flip-flop for consideration
|
||||||
during techmapping.
|
during techmapping.
|
||||||
|
|
||||||
|
- The frontend sets attributes ``always_comb``, ``always_latch`` and
|
||||||
|
``always_ff`` on processes derived from SystemVerilog style always blocks
|
||||||
|
according to the type of the always. These are checked for correctness in
|
||||||
|
``proc_dlatch``.
|
||||||
|
|
||||||
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
|
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
|
||||||
the non-standard ``{* ... *}`` attribute syntax to set default attributes
|
the non-standard ``{* ... *}`` attribute syntax to set default attributes
|
||||||
for everything that comes after the ``{* ... *}`` statement. (Reset
|
for everything that comes after the ``{* ... *}`` statement. (Reset
|
||||||
|
@ -510,6 +515,8 @@ from SystemVerilog:
|
||||||
into a design with ``read_verilog``, all its packages are available to
|
into a design with ``read_verilog``, all its packages are available to
|
||||||
SystemVerilog files being read into the same design afterwards.
|
SystemVerilog files being read into the same design afterwards.
|
||||||
|
|
||||||
|
- typedefs are supported (including inside packages)
|
||||||
|
|
||||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||||
ports are inputs or outputs are supported.
|
ports are inputs or outputs are supported.
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,9 @@ struct AigerWriter
|
||||||
} else
|
} else
|
||||||
if (alias_map.count(bit)) {
|
if (alias_map.count(bit)) {
|
||||||
a = bit2aig(alias_map.at(bit));
|
a = bit2aig(alias_map.at(bit));
|
||||||
|
} else
|
||||||
|
if (initstate_bits.count(bit)) {
|
||||||
|
a = initstate_ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bit == State::Sx || bit == State::Sz)
|
if (bit == State::Sx || bit == State::Sz)
|
||||||
|
@ -777,7 +780,7 @@ struct AigerBackend : public Backend {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||||
|
|
||||||
Module *top_module = design->top_module();
|
Module *top_module = design->top_module();
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ struct XAigerWriter
|
||||||
// box ordering, but not individual AIG cells
|
// box ordering, but not individual AIG cells
|
||||||
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||||
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
||||||
bool abc_box_seen = false;
|
bool abc9_box_seen = false;
|
||||||
|
|
||||||
for (auto cell : module->selected_cells()) {
|
for (auto cell : module->selected_cells()) {
|
||||||
if (cell->type == "$_NOT_")
|
if (cell->type == "$_NOT_")
|
||||||
|
@ -242,8 +242,8 @@ struct XAigerWriter
|
||||||
log_assert(!holes_mode);
|
log_assert(!holes_mode);
|
||||||
|
|
||||||
RTLIL::Module* inst_module = module->design->module(cell->type);
|
RTLIL::Module* inst_module = module->design->module(cell->type);
|
||||||
if (inst_module && inst_module->attributes.count("\\abc_box_id")) {
|
if (inst_module && inst_module->attributes.count("\\abc9_box_id")) {
|
||||||
abc_box_seen = true;
|
abc9_box_seen = true;
|
||||||
|
|
||||||
if (!holes_mode) {
|
if (!holes_mode) {
|
||||||
toposort.node(cell->name);
|
toposort.node(cell->name);
|
||||||
|
@ -291,10 +291,10 @@ struct XAigerWriter
|
||||||
if (is_output) {
|
if (is_output) {
|
||||||
int arrival = 0;
|
int arrival = 0;
|
||||||
if (port_wire) {
|
if (port_wire) {
|
||||||
auto it = port_wire->attributes.find("\\abc_arrival");
|
auto it = port_wire->attributes.find("\\abc9_arrival");
|
||||||
if (it != port_wire->attributes.end()) {
|
if (it != port_wire->attributes.end()) {
|
||||||
if (it->second.flags != 0)
|
if (it->second.flags != 0)
|
||||||
log_error("Attribute 'abc_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type));
|
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();
|
arrival = it->second.as_int();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ struct XAigerWriter
|
||||||
//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abc_box_seen) {
|
if (abc9_box_seen) {
|
||||||
for (auto &it : bit_users)
|
for (auto &it : bit_users)
|
||||||
if (bit_drivers.count(it.first))
|
if (bit_drivers.count(it.first))
|
||||||
for (auto driver_cell : bit_drivers.at(it.first))
|
for (auto driver_cell : bit_drivers.at(it.first))
|
||||||
|
@ -347,9 +347,11 @@ struct XAigerWriter
|
||||||
log_assert(cell);
|
log_assert(cell);
|
||||||
|
|
||||||
RTLIL::Module* box_module = module->design->module(cell->type);
|
RTLIL::Module* box_module = module->design->module(cell->type);
|
||||||
if (!box_module || !box_module->attributes.count("\\abc_box_id"))
|
if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
|
||||||
|
|
||||||
// Fully pad all unused input connections of this box cell with S0
|
// Fully pad all unused input connections of this box cell with S0
|
||||||
// Fully pad all undriven output connections of this box cell with anonymous wires
|
// Fully pad all undriven output connections of this box cell with anonymous wires
|
||||||
// NB: Assume box_module->ports are sorted alphabetically
|
// NB: Assume box_module->ports are sorted alphabetically
|
||||||
|
@ -394,7 +396,10 @@ struct XAigerWriter
|
||||||
rhs = it->second;
|
rhs = it->second;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rhs = module->addWire(NEW_ID, GetSize(w));
|
Wire *wire = module->addWire(NEW_ID, GetSize(w));
|
||||||
|
if (blackbox)
|
||||||
|
wire->set_bool_attribute(ID(abc9_padding));
|
||||||
|
rhs = wire;
|
||||||
cell->setPort(port_name, rhs);
|
cell->setPort(port_name, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,15 +410,10 @@ struct XAigerWriter
|
||||||
if (O != b)
|
if (O != b)
|
||||||
alias_map[O] = b;
|
alias_map[O] = b;
|
||||||
undriven_bits.erase(O);
|
undriven_bits.erase(O);
|
||||||
|
|
||||||
auto jt = input_bits.find(b);
|
|
||||||
if (jt != input_bits.end()) {
|
|
||||||
log_assert(keep_bits.count(O));
|
|
||||||
input_bits.erase(b);
|
input_bits.erase(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
box_list.emplace_back(cell);
|
box_list.emplace_back(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ struct XAigerWriter
|
||||||
// inherit existing inout's drivers
|
// inherit existing inout's drivers
|
||||||
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|
||||||
|| keep_bits.count(bit)) {
|
|| keep_bits.count(bit)) {
|
||||||
RTLIL::IdString wire_name = wire->name.str() + "$inout.out";
|
RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str());
|
||||||
RTLIL::Wire *new_wire = module->wire(wire_name);
|
RTLIL::Wire *new_wire = module->wire(wire_name);
|
||||||
if (!new_wire)
|
if (!new_wire)
|
||||||
new_wire = module->addWire(wire_name, GetSize(wire));
|
new_wire = module->addWire(wire_name, GetSize(wire));
|
||||||
|
@ -666,7 +666,7 @@ struct XAigerWriter
|
||||||
|
|
||||||
write_h_buffer(box_inputs);
|
write_h_buffer(box_inputs);
|
||||||
write_h_buffer(box_outputs);
|
write_h_buffer(box_outputs);
|
||||||
write_h_buffer(box_module->attributes.at("\\abc_box_id").as_int());
|
write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int());
|
||||||
write_h_buffer(box_count++);
|
write_h_buffer(box_count++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,7 +856,7 @@ struct XAigerBackend : public Backend {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||||
|
|
||||||
Module *top_module = design->top_module();
|
Module *top_module = design->top_module();
|
||||||
|
|
||||||
|
|
|
@ -569,7 +569,7 @@ struct BtorWorker
|
||||||
int nid_init_val = -1;
|
int nid_init_val = -1;
|
||||||
|
|
||||||
if (!initval.is_fully_undef())
|
if (!initval.is_fully_undef())
|
||||||
nid_init_val = get_sig_nid(initval);
|
nid_init_val = get_sig_nid(initval, -1, false, true);
|
||||||
|
|
||||||
int sid = get_bv_sid(GetSize(sig_q));
|
int sid = get_bv_sid(GetSize(sig_q));
|
||||||
int nid = next_nid++;
|
int nid = next_nid++;
|
||||||
|
@ -681,7 +681,7 @@ struct BtorWorker
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
btorf("; initval = %s\n", log_signal(firstword));
|
btorf("; initval = %s\n", log_signal(firstword));
|
||||||
nid_init_val = get_sig_nid(firstword);
|
nid_init_val = get_sig_nid(firstword, -1, false, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -693,8 +693,8 @@ struct BtorWorker
|
||||||
if (thisword.is_fully_undef())
|
if (thisword.is_fully_undef())
|
||||||
continue;
|
continue;
|
||||||
Const thisaddr(i, abits);
|
Const thisaddr(i, abits);
|
||||||
int nid_thisword = get_sig_nid(thisword);
|
int nid_thisword = get_sig_nid(thisword, -1, false, true);
|
||||||
int nid_thisaddr = get_sig_nid(thisaddr);
|
int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true);
|
||||||
int last_nid_init_val = nid_init_val;
|
int last_nid_init_val = nid_init_val;
|
||||||
nid_init_val = next_nid++;
|
nid_init_val = next_nid++;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
|
@ -792,7 +792,7 @@ struct BtorWorker
|
||||||
cell_recursion_guard.erase(cell);
|
cell_recursion_guard.erase(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false)
|
int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false)
|
||||||
{
|
{
|
||||||
int nid = -1;
|
int nid = -1;
|
||||||
sigmap.apply(sig);
|
sigmap.apply(sig);
|
||||||
|
@ -823,6 +823,9 @@ struct BtorWorker
|
||||||
int sid = get_bv_sid(GetSize(sig));
|
int sid = get_bv_sid(GetSize(sig));
|
||||||
|
|
||||||
int nid_input = next_nid++;
|
int nid_input = next_nid++;
|
||||||
|
if (is_init)
|
||||||
|
btorf("%d state %d\n", nid_input, sid);
|
||||||
|
else
|
||||||
btorf("%d input %d\n", nid_input, sid);
|
btorf("%d input %d\n", nid_input, sid);
|
||||||
|
|
||||||
int nid_masked_input;
|
int nid_masked_input;
|
||||||
|
@ -897,9 +900,12 @@ struct BtorWorker
|
||||||
|
|
||||||
int sid = get_bv_sid(GetSize(s));
|
int sid = get_bv_sid(GetSize(s));
|
||||||
int nid = next_nid++;
|
int nid = next_nid++;
|
||||||
btorf("%d input %d %s\n", nid, sid);
|
btorf("%d input %d\n", nid, sid);
|
||||||
nid_width[nid] = GetSize(s);
|
nid_width[nid] = GetSize(s);
|
||||||
|
|
||||||
|
for (int j = 0; j < GetSize(s); j++)
|
||||||
|
nidbits.push_back(make_pair(nid, j));
|
||||||
|
|
||||||
i += GetSize(s)-1;
|
i += GetSize(s)-1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1070,12 @@ struct BtorWorker
|
||||||
bad_properties.push_back(nid_en_and_not_a);
|
bad_properties.push_back(nid_en_and_not_a);
|
||||||
} else {
|
} else {
|
||||||
int nid = next_nid++;
|
int nid = next_nid++;
|
||||||
btorf("%d bad %d\n", nid, nid_en_and_not_a);
|
string infostr = log_id(cell);
|
||||||
|
if (infostr[0] == '$' && cell->attributes.count("\\src")) {
|
||||||
|
infostr = cell->attributes.at("\\src").decode_string().c_str();
|
||||||
|
std::replace(infostr.begin(), infostr.end(), ' ', '_');
|
||||||
|
}
|
||||||
|
btorf("%d bad %d %s\n", nid, nid_en_and_not_a, infostr.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
btorf_pop(log_id(cell));
|
btorf_pop(log_id(cell));
|
||||||
|
|
|
@ -266,7 +266,7 @@ struct ProtobufBackend : public Backend {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx, !text_mode);
|
||||||
|
|
||||||
log_header(design, "Executing Protobuf backend.\n");
|
log_header(design, "Executing Protobuf backend.\n");
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ struct ProtobufPass : public Pass {
|
||||||
if (!filename.empty()) {
|
if (!filename.empty()) {
|
||||||
rewrite_filename(filename);
|
rewrite_filename(filename);
|
||||||
std::ofstream *ff = new std::ofstream;
|
std::ofstream *ff = new std::ofstream;
|
||||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary));
|
||||||
if (ff->fail()) {
|
if (ff->fail()) {
|
||||||
delete ff;
|
delete ff;
|
||||||
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||||
|
|
|
@ -1256,7 +1256,7 @@ def smt_check_sat():
|
||||||
return smt.check_sat()
|
return smt.check_sat()
|
||||||
|
|
||||||
if tempind:
|
if tempind:
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
skip_counter = step_size
|
skip_counter = step_size
|
||||||
for step in range(num_steps, -1, -1):
|
for step in range(num_steps, -1, -1):
|
||||||
if smt.forall:
|
if smt.forall:
|
||||||
|
@ -1303,7 +1303,7 @@ if tempind:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print_msg("Temporal induction successful.")
|
print_msg("Temporal induction successful.")
|
||||||
retstatus = True
|
retstatus = "PASSED"
|
||||||
break
|
break
|
||||||
|
|
||||||
elif covermode:
|
elif covermode:
|
||||||
|
@ -1321,7 +1321,7 @@ elif covermode:
|
||||||
smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr))
|
smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr))
|
||||||
|
|
||||||
step = 0
|
step = 0
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
found_failed_assert = False
|
found_failed_assert = False
|
||||||
|
|
||||||
assert step_size == 1
|
assert step_size == 1
|
||||||
|
@ -1365,7 +1365,7 @@ elif covermode:
|
||||||
if smt_check_sat() == "unsat":
|
if smt_check_sat() == "unsat":
|
||||||
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
||||||
found_failed_assert = True
|
found_failed_assert = True
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
break
|
break
|
||||||
|
|
||||||
reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
|
reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
|
||||||
|
@ -1400,7 +1400,7 @@ elif covermode:
|
||||||
break
|
break
|
||||||
|
|
||||||
if "1" not in cover_mask:
|
if "1" not in cover_mask:
|
||||||
retstatus = True
|
retstatus = "PASSED"
|
||||||
break
|
break
|
||||||
|
|
||||||
step += 1
|
step += 1
|
||||||
|
@ -1412,7 +1412,7 @@ elif covermode:
|
||||||
|
|
||||||
else: # not tempind, covermode
|
else: # not tempind, covermode
|
||||||
step = 0
|
step = 0
|
||||||
retstatus = True
|
retstatus = "PASSED"
|
||||||
while step < num_steps:
|
while step < num_steps:
|
||||||
smt_state(step)
|
smt_state(step)
|
||||||
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
|
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
|
||||||
|
@ -1459,8 +1459,8 @@ else: # not tempind, covermode
|
||||||
print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step))
|
print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step))
|
||||||
|
|
||||||
if smt_check_sat() == "unsat":
|
if smt_check_sat() == "unsat":
|
||||||
print("%s Warmup failed!" % smt.timestamp())
|
print("%s Assumptions are unsatisfiable!" % smt.timestamp())
|
||||||
retstatus = False
|
retstatus = "PREUNSAT"
|
||||||
break
|
break
|
||||||
|
|
||||||
if not final_only:
|
if not final_only:
|
||||||
|
@ -1487,13 +1487,13 @@ else: # not tempind, covermode
|
||||||
print_msg("Re-solving with appended steps..")
|
print_msg("Re-solving with appended steps..")
|
||||||
if smt_check_sat() == "unsat":
|
if smt_check_sat() == "unsat":
|
||||||
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
break
|
break
|
||||||
print_anyconsts(step)
|
print_anyconsts(step)
|
||||||
for i in range(step, last_check_step+1):
|
for i in range(step, last_check_step+1):
|
||||||
print_failed_asserts(i)
|
print_failed_asserts(i)
|
||||||
write_trace(0, last_check_step+1+append_steps, '%')
|
write_trace(0, last_check_step+1+append_steps, '%')
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
break
|
break
|
||||||
|
|
||||||
smt_pop()
|
smt_pop()
|
||||||
|
@ -1519,7 +1519,7 @@ else: # not tempind, covermode
|
||||||
print_anyconsts(i)
|
print_anyconsts(i)
|
||||||
print_failed_asserts(i, final=True)
|
print_failed_asserts(i, final=True)
|
||||||
write_trace(0, i+1, '%')
|
write_trace(0, i+1, '%')
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
break
|
break
|
||||||
|
|
||||||
smt_pop()
|
smt_pop()
|
||||||
|
@ -1534,7 +1534,7 @@ else: # not tempind, covermode
|
||||||
print_msg("Solving for step %d.." % (last_check_step))
|
print_msg("Solving for step %d.." % (last_check_step))
|
||||||
if smt_check_sat() != "sat":
|
if smt_check_sat() != "sat":
|
||||||
print("%s No solution found!" % smt.timestamp())
|
print("%s No solution found!" % smt.timestamp())
|
||||||
retstatus = False
|
retstatus = "FAILED"
|
||||||
break
|
break
|
||||||
|
|
||||||
elif dumpall:
|
elif dumpall:
|
||||||
|
@ -1551,5 +1551,5 @@ else: # not tempind, covermode
|
||||||
smt.write("(exit)")
|
smt.write("(exit)")
|
||||||
smt.wait()
|
smt.wait()
|
||||||
|
|
||||||
print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)"))
|
print_msg("Status: %s" % retstatus)
|
||||||
sys.exit(0 if retstatus else 1)
|
sys.exit(0 if retstatus == "PASSED" else 1)
|
||||||
|
|
|
@ -1032,12 +1032,17 @@ class MkVcd:
|
||||||
print("$var integer 32 t smt_step $end", file=self.f)
|
print("$var integer 32 t smt_step $end", file=self.f)
|
||||||
print("$var event 1 ! smt_clock $end", file=self.f)
|
print("$var event 1 ! smt_clock $end", file=self.f)
|
||||||
|
|
||||||
|
def vcdescape(n):
|
||||||
|
if n.startswith("$") or ":" in n:
|
||||||
|
return "\\" + n
|
||||||
|
return n
|
||||||
|
|
||||||
scope = []
|
scope = []
|
||||||
for path in sorted(self.nets):
|
for path in sorted(self.nets):
|
||||||
key, width = self.nets[path]
|
key, width = self.nets[path]
|
||||||
|
|
||||||
uipath = list(path)
|
uipath = list(path)
|
||||||
if "." in uipath[-1]:
|
if "." in uipath[-1] and not uipath[-1].startswith("$"):
|
||||||
uipath = uipath[0:-1] + uipath[-1].split(".")
|
uipath = uipath[0:-1] + uipath[-1].split(".")
|
||||||
for i in range(len(uipath)):
|
for i in range(len(uipath)):
|
||||||
uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i])
|
uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i])
|
||||||
|
@ -1048,15 +1053,13 @@ class MkVcd:
|
||||||
|
|
||||||
while uipath[:-1] != scope:
|
while uipath[:-1] != scope:
|
||||||
scopename = uipath[len(scope)]
|
scopename = uipath[len(scope)]
|
||||||
if scopename.startswith("$"):
|
print("$scope module %s $end" % vcdescape(scopename), file=self.f)
|
||||||
scopename = "\\" + scopename
|
|
||||||
print("$scope module %s $end" % scopename, file=self.f)
|
|
||||||
scope.append(uipath[len(scope)])
|
scope.append(uipath[len(scope)])
|
||||||
|
|
||||||
if path in self.clocks and self.clocks[path][1] == "event":
|
if path in self.clocks and self.clocks[path][1] == "event":
|
||||||
print("$var event 1 %s %s $end" % (key, uipath[-1]), file=self.f)
|
print("$var event 1 %s %s $end" % (key, vcdescape(uipath[-1])), file=self.f)
|
||||||
else:
|
else:
|
||||||
print("$var wire %d %s %s $end" % (width, key, uipath[-1]), file=self.f)
|
print("$var wire %d %s %s $end" % (width, key, vcdescape(uipath[-1])), file=self.f)
|
||||||
|
|
||||||
for i in range(len(scope)):
|
for i in range(len(scope)):
|
||||||
print("$upscope $end", file=self.f)
|
print("$upscope $end", file=self.f)
|
||||||
|
|
|
@ -33,11 +33,11 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit;
|
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit;
|
||||||
int auto_name_counter, auto_name_offset, auto_name_digits;
|
int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
|
||||||
std::map<RTLIL::IdString, int> auto_name_map;
|
std::map<RTLIL::IdString, int> auto_name_map;
|
||||||
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
||||||
std::string auto_prefix;
|
std::string auto_prefix, extmem_prefix;
|
||||||
|
|
||||||
RTLIL::Module *active_module;
|
RTLIL::Module *active_module;
|
||||||
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
|
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
|
||||||
|
@ -371,13 +371,14 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool as_comment = false)
|
void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false)
|
||||||
{
|
{
|
||||||
if (noattr)
|
if (noattr)
|
||||||
return;
|
return;
|
||||||
if (attr2comment)
|
if (attr2comment)
|
||||||
as_comment = true;
|
as_comment = true;
|
||||||
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
|
||||||
|
if (it->first == "\\init" && regattr) continue;
|
||||||
f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
|
f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
|
||||||
f << stringf(" = ");
|
f << stringf(" = ");
|
||||||
if (modattr && (it->second == State::S0 || it->second == Const(0)))
|
if (modattr && (it->second == State::S0 || it->second == Const(0)))
|
||||||
|
@ -392,7 +393,7 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
|
||||||
|
|
||||||
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
|
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
|
||||||
{
|
{
|
||||||
dump_attributes(f, indent, wire->attributes);
|
dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
|
||||||
#if 0
|
#if 0
|
||||||
if (wire->port_input && !wire->port_output)
|
if (wire->port_input && !wire->port_output)
|
||||||
f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
|
||||||
|
@ -1067,6 +1068,55 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
// end
|
// end
|
||||||
f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
|
f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
|
||||||
if (use_init)
|
if (use_init)
|
||||||
|
{
|
||||||
|
if (extmem)
|
||||||
|
{
|
||||||
|
std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++);
|
||||||
|
|
||||||
|
std::string extmem_filename_esc;
|
||||||
|
for (auto c : extmem_filename)
|
||||||
|
{
|
||||||
|
if (c == '\n')
|
||||||
|
extmem_filename_esc += "\\n";
|
||||||
|
else if (c == '\t')
|
||||||
|
extmem_filename_esc += "\\t";
|
||||||
|
else if (c < 32)
|
||||||
|
extmem_filename_esc += stringf("\\%03o", c);
|
||||||
|
else if (c == '"')
|
||||||
|
extmem_filename_esc += "\\\"";
|
||||||
|
else if (c == '\\')
|
||||||
|
extmem_filename_esc += "\\\\";
|
||||||
|
else
|
||||||
|
extmem_filename_esc += c;
|
||||||
|
}
|
||||||
|
f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str());
|
||||||
|
|
||||||
|
std::ofstream extmem_f(extmem_filename, std::ofstream::trunc);
|
||||||
|
if (extmem_f.fail())
|
||||||
|
log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i=0; i<size; i++)
|
||||||
|
{
|
||||||
|
RTLIL::Const element = cell->parameters["\\INIT"].extract(i*width, width);
|
||||||
|
for (int j=0; j<element.size(); j++)
|
||||||
|
{
|
||||||
|
switch (element[element.size()-j-1])
|
||||||
|
{
|
||||||
|
case State::S0: extmem_f << '0'; break;
|
||||||
|
case State::S1: extmem_f << '1'; break;
|
||||||
|
case State::Sx: extmem_f << 'x'; break;
|
||||||
|
case State::Sz: extmem_f << 'z'; break;
|
||||||
|
case State::Sa: extmem_f << '_'; break;
|
||||||
|
case State::Sm: log_error("Found marker state in final netlist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extmem_f << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||||
for (int i=0; i<size; i++)
|
for (int i=0; i<size; i++)
|
||||||
|
@ -1077,6 +1127,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
}
|
}
|
||||||
f << stringf("%s" "end\n", indent.c_str());
|
f << stringf("%s" "end\n", indent.c_str());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create a map : "edge clk" -> expressions within that clock domain
|
// create a map : "edge clk" -> expressions within that clock domain
|
||||||
dict<std::string, std::vector<std::string>> clk_to_lof_body;
|
dict<std::string, std::vector<std::string>> clk_to_lof_body;
|
||||||
|
@ -1521,7 +1572,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
|
||||||
|
|
||||||
bool got_default = false;
|
bool got_default = false;
|
||||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
|
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
|
||||||
dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*as_comment=*/true);
|
dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true);
|
||||||
if ((*it)->compare.size() == 0) {
|
if ((*it)->compare.size() == 0) {
|
||||||
if (got_default)
|
if (got_default)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1686,7 +1737,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true);
|
dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true);
|
||||||
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
|
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
|
||||||
bool keep_running = true;
|
bool keep_running = true;
|
||||||
for (int port_id = 1; keep_running; port_id++) {
|
for (int port_id = 1; keep_running; port_id++) {
|
||||||
|
@ -1776,8 +1827,16 @@ struct VerilogBackend : public Backend {
|
||||||
log(" deactivates this feature and instead will write string constants\n");
|
log(" deactivates this feature and instead will write string constants\n");
|
||||||
log(" as binary numbers.\n");
|
log(" as binary numbers.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -extmem\n");
|
||||||
|
log(" instead of initializing memories using assignments to individual\n");
|
||||||
|
log(" elements, use the '$readmemh' function to read initialization data\n");
|
||||||
|
log(" from a file. This data is written to a file named by appending\n");
|
||||||
|
log(" a sequential index to the Verilog filename and replacing the extension\n");
|
||||||
|
log(" with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n");
|
||||||
|
log(" 'foo-2.mem' and so on.\n");
|
||||||
|
log("\n");
|
||||||
log(" -defparam\n");
|
log(" -defparam\n");
|
||||||
log(" Use 'defparam' statements instead of the Verilog-2001 syntax for\n");
|
log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n");
|
||||||
log(" cell parameters.\n");
|
log(" cell parameters.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -blackboxes\n");
|
log(" -blackboxes\n");
|
||||||
|
@ -1811,6 +1870,7 @@ struct VerilogBackend : public Backend {
|
||||||
nodec = false;
|
nodec = false;
|
||||||
nohex = false;
|
nohex = false;
|
||||||
nostr = false;
|
nostr = false;
|
||||||
|
extmem = false;
|
||||||
defparam = false;
|
defparam = false;
|
||||||
decimal = false;
|
decimal = false;
|
||||||
siminit = false;
|
siminit = false;
|
||||||
|
@ -1884,6 +1944,11 @@ struct VerilogBackend : public Backend {
|
||||||
nostr = true;
|
nostr = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-extmem") {
|
||||||
|
extmem = true;
|
||||||
|
extmem_counter = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (arg == "-defparam") {
|
if (arg == "-defparam") {
|
||||||
defparam = true;
|
defparam = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -1911,6 +1976,12 @@ struct VerilogBackend : public Backend {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx);
|
||||||
|
if (extmem)
|
||||||
|
{
|
||||||
|
if (filename.empty())
|
||||||
|
log_cmd_error("Option -extmem must be used with a filename.\n");
|
||||||
|
extmem_prefix = filename.substr(0, filename.rfind('.'));
|
||||||
|
}
|
||||||
|
|
||||||
design->sort();
|
design->sort();
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,10 @@
|
||||||
// 50 MHz Clock
|
IO_LOC "clk" 35;
|
||||||
IO_LOC "clk" D11;
|
//IO_LOC "rst_n" 77;
|
||||||
|
IO_LOC "leds[0]" 79;
|
||||||
// LEDs
|
IO_LOC "leds[1]" 80;
|
||||||
IO_LOC "leds[0]" D22;
|
IO_LOC "leds[2]" 81;
|
||||||
IO_LOC "leds[1]" E22;
|
IO_LOC "leds[3]" 82;
|
||||||
IO_LOC "leds[2]" G22;
|
IO_LOC "leds[4]" 83;
|
||||||
IO_LOC "leds[3]" J22;
|
IO_LOC "leds[5]" 84;
|
||||||
IO_LOC "leds[4]" L22;
|
IO_LOC "leds[6]" 85;
|
||||||
IO_LOC "leds[5]" L19;
|
IO_LOC "leds[7]" 86;
|
||||||
IO_LOC "leds[6]" L20;
|
|
||||||
IO_LOC "leds[7]" M21;
|
|
||||||
IO_LOC "leds[8]" N19;
|
|
||||||
IO_LOC "leds[9]" R19;
|
|
||||||
IO_LOC "leds[10]" T18;
|
|
||||||
IO_LOC "leds[11]" AA22;
|
|
||||||
IO_LOC "leds[12]" U18;
|
|
||||||
IO_LOC "leds[13]" V20;
|
|
||||||
IO_LOC "leds[14]" AA21;
|
|
||||||
IO_LOC "leds[15]" AB21;
|
|
||||||
|
|
||||||
|
|
||||||
// 7-Segment Display
|
|
||||||
IO_LOC "seg7dig[0]" E20;
|
|
||||||
IO_LOC "seg7dig[1]" G18;
|
|
||||||
IO_LOC "seg7dig[2]" G20;
|
|
||||||
IO_LOC "seg7dig[3]" F21;
|
|
||||||
IO_LOC "seg7dig[4]" J20;
|
|
||||||
IO_LOC "seg7dig[5]" H21;
|
|
||||||
IO_LOC "seg7dig[6]" H18;
|
|
||||||
IO_LOC "seg7dig[7]" D20;
|
|
||||||
IO_LOC "seg7sel[0]" C19;
|
|
||||||
IO_LOC "seg7sel[1]" B22;
|
|
||||||
IO_LOC "seg7sel[2]" C20;
|
|
||||||
IO_LOC "seg7sel[3]" C21;
|
|
||||||
|
|
||||||
// Switches
|
|
||||||
IO_LOC "sw[0]" AB20;
|
|
||||||
IO_LOC "sw[1]" AB19;
|
|
||||||
IO_LOC "sw[2]" AB18;
|
|
||||||
IO_LOC "sw[3]" AB17;
|
|
|
@ -1,9 +1,7 @@
|
||||||
module demo (
|
module demo (
|
||||||
input clk,
|
input clk,
|
||||||
input [3:0] sw,
|
|
||||||
output [15:0] leds,
|
output [15:0] leds,
|
||||||
output [7:0] seg7dig,
|
output unused
|
||||||
output [3:0] seg7sel
|
|
||||||
);
|
);
|
||||||
localparam PRESCALE = 20;
|
localparam PRESCALE = 20;
|
||||||
reg [PRESCALE+3:0] counter = 0;
|
reg [PRESCALE+3:0] counter = 0;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
set JTAG regular_io = false
|
||||||
|
set SSPI regular_io = false
|
||||||
|
set MSPI regular_io = false
|
||||||
|
set READY regular_io = false
|
||||||
|
set DONE regular_io = false
|
||||||
|
set RECONFIG_N regular_io = false
|
||||||
|
set MODE regular_io = false
|
||||||
|
set CRC_check = true
|
||||||
|
set compress = false
|
||||||
|
set encryption = false
|
||||||
|
set security_bit_enable = true
|
||||||
|
set bsram_init_fuse_print = true
|
||||||
|
set download_speed = 250/100
|
||||||
|
set spi_flash_address = 0x00FFF000
|
||||||
|
set format = txt
|
||||||
|
set background_programming = false
|
|
@ -0,0 +1,8 @@
|
||||||
|
-sdf
|
||||||
|
-oc
|
||||||
|
-ibs
|
||||||
|
-posp
|
||||||
|
-o
|
||||||
|
-warning_all
|
||||||
|
-tt
|
||||||
|
-timing
|
|
@ -1,8 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -ex
|
set -ex
|
||||||
yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v
|
yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v
|
||||||
$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW2A55-PBGA484-6 \
|
$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW1NR-9-QFN88-6 -pn GW1NR-LV9QN88C6/I5 -cfg device.cfg -bit -tr -ph -timing -gpa -rpt -warning_all
|
||||||
-warning_all -out demo_out.v -rpt demo.rpt -tr demo_tr.html -bit demo.bit
|
|
||||||
|
|
||||||
# post place&route simulation (icarus verilog)
|
# post place&route simulation (icarus verilog)
|
||||||
if false; then
|
if false; then
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# gw_sh run.tcl
|
||||||
|
exec yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v
|
||||||
|
add_file -cst demo.cst
|
||||||
|
add_file -sdc demo.sdc
|
||||||
|
add_file -vm demo_syn.v
|
||||||
|
add_file -cfg device.cfg
|
||||||
|
set_option -device GW1NR-9-QFN88-6
|
||||||
|
set_option -pn GW1NR-LV9QN88C6/I5
|
||||||
|
run_pnr -opt pnr.cfg
|
|
@ -285,6 +285,8 @@ end_of_header:
|
||||||
}
|
}
|
||||||
else if (c == 'c') {
|
else if (c == 'c') {
|
||||||
f.ignore(1);
|
f.ignore(1);
|
||||||
|
if (f.peek() == '\r')
|
||||||
|
f.ignore(1);
|
||||||
if (f.peek() == '\n')
|
if (f.peek() == '\n')
|
||||||
break;
|
break;
|
||||||
// Else constraint (TODO)
|
// Else constraint (TODO)
|
||||||
|
@ -738,22 +740,22 @@ void AigerReader::post_process()
|
||||||
log_assert(box_module);
|
log_assert(box_module);
|
||||||
|
|
||||||
if (seen_boxes.insert(cell->type).second) {
|
if (seen_boxes.insert(cell->type).second) {
|
||||||
auto it = box_module->attributes.find("\\abc_carry");
|
auto it = box_module->attributes.find("\\abc9_carry");
|
||||||
if (it != box_module->attributes.end()) {
|
if (it != box_module->attributes.end()) {
|
||||||
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
|
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
|
||||||
auto carry_in_out = it->second.decode_string();
|
auto carry_in_out = it->second.decode_string();
|
||||||
auto pos = carry_in_out.find(',');
|
auto pos = carry_in_out.find(',');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
|
log_error("'abc9_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
|
||||||
auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos));
|
auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos));
|
||||||
carry_in = box_module->wire(carry_in_name);
|
carry_in = box_module->wire(carry_in_name);
|
||||||
if (!carry_in || !carry_in->port_input)
|
if (!carry_in || !carry_in->port_input)
|
||||||
log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
|
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
|
||||||
|
|
||||||
auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
|
auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
|
||||||
carry_out = box_module->wire(carry_out_name);
|
carry_out = box_module->wire(carry_out_name);
|
||||||
if (!carry_out || !carry_out->port_output)
|
if (!carry_out || !carry_out->port_output)
|
||||||
log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
|
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
|
||||||
|
|
||||||
auto &ports = box_module->ports;
|
auto &ports = box_module->ports;
|
||||||
for (auto jt = ports.begin(); jt != ports.end(); ) {
|
for (auto jt = ports.begin(); jt != ports.end(); ) {
|
||||||
|
@ -868,7 +870,7 @@ void AigerReader::post_process()
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
if (escaped_s.ends_with("$inout.out")) {
|
if (escaped_s.ends_with("$inout.out")) {
|
||||||
wire->port_output = false;
|
wire->port_output = false;
|
||||||
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10));
|
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11));
|
||||||
log_assert(in_wire);
|
log_assert(in_wire);
|
||||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||||
in_wire->port_output = true;
|
in_wire->port_output = true;
|
||||||
|
@ -889,7 +891,7 @@ void AigerReader::post_process()
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
if (escaped_s.ends_with("$inout.out")) {
|
if (escaped_s.ends_with("$inout.out")) {
|
||||||
wire->port_output = false;
|
wire->port_output = false;
|
||||||
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index));
|
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index));
|
||||||
log_assert(in_wire);
|
log_assert(in_wire);
|
||||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||||
in_wire->port_output = true;
|
in_wire->port_output = true;
|
||||||
|
@ -1056,13 +1058,15 @@ struct AigerFrontend : public Frontend {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx, true);
|
||||||
|
|
||||||
if (module_name.empty()) {
|
if (module_name.empty()) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char fname[_MAX_FNAME];
|
char fname[_MAX_FNAME];
|
||||||
_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */);
|
_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */);
|
||||||
module_name = fname;
|
char* bn = strdup(fname);
|
||||||
|
module_name = RTLIL::escape_id(bn);
|
||||||
|
free(bn);
|
||||||
#else
|
#else
|
||||||
char* bn = strdup(filename.c_str());
|
char* bn = strdup(filename.c_str());
|
||||||
module_name = RTLIL::escape_id(bn);
|
module_name = RTLIL::escape_id(bn);
|
||||||
|
|
|
@ -164,6 +164,8 @@ std::string AST::type2str(AstNodeType type)
|
||||||
X(AST_MODPORT)
|
X(AST_MODPORT)
|
||||||
X(AST_MODPORTMEMBER)
|
X(AST_MODPORTMEMBER)
|
||||||
X(AST_PACKAGE)
|
X(AST_PACKAGE)
|
||||||
|
X(AST_WIRETYPE)
|
||||||
|
X(AST_TYPEDEF)
|
||||||
#undef X
|
#undef X
|
||||||
default:
|
default:
|
||||||
log_abort();
|
log_abort();
|
||||||
|
@ -206,6 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
||||||
was_checked = false;
|
was_checked = false;
|
||||||
range_valid = false;
|
range_valid = false;
|
||||||
range_swapped = false;
|
range_swapped = false;
|
||||||
|
is_custom_type = false;
|
||||||
port_id = 0;
|
port_id = 0;
|
||||||
range_left = -1;
|
range_left = -1;
|
||||||
range_right = 0;
|
range_right = 0;
|
||||||
|
@ -1382,10 +1385,10 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
|
||||||
|
|
||||||
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
|
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
|
||||||
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
|
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
|
||||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail)
|
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool /*mayfail*/)
|
||||||
{
|
{
|
||||||
AstNode *new_ast = NULL;
|
AstNode *new_ast = NULL;
|
||||||
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
|
std::string modname = derive_common(design, parameters, &new_ast);
|
||||||
|
|
||||||
// Since interfaces themselves may be instantiated with different parameters,
|
// Since interfaces themselves may be instantiated with different parameters,
|
||||||
// "modname" must also take those into account, so that unique modules
|
// "modname" must also take those into account, so that unique modules
|
||||||
|
@ -1398,11 +1401,17 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
has_interfaces = true;
|
has_interfaces = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string new_modname = modname;
|
||||||
if (has_interfaces)
|
if (has_interfaces)
|
||||||
modname += "$interfaces$" + interf_info;
|
new_modname += "$interfaces$" + interf_info;
|
||||||
|
|
||||||
|
|
||||||
if (!design->has(modname)) {
|
if (!design->has(new_modname)) {
|
||||||
|
if (!new_ast) {
|
||||||
|
auto mod = dynamic_cast<AstModule*>(design->module(modname));
|
||||||
|
new_ast = mod->ast->clone();
|
||||||
|
}
|
||||||
|
modname = new_modname;
|
||||||
new_ast->str = modname;
|
new_ast->str = modname;
|
||||||
|
|
||||||
// Iterate over all interfaces which are ports in this module:
|
// Iterate over all interfaces which are ports in this module:
|
||||||
|
@ -1455,10 +1464,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
|
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
|
||||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail)
|
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/)
|
||||||
{
|
{
|
||||||
AstNode *new_ast = NULL;
|
AstNode *new_ast = NULL;
|
||||||
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
|
std::string modname = derive_common(design, parameters, &new_ast);
|
||||||
|
|
||||||
if (!design->has(modname)) {
|
if (!design->has(modname)) {
|
||||||
new_ast->str = modname;
|
new_ast->str = modname;
|
||||||
|
@ -1473,31 +1482,66 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new parametric module (when needed) and return the name of the generated module
|
// create a new parametric module (when needed) and return the name of the generated module
|
||||||
std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool)
|
std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out)
|
||||||
{
|
{
|
||||||
std::string stripped_name = name.str();
|
std::string stripped_name = name.str();
|
||||||
|
|
||||||
if (stripped_name.compare(0, 9, "$abstract") == 0)
|
if (stripped_name.compare(0, 9, "$abstract") == 0)
|
||||||
stripped_name = stripped_name.substr(9);
|
stripped_name = stripped_name.substr(9);
|
||||||
|
|
||||||
log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
|
|
||||||
loadconfig();
|
|
||||||
|
|
||||||
std::string para_info;
|
std::string para_info;
|
||||||
AstNode *new_ast = ast->clone();
|
|
||||||
|
|
||||||
int para_counter = 0;
|
int para_counter = 0;
|
||||||
int orig_parameters_n = parameters.size();
|
for (const auto child : ast->children) {
|
||||||
for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) {
|
|
||||||
AstNode *child = *it;
|
|
||||||
if (child->type != AST_PARAMETER)
|
if (child->type != AST_PARAMETER)
|
||||||
continue;
|
continue;
|
||||||
para_counter++;
|
para_counter++;
|
||||||
std::string para_id = child->str;
|
std::string para_id = child->str;
|
||||||
if (parameters.count(para_id) > 0) {
|
if (parameters.count(para_id) > 0) {
|
||||||
log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
|
log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
|
||||||
rewrite_parameter:
|
|
||||||
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
para_id = stringf("$%d", para_counter);
|
||||||
|
if (parameters.count(para_id) > 0) {
|
||||||
|
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||||
|
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string modname;
|
||||||
|
if (parameters.size() == 0)
|
||||||
|
modname = stripped_name;
|
||||||
|
else if (para_info.size() > 60)
|
||||||
|
modname = "$paramod$" + sha1(para_info) + stripped_name;
|
||||||
|
else
|
||||||
|
modname = "$paramod" + stripped_name + para_info;
|
||||||
|
|
||||||
|
if (design->has(modname))
|
||||||
|
return modname;
|
||||||
|
|
||||||
|
log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
|
||||||
|
loadconfig();
|
||||||
|
|
||||||
|
AstNode *new_ast = ast->clone();
|
||||||
|
para_counter = 0;
|
||||||
|
for (auto child : new_ast->children) {
|
||||||
|
if (child->type != AST_PARAMETER)
|
||||||
|
continue;
|
||||||
|
para_counter++;
|
||||||
|
std::string para_id = child->str;
|
||||||
|
if (parameters.count(para_id) > 0) {
|
||||||
|
log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
|
||||||
|
goto rewrite_parameter;
|
||||||
|
}
|
||||||
|
para_id = stringf("$%d", para_counter);
|
||||||
|
if (parameters.count(para_id) > 0) {
|
||||||
|
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||||
|
goto rewrite_parameter;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
rewrite_parameter:
|
||||||
delete child->children.at(0);
|
delete child->children.at(0);
|
||||||
if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
|
if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
|
||||||
child->children[0] = new AstNode(AST_REALVALUE);
|
child->children[0] = new AstNode(AST_REALVALUE);
|
||||||
|
@ -1507,13 +1551,6 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
|
||||||
else
|
else
|
||||||
child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
||||||
parameters.erase(para_id);
|
parameters.erase(para_id);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
para_id = stringf("$%d", para_counter);
|
|
||||||
if (parameters.count(para_id) > 0) {
|
|
||||||
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
|
||||||
goto rewrite_parameter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto param : parameters) {
|
for (auto param : parameters) {
|
||||||
|
@ -1526,16 +1563,6 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
|
||||||
new_ast->children.push_back(defparam);
|
new_ast->children.push_back(defparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string modname;
|
|
||||||
|
|
||||||
if (orig_parameters_n == 0)
|
|
||||||
modname = stripped_name;
|
|
||||||
else if (para_info.size() > 60)
|
|
||||||
modname = "$paramod$" + sha1(para_info) + stripped_name;
|
|
||||||
else
|
|
||||||
modname = "$paramod" + stripped_name + para_info;
|
|
||||||
|
|
||||||
|
|
||||||
(*new_ast_out) = new_ast;
|
(*new_ast_out) = new_ast;
|
||||||
return modname;
|
return modname;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,10 @@ namespace AST
|
||||||
AST_INTERFACEPORTTYPE,
|
AST_INTERFACEPORTTYPE,
|
||||||
AST_MODPORT,
|
AST_MODPORT,
|
||||||
AST_MODPORTMEMBER,
|
AST_MODPORTMEMBER,
|
||||||
AST_PACKAGE
|
AST_PACKAGE,
|
||||||
|
|
||||||
|
AST_WIRETYPE,
|
||||||
|
AST_TYPEDEF
|
||||||
};
|
};
|
||||||
|
|
||||||
// convert an node type to a string (e.g. for debug output)
|
// convert an node type to a string (e.g. for debug output)
|
||||||
|
@ -174,7 +177,7 @@ namespace AST
|
||||||
// node content - most of it is unused in most node types
|
// node content - most of it is unused in most node types
|
||||||
std::string str;
|
std::string str;
|
||||||
std::vector<RTLIL::State> bits;
|
std::vector<RTLIL::State> bits;
|
||||||
bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized;
|
bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type;
|
||||||
int port_id, range_left, range_right;
|
int port_id, range_left, range_right;
|
||||||
uint32_t integer;
|
uint32_t integer;
|
||||||
double realvalue;
|
double realvalue;
|
||||||
|
@ -296,7 +299,7 @@ namespace AST
|
||||||
~AstModule() YS_OVERRIDE;
|
~AstModule() YS_OVERRIDE;
|
||||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
|
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
|
||||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
|
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
|
||||||
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
|
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out);
|
||||||
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
|
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
|
||||||
RTLIL::Module *clone() const YS_OVERRIDE;
|
RTLIL::Module *clone() const YS_OVERRIDE;
|
||||||
void loadconfig() const;
|
void loadconfig() const;
|
||||||
|
|
|
@ -863,6 +863,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
case AST_PACKAGE:
|
case AST_PACKAGE:
|
||||||
case AST_MODPORT:
|
case AST_MODPORT:
|
||||||
case AST_MODPORTMEMBER:
|
case AST_MODPORTMEMBER:
|
||||||
|
case AST_TYPEDEF:
|
||||||
break;
|
break;
|
||||||
case AST_INTERFACEPORT: {
|
case AST_INTERFACEPORT: {
|
||||||
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
|
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
|
||||||
|
|
|
@ -318,7 +318,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
}
|
}
|
||||||
|
|
||||||
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
|
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
|
||||||
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX)
|
if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
|
||||||
const_fold = true;
|
const_fold = true;
|
||||||
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
|
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
|
||||||
const_fold = true;
|
const_fold = true;
|
||||||
|
@ -336,6 +336,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
std::map<std::string, AstNode*> this_wire_scope;
|
std::map<std::string, AstNode*> this_wire_scope;
|
||||||
for (size_t i = 0; i < children.size(); i++) {
|
for (size_t i = 0; i < children.size(); i++) {
|
||||||
AstNode *node = children[i];
|
AstNode *node = children[i];
|
||||||
|
|
||||||
if (node->type == AST_WIRE) {
|
if (node->type == AST_WIRE) {
|
||||||
if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
|
if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
|
||||||
for (auto c : node->children[0]->children) {
|
for (auto c : node->children[0]->children) {
|
||||||
|
@ -405,14 +406,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
this_wire_scope[node->str] = node;
|
this_wire_scope[node->str] = node;
|
||||||
}
|
}
|
||||||
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
|
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
|
||||||
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) {
|
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
|
||||||
|
node->type == AST_TYPEDEF) {
|
||||||
backup_scope[node->str] = current_scope[node->str];
|
backup_scope[node->str] = current_scope[node->str];
|
||||||
current_scope[node->str] = node;
|
current_scope[node->str] = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < children.size(); i++) {
|
for (size_t i = 0; i < children.size(); i++) {
|
||||||
AstNode *node = children[i];
|
AstNode *node = children[i];
|
||||||
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY)
|
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF)
|
||||||
while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
|
while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
|
@ -780,6 +782,99 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
delete_children();
|
delete_children();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolve typedefs
|
||||||
|
if (type == AST_TYPEDEF) {
|
||||||
|
log_assert(children.size() == 1);
|
||||||
|
log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY);
|
||||||
|
while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param))
|
||||||
|
did_something = true;
|
||||||
|
log_assert(!children[0]->is_custom_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve types of wires
|
||||||
|
if (type == AST_WIRE || type == AST_MEMORY) {
|
||||||
|
if (is_custom_type) {
|
||||||
|
log_assert(children.size() >= 1);
|
||||||
|
log_assert(children[0]->type == AST_WIRETYPE);
|
||||||
|
if (!current_scope.count(children[0]->str))
|
||||||
|
log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str());
|
||||||
|
AstNode *resolved_type = current_scope.at(children[0]->str);
|
||||||
|
if (resolved_type->type != AST_TYPEDEF)
|
||||||
|
log_file_error(filename, linenum, "`%s' does not name a type\n", children[0]->str.c_str());
|
||||||
|
log_assert(resolved_type->children.size() == 1);
|
||||||
|
AstNode *templ = resolved_type->children[0];
|
||||||
|
// Remove type reference
|
||||||
|
delete children[0];
|
||||||
|
children.erase(children.begin());
|
||||||
|
|
||||||
|
// Ensure typedef itself is fully simplified
|
||||||
|
while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
|
||||||
|
|
||||||
|
if (type == AST_WIRE)
|
||||||
|
type = templ->type;
|
||||||
|
is_reg = templ->is_reg;
|
||||||
|
is_logic = templ->is_logic;
|
||||||
|
is_signed = templ->is_signed;
|
||||||
|
is_string = templ->is_string;
|
||||||
|
is_custom_type = templ->is_custom_type;
|
||||||
|
|
||||||
|
range_valid = templ->range_valid;
|
||||||
|
range_swapped = templ->range_swapped;
|
||||||
|
range_left = templ->range_left;
|
||||||
|
range_right = templ->range_right;
|
||||||
|
|
||||||
|
// Insert clones children from template at beginning
|
||||||
|
for (int i = 0; i < GetSize(templ->children); i++)
|
||||||
|
children.insert(children.begin() + i, templ->children[i]->clone());
|
||||||
|
|
||||||
|
if (type == AST_MEMORY && GetSize(children) == 1) {
|
||||||
|
// Single-bit memories must have [0:0] range
|
||||||
|
AstNode *rng = new AstNode(AST_RANGE);
|
||||||
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
|
children.insert(children.begin(), rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
did_something = true;
|
||||||
|
}
|
||||||
|
log_assert(!is_custom_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve types of parameters
|
||||||
|
if (type == AST_LOCALPARAM || type == AST_PARAMETER) {
|
||||||
|
if (is_custom_type) {
|
||||||
|
log_assert(children.size() == 2);
|
||||||
|
log_assert(children[1]->type == AST_WIRETYPE);
|
||||||
|
if (!current_scope.count(children[1]->str))
|
||||||
|
log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
|
||||||
|
AstNode *resolved_type = current_scope.at(children[1]->str);
|
||||||
|
if (resolved_type->type != AST_TYPEDEF)
|
||||||
|
log_file_error(filename, linenum, "`%s' does not name a type\n", children[1]->str.c_str());
|
||||||
|
log_assert(resolved_type->children.size() == 1);
|
||||||
|
AstNode *templ = resolved_type->children[0];
|
||||||
|
delete children[1];
|
||||||
|
children.pop_back();
|
||||||
|
|
||||||
|
// Ensure typedef itself is fully simplified
|
||||||
|
while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
|
||||||
|
|
||||||
|
if (templ->type == AST_MEMORY)
|
||||||
|
log_file_error(filename, linenum, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
|
||||||
|
is_signed = templ->is_signed;
|
||||||
|
is_string = templ->is_string;
|
||||||
|
is_custom_type = templ->is_custom_type;
|
||||||
|
|
||||||
|
range_valid = templ->range_valid;
|
||||||
|
range_swapped = templ->range_swapped;
|
||||||
|
range_left = templ->range_left;
|
||||||
|
range_right = templ->range_right;
|
||||||
|
for (auto template_child : templ->children)
|
||||||
|
children.push_back(template_child->clone());
|
||||||
|
did_something = true;
|
||||||
|
}
|
||||||
|
log_assert(!is_custom_type);
|
||||||
|
}
|
||||||
|
|
||||||
// resolve constant prefixes
|
// resolve constant prefixes
|
||||||
if (type == AST_PREFIX) {
|
if (type == AST_PREFIX) {
|
||||||
if (children[0]->type != AST_CONSTANT) {
|
if (children[0]->type != AST_CONSTANT) {
|
||||||
|
@ -1194,7 +1289,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
if (type == AST_BLOCK && str.empty())
|
if (type == AST_BLOCK && str.empty())
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < children.size(); i++)
|
for (size_t i = 0; i < children.size(); i++)
|
||||||
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM)
|
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF)
|
||||||
log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
|
log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,7 +1301,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
||||||
|
|
||||||
std::vector<AstNode*> new_children;
|
std::vector<AstNode*> new_children;
|
||||||
for (size_t i = 0; i < children.size(); i++)
|
for (size_t i = 0; i < children.size(); i++)
|
||||||
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
|
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
|
||||||
children[i]->simplify(false, false, false, stage, -1, false, false);
|
children[i]->simplify(false, false, false, stage, -1, false, false);
|
||||||
current_ast_mod->children.push_back(children[i]);
|
current_ast_mod->children.push_back(children[i]);
|
||||||
current_scope[children[i]->str] = children[i];
|
current_scope[children[i]->str] = children[i];
|
||||||
|
@ -2906,7 +3001,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0)
|
if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0)
|
||||||
str = name_map[str];
|
str = name_map[str];
|
||||||
|
|
||||||
std::map<std::string, std::string> backup_name_map;
|
std::map<std::string, std::string> backup_name_map;
|
||||||
|
@ -2914,7 +3009,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
||||||
for (size_t i = 0; i < children.size(); i++) {
|
for (size_t i = 0; i < children.size(); i++) {
|
||||||
AstNode *child = children[i];
|
AstNode *child = children[i];
|
||||||
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
|
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
|
||||||
child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) {
|
child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) {
|
||||||
if (backup_name_map.size() == 0)
|
if (backup_name_map.size() == 0)
|
||||||
backup_name_map = name_map;
|
backup_name_map = name_map;
|
||||||
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
|
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
|
||||||
|
@ -2945,6 +3040,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
||||||
child->expand_genblock(index_var, prefix, name_map);
|
child->expand_genblock(index_var, prefix, name_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (backup_name_map.size() > 0)
|
if (backup_name_map.size() > 0)
|
||||||
name_map.swap(backup_name_map);
|
name_map.swap(backup_name_map);
|
||||||
}
|
}
|
||||||
|
@ -2998,6 +3094,9 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
||||||
uint32_t children_flags = 0;
|
uint32_t children_flags = 0;
|
||||||
int lhs_children_counter = 0;
|
int lhs_children_counter = 0;
|
||||||
|
|
||||||
|
if (type == AST_TYPEDEF)
|
||||||
|
return; // don't touch content of typedefs
|
||||||
|
|
||||||
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
|
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
|
||||||
{
|
{
|
||||||
// mark all memories that are used in a complex expression on the left side of an assignment
|
// mark all memories that are used in a complex expression on the left side of an assignment
|
||||||
|
@ -3155,6 +3254,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
|
||||||
if (type == AST_FUNCTION || type == AST_TASK)
|
if (type == AST_FUNCTION || type == AST_TASK)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (type == AST_TYPEDEF)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
|
if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
|
||||||
{
|
{
|
||||||
log_assert(children[0]->type == AST_CONSTANT);
|
log_assert(children[0]->type == AST_CONSTANT);
|
||||||
|
|
|
@ -174,6 +174,12 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
||||||
if (module == nullptr)
|
if (module == nullptr)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if (!strcmp(cmd, ".blackbox"))
|
||||||
|
{
|
||||||
|
module->attributes["\\blackbox"] = RTLIL::Const(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(cmd, ".end"))
|
if (!strcmp(cmd, ".end"))
|
||||||
{
|
{
|
||||||
for (auto &wp : wideports_cache)
|
for (auto &wp : wideports_cache)
|
||||||
|
@ -280,7 +286,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
||||||
goto error_with_reason;
|
goto error_with_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
module->rename(lastcell, p);
|
module->rename(lastcell, RTLIL::escape_id(p));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
OBJS += frontends/rpc/rpc_frontend.o
|
|
@ -0,0 +1,595 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 whitequark <whitequark@whitequark.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The reason the -path mode of connect_rpc uses byte-oriented and not message-oriented sockets, even though
|
||||||
|
// it is a message-oriented interface, is that the system can place various limits on the message size, which
|
||||||
|
// are not always transparent or easy to change. Given that generated HDL code get be extremely large, it is
|
||||||
|
// unwise to rely on those limits being large enough, and using byte-oriented sockets is guaranteed to work.
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
extern char **environ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libs/json11/json11.hpp"
|
||||||
|
#include "libs/sha1/sha1.h"
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
static std::wstring str2wstr(const std::string &in) {
|
||||||
|
if(in == "") return L"";
|
||||||
|
std::wstring out;
|
||||||
|
out.resize(MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/NULL, /*cchWideChar=*/0));
|
||||||
|
int written = MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/&out[0], /*cchWideChar=*/(int)out.length());
|
||||||
|
log_assert(written == (int)out.length());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string wstr2str(const std::wstring &in) {
|
||||||
|
if(in == L"") return "";
|
||||||
|
std::string out;
|
||||||
|
out.resize(WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/NULL, /*cbMultiByte=*/0, /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL));
|
||||||
|
int written = WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/&out[0], /*cbMultiByte=*/(int)out.length(), /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL);
|
||||||
|
log_assert(written == (int)out.length());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_last_error_str() {
|
||||||
|
DWORD last_error = GetLastError();
|
||||||
|
LPWSTR out_w;
|
||||||
|
DWORD size_w = FormatMessageW(/*dwFlags=*/FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, /*lpSource=*/NULL, /*dwMessageId=*/last_error, /*dwLanguageId=*/0, /*lpBuffer=*/(LPWSTR)&out_w, /*nSize=*/0, /*Arguments=*/NULL);
|
||||||
|
if (size_w == 0)
|
||||||
|
return std::to_string(last_error);
|
||||||
|
std::string out = wstr2str(std::wstring(out_w, size_w));
|
||||||
|
LocalFree(out_w);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using json11::Json;
|
||||||
|
|
||||||
|
struct RpcServer {
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
RpcServer(const std::string &name) : name(name) { }
|
||||||
|
virtual ~RpcServer() { }
|
||||||
|
|
||||||
|
virtual void write(const std::string &data) = 0;
|
||||||
|
virtual std::string read() = 0;
|
||||||
|
|
||||||
|
Json call(const Json &json_request) {
|
||||||
|
std::string request;
|
||||||
|
json_request.dump(request);
|
||||||
|
request += '\n';
|
||||||
|
log_debug("RPC frontend request: %s", request.c_str());
|
||||||
|
write(request);
|
||||||
|
|
||||||
|
std::string response = read();
|
||||||
|
log_debug("RPC frontend response: %s", response.c_str());
|
||||||
|
std::string error;
|
||||||
|
Json json_response = Json::parse(response, error);
|
||||||
|
if (json_response.is_null())
|
||||||
|
log_cmd_error("parsing JSON failed: %s\n", error.c_str());
|
||||||
|
if (json_response["error"].is_string())
|
||||||
|
log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str());
|
||||||
|
return json_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> get_module_names() {
|
||||||
|
Json response = call(Json::object {
|
||||||
|
{ "method", "modules" },
|
||||||
|
});
|
||||||
|
bool is_valid = true;
|
||||||
|
std::vector<std::string> modules;
|
||||||
|
if (response["modules"].is_array()) {
|
||||||
|
for (auto &json_module : response["modules"].array_items()) {
|
||||||
|
if (json_module.is_string())
|
||||||
|
modules.push_back(json_module.string_value());
|
||||||
|
else is_valid = false;
|
||||||
|
}
|
||||||
|
} else is_valid = false;
|
||||||
|
if (!is_valid)
|
||||||
|
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
|
||||||
|
return modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> derive_module(const std::string &module, const dict<RTLIL::IdString, RTLIL::Const> ¶meters) {
|
||||||
|
Json::object json_parameters;
|
||||||
|
for (auto ¶m : parameters) {
|
||||||
|
std::string type, value;
|
||||||
|
if (param.second.flags & RTLIL::CONST_FLAG_REAL) {
|
||||||
|
type = "real";
|
||||||
|
value = param.second.decode_string();
|
||||||
|
} else if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
|
||||||
|
type = "string";
|
||||||
|
value = param.second.decode_string();
|
||||||
|
} else if ((param.second.flags & ~RTLIL::CONST_FLAG_SIGNED) == RTLIL::CONST_FLAG_NONE) {
|
||||||
|
type = (param.second.flags & RTLIL::CONST_FLAG_SIGNED) ? "signed" : "unsigned";
|
||||||
|
value = param.second.as_string();
|
||||||
|
} else
|
||||||
|
log_cmd_error("Unserializable constant flags 0x%x\n", param.second.flags);
|
||||||
|
json_parameters[param.first.str()] = Json::object {
|
||||||
|
{ "type", type },
|
||||||
|
{ "value", value },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Json response = call(Json::object {
|
||||||
|
{ "method", "derive" },
|
||||||
|
{ "module", module },
|
||||||
|
{ "parameters", json_parameters },
|
||||||
|
});
|
||||||
|
bool is_valid = true;
|
||||||
|
std::string frontend, source;
|
||||||
|
if (response["frontend"].is_string())
|
||||||
|
frontend = response["frontend"].string_value();
|
||||||
|
else is_valid = false;
|
||||||
|
if (response["source"].is_string())
|
||||||
|
source = response["source"].string_value();
|
||||||
|
else is_valid = false;
|
||||||
|
if (!is_valid)
|
||||||
|
log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
|
||||||
|
return std::make_pair(frontend, source);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RpcModule : RTLIL::Module {
|
||||||
|
std::shared_ptr<RpcServer> server;
|
||||||
|
|
||||||
|
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) YS_OVERRIDE {
|
||||||
|
std::string stripped_name = name.str();
|
||||||
|
if (stripped_name.compare(0, 9, "$abstract") == 0)
|
||||||
|
stripped_name = stripped_name.substr(9);
|
||||||
|
log_assert(stripped_name[0] == '\\');
|
||||||
|
|
||||||
|
log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str());
|
||||||
|
|
||||||
|
std::string parameter_info;
|
||||||
|
for (auto ¶m : parameters) {
|
||||||
|
log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
|
||||||
|
parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string derived_name;
|
||||||
|
if (parameters.empty())
|
||||||
|
derived_name = stripped_name;
|
||||||
|
else if (parameter_info.size() > 60)
|
||||||
|
derived_name = "$paramod$" + sha1(parameter_info) + stripped_name;
|
||||||
|
else
|
||||||
|
derived_name = "$paramod" + stripped_name + parameter_info;
|
||||||
|
|
||||||
|
if (design->has(derived_name)) {
|
||||||
|
log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str());
|
||||||
|
} else {
|
||||||
|
std::string command, input;
|
||||||
|
std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters);
|
||||||
|
|
||||||
|
std::istringstream input_stream(input);
|
||||||
|
RTLIL::Design *derived_design = new RTLIL::Design;
|
||||||
|
Frontend::frontend_call(derived_design, &input_stream, "<rpc>" + derived_name.substr(8), command);
|
||||||
|
derived_design->check();
|
||||||
|
|
||||||
|
dict<std::string, std::string> name_mangling;
|
||||||
|
bool found_derived_top = false;
|
||||||
|
for (auto module : derived_design->modules()) {
|
||||||
|
std::string original_name = module->name.str();
|
||||||
|
if (original_name == stripped_name) {
|
||||||
|
found_derived_top = true;
|
||||||
|
name_mangling[original_name] = derived_name;
|
||||||
|
} else {
|
||||||
|
name_mangling[original_name] = derived_name + module->name.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found_derived_top)
|
||||||
|
log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str());
|
||||||
|
|
||||||
|
for (auto module : derived_design->modules())
|
||||||
|
for (auto cell : module->cells())
|
||||||
|
if (name_mangling.count(cell->type.str()))
|
||||||
|
cell->type = name_mangling[cell->type.str()];
|
||||||
|
|
||||||
|
for (auto module : derived_design->modules_) {
|
||||||
|
std::string mangled_name = name_mangling[module.first.str()];
|
||||||
|
|
||||||
|
log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name));
|
||||||
|
|
||||||
|
module.second->name = mangled_name;
|
||||||
|
module.second->design = design;
|
||||||
|
module.second->attributes.erase("\\top");
|
||||||
|
design->modules_[mangled_name] = module.second;
|
||||||
|
derived_design->modules_.erase(module.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete derived_design;
|
||||||
|
}
|
||||||
|
|
||||||
|
return derived_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTLIL::Module *clone() const YS_OVERRIDE {
|
||||||
|
RpcModule *new_mod = new RpcModule;
|
||||||
|
new_mod->server = server;
|
||||||
|
cloneInto(new_mod);
|
||||||
|
return new_mod;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <BaseTsd.h>
|
||||||
|
typedef SSIZE_T ssize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct HandleRpcServer : RpcServer {
|
||||||
|
HANDLE hsend, hrecv;
|
||||||
|
|
||||||
|
HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv)
|
||||||
|
: RpcServer(name), hsend(hsend), hrecv(hrecv) { }
|
||||||
|
|
||||||
|
void write(const std::string &data) YS_OVERRIDE {
|
||||||
|
log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
|
||||||
|
ssize_t offset = 0;
|
||||||
|
do {
|
||||||
|
DWORD data_written;
|
||||||
|
if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL))
|
||||||
|
log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str());
|
||||||
|
offset += data_written;
|
||||||
|
} while(offset < (ssize_t)data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read() YS_OVERRIDE {
|
||||||
|
std::string data;
|
||||||
|
ssize_t offset = 0;
|
||||||
|
while (data.length() == 0 || data[data.length() - 1] != '\n') {
|
||||||
|
data.resize(data.length() + 1024);
|
||||||
|
DWORD data_read;
|
||||||
|
if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL))
|
||||||
|
log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str());
|
||||||
|
offset += data_read;
|
||||||
|
data.resize(offset);
|
||||||
|
size_t term_pos = data.find('\n', offset);
|
||||||
|
if (term_pos != data.length() - 1 && term_pos != std::string::npos)
|
||||||
|
log_cmd_error("read failed: more than one response\n");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
~HandleRpcServer() {
|
||||||
|
CloseHandle(hsend);
|
||||||
|
if (hrecv != hsend)
|
||||||
|
CloseHandle(hrecv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct FdRpcServer : RpcServer {
|
||||||
|
int fdsend, fdrecv;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1)
|
||||||
|
: RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { }
|
||||||
|
|
||||||
|
void check_pid() {
|
||||||
|
if (pid == -1) return;
|
||||||
|
// If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE.
|
||||||
|
pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
|
||||||
|
if (wait_result == -1)
|
||||||
|
log_cmd_error("waitpid failed: %s\n", strerror(errno));
|
||||||
|
if (wait_result == pid)
|
||||||
|
log_cmd_error("RPC frontend terminated unexpectedly\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const std::string &data) YS_OVERRIDE {
|
||||||
|
log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
|
||||||
|
ssize_t offset = 0;
|
||||||
|
do {
|
||||||
|
check_pid();
|
||||||
|
ssize_t result = ::write(fdsend, &data[offset], data.length() - offset);
|
||||||
|
if (result == -1)
|
||||||
|
log_cmd_error("write failed: %s\n", strerror(errno));
|
||||||
|
offset += result;
|
||||||
|
} while(offset < (ssize_t)data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read() YS_OVERRIDE {
|
||||||
|
std::string data;
|
||||||
|
ssize_t offset = 0;
|
||||||
|
while (data.length() == 0 || data[data.length() - 1] != '\n') {
|
||||||
|
data.resize(data.length() + 1024);
|
||||||
|
check_pid();
|
||||||
|
ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset);
|
||||||
|
if (result == -1)
|
||||||
|
log_cmd_error("read failed: %s\n", strerror(errno));
|
||||||
|
offset += result;
|
||||||
|
data.resize(offset);
|
||||||
|
size_t term_pos = data.find('\n', offset);
|
||||||
|
if (term_pos != data.length() - 1 && term_pos != std::string::npos)
|
||||||
|
log_cmd_error("read failed: more than one response\n");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
~FdRpcServer() {
|
||||||
|
close(fdsend);
|
||||||
|
if (fdrecv != fdsend)
|
||||||
|
close(fdrecv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// RpcFrontend does not inherit from Frontend since it does not read files.
|
||||||
|
struct RpcFrontend : public Pass {
|
||||||
|
RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" connect_rpc -exec <command> [args...]\n");
|
||||||
|
log(" connect_rpc -path <path>\n");
|
||||||
|
log("\n");
|
||||||
|
log("Load modules using an out-of-process frontend.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -exec <command> [args...]\n");
|
||||||
|
log(" run <command> with arguments [args...]. send requests on stdin, read\n");
|
||||||
|
log(" responses from stdout.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -path <path>\n");
|
||||||
|
log(" connect to Unix domain socket at <path>. (Unix)\n");
|
||||||
|
log(" connect to bidirectional byte-type named pipe at <path>. (Windows)\n");
|
||||||
|
log("\n");
|
||||||
|
log("A simple JSON-based, newline-delimited protocol is used for communicating with\n");
|
||||||
|
log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n");
|
||||||
|
log("of JSON. Frontend responds with data or error message by replying with exactly\n");
|
||||||
|
log("1 line of JSON as well.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -> {\"method\": \"modules\"}\n");
|
||||||
|
log(" <- {\"modules\": [\"<module-name>\", ...]}\n");
|
||||||
|
log(" <- {\"error\": \"<error-message>\"}\n");
|
||||||
|
log(" request for the list of modules that can be derived by this frontend.\n");
|
||||||
|
log(" the 'hierarchy' command will call back into this frontend if a cell\n");
|
||||||
|
log(" with type <module-name> is instantiated in the design.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -> {\"method\": \"derive\", \"module\": \"<module-name\">, \"parameters\": {\n");
|
||||||
|
log(" \"<param-name>\": {\"type\": \"[unsigned|signed|string|real]\",\n");
|
||||||
|
log(" \"value\": \"<param-value>\"}, ...}}\n");
|
||||||
|
log(" <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"<source>\"}}\n");
|
||||||
|
log(" <- {\"error\": \"<error-message>\"}\n");
|
||||||
|
log(" request for the module <module-name> to be derived for a specific set of\n");
|
||||||
|
log(" parameters. <param-name> starts with \\ for named parameters, and with $\n");
|
||||||
|
log(" for unnamed parameters, which are numbered starting at 1.<param-value>\n");
|
||||||
|
log(" for integer parameters is always specified as a binary string of unlimited\n");
|
||||||
|
log(" precision. the <source> returned by the frontend is hygienically parsed\n");
|
||||||
|
log(" by a built-in Yosys <frontend>, allowing the RPC frontend to return any\n");
|
||||||
|
log(" convenient representation of the module. the derived module is cached,\n");
|
||||||
|
log(" so the response should be the same whenever the same set of parameters\n");
|
||||||
|
log(" is provided.\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Connecting to RPC frontend.\n");
|
||||||
|
|
||||||
|
std::vector<std::string> command;
|
||||||
|
std::string path;
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
std::string arg = args[argidx];
|
||||||
|
if (arg == "-exec" && argidx+1 < args.size()) {
|
||||||
|
command.insert(command.begin(), args.begin() + argidx + 1, args.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-path" && argidx+1 < args.size()) {
|
||||||
|
path = args[argidx+1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
if ((!command.empty()) + (!path.empty()) != 1)
|
||||||
|
log_cmd_error("Exactly one of -exec, -unix must be specified.\n");
|
||||||
|
|
||||||
|
std::shared_ptr<RpcServer> server;
|
||||||
|
if (!command.empty()) {
|
||||||
|
std::string command_line;
|
||||||
|
bool first = true;
|
||||||
|
for (auto &arg : command) {
|
||||||
|
if (!first) command_line += ' ';
|
||||||
|
command_line += arg;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring command_w = str2wstr(command[0]);
|
||||||
|
std::wstring command_path_w;
|
||||||
|
std::wstring command_line_w = str2wstr(command_line);
|
||||||
|
DWORD command_path_len_w;
|
||||||
|
SECURITY_ATTRIBUTES pipe_attr = {};
|
||||||
|
HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL;
|
||||||
|
STARTUPINFOW startup_info = {};
|
||||||
|
PROCESS_INFORMATION proc_info = {};
|
||||||
|
|
||||||
|
command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL);
|
||||||
|
if (command_path_len_w == 0) {
|
||||||
|
log_error("SearchPathW failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
command_path_w.resize(command_path_len_w - 1);
|
||||||
|
command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL);
|
||||||
|
log_assert(command_path_len_w == command_path_w.length());
|
||||||
|
|
||||||
|
pipe_attr.nLength = sizeof(pipe_attr);
|
||||||
|
pipe_attr.bInheritHandle = TRUE;
|
||||||
|
pipe_attr.lpSecurityDescriptor = NULL;
|
||||||
|
if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) {
|
||||||
|
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) {
|
||||||
|
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
startup_info.hStdInput = send_r;
|
||||||
|
startup_info.hStdOutput = recv_w;
|
||||||
|
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) {
|
||||||
|
log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
CloseHandle(proc_info.hProcess);
|
||||||
|
CloseHandle(proc_info.hThread);
|
||||||
|
|
||||||
|
server = std::make_shared<HandleRpcServer>(path, send_w, recv_r);
|
||||||
|
send_w = NULL;
|
||||||
|
recv_r = NULL;
|
||||||
|
|
||||||
|
cleanup_exec:
|
||||||
|
if (send_r != NULL) CloseHandle(send_r);
|
||||||
|
if (send_w != NULL) CloseHandle(send_w);
|
||||||
|
if (recv_r != NULL) CloseHandle(recv_r);
|
||||||
|
if (recv_w != NULL) CloseHandle(recv_w);
|
||||||
|
#else
|
||||||
|
std::vector<char *> argv;
|
||||||
|
int send[2] = {-1,-1}, recv[2] = {-1,-1};
|
||||||
|
posix_spawn_file_actions_t file_actions, *file_actions_p = NULL;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
for (auto &arg : command)
|
||||||
|
argv.push_back(&arg[0]);
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
|
||||||
|
if (pipe(send) != 0) {
|
||||||
|
log_error("pipe failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (pipe(recv) != 0) {
|
||||||
|
log_error("pipe failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posix_spawn_file_actions_init(&file_actions) != 0) {
|
||||||
|
log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
file_actions_p = &file_actions;
|
||||||
|
if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) {
|
||||||
|
log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) {
|
||||||
|
log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) {
|
||||||
|
log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) {
|
||||||
|
log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) {
|
||||||
|
log_error("posix_spawnp failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = std::make_shared<FdRpcServer>(command_line, send[1], recv[0], pid);
|
||||||
|
send[1] = -1;
|
||||||
|
recv[0] = -1;
|
||||||
|
|
||||||
|
cleanup_exec:
|
||||||
|
if (send[0] != -1) close(send[0]);
|
||||||
|
if (send[1] != -1) close(send[1]);
|
||||||
|
if (recv[0] != -1) close(recv[0]);
|
||||||
|
if (recv[1] != -1) close(recv[1]);
|
||||||
|
if (file_actions_p != NULL)
|
||||||
|
posix_spawn_file_actions_destroy(file_actions_p);
|
||||||
|
#endif
|
||||||
|
} else if (!path.empty()) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring path_w = str2wstr(path);
|
||||||
|
HANDLE h;
|
||||||
|
|
||||||
|
h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL);
|
||||||
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
|
log_error("CreateFileW failed: %s\n", get_last_error_str().c_str());
|
||||||
|
goto cleanup_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = std::make_shared<HandleRpcServer>(path, h, h);
|
||||||
|
|
||||||
|
cleanup_path:
|
||||||
|
;
|
||||||
|
#else
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
log_error("socket failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||||
|
log_error("connect failed: %s\n", strerror(errno));
|
||||||
|
goto cleanup_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = std::make_shared<FdRpcServer>(path, fd, fd);
|
||||||
|
fd = -1;
|
||||||
|
|
||||||
|
cleanup_path:
|
||||||
|
if (fd != -1) close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server)
|
||||||
|
log_cmd_error("Failed to connect to RPC frontend.\n");
|
||||||
|
|
||||||
|
for (auto &module_name : server->get_module_names()) {
|
||||||
|
log("Linking module `%s'.\n", module_name.c_str());
|
||||||
|
RpcModule *module = new RpcModule;
|
||||||
|
module->name = "$abstract\\" + module_name;
|
||||||
|
module->server = server;
|
||||||
|
design->add(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} RpcFrontend;
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
|
@ -130,7 +130,7 @@ RTLIL::SigBit VerificImporter::net_map_at(Net *net)
|
||||||
|
|
||||||
bool is_blackbox(Netlist *nl)
|
bool is_blackbox(Netlist *nl)
|
||||||
{
|
{
|
||||||
if (nl->IsBlackBox())
|
if (nl->IsBlackBox() || nl->IsEmptyBox())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const char *attr = nl->GetAttValue("blackbox");
|
const char *attr = nl->GetAttValue("blackbox");
|
||||||
|
@ -784,10 +784,21 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
|
||||||
merge_past_ffs_clock(it.second, it.first.first, it.first.second);
|
merge_past_ffs_clock(it.second, it.first.first, it.first.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo)
|
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool norename)
|
||||||
{
|
{
|
||||||
std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
|
std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
|
||||||
std::string module_name = nl->IsOperator() ? "$verific$" + netlist_name : RTLIL::escape_id(netlist_name);
|
std::string module_name = netlist_name;
|
||||||
|
|
||||||
|
if (nl->IsOperator() || nl->IsPrimitive()) {
|
||||||
|
module_name = "$verific$" + module_name;
|
||||||
|
} else {
|
||||||
|
if (!norename && *nl->Name()) {
|
||||||
|
module_name += "(";
|
||||||
|
module_name += nl->Name();
|
||||||
|
module_name += ")";
|
||||||
|
}
|
||||||
|
module_name = "\\" + module_name;
|
||||||
|
}
|
||||||
|
|
||||||
netlist = nl;
|
netlist = nl;
|
||||||
|
|
||||||
|
@ -1256,7 +1267,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
|
||||||
if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT)
|
if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT)
|
||||||
sva_asserts.insert(inst);
|
sva_asserts.insert(inst);
|
||||||
|
|
||||||
if (inst->Type() == PRIM_SVA_ASSUME || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME)
|
if (inst->Type() == PRIM_SVA_ASSUME || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME || inst->Type() == PRIM_SVA_RESTRICT)
|
||||||
sva_assumes.insert(inst);
|
sva_assumes.insert(inst);
|
||||||
|
|
||||||
if (inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_COVER)
|
if (inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_COVER)
|
||||||
|
@ -1396,8 +1407,20 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
|
||||||
import_verific_cells:
|
import_verific_cells:
|
||||||
nl_todo.insert(inst->View());
|
nl_todo.insert(inst->View());
|
||||||
|
|
||||||
RTLIL::Cell *cell = module->addCell(inst_name, inst->IsOperator() ?
|
std::string inst_type = inst->View()->Owner()->Name();
|
||||||
std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name()));
|
|
||||||
|
if (inst->View()->IsOperator() || inst->View()->IsPrimitive()) {
|
||||||
|
inst_type = "$verific$" + inst_type;
|
||||||
|
} else {
|
||||||
|
if (*inst->View()->Name()) {
|
||||||
|
inst_type += "(";
|
||||||
|
inst_type += inst->View()->Name();
|
||||||
|
inst_type += ")";
|
||||||
|
}
|
||||||
|
inst_type = "\\" + inst_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTLIL::Cell *cell = module->addCell(inst_name, inst_type);
|
||||||
|
|
||||||
if (inst->IsPrimitive() && mode_keep)
|
if (inst->IsPrimitive() && mode_keep)
|
||||||
cell->attributes["\\keep"] = 1;
|
cell->attributes["\\keep"] = 1;
|
||||||
|
@ -1876,7 +1899,7 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
|
||||||
Netlist *nl = *nl_todo.begin();
|
Netlist *nl = *nl_todo.begin();
|
||||||
if (nl_done.count(nl) == 0) {
|
if (nl_done.count(nl) == 0) {
|
||||||
VerificImporter importer(false, false, false, false, false, false, false);
|
VerificImporter importer(false, false, false, false, false, false, false);
|
||||||
importer.import_netlist(design, nl, nl_todo);
|
importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == top);
|
||||||
}
|
}
|
||||||
nl_todo.erase(nl);
|
nl_todo.erase(nl);
|
||||||
nl_done.insert(nl);
|
nl_done.insert(nl);
|
||||||
|
@ -1939,12 +1962,18 @@ struct VerificPass : public Pass {
|
||||||
log("Load the specified VHDL files into Verific.\n");
|
log("Load the specified VHDL files into Verific.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" verific -work <libname> {-sv|-vhdl|...} <hdl-file>\n");
|
log(" verific [-work <libname>] {-sv|-vhdl|...} <hdl-file>\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n");
|
log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n");
|
||||||
log("(default library when -work is not present: \"work\")\n");
|
log("(default library when -work is not present: \"work\")\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" verific [-L <libname>] {-sv|-vhdl|...} <hdl-file>\n");
|
||||||
|
log("\n");
|
||||||
|
log("Look up external definitions in the specified library.\n");
|
||||||
|
log("(-L may be used more than once)\n");
|
||||||
|
log("\n");
|
||||||
|
log("\n");
|
||||||
log(" verific -vlog-incdir <directory>..\n");
|
log(" verific -vlog-incdir <directory>..\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Add Verilog include directories.\n");
|
log("Add Verilog include directories.\n");
|
||||||
|
@ -2158,12 +2187,17 @@ struct VerificPass : public Pass {
|
||||||
goto check_error;
|
goto check_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
veri_file::RemoveAllLOptions();
|
||||||
for (; argidx < GetSize(args); argidx++)
|
for (; argidx < GetSize(args); argidx++)
|
||||||
{
|
{
|
||||||
if (args[argidx] == "-work" && argidx+1 < GetSize(args)) {
|
if (args[argidx] == "-work" && argidx+1 < GetSize(args)) {
|
||||||
work = args[++argidx];
|
work = args[++argidx];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-L" && argidx+1 < GetSize(args)) {
|
||||||
|
veri_file::AddLOption(args[++argidx].c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2339,6 +2373,8 @@ struct VerificPass : public Pass {
|
||||||
if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0)
|
if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0)
|
||||||
cmd_error(args, argidx, "unknown option");
|
cmd_error(args, argidx, "unknown option");
|
||||||
|
|
||||||
|
std::set<std::string> top_mod_names;
|
||||||
|
|
||||||
if (mode_all)
|
if (mode_all)
|
||||||
{
|
{
|
||||||
log("Running hier_tree::ElaborateAll().\n");
|
log("Running hier_tree::ElaborateAll().\n");
|
||||||
|
@ -2367,6 +2403,7 @@ struct VerificPass : public Pass {
|
||||||
for (; argidx < GetSize(args); argidx++)
|
for (; argidx < GetSize(args); argidx++)
|
||||||
{
|
{
|
||||||
const char *name = args[argidx].c_str();
|
const char *name = args[argidx].c_str();
|
||||||
|
top_mod_names.insert(name);
|
||||||
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
|
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
|
||||||
|
|
||||||
if (veri_lib) {
|
if (veri_lib) {
|
||||||
|
@ -2432,7 +2469,7 @@ struct VerificPass : public Pass {
|
||||||
if (nl_done.count(nl) == 0) {
|
if (nl_done.count(nl) == 0) {
|
||||||
VerificImporter importer(mode_gates, mode_keep, mode_nosva,
|
VerificImporter importer(mode_gates, mode_keep, mode_nosva,
|
||||||
mode_names, mode_verific, mode_autocover, mode_fullinit);
|
mode_names, mode_verific, mode_autocover, mode_fullinit);
|
||||||
importer.import_netlist(design, nl, nl_todo);
|
importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->Owner()->Name()));
|
||||||
}
|
}
|
||||||
nl_todo.erase(nl);
|
nl_todo.erase(nl);
|
||||||
nl_done.insert(nl);
|
nl_done.insert(nl);
|
||||||
|
|
|
@ -93,7 +93,7 @@ struct VerificImporter
|
||||||
void merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol);
|
void merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol);
|
||||||
void merge_past_ffs(pool<RTLIL::Cell*> &candidates);
|
void merge_past_ffs(pool<RTLIL::Cell*> &candidates);
|
||||||
|
|
||||||
void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo);
|
void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo, bool norename = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst);
|
void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst);
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
// basic_property:
|
// basic_property:
|
||||||
// sequence
|
// sequence
|
||||||
// not basic_property
|
// not basic_property
|
||||||
|
// nexttime basic_property
|
||||||
|
// nexttime[N] basic_property
|
||||||
// sequence #-# basic_property
|
// sequence #-# basic_property
|
||||||
// sequence #=# basic_property
|
// sequence #=# basic_property
|
||||||
// basic_property or basic_property (cover only)
|
// basic_property or basic_property (cover only)
|
||||||
|
@ -1264,6 +1266,26 @@ struct VerificSvaImporter
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inst->Type() == PRIM_SVA_NEXTTIME || inst->Type() == PRIM_SVA_S_NEXTTIME)
|
||||||
|
{
|
||||||
|
const char *sva_low_s = inst->GetAttValue("sva:low");
|
||||||
|
const char *sva_high_s = inst->GetAttValue("sva:high");
|
||||||
|
|
||||||
|
int sva_low = atoi(sva_low_s);
|
||||||
|
int sva_high = atoi(sva_high_s);
|
||||||
|
log_assert(sva_low == sva_high);
|
||||||
|
|
||||||
|
int node = start_node;
|
||||||
|
|
||||||
|
for (int i = 0; i < sva_low; i++) {
|
||||||
|
int next_node = fsm.createNode();
|
||||||
|
fsm.createEdge(node, next_node);
|
||||||
|
node = next_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_sequence(fsm, node, inst->GetInput());
|
||||||
|
}
|
||||||
|
|
||||||
if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
|
if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
|
||||||
{
|
{
|
||||||
const char *sva_low_s = inst->GetAttValue("sva:low");
|
const char *sva_low_s = inst->GetAttValue("sva:low");
|
||||||
|
@ -1590,15 +1612,25 @@ struct VerificSvaImporter
|
||||||
Instance *consequent_inst = net_to_ast_driver(consequent_net);
|
Instance *consequent_inst = net_to_ast_driver(consequent_net);
|
||||||
|
|
||||||
if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL ||
|
if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL ||
|
||||||
consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH))
|
consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH ||
|
||||||
|
consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS))
|
||||||
{
|
{
|
||||||
bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH;
|
bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH;
|
||||||
|
|
||||||
Net *until_net = consequent_inst->GetInput2();
|
Net *until_net = nullptr;
|
||||||
|
if (consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS)
|
||||||
|
{
|
||||||
|
consequent_net = consequent_inst->GetInput();
|
||||||
|
consequent_inst = net_to_ast_driver(consequent_net);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
until_net = consequent_inst->GetInput2();
|
||||||
consequent_net = consequent_inst->GetInput1();
|
consequent_net = consequent_inst->GetInput1();
|
||||||
consequent_inst = net_to_ast_driver(consequent_net);
|
consequent_inst = net_to_ast_driver(consequent_net);
|
||||||
|
}
|
||||||
|
|
||||||
SigBit until_sig = parse_expression(until_net);
|
SigBit until_sig = until_net ? parse_expression(until_net) : RTLIL::S0;
|
||||||
SigBit not_until_sig = module->Not(NEW_ID, until_sig);
|
SigBit not_until_sig = module->Not(NEW_ID, until_sig);
|
||||||
antecedent_fsm.createEdge(node, node, not_until_sig);
|
antecedent_fsm.createEdge(node, node, not_until_sig);
|
||||||
|
|
||||||
|
|
|
@ -490,6 +490,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
|
||||||
}
|
}
|
||||||
while (newline_count-- > 0)
|
while (newline_count-- > 0)
|
||||||
return_char('\n');
|
return_char('\n');
|
||||||
|
if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) {
|
||||||
// printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
|
// printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str());
|
||||||
defines_map[name] = value;
|
defines_map[name] = value;
|
||||||
if (state == 2)
|
if (state == 2)
|
||||||
|
@ -497,6 +498,9 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
|
||||||
else
|
else
|
||||||
defines_with_args.erase(name);
|
defines_with_args.erase(name);
|
||||||
global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2);
|
global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2);
|
||||||
|
} else {
|
||||||
|
log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str());
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -553,6 +553,12 @@ struct VerilogDefines : public Pass {
|
||||||
log(" -Uname[=definition]\n");
|
log(" -Uname[=definition]\n");
|
||||||
log(" undefine the preprocessor symbol 'name'\n");
|
log(" undefine the preprocessor symbol 'name'\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -reset\n");
|
||||||
|
log(" clear list of defined preprocessor symbols\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -list\n");
|
||||||
|
log(" list currently defined preprocessor symbols\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -588,6 +594,16 @@ struct VerilogDefines : public Pass {
|
||||||
design->verilog_defines.erase(name);
|
design->verilog_defines.erase(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-reset") {
|
||||||
|
design->verilog_defines.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-list") {
|
||||||
|
for (auto &it : design->verilog_defines) {
|
||||||
|
log("`define %s%s %s\n", it.first.c_str(), it.second.second ? "()" : "", it.second.first.c_str());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,9 +188,9 @@ YOSYS_NAMESPACE_END
|
||||||
"unique0" { SV_KEYWORD(TOK_UNIQUE); }
|
"unique0" { SV_KEYWORD(TOK_UNIQUE); }
|
||||||
"priority" { SV_KEYWORD(TOK_PRIORITY); }
|
"priority" { SV_KEYWORD(TOK_PRIORITY); }
|
||||||
|
|
||||||
"always_comb" { SV_KEYWORD(TOK_ALWAYS); }
|
"always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); }
|
||||||
"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
|
"always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); }
|
||||||
"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
|
"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); }
|
||||||
|
|
||||||
/* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
|
/* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
|
||||||
to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
|
to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
|
||||||
|
|
|
@ -141,6 +141,7 @@ struct specify_rise_fall {
|
||||||
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
|
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
|
||||||
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
|
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
|
||||||
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
|
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
|
||||||
|
%token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH
|
||||||
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
|
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
|
||||||
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
|
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
|
||||||
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
|
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
|
||||||
|
@ -155,8 +156,8 @@ struct specify_rise_fall {
|
||||||
|
|
||||||
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
|
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
|
||||||
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
|
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
|
||||||
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id
|
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id
|
||||||
%type <boolean> opt_signed opt_property unique_case_attr
|
%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
|
||||||
%type <al> attr case_attr
|
%type <al> attr case_attr
|
||||||
|
|
||||||
%type <specify_target_ptr> specify_target
|
%type <specify_target_ptr> specify_target
|
||||||
|
@ -206,6 +207,7 @@ design:
|
||||||
task_func_decl design |
|
task_func_decl design |
|
||||||
param_decl design |
|
param_decl design |
|
||||||
localparam_decl design |
|
localparam_decl design |
|
||||||
|
typedef_decl design |
|
||||||
package design |
|
package design |
|
||||||
interface design |
|
interface design |
|
||||||
/* empty */;
|
/* empty */;
|
||||||
|
@ -290,6 +292,9 @@ hierarchical_id:
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hierarchical_type_id:
|
||||||
|
'(' hierarchical_id ')' { $$ = $2; };
|
||||||
|
|
||||||
module:
|
module:
|
||||||
attr TOK_MODULE TOK_ID {
|
attr TOK_MODULE TOK_ID {
|
||||||
do_not_require_port_stubs = false;
|
do_not_require_port_stubs = false;
|
||||||
|
@ -324,13 +329,13 @@ single_module_para:
|
||||||
astbuf1 = new AstNode(AST_PARAMETER);
|
astbuf1 = new AstNode(AST_PARAMETER);
|
||||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
append_attr(astbuf1, $1);
|
append_attr(astbuf1, $1);
|
||||||
} param_signed param_integer param_range single_param_decl |
|
} param_type single_param_decl |
|
||||||
attr TOK_LOCALPARAM {
|
attr TOK_LOCALPARAM {
|
||||||
if (astbuf1) delete astbuf1;
|
if (astbuf1) delete astbuf1;
|
||||||
astbuf1 = new AstNode(AST_LOCALPARAM);
|
astbuf1 = new AstNode(AST_LOCALPARAM);
|
||||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
append_attr(astbuf1, $1);
|
append_attr(astbuf1, $1);
|
||||||
} param_signed param_integer param_range single_param_decl |
|
} param_type single_param_decl |
|
||||||
single_param_decl;
|
single_param_decl;
|
||||||
|
|
||||||
module_args_opt:
|
module_args_opt:
|
||||||
|
@ -426,6 +431,7 @@ package_body:
|
||||||
package_body package_body_stmt |;
|
package_body package_body_stmt |;
|
||||||
|
|
||||||
package_body_stmt:
|
package_body_stmt:
|
||||||
|
typedef_decl |
|
||||||
localparam_decl;
|
localparam_decl;
|
||||||
|
|
||||||
interface:
|
interface:
|
||||||
|
@ -452,7 +458,7 @@ interface_body:
|
||||||
interface_body interface_body_stmt |;
|
interface_body interface_body_stmt |;
|
||||||
|
|
||||||
interface_body_stmt:
|
interface_body_stmt:
|
||||||
param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
|
param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
|
||||||
modport_stmt;
|
modport_stmt;
|
||||||
|
|
||||||
non_opt_delay:
|
non_opt_delay:
|
||||||
|
@ -475,8 +481,14 @@ wire_type:
|
||||||
};
|
};
|
||||||
|
|
||||||
wire_type_token_list:
|
wire_type_token_list:
|
||||||
wire_type_token | wire_type_token_list wire_type_token |
|
wire_type_token |
|
||||||
wire_type_token_io ;
|
wire_type_token_list wire_type_token |
|
||||||
|
wire_type_token_io |
|
||||||
|
hierarchical_type_id {
|
||||||
|
astbuf3->is_custom_type = true;
|
||||||
|
astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
|
||||||
|
astbuf3->children.back()->str = *$1;
|
||||||
|
};
|
||||||
|
|
||||||
wire_type_token_io:
|
wire_type_token_io:
|
||||||
TOK_INPUT {
|
TOK_INPUT {
|
||||||
|
@ -591,7 +603,7 @@ module_body:
|
||||||
/* empty */;
|
/* empty */;
|
||||||
|
|
||||||
module_body_stmt:
|
module_body_stmt:
|
||||||
task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
|
task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
|
||||||
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
||||||
|
|
||||||
checker_decl:
|
checker_decl:
|
||||||
|
@ -1149,12 +1161,20 @@ param_range:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
param_type:
|
||||||
|
param_signed param_integer param_real param_range |
|
||||||
|
hierarchical_type_id {
|
||||||
|
astbuf1->is_custom_type = true;
|
||||||
|
astbuf1->children.push_back(new AstNode(AST_WIRETYPE));
|
||||||
|
astbuf1->children.back()->str = *$1;
|
||||||
|
};
|
||||||
|
|
||||||
param_decl:
|
param_decl:
|
||||||
attr TOK_PARAMETER {
|
attr TOK_PARAMETER {
|
||||||
astbuf1 = new AstNode(AST_PARAMETER);
|
astbuf1 = new AstNode(AST_PARAMETER);
|
||||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
append_attr(astbuf1, $1);
|
append_attr(astbuf1, $1);
|
||||||
} param_signed param_integer param_real param_range param_decl_list ';' {
|
} param_type param_decl_list ';' {
|
||||||
delete astbuf1;
|
delete astbuf1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1163,7 +1183,7 @@ localparam_decl:
|
||||||
astbuf1 = new AstNode(AST_LOCALPARAM);
|
astbuf1 = new AstNode(AST_LOCALPARAM);
|
||||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
append_attr(astbuf1, $1);
|
append_attr(astbuf1, $1);
|
||||||
} param_signed param_integer param_real param_range param_decl_list ';' {
|
} param_type param_decl_list ';' {
|
||||||
delete astbuf1;
|
delete astbuf1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1327,7 +1347,7 @@ wire_name:
|
||||||
if ($2 != NULL) {
|
if ($2 != NULL) {
|
||||||
if (node->is_input || node->is_output)
|
if (node->is_input || node->is_output)
|
||||||
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
|
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
|
||||||
if (!astbuf2) {
|
if (!astbuf2 && !node->is_custom_type) {
|
||||||
AstNode *rng = new AstNode(AST_RANGE);
|
AstNode *rng = new AstNode(AST_RANGE);
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
rng->children.push_back(AstNode::mkconst_int(0, true));
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
|
@ -1377,6 +1397,45 @@ assign_expr:
|
||||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
|
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef_decl:
|
||||||
|
TOK_TYPEDEF wire_type range TOK_ID range_or_multirange ';' {
|
||||||
|
astbuf1 = $2;
|
||||||
|
astbuf2 = $3;
|
||||||
|
if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
|
||||||
|
if (astbuf2) {
|
||||||
|
frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
|
||||||
|
} else {
|
||||||
|
astbuf2 = new AstNode(AST_RANGE);
|
||||||
|
astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
|
||||||
|
astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (astbuf2 && astbuf2->children.size() != 2)
|
||||||
|
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
|
||||||
|
if (astbuf2)
|
||||||
|
astbuf1->children.push_back(astbuf2);
|
||||||
|
|
||||||
|
if ($5 != NULL) {
|
||||||
|
if (!astbuf2) {
|
||||||
|
AstNode *rng = new AstNode(AST_RANGE);
|
||||||
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
|
rng->children.push_back(AstNode::mkconst_int(0, true));
|
||||||
|
astbuf1->children.push_back(rng);
|
||||||
|
}
|
||||||
|
astbuf1->type = AST_MEMORY;
|
||||||
|
auto *rangeNode = $5;
|
||||||
|
if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
|
||||||
|
// SV array size [n], rewrite as [n-1:0]
|
||||||
|
rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
|
||||||
|
rangeNode->children.push_back(AstNode::mkconst_int(0, false));
|
||||||
|
}
|
||||||
|
astbuf1->children.push_back(rangeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1));
|
||||||
|
ast_stack.back()->children.back()->str = *$4;
|
||||||
|
};
|
||||||
|
|
||||||
cell_stmt:
|
cell_stmt:
|
||||||
attr TOK_ID {
|
attr TOK_ID {
|
||||||
astbuf1 = new AstNode(AST_CELL);
|
astbuf1 = new AstNode(AST_CELL);
|
||||||
|
@ -1523,10 +1582,28 @@ cell_port:
|
||||||
free_attr($1);
|
free_attr($1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
always_comb_or_latch:
|
||||||
|
TOK_ALWAYS_COMB {
|
||||||
|
$$ = false;
|
||||||
|
} |
|
||||||
|
TOK_ALWAYS_LATCH {
|
||||||
|
$$ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
always_or_always_ff:
|
||||||
|
TOK_ALWAYS {
|
||||||
|
$$ = false;
|
||||||
|
} |
|
||||||
|
TOK_ALWAYS_FF {
|
||||||
|
$$ = true;
|
||||||
|
};
|
||||||
|
|
||||||
always_stmt:
|
always_stmt:
|
||||||
attr TOK_ALWAYS {
|
attr always_or_always_ff {
|
||||||
AstNode *node = new AstNode(AST_ALWAYS);
|
AstNode *node = new AstNode(AST_ALWAYS);
|
||||||
append_attr(node, $1);
|
append_attr(node, $1);
|
||||||
|
if ($2)
|
||||||
|
node->attributes[ID(always_ff)] = AstNode::mkconst_int(1, false);
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
ast_stack.push_back(node);
|
ast_stack.push_back(node);
|
||||||
} always_cond {
|
} always_cond {
|
||||||
|
@ -1537,6 +1614,22 @@ always_stmt:
|
||||||
ast_stack.pop_back();
|
ast_stack.pop_back();
|
||||||
ast_stack.pop_back();
|
ast_stack.pop_back();
|
||||||
} |
|
} |
|
||||||
|
attr always_comb_or_latch {
|
||||||
|
AstNode *node = new AstNode(AST_ALWAYS);
|
||||||
|
append_attr(node, $1);
|
||||||
|
if ($2)
|
||||||
|
node->attributes[ID(always_latch)] = AstNode::mkconst_int(1, false);
|
||||||
|
else
|
||||||
|
node->attributes[ID(always_comb)] = AstNode::mkconst_int(1, false);
|
||||||
|
ast_stack.back()->children.push_back(node);
|
||||||
|
ast_stack.push_back(node);
|
||||||
|
AstNode *block = new AstNode(AST_BLOCK);
|
||||||
|
ast_stack.back()->children.push_back(block);
|
||||||
|
ast_stack.push_back(block);
|
||||||
|
} behavioral_stmt {
|
||||||
|
ast_stack.pop_back();
|
||||||
|
ast_stack.pop_back();
|
||||||
|
} |
|
||||||
attr TOK_INITIAL {
|
attr TOK_INITIAL {
|
||||||
AstNode *node = new AstNode(AST_INITIAL);
|
AstNode *node = new AstNode(AST_INITIAL);
|
||||||
append_attr(node, $1);
|
append_attr(node, $1);
|
||||||
|
@ -1823,7 +1916,7 @@ simple_behavioral_stmt:
|
||||||
|
|
||||||
// this production creates the obligatory if-else shift/reduce conflict
|
// this production creates the obligatory if-else shift/reduce conflict
|
||||||
behavioral_stmt:
|
behavioral_stmt:
|
||||||
defattr | assert | wire_decl | param_decl | localparam_decl |
|
defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl |
|
||||||
non_opt_delay behavioral_stmt |
|
non_opt_delay behavioral_stmt |
|
||||||
simple_behavioral_stmt ';' | ';' |
|
simple_behavioral_stmt ';' | ';' |
|
||||||
hierarchical_id attr {
|
hierarchical_id attr {
|
||||||
|
|
|
@ -551,6 +551,10 @@ void log_dump_val_worker(RTLIL::SigSpec v) {
|
||||||
log("%s", log_signal(v));
|
log("%s", log_signal(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void log_dump_val_worker(RTLIL::State v) {
|
||||||
|
log("%s", log_signal(v));
|
||||||
|
}
|
||||||
|
|
||||||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
||||||
{
|
{
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
|
|
|
@ -292,6 +292,7 @@ static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p
|
||||||
static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); }
|
static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); }
|
||||||
void log_dump_val_worker(RTLIL::IdString v);
|
void log_dump_val_worker(RTLIL::IdString v);
|
||||||
void log_dump_val_worker(RTLIL::SigSpec v);
|
void log_dump_val_worker(RTLIL::SigSpec v);
|
||||||
|
void log_dump_val_worker(RTLIL::State v);
|
||||||
|
|
||||||
template<typename K, typename T, typename OPS>
|
template<typename K, typename T, typename OPS>
|
||||||
static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
|
static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
|
||||||
|
|
|
@ -439,7 +439,7 @@ void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||||
FILE *Frontend::current_script_file = NULL;
|
FILE *Frontend::current_script_file = NULL;
|
||||||
std::string Frontend::last_here_document;
|
std::string Frontend::last_here_document;
|
||||||
|
|
||||||
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input)
|
||||||
{
|
{
|
||||||
bool called_with_fp = f != NULL;
|
bool called_with_fp = f != NULL;
|
||||||
|
|
||||||
|
@ -489,7 +489,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
|
||||||
next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
|
next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
|
||||||
}
|
}
|
||||||
std::ifstream *ff = new std::ifstream;
|
std::ifstream *ff = new std::ifstream;
|
||||||
ff->open(filename.c_str());
|
ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in);
|
||||||
yosys_input_files.insert(filename);
|
yosys_input_files.insert(filename);
|
||||||
if (ff->fail())
|
if (ff->fail())
|
||||||
delete ff;
|
delete ff;
|
||||||
|
@ -612,7 +612,7 @@ void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||||
delete f;
|
delete f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output)
|
||||||
{
|
{
|
||||||
bool called_with_fp = f != NULL;
|
bool called_with_fp = f != NULL;
|
||||||
|
|
||||||
|
@ -647,7 +647,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
std::ofstream *ff = new std::ofstream;
|
std::ofstream *ff = new std::ofstream;
|
||||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
ff->open(filename.c_str(), bin_output ? (std::ofstream::trunc | std::ofstream::binary) : std::ofstream::trunc);
|
||||||
yosys_output_files.insert(filename);
|
yosys_output_files.insert(filename);
|
||||||
if (ff->fail()) {
|
if (ff->fail()) {
|
||||||
delete ff;
|
delete ff;
|
||||||
|
|
|
@ -94,7 +94,7 @@ struct Frontend : Pass
|
||||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||||
|
|
||||||
static std::vector<std::string> next_args;
|
static std::vector<std::string> next_args;
|
||||||
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input = false);
|
||||||
|
|
||||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command);
|
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command);
|
||||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args);
|
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args);
|
||||||
|
@ -109,7 +109,7 @@ struct Backend : Pass
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL;
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL;
|
||||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||||
|
|
||||||
void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output = false);
|
||||||
|
|
||||||
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command);
|
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command);
|
||||||
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args);
|
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args);
|
||||||
|
|
|
@ -3083,6 +3083,7 @@ void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules, RT
|
||||||
log_assert(other != NULL);
|
log_assert(other != NULL);
|
||||||
log_assert(width_ == other->width_);
|
log_assert(width_ == other->width_);
|
||||||
|
|
||||||
|
if (rules.empty()) return;
|
||||||
unpack();
|
unpack();
|
||||||
other->unpack();
|
other->unpack();
|
||||||
|
|
||||||
|
@ -3107,6 +3108,7 @@ void RTLIL::SigSpec::replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules
|
||||||
log_assert(other != NULL);
|
log_assert(other != NULL);
|
||||||
log_assert(width_ == other->width_);
|
log_assert(width_ == other->width_);
|
||||||
|
|
||||||
|
if (rules.empty()) return;
|
||||||
unpack();
|
unpack();
|
||||||
other->unpack();
|
other->unpack();
|
||||||
|
|
||||||
|
@ -3552,6 +3554,12 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
|
||||||
if (width_ != other.width_)
|
if (width_ != other.width_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Without this, SigSpec() == SigSpec(State::S0, 0) will fail
|
||||||
|
// since the RHS will contain one SigChunk of width 0 causing
|
||||||
|
// the size check below to fail
|
||||||
|
if (width_ == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
other.pack();
|
other.pack();
|
||||||
|
|
||||||
|
|
|
@ -609,8 +609,11 @@ struct RTLIL::Const
|
||||||
std::string decode_string() const;
|
std::string decode_string() const;
|
||||||
|
|
||||||
inline int size() const { return bits.size(); }
|
inline int size() const { return bits.size(); }
|
||||||
|
inline bool empty() const { return bits.empty(); }
|
||||||
inline RTLIL::State &operator[](int index) { return bits.at(index); }
|
inline RTLIL::State &operator[](int index) { return bits.at(index); }
|
||||||
inline const RTLIL::State &operator[](int index) const { return bits.at(index); }
|
inline const RTLIL::State &operator[](int index) const { return bits.at(index); }
|
||||||
|
inline decltype(bits)::iterator begin() { return bits.begin(); }
|
||||||
|
inline decltype(bits)::iterator end() { return bits.end(); }
|
||||||
|
|
||||||
bool is_fully_zero() const;
|
bool is_fully_zero() const;
|
||||||
bool is_fully_ones() const;
|
bool is_fully_ones() const;
|
||||||
|
|
|
@ -210,6 +210,7 @@ namespace RTLIL {
|
||||||
struct Module;
|
struct Module;
|
||||||
struct Design;
|
struct Design;
|
||||||
struct Monitor;
|
struct Monitor;
|
||||||
|
enum State : unsigned char;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AST {
|
namespace AST {
|
||||||
|
|
|
@ -0,0 +1,788 @@
|
||||||
|
/* Copyright (c) 2013 Dropbox, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "json11.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace json11 {
|
||||||
|
|
||||||
|
static const int max_depth = 200;
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::map;
|
||||||
|
using std::make_shared;
|
||||||
|
using std::initializer_list;
|
||||||
|
using std::move;
|
||||||
|
|
||||||
|
/* Helper for representing null - just a do-nothing struct, plus comparison
|
||||||
|
* operators so the helpers in JsonValue work. We can't use nullptr_t because
|
||||||
|
* it may not be orderable.
|
||||||
|
*/
|
||||||
|
struct NullStruct {
|
||||||
|
bool operator==(NullStruct) const { return true; }
|
||||||
|
bool operator<(NullStruct) const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Serialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void dump(NullStruct, string &out) {
|
||||||
|
out += "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(double value, string &out) {
|
||||||
|
if (std::isfinite(value)) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof buf, "%.17g", value);
|
||||||
|
out += buf;
|
||||||
|
} else {
|
||||||
|
out += "null";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(int value, string &out) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof buf, "%d", value);
|
||||||
|
out += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(bool value, string &out) {
|
||||||
|
out += value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const string &value, string &out) {
|
||||||
|
out += '"';
|
||||||
|
for (size_t i = 0; i < value.length(); i++) {
|
||||||
|
const char ch = value[i];
|
||||||
|
if (ch == '\\') {
|
||||||
|
out += "\\\\";
|
||||||
|
} else if (ch == '"') {
|
||||||
|
out += "\\\"";
|
||||||
|
} else if (ch == '\b') {
|
||||||
|
out += "\\b";
|
||||||
|
} else if (ch == '\f') {
|
||||||
|
out += "\\f";
|
||||||
|
} else if (ch == '\n') {
|
||||||
|
out += "\\n";
|
||||||
|
} else if (ch == '\r') {
|
||||||
|
out += "\\r";
|
||||||
|
} else if (ch == '\t') {
|
||||||
|
out += "\\t";
|
||||||
|
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
|
||||||
|
char buf[8];
|
||||||
|
snprintf(buf, sizeof buf, "\\u%04x", ch);
|
||||||
|
out += buf;
|
||||||
|
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||||
|
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
|
||||||
|
out += "\\u2028";
|
||||||
|
i += 2;
|
||||||
|
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||||
|
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
|
||||||
|
out += "\\u2029";
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
out += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const Json::array &values, string &out) {
|
||||||
|
bool first = true;
|
||||||
|
out += "[";
|
||||||
|
for (const auto &value : values) {
|
||||||
|
if (!first)
|
||||||
|
out += ", ";
|
||||||
|
value.dump(out);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
out += "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const Json::object &values, string &out) {
|
||||||
|
bool first = true;
|
||||||
|
out += "{";
|
||||||
|
for (const auto &kv : values) {
|
||||||
|
if (!first)
|
||||||
|
out += ", ";
|
||||||
|
dump(kv.first, out);
|
||||||
|
out += ": ";
|
||||||
|
kv.second.dump(out);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
out += "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Json::dump(string &out) const {
|
||||||
|
m_ptr->dump(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Value wrappers
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <Json::Type tag, typename T>
|
||||||
|
class Value : public JsonValue {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
explicit Value(const T &value) : m_value(value) {}
|
||||||
|
explicit Value(T &&value) : m_value(move(value)) {}
|
||||||
|
|
||||||
|
// Get type tag
|
||||||
|
Json::Type type() const override {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparisons
|
||||||
|
bool equals(const JsonValue * other) const override {
|
||||||
|
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
|
||||||
|
}
|
||||||
|
bool less(const JsonValue * other) const override {
|
||||||
|
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T m_value;
|
||||||
|
void dump(string &out) const override { json11::dump(m_value, out); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonDouble final : public Value<Json::NUMBER, double> {
|
||||||
|
double number_value() const override { return m_value; }
|
||||||
|
int int_value() const override { return static_cast<int>(m_value); }
|
||||||
|
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||||
|
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||||
|
public:
|
||||||
|
explicit JsonDouble(double value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonInt final : public Value<Json::NUMBER, int> {
|
||||||
|
double number_value() const override { return m_value; }
|
||||||
|
int int_value() const override { return m_value; }
|
||||||
|
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||||
|
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||||
|
public:
|
||||||
|
explicit JsonInt(int value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonBoolean final : public Value<Json::BOOL, bool> {
|
||||||
|
bool bool_value() const override { return m_value; }
|
||||||
|
public:
|
||||||
|
explicit JsonBoolean(bool value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonString final : public Value<Json::STRING, string> {
|
||||||
|
const string &string_value() const override { return m_value; }
|
||||||
|
public:
|
||||||
|
explicit JsonString(const string &value) : Value(value) {}
|
||||||
|
explicit JsonString(string &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonArray final : public Value<Json::ARRAY, Json::array> {
|
||||||
|
const Json::array &array_items() const override { return m_value; }
|
||||||
|
const Json & operator[](size_t i) const override;
|
||||||
|
public:
|
||||||
|
explicit JsonArray(const Json::array &value) : Value(value) {}
|
||||||
|
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonObject final : public Value<Json::OBJECT, Json::object> {
|
||||||
|
const Json::object &object_items() const override { return m_value; }
|
||||||
|
const Json & operator[](const string &key) const override;
|
||||||
|
public:
|
||||||
|
explicit JsonObject(const Json::object &value) : Value(value) {}
|
||||||
|
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonNull final : public Value<Json::NUL, NullStruct> {
|
||||||
|
public:
|
||||||
|
JsonNull() : Value({}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Static globals - static-init-safe
|
||||||
|
*/
|
||||||
|
struct Statics {
|
||||||
|
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
|
||||||
|
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
|
||||||
|
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
|
||||||
|
const string empty_string;
|
||||||
|
const vector<Json> empty_vector;
|
||||||
|
const map<string, Json> empty_map;
|
||||||
|
Statics() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Statics & statics() {
|
||||||
|
static const Statics s {};
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Json & static_null() {
|
||||||
|
// This has to be separate, not in Statics, because Json() accesses statics().null.
|
||||||
|
static const Json json_null;
|
||||||
|
return json_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Constructors
|
||||||
|
*/
|
||||||
|
|
||||||
|
Json::Json() noexcept : m_ptr(statics().null) {}
|
||||||
|
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
|
||||||
|
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
|
||||||
|
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
|
||||||
|
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
|
||||||
|
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||||
|
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
|
||||||
|
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||||
|
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
|
||||||
|
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
|
||||||
|
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
|
||||||
|
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Accessors
|
||||||
|
*/
|
||||||
|
|
||||||
|
Json::Type Json::type() const { return m_ptr->type(); }
|
||||||
|
double Json::number_value() const { return m_ptr->number_value(); }
|
||||||
|
int Json::int_value() const { return m_ptr->int_value(); }
|
||||||
|
bool Json::bool_value() const { return m_ptr->bool_value(); }
|
||||||
|
const string & Json::string_value() const { return m_ptr->string_value(); }
|
||||||
|
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
|
||||||
|
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
|
||||||
|
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
|
||||||
|
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
|
||||||
|
|
||||||
|
double JsonValue::number_value() const { return 0; }
|
||||||
|
int JsonValue::int_value() const { return 0; }
|
||||||
|
bool JsonValue::bool_value() const { return false; }
|
||||||
|
const string & JsonValue::string_value() const { return statics().empty_string; }
|
||||||
|
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
|
||||||
|
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
|
||||||
|
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
|
||||||
|
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
|
||||||
|
|
||||||
|
const Json & JsonObject::operator[] (const string &key) const {
|
||||||
|
auto iter = m_value.find(key);
|
||||||
|
return (iter == m_value.end()) ? static_null() : iter->second;
|
||||||
|
}
|
||||||
|
const Json & JsonArray::operator[] (size_t i) const {
|
||||||
|
if (i >= m_value.size()) return static_null();
|
||||||
|
else return m_value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Json::operator== (const Json &other) const {
|
||||||
|
if (m_ptr == other.m_ptr)
|
||||||
|
return true;
|
||||||
|
if (m_ptr->type() != other.m_ptr->type())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_ptr->equals(other.m_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Json::operator< (const Json &other) const {
|
||||||
|
if (m_ptr == other.m_ptr)
|
||||||
|
return false;
|
||||||
|
if (m_ptr->type() != other.m_ptr->type())
|
||||||
|
return m_ptr->type() < other.m_ptr->type();
|
||||||
|
|
||||||
|
return m_ptr->less(other.m_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* esc(c)
|
||||||
|
*
|
||||||
|
* Format char c suitable for printing in an error message.
|
||||||
|
*/
|
||||||
|
static inline string esc(char c) {
|
||||||
|
char buf[12];
|
||||||
|
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
|
||||||
|
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof buf, "(%d)", c);
|
||||||
|
}
|
||||||
|
return string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool in_range(long x, long lower, long upper) {
|
||||||
|
return (x >= lower && x <= upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/* JsonParser
|
||||||
|
*
|
||||||
|
* Object that tracks all state of an in-progress parse.
|
||||||
|
*/
|
||||||
|
struct JsonParser final {
|
||||||
|
|
||||||
|
/* State
|
||||||
|
*/
|
||||||
|
const string &str;
|
||||||
|
size_t i;
|
||||||
|
string &err;
|
||||||
|
bool failed;
|
||||||
|
const JsonParse strategy;
|
||||||
|
|
||||||
|
/* fail(msg, err_ret = Json())
|
||||||
|
*
|
||||||
|
* Mark this parse as failed.
|
||||||
|
*/
|
||||||
|
Json fail(string &&msg) {
|
||||||
|
return fail(move(msg), Json());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T fail(string &&msg, const T err_ret) {
|
||||||
|
if (!failed)
|
||||||
|
err = std::move(msg);
|
||||||
|
failed = true;
|
||||||
|
return err_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_whitespace()
|
||||||
|
*
|
||||||
|
* Advance until the current character is non-whitespace.
|
||||||
|
*/
|
||||||
|
void consume_whitespace() {
|
||||||
|
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_comment()
|
||||||
|
*
|
||||||
|
* Advance comments (c-style inline and multiline).
|
||||||
|
*/
|
||||||
|
bool consume_comment() {
|
||||||
|
bool comment_found = false;
|
||||||
|
if (str[i] == '/') {
|
||||||
|
i++;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input after start of comment", false);
|
||||||
|
if (str[i] == '/') { // inline comment
|
||||||
|
i++;
|
||||||
|
// advance until next line, or end of input
|
||||||
|
while (i < str.size() && str[i] != '\n') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else if (str[i] == '*') { // multiline comment
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail("unexpected end of input inside multi-line comment", false);
|
||||||
|
// advance until closing tokens
|
||||||
|
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail(
|
||||||
|
"unexpected end of input inside multi-line comment", false);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return fail("malformed comment", false);
|
||||||
|
}
|
||||||
|
return comment_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_garbage()
|
||||||
|
*
|
||||||
|
* Advance until the current character is non-whitespace and non-comment.
|
||||||
|
*/
|
||||||
|
void consume_garbage() {
|
||||||
|
consume_whitespace();
|
||||||
|
if(strategy == JsonParse::COMMENTS) {
|
||||||
|
bool comment_found = false;
|
||||||
|
do {
|
||||||
|
comment_found = consume_comment();
|
||||||
|
if (failed) return;
|
||||||
|
consume_whitespace();
|
||||||
|
}
|
||||||
|
while(comment_found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get_next_token()
|
||||||
|
*
|
||||||
|
* Return the next non-whitespace character. If the end of the input is reached,
|
||||||
|
* flag an error and return 0.
|
||||||
|
*/
|
||||||
|
char get_next_token() {
|
||||||
|
consume_garbage();
|
||||||
|
if (failed) return static_cast<char>(0);
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input", static_cast<char>(0));
|
||||||
|
|
||||||
|
return str[i++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode_utf8(pt, out)
|
||||||
|
*
|
||||||
|
* Encode pt as UTF-8 and add it to out.
|
||||||
|
*/
|
||||||
|
void encode_utf8(long pt, string & out) {
|
||||||
|
if (pt < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pt < 0x80) {
|
||||||
|
out += static_cast<char>(pt);
|
||||||
|
} else if (pt < 0x800) {
|
||||||
|
out += static_cast<char>((pt >> 6) | 0xC0);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
} else if (pt < 0x10000) {
|
||||||
|
out += static_cast<char>((pt >> 12) | 0xE0);
|
||||||
|
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
} else {
|
||||||
|
out += static_cast<char>((pt >> 18) | 0xF0);
|
||||||
|
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_string()
|
||||||
|
*
|
||||||
|
* Parse a string, starting at the current position.
|
||||||
|
*/
|
||||||
|
string parse_string() {
|
||||||
|
string out;
|
||||||
|
long last_escaped_codepoint = -1;
|
||||||
|
while (true) {
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input in string", "");
|
||||||
|
|
||||||
|
char ch = str[i++];
|
||||||
|
|
||||||
|
if (ch == '"') {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_range(ch, 0, 0x1f))
|
||||||
|
return fail("unescaped " + esc(ch) + " in string", "");
|
||||||
|
|
||||||
|
// The usual case: non-escaped characters
|
||||||
|
if (ch != '\\') {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
out += ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle escapes
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input in string", "");
|
||||||
|
|
||||||
|
ch = str[i++];
|
||||||
|
|
||||||
|
if (ch == 'u') {
|
||||||
|
// Extract 4-byte escape sequence
|
||||||
|
string esc = str.substr(i, 4);
|
||||||
|
// Explicitly check length of the substring. The following loop
|
||||||
|
// relies on std::string returning the terminating NUL when
|
||||||
|
// accessing str[length]. Checking here reduces brittleness.
|
||||||
|
if (esc.length() < 4) {
|
||||||
|
return fail("bad \\u escape: " + esc, "");
|
||||||
|
}
|
||||||
|
for (size_t j = 0; j < 4; j++) {
|
||||||
|
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
|
||||||
|
&& !in_range(esc[j], '0', '9'))
|
||||||
|
return fail("bad \\u escape: " + esc, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
long codepoint = strtol(esc.data(), nullptr, 16);
|
||||||
|
|
||||||
|
// JSON specifies that characters outside the BMP shall be encoded as a pair
|
||||||
|
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
|
||||||
|
// whether we're in the middle of such a beast: the previous codepoint was an
|
||||||
|
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
|
||||||
|
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
|
||||||
|
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
|
||||||
|
// Reassemble the two surrogate pairs into one astral-plane character, per
|
||||||
|
// the UTF-16 algorithm.
|
||||||
|
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
|
||||||
|
| (codepoint - 0xDC00)) + 0x10000, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
} else {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
|
||||||
|
if (ch == 'b') {
|
||||||
|
out += '\b';
|
||||||
|
} else if (ch == 'f') {
|
||||||
|
out += '\f';
|
||||||
|
} else if (ch == 'n') {
|
||||||
|
out += '\n';
|
||||||
|
} else if (ch == 'r') {
|
||||||
|
out += '\r';
|
||||||
|
} else if (ch == 't') {
|
||||||
|
out += '\t';
|
||||||
|
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||||
|
out += ch;
|
||||||
|
} else {
|
||||||
|
return fail("invalid escape character " + esc(ch), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_number()
|
||||||
|
*
|
||||||
|
* Parse a double.
|
||||||
|
*/
|
||||||
|
Json parse_number() {
|
||||||
|
size_t start_pos = i;
|
||||||
|
|
||||||
|
if (str[i] == '-')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// Integer part
|
||||||
|
if (str[i] == '0') {
|
||||||
|
i++;
|
||||||
|
if (in_range(str[i], '0', '9'))
|
||||||
|
return fail("leading 0s not permitted in numbers");
|
||||||
|
} else if (in_range(str[i], '1', '9')) {
|
||||||
|
i++;
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
return fail("invalid " + esc(str[i]) + " in number");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
|
||||||
|
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
|
||||||
|
return std::atoi(str.c_str() + start_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal part
|
||||||
|
if (str[i] == '.') {
|
||||||
|
i++;
|
||||||
|
if (!in_range(str[i], '0', '9'))
|
||||||
|
return fail("at least one digit required in fractional part");
|
||||||
|
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exponent part
|
||||||
|
if (str[i] == 'e' || str[i] == 'E') {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (str[i] == '+' || str[i] == '-')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (!in_range(str[i], '0', '9'))
|
||||||
|
return fail("at least one digit required in exponent");
|
||||||
|
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::strtod(str.c_str() + start_pos, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expect(str, res)
|
||||||
|
*
|
||||||
|
* Expect that 'str' starts at the character that was just read. If it does, advance
|
||||||
|
* the input and return res. If not, flag an error.
|
||||||
|
*/
|
||||||
|
Json expect(const string &expected, Json res) {
|
||||||
|
assert(i != 0);
|
||||||
|
i--;
|
||||||
|
if (str.compare(i, expected.length(), expected) == 0) {
|
||||||
|
i += expected.length();
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_json()
|
||||||
|
*
|
||||||
|
* Parse a JSON object.
|
||||||
|
*/
|
||||||
|
Json parse_json(int depth) {
|
||||||
|
if (depth > max_depth) {
|
||||||
|
return fail("exceeded maximum nesting depth");
|
||||||
|
}
|
||||||
|
|
||||||
|
char ch = get_next_token();
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
if (ch == '-' || (ch >= '0' && ch <= '9')) {
|
||||||
|
i--;
|
||||||
|
return parse_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 't')
|
||||||
|
return expect("true", true);
|
||||||
|
|
||||||
|
if (ch == 'f')
|
||||||
|
return expect("false", false);
|
||||||
|
|
||||||
|
if (ch == 'n')
|
||||||
|
return expect("null", Json());
|
||||||
|
|
||||||
|
if (ch == '"')
|
||||||
|
return parse_string();
|
||||||
|
|
||||||
|
if (ch == '{') {
|
||||||
|
map<string, Json> data;
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == '}')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (ch != '"')
|
||||||
|
return fail("expected '\"' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
string key = parse_string();
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch != ':')
|
||||||
|
return fail("expected ':' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
data[std::move(key)] = parse_json(depth + 1);
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == '}')
|
||||||
|
break;
|
||||||
|
if (ch != ',')
|
||||||
|
return fail("expected ',' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '[') {
|
||||||
|
vector<Json> data;
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == ']')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
i--;
|
||||||
|
data.push_back(parse_json(depth + 1));
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == ']')
|
||||||
|
break;
|
||||||
|
if (ch != ',')
|
||||||
|
return fail("expected ',' in list, got " + esc(ch));
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
(void)ch;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fail("expected value, got " + esc(ch));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}//namespace {
|
||||||
|
|
||||||
|
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||||
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
|
Json result = parser.parse_json(0);
|
||||||
|
|
||||||
|
// Check for any trailing garbage
|
||||||
|
parser.consume_garbage();
|
||||||
|
if (parser.failed)
|
||||||
|
return Json();
|
||||||
|
if (parser.i != in.size())
|
||||||
|
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documented in json11.hpp
|
||||||
|
vector<Json> Json::parse_multi(const string &in,
|
||||||
|
std::string::size_type &parser_stop_pos,
|
||||||
|
string &err,
|
||||||
|
JsonParse strategy) {
|
||||||
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
|
parser_stop_pos = 0;
|
||||||
|
vector<Json> json_vec;
|
||||||
|
while (parser.i != in.size() && !parser.failed) {
|
||||||
|
json_vec.push_back(parser.parse_json(0));
|
||||||
|
if (parser.failed)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check for another object
|
||||||
|
parser.consume_garbage();
|
||||||
|
if (parser.failed)
|
||||||
|
break;
|
||||||
|
parser_stop_pos = parser.i;
|
||||||
|
}
|
||||||
|
return json_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Shape-checking
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Json::has_shape(const shape & types, string & err) const {
|
||||||
|
if (!is_object()) {
|
||||||
|
err = "expected JSON object, got " + dump();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto & item : types) {
|
||||||
|
if ((*this)[item.first].type() != item.second) {
|
||||||
|
err = "bad type for " + item.first + " in " + dump();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json11
|
|
@ -0,0 +1,232 @@
|
||||||
|
/* json11
|
||||||
|
*
|
||||||
|
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||||
|
*
|
||||||
|
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||||
|
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||||
|
* object (std::map).
|
||||||
|
*
|
||||||
|
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||||
|
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||||
|
* Json::parse (static) to parse a std::string as a Json object.
|
||||||
|
*
|
||||||
|
* Internally, the various types of Json object are represented by the JsonValue class
|
||||||
|
* hierarchy.
|
||||||
|
*
|
||||||
|
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||||
|
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||||
|
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||||
|
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||||
|
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||||
|
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||||
|
* provides integer helpers.
|
||||||
|
*
|
||||||
|
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||||
|
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||||
|
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||||
|
* will be exact for +/- 275 years.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (c) 2013 Dropbox, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_VER <= 1800 // VS 2013
|
||||||
|
#ifndef noexcept
|
||||||
|
#define noexcept throw()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef snprintf
|
||||||
|
#define snprintf _snprintf_s
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace json11 {
|
||||||
|
|
||||||
|
enum JsonParse {
|
||||||
|
STANDARD, COMMENTS
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonValue;
|
||||||
|
|
||||||
|
class Json final {
|
||||||
|
public:
|
||||||
|
// Types
|
||||||
|
enum Type {
|
||||||
|
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array and object typedefs
|
||||||
|
typedef std::vector<Json> array;
|
||||||
|
typedef std::map<std::string, Json> object;
|
||||||
|
|
||||||
|
// Constructors for the various types of JSON value.
|
||||||
|
Json() noexcept; // NUL
|
||||||
|
Json(std::nullptr_t) noexcept; // NUL
|
||||||
|
Json(double value); // NUMBER
|
||||||
|
Json(int value); // NUMBER
|
||||||
|
Json(bool value); // BOOL
|
||||||
|
Json(const std::string &value); // STRING
|
||||||
|
Json(std::string &&value); // STRING
|
||||||
|
Json(const char * value); // STRING
|
||||||
|
Json(const array &values); // ARRAY
|
||||||
|
Json(array &&values); // ARRAY
|
||||||
|
Json(const object &values); // OBJECT
|
||||||
|
Json(object &&values); // OBJECT
|
||||||
|
|
||||||
|
// Implicit constructor: anything with a to_json() function.
|
||||||
|
template <class T, class = decltype(&T::to_json)>
|
||||||
|
Json(const T & t) : Json(t.to_json()) {}
|
||||||
|
|
||||||
|
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||||
|
template <class M, typename std::enable_if<
|
||||||
|
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
|
||||||
|
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
Json(const M & m) : Json(object(m.begin(), m.end())) {}
|
||||||
|
|
||||||
|
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||||
|
template <class V, typename std::enable_if<
|
||||||
|
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
Json(const V & v) : Json(array(v.begin(), v.end())) {}
|
||||||
|
|
||||||
|
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||||
|
// Json(bool(some_pointer)) if that behavior is desired.
|
||||||
|
Json(void *) = delete;
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
Type type() const;
|
||||||
|
|
||||||
|
bool is_null() const { return type() == NUL; }
|
||||||
|
bool is_number() const { return type() == NUMBER; }
|
||||||
|
bool is_bool() const { return type() == BOOL; }
|
||||||
|
bool is_string() const { return type() == STRING; }
|
||||||
|
bool is_array() const { return type() == ARRAY; }
|
||||||
|
bool is_object() const { return type() == OBJECT; }
|
||||||
|
|
||||||
|
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||||
|
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||||
|
// can both be applied to a NUMBER-typed object.
|
||||||
|
double number_value() const;
|
||||||
|
int int_value() const;
|
||||||
|
|
||||||
|
// Return the enclosed value if this is a boolean, false otherwise.
|
||||||
|
bool bool_value() const;
|
||||||
|
// Return the enclosed string if this is a string, "" otherwise.
|
||||||
|
const std::string &string_value() const;
|
||||||
|
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||||
|
const array &array_items() const;
|
||||||
|
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||||
|
const object &object_items() const;
|
||||||
|
|
||||||
|
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||||
|
const Json & operator[](size_t i) const;
|
||||||
|
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||||
|
const Json & operator[](const std::string &key) const;
|
||||||
|
|
||||||
|
// Serialize.
|
||||||
|
void dump(std::string &out) const;
|
||||||
|
std::string dump() const {
|
||||||
|
std::string out;
|
||||||
|
dump(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||||
|
static Json parse(const std::string & in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
static Json parse(const char * in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD) {
|
||||||
|
if (in) {
|
||||||
|
return parse(std::string(in), err, strategy);
|
||||||
|
} else {
|
||||||
|
err = "null input";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse multiple objects, concatenated or separated by whitespace
|
||||||
|
static std::vector<Json> parse_multi(
|
||||||
|
const std::string & in,
|
||||||
|
std::string::size_type & parser_stop_pos,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
|
||||||
|
static inline std::vector<Json> parse_multi(
|
||||||
|
const std::string & in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD) {
|
||||||
|
std::string::size_type parser_stop_pos;
|
||||||
|
return parse_multi(in, parser_stop_pos, err, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const Json &rhs) const;
|
||||||
|
bool operator< (const Json &rhs) const;
|
||||||
|
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
|
||||||
|
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
|
||||||
|
bool operator> (const Json &rhs) const { return (rhs < *this); }
|
||||||
|
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
|
||||||
|
|
||||||
|
/* has_shape(types, err)
|
||||||
|
*
|
||||||
|
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||||
|
* the given type. If not, return false and set err to a descriptive message.
|
||||||
|
*/
|
||||||
|
typedef std::initializer_list<std::pair<std::string, Type>> shape;
|
||||||
|
bool has_shape(const shape & types, std::string & err) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<JsonValue> m_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||||
|
class JsonValue {
|
||||||
|
protected:
|
||||||
|
friend class Json;
|
||||||
|
friend class JsonInt;
|
||||||
|
friend class JsonDouble;
|
||||||
|
virtual Json::Type type() const = 0;
|
||||||
|
virtual bool equals(const JsonValue * other) const = 0;
|
||||||
|
virtual bool less(const JsonValue * other) const = 0;
|
||||||
|
virtual void dump(std::string &out) const = 0;
|
||||||
|
virtual double number_value() const;
|
||||||
|
virtual int int_value() const;
|
||||||
|
virtual bool bool_value() const;
|
||||||
|
virtual const std::string &string_value() const;
|
||||||
|
virtual const Json::array &array_items() const;
|
||||||
|
virtual const Json &operator[](size_t i) const;
|
||||||
|
virtual const Json::object &object_items() const;
|
||||||
|
virtual const Json &operator[](const std::string &key) const;
|
||||||
|
virtual ~JsonValue() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace json11
|
|
@ -253,6 +253,8 @@ class WContainer:
|
||||||
candidate = WType.from_string(arg.strip(), containing_file, line_number)
|
candidate = WType.from_string(arg.strip(), containing_file, line_number)
|
||||||
if candidate == None:
|
if candidate == None:
|
||||||
return None
|
return None
|
||||||
|
if candidate.name == "void":
|
||||||
|
return None
|
||||||
cont.args.append(candidate)
|
cont.args.append(candidate)
|
||||||
return cont
|
return cont
|
||||||
|
|
||||||
|
@ -880,11 +882,8 @@ class WClass:
|
||||||
text += fun.gen_def_virtual()
|
text += fun.gen_def_virtual()
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def gen_boost_py(self):
|
def gen_boost_py_body(self):
|
||||||
text = "\n\t\tclass_<" + self.name
|
text = ""
|
||||||
if self.link_type == link_types.derive:
|
|
||||||
text += "Wrap, boost::noncopyable"
|
|
||||||
text += ">(\"" + self.name + "\""
|
|
||||||
if self.printable_constrs() == 0 or not self.contains_default_constr():
|
if self.printable_constrs() == 0 or not self.contains_default_constr():
|
||||||
text += ", no_init"
|
text += ", no_init"
|
||||||
text += ")"
|
text += ")"
|
||||||
|
@ -907,6 +906,21 @@ class WClass:
|
||||||
text += "\n\t\t\t;\n"
|
text += "\n\t\t\t;\n"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def gen_boost_py(self):
|
||||||
|
body = self.gen_boost_py_body()
|
||||||
|
if self.link_type == link_types.derive:
|
||||||
|
text = "\n\t\tclass_<" + self.name + ">(\"Cpp" + self.name + "\""
|
||||||
|
text += body
|
||||||
|
text += "\n\t\tclass_<" + self.name
|
||||||
|
text += "Wrap, boost::noncopyable"
|
||||||
|
text += ">(\"" + self.name + "\""
|
||||||
|
text += body
|
||||||
|
else:
|
||||||
|
text = "\n\t\tclass_<" + self.name + ">(\"" + self.name + "\""
|
||||||
|
text += body
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
def contains_default_constr(self):
|
def contains_default_constr(self):
|
||||||
for c in self.found_constrs:
|
for c in self.found_constrs:
|
||||||
if len(c.args) == 0:
|
if len(c.args) == 0:
|
||||||
|
@ -974,6 +988,7 @@ blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Mo
|
||||||
enum_names = ["State","SyncType","ConstFlags"]
|
enum_names = ["State","SyncType","ConstFlags"]
|
||||||
|
|
||||||
enums = [] #Do not edit
|
enums = [] #Do not edit
|
||||||
|
glbls = []
|
||||||
|
|
||||||
unowned_functions = []
|
unowned_functions = []
|
||||||
|
|
||||||
|
@ -1081,6 +1096,8 @@ class WConstructor:
|
||||||
con.args = []
|
con.args = []
|
||||||
con.duplicate = False
|
con.duplicate = False
|
||||||
con.protected = protected
|
con.protected = protected
|
||||||
|
if str.startswith(str_def, "inline "):
|
||||||
|
str_def = str_def[7:]
|
||||||
if not str.startswith(str_def, class_.name + "("):
|
if not str.startswith(str_def, class_.name + "("):
|
||||||
return None
|
return None
|
||||||
str_def = str_def[len(class_.name)+1:]
|
str_def = str_def[len(class_.name)+1:]
|
||||||
|
@ -1721,6 +1738,159 @@ class WMember:
|
||||||
text += ")"
|
text += ")"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
class WGlobal:
|
||||||
|
orig_text = None
|
||||||
|
wtype = attr_types.default
|
||||||
|
name = None
|
||||||
|
containing_file = None
|
||||||
|
namespace = ""
|
||||||
|
is_const = False
|
||||||
|
|
||||||
|
def from_string(str_def, containing_file, line_number, namespace):
|
||||||
|
glbl = WGlobal()
|
||||||
|
glbl.orig_text = str_def
|
||||||
|
glbl.wtype = None
|
||||||
|
glbl.name = ""
|
||||||
|
glbl.containing_file = containing_file
|
||||||
|
glbl.namespace = namespace
|
||||||
|
glbl.is_const = False
|
||||||
|
|
||||||
|
if not str.startswith(str_def, "extern"):
|
||||||
|
return None
|
||||||
|
str_def = str_def[7:]
|
||||||
|
|
||||||
|
if str.startswith(str_def, "const "):
|
||||||
|
glbl.is_const = True
|
||||||
|
str_def = str_def[6:]
|
||||||
|
|
||||||
|
if str_def.count(" ") == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
parts = split_list(str_def.strip(), " ")
|
||||||
|
|
||||||
|
prefix = ""
|
||||||
|
i = 0
|
||||||
|
for part in parts:
|
||||||
|
if part in ["unsigned", "long", "short"]:
|
||||||
|
prefix += part + " "
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
parts = parts[i:]
|
||||||
|
|
||||||
|
if len(parts) <= 1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
glbl.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
|
||||||
|
|
||||||
|
if glbl.wtype == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
str_def = parts[1]
|
||||||
|
for part in parts[2:]:
|
||||||
|
str_def = str_def + " " + part
|
||||||
|
|
||||||
|
if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
found = str_def.find(";")
|
||||||
|
if found == -1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
found_eq = str_def.find("=")
|
||||||
|
if found_eq != -1:
|
||||||
|
found = found_eq
|
||||||
|
|
||||||
|
glbl.name = str_def[:found]
|
||||||
|
str_def = str_def[found+1:]
|
||||||
|
if glbl.name.find("*") == 0:
|
||||||
|
glbl.name = glbl.name.replace("*", "")
|
||||||
|
glbl.wtype.attr_type = attr_types.star
|
||||||
|
if glbl.name.find("&&") == 0:
|
||||||
|
glbl.name = glbl.name.replace("&&", "")
|
||||||
|
glbl.wtype.attr_type = attr_types.ampamp
|
||||||
|
if glbl.name.find("&") == 0:
|
||||||
|
glbl.name = glbl.name.replace("&", "")
|
||||||
|
glbl.wtype.attr_type = attr_types.amp
|
||||||
|
|
||||||
|
if(len(str_def.strip()) != 0):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(glbl.name.split(",")) > 1:
|
||||||
|
glbl_list = []
|
||||||
|
for name in glbl.name.split(","):
|
||||||
|
name = name.strip();
|
||||||
|
glbl_list.append(WGlobal())
|
||||||
|
glbl_list[-1].orig_text = glbl.orig_text
|
||||||
|
glbl_list[-1].wtype = glbl.wtype
|
||||||
|
glbl_list[-1].name = name
|
||||||
|
glbl_list[-1].containing_file = glbl.containing_file
|
||||||
|
glbl_list[-1].namespace = glbl.namespace
|
||||||
|
glbl_list[-1].is_const = glbl.is_const
|
||||||
|
return glbl_list
|
||||||
|
|
||||||
|
return glbl
|
||||||
|
|
||||||
|
def gen_def(self):
|
||||||
|
text = "\n\t"
|
||||||
|
if self.is_const:
|
||||||
|
text += "const "
|
||||||
|
text += self.wtype.gen_text() + " get_var_py_" + self.name + "()"
|
||||||
|
text += "\n\t{\n\t\t"
|
||||||
|
if self.wtype.attr_type == attr_types.star:
|
||||||
|
text += "if(" + self.namespace + "::" + self.name + " == NULL)\n\t\t\t"
|
||||||
|
text += "throw std::runtime_error(\"" + self.namespace + "::" + self.name + " is NULL\");\n\t\t"
|
||||||
|
if self.wtype.name in known_containers:
|
||||||
|
text += self.wtype.gen_text_cpp()
|
||||||
|
else:
|
||||||
|
if self.is_const:
|
||||||
|
text += "const "
|
||||||
|
text += self.wtype.gen_text()
|
||||||
|
|
||||||
|
if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
|
||||||
|
text += "*"
|
||||||
|
text += " ret_ = "
|
||||||
|
if self.wtype.name in classnames:
|
||||||
|
text += self.wtype.name + "::get_py_obj("
|
||||||
|
if self.wtype.attr_type != attr_types.star:
|
||||||
|
text += "&"
|
||||||
|
text += self.namespace + "::" + self.name
|
||||||
|
if self.wtype.name in classnames:
|
||||||
|
text += ")"
|
||||||
|
text += ";"
|
||||||
|
|
||||||
|
if self.wtype.name in classnames:
|
||||||
|
text += "\n\t\treturn *ret_;"
|
||||||
|
elif self.wtype.name in known_containers:
|
||||||
|
text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
|
||||||
|
text += "\n\t\treturn ret____tmp;"
|
||||||
|
else:
|
||||||
|
text += "\n\t\treturn ret_;"
|
||||||
|
text += "\n\t}\n"
|
||||||
|
|
||||||
|
if self.is_const:
|
||||||
|
return text
|
||||||
|
|
||||||
|
ret = Attribute(self.wtype, "rhs");
|
||||||
|
|
||||||
|
if self.wtype.name in classnames:
|
||||||
|
text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
|
||||||
|
else:
|
||||||
|
text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
|
||||||
|
text += "\n\t{"
|
||||||
|
text += ret.gen_translation()
|
||||||
|
text += "\n\t\t" + self.namespace + "::" + self.name + " = " + ret.gen_call() + ";"
|
||||||
|
text += "\n\t}\n"
|
||||||
|
|
||||||
|
return text;
|
||||||
|
|
||||||
|
def gen_boost_py(self):
|
||||||
|
text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name
|
||||||
|
if not self.is_const:
|
||||||
|
text += ", &YOSYS_PYTHON::set_var_py_" + self.name
|
||||||
|
text += ")"
|
||||||
|
return text
|
||||||
|
|
||||||
def concat_namespace(tuple_list):
|
def concat_namespace(tuple_list):
|
||||||
if len(tuple_list) == 0:
|
if len(tuple_list) == 0:
|
||||||
return ""
|
return ""
|
||||||
|
@ -1857,6 +2027,16 @@ def parse_header(source):
|
||||||
else:
|
else:
|
||||||
debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
|
debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
|
||||||
class_[0].found_vars.append(candidate)
|
class_[0].found_vars.append(candidate)
|
||||||
|
if candidate == None and class_ == None:
|
||||||
|
candidate = WGlobal.from_string(ugly_line, source.name, i, concat_namespace(namespaces))
|
||||||
|
if candidate != None:
|
||||||
|
if type(candidate) == list:
|
||||||
|
for c in candidate:
|
||||||
|
glbls.append(c)
|
||||||
|
debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
|
||||||
|
else:
|
||||||
|
glbls.append(candidate)
|
||||||
|
debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
|
||||||
|
|
||||||
j = i
|
j = i
|
||||||
line = unpretty_string(line)
|
line = unpretty_string(line)
|
||||||
|
@ -1886,6 +2066,17 @@ def parse_header(source):
|
||||||
debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
|
debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
|
||||||
class_[0].found_constrs.append(candidate)
|
class_[0].found_constrs.append(candidate)
|
||||||
continue
|
continue
|
||||||
|
if class_ == None:
|
||||||
|
candidate = WGlobal.from_string(line, source.name, i, concat_namespace(namespaces))
|
||||||
|
if candidate != None:
|
||||||
|
if type(candidate) == list:
|
||||||
|
for c in candidate:
|
||||||
|
glbls.append(c)
|
||||||
|
debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
|
||||||
|
else:
|
||||||
|
glbls.append(candidate)
|
||||||
|
debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
|
||||||
|
continue
|
||||||
if candidate != None:
|
if candidate != None:
|
||||||
while i < j:
|
while i < j:
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -1988,6 +2179,7 @@ def gen_wrappers(filename, debug_level_ = 0):
|
||||||
if len(class_.found_constrs) == 0:
|
if len(class_.found_constrs) == 0:
|
||||||
class_.found_constrs.append(WConstructor(source.name, class_))
|
class_.found_constrs.append(WConstructor(source.name, class_))
|
||||||
debug(str(len(unowned_functions)) + " functions are unowned", 1)
|
debug(str(len(unowned_functions)) + " functions are unowned", 1)
|
||||||
|
debug(str(len(unowned_functions)) + " global variables", 1)
|
||||||
for enum in enums:
|
for enum in enums:
|
||||||
debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
|
debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
|
||||||
debug("-"*col, 1)
|
debug("-"*col, 1)
|
||||||
|
@ -2023,10 +2215,15 @@ def gen_wrappers(filename, debug_level_ = 0):
|
||||||
#include <boost/python/wrapper.hpp>
|
#include <boost/python/wrapper.hpp>
|
||||||
#include <boost/python/call.hpp>
|
#include <boost/python/call.hpp>
|
||||||
#include <boost/python.hpp>
|
#include <boost/python.hpp>
|
||||||
|
#include <iosfwd> // std::streamsize
|
||||||
|
#include <iostream>
|
||||||
|
#include <boost/iostreams/concepts.hpp> // boost::iostreams::sink
|
||||||
|
#include <boost/iostreams/stream.hpp>
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
|
||||||
namespace YOSYS_PYTHON {
|
namespace YOSYS_PYTHON {
|
||||||
|
|
||||||
|
struct YosysStatics{};
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for source in sources:
|
for source in sources:
|
||||||
|
@ -2048,6 +2245,9 @@ namespace YOSYS_PYTHON {
|
||||||
for fun in unowned_functions:
|
for fun in unowned_functions:
|
||||||
wrapper_file.write(fun.gen_def())
|
wrapper_file.write(fun.gen_def())
|
||||||
|
|
||||||
|
for glbl in glbls:
|
||||||
|
wrapper_file.write(glbl.gen_def())
|
||||||
|
|
||||||
wrapper_file.write(""" struct Initializer
|
wrapper_file.write(""" struct Initializer
|
||||||
{
|
{
|
||||||
Initializer() {
|
Initializer() {
|
||||||
|
@ -2066,12 +2266,89 @@ namespace YOSYS_PYTHON {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// source: https://stackoverflow.com/questions/26033781/converting-python-io-object-to-stdostream-when-using-boostpython?noredirect=1&lq=1
|
||||||
|
/// @brief Type that implements the Boost.IOStream's Sink and Flushable
|
||||||
|
/// concept for writing data to Python object that support:
|
||||||
|
/// n = object.write(str) # n = None or bytes written
|
||||||
|
/// object.flush() # if flush exists, then it is callable
|
||||||
|
class PythonOutputDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// This class models both the Sink and Flushable concepts.
|
||||||
|
struct category
|
||||||
|
: boost::iostreams::sink_tag,
|
||||||
|
boost::iostreams::flushable_tag
|
||||||
|
{};
|
||||||
|
|
||||||
|
explicit
|
||||||
|
PythonOutputDevice(boost::python::object object)
|
||||||
|
: object_(object)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Sink concept.
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef char char_type;
|
||||||
|
|
||||||
|
std::streamsize write(const char* buffer, std::streamsize buffer_size)
|
||||||
|
{
|
||||||
|
namespace python = boost::python;
|
||||||
|
// Copy the buffer to a python string.
|
||||||
|
python::str data(buffer, buffer_size);
|
||||||
|
|
||||||
|
// Invoke write on the python object, passing in the data. The following
|
||||||
|
// is equivalent to:
|
||||||
|
// n = object_.write(data)
|
||||||
|
python::extract<std::streamsize> bytes_written(
|
||||||
|
object_.attr("write")(data));
|
||||||
|
|
||||||
|
// Per the Sink concept, return the number of bytes written. If the
|
||||||
|
// Python return value provides a numeric result, then use it. Otherwise,
|
||||||
|
// such as the case of a File object, use the buffer_size.
|
||||||
|
return bytes_written.check()
|
||||||
|
? bytes_written
|
||||||
|
: buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flushable concept.
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool flush()
|
||||||
|
{
|
||||||
|
// If flush exists, then call it.
|
||||||
|
boost::python::object flush = object_.attr("flush");
|
||||||
|
if (!flush.is_none())
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return true. If an error occurs, an exception should be thrown.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::python::object object_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Use an auxiliary function to adapt the legacy function.
|
||||||
|
void log_to_stream(boost::python::object object)
|
||||||
|
{
|
||||||
|
// Create an ostream that delegates to the python object.
|
||||||
|
boost::iostreams::stream<PythonOutputDevice>* output = new boost::iostreams::stream<PythonOutputDevice>(object);
|
||||||
|
Yosys::log_streams.insert(Yosys::log_streams.begin(), output);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
BOOST_PYTHON_MODULE(libyosys)
|
BOOST_PYTHON_MODULE(libyosys)
|
||||||
{
|
{
|
||||||
using namespace boost::python;
|
using namespace boost::python;
|
||||||
|
|
||||||
class_<Initializer>("Initializer");
|
class_<Initializer>("Initializer");
|
||||||
scope().attr("_hidden") = new Initializer();
|
scope().attr("_hidden") = new Initializer();
|
||||||
|
|
||||||
|
def("log_to_stream", &log_to_stream);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for enum in enums:
|
for enum in enums:
|
||||||
|
@ -2084,6 +2361,11 @@ namespace YOSYS_PYTHON {
|
||||||
for fun in unowned_functions:
|
for fun in unowned_functions:
|
||||||
wrapper_file.write(fun.gen_boost_py())
|
wrapper_file.write(fun.gen_boost_py())
|
||||||
|
|
||||||
|
wrapper_file.write("\n\n\t\tclass_<YosysStatics>(\"Yosys\")\n")
|
||||||
|
for glbl in glbls:
|
||||||
|
wrapper_file.write(glbl.gen_boost_py())
|
||||||
|
wrapper_file.write("\t\t;\n")
|
||||||
|
|
||||||
wrapper_file.write("\n\t}\n}\n#endif")
|
wrapper_file.write("\n\t}\n}\n#endif")
|
||||||
|
|
||||||
def print_includes():
|
def print_includes():
|
||||||
|
|
|
@ -5,6 +5,7 @@ OBJS += passes/cmds/design.o
|
||||||
OBJS += passes/cmds/select.o
|
OBJS += passes/cmds/select.o
|
||||||
OBJS += passes/cmds/show.o
|
OBJS += passes/cmds/show.o
|
||||||
OBJS += passes/cmds/rename.o
|
OBJS += passes/cmds/rename.o
|
||||||
|
OBJS += passes/cmds/autoname.o
|
||||||
OBJS += passes/cmds/connect.o
|
OBJS += passes/cmds/connect.o
|
||||||
OBJS += passes/cmds/scatter.o
|
OBJS += passes/cmds/scatter.o
|
||||||
OBJS += passes/cmds/setundef.o
|
OBJS += passes/cmds/setundef.o
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
int autoname_worker(Module *module)
|
||||||
|
{
|
||||||
|
dict<Cell*, pair<int, IdString>> proposed_cell_names;
|
||||||
|
dict<Wire*, pair<int, IdString>> proposed_wire_names;
|
||||||
|
dict<Wire*, int> wire_score;
|
||||||
|
int best_score = -1;
|
||||||
|
|
||||||
|
for (auto cell : module->selected_cells())
|
||||||
|
for (auto &conn : cell->connections())
|
||||||
|
for (auto bit : conn.second)
|
||||||
|
if (bit.wire != nullptr)
|
||||||
|
wire_score[bit.wire]++;
|
||||||
|
|
||||||
|
for (auto cell : module->selected_cells()) {
|
||||||
|
if (cell->name[0] == '$') {
|
||||||
|
for (auto &conn : cell->connections()) {
|
||||||
|
string suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
|
||||||
|
for (auto bit : conn.second)
|
||||||
|
if (bit.wire != nullptr && bit.wire->name[0] != '$') {
|
||||||
|
IdString new_name(bit.wire->name.str() + suffix);
|
||||||
|
int score = wire_score.at(bit.wire);
|
||||||
|
if (cell->output(conn.first)) score = 0;
|
||||||
|
score = 10000*score + new_name.size();
|
||||||
|
if (!proposed_cell_names.count(cell) || score < proposed_cell_names.at(cell).first) {
|
||||||
|
if (best_score < 0 || score < best_score)
|
||||||
|
best_score = score;
|
||||||
|
proposed_cell_names[cell] = make_pair(score, new_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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] == '$') {
|
||||||
|
IdString new_name(cell->name.str() + suffix);
|
||||||
|
int score = wire_score.at(bit.wire);
|
||||||
|
if (cell->output(conn.first)) score = 0;
|
||||||
|
score = 10000*score + new_name.size();
|
||||||
|
if (!proposed_wire_names.count(bit.wire) || score < proposed_wire_names.at(bit.wire).first) {
|
||||||
|
if (best_score < 0 || score < best_score)
|
||||||
|
best_score = score;
|
||||||
|
proposed_wire_names[bit.wire] = make_pair(score, new_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &it : proposed_cell_names) {
|
||||||
|
if (best_score*2 < it.second.first)
|
||||||
|
continue;
|
||||||
|
IdString n = module->uniquify(it.second.second);
|
||||||
|
log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
|
||||||
|
module->rename(it.first, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &it : proposed_wire_names) {
|
||||||
|
if (best_score*2 < it.second.first)
|
||||||
|
continue;
|
||||||
|
IdString n = module->uniquify(it.second.second);
|
||||||
|
log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
|
||||||
|
module->rename(it.first, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return proposed_cell_names.size() + proposed_wire_names.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AutonamePass : public Pass {
|
||||||
|
AutonamePass() : Pass("autoname", "automatically assign names to objects") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" autoname [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Assign auto-generated public names to objects with private names (the ones\n");
|
||||||
|
log("with $-prefix).\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] == "-foo") {
|
||||||
|
// foo = true;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_header(design, "Executing AUTONAME pass.\n");
|
||||||
|
|
||||||
|
for (auto module : design->selected_modules())
|
||||||
|
{
|
||||||
|
int count = 0, iter = 0;
|
||||||
|
while (1) {
|
||||||
|
iter++;
|
||||||
|
int n = autoname_worker(module);
|
||||||
|
if (!n) break;
|
||||||
|
count += n;
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
log("Renamed %d objects in module %s (%d iterations).\n", count, log_id(module), iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} AutonamePass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -41,14 +41,24 @@ struct CheckPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" - used wires that do not have a driver\n");
|
log(" - used wires that do not have a driver\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("When called with -noinit then this command also checks for wires which have\n");
|
log("Options:\n");
|
||||||
log("the 'init' attribute set.\n");
|
|
||||||
log("\n");
|
log("\n");
|
||||||
log("When called with -initdrv then this command also checks for wires which have\n");
|
log(" -noinit\n");
|
||||||
log("the 'init' attribute set and aren't driven by a FF cell type.\n");
|
log(" Also check for wires which have the 'init' attribute set.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("When called with -assert then the command will produce an error if any\n");
|
log(" -initdrv\n");
|
||||||
log("problems are found in the current design.\n");
|
log(" Also check for wires that have the 'init' attribute set and are not\n");
|
||||||
|
log(" driven by an FF cell type.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -mapped\n");
|
||||||
|
log(" Also check for internal cells that have not been mapped to cells of the\n");
|
||||||
|
log(" target architecture.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -allow-tbuf\n");
|
||||||
|
log(" Modify the -mapped behavior to still allow $_TBUF_ cells.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -assert\n");
|
||||||
|
log(" Produce a runtime error if any problems are found in the current design.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -56,6 +66,8 @@ struct CheckPass : public Pass {
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
bool noinit = false;
|
bool noinit = false;
|
||||||
bool initdrv = false;
|
bool initdrv = false;
|
||||||
|
bool mapped = false;
|
||||||
|
bool allow_tbuf = false;
|
||||||
bool assert_mode = false;
|
bool assert_mode = false;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
|
@ -68,6 +80,14 @@ struct CheckPass : public Pass {
|
||||||
initdrv = true;
|
initdrv = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-mapped") {
|
||||||
|
mapped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-allow-tbuf") {
|
||||||
|
allow_tbuf = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-assert") {
|
if (args[argidx] == "-assert") {
|
||||||
assert_mode = true;
|
assert_mode = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -135,6 +155,13 @@ struct CheckPass : public Pass {
|
||||||
TopoSort<string> topo;
|
TopoSort<string> topo;
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
|
{
|
||||||
|
if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) {
|
||||||
|
if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed;
|
||||||
|
log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||||
|
counter++;
|
||||||
|
cell_allowed:;
|
||||||
|
}
|
||||||
for (auto &conn : cell->connections()) {
|
for (auto &conn : cell->connections()) {
|
||||||
SigSpec sig = sigmap(conn.second);
|
SigSpec sig = sigmap(conn.second);
|
||||||
bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
|
bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
|
||||||
|
@ -159,6 +186,7 @@ struct CheckPass : public Pass {
|
||||||
for (auto bit : sig)
|
for (auto bit : sig)
|
||||||
if (bit.wire) wire_drivers_count[bit]++;
|
if (bit.wire) wire_drivers_count[bit]++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pool<SigBit> init_bits;
|
pool<SigBit> init_bits;
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ struct EquivOptPass:public ScriptPass
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" equiv_opt [options] [command]\n");
|
log(" equiv_opt [options] [command]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("This command checks circuit equivalence before and after an optimization pass.\n");
|
log("This command uses temporal induction to check circuit equivalence before and\n");
|
||||||
|
log("after an optimization pass.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -run <from_label>:<to_label>\n");
|
log(" -run <from_label>:<to_label>\n");
|
||||||
log(" only run the commands between the labels (see below). an empty\n");
|
log(" only run the commands between the labels (see below). an empty\n");
|
||||||
|
@ -49,6 +50,9 @@ struct EquivOptPass:public ScriptPass
|
||||||
log(" -multiclock\n");
|
log(" -multiclock\n");
|
||||||
log(" run clk2fflogic before equivalence checking.\n");
|
log(" run clk2fflogic before equivalence checking.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -async2sync\n");
|
||||||
|
log(" run async2sync before equivalence checking.\n");
|
||||||
|
log("\n");
|
||||||
log(" -undef\n");
|
log(" -undef\n");
|
||||||
log(" enable modelling of undef states during equiv_induct.\n");
|
log(" enable modelling of undef states during equiv_induct.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -58,7 +62,7 @@ struct EquivOptPass:public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string command, techmap_opts;
|
std::string command, techmap_opts;
|
||||||
bool assert, undef, multiclock;
|
bool assert, undef, multiclock, async2sync;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -67,6 +71,7 @@ struct EquivOptPass:public ScriptPass
|
||||||
assert = false;
|
assert = false;
|
||||||
undef = false;
|
undef = false;
|
||||||
multiclock = false;
|
multiclock = false;
|
||||||
|
async2sync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
|
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
|
||||||
|
@ -100,6 +105,10 @@ struct EquivOptPass:public ScriptPass
|
||||||
multiclock = true;
|
multiclock = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-async2sync") {
|
||||||
|
async2sync = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +128,9 @@ struct EquivOptPass:public ScriptPass
|
||||||
if (!design->full_selection())
|
if (!design->full_selection())
|
||||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||||
|
|
||||||
|
if (async2sync && multiclock)
|
||||||
|
log_cmd_error("The '-async2sync' and '-multiclock' options are mutually exclusive!\n");
|
||||||
|
|
||||||
log_header(design, "Executing EQUIV_OPT pass.\n");
|
log_header(design, "Executing EQUIV_OPT pass.\n");
|
||||||
log_push();
|
log_push();
|
||||||
|
|
||||||
|
@ -156,6 +168,8 @@ struct EquivOptPass:public ScriptPass
|
||||||
if (check_label("prove")) {
|
if (check_label("prove")) {
|
||||||
if (multiclock || help_mode)
|
if (multiclock || help_mode)
|
||||||
run("clk2fflogic", "(only with -multiclock)");
|
run("clk2fflogic", "(only with -multiclock)");
|
||||||
|
if (async2sync || help_mode)
|
||||||
|
run("async2sync", " (only with -async2sync)");
|
||||||
run("equiv_make gold gate equiv");
|
run("equiv_make gold gate equiv");
|
||||||
if (help_mode)
|
if (help_mode)
|
||||||
run("equiv_induct [-undef] equiv");
|
run("equiv_induct [-undef] equiv");
|
||||||
|
|
|
@ -158,22 +158,25 @@ static void detect_fsm(RTLIL::Wire *wire)
|
||||||
std::set<sig2driver_entry_t> cellport_list;
|
std::set<sig2driver_entry_t> cellport_list;
|
||||||
sig2user.find(sig_q, cellport_list);
|
sig2user.find(sig_q, cellport_list);
|
||||||
|
|
||||||
|
auto sig_q_bits = sig_q.to_sigbit_pool();
|
||||||
|
|
||||||
for (auto &cellport : cellport_list)
|
for (auto &cellport : cellport_list)
|
||||||
{
|
{
|
||||||
RTLIL::Cell *cell = cellport.first;
|
RTLIL::Cell *cell = cellport.first;
|
||||||
bool set_output = false, clr_output = false;
|
bool set_output = false, clr_output = false;
|
||||||
|
|
||||||
if (cell->type == "$ne")
|
if (cell->type.in("$ne", "$reduce_or", "$reduce_bool"))
|
||||||
set_output = true;
|
set_output = true;
|
||||||
|
|
||||||
if (cell->type == "$eq")
|
if (cell->type.in("$eq", "$logic_not", "$reduce_and"))
|
||||||
clr_output = true;
|
clr_output = true;
|
||||||
|
|
||||||
if (!set_output && !clr_output) {
|
if (set_output || clr_output) {
|
||||||
clr_output = true;
|
|
||||||
for (auto &port_it : cell->connections())
|
for (auto &port_it : cell->connections())
|
||||||
if (port_it.first != "\\A" || port_it.first != "\\Y")
|
if (cell->input(port_it.first))
|
||||||
clr_output = false;
|
for (auto bit : assign_map(port_it.second))
|
||||||
|
if (bit.wire != nullptr && !sig_q_bits.count(bit))
|
||||||
|
goto next_cellport;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_output || clr_output) {
|
if (set_output || clr_output) {
|
||||||
|
@ -184,6 +187,7 @@ static void detect_fsm(RTLIL::Wire *wire)
|
||||||
ce.set(sig, val);
|
ce.set(sig, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
next_cellport:;
|
||||||
}
|
}
|
||||||
|
|
||||||
SigSpec sig_y = sig_d, sig_undef;
|
SigSpec sig_y = sig_d, sig_undef;
|
||||||
|
|
|
@ -35,6 +35,7 @@ struct MemoryPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log("This pass calls all the other memory_* passes in a useful order:\n");
|
log("This pass calls all the other memory_* passes in a useful order:\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" opt_mem\n");
|
||||||
log(" memory_dff [-nordff] (-memx implies -nordff)\n");
|
log(" memory_dff [-nordff] (-memx implies -nordff)\n");
|
||||||
log(" opt_clean\n");
|
log(" opt_clean\n");
|
||||||
log(" memory_share\n");
|
log(" memory_share\n");
|
||||||
|
@ -81,6 +82,7 @@ struct MemoryPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
Pass::call(design, "opt_mem");
|
||||||
Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
|
Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
|
||||||
Pass::call(design, "opt_clean");
|
Pass::call(design, "opt_clean");
|
||||||
Pass::call(design, "memory_share");
|
Pass::call(design, "memory_share");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
OBJS += passes/opt/opt.o
|
OBJS += passes/opt/opt.o
|
||||||
OBJS += passes/opt/opt_merge.o
|
OBJS += passes/opt/opt_merge.o
|
||||||
|
OBJS += passes/opt/opt_mem.o
|
||||||
OBJS += passes/opt/opt_muxtree.o
|
OBJS += passes/opt/opt_muxtree.o
|
||||||
OBJS += passes/opt/opt_reduce.o
|
OBJS += passes/opt/opt_reduce.o
|
||||||
OBJS += passes/opt/opt_rmdff.o
|
OBJS += passes/opt/opt_rmdff.o
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct OptMemWorker
|
||||||
|
{
|
||||||
|
RTLIL::Design *design;
|
||||||
|
RTLIL::Module *module;
|
||||||
|
SigMap sigmap;
|
||||||
|
bool restart;
|
||||||
|
|
||||||
|
dict<IdString, vector<IdString>> memrd, memwr, meminit;
|
||||||
|
pool<IdString> remove_mem, remove_cells;
|
||||||
|
|
||||||
|
OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false)
|
||||||
|
{
|
||||||
|
for (auto &it : module->memories)
|
||||||
|
{
|
||||||
|
memrd[it.first];
|
||||||
|
memwr[it.first];
|
||||||
|
meminit[it.first];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell : module->cells())
|
||||||
|
{
|
||||||
|
if (cell->type == ID($memrd)) {
|
||||||
|
IdString id = cell->getParam(ID(MEMID)).decode_string();
|
||||||
|
memrd.at(id).push_back(cell->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($memwr)) {
|
||||||
|
IdString id = cell->getParam(ID(MEMID)).decode_string();
|
||||||
|
memwr.at(id).push_back(cell->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($meminit)) {
|
||||||
|
IdString id = cell->getParam(ID(MEMID)).decode_string();
|
||||||
|
meminit.at(id).push_back(cell->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~OptMemWorker()
|
||||||
|
{
|
||||||
|
for (auto it : remove_mem)
|
||||||
|
{
|
||||||
|
for (auto cell_name : memrd[it])
|
||||||
|
module->remove(module->cell(cell_name));
|
||||||
|
for (auto cell_name : memwr[it])
|
||||||
|
module->remove(module->cell(cell_name));
|
||||||
|
for (auto cell_name : meminit[it])
|
||||||
|
module->remove(module->cell(cell_name));
|
||||||
|
|
||||||
|
delete module->memories.at(it);
|
||||||
|
module->memories.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell_name : remove_cells)
|
||||||
|
module->remove(module->cell(cell_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
int run(RTLIL::Memory *mem)
|
||||||
|
{
|
||||||
|
if (restart || remove_mem.count(mem->name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) {
|
||||||
|
log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem));
|
||||||
|
remove_mem.insert(mem->name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptMemPass : public Pass {
|
||||||
|
OptMemPass() : Pass("opt_mem", "optimize memories") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" opt_mem [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("This pass performs various optimizations on memories in the design.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Executing OPT_MEM pass (optimize memories).\n");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
// if (args[argidx] == "-nomux") {
|
||||||
|
// mode_nomux = true;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
int total_count = 0;
|
||||||
|
for (auto module : design->selected_modules()) {
|
||||||
|
while (1) {
|
||||||
|
int cnt = 0;
|
||||||
|
OptMemWorker worker(module);
|
||||||
|
for (auto &it : module->memories)
|
||||||
|
if (module->selected(it.second))
|
||||||
|
cnt += worker.run(it.second);
|
||||||
|
if (!cnt && !worker.restart)
|
||||||
|
break;
|
||||||
|
total_count += cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_count)
|
||||||
|
design->scratchpad_set_bool("opt.did_something", true);
|
||||||
|
log("Performed a total of %d transformations.\n", total_count);
|
||||||
|
}
|
||||||
|
} OptMemPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -143,13 +143,18 @@ struct WreduceWorker
|
||||||
|
|
||||||
SigSpec sig_d = mi.sigmap(cell->getPort(ID(D)));
|
SigSpec sig_d = mi.sigmap(cell->getPort(ID(D)));
|
||||||
SigSpec sig_q = mi.sigmap(cell->getPort(ID(Q)));
|
SigSpec sig_q = mi.sigmap(cell->getPort(ID(Q)));
|
||||||
Const initval;
|
bool is_adff = (cell->type == ID($adff));
|
||||||
|
Const initval, arst_value;
|
||||||
|
|
||||||
int width_before = GetSize(sig_q);
|
int width_before = GetSize(sig_q);
|
||||||
|
|
||||||
if (width_before == 0)
|
if (width_before == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (cell->parameters.count(ID(ARST_VALUE))) {
|
||||||
|
arst_value = cell->parameters[ID(ARST_VALUE)];
|
||||||
|
}
|
||||||
|
|
||||||
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||||
bool sign_ext = !zero_ext;
|
bool sign_ext = !zero_ext;
|
||||||
|
|
||||||
|
@ -163,7 +168,8 @@ struct WreduceWorker
|
||||||
|
|
||||||
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
|
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) &&
|
||||||
|
(!is_adff || i >= GetSize(arst_value) || arst_value[i] == State::S0 || arst_value[i] == State::Sx)) {
|
||||||
module->connect(sig_q[i], State::S0);
|
module->connect(sig_q[i], State::S0);
|
||||||
remove_init_bits.insert(sig_q[i]);
|
remove_init_bits.insert(sig_q[i]);
|
||||||
sig_d.remove(i);
|
sig_d.remove(i);
|
||||||
|
@ -171,7 +177,8 @@ struct WreduceWorker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
|
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] &&
|
||||||
|
(!is_adff || i >= GetSize(arst_value) || arst_value[i] == arst_value[i-1])) {
|
||||||
module->connect(sig_q[i], sig_q[i-1]);
|
module->connect(sig_q[i], sig_q[i-1]);
|
||||||
remove_init_bits.insert(sig_q[i]);
|
remove_init_bits.insert(sig_q[i]);
|
||||||
sig_d.remove(i);
|
sig_d.remove(i);
|
||||||
|
@ -214,7 +221,6 @@ struct WreduceWorker
|
||||||
|
|
||||||
// Narrow ARST_VALUE parameter to new size.
|
// Narrow ARST_VALUE parameter to new size.
|
||||||
if (cell->parameters.count(ID(ARST_VALUE))) {
|
if (cell->parameters.count(ID(ARST_VALUE))) {
|
||||||
Const arst_value = cell->getParam(ID(ARST_VALUE));
|
|
||||||
arst_value.bits.resize(GetSize(sig_q));
|
arst_value.bits.resize(GetSize(sig_q));
|
||||||
cell->setParam(ID(ARST_VALUE), arst_value);
|
cell->setParam(ID(ARST_VALUE), arst_value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%_pm.h: passes/pmgen/pmgen.py %.pmg
|
%_pm.h: passes/pmgen/pmgen.py %.pmg
|
||||||
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|
||||||
|
@ -21,6 +21,14 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|
||||||
|
OBJS += passes/pmgen/xilinx_dsp.o
|
||||||
|
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||||
|
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
|
||||||
|
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
|
||||||
|
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
|
||||||
|
|
||||||
|
# --------------------------------------
|
||||||
|
|
||||||
OBJS += passes/pmgen/peepopt.o
|
OBJS += passes/pmgen/peepopt.o
|
||||||
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
|
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
|
||||||
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
|
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
|
||||||
|
@ -30,7 +38,7 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
|
PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
|
||||||
|
|
||||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||||
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
|
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PMGEN_GENERATE
|
||||||
|
#define PMGEN_GENERATE
|
||||||
|
|
||||||
|
#define GENERATE_PATTERN(pmclass, pattern) \
|
||||||
|
generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)
|
||||||
|
|
||||||
|
void pmtest_addports(Module *module)
|
||||||
|
{
|
||||||
|
pool<SigBit> driven_bits, used_bits;
|
||||||
|
SigMap sigmap(module);
|
||||||
|
int icnt = 0, ocnt = 0;
|
||||||
|
|
||||||
|
for (auto cell : module->cells())
|
||||||
|
for (auto conn : cell->connections())
|
||||||
|
{
|
||||||
|
if (cell->input(conn.first))
|
||||||
|
for (auto bit : sigmap(conn.second))
|
||||||
|
used_bits.insert(bit);
|
||||||
|
if (cell->output(conn.first))
|
||||||
|
for (auto bit : sigmap(conn.second))
|
||||||
|
driven_bits.insert(bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wire : vector<Wire*>(module->wires()))
|
||||||
|
{
|
||||||
|
SigSpec ibits, obits;
|
||||||
|
for (auto bit : sigmap(wire)) {
|
||||||
|
if (!used_bits.count(bit))
|
||||||
|
obits.append(bit);
|
||||||
|
if (!driven_bits.count(bit))
|
||||||
|
ibits.append(bit);
|
||||||
|
}
|
||||||
|
if (!ibits.empty()) {
|
||||||
|
Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
|
||||||
|
w->port_input = true;
|
||||||
|
module->connect(ibits, w);
|
||||||
|
}
|
||||||
|
if (!obits.empty()) {
|
||||||
|
Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
|
||||||
|
w->port_output = true;
|
||||||
|
module->connect(w, obits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module->fixup_ports();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class pm>
|
||||||
|
void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
|
||||||
|
{
|
||||||
|
log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
|
||||||
|
|
||||||
|
int modcnt = 0;
|
||||||
|
int maxmodcnt = 100;
|
||||||
|
int maxsubcnt = 4;
|
||||||
|
int timeout = 0;
|
||||||
|
vector<Module*> mods;
|
||||||
|
|
||||||
|
while (modcnt < maxmodcnt)
|
||||||
|
{
|
||||||
|
int submodcnt = 0, itercnt = 0, cellcnt = 0;
|
||||||
|
Module *mod = design->addModule(NEW_ID);
|
||||||
|
|
||||||
|
while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
|
||||||
|
{
|
||||||
|
if (timeout++ > 10000)
|
||||||
|
log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
|
||||||
|
|
||||||
|
pm matcher(mod, mod->cells());
|
||||||
|
|
||||||
|
matcher.rng(1);
|
||||||
|
matcher.rngseed += modcnt;
|
||||||
|
matcher.rng(1);
|
||||||
|
matcher.rngseed += submodcnt;
|
||||||
|
matcher.rng(1);
|
||||||
|
matcher.rngseed += itercnt;
|
||||||
|
matcher.rng(1);
|
||||||
|
matcher.rngseed += cellcnt;
|
||||||
|
matcher.rng(1);
|
||||||
|
|
||||||
|
if (GetSize(mod->cells()) != cellcnt)
|
||||||
|
{
|
||||||
|
bool found_match = false;
|
||||||
|
run(matcher, [&](){ found_match = true; });
|
||||||
|
cellcnt = GetSize(mod->cells());
|
||||||
|
|
||||||
|
if (found_match) {
|
||||||
|
Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
|
||||||
|
pmclass, pattern, modcnt++));
|
||||||
|
log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
|
||||||
|
mod->cloneInto(m);
|
||||||
|
pmtest_addports(m);
|
||||||
|
mods.push_back(m);
|
||||||
|
submodcnt++;
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher.generate_mode = true;
|
||||||
|
run(matcher, [](){});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submodcnt && maxsubcnt < (1 << 16))
|
||||||
|
maxsubcnt *= 2;
|
||||||
|
|
||||||
|
design->remove(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
|
||||||
|
log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
|
||||||
|
for (auto mod : mods) {
|
||||||
|
Cell *c = m->addCell(mod->name, mod->name);
|
||||||
|
for (auto port : mod->ports) {
|
||||||
|
Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
|
||||||
|
c->setPort(port, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pmtest_addports(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -29,19 +29,19 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||||
{
|
{
|
||||||
auto &st = pm.st_ice40_dsp;
|
auto &st = pm.st_ice40_dsp;
|
||||||
|
|
||||||
#if 0
|
|
||||||
log("\n");
|
|
||||||
log("ffA: %s\n", log_id(st.ffA, "--"));
|
|
||||||
log("ffB: %s\n", log_id(st.ffB, "--"));
|
|
||||||
log("mul: %s\n", log_id(st.mul, "--"));
|
|
||||||
log("ffY: %s\n", log_id(st.ffY, "--"));
|
|
||||||
log("addAB: %s\n", log_id(st.addAB, "--"));
|
|
||||||
log("muxAB: %s\n", log_id(st.muxAB, "--"));
|
|
||||||
log("ffS: %s\n", log_id(st.ffS, "--"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
|
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
|
||||||
|
|
||||||
|
log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
|
||||||
|
log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
|
||||||
|
log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
|
||||||
|
log_debug("mul: %s\n", log_id(st.mul, "--"));
|
||||||
|
log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
|
||||||
|
log_debug("ffH: %s\n", log_id(st.ffH, "--"));
|
||||||
|
log_debug("add: %s\n", log_id(st.add, "--"));
|
||||||
|
log_debug("mux: %s\n", log_id(st.mux, "--"));
|
||||||
|
log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
|
||||||
|
log_debug("\n");
|
||||||
|
|
||||||
if (GetSize(st.sigA) > 16) {
|
if (GetSize(st.sigA) > 16) {
|
||||||
log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
|
log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
|
||||||
return;
|
return;
|
||||||
|
@ -52,59 +52,85 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetSize(st.sigS) > 32) {
|
if (GetSize(st.sigO) > 33) {
|
||||||
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
|
log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetSize(st.sigY) > 32) {
|
if (GetSize(st.sigH) > 32) {
|
||||||
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
|
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
|
Cell *cell = st.mul;
|
||||||
|
if (cell->type == ID($mul)) {
|
||||||
|
log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type));
|
||||||
|
|
||||||
log(" replacing $mul with SB_MAC16 cell.\n");
|
cell = pm.module->addCell(NEW_ID, ID(SB_MAC16));
|
||||||
|
|
||||||
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
|
||||||
pm.module->swap_names(cell, st.mul);
|
pm.module->swap_names(cell, st.mul);
|
||||||
|
}
|
||||||
|
else log_assert(cell->type == ID(SB_MAC16));
|
||||||
|
|
||||||
// SB_MAC16 Input Interface
|
// SB_MAC16 Input Interface
|
||||||
|
|
||||||
SigSpec A = st.sigA;
|
SigSpec A = st.sigA;
|
||||||
A.extend_u0(16, mul_signed);
|
A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||||
|
log_assert(GetSize(A) == 16);
|
||||||
|
|
||||||
SigSpec B = st.sigB;
|
SigSpec B = st.sigB;
|
||||||
B.extend_u0(16, mul_signed);
|
B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||||
|
log_assert(GetSize(B) == 16);
|
||||||
|
|
||||||
SigSpec CD;
|
SigSpec CD = st.sigCD;
|
||||||
if (st.muxA)
|
if (CD.empty())
|
||||||
CD = st.muxA->getPort("\\B");
|
CD = RTLIL::Const(0, 32);
|
||||||
if (st.muxB)
|
else
|
||||||
CD = st.muxB->getPort("\\A");
|
log_assert(GetSize(CD) == 32);
|
||||||
CD.extend_u0(32, mul_signed);
|
|
||||||
|
|
||||||
cell->setPort("\\A", A);
|
cell->setPort(ID::A, A);
|
||||||
cell->setPort("\\B", B);
|
cell->setPort(ID::B, B);
|
||||||
cell->setPort("\\C", CD.extract(0, 16));
|
cell->setPort(ID(C), CD.extract(16, 16));
|
||||||
cell->setPort("\\D", CD.extract(16, 16));
|
cell->setPort(ID(D), CD.extract(0, 16));
|
||||||
|
|
||||||
cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
|
cell->setParam(ID(A_REG), st.ffA ? State::S1 : State::S0);
|
||||||
cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
|
cell->setParam(ID(B_REG), st.ffB ? State::S1 : State::S0);
|
||||||
|
cell->setParam(ID(C_REG), st.ffCD ? State::S1 : State::S0);
|
||||||
|
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
|
||||||
|
|
||||||
cell->setPort("\\AHOLD", State::S0);
|
SigSpec AHOLD, BHOLD, CDHOLD;
|
||||||
cell->setPort("\\BHOLD", State::S0);
|
if (st.ffAholdmux)
|
||||||
cell->setPort("\\CHOLD", State::S0);
|
AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S)));
|
||||||
cell->setPort("\\DHOLD", State::S0);
|
else
|
||||||
|
AHOLD = State::S0;
|
||||||
|
if (st.ffBholdmux)
|
||||||
|
BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
BHOLD = State::S0;
|
||||||
|
if (st.ffCDholdmux)
|
||||||
|
CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
CDHOLD = State::S0;
|
||||||
|
cell->setPort(ID(AHOLD), AHOLD);
|
||||||
|
cell->setPort(ID(BHOLD), BHOLD);
|
||||||
|
cell->setPort(ID(CHOLD), CDHOLD);
|
||||||
|
cell->setPort(ID(DHOLD), CDHOLD);
|
||||||
|
|
||||||
cell->setPort("\\IRSTTOP", State::S0);
|
SigSpec IRSTTOP, IRSTBOT;
|
||||||
cell->setPort("\\IRSTBOT", State::S0);
|
if (st.ffArstmux)
|
||||||
|
IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
IRSTTOP = State::S0;
|
||||||
|
if (st.ffBrstmux)
|
||||||
|
IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
IRSTBOT = State::S0;
|
||||||
|
cell->setPort(ID(IRSTTOP), IRSTTOP);
|
||||||
|
cell->setPort(ID(IRSTBOT), IRSTBOT);
|
||||||
|
|
||||||
if (st.clock_vld)
|
if (st.clock != SigBit())
|
||||||
{
|
{
|
||||||
cell->setPort("\\CLK", st.clock);
|
cell->setPort(ID(CLK), st.clock);
|
||||||
cell->setPort("\\CE", State::S1);
|
cell->setPort(ID(CE), State::S1);
|
||||||
cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
|
cell->setParam(ID(NEG_TRIGGER), st.clock_pol ? State::S0 : State::S1);
|
||||||
|
|
||||||
log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
|
log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
|
||||||
|
|
||||||
|
@ -114,91 +140,137 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||||
if (st.ffB)
|
if (st.ffB)
|
||||||
log(" ffB:%s", log_id(st.ffB));
|
log(" ffB:%s", log_id(st.ffB));
|
||||||
|
|
||||||
if (st.ffY)
|
if (st.ffCD)
|
||||||
log(" ffY:%s", log_id(st.ffY));
|
log(" ffCD:%s", log_id(st.ffCD));
|
||||||
|
|
||||||
if (st.ffS)
|
if (st.ffFJKG)
|
||||||
log(" ffS:%s", log_id(st.ffS));
|
log(" ffFJKG:%s", log_id(st.ffFJKG));
|
||||||
|
|
||||||
|
if (st.ffH)
|
||||||
|
log(" ffH:%s", log_id(st.ffH));
|
||||||
|
|
||||||
|
if (st.ffO)
|
||||||
|
log(" ffO:%s", log_id(st.ffO));
|
||||||
|
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell->setPort("\\CLK", State::S0);
|
cell->setPort(ID(CLK), State::S0);
|
||||||
cell->setPort("\\CE", State::S0);
|
cell->setPort(ID(CE), State::S0);
|
||||||
cell->setParam("\\NEG_TRIGGER", State::S0);
|
cell->setParam(ID(NEG_TRIGGER), State::S0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SB_MAC16 Cascade Interface
|
// SB_MAC16 Cascade Interface
|
||||||
|
|
||||||
cell->setPort("\\SIGNEXTIN", State::Sx);
|
cell->setPort(ID(SIGNEXTIN), State::Sx);
|
||||||
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID));
|
||||||
|
|
||||||
cell->setPort("\\CI", State::Sx);
|
cell->setPort(ID(CI), State::Sx);
|
||||||
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
|
||||||
|
|
||||||
cell->setPort("\\ACCUMCI", State::Sx);
|
cell->setPort(ID(ACCUMCI), State::Sx);
|
||||||
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID));
|
||||||
|
|
||||||
// SB_MAC16 Output Interface
|
// SB_MAC16 Output Interface
|
||||||
|
|
||||||
SigSpec O = st.ffS ? st.sigS : st.sigY;
|
SigSpec O = st.sigO;
|
||||||
|
int O_width = GetSize(O);
|
||||||
|
if (O_width == 33) {
|
||||||
|
log_assert(st.add);
|
||||||
|
// If we have a signed multiply-add, then perform sign extension
|
||||||
|
if (st.add->getParam(ID(A_SIGNED)).as_bool() && st.add->getParam(ID(B_SIGNED)).as_bool())
|
||||||
|
pm.module->connect(O[32], O[31]);
|
||||||
|
else
|
||||||
|
cell->setPort(ID(CO), O[32]);
|
||||||
|
O.remove(O_width-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cell->setPort(ID(CO), pm.module->addWire(NEW_ID));
|
||||||
|
log_assert(GetSize(O) <= 32);
|
||||||
if (GetSize(O) < 32)
|
if (GetSize(O) < 32)
|
||||||
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||||
|
|
||||||
cell->setPort("\\O", O);
|
cell->setPort(ID(O), O);
|
||||||
|
|
||||||
if (st.addAB) {
|
bool accum = false;
|
||||||
log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
|
if (st.add) {
|
||||||
cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
|
accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO);
|
||||||
cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
|
if (accum)
|
||||||
|
log(" accumulator %s (%s)\n", log_id(st.add), log_id(st.add->type));
|
||||||
|
else
|
||||||
|
log(" adder %s (%s)\n", log_id(st.add), log_id(st.add->type));
|
||||||
|
cell->setPort(ID(ADDSUBTOP), st.add->type == ID($add) ? State::S0 : State::S1);
|
||||||
|
cell->setPort(ID(ADDSUBBOT), st.add->type == ID($add) ? State::S0 : State::S1);
|
||||||
} else {
|
} else {
|
||||||
cell->setPort("\\ADDSUBTOP", State::S0);
|
cell->setPort(ID(ADDSUBTOP), State::S0);
|
||||||
cell->setPort("\\ADDSUBBOT", State::S0);
|
cell->setPort(ID(ADDSUBBOT), State::S0);
|
||||||
}
|
}
|
||||||
|
|
||||||
cell->setPort("\\ORSTTOP", State::S0);
|
SigSpec OHOLD;
|
||||||
cell->setPort("\\ORSTBOT", State::S0);
|
if (st.ffOholdmux)
|
||||||
|
OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
OHOLD = State::S0;
|
||||||
|
cell->setPort(ID(OHOLDTOP), OHOLD);
|
||||||
|
cell->setPort(ID(OHOLDBOT), OHOLD);
|
||||||
|
|
||||||
cell->setPort("\\OHOLDTOP", State::S0);
|
SigSpec ORST;
|
||||||
cell->setPort("\\OHOLDBOT", State::S0);
|
if (st.ffOrstmux)
|
||||||
|
ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID(S)));
|
||||||
|
else
|
||||||
|
ORST = State::S0;
|
||||||
|
cell->setPort(ID(ORSTTOP), ORST);
|
||||||
|
cell->setPort(ID(ORSTBOT), ORST);
|
||||||
|
|
||||||
SigSpec acc_reset = State::S0;
|
SigSpec acc_reset = State::S0;
|
||||||
if (st.muxA)
|
if (st.mux) {
|
||||||
acc_reset = st.muxA->getPort("\\S");
|
if (st.muxAB == ID::A)
|
||||||
if (st.muxB)
|
acc_reset = st.mux->getPort(ID(S));
|
||||||
acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
|
else
|
||||||
|
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S)));
|
||||||
cell->setPort("\\OLOADTOP", acc_reset);
|
}
|
||||||
cell->setPort("\\OLOADBOT", acc_reset);
|
cell->setPort(ID(OLOADTOP), acc_reset);
|
||||||
|
cell->setPort(ID(OLOADBOT), acc_reset);
|
||||||
|
|
||||||
// SB_MAC16 Remaining Parameters
|
// SB_MAC16 Remaining Parameters
|
||||||
|
|
||||||
cell->setParam("\\C_REG", State::S0);
|
cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
|
||||||
cell->setParam("\\D_REG", State::S0);
|
cell->setParam(ID(BOT_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
|
||||||
|
cell->setParam(ID(PIPELINE_16x16_MULT_REG1), st.ffFJKG ? State::S1 : State::S0);
|
||||||
|
cell->setParam(ID(PIPELINE_16x16_MULT_REG2), st.ffH ? State::S1 : State::S0);
|
||||||
|
|
||||||
cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
|
cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2));
|
||||||
cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
|
cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
|
||||||
cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
|
cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2));
|
||||||
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
|
||||||
|
|
||||||
cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
|
cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2));
|
||||||
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
|
||||||
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
|
||||||
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
|
||||||
|
|
||||||
cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
|
cell->setParam(ID(MODE_8x8), State::S0);
|
||||||
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||||
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||||
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
|
||||||
|
|
||||||
cell->setParam("\\MODE_8x8", State::S0);
|
if (st.ffO) {
|
||||||
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
if (st.o_lo)
|
||||||
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||||
|
else
|
||||||
|
cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2));
|
||||||
|
|
||||||
|
st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O)));
|
||||||
|
cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||||
|
cell->setParam(ID(BOTOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell != st.mul)
|
||||||
pm.autoremove(st.mul);
|
pm.autoremove(st.mul);
|
||||||
pm.autoremove(st.ffY);
|
else
|
||||||
pm.autoremove(st.ffS);
|
pm.blacklist(st.mul);
|
||||||
|
pm.autoremove(st.ffFJKG);
|
||||||
|
pm.autoremove(st.add);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Ice40DspPass : public Pass {
|
struct Ice40DspPass : public Pass {
|
||||||
|
@ -209,7 +281,17 @@ struct Ice40DspPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" ice40_dsp [options] [selection]\n");
|
log(" ice40_dsp [options] [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
log("Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n");
|
||||||
|
log("cells into iCE40 DSP resources.\n");
|
||||||
|
log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n");
|
||||||
|
log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n");
|
||||||
|
log("optional hold), and post-adder into into the SB_MAC16 resource.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n");
|
||||||
|
log("input will be folded into the DSP. In this scenario only, resetting the\n");
|
||||||
|
log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
|
|
@ -1,163 +1,574 @@
|
||||||
pattern ice40_dsp
|
pattern ice40_dsp
|
||||||
|
|
||||||
state <SigBit> clock
|
state <SigBit> clock
|
||||||
state <bool> clock_pol clock_vld
|
state <bool> clock_pol cd_signed o_lo
|
||||||
state <SigSpec> sigA sigB sigY sigS
|
state <SigSpec> sigA sigB sigCD sigH sigO
|
||||||
state <Cell*> addAB muxAB
|
state <Cell*> add mux
|
||||||
|
state <IdString> addAB muxAB
|
||||||
|
|
||||||
|
state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
|
||||||
|
state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
|
||||||
|
|
||||||
|
state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
|
||||||
|
state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
|
||||||
|
|
||||||
|
// subpattern
|
||||||
|
state <SigSpec> argQ argD
|
||||||
|
state <bool> ffholdpol ffrstpol
|
||||||
|
state <int> ffoffset
|
||||||
|
udata <SigSpec> dffD dffQ
|
||||||
|
udata <SigBit> dffclock
|
||||||
|
udata <Cell*> dff dffholdmux dffrstmux
|
||||||
|
udata <bool> dffholdpol dffrstpol dffclock_pol
|
||||||
|
|
||||||
match mul
|
match mul
|
||||||
select mul->type.in($mul)
|
select mul->type.in($mul, \SB_MAC16)
|
||||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||||
select GetSize(mul->getPort(\Y)) > 10
|
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
match ffA
|
code sigA sigB sigH
|
||||||
select ffA->type.in($dff)
|
auto unextend = [](const SigSpec &sig) {
|
||||||
// select nusers(port(ffA, \Q)) == 2
|
int i;
|
||||||
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
optional
|
if (sig[i] != sig[i-1])
|
||||||
endmatch
|
break;
|
||||||
|
// Do not remove non-const sign bit
|
||||||
|
if (sig[i].wire)
|
||||||
|
++i;
|
||||||
|
return sig.extract(0, i);
|
||||||
|
};
|
||||||
|
sigA = unextend(port(mul, \A));
|
||||||
|
sigB = unextend(port(mul, \B));
|
||||||
|
|
||||||
code sigA clock clock_pol clock_vld
|
SigSpec O;
|
||||||
sigA = port(mul, \A);
|
if (mul->type == $mul)
|
||||||
|
O = mul->getPort(\Y);
|
||||||
if (ffA) {
|
else if (mul->type == \SB_MAC16)
|
||||||
sigA = port(ffA, \D);
|
O = mul->getPort(\O);
|
||||||
|
else log_abort();
|
||||||
clock = port(ffA, \CLK).as_bit();
|
if (GetSize(O) <= 10)
|
||||||
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
|
||||||
clock_vld = true;
|
|
||||||
}
|
|
||||||
endcode
|
|
||||||
|
|
||||||
match ffB
|
|
||||||
select ffB->type.in($dff)
|
|
||||||
// select nusers(port(ffB, \Q)) == 2
|
|
||||||
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
|
||||||
optional
|
|
||||||
endmatch
|
|
||||||
|
|
||||||
code sigB clock clock_pol clock_vld
|
|
||||||
sigB = port(mul, \B);
|
|
||||||
|
|
||||||
if (ffB) {
|
|
||||||
sigB = port(ffB, \D);
|
|
||||||
SigBit c = port(ffB, \CLK).as_bit();
|
|
||||||
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
|
||||||
|
|
||||||
if (clock_vld && (c != clock || cp != clock_pol))
|
|
||||||
reject;
|
reject;
|
||||||
|
|
||||||
clock = c;
|
// Only care about those bits that are used
|
||||||
clock_pol = cp;
|
int i;
|
||||||
clock_vld = true;
|
for (i = 0; i < GetSize(O); i++) {
|
||||||
|
if (nusers(O[i]) <= 1)
|
||||||
|
break;
|
||||||
|
sigH.append(O[i]);
|
||||||
|
}
|
||||||
|
log_assert(nusers(O.extract_end(i)) <= 1);
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
||||||
|
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
|
||||||
|
argQ = sigA;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffA = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
clock_pol = dffclock_pol;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffArstmux = dffrstmux;
|
||||||
|
ffArstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffholdmux) {
|
||||||
|
ffAholdmux = dffholdmux;
|
||||||
|
ffAholdpol = dffholdpol;
|
||||||
|
}
|
||||||
|
sigA = dffD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match ffY
|
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
|
||||||
select ffY->type.in($dff)
|
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
|
||||||
select nusers(port(ffY, \D)) == 2
|
argQ = sigB;
|
||||||
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
subpattern(in_dffe);
|
||||||
optional
|
if (dff) {
|
||||||
endmatch
|
ffB = dff;
|
||||||
|
clock = dffclock;
|
||||||
code sigY clock clock_pol clock_vld
|
clock_pol = dffclock_pol;
|
||||||
sigY = port(mul, \Y);
|
if (dffrstmux) {
|
||||||
|
ffBrstmux = dffrstmux;
|
||||||
if (ffY) {
|
ffBrstpol = dffrstpol;
|
||||||
sigY = port(ffY, \Q);
|
}
|
||||||
SigBit c = port(ffY, \CLK).as_bit();
|
if (dffholdmux) {
|
||||||
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
ffBholdmux = dffholdmux;
|
||||||
|
ffBholdpol = dffholdpol;
|
||||||
if (clock_vld && (c != clock || cp != clock_pol))
|
}
|
||||||
reject;
|
sigB = dffD;
|
||||||
|
}
|
||||||
clock = c;
|
|
||||||
clock_pol = cp;
|
|
||||||
clock_vld = true;
|
|
||||||
}
|
}
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match addA
|
code argD ffFJKG sigH clock clock_pol
|
||||||
select addA->type.in($add)
|
if (nusers(sigH) == 2 &&
|
||||||
select nusers(port(addA, \A)) == 2
|
(mul->type != \SB_MAC16 ||
|
||||||
index <SigSpec> port(addA, \A) === sigY
|
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
|
||||||
|
argD = sigH;
|
||||||
|
subpattern(out_dffe);
|
||||||
|
if (dff) {
|
||||||
|
// F/J/K/G do not have a CE-like (hold) input
|
||||||
|
if (dffholdmux)
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
|
||||||
|
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
|
||||||
|
// shared with A and B
|
||||||
|
if ((ffArstmux != NULL) != (dffrstmux != NULL))
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
if (ffArstmux) {
|
||||||
|
if (port(ffArstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
if (ffArstpol != dffrstpol)
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
}
|
||||||
|
if (ffBrstmux) {
|
||||||
|
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
if (ffBrstpol != dffrstpol)
|
||||||
|
goto reject_ffFJKG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ffFJKG = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
clock_pol = dffclock_pol;
|
||||||
|
sigH = dffQ;
|
||||||
|
|
||||||
|
reject_ffFJKG: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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())) {
|
||||||
|
argD = sigH;
|
||||||
|
subpattern(out_dffe);
|
||||||
|
if (dff) {
|
||||||
|
// H does not have a CE-like (hold) input
|
||||||
|
if (dffholdmux)
|
||||||
|
goto reject_ffH;
|
||||||
|
|
||||||
|
// Reset signal of H (IRSTBOT) shared with B
|
||||||
|
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||||
|
goto reject_ffH;
|
||||||
|
if (ffBrstmux) {
|
||||||
|
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto reject_ffH;
|
||||||
|
if (ffBrstpol != dffrstpol)
|
||||||
|
goto reject_ffH;
|
||||||
|
}
|
||||||
|
|
||||||
|
ffH = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
clock_pol = dffclock_pol;
|
||||||
|
sigH = dffQ;
|
||||||
|
|
||||||
|
reject_ffH: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sigO = sigH;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match add
|
||||||
|
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
|
||||||
|
|
||||||
|
select add->type.in($add)
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
select nusers(port(add, AB)) == 2
|
||||||
|
|
||||||
|
index <SigBit> port(add, AB)[0] === sigH[0]
|
||||||
|
filter GetSize(port(add, AB)) <= GetSize(sigH)
|
||||||
|
filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
|
||||||
|
filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
|
||||||
|
set addAB AB
|
||||||
optional
|
optional
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
match addB
|
code sigCD sigO cd_signed
|
||||||
if !addA
|
if (add) {
|
||||||
select addB->type.in($add, $sub)
|
sigCD = port(add, addAB == \A ? \B : \A);
|
||||||
select nusers(port(addB, \B)) == 2
|
cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
|
||||||
index <SigSpec> port(addB, \B) === sigY
|
|
||||||
optional
|
|
||||||
endmatch
|
|
||||||
|
|
||||||
code addAB sigS
|
|
||||||
if (addA) {
|
|
||||||
addAB = addA;
|
|
||||||
sigS = port(addA, \B);
|
|
||||||
}
|
|
||||||
if (addB) {
|
|
||||||
addAB = addB;
|
|
||||||
sigS = port(addB, \A);
|
|
||||||
}
|
|
||||||
if (addAB) {
|
|
||||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||||
int actual_mul_width = GetSize(sigY);
|
int actual_mul_width = GetSize(sigH);
|
||||||
int actual_acc_width = GetSize(sigS);
|
int actual_acc_width = GetSize(sigCD);
|
||||||
|
|
||||||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||||
reject;
|
reject;
|
||||||
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
// If accumulator, check adder width and signedness
|
||||||
|
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
|
||||||
reject;
|
reject;
|
||||||
|
|
||||||
|
sigO = port(add, \Y);
|
||||||
}
|
}
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match muxA
|
match mux
|
||||||
if addAB
|
select mux->type == $mux
|
||||||
select muxA->type.in($mux)
|
choice <IdString> AB {\A, \B}
|
||||||
select nusers(port(muxA, \A)) == 2
|
select nusers(port(mux, AB)) == 2
|
||||||
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
index <SigSpec> port(mux, AB) === sigO
|
||||||
|
set muxAB AB
|
||||||
optional
|
optional
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
match muxB
|
code sigO
|
||||||
if addAB
|
if (mux)
|
||||||
if !muxA
|
sigO = port(mux, \Y);
|
||||||
select muxB->type.in($mux)
|
|
||||||
select nusers(port(muxB, \B)) == 2
|
|
||||||
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
|
||||||
optional
|
|
||||||
endmatch
|
|
||||||
|
|
||||||
code muxAB
|
|
||||||
muxAB = addAB;
|
|
||||||
if (muxA)
|
|
||||||
muxAB = muxA;
|
|
||||||
if (muxB)
|
|
||||||
muxAB = muxB;
|
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match ffS
|
code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
|
||||||
if muxAB
|
if (mul->type != \SB_MAC16 ||
|
||||||
select ffS->type.in($dff)
|
// Ensure that register is not already used
|
||||||
select nusers(port(ffS, \D)) == 2
|
((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) &&
|
||||||
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
// Ensure that OLOADTOP/OLOADBOT is unused or zero
|
||||||
index <SigSpec> port(ffS, \Q) === sigS
|
(port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
|
||||||
endmatch
|
|
||||||
|
|
||||||
code clock clock_pol clock_vld
|
dff = nullptr;
|
||||||
if (ffS) {
|
|
||||||
SigBit c = port(ffS, \CLK).as_bit();
|
|
||||||
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
|
||||||
|
|
||||||
if (clock_vld && (c != clock || cp != clock_pol))
|
// First try entire sigO
|
||||||
reject;
|
if (nusers(sigO) == 2) {
|
||||||
|
argD = sigO;
|
||||||
clock = c;
|
subpattern(out_dffe);
|
||||||
clock_pol = cp;
|
|
||||||
clock_vld = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise try just its least significant 16 bits
|
||||||
|
if (!dff && GetSize(sigO) > 16) {
|
||||||
|
argD = sigO.extract(0, 16);
|
||||||
|
if (nusers(argD) == 2) {
|
||||||
|
subpattern(out_dffe);
|
||||||
|
o_lo = dff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dff) {
|
||||||
|
ffO = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
clock_pol = dffclock_pol;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffOrstmux = dffrstmux;
|
||||||
|
ffOrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffholdmux) {
|
||||||
|
ffOholdmux = dffholdmux;
|
||||||
|
ffOholdpol = dffholdpol;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading value into output register is not
|
||||||
|
// supported unless using accumulator
|
||||||
|
if (mux) {
|
||||||
|
if (sigCD != sigO)
|
||||||
|
reject;
|
||||||
|
sigCD = port(mux, muxAB == \B ? \A : \B);
|
||||||
|
|
||||||
|
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
|
||||||
|
if (!sigCD.empty() && sigCD != sigO &&
|
||||||
|
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
|
||||||
|
argQ = sigCD;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
if (dffholdmux) {
|
||||||
|
ffCDholdmux = dffholdmux;
|
||||||
|
ffCDholdpol = dffholdpol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
|
||||||
|
// shared with A and B
|
||||||
|
if ((ffArstmux != NULL) != (dffrstmux != NULL))
|
||||||
|
goto reject_ffCD;
|
||||||
|
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||||
|
goto reject_ffCD;
|
||||||
|
if (ffArstmux) {
|
||||||
|
if (port(ffArstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto reject_ffCD;
|
||||||
|
if (ffArstpol != dffrstpol)
|
||||||
|
goto reject_ffCD;
|
||||||
|
}
|
||||||
|
if (ffBrstmux) {
|
||||||
|
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto reject_ffCD;
|
||||||
|
if (ffBrstpol != dffrstpol)
|
||||||
|
goto reject_ffCD;
|
||||||
|
}
|
||||||
|
|
||||||
|
ffCD = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
clock_pol = dffclock_pol;
|
||||||
|
sigCD = dffD;
|
||||||
|
|
||||||
|
reject_ffCD: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code sigCD
|
||||||
|
sigCD.extend_u0(32, cd_signed);
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code
|
||||||
accept;
|
accept;
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
|
// #######################
|
||||||
|
|
||||||
|
subpattern in_dffe
|
||||||
|
arg argD argQ clock clock_pol
|
||||||
|
|
||||||
|
code
|
||||||
|
dff = nullptr;
|
||||||
|
for (auto c : argQ.chunks()) {
|
||||||
|
if (!c.wire)
|
||||||
|
reject;
|
||||||
|
if (c.wire->get_bool_attribute(\keep))
|
||||||
|
reject;
|
||||||
|
Const init = c.wire->attributes.at(\init, State::Sx);
|
||||||
|
if (!init.is_fully_undef() && !init.is_fully_zero())
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ff
|
||||||
|
select ff->type.in($dff)
|
||||||
|
// DSP48E1 does not support clock inversion
|
||||||
|
select param(ff, \CLK_POLARITY).as_bool()
|
||||||
|
|
||||||
|
slice offset GetSize(port(ff, \D))
|
||||||
|
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||||
|
|
||||||
|
// Check that the rest of argQ is present
|
||||||
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argQ argD
|
||||||
|
{
|
||||||
|
if (clock != SigBit()) {
|
||||||
|
if (port(ff, \CLK) != clock)
|
||||||
|
reject;
|
||||||
|
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
SigSpec Q = port(ff, \Q);
|
||||||
|
dff = ff;
|
||||||
|
dffclock = port(ff, \CLK);
|
||||||
|
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
|
||||||
|
dffD = argQ;
|
||||||
|
argD = port(ff, \D);
|
||||||
|
argQ = Q;
|
||||||
|
dffD.replace(argQ, argD);
|
||||||
|
// Only search for ffrstmux if dffD only
|
||||||
|
// has two (ff, ffrstmux) users
|
||||||
|
if (nusers(dffD) > 2)
|
||||||
|
argD = SigSpec();
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffrstmux
|
||||||
|
if false /* TODO: ice40 resets are actually async */
|
||||||
|
|
||||||
|
if !argD.empty()
|
||||||
|
select ffrstmux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffrstmux, \Y) === argD
|
||||||
|
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
// DSP48E1 only supports reset to zero
|
||||||
|
select port(ffrstmux, BA).is_fully_zero()
|
||||||
|
|
||||||
|
define <bool> pol (BA == \B)
|
||||||
|
set ffrstpol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffrstmux) {
|
||||||
|
dffrstmux = ffrstmux;
|
||||||
|
dffrstpol = ffrstpol;
|
||||||
|
argD = port(ffrstmux, ffrstpol ? \A : \B);
|
||||||
|
dffD.replace(port(ffrstmux, \Y), argD);
|
||||||
|
|
||||||
|
// Only search for ffholdmux if argQ has at
|
||||||
|
// least 3 users (ff, <upstream>, ffrstmux) and
|
||||||
|
// dffD only has two (ff, ffrstmux)
|
||||||
|
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
|
||||||
|
argD = SigSpec();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffrstmux = nullptr;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffholdmux
|
||||||
|
if !argD.empty()
|
||||||
|
select ffholdmux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffholdmux, \Y) === argD
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
index <SigSpec> port(ffholdmux, BA) === argQ
|
||||||
|
define <bool> pol (BA == \B)
|
||||||
|
set ffholdpol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffholdmux) {
|
||||||
|
dffholdmux = ffholdmux;
|
||||||
|
dffholdpol = ffholdpol;
|
||||||
|
argD = port(ffholdmux, ffholdpol ? \A : \B);
|
||||||
|
dffD.replace(port(ffholdmux, \Y), argD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffholdmux = nullptr;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// #######################
|
||||||
|
|
||||||
|
subpattern out_dffe
|
||||||
|
arg argD argQ clock clock_pol
|
||||||
|
|
||||||
|
code
|
||||||
|
dff = nullptr;
|
||||||
|
for (auto c : argD.chunks())
|
||||||
|
if (c.wire->get_bool_attribute(\keep))
|
||||||
|
reject;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffholdmux
|
||||||
|
select ffholdmux->type.in($mux)
|
||||||
|
// ffholdmux output must have two users: ffholdmux and ff.D
|
||||||
|
select nusers(port(ffholdmux, \Y)) == 2
|
||||||
|
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
// keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
|
||||||
|
select nusers(port(ffholdmux, BA)) >= 3
|
||||||
|
|
||||||
|
slice offset GetSize(port(ffholdmux, \Y))
|
||||||
|
define <IdString> AB (BA == \B ? \A : \B)
|
||||||
|
index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
|
||||||
|
|
||||||
|
// Check that the rest of argD is present
|
||||||
|
filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
|
||||||
|
filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
define <bool> pol (BA == \B)
|
||||||
|
set ffholdpol pol
|
||||||
|
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD argQ
|
||||||
|
dffholdmux = ffholdmux;
|
||||||
|
if (ffholdmux) {
|
||||||
|
SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
|
||||||
|
SigSpec Y = port(ffholdmux, \Y);
|
||||||
|
argQ = argD;
|
||||||
|
argD.replace(AB, Y);
|
||||||
|
argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
|
||||||
|
|
||||||
|
dffholdmux = ffholdmux;
|
||||||
|
dffholdpol = ffholdpol;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ffrstmux
|
||||||
|
if false /* TODO: ice40 resets are actually async */
|
||||||
|
|
||||||
|
select ffrstmux->type.in($mux)
|
||||||
|
// ffrstmux output must have two users: ffrstmux and ff.D
|
||||||
|
select nusers(port(ffrstmux, \Y)) == 2
|
||||||
|
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
// DSP48E1 only supports reset to zero
|
||||||
|
select port(ffrstmux, BA).is_fully_zero()
|
||||||
|
|
||||||
|
slice offset GetSize(port(ffrstmux, \Y))
|
||||||
|
define <IdString> AB (BA == \B ? \A : \B)
|
||||||
|
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
|
||||||
|
|
||||||
|
// Check that offset is consistent
|
||||||
|
filter !ffholdmux || ffoffset == offset
|
||||||
|
// Check that the rest of argD is present
|
||||||
|
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
|
||||||
|
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
define <bool> pol (AB == \A)
|
||||||
|
set ffrstpol pol
|
||||||
|
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD argQ
|
||||||
|
dffrstmux = ffrstmux;
|
||||||
|
if (ffrstmux) {
|
||||||
|
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
|
||||||
|
SigSpec Y = port(ffrstmux, \Y);
|
||||||
|
argD.replace(AB, Y);
|
||||||
|
|
||||||
|
dffrstmux = ffrstmux;
|
||||||
|
dffrstpol = ffrstpol;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
match ff
|
||||||
|
select ff->type.in($dff)
|
||||||
|
// DSP48E1 does not support clock inversion
|
||||||
|
select param(ff, \CLK_POLARITY).as_bool()
|
||||||
|
|
||||||
|
slice offset GetSize(port(ff, \D))
|
||||||
|
index <SigBit> port(ff, \D)[offset] === argD[0]
|
||||||
|
|
||||||
|
// Check that offset is consistent
|
||||||
|
filter (!ffholdmux && !ffrstmux) || ffoffset == offset
|
||||||
|
// Check that the rest of argD is present
|
||||||
|
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||||
|
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||||
|
// Check that FF.Q is connected to CE-mux
|
||||||
|
filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argQ
|
||||||
|
if (ff) {
|
||||||
|
if (clock != SigBit()) {
|
||||||
|
if (port(ff, \CLK) != clock)
|
||||||
|
reject;
|
||||||
|
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
SigSpec D = port(ff, \D);
|
||||||
|
SigSpec Q = port(ff, \Q);
|
||||||
|
if (!ffholdmux) {
|
||||||
|
argQ = argD;
|
||||||
|
argQ.replace(D, Q);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto c : argQ.chunks()) {
|
||||||
|
Const init = c.wire->attributes.at(\init, State::Sx);
|
||||||
|
if (!init.is_fully_undef() && !init.is_fully_zero())
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
dff = ff;
|
||||||
|
dffQ = argQ;
|
||||||
|
dffclock = port(ff, \CLK);
|
||||||
|
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
|
||||||
|
}
|
||||||
|
// No enable/reset mux possible without flop
|
||||||
|
else if (dffholdmux || dffrstmux)
|
||||||
|
reject;
|
||||||
|
endcode
|
||||||
|
|
|
@ -9,3 +9,7 @@ match lut
|
||||||
index <SigSpec> port(lut, \I1) === port(carry, \I0)
|
index <SigSpec> port(lut, \I1) === port(carry, \I0)
|
||||||
index <SigSpec> port(lut, \I2) === port(carry, \I1)
|
index <SigSpec> port(lut, \I2) === port(carry, \I1)
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
accept;
|
||||||
|
endcode
|
||||||
|
|
|
@ -24,8 +24,11 @@ USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
bool did_something;
|
bool did_something;
|
||||||
|
dict<SigBit, State> initbits;
|
||||||
|
pool<SigBit> rminitbits;
|
||||||
|
|
||||||
#include "passes/pmgen/peepopt_pm.h"
|
#include "passes/pmgen/peepopt_pm.h"
|
||||||
|
#include "generate.h"
|
||||||
|
|
||||||
struct PeepoptPass : public Pass {
|
struct PeepoptPass : public Pass {
|
||||||
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
|
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
|
||||||
|
@ -40,27 +43,86 @@ struct PeepoptPass : public Pass {
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
std::string genmode;
|
||||||
|
|
||||||
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
|
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-singleton") {
|
if (args[argidx] == "-generate" && argidx+1 < args.size()) {
|
||||||
// singleton_mode = true;
|
genmode = args[++argidx];
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
if (!genmode.empty())
|
||||||
|
{
|
||||||
|
initbits.clear();
|
||||||
|
rminitbits.clear();
|
||||||
|
|
||||||
|
if (genmode == "shiftmul")
|
||||||
|
GENERATE_PATTERN(peepopt_pm, shiftmul);
|
||||||
|
else if (genmode == "muldiv")
|
||||||
|
GENERATE_PATTERN(peepopt_pm, muldiv);
|
||||||
|
else if (genmode == "dffmux")
|
||||||
|
GENERATE_PATTERN(peepopt_pm, dffmux);
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto module : design->selected_modules())
|
||||||
|
{
|
||||||
did_something = true;
|
did_something = true;
|
||||||
while (did_something) {
|
|
||||||
|
while (did_something)
|
||||||
|
{
|
||||||
did_something = false;
|
did_something = false;
|
||||||
peepopt_pm pm(module, module->selected_cells());
|
initbits.clear();
|
||||||
|
rminitbits.clear();
|
||||||
|
|
||||||
|
peepopt_pm pm(module);
|
||||||
|
|
||||||
|
for (auto w : module->wires()) {
|
||||||
|
auto it = w->attributes.find(ID(init));
|
||||||
|
if (it != w->attributes.end()) {
|
||||||
|
SigSpec sig = pm.sigmap(w);
|
||||||
|
Const val = it->second;
|
||||||
|
int len = std::min(GetSize(sig), GetSize(val));
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (sig[i].wire == nullptr)
|
||||||
|
continue;
|
||||||
|
if (val[i] != State::S0 && val[i] != State::S1)
|
||||||
|
continue;
|
||||||
|
initbits[sig[i]] = val[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.setup(module->selected_cells());
|
||||||
|
|
||||||
pm.run_shiftmul();
|
pm.run_shiftmul();
|
||||||
pm.run_muldiv();
|
pm.run_muldiv();
|
||||||
pm.run_dffmux();
|
pm.run_dffmux();
|
||||||
|
|
||||||
|
for (auto w : module->wires()) {
|
||||||
|
auto it = w->attributes.find(ID(init));
|
||||||
|
if (it != w->attributes.end()) {
|
||||||
|
SigSpec sig = pm.sigmap(w);
|
||||||
|
Const &val = it->second;
|
||||||
|
int len = std::min(GetSize(sig), GetSize(val));
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (rminitbits.count(sig[i]))
|
||||||
|
val[i] = State::Sx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initbits.clear();
|
||||||
|
rminitbits.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,21 +8,23 @@ match dff
|
||||||
select GetSize(port(dff, \D)) > 1
|
select GetSize(port(dff, \D)) > 1
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
|
code sigD
|
||||||
|
sigD = port(dff, \D);
|
||||||
|
endcode
|
||||||
|
|
||||||
match rstmux
|
match rstmux
|
||||||
select rstmux->type == $mux
|
select rstmux->type == $mux
|
||||||
select GetSize(port(rstmux, \Y)) > 1
|
select GetSize(port(rstmux, \Y)) > 1
|
||||||
index <SigSpec> port(rstmux, \Y) === port(dff, \D)
|
index <SigSpec> port(rstmux, \Y) === sigD
|
||||||
choice <IdString> BA {\B, \A}
|
choice <IdString> BA {\B, \A}
|
||||||
select port(rstmux, BA).is_fully_const()
|
select port(rstmux, BA).is_fully_const()
|
||||||
set rstmuxBA BA
|
set rstmuxBA BA
|
||||||
optional
|
semioptional
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code sigD
|
code sigD
|
||||||
if (rstmux)
|
if (rstmux)
|
||||||
sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
|
sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
|
||||||
else
|
|
||||||
sigD = port(dff, \D);
|
|
||||||
endcode
|
endcode
|
||||||
|
|
||||||
match cemux
|
match cemux
|
||||||
|
@ -32,67 +34,111 @@ match cemux
|
||||||
choice <IdString> AB {\A, \B}
|
choice <IdString> AB {\A, \B}
|
||||||
index <SigSpec> port(cemux, AB) === port(dff, \Q)
|
index <SigSpec> port(cemux, AB) === port(dff, \Q)
|
||||||
set cemuxAB AB
|
set cemuxAB AB
|
||||||
|
semioptional
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code
|
code
|
||||||
SigSpec D = port(cemux, cemuxAB == \A ? \B : \A);
|
if (!cemux && !rstmux)
|
||||||
SigSpec Q = port(dff, \Q);
|
reject;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code
|
||||||
Const rst;
|
Const rst;
|
||||||
|
SigSpec D;
|
||||||
|
if (cemux) {
|
||||||
|
D = port(cemux, cemuxAB == \A ? \B : \A);
|
||||||
if (rstmux)
|
if (rstmux)
|
||||||
rst = port(rstmux, rstmuxBA).as_const();
|
rst = port(rstmux, rstmuxBA).as_const();
|
||||||
|
else
|
||||||
|
rst = Const(State::Sx, GetSize(D));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_assert(rstmux);
|
||||||
|
D = port(rstmux, rstmuxBA == \B ? \A : \B);
|
||||||
|
rst = port(rstmux, rstmuxBA).as_const();
|
||||||
|
}
|
||||||
|
SigSpec Q = port(dff, \Q);
|
||||||
int width = GetSize(D);
|
int width = GetSize(D);
|
||||||
|
|
||||||
SigSpec &ceA = cemux->connections_.at(\A);
|
SigSpec dffD = dff->getPort(\D);
|
||||||
SigSpec &ceB = cemux->connections_.at(\B);
|
SigSpec dffQ = dff->getPort(\Q);
|
||||||
SigSpec &ceY = cemux->connections_.at(\Y);
|
|
||||||
SigSpec &dffD = dff->connections_.at(\D);
|
|
||||||
SigSpec &dffQ = dff->connections_.at(\Q);
|
|
||||||
|
|
||||||
if (D[width-1] == D[width-2]) {
|
Const initval;
|
||||||
did_something = true;
|
for (auto b : Q) {
|
||||||
|
auto it = initbits.find(b);
|
||||||
SigBit sign = D[width-1];
|
initval.bits.push_back(it == initbits.end() ? State::Sx : it->second);
|
||||||
bool is_signed = sign.wire;
|
|
||||||
int i;
|
|
||||||
for (i = width-1; i >= 2; i--) {
|
|
||||||
if (!is_signed) {
|
|
||||||
module->connect(Q[i], sign);
|
|
||||||
if (D[i-1] != sign || (rst.size() && rst[i-1] != rst[width-1]))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
auto cmpx = [=](State lhs, State rhs) {
|
||||||
|
if (lhs == State::Sx || rhs == State::Sx)
|
||||||
|
return true;
|
||||||
|
return lhs == rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
int i = width-1;
|
||||||
|
while (i > 1) {
|
||||||
|
if (D[i] != D[i-1])
|
||||||
|
break;
|
||||||
|
if (!cmpx(rst[i], rst[i-1]))
|
||||||
|
break;
|
||||||
|
if (!cmpx(initval[i], initval[i-1]))
|
||||||
|
break;
|
||||||
|
if (!cmpx(rst[i], initval[i]))
|
||||||
|
break;
|
||||||
|
rminitbits.insert(Q[i]);
|
||||||
module->connect(Q[i], Q[i-1]);
|
module->connect(Q[i], Q[i-1]);
|
||||||
if (D[i-2] != sign || (rst.size() && rst[i-1] != rst[width-1]))
|
i--;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
if (i < width-1) {
|
||||||
|
did_something = true;
|
||||||
ceA.remove(i, width-i);
|
if (cemux) {
|
||||||
ceB.remove(i, width-i);
|
SigSpec ceA = cemux->getPort(\A);
|
||||||
ceY.remove(i, width-i);
|
SigSpec ceB = cemux->getPort(\B);
|
||||||
|
SigSpec ceY = cemux->getPort(\Y);
|
||||||
|
ceA.remove(i, width-1-i);
|
||||||
|
ceB.remove(i, width-1-i);
|
||||||
|
ceY.remove(i, width-1-i);
|
||||||
|
cemux->setPort(\A, ceA);
|
||||||
|
cemux->setPort(\B, ceB);
|
||||||
|
cemux->setPort(\Y, ceY);
|
||||||
cemux->fixup_parameters();
|
cemux->fixup_parameters();
|
||||||
dffD.remove(i, width-i);
|
blacklist(cemux);
|
||||||
dffQ.remove(i, width-i);
|
|
||||||
dff->fixup_parameters();
|
|
||||||
|
|
||||||
log("dffcemux pattern in %s: dff=%s, cemux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux), width-i);
|
|
||||||
accept;
|
|
||||||
}
|
}
|
||||||
else {
|
if (rstmux) {
|
||||||
|
SigSpec rstA = rstmux->getPort(\A);
|
||||||
|
SigSpec rstB = rstmux->getPort(\B);
|
||||||
|
SigSpec rstY = rstmux->getPort(\Y);
|
||||||
|
rstA.remove(i, width-1-i);
|
||||||
|
rstB.remove(i, width-1-i);
|
||||||
|
rstY.remove(i, width-1-i);
|
||||||
|
rstmux->setPort(\A, rstA);
|
||||||
|
rstmux->setPort(\B, rstB);
|
||||||
|
rstmux->setPort(\Y, rstY);
|
||||||
|
rstmux->fixup_parameters();
|
||||||
|
blacklist(rstmux);
|
||||||
|
}
|
||||||
|
dffD.remove(i, width-1-i);
|
||||||
|
dffQ.remove(i, width-1-i);
|
||||||
|
dff->setPort(\D, dffD);
|
||||||
|
dff->setPort(\Q, dffQ);
|
||||||
|
dff->fixup_parameters();
|
||||||
|
blacklist(dff);
|
||||||
|
|
||||||
|
log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux, "n/a"), log_id(rstmux, "n/a"), width-1-i);
|
||||||
|
width = i+1;
|
||||||
|
}
|
||||||
|
if (cemux) {
|
||||||
|
SigSpec ceA = cemux->getPort(\A);
|
||||||
|
SigSpec ceB = cemux->getPort(\B);
|
||||||
|
SigSpec ceY = cemux->getPort(\Y);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = width-1; i >= 0; i--) {
|
for (int i = width-1; i >= 0; i--) {
|
||||||
if (D[i].wire)
|
if (D[i].wire)
|
||||||
continue;
|
continue;
|
||||||
Wire *w = Q[i].wire;
|
if (cmpx(rst[i], D[i].data) && cmpx(initval[i], D[i].data)) {
|
||||||
auto it = w->attributes.find(\init);
|
|
||||||
State init;
|
|
||||||
if (it != w->attributes.end())
|
|
||||||
init = it->second[Q[i].offset];
|
|
||||||
else
|
|
||||||
init = State::Sx;
|
|
||||||
|
|
||||||
if (init == State::Sx || init == D[i].data) {
|
|
||||||
count++;
|
count++;
|
||||||
|
rminitbits.insert(Q[i]);
|
||||||
module->connect(Q[i], D[i]);
|
module->connect(Q[i], D[i]);
|
||||||
ceA.remove(i);
|
ceA.remove(i);
|
||||||
ceB.remove(i);
|
ceB.remove(i);
|
||||||
|
@ -101,13 +147,25 @@ code
|
||||||
dffQ.remove(i);
|
dffQ.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0)
|
||||||
|
{
|
||||||
did_something = true;
|
did_something = true;
|
||||||
|
|
||||||
|
cemux->setPort(\A, ceA);
|
||||||
|
cemux->setPort(\B, ceB);
|
||||||
|
cemux->setPort(\Y, ceY);
|
||||||
cemux->fixup_parameters();
|
cemux->fixup_parameters();
|
||||||
|
blacklist(cemux);
|
||||||
|
|
||||||
|
dff->setPort(\D, dffD);
|
||||||
|
dff->setPort(\Q, dffQ);
|
||||||
dff->fixup_parameters();
|
dff->fixup_parameters();
|
||||||
log("dffcemux pattern in %s: dff=%s, cemux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), count);
|
blacklist(dff);
|
||||||
|
|
||||||
|
log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), log_id(rstmux, "n/a"), count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (did_something)
|
||||||
accept;
|
accept;
|
||||||
}
|
|
||||||
endcode
|
endcode
|
||||||
|
|
|
@ -286,7 +286,7 @@ def process_pmgfile(f, filename):
|
||||||
block["gencode"].append(rewrite_cpp(l.rstrip()))
|
block["gencode"].append(rewrite_cpp(l.rstrip()))
|
||||||
break
|
break
|
||||||
|
|
||||||
assert False
|
raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr))
|
||||||
|
|
||||||
if block["optional"]:
|
if block["optional"]:
|
||||||
assert not block["semioptional"]
|
assert not block["semioptional"]
|
||||||
|
@ -305,7 +305,8 @@ def process_pmgfile(f, filename):
|
||||||
block["states"] = set()
|
block["states"] = set()
|
||||||
|
|
||||||
for s in line.split()[1:]:
|
for s in line.split()[1:]:
|
||||||
assert s in state_types[current_pattern]
|
if s not in state_types[current_pattern]:
|
||||||
|
raise RuntimeError("'%s' not in state_types" % s)
|
||||||
block["states"].add(s)
|
block["states"].add(s)
|
||||||
|
|
||||||
codetype = "code"
|
codetype = "code"
|
||||||
|
@ -327,7 +328,7 @@ def process_pmgfile(f, filename):
|
||||||
blocks.append(block)
|
blocks.append(block)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
assert False
|
raise RuntimeError("'%s' command not recognised" % cmd)
|
||||||
|
|
||||||
for fn in pmgfiles:
|
for fn in pmgfiles:
|
||||||
with open(fn, "r") as f:
|
with open(fn, "r") as f:
|
||||||
|
@ -361,6 +362,7 @@ with open(outfile, "w") as f:
|
||||||
print(" Module *module;", file=f)
|
print(" Module *module;", file=f)
|
||||||
print(" SigMap sigmap;", file=f)
|
print(" SigMap sigmap;", file=f)
|
||||||
print(" std::function<void()> on_accept;", file=f)
|
print(" std::function<void()> on_accept;", file=f)
|
||||||
|
print(" bool setup_done;", file=f)
|
||||||
print(" bool generate_mode;", file=f)
|
print(" bool generate_mode;", file=f)
|
||||||
print(" int accept_cnt;", file=f)
|
print(" int accept_cnt;", file=f)
|
||||||
print("", file=f)
|
print("", file=f)
|
||||||
|
@ -452,11 +454,19 @@ with open(outfile, "w") as f:
|
||||||
print(" return sigmap(cell->getPort(portname));", file=f)
|
print(" return sigmap(cell->getPort(portname));", file=f)
|
||||||
print(" }", file=f)
|
print(" }", file=f)
|
||||||
print("", file=f)
|
print("", file=f)
|
||||||
|
print(" SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f)
|
||||||
|
print(" return sigmap(cell->connections_.at(portname, defval));", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
||||||
print(" return cell->getParam(paramname);", file=f)
|
print(" return cell->getParam(paramname);", file=f)
|
||||||
print(" }", file=f)
|
print(" }", file=f)
|
||||||
print("", file=f)
|
print("", file=f)
|
||||||
|
print(" Const param(Cell *cell, IdString paramname, const Const& defval) {", file=f)
|
||||||
|
print(" return cell->parameters.at(paramname, defval);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
print(" int nusers(const SigSpec &sig) {", file=f)
|
print(" int nusers(const SigSpec &sig) {", file=f)
|
||||||
print(" pool<Cell*> users;", file=f)
|
print(" pool<Cell*> users;", file=f)
|
||||||
|
@ -468,7 +478,17 @@ with open(outfile, "w") as f:
|
||||||
print("", file=f)
|
print("", file=f)
|
||||||
|
|
||||||
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
|
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
|
||||||
print(" module(module), sigmap(module), generate_mode(false), rngseed(12345678) {", file=f)
|
print(" module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f)
|
||||||
|
print(" setup(cells);", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" {}_pm(Module *module) :".format(prefix), file=f)
|
||||||
|
print(" module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f)
|
||||||
|
print(" }", file=f)
|
||||||
|
print("", file=f)
|
||||||
|
|
||||||
|
print(" void setup(const vector<Cell*> &cells) {", file=f)
|
||||||
for current_pattern in sorted(patterns.keys()):
|
for current_pattern in sorted(patterns.keys()):
|
||||||
for s, t in sorted(udata_types[current_pattern].items()):
|
for s, t in sorted(udata_types[current_pattern].items()):
|
||||||
if t.endswith("*"):
|
if t.endswith("*"):
|
||||||
|
@ -476,6 +496,8 @@ with open(outfile, "w") as f:
|
||||||
else:
|
else:
|
||||||
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
|
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
|
||||||
current_pattern = None
|
current_pattern = None
|
||||||
|
print(" log_assert(!setup_done);", file=f)
|
||||||
|
print(" setup_done = true;", file=f)
|
||||||
print(" for (auto port : module->ports)", file=f)
|
print(" for (auto port : module->ports)", file=f)
|
||||||
print(" add_siguser(module->wire(port), nullptr);", file=f)
|
print(" add_siguser(module->wire(port), nullptr);", file=f)
|
||||||
print(" for (auto cell : module->cells())", file=f)
|
print(" for (auto cell : module->cells())", file=f)
|
||||||
|
@ -530,6 +552,7 @@ with open(outfile, "w") as f:
|
||||||
|
|
||||||
for current_pattern in sorted(patterns.keys()):
|
for current_pattern in sorted(patterns.keys()):
|
||||||
print(" int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
|
print(" int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
|
||||||
|
print(" log_assert(setup_done);", file=f)
|
||||||
print(" accept_cnt = 0;", file=f)
|
print(" accept_cnt = 0;", file=f)
|
||||||
print(" on_accept = on_accept_f;", file=f)
|
print(" on_accept = on_accept_f;", file=f)
|
||||||
print(" rollback = 0;", file=f)
|
print(" rollback = 0;", file=f)
|
||||||
|
|
|
@ -23,13 +23,11 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// for peepopt_pm
|
|
||||||
bool did_something;
|
|
||||||
|
|
||||||
#include "passes/pmgen/test_pmgen_pm.h"
|
#include "passes/pmgen/test_pmgen_pm.h"
|
||||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||||
#include "passes/pmgen/xilinx_srl_pm.h"
|
#include "passes/pmgen/xilinx_srl_pm.h"
|
||||||
#include "passes/pmgen/peepopt_pm.h"
|
|
||||||
|
#include "generate.h"
|
||||||
|
|
||||||
void reduce_chain(test_pmgen_pm &pm)
|
void reduce_chain(test_pmgen_pm &pm)
|
||||||
{
|
{
|
||||||
|
@ -118,123 +116,6 @@ void opt_eqpmux(test_pmgen_pm &pm)
|
||||||
log(" -> %s (%s)\n", log_id(c), log_id(c->type));
|
log(" -> %s (%s)\n", log_id(c), log_id(c->type));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GENERATE_PATTERN(pmclass, pattern) \
|
|
||||||
generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)
|
|
||||||
|
|
||||||
void pmtest_addports(Module *module)
|
|
||||||
{
|
|
||||||
pool<SigBit> driven_bits, used_bits;
|
|
||||||
SigMap sigmap(module);
|
|
||||||
int icnt = 0, ocnt = 0;
|
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
|
||||||
for (auto conn : cell->connections())
|
|
||||||
{
|
|
||||||
if (cell->input(conn.first))
|
|
||||||
for (auto bit : sigmap(conn.second))
|
|
||||||
used_bits.insert(bit);
|
|
||||||
if (cell->output(conn.first))
|
|
||||||
for (auto bit : sigmap(conn.second))
|
|
||||||
driven_bits.insert(bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto wire : vector<Wire*>(module->wires()))
|
|
||||||
{
|
|
||||||
SigSpec ibits, obits;
|
|
||||||
for (auto bit : sigmap(wire)) {
|
|
||||||
if (!used_bits.count(bit))
|
|
||||||
obits.append(bit);
|
|
||||||
if (!driven_bits.count(bit))
|
|
||||||
ibits.append(bit);
|
|
||||||
}
|
|
||||||
if (!ibits.empty()) {
|
|
||||||
Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
|
|
||||||
w->port_input = true;
|
|
||||||
module->connect(ibits, w);
|
|
||||||
}
|
|
||||||
if (!obits.empty()) {
|
|
||||||
Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
|
|
||||||
w->port_output = true;
|
|
||||||
module->connect(w, obits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module->fixup_ports();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class pm>
|
|
||||||
void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
|
|
||||||
{
|
|
||||||
log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
|
|
||||||
|
|
||||||
int modcnt = 0;
|
|
||||||
int maxmodcnt = 100;
|
|
||||||
int maxsubcnt = 4;
|
|
||||||
int timeout = 0;
|
|
||||||
vector<Module*> mods;
|
|
||||||
|
|
||||||
while (modcnt < maxmodcnt)
|
|
||||||
{
|
|
||||||
int submodcnt = 0, itercnt = 0, cellcnt = 0;
|
|
||||||
Module *mod = design->addModule(NEW_ID);
|
|
||||||
|
|
||||||
while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
|
|
||||||
{
|
|
||||||
if (timeout++ > 10000)
|
|
||||||
log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
|
|
||||||
|
|
||||||
pm matcher(mod, mod->cells());
|
|
||||||
|
|
||||||
matcher.rng(1);
|
|
||||||
matcher.rngseed += modcnt;
|
|
||||||
matcher.rng(1);
|
|
||||||
matcher.rngseed += submodcnt;
|
|
||||||
matcher.rng(1);
|
|
||||||
matcher.rngseed += itercnt;
|
|
||||||
matcher.rng(1);
|
|
||||||
matcher.rngseed += cellcnt;
|
|
||||||
matcher.rng(1);
|
|
||||||
|
|
||||||
if (GetSize(mod->cells()) != cellcnt)
|
|
||||||
{
|
|
||||||
bool found_match = false;
|
|
||||||
run(matcher, [&](){ found_match = true; });
|
|
||||||
cellcnt = GetSize(mod->cells());
|
|
||||||
|
|
||||||
if (found_match) {
|
|
||||||
Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
|
|
||||||
pmclass, pattern, modcnt++));
|
|
||||||
log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
|
|
||||||
mod->cloneInto(m);
|
|
||||||
pmtest_addports(m);
|
|
||||||
mods.push_back(m);
|
|
||||||
submodcnt++;
|
|
||||||
timeout = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matcher.generate_mode = true;
|
|
||||||
run(matcher, [](){});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (submodcnt && maxsubcnt < (1 << 16))
|
|
||||||
maxsubcnt *= 2;
|
|
||||||
|
|
||||||
design->remove(mod);
|
|
||||||
}
|
|
||||||
|
|
||||||
Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
|
|
||||||
log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
|
|
||||||
for (auto mod : mods) {
|
|
||||||
Cell *c = m->addCell(mod->name, mod->name);
|
|
||||||
for (auto port : mod->ports) {
|
|
||||||
Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
|
|
||||||
c->setPort(port, w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pmtest_addports(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestPmgenPass : public Pass {
|
struct TestPmgenPass : public Pass {
|
||||||
TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
|
TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
|
||||||
void help() YS_OVERRIDE
|
void help() YS_OVERRIDE
|
||||||
|
@ -355,12 +236,6 @@ struct TestPmgenPass : public Pass {
|
||||||
if (pattern == "xilinx_srl.variable")
|
if (pattern == "xilinx_srl.variable")
|
||||||
return GENERATE_PATTERN(xilinx_srl_pm, variable);
|
return GENERATE_PATTERN(xilinx_srl_pm, variable);
|
||||||
|
|
||||||
if (pattern == "peepopt-muldiv")
|
|
||||||
return GENERATE_PATTERN(peepopt_pm, muldiv);
|
|
||||||
|
|
||||||
if (pattern == "peepopt-shiftmul")
|
|
||||||
return GENERATE_PATTERN(peepopt_pm, shiftmul);
|
|
||||||
|
|
||||||
log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
|
log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,646 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#include "passes/pmgen/xilinx_dsp_pm.h"
|
||||||
|
#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
|
||||||
|
#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
|
||||||
|
|
||||||
|
static Cell* addDsp(Module *module) {
|
||||||
|
Cell *cell = module->addCell(NEW_ID, ID(DSP48E1));
|
||||||
|
cell->setParam(ID(ACASCREG), 0);
|
||||||
|
cell->setParam(ID(ADREG), 0);
|
||||||
|
cell->setParam(ID(A_INPUT), Const("DIRECT"));
|
||||||
|
cell->setParam(ID(ALUMODEREG), 0);
|
||||||
|
cell->setParam(ID(AREG), 0);
|
||||||
|
cell->setParam(ID(BCASCREG), 0);
|
||||||
|
cell->setParam(ID(B_INPUT), Const("DIRECT"));
|
||||||
|
cell->setParam(ID(BREG), 0);
|
||||||
|
cell->setParam(ID(CARRYINREG), 0);
|
||||||
|
cell->setParam(ID(CARRYINSELREG), 0);
|
||||||
|
cell->setParam(ID(CREG), 0);
|
||||||
|
cell->setParam(ID(DREG), 0);
|
||||||
|
cell->setParam(ID(INMODEREG), 0);
|
||||||
|
cell->setParam(ID(MREG), 0);
|
||||||
|
cell->setParam(ID(OPMODEREG), 0);
|
||||||
|
cell->setParam(ID(PREG), 0);
|
||||||
|
cell->setParam(ID(USE_MULT), Const("NONE"));
|
||||||
|
cell->setParam(ID(USE_SIMD), Const("ONE48"));
|
||||||
|
cell->setParam(ID(USE_DPORT), Const("FALSE"));
|
||||||
|
|
||||||
|
cell->setPort(ID(D), Const(0, 25));
|
||||||
|
cell->setPort(ID(INMODE), Const(0, 5));
|
||||||
|
cell->setPort(ID(ALUMODE), Const(0, 4));
|
||||||
|
cell->setPort(ID(OPMODE), Const(0, 7));
|
||||||
|
cell->setPort(ID(CARRYINSEL), Const(0, 3));
|
||||||
|
cell->setPort(ID(ACIN), Const(0, 30));
|
||||||
|
cell->setPort(ID(BCIN), Const(0, 18));
|
||||||
|
cell->setPort(ID(PCIN), Const(0, 48));
|
||||||
|
cell->setPort(ID(CARRYIN), Const(0, 1));
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xilinx_simd_pack(Module *module, const std::vector<Cell*> &selected_cells)
|
||||||
|
{
|
||||||
|
std::deque<Cell*> simd12_add, simd12_sub;
|
||||||
|
std::deque<Cell*> simd24_add, simd24_sub;
|
||||||
|
|
||||||
|
for (auto cell : selected_cells) {
|
||||||
|
if (!cell->type.in(ID($add), ID($sub)))
|
||||||
|
continue;
|
||||||
|
SigSpec Y = cell->getPort(ID(Y));
|
||||||
|
if (!Y.is_chunk())
|
||||||
|
continue;
|
||||||
|
if (!Y.as_chunk().wire->get_strpool_attribute(ID(use_dsp)).count("simd"))
|
||||||
|
continue;
|
||||||
|
if (GetSize(Y) > 25)
|
||||||
|
continue;
|
||||||
|
SigSpec A = cell->getPort(ID(A));
|
||||||
|
SigSpec B = cell->getPort(ID(B));
|
||||||
|
if (GetSize(Y) <= 13) {
|
||||||
|
if (GetSize(A) > 12)
|
||||||
|
continue;
|
||||||
|
if (GetSize(B) > 12)
|
||||||
|
continue;
|
||||||
|
if (cell->type == ID($add))
|
||||||
|
simd12_add.push_back(cell);
|
||||||
|
else if (cell->type == ID($sub))
|
||||||
|
simd12_sub.push_back(cell);
|
||||||
|
}
|
||||||
|
else if (GetSize(Y) <= 25) {
|
||||||
|
if (GetSize(A) > 24)
|
||||||
|
continue;
|
||||||
|
if (GetSize(B) > 24)
|
||||||
|
continue;
|
||||||
|
if (cell->type == ID($add))
|
||||||
|
simd24_add.push_back(cell);
|
||||||
|
else if (cell->type == ID($sub))
|
||||||
|
simd24_sub.push_back(cell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto f12 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
|
||||||
|
SigSpec A = lane->getPort(ID(A));
|
||||||
|
SigSpec B = lane->getPort(ID(B));
|
||||||
|
SigSpec Y = lane->getPort(ID(Y));
|
||||||
|
A.extend_u0(12, lane->getParam(ID(A_SIGNED)).as_bool());
|
||||||
|
B.extend_u0(12, lane->getParam(ID(B_SIGNED)).as_bool());
|
||||||
|
AB.append(A);
|
||||||
|
C.append(B);
|
||||||
|
if (GetSize(Y) < 13)
|
||||||
|
Y.append(module->addWire(NEW_ID, 13-GetSize(Y)));
|
||||||
|
else
|
||||||
|
log_assert(GetSize(Y) == 13);
|
||||||
|
P.append(Y.extract(0, 12));
|
||||||
|
CARRYOUT.append(Y[12]);
|
||||||
|
};
|
||||||
|
auto g12 = [&f12,module](std::deque<Cell*> &simd12) {
|
||||||
|
while (simd12.size() > 1) {
|
||||||
|
SigSpec AB, C, P, CARRYOUT;
|
||||||
|
|
||||||
|
Cell *lane1 = simd12.front();
|
||||||
|
simd12.pop_front();
|
||||||
|
Cell *lane2 = simd12.front();
|
||||||
|
simd12.pop_front();
|
||||||
|
Cell *lane3 = nullptr;
|
||||||
|
Cell *lane4 = nullptr;
|
||||||
|
|
||||||
|
if (!simd12.empty()) {
|
||||||
|
lane3 = simd12.front();
|
||||||
|
simd12.pop_front();
|
||||||
|
if (!simd12.empty()) {
|
||||||
|
lane4 = simd12.front();
|
||||||
|
simd12.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Analysing %s.%s for Xilinx DSP SIMD12 packing.\n", log_id(module), log_id(lane1));
|
||||||
|
|
||||||
|
Cell *cell = addDsp(module);
|
||||||
|
cell->setParam(ID(USE_SIMD), Const("FOUR12"));
|
||||||
|
// X = A:B
|
||||||
|
// Y = 0
|
||||||
|
// Z = C
|
||||||
|
cell->setPort(ID(OPMODE), Const::from_string("0110011"));
|
||||||
|
|
||||||
|
log_assert(lane1);
|
||||||
|
log_assert(lane2);
|
||||||
|
f12(AB, C, P, CARRYOUT, lane1);
|
||||||
|
f12(AB, C, P, CARRYOUT, lane2);
|
||||||
|
if (lane3) {
|
||||||
|
f12(AB, C, P, CARRYOUT, lane3);
|
||||||
|
if (lane4)
|
||||||
|
f12(AB, C, P, CARRYOUT, lane4);
|
||||||
|
else {
|
||||||
|
AB.append(Const(0, 12));
|
||||||
|
C.append(Const(0, 12));
|
||||||
|
P.append(module->addWire(NEW_ID, 12));
|
||||||
|
CARRYOUT.append(module->addWire(NEW_ID, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AB.append(Const(0, 24));
|
||||||
|
C.append(Const(0, 24));
|
||||||
|
P.append(module->addWire(NEW_ID, 24));
|
||||||
|
CARRYOUT.append(module->addWire(NEW_ID, 2));
|
||||||
|
}
|
||||||
|
log_assert(GetSize(AB) == 48);
|
||||||
|
log_assert(GetSize(C) == 48);
|
||||||
|
log_assert(GetSize(P) == 48);
|
||||||
|
log_assert(GetSize(CARRYOUT) == 4);
|
||||||
|
cell->setPort(ID(A), AB.extract(18, 30));
|
||||||
|
cell->setPort(ID(B), AB.extract(0, 18));
|
||||||
|
cell->setPort(ID(C), C);
|
||||||
|
cell->setPort(ID(P), P);
|
||||||
|
cell->setPort(ID(CARRYOUT), CARRYOUT);
|
||||||
|
if (lane1->type == ID($sub))
|
||||||
|
cell->setPort(ID(ALUMODE), Const::from_string("0011"));
|
||||||
|
|
||||||
|
module->remove(lane1);
|
||||||
|
module->remove(lane2);
|
||||||
|
if (lane3) module->remove(lane3);
|
||||||
|
if (lane4) module->remove(lane4);
|
||||||
|
|
||||||
|
module->design->select(module, cell);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
g12(simd12_add);
|
||||||
|
g12(simd12_sub);
|
||||||
|
|
||||||
|
auto f24 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
|
||||||
|
SigSpec A = lane->getPort(ID(A));
|
||||||
|
SigSpec B = lane->getPort(ID(B));
|
||||||
|
SigSpec Y = lane->getPort(ID(Y));
|
||||||
|
A.extend_u0(24, lane->getParam(ID(A_SIGNED)).as_bool());
|
||||||
|
B.extend_u0(24, lane->getParam(ID(B_SIGNED)).as_bool());
|
||||||
|
C.append(A);
|
||||||
|
AB.append(B);
|
||||||
|
if (GetSize(Y) < 25)
|
||||||
|
Y.append(module->addWire(NEW_ID, 25-GetSize(Y)));
|
||||||
|
else
|
||||||
|
log_assert(GetSize(Y) == 25);
|
||||||
|
P.append(Y.extract(0, 24));
|
||||||
|
CARRYOUT.append(module->addWire(NEW_ID)); // TWO24 uses every other bit
|
||||||
|
CARRYOUT.append(Y[24]);
|
||||||
|
};
|
||||||
|
auto g24 = [&f24,module](std::deque<Cell*> &simd24) {
|
||||||
|
while (simd24.size() > 1) {
|
||||||
|
SigSpec AB;
|
||||||
|
SigSpec C;
|
||||||
|
SigSpec P;
|
||||||
|
SigSpec CARRYOUT;
|
||||||
|
|
||||||
|
Cell *lane1 = simd24.front();
|
||||||
|
simd24.pop_front();
|
||||||
|
Cell *lane2 = simd24.front();
|
||||||
|
simd24.pop_front();
|
||||||
|
|
||||||
|
log("Analysing %s.%s for Xilinx DSP SIMD24 packing.\n", log_id(module), log_id(lane1));
|
||||||
|
|
||||||
|
Cell *cell = addDsp(module);
|
||||||
|
cell->setParam(ID(USE_SIMD), Const("TWO24"));
|
||||||
|
// X = A:B
|
||||||
|
// Y = 0
|
||||||
|
// Z = C
|
||||||
|
cell->setPort(ID(OPMODE), Const::from_string("0110011"));
|
||||||
|
|
||||||
|
log_assert(lane1);
|
||||||
|
log_assert(lane2);
|
||||||
|
f24(AB, C, P, CARRYOUT, lane1);
|
||||||
|
f24(AB, C, P, CARRYOUT, lane2);
|
||||||
|
log_assert(GetSize(AB) == 48);
|
||||||
|
log_assert(GetSize(C) == 48);
|
||||||
|
log_assert(GetSize(P) == 48);
|
||||||
|
log_assert(GetSize(CARRYOUT) == 4);
|
||||||
|
cell->setPort(ID(A), AB.extract(18, 30));
|
||||||
|
cell->setPort(ID(B), AB.extract(0, 18));
|
||||||
|
cell->setPort(ID(C), C);
|
||||||
|
cell->setPort(ID(P), P);
|
||||||
|
cell->setPort(ID(CARRYOUT), CARRYOUT);
|
||||||
|
if (lane1->type == ID($sub))
|
||||||
|
cell->setPort(ID(ALUMODE), Const::from_string("0011"));
|
||||||
|
|
||||||
|
module->remove(lane1);
|
||||||
|
module->remove(lane2);
|
||||||
|
|
||||||
|
module->design->select(module, cell);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
g24(simd24_add);
|
||||||
|
g24(simd24_sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xilinx_dsp_pack(xilinx_dsp_pm &pm)
|
||||||
|
{
|
||||||
|
auto &st = pm.st_xilinx_dsp_pack;
|
||||||
|
|
||||||
|
log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
|
||||||
|
|
||||||
|
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
|
||||||
|
log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--"));
|
||||||
|
log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--"));
|
||||||
|
log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
|
||||||
|
log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--"));
|
||||||
|
log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
|
||||||
|
log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
|
||||||
|
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
|
||||||
|
log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
|
||||||
|
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
|
||||||
|
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
|
||||||
|
log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
|
||||||
|
log_debug("overflow: %s\n", log_id(st.overflow, "--"));
|
||||||
|
|
||||||
|
Cell *cell = st.dsp;
|
||||||
|
|
||||||
|
if (st.preAdd) {
|
||||||
|
log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
|
||||||
|
bool A_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
|
||||||
|
bool D_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
|
||||||
|
if (st.sigA == st.preAdd->getPort(ID(B)))
|
||||||
|
std::swap(A_SIGNED, D_SIGNED);
|
||||||
|
st.sigA.extend_u0(30, A_SIGNED);
|
||||||
|
st.sigD.extend_u0(25, D_SIGNED);
|
||||||
|
cell->setPort(ID(A), st.sigA);
|
||||||
|
cell->setPort(ID(D), st.sigD);
|
||||||
|
cell->setPort(ID(INMODE), Const::from_string("00100"));
|
||||||
|
|
||||||
|
if (st.ffAD) {
|
||||||
|
if (st.ffADcemux) {
|
||||||
|
SigSpec S = st.ffADcemux->getPort(ID(S));
|
||||||
|
cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cell->setPort(ID(CEAD), State::S1);
|
||||||
|
cell->setParam(ID(ADREG), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell->setParam(ID(USE_DPORT), Const("TRUE"));
|
||||||
|
|
||||||
|
pm.autoremove(st.preAdd);
|
||||||
|
}
|
||||||
|
if (st.postAdd) {
|
||||||
|
log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
|
||||||
|
|
||||||
|
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
|
||||||
|
if (st.postAddMux) {
|
||||||
|
log_assert(st.ffP);
|
||||||
|
opmode[4] = st.postAddMux->getPort(ID(S));
|
||||||
|
pm.autoremove(st.postAddMux);
|
||||||
|
}
|
||||||
|
else if (st.ffP && st.sigC == st.sigP)
|
||||||
|
opmode[4] = State::S0;
|
||||||
|
else
|
||||||
|
opmode[4] = State::S1;
|
||||||
|
opmode[6] = State::S0;
|
||||||
|
opmode[5] = State::S1;
|
||||||
|
|
||||||
|
if (opmode[4] != State::S0) {
|
||||||
|
if (st.postAddMuxAB == ID(A))
|
||||||
|
st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
|
||||||
|
else
|
||||||
|
st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
|
||||||
|
cell->setPort(ID(C), st.sigC);
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.autoremove(st.postAdd);
|
||||||
|
}
|
||||||
|
if (st.overflow) {
|
||||||
|
log(" overflow %s (%s)\n", log_id(st.overflow), log_id(st.overflow->type));
|
||||||
|
cell->setParam(ID(USE_PATTERN_DETECT), Const("PATDET"));
|
||||||
|
cell->setParam(ID(SEL_PATTERN), Const("PATTERN"));
|
||||||
|
cell->setParam(ID(SEL_MASK), Const("MASK"));
|
||||||
|
|
||||||
|
if (st.overflow->type == ID($ge)) {
|
||||||
|
Const B = st.overflow->getPort(ID(B)).as_const();
|
||||||
|
log_assert(std::count(B.bits.begin(), B.bits.end(), State::S1) == 1);
|
||||||
|
// Since B is an exact power of 2, subtract 1
|
||||||
|
// by inverting all bits up until hitting
|
||||||
|
// that one hi bit
|
||||||
|
for (auto &b : B.bits)
|
||||||
|
if (b == State::S0) b = State::S1;
|
||||||
|
else if (b == State::S1) {
|
||||||
|
b = State::S0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
B.extu(48);
|
||||||
|
|
||||||
|
cell->setParam(ID(MASK), B);
|
||||||
|
cell->setParam(ID(PATTERN), Const(0, 48));
|
||||||
|
cell->setPort(ID(OVERFLOW), st.overflow->getPort(ID(Y)));
|
||||||
|
}
|
||||||
|
else log_abort();
|
||||||
|
|
||||||
|
pm.autoremove(st.overflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.clock != SigBit())
|
||||||
|
{
|
||||||
|
cell->setPort(ID(CLK), st.clock);
|
||||||
|
|
||||||
|
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
|
||||||
|
SigSpec D = ff->getPort(ID(D));
|
||||||
|
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Q, D);
|
||||||
|
if (rstmux) {
|
||||||
|
SigSpec Y = rstmux->getPort(ID(Y));
|
||||||
|
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Y, AB);
|
||||||
|
if (rstport != IdString()) {
|
||||||
|
SigSpec S = rstmux->getPort(ID(S));
|
||||||
|
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rstport != IdString())
|
||||||
|
cell->setPort(rstport, State::S0);
|
||||||
|
if (cemux) {
|
||||||
|
SigSpec Y = cemux->getPort(ID(Y));
|
||||||
|
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
|
||||||
|
SigSpec S = cemux->getPort(ID(S));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Y, BA);
|
||||||
|
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cell->setPort(ceport, State::S1);
|
||||||
|
|
||||||
|
for (auto c : Q.chunks()) {
|
||||||
|
auto it = c.wire->attributes.find(ID(init));
|
||||||
|
if (it == c.wire->attributes.end())
|
||||||
|
continue;
|
||||||
|
for (int i = c.offset; i < c.offset+c.width; i++) {
|
||||||
|
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
|
||||||
|
it->second[i] = State::Sx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (st.ffA2) {
|
||||||
|
SigSpec A = cell->getPort(ID(A));
|
||||||
|
f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA));
|
||||||
|
if (st.ffA1) {
|
||||||
|
f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString());
|
||||||
|
cell->setParam(ID(AREG), 2);
|
||||||
|
cell->setParam(ID(ACASCREG), 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell->setParam(ID(AREG), 1);
|
||||||
|
cell->setParam(ID(ACASCREG), 1);
|
||||||
|
}
|
||||||
|
pm.add_siguser(A, cell);
|
||||||
|
cell->setPort(ID(A), A);
|
||||||
|
}
|
||||||
|
if (st.ffB2) {
|
||||||
|
SigSpec B = cell->getPort(ID(B));
|
||||||
|
f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB));
|
||||||
|
if (st.ffB1) {
|
||||||
|
f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString());
|
||||||
|
cell->setParam(ID(BREG), 2);
|
||||||
|
cell->setParam(ID(BCASCREG), 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell->setParam(ID(BREG), 1);
|
||||||
|
cell->setParam(ID(BCASCREG), 1);
|
||||||
|
}
|
||||||
|
pm.add_siguser(B, cell);
|
||||||
|
cell->setPort(ID(B), B);
|
||||||
|
}
|
||||||
|
if (st.ffD) {
|
||||||
|
SigSpec D = cell->getPort(ID(D));
|
||||||
|
f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
|
||||||
|
pm.add_siguser(D, cell);
|
||||||
|
cell->setPort(ID(D), D);
|
||||||
|
cell->setParam(ID(DREG), 1);
|
||||||
|
}
|
||||||
|
if (st.ffM) {
|
||||||
|
SigSpec M; // unused
|
||||||
|
f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
|
||||||
|
st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
|
||||||
|
cell->setParam(ID(MREG), State::S1);
|
||||||
|
}
|
||||||
|
if (st.ffP) {
|
||||||
|
SigSpec P; // unused
|
||||||
|
f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
|
||||||
|
st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
|
||||||
|
cell->setParam(ID(PREG), State::S1);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
|
||||||
|
|
||||||
|
if (st.ffA2) {
|
||||||
|
log(" ffA2:%s", log_id(st.ffA2));
|
||||||
|
if (st.ffA1)
|
||||||
|
log(" ffA1:%s", log_id(st.ffA1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.ffAD)
|
||||||
|
log(" ffAD:%s", log_id(st.ffAD));
|
||||||
|
|
||||||
|
if (st.ffB2) {
|
||||||
|
log(" ffB2:%s", log_id(st.ffB2));
|
||||||
|
if (st.ffB1)
|
||||||
|
log(" ffB1:%s", log_id(st.ffB1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.ffD)
|
||||||
|
log(" ffD:%s", log_id(st.ffD));
|
||||||
|
|
||||||
|
if (st.ffM)
|
||||||
|
log(" ffM:%s", log_id(st.ffM));
|
||||||
|
|
||||||
|
if (st.ffP)
|
||||||
|
log(" ffP:%s", log_id(st.ffP));
|
||||||
|
}
|
||||||
|
log("\n");
|
||||||
|
|
||||||
|
SigSpec P = st.sigP;
|
||||||
|
if (GetSize(P) < 48)
|
||||||
|
P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
|
||||||
|
cell->setPort(ID(P), P);
|
||||||
|
|
||||||
|
pm.blacklist(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
|
||||||
|
{
|
||||||
|
auto &st = pm.st_xilinx_dsp_packC;
|
||||||
|
|
||||||
|
log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp));
|
||||||
|
log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--"));
|
||||||
|
|
||||||
|
Cell *cell = st.dsp;
|
||||||
|
|
||||||
|
if (st.clock != SigBit())
|
||||||
|
{
|
||||||
|
cell->setPort(ID(CLK), st.clock);
|
||||||
|
|
||||||
|
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
|
||||||
|
SigSpec D = ff->getPort(ID(D));
|
||||||
|
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Q, D);
|
||||||
|
if (rstmux) {
|
||||||
|
SigSpec Y = rstmux->getPort(ID(Y));
|
||||||
|
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Y, AB);
|
||||||
|
if (rstport != IdString()) {
|
||||||
|
SigSpec S = rstmux->getPort(ID(S));
|
||||||
|
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rstport != IdString())
|
||||||
|
cell->setPort(rstport, State::S0);
|
||||||
|
if (cemux) {
|
||||||
|
SigSpec Y = cemux->getPort(ID(Y));
|
||||||
|
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
|
||||||
|
SigSpec S = cemux->getPort(ID(S));
|
||||||
|
if (!A.empty())
|
||||||
|
A.replace(Y, BA);
|
||||||
|
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cell->setPort(ceport, State::S1);
|
||||||
|
|
||||||
|
for (auto c : Q.chunks()) {
|
||||||
|
auto it = c.wire->attributes.find(ID(init));
|
||||||
|
if (it == c.wire->attributes.end())
|
||||||
|
continue;
|
||||||
|
for (int i = c.offset; i < c.offset+c.width; i++) {
|
||||||
|
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
|
||||||
|
it->second[i] = State::Sx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (st.ffC) {
|
||||||
|
SigSpec C = cell->getPort(ID(C));
|
||||||
|
f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC));
|
||||||
|
pm.add_siguser(C, cell);
|
||||||
|
cell->setPort(ID(C), C);
|
||||||
|
cell->setParam(ID(CREG), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
|
||||||
|
|
||||||
|
if (st.ffC)
|
||||||
|
log(" ffC:%s", log_id(st.ffC));
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.blacklist(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XilinxDspPass : public Pass {
|
||||||
|
XilinxDspPass() : Pass("xilinx_dsp", "Xilinx: pack resources into DSPs") { }
|
||||||
|
void help() YS_OVERRIDE
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" xilinx_dsp [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Pack input registers (A2, A1, B2, B1, C, D, AD; with optional enable/reset),\n");
|
||||||
|
log("pipeline registers (M; with optional enable/reset), output registers (P; with\n");
|
||||||
|
log("optional enable/reset), pre-adder and/or post-adder into Xilinx DSP resources.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n");
|
||||||
|
log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n");
|
||||||
|
log("used to override the current accumulation result with a new value, which will\n");
|
||||||
|
log("be added to the multiplier result to form the next accumulation result.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n");
|
||||||
|
log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n");
|
||||||
|
log("input to the post-adder -- a pattern common for summing partial products to\n");
|
||||||
|
log("implement wide multipliers). Limited support also exists for similar cascading\n");
|
||||||
|
log("for A and B using '[AB]COUT' -> '[AB]CIN'. Currently, cascade chains are limited\n");
|
||||||
|
log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n");
|
||||||
|
log("device.\n");
|
||||||
|
log("\n");
|
||||||
|
log("\n");
|
||||||
|
log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n");
|
||||||
|
log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n");
|
||||||
|
log("the add/subtract operator will cause those operations to be implemented using\n");
|
||||||
|
log("the 'SIMD' feature of DSPs.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Experimental feature: the presence of a `$ge' cell attached to the registered\n");
|
||||||
|
log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n");
|
||||||
|
log("into using the DSP48E1's pattern detector feature for overflow detection.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
{
|
||||||
|
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
|
{
|
||||||
|
// if (args[argidx] == "-singleton") {
|
||||||
|
// singleton_mode = true;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
for (auto module : design->selected_modules()) {
|
||||||
|
// Experimental feature: pack $add/$sub cells with
|
||||||
|
// (* use_dsp48="simd" *) into DSP48E1's using its
|
||||||
|
// SIMD feature
|
||||||
|
xilinx_simd_pack(module, module->selected_cells());
|
||||||
|
|
||||||
|
// Match for all features ([ABDMP][12]?REG, pre-adder,
|
||||||
|
// post-adder, pattern detector, etc.) except for CREG
|
||||||
|
{
|
||||||
|
xilinx_dsp_pm pm(module, module->selected_cells());
|
||||||
|
pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
|
||||||
|
}
|
||||||
|
// Separating out CREG packing is necessary since there
|
||||||
|
// is no guarantee that the cell ordering corresponds
|
||||||
|
// to the "expected" case (i.e. the order in which
|
||||||
|
// they appear in the source) thus the possiblity
|
||||||
|
// existed that a register got packed as a CREG into a
|
||||||
|
// downstream DSP that should have otherwise been a
|
||||||
|
// PREG of an upstream DSP that had not been visited
|
||||||
|
// yet
|
||||||
|
{
|
||||||
|
xilinx_dsp_CREG_pm pm(module, module->selected_cells());
|
||||||
|
pm.run_xilinx_dsp_packC(xilinx_dsp_packC);
|
||||||
|
}
|
||||||
|
// Lastly, identify and utilise PCOUT -> PCIN,
|
||||||
|
// ACOUT -> ACIN, and BCOUT-> BCIN dedicated cascade
|
||||||
|
// chains
|
||||||
|
{
|
||||||
|
xilinx_dsp_cascade_pm pm(module, module->selected_cells());
|
||||||
|
pm.run_xilinx_dsp_cascade();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} XilinxDspPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,727 @@
|
||||||
|
// This file describes the main pattern matcher setup (of three total) that
|
||||||
|
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
||||||
|
// At a high level, it works as follows:
|
||||||
|
// ( 1) Starting from a DSP48E1 cell
|
||||||
|
// ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
|
||||||
|
// (attached to at most two $mux cells that implement clock-enable or
|
||||||
|
// reset functionality, using a subpattern discussed below)
|
||||||
|
// If ADREG matched, treat 'A' input as input of ADREG
|
||||||
|
// ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
|
||||||
|
// (pre-adder)
|
||||||
|
// ( 4) If pre-adder was present, find match 'A' input for A2REG
|
||||||
|
// If pre-adder was not present, move ADREG to A2REG
|
||||||
|
// If A2REG, then match 'A' input for A1REG
|
||||||
|
// ( 5) Match 'B' input for B2REG
|
||||||
|
// If B2REG, then match 'B' input for B1REG
|
||||||
|
// ( 6) Match 'D' input for DREG
|
||||||
|
// ( 7) Match 'P' output that exclusively drives an MREG
|
||||||
|
// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
|
||||||
|
// cell (post-adder).
|
||||||
|
// The other input to the adder is assumed to come in from the 'C' input
|
||||||
|
// (note: 'P' -> 'C' connections that exist for accumulators are
|
||||||
|
// recognised in xilinx_dsp.cc).
|
||||||
|
// ( 9) Match 'P' output that exclusively drives a PREG
|
||||||
|
// (10) If post-adder and PREG both present, match for a $mux cell driving
|
||||||
|
// the 'C' input, where one of the $mux's inputs is the PREG output.
|
||||||
|
// This indicates an accumulator situation, and one where a $mux exists
|
||||||
|
// to override the accumulated value:
|
||||||
|
// +--------------------------------+
|
||||||
|
// | ____ |
|
||||||
|
// +--| \ |
|
||||||
|
// |$mux|-+ |
|
||||||
|
// 'C' ---|____/ | |
|
||||||
|
// | /-------\ +----+ |
|
||||||
|
// +----+ +-| post- |___|PREG|---+ 'P'
|
||||||
|
// |MREG|------ | adder | +----+
|
||||||
|
// +----+ \-------/
|
||||||
|
// (11) If PREG present, match for a greater-than-or-equal $ge cell attached
|
||||||
|
// to the 'P' output where it is compared to a constant that is a
|
||||||
|
// power-of-2: e.g. `assign overflow = (PREG >= 2**40);`
|
||||||
|
// In this scenario, the pattern detector functionality of a DSP48E1 can
|
||||||
|
// to implement this function
|
||||||
|
// Notes:
|
||||||
|
// - The intention of this pattern matcher is for it to be compatible with
|
||||||
|
// DSP48E1 cells inferred from multiply operations by Yosys, as well as for
|
||||||
|
// user instantiations that may already contain the cells being packed...
|
||||||
|
// (though the latter is currently untested)
|
||||||
|
// - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used
|
||||||
|
// for each *REG match, it has been factored out into two subpatterns:
|
||||||
|
// in_dffe and out_dffe located at the bottom of this file.
|
||||||
|
// - Matching for pattern detector features is currently incomplete. For
|
||||||
|
// example, matching for underflow as well as overflow detection is
|
||||||
|
// possible, as would auto-reset, enabling saturated arithmetic, detecting
|
||||||
|
// custom patterns, etc.
|
||||||
|
|
||||||
|
pattern xilinx_dsp_pack
|
||||||
|
|
||||||
|
state <SigBit> clock
|
||||||
|
state <SigSpec> sigA sigB sigC sigD sigM sigP
|
||||||
|
state <IdString> postAddAB postAddMuxAB
|
||||||
|
state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol
|
||||||
|
state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
|
||||||
|
state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
|
||||||
|
state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
|
||||||
|
state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
|
||||||
|
|
||||||
|
// Variables used for subpatterns
|
||||||
|
state <SigSpec> argQ argD
|
||||||
|
state <bool> ffcepol ffrstpol
|
||||||
|
state <int> ffoffset
|
||||||
|
udata <SigSpec> dffD dffQ
|
||||||
|
udata <SigBit> dffclock
|
||||||
|
udata <Cell*> dff dffcemux dffrstmux
|
||||||
|
udata <bool> dffcepol dffrstpol
|
||||||
|
|
||||||
|
// (1) Starting from a DSP48E1 cell
|
||||||
|
match dsp
|
||||||
|
select dsp->type.in(\DSP48E1)
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigA sigB sigC sigD sigM clock
|
||||||
|
auto unextend = [](const SigSpec &sig) {
|
||||||
|
int i;
|
||||||
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
|
if (sig[i] != sig[i-1])
|
||||||
|
break;
|
||||||
|
// Do not remove non-const sign bit
|
||||||
|
if (sig[i].wire)
|
||||||
|
++i;
|
||||||
|
return sig.extract(0, i);
|
||||||
|
};
|
||||||
|
sigA = unextend(port(dsp, \A));
|
||||||
|
sigB = unextend(port(dsp, \B));
|
||||||
|
|
||||||
|
sigC = port(dsp, \C, SigSpec());
|
||||||
|
sigD = port(dsp, \D, SigSpec());
|
||||||
|
|
||||||
|
SigSpec P = port(dsp, \P);
|
||||||
|
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||||
|
// Only care about those bits that are used
|
||||||
|
int i;
|
||||||
|
for (i = 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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sigM = P;
|
||||||
|
|
||||||
|
clock = port(dsp, \CLK, SigBit());
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
|
||||||
|
// (attached to at most two $mux cells that implement clock-enable or
|
||||||
|
// reset functionality, using a subpattern discussed above)
|
||||||
|
// If matched, treat 'A' input as input of ADREG
|
||||||
|
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
|
||||||
|
if (param(dsp, \ADREG).as_int() == 0) {
|
||||||
|
argQ = sigA;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffAD = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffADrstmux = dffrstmux;
|
||||||
|
ffADrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffADcemux = dffcemux;
|
||||||
|
ffADcepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigA = dffD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
|
||||||
|
// (pre-adder)
|
||||||
|
match preAdd
|
||||||
|
if sigD.empty() || sigD.is_fully_zero()
|
||||||
|
// Ensure that preAdder not already used
|
||||||
|
if param(dsp, \USE_DPORT, Const("FALSE")).decode_string() == "FALSE"
|
||||||
|
if port(dsp, \INMODE, Const(0, 5)).is_fully_zero()
|
||||||
|
|
||||||
|
select preAdd->type.in($add)
|
||||||
|
// Output has to be 25 bits or less
|
||||||
|
select GetSize(port(preAdd, \Y)) <= 25
|
||||||
|
select nusers(port(preAdd, \Y)) == 2
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
// A port has to be 30 bits or less
|
||||||
|
select GetSize(port(preAdd, AB)) <= 30
|
||||||
|
define <IdString> BA (AB == \A ? \B : \A)
|
||||||
|
// D port has to be 25 bits or less
|
||||||
|
select GetSize(port(preAdd, BA)) <= 25
|
||||||
|
index <SigSpec> port(preAdd, \Y) === sigA
|
||||||
|
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigA sigD
|
||||||
|
if (preAdd) {
|
||||||
|
sigA = port(preAdd, \A);
|
||||||
|
sigD = port(preAdd, \B);
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (4) If pre-adder was present, find match 'A' input for A2REG
|
||||||
|
// If pre-adder was not present, move ADREG to A2REG
|
||||||
|
// Then match 'A' input for A1REG
|
||||||
|
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol
|
||||||
|
// Only search for ffA2 if there was a pre-adder
|
||||||
|
// (otherwise ffA2 would have been matched as ffAD)
|
||||||
|
if (preAdd) {
|
||||||
|
if (param(dsp, \AREG).as_int() == 0) {
|
||||||
|
argQ = sigA;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffA2 = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffA2rstmux = dffrstmux;
|
||||||
|
ffArstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffA2cepol = dffcepol;
|
||||||
|
ffA2cemux = dffcemux;
|
||||||
|
}
|
||||||
|
sigA = dffD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And if there wasn't a pre-adder,
|
||||||
|
// move AD register to A
|
||||||
|
else if (ffAD) {
|
||||||
|
log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux);
|
||||||
|
std::swap(ffA2, ffAD);
|
||||||
|
std::swap(ffA2cemux, ffADcemux);
|
||||||
|
std::swap(ffA2rstmux, ffADrstmux);
|
||||||
|
ffA2cepol = ffADcepol;
|
||||||
|
ffArstpol = ffADrstpol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now attempt to match A1
|
||||||
|
if (ffA2) {
|
||||||
|
argQ = sigA;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
|
||||||
|
goto ffA1_end;
|
||||||
|
if (dffrstmux) {
|
||||||
|
if (ffArstpol != dffrstpol)
|
||||||
|
goto ffA1_end;
|
||||||
|
if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto ffA1_end;
|
||||||
|
ffA1rstmux = dffrstmux;
|
||||||
|
}
|
||||||
|
|
||||||
|
ffA1 = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
|
||||||
|
if (dffcemux) {
|
||||||
|
ffA1cemux = dffcemux;
|
||||||
|
ffA1cepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigA = dffD;
|
||||||
|
|
||||||
|
ffA1_end: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (5) Match 'B' input for B2REG
|
||||||
|
// If B2REG, then match 'B' input for B1REG
|
||||||
|
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
|
||||||
|
if (param(dsp, \BREG).as_int() == 0) {
|
||||||
|
argQ = sigB;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffB2 = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffB2rstmux = dffrstmux;
|
||||||
|
ffBrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffB2cemux = dffcemux;
|
||||||
|
ffB2cepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigB = dffD;
|
||||||
|
|
||||||
|
// Now attempt to match B1
|
||||||
|
if (ffB2) {
|
||||||
|
argQ = sigB;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
|
||||||
|
goto ffB1_end;
|
||||||
|
if (dffrstmux) {
|
||||||
|
if (ffBrstpol != dffrstpol)
|
||||||
|
goto ffB1_end;
|
||||||
|
if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
|
||||||
|
goto ffB1_end;
|
||||||
|
ffB1rstmux = dffrstmux;
|
||||||
|
}
|
||||||
|
|
||||||
|
ffB1 = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
|
||||||
|
if (dffcemux) {
|
||||||
|
ffB1cemux = dffcemux;
|
||||||
|
ffB1cepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigB = dffD;
|
||||||
|
|
||||||
|
ffB1_end: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (6) Match 'D' input for DREG
|
||||||
|
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
|
||||||
|
if (param(dsp, \DREG).as_int() == 0) {
|
||||||
|
argQ = sigD;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffD = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffDrstmux = dffrstmux;
|
||||||
|
ffDrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffDcemux = dffcemux;
|
||||||
|
ffDcepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigD = dffD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (7) Match 'P' output that exclusively drives an MREG
|
||||||
|
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
|
||||||
|
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
|
||||||
|
argD = sigM;
|
||||||
|
subpattern(out_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffM = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffMrstmux = dffrstmux;
|
||||||
|
ffMrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffMcemux = dffcemux;
|
||||||
|
ffMcepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigM = dffQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sigP = sigM;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (8) Match 'P' output that exclusively drives one of two inputs to an $add
|
||||||
|
// cell (post-adder).
|
||||||
|
// The other input to the adder is assumed to come in from the 'C' input
|
||||||
|
// (note: 'P' -> 'C' connections that exist for accumulators are
|
||||||
|
// recognised in xilinx_dsp.cc).
|
||||||
|
match postAdd
|
||||||
|
// Ensure that Z mux is not already used
|
||||||
|
if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero()
|
||||||
|
|
||||||
|
select postAdd->type.in($add)
|
||||||
|
select GetSize(port(postAdd, \Y)) <= 48
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
select nusers(port(postAdd, AB)) <= 3
|
||||||
|
filter ffMcemux || nusers(port(postAdd, AB)) == 2
|
||||||
|
filter !ffMcemux || nusers(port(postAdd, AB)) == 3
|
||||||
|
|
||||||
|
index <SigBit> port(postAdd, AB)[0] === sigP[0]
|
||||||
|
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
|
||||||
|
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
|
||||||
|
// Check that remainder of AB is a sign-extension
|
||||||
|
define <bool> AB_SIGNED (param(postAdd, AB == \A ? \A_SIGNED : \B_SIGNED).as_bool())
|
||||||
|
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(AB_SIGNED ? sigP[GetSize(sigP)-1] : State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
|
||||||
|
set postAddAB AB
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigC sigP
|
||||||
|
if (postAdd) {
|
||||||
|
sigC = port(postAdd, postAddAB == \A ? \B : \A);
|
||||||
|
sigP = port(postAdd, \Y);
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (9) Match 'P' output that exclusively drives a PREG
|
||||||
|
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
|
||||||
|
if (param(dsp, \PREG).as_int() == 0) {
|
||||||
|
int users = 2;
|
||||||
|
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
|
||||||
|
if (ffMcemux && !postAdd) users++;
|
||||||
|
if (nusers(sigP) == users) {
|
||||||
|
argD = sigP;
|
||||||
|
subpattern(out_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffP = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffPrstmux = dffrstmux;
|
||||||
|
ffPrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffPcemux = dffcemux;
|
||||||
|
ffPcepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigP = dffQ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (10) If post-adder and PREG both present, match for a $mux cell driving
|
||||||
|
// the 'C' input, where one of the $mux's inputs is the PREG output.
|
||||||
|
// This indicates an accumulator situation, and one where a $mux exists
|
||||||
|
// to override the accumulated value:
|
||||||
|
// +--------------------------------+
|
||||||
|
// | ____ |
|
||||||
|
// +--| \ |
|
||||||
|
// |$mux|-+ |
|
||||||
|
// 'C' ---|____/ | |
|
||||||
|
// | /-------\ +----+ |
|
||||||
|
// +----+ +-| post- |___|PREG|---+ 'P'
|
||||||
|
// |MREG|------ | adder | +----+
|
||||||
|
// +----+ \-------/
|
||||||
|
match postAddMux
|
||||||
|
if postAdd
|
||||||
|
if ffP
|
||||||
|
select postAddMux->type.in($mux)
|
||||||
|
select nusers(port(postAddMux, \Y)) == 2
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
index <SigSpec> port(postAddMux, AB) === sigP
|
||||||
|
index <SigSpec> port(postAddMux, \Y) === sigC
|
||||||
|
set postAddMuxAB AB
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigC
|
||||||
|
if (postAddMux)
|
||||||
|
sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (11) If PREG present, match for a greater-than-or-equal $ge cell attached to
|
||||||
|
// the 'P' output where it is compared to a constant that is a power-of-2:
|
||||||
|
// e.g. `assign overflow = (PREG >= 2**40);`
|
||||||
|
// In this scenario, the pattern detector functionality of a DSP48E1 can
|
||||||
|
// to implement this function
|
||||||
|
match overflow
|
||||||
|
if ffP
|
||||||
|
if param(dsp, \USE_PATTERN_DETECT, Const("NO_PATDET")).decode_string() == "NO_PATDET"
|
||||||
|
select overflow->type.in($ge)
|
||||||
|
select GetSize(port(overflow, \Y)) <= 48
|
||||||
|
select port(overflow, \B).is_fully_const()
|
||||||
|
define <Const> B port(overflow, \B).as_const()
|
||||||
|
select std::count(B.bits.begin(), B.bits.end(), State::S1) == 1
|
||||||
|
index <SigSpec> port(overflow, \A) === sigP
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
accept;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// #######################
|
||||||
|
|
||||||
|
// Subpattern for matching against input registers, based on knowledge of the
|
||||||
|
// 'Q' input. Typically, identifying registers with clock-enable and reset
|
||||||
|
// capability would be a task would be handled by other Yosys passes such as
|
||||||
|
// dff2dffe, but since DSP inference happens much before this, these patterns
|
||||||
|
// have to be manually identified.
|
||||||
|
// At a high level:
|
||||||
|
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||||
|
// 'Q' argument
|
||||||
|
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||||
|
// one that exclusively drives the 'D' input of the $dff, with one of its
|
||||||
|
// $mux inputs being fully zero
|
||||||
|
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||||
|
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||||
|
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||||
|
// the 'Q' output of the $dff
|
||||||
|
subpattern in_dffe
|
||||||
|
arg argD argQ clock
|
||||||
|
|
||||||
|
code
|
||||||
|
dff = nullptr;
|
||||||
|
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
|
|
@ -0,0 +1,234 @@
|
||||||
|
// This file describes the second of three pattern matcher setups that
|
||||||
|
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
||||||
|
// At a high level, it works as follows:
|
||||||
|
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
|
||||||
|
// and (b) uses the 'C' port
|
||||||
|
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
|
||||||
|
// (attached to at most two $mux cells that implement clock-enable or
|
||||||
|
// reset functionality, using a subpattern discussed below)
|
||||||
|
// Notes:
|
||||||
|
// - Running CREG packing after xilinx_dsp_pack is necessary since there is no
|
||||||
|
// guarantee that the cell ordering corresponds to the "expected" case (i.e.
|
||||||
|
// the order in which they appear in the source) thus the possiblity existed
|
||||||
|
// that a register got packed as a CREG into a downstream DSP that should
|
||||||
|
// have otherwise been a PREG of an upstream DSP that had not been visited
|
||||||
|
// yet
|
||||||
|
// - The reason this is separated out from the xilinx_dsp.pmg file is
|
||||||
|
// for efficiency --- each *.pmg file creates a class of the same basename,
|
||||||
|
// which when constructed, creates a custom database tailored to the
|
||||||
|
// pattern(s) contained within. Since the pattern in this file must be
|
||||||
|
// executed after the pattern contained in xilinx_dsp.pmg, it is necessary
|
||||||
|
// to reconstruct this database. Separating the two patterns into
|
||||||
|
// independent files causes two smaller, more specific, databases.
|
||||||
|
|
||||||
|
pattern xilinx_dsp_packC
|
||||||
|
|
||||||
|
udata <std::function<SigSpec(const SigSpec&)>> unextend
|
||||||
|
state <SigBit> clock
|
||||||
|
state <SigSpec> sigC sigP
|
||||||
|
state <bool> ffCcepol ffCrstpol
|
||||||
|
state <Cell*> ffC ffCcemux ffCrstmux
|
||||||
|
|
||||||
|
// Variables used for subpatterns
|
||||||
|
state <SigSpec> argQ argD
|
||||||
|
state <bool> ffcepol ffrstpol
|
||||||
|
state <int> ffoffset
|
||||||
|
udata <SigSpec> dffD dffQ
|
||||||
|
udata <SigBit> dffclock
|
||||||
|
udata <Cell*> dff dffcemux dffrstmux
|
||||||
|
udata <bool> dffcepol dffrstpol
|
||||||
|
|
||||||
|
// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
|
||||||
|
// and (b) uses the 'C' port
|
||||||
|
match dsp
|
||||||
|
select dsp->type.in(\DSP48E1)
|
||||||
|
select param(dsp, \CREG, 1).as_int() == 0
|
||||||
|
select nusers(port(dsp, \C, SigSpec())) > 1
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code sigC sigP clock
|
||||||
|
unextend = [](const SigSpec &sig) {
|
||||||
|
int i;
|
||||||
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
|
if (sig[i] != sig[i-1])
|
||||||
|
break;
|
||||||
|
// Do not remove non-const sign bit
|
||||||
|
if (sig[i].wire)
|
||||||
|
++i;
|
||||||
|
return sig.extract(0, i);
|
||||||
|
};
|
||||||
|
sigC = unextend(port(dsp, \C, SigSpec()));
|
||||||
|
|
||||||
|
SigSpec P = port(dsp, \P);
|
||||||
|
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||||
|
// Only care about those bits that are used
|
||||||
|
int i;
|
||||||
|
for (i = GetSize(P)-1; i >= 0; i--)
|
||||||
|
if (nusers(P[i]) > 1)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
log_assert(nusers(P.extract_end(i)) <= 1);
|
||||||
|
sigP = P.extract(0, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sigP = P;
|
||||||
|
|
||||||
|
clock = port(dsp, \CLK, SigBit());
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
|
||||||
|
// (attached to at most two $mux cells that implement clock-enable or
|
||||||
|
// reset functionality, using the in_dffe subpattern)
|
||||||
|
code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
|
||||||
|
argQ = sigC;
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
ffC = dff;
|
||||||
|
clock = dffclock;
|
||||||
|
if (dffrstmux) {
|
||||||
|
ffCrstmux = dffrstmux;
|
||||||
|
ffCrstpol = dffrstpol;
|
||||||
|
}
|
||||||
|
if (dffcemux) {
|
||||||
|
ffCcemux = dffcemux;
|
||||||
|
ffCcepol = dffcepol;
|
||||||
|
}
|
||||||
|
sigC = dffD;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
code
|
||||||
|
if (ffC)
|
||||||
|
accept;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// #######################
|
||||||
|
|
||||||
|
// Subpattern for matching against input registers, based on knowledge of the
|
||||||
|
// 'Q' input. Typically, identifying registers with clock-enable and reset
|
||||||
|
// capability would be a task would be handled by other Yosys passes such as
|
||||||
|
// dff2dffe, but since DSP inference happens much before this, these patterns
|
||||||
|
// have to be manually identified.
|
||||||
|
// At a high level:
|
||||||
|
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||||
|
// 'Q' argument
|
||||||
|
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||||
|
// one that exclusively drives the 'D' input of the $dff, with one of its
|
||||||
|
// $mux inputs being fully zero
|
||||||
|
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||||
|
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||||
|
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||||
|
// the 'Q' output of the $dff
|
||||||
|
subpattern in_dffe
|
||||||
|
arg argD argQ clock
|
||||||
|
|
||||||
|
code
|
||||||
|
dff = nullptr;
|
||||||
|
for (const auto &c : argQ.chunks()) {
|
||||||
|
// Abandon matches when 'Q' is a constant
|
||||||
|
if (!c.wire)
|
||||||
|
reject;
|
||||||
|
// Abandon matches when 'Q' has the keep attribute set
|
||||||
|
if (c.wire->get_bool_attribute(\keep))
|
||||||
|
reject;
|
||||||
|
// Abandon matches when 'Q' has a non-zero init attribute set
|
||||||
|
// (not supported by DSP48E1)
|
||||||
|
Const init = c.wire->attributes.at(\init, Const());
|
||||||
|
for (auto b : init.extract(c.offset, c.width))
|
||||||
|
if (b != State::Sx && b != State::S0)
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||||
|
// 'Q' argument
|
||||||
|
match ff
|
||||||
|
select ff->type.in($dff)
|
||||||
|
// DSP48E1 does not support clock inversion
|
||||||
|
select param(ff, \CLK_POLARITY).as_bool()
|
||||||
|
|
||||||
|
slice offset GetSize(port(ff, \D))
|
||||||
|
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||||
|
|
||||||
|
// Check that the rest of argQ is present
|
||||||
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
|
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argQ argD
|
||||||
|
SigSpec Q = port(ff, \Q);
|
||||||
|
dff = ff;
|
||||||
|
dffclock = port(ff, \CLK);
|
||||||
|
dffD = argQ;
|
||||||
|
argD = port(ff, \D);
|
||||||
|
argQ = Q;
|
||||||
|
dffD.replace(argQ, argD);
|
||||||
|
// Only search for ffrstmux if dffD only
|
||||||
|
// has two (ff, ffrstmux) users
|
||||||
|
if (nusers(dffD) > 2)
|
||||||
|
argD = SigSpec();
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||||
|
// exclusively drives the 'D' input of the $dff, with one of the $mux
|
||||||
|
// inputs being fully zero
|
||||||
|
match ffrstmux
|
||||||
|
if !argD.empty()
|
||||||
|
select ffrstmux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffrstmux, \Y) === argD
|
||||||
|
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
// DSP48E1 only supports reset to zero
|
||||||
|
select port(ffrstmux, BA).is_fully_zero()
|
||||||
|
|
||||||
|
define <bool> pol (BA == \B)
|
||||||
|
set ffrstpol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffrstmux) {
|
||||||
|
dffrstmux = ffrstmux;
|
||||||
|
dffrstpol = ffrstpol;
|
||||||
|
argD = port(ffrstmux, ffrstpol ? \A : \B);
|
||||||
|
dffD.replace(port(ffrstmux, \Y), argD);
|
||||||
|
|
||||||
|
// Only search for ffcemux if argQ has at
|
||||||
|
// least 3 users (ff, <upstream>, ffrstmux) and
|
||||||
|
// dffD only has two (ff, ffrstmux)
|
||||||
|
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
|
||||||
|
argD = SigSpec();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffrstmux = nullptr;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||||
|
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||||
|
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||||
|
// the 'Q' output of the $dff
|
||||||
|
match ffcemux
|
||||||
|
if !argD.empty()
|
||||||
|
select ffcemux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffcemux, \Y) === argD
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
index <SigSpec> port(ffcemux, AB) === argQ
|
||||||
|
define <bool> pol (AB == \A)
|
||||||
|
set ffcepol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffcemux) {
|
||||||
|
dffcemux = ffcemux;
|
||||||
|
dffcepol = ffcepol;
|
||||||
|
argD = port(ffcemux, ffcepol ? \B : \A);
|
||||||
|
dffD.replace(port(ffcemux, \Y), argD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffcemux = nullptr;
|
||||||
|
endcode
|
|
@ -0,0 +1,427 @@
|
||||||
|
// This file describes the third of three pattern matcher setups that
|
||||||
|
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
|
||||||
|
// At a high level, it works as follows:
|
||||||
|
// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer
|
||||||
|
// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already
|
||||||
|
// use the 'PCOUT' port
|
||||||
|
// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled,
|
||||||
|
// (b) has its Z multiplexer output set to the 'C' port, which is
|
||||||
|
// driven by the 'P' output of the previous DSP cell, and (c) has its
|
||||||
|
// 'PCIN' port unused
|
||||||
|
// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the
|
||||||
|
// previous DSP cell right-shifted by 17 bits
|
||||||
|
// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
|
||||||
|
// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this
|
||||||
|
// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already
|
||||||
|
// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already
|
||||||
|
// use its ACOUT port, then examine if an ACOUT -> ACIN cascade
|
||||||
|
// opportunity exists by matching for a $dff-with-optional-clock-enable-
|
||||||
|
// or-reset and checking that the 'D' input of this register is the same
|
||||||
|
// as the 'A' input of the previous DSP
|
||||||
|
// (4) Same as (3) but for BCOUT -> BCIN cascade
|
||||||
|
// (5) Recursively go to (2.1) until no more matches possible, keeping track
|
||||||
|
// of the longest possible chain found
|
||||||
|
// (6) The longest chain is then divided into chunks of no more than
|
||||||
|
// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
|
||||||
|
// height of a DSP column) with each DSP in each chunk being rewritten
|
||||||
|
// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
|
||||||
|
// Notes:
|
||||||
|
// - Currently, [AB]COUT -> [AB]COUT cascades (3 or 4) are only considered
|
||||||
|
// if a PCOUT -> PCIN cascade is (2.1 or 2.2) first identified; this need
|
||||||
|
// not be the case --- [AB] cascades can exist independently of a P cascade
|
||||||
|
// (though all three cascades must come from the same DSP). This situation
|
||||||
|
// is not handled currently.
|
||||||
|
// - In addition, [AB]COUT -> [AB]COUT cascades (3 or 4) are currently
|
||||||
|
// conservative in that they examine the situation where (a) the previous
|
||||||
|
// DSP has [AB]2REG or [AB]1REG enabled, (b) that the downstream DSP has no
|
||||||
|
// registers enabled, and (c) that there exists only one additional register
|
||||||
|
// between the upstream and downstream DSPs. This can certainly be relaxed
|
||||||
|
// to identify situations ranging from (i) neither DSP uses any registers,
|
||||||
|
// to (ii) upstream DSP has 2 registers, downstream DSP has 2 registers, and
|
||||||
|
// there exists a further 2 registers between them. This remains a TODO
|
||||||
|
// item.
|
||||||
|
|
||||||
|
pattern xilinx_dsp_cascade
|
||||||
|
|
||||||
|
udata <std::function<SigSpec(const SigSpec&)>> unextend
|
||||||
|
udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
|
||||||
|
state <Cell*> next
|
||||||
|
state <SigSpec> clock
|
||||||
|
state <int> AREG BREG
|
||||||
|
|
||||||
|
// Variables used for subpatterns
|
||||||
|
state <SigSpec> argQ argD
|
||||||
|
state <bool> ffcepol ffrstpol
|
||||||
|
state <int> ffoffset
|
||||||
|
udata <SigSpec> dffD dffQ
|
||||||
|
udata <SigBit> dffclock
|
||||||
|
udata <Cell*> dff dffcemux dffrstmux
|
||||||
|
udata <bool> dffcepol dffrstpol
|
||||||
|
|
||||||
|
code
|
||||||
|
#define MAX_DSP_CASCADE 20
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer
|
||||||
|
// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already
|
||||||
|
// use the 'PCOUT' port
|
||||||
|
match first
|
||||||
|
select first->type.in(\DSP48E1)
|
||||||
|
select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
|
||||||
|
select nusers(port(first, \PCOUT, SigSpec())) <= 1
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
// (6) The longest chain is then divided into chunks of no more than
|
||||||
|
// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
|
||||||
|
// height of a DSP column) with each DSP in each chunk being rewritten
|
||||||
|
// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
|
||||||
|
code
|
||||||
|
longest_chain.clear();
|
||||||
|
chain.emplace_back(first, -1, -1, -1);
|
||||||
|
subpattern(tail);
|
||||||
|
finally
|
||||||
|
chain.pop_back();
|
||||||
|
log_assert(chain.empty());
|
||||||
|
if (GetSize(longest_chain) > 1) {
|
||||||
|
Cell *dsp = std::get<0>(longest_chain.front());
|
||||||
|
|
||||||
|
Cell *dsp_pcin;
|
||||||
|
int P, AREG, BREG;
|
||||||
|
for (int i = 1; i < GetSize(longest_chain); i++) {
|
||||||
|
std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i];
|
||||||
|
|
||||||
|
if (i % MAX_DSP_CASCADE > 0) {
|
||||||
|
if (P >= 0) {
|
||||||
|
Wire *cascade = module->addWire(NEW_ID, 48);
|
||||||
|
dsp_pcin->setPort(ID(C), Const(0, 48));
|
||||||
|
dsp_pcin->setPort(ID(PCIN), cascade);
|
||||||
|
dsp->setPort(ID(PCOUT), cascade);
|
||||||
|
add_siguser(cascade, dsp_pcin);
|
||||||
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
|
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
|
||||||
|
if (P == 17)
|
||||||
|
opmode[6] = State::S1;
|
||||||
|
else if (P == 0)
|
||||||
|
opmode[6] = State::S0;
|
||||||
|
else log_abort();
|
||||||
|
|
||||||
|
opmode[5] = State::S0;
|
||||||
|
opmode[4] = State::S1;
|
||||||
|
dsp_pcin->setPort(\OPMODE, opmode);
|
||||||
|
|
||||||
|
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
|
}
|
||||||
|
if (AREG >= 0) {
|
||||||
|
Wire *cascade = module->addWire(NEW_ID, 30);
|
||||||
|
dsp_pcin->setPort(ID(A), Const(0, 30));
|
||||||
|
dsp_pcin->setPort(ID(ACIN), cascade);
|
||||||
|
dsp->setPort(ID(ACOUT), cascade);
|
||||||
|
add_siguser(cascade, dsp_pcin);
|
||||||
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
|
dsp->setParam(ID(ACASCREG), AREG);
|
||||||
|
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
|
||||||
|
|
||||||
|
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
|
}
|
||||||
|
if (BREG >= 0) {
|
||||||
|
Wire *cascade = module->addWire(NEW_ID, 18);
|
||||||
|
dsp_pcin->setPort(ID(B), Const(0, 18));
|
||||||
|
dsp_pcin->setPort(ID(BCIN), cascade);
|
||||||
|
dsp->setPort(ID(BCOUT), cascade);
|
||||||
|
add_siguser(cascade, dsp_pcin);
|
||||||
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
|
dsp->setParam(ID(BCASCREG), BREG);
|
||||||
|
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
|
||||||
|
|
||||||
|
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp = dsp_pcin;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
subpattern tail
|
||||||
|
arg first
|
||||||
|
arg next
|
||||||
|
|
||||||
|
// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled,
|
||||||
|
// (b) has its Z multiplexer output set to the 'C' port, which is
|
||||||
|
// driven by the 'P' output of the previous DSP cell, and (c) has its
|
||||||
|
// 'PCIN' port unused
|
||||||
|
match nextP
|
||||||
|
select nextP->type.in(\DSP48E1)
|
||||||
|
select !param(nextP, \CREG, State::S1).as_bool()
|
||||||
|
select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
|
||||||
|
select nusers(port(nextP, \C, SigSpec())) > 1
|
||||||
|
select nusers(port(nextP, \PCIN, SigSpec())) == 0
|
||||||
|
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the
|
||||||
|
// previous DSP cell right-shifted by 17 bits
|
||||||
|
match nextP_shift17
|
||||||
|
if !nextP
|
||||||
|
select nextP_shift17->type.in(\DSP48E1)
|
||||||
|
select !param(nextP_shift17, \CREG, State::S1).as_bool()
|
||||||
|
select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
|
||||||
|
select nusers(port(nextP_shift17, \C, SigSpec())) > 1
|
||||||
|
select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0
|
||||||
|
index <SigBit> port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17]
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code next
|
||||||
|
next = nextP;
|
||||||
|
if (!nextP)
|
||||||
|
next = nextP_shift17;
|
||||||
|
if (next) {
|
||||||
|
unextend = [](const SigSpec &sig) {
|
||||||
|
int i;
|
||||||
|
for (i = GetSize(sig)-1; i > 0; i--)
|
||||||
|
if (sig[i] != sig[i-1])
|
||||||
|
break;
|
||||||
|
// Do not remove non-const sign bit
|
||||||
|
if (sig[i].wire)
|
||||||
|
++i;
|
||||||
|
return sig.extract(0, i);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
|
||||||
|
// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this
|
||||||
|
// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already
|
||||||
|
// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already
|
||||||
|
// use its ACOUT port, then examine if an ACOUT -> ACIN cascade
|
||||||
|
// opportunity exists by matching for a $dff-with-optional-clock-enable-
|
||||||
|
// or-reset and checking that the 'D' input of this register is the same
|
||||||
|
// as the 'A' input of the previous DSP
|
||||||
|
code argQ clock AREG
|
||||||
|
AREG = -1;
|
||||||
|
if (next) {
|
||||||
|
Cell *prev = std::get<0>(chain.back());
|
||||||
|
if (param(prev, \AREG, 2).as_int() > 0 &&
|
||||||
|
param(next, \AREG, 2).as_int() > 0 &&
|
||||||
|
param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||||
|
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
|
||||||
|
argQ = unextend(port(next, \A));
|
||||||
|
clock = port(prev, \CLK);
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
||||||
|
goto reject_AREG;
|
||||||
|
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
||||||
|
goto reject_AREG;
|
||||||
|
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0)
|
||||||
|
goto reject_AREG;
|
||||||
|
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0))
|
||||||
|
goto reject_AREG;
|
||||||
|
if (dffD == unextend(port(prev, \A)))
|
||||||
|
AREG = 1;
|
||||||
|
reject_AREG: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (4) Same as (3) but for BCOUT -> BCIN cascade
|
||||||
|
code argQ clock BREG
|
||||||
|
BREG = -1;
|
||||||
|
if (next) {
|
||||||
|
Cell *prev = std::get<0>(chain.back());
|
||||||
|
if (param(prev, \BREG, 2).as_int() > 0 &&
|
||||||
|
param(next, \BREG, 2).as_int() > 0 &&
|
||||||
|
param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
|
||||||
|
port(next, \BCIN, SigSpec()).is_fully_zero() &&
|
||||||
|
nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
|
||||||
|
argQ = unextend(port(next, \B));
|
||||||
|
clock = port(prev, \CLK);
|
||||||
|
subpattern(in_dffe);
|
||||||
|
if (dff) {
|
||||||
|
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
|
||||||
|
goto reject_BREG;
|
||||||
|
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
|
||||||
|
goto reject_BREG;
|
||||||
|
if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0)
|
||||||
|
goto reject_BREG;
|
||||||
|
if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0))
|
||||||
|
goto reject_BREG;
|
||||||
|
if (dffD == unextend(port(prev, \B)))
|
||||||
|
BREG = 1;
|
||||||
|
reject_BREG: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (5) Recursively go to (2.1) until no more matches possible, recording the
|
||||||
|
// longest possible chain
|
||||||
|
code
|
||||||
|
if (next) {
|
||||||
|
chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
|
||||||
|
|
||||||
|
SigSpec sigC = unextend(port(next, \C));
|
||||||
|
|
||||||
|
if (nextP_shift17) {
|
||||||
|
if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) &&
|
||||||
|
port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC)
|
||||||
|
subpattern(tail);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) &&
|
||||||
|
port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC)
|
||||||
|
subpattern(tail);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (GetSize(chain) > GetSize(longest_chain))
|
||||||
|
longest_chain = chain;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
if (next)
|
||||||
|
chain.pop_back();
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// #######################
|
||||||
|
|
||||||
|
// Subpattern for matching against input registers, based on knowledge of the
|
||||||
|
// 'Q' input. Typically, identifying registers with clock-enable and reset
|
||||||
|
// capability would be a task would be handled by other Yosys passes such as
|
||||||
|
// dff2dffe, but since DSP inference happens much before this, these patterns
|
||||||
|
// have to be manually identified.
|
||||||
|
// At a high level:
|
||||||
|
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||||
|
// 'Q' argument
|
||||||
|
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||||
|
// one that exclusively drives the 'D' input of the $dff, with one of its
|
||||||
|
// $mux inputs being fully zero
|
||||||
|
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||||
|
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||||
|
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||||
|
// the 'Q' output of the $dff
|
||||||
|
subpattern in_dffe
|
||||||
|
arg argD argQ clock
|
||||||
|
|
||||||
|
code
|
||||||
|
dff = nullptr;
|
||||||
|
for (const auto &c : argQ.chunks()) {
|
||||||
|
// Abandon matches when 'Q' is a constant
|
||||||
|
if (!c.wire)
|
||||||
|
reject;
|
||||||
|
// Abandon matches when 'Q' has the keep attribute set
|
||||||
|
if (c.wire->get_bool_attribute(\keep))
|
||||||
|
reject;
|
||||||
|
// Abandon matches when 'Q' has a non-zero init attribute set
|
||||||
|
// (not supported by DSP48E1)
|
||||||
|
Const init = c.wire->attributes.at(\init, Const());
|
||||||
|
for (auto b : init.extract(c.offset, c.width))
|
||||||
|
if (b != State::Sx && b != State::S0)
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (1) Starting from a $dff cell that (partially or fully) drives the given
|
||||||
|
// 'Q' argument
|
||||||
|
match ff
|
||||||
|
select ff->type.in($dff)
|
||||||
|
// DSP48E1 does not support clock inversion
|
||||||
|
select param(ff, \CLK_POLARITY).as_bool()
|
||||||
|
|
||||||
|
slice offset GetSize(port(ff, \D))
|
||||||
|
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||||
|
|
||||||
|
// Check that the rest of argQ is present
|
||||||
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
|
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||||
|
|
||||||
|
set ffoffset offset
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argQ argD
|
||||||
|
SigSpec Q = port(ff, \Q);
|
||||||
|
dff = ff;
|
||||||
|
dffclock = port(ff, \CLK);
|
||||||
|
dffD = argQ;
|
||||||
|
argD = port(ff, \D);
|
||||||
|
argQ = Q;
|
||||||
|
dffD.replace(argQ, argD);
|
||||||
|
// Only search for ffrstmux if dffD only
|
||||||
|
// has two (ff, ffrstmux) users
|
||||||
|
if (nusers(dffD) > 2)
|
||||||
|
argD = SigSpec();
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (2) Match for a $mux cell implementing synchronous reset semantics ---
|
||||||
|
// exclusively drives the 'D' input of the $dff, with one of the $mux
|
||||||
|
// inputs being fully zero
|
||||||
|
match ffrstmux
|
||||||
|
if !argD.empty()
|
||||||
|
select ffrstmux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffrstmux, \Y) === argD
|
||||||
|
|
||||||
|
choice <IdString> BA {\B, \A}
|
||||||
|
// DSP48E1 only supports reset to zero
|
||||||
|
select port(ffrstmux, BA).is_fully_zero()
|
||||||
|
|
||||||
|
define <bool> pol (BA == \B)
|
||||||
|
set ffrstpol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffrstmux) {
|
||||||
|
dffrstmux = ffrstmux;
|
||||||
|
dffrstpol = ffrstpol;
|
||||||
|
argD = port(ffrstmux, ffrstpol ? \A : \B);
|
||||||
|
dffD.replace(port(ffrstmux, \Y), argD);
|
||||||
|
|
||||||
|
// Only search for ffcemux if argQ has at
|
||||||
|
// least 3 users (ff, <upstream>, ffrstmux) and
|
||||||
|
// dffD only has two (ff, ffrstmux)
|
||||||
|
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
|
||||||
|
argD = SigSpec();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffrstmux = nullptr;
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// (3) Match for a $mux cell implement clock enable semantics --- one that
|
||||||
|
// exclusively drives the 'D' input of the $dff (or the other input of
|
||||||
|
// the reset $mux) and where one of this $mux's inputs is connected to
|
||||||
|
// the 'Q' output of the $dff
|
||||||
|
match ffcemux
|
||||||
|
if !argD.empty()
|
||||||
|
select ffcemux->type.in($mux)
|
||||||
|
index <SigSpec> port(ffcemux, \Y) === argD
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
index <SigSpec> port(ffcemux, AB) === argQ
|
||||||
|
define <bool> pol (AB == \A)
|
||||||
|
set ffcepol pol
|
||||||
|
semioptional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code argD
|
||||||
|
if (ffcemux) {
|
||||||
|
dffcemux = ffcemux;
|
||||||
|
dffcepol = ffcepol;
|
||||||
|
argD = port(ffcemux, ffcepol ? \B : \A);
|
||||||
|
dffD.replace(port(ffcemux, \Y), argD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dffcemux = nullptr;
|
||||||
|
endcode
|
|
@ -13,9 +13,9 @@ endcode
|
||||||
match first
|
match first
|
||||||
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
||||||
select !first->has_keep_attr()
|
select !first->has_keep_attr()
|
||||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||||
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
|
||||||
filter !non_first_cells.count(first)
|
filter !non_first_cells.count(first)
|
||||||
generate
|
generate
|
||||||
SigSpec C = module->addWire(NEW_ID);
|
SigSpec C = module->addWire(NEW_ID);
|
||||||
|
@ -84,9 +84,9 @@ arg en_port
|
||||||
match first
|
match first
|
||||||
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
||||||
select !first->has_keep_attr()
|
select !first->has_keep_attr()
|
||||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||||
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code clk_port en_port
|
code clk_port en_port
|
||||||
|
@ -111,10 +111,10 @@ match next
|
||||||
index <SigBit> port(next, \Q) === port(first, \D)
|
index <SigBit> port(next, \Q) === port(first, \D)
|
||||||
filter port(next, clk_port) == port(first, clk_port)
|
filter port(next, clk_port) == port(first, clk_port)
|
||||||
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code
|
code
|
||||||
|
@ -138,10 +138,10 @@ match next
|
||||||
index <SigBit> port(next, \Q) === port(chain.back(), \D)
|
index <SigBit> port(next, \Q) === port(chain.back(), \D)
|
||||||
filter port(next, clk_port) == port(first, clk_port)
|
filter port(next, clk_port) == port(first, clk_port)
|
||||||
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||||
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
|
||||||
generate
|
generate
|
||||||
Cell *cell = module->addCell(NEW_ID, chain.back()->type);
|
Cell *cell = module->addCell(NEW_ID, chain.back()->type);
|
||||||
cell->setPort(\C, chain.back()->getPort(\C));
|
cell->setPort(\C, chain.back()->getPort(\C));
|
||||||
|
@ -149,7 +149,7 @@ generate
|
||||||
cell->setPort(\Q, chain.back()->getPort(\D));
|
cell->setPort(\Q, chain.back()->getPort(\D));
|
||||||
if (cell->type == \FDRE) {
|
if (cell->type == \FDRE) {
|
||||||
if (rng(2) == 0)
|
if (rng(2) == 0)
|
||||||
cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
|
cell->setPort(\R, port(chain.back(), \R, State::S0));
|
||||||
cell->setPort(\CE, chain.back()->getPort(\CE));
|
cell->setPort(\CE, chain.back()->getPort(\CE));
|
||||||
}
|
}
|
||||||
else if (cell->type.begins_with("$_DFFE_"))
|
else if (cell->type.begins_with("$_DFFE_"))
|
||||||
|
|
|
@ -349,6 +349,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proc->get_bool_attribute(ID(always_ff)))
|
||||||
|
log_error("Found non edge/level sensitive event in always_ff process `%s.%s'.\n",
|
||||||
|
db.module->name.c_str(), proc->name.c_str());
|
||||||
|
|
||||||
for (auto ss : sr->actions)
|
for (auto ss : sr->actions)
|
||||||
{
|
{
|
||||||
db.sigmap.apply(ss.first);
|
db.sigmap.apply(ss.first);
|
||||||
|
@ -383,6 +387,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (auto chunk : nolatches_bits.first.chunks()) {
|
for (auto chunk : nolatches_bits.first.chunks()) {
|
||||||
SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
|
SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
|
||||||
|
if (proc->get_bool_attribute(ID(always_latch)))
|
||||||
|
log_error("No latch inferred for signal `%s.%s' from always_latch process `%s.%s'.\n",
|
||||||
|
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
|
||||||
|
else
|
||||||
log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
|
log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
|
||||||
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
|
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
|
||||||
db.module->connect(lhs, rhs);
|
db.module->connect(lhs, rhs);
|
||||||
|
@ -410,6 +418,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
cell->set_src_attribute(src);
|
cell->set_src_attribute(src);
|
||||||
db.generated_dlatches.insert(cell);
|
db.generated_dlatches.insert(cell);
|
||||||
|
|
||||||
|
if (proc->get_bool_attribute(ID(always_comb)))
|
||||||
|
log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n",
|
||||||
|
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
|
||||||
|
else
|
||||||
log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
|
log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
|
||||||
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
|
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,6 +198,7 @@ struct Async2syncPass : public Pass {
|
||||||
module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
|
module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell->setPort("\\D", sig_q);
|
||||||
cell->setPort("\\Q", new_q);
|
cell->setPort("\\Q", new_q);
|
||||||
cell->unsetPort("\\EN");
|
cell->unsetPort("\\EN");
|
||||||
cell->unsetParam("\\EN_POLARITY");
|
cell->unsetParam("\\EN_POLARITY");
|
||||||
|
|
|
@ -71,21 +71,21 @@ RTLIL::Module *module;
|
||||||
bool clk_polarity, en_polarity;
|
bool clk_polarity, en_polarity;
|
||||||
RTLIL::SigSpec clk_sig, en_sig;
|
RTLIL::SigSpec clk_sig, en_sig;
|
||||||
|
|
||||||
inline std::string remap_name(RTLIL::IdString abc_name)
|
inline std::string remap_name(RTLIL::IdString abc9_name)
|
||||||
{
|
{
|
||||||
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
|
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_loops(RTLIL::Design *design)
|
void handle_loops(RTLIL::Design *design)
|
||||||
{
|
{
|
||||||
Pass::call(design, "scc -set_attr abc_scc_id {}");
|
Pass::call(design, "scc -set_attr abc9_scc_id {}");
|
||||||
|
|
||||||
// For every unique SCC found, (arbitrarily) find the first
|
// For every unique SCC found, (arbitrarily) find the first
|
||||||
// cell in the component, and select (and mark) all its output
|
// cell in the component, and select (and mark) all its output
|
||||||
// wires
|
// wires
|
||||||
pool<RTLIL::Const> ids_seen;
|
pool<RTLIL::Const> ids_seen;
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
auto it = cell->attributes.find(ID(abc_scc_id));
|
auto it = cell->attributes.find(ID(abc9_scc_id));
|
||||||
if (it != cell->attributes.end()) {
|
if (it != cell->attributes.end()) {
|
||||||
auto r = ids_seen.insert(it->second);
|
auto r = ids_seen.insert(it->second);
|
||||||
if (r.second) {
|
if (r.second) {
|
||||||
|
@ -105,7 +105,7 @@ void handle_loops(RTLIL::Design *design)
|
||||||
log_assert(w->port_input);
|
log_assert(w->port_input);
|
||||||
log_assert(b.offset < GetSize(w));
|
log_assert(b.offset < GetSize(w));
|
||||||
}
|
}
|
||||||
w->set_bool_attribute(ID(abc_scc_break));
|
w->set_bool_attribute(ID(abc9_scc_break));
|
||||||
module->swap_names(b.wire, w);
|
module->swap_names(b.wire, w);
|
||||||
c.second = RTLIL::SigBit(w, b.offset);
|
c.second = RTLIL::SigBit(w, b.offset);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ void handle_loops(RTLIL::Design *design)
|
||||||
module->fixup_ports();
|
module->fixup_ports();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string add_echos_to_abc_cmd(std::string str)
|
std::string add_echos_to_abc9_cmd(std::string str)
|
||||||
{
|
{
|
||||||
std::string new_str, token;
|
std::string new_str, token;
|
||||||
for (size_t i = 0; i < str.size(); i++) {
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
|
@ -140,7 +140,7 @@ std::string add_echos_to_abc_cmd(std::string str)
|
||||||
return new_str;
|
return new_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fold_abc_cmd(std::string str)
|
std::string fold_abc9_cmd(std::string str)
|
||||||
{
|
{
|
||||||
std::string token, new_str = " ";
|
std::string token, new_str = " ";
|
||||||
int char_counter = 10;
|
int char_counter = 10;
|
||||||
|
@ -184,7 +184,7 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct abc_output_filter
|
struct abc9_output_filter
|
||||||
{
|
{
|
||||||
bool got_cr;
|
bool got_cr;
|
||||||
int escape_seq_state;
|
int escape_seq_state;
|
||||||
|
@ -192,7 +192,7 @@ struct abc_output_filter
|
||||||
std::string tempdir_name;
|
std::string tempdir_name;
|
||||||
bool show_tempdir;
|
bool show_tempdir;
|
||||||
|
|
||||||
abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
|
abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
|
||||||
{
|
{
|
||||||
got_cr = false;
|
got_cr = false;
|
||||||
escape_seq_state = 0;
|
escape_seq_state = 0;
|
||||||
|
@ -247,7 +247,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
|
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
|
||||||
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
|
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
|
||||||
bool show_tempdir, std::string box_file, std::string lut_file,
|
bool show_tempdir, std::string box_file, std::string lut_file,
|
||||||
std::string wire_delay, const dict<int,IdString> &box_lookup
|
std::string wire_delay, const dict<int,IdString> &box_lookup, bool nomfs
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
module = current_module;
|
module = current_module;
|
||||||
|
@ -293,68 +293,72 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n",
|
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n",
|
||||||
module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
|
module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
|
||||||
|
|
||||||
std::string abc_script;
|
std::string abc9_script;
|
||||||
|
|
||||||
if (!lut_costs.empty()) {
|
if (!lut_costs.empty()) {
|
||||||
abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
|
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
|
||||||
if (!box_file.empty())
|
if (!box_file.empty())
|
||||||
abc_script += stringf("read_box -v %s; ", box_file.c_str());
|
abc9_script += stringf("read_box -v %s; ", box_file.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (!lut_file.empty()) {
|
if (!lut_file.empty()) {
|
||||||
abc_script += stringf("read_lut %s; ", lut_file.c_str());
|
abc9_script += stringf("read_lut %s; ", lut_file.c_str());
|
||||||
if (!box_file.empty())
|
if (!box_file.empty())
|
||||||
abc_script += stringf("read_box -v %s; ", box_file.c_str());
|
abc9_script += stringf("read_box -v %s; ", box_file.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
||||||
abc_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
|
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
|
||||||
|
|
||||||
if (!script_file.empty()) {
|
if (!script_file.empty()) {
|
||||||
if (script_file[0] == '+') {
|
if (script_file[0] == '+') {
|
||||||
for (size_t i = 1; i < script_file.size(); i++)
|
for (size_t i = 1; i < script_file.size(); i++)
|
||||||
if (script_file[i] == '\'')
|
if (script_file[i] == '\'')
|
||||||
abc_script += "'\\''";
|
abc9_script += "'\\''";
|
||||||
else if (script_file[i] == ',')
|
else if (script_file[i] == ',')
|
||||||
abc_script += " ";
|
abc9_script += " ";
|
||||||
else
|
else
|
||||||
abc_script += script_file[i];
|
abc9_script += script_file[i];
|
||||||
} else
|
} else
|
||||||
abc_script += stringf("source %s", script_file.c_str());
|
abc9_script += stringf("source %s", script_file.c_str());
|
||||||
} else if (!lut_costs.empty() || !lut_file.empty()) {
|
} else if (!lut_costs.empty() || !lut_file.empty()) {
|
||||||
//bool all_luts_cost_same = true;
|
//bool all_luts_cost_same = true;
|
||||||
//for (int this_cost : lut_costs)
|
//for (int this_cost : lut_costs)
|
||||||
// if (this_cost != lut_costs.front())
|
// if (this_cost != lut_costs.front())
|
||||||
// all_luts_cost_same = false;
|
// all_luts_cost_same = false;
|
||||||
abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
|
abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
|
||||||
//if (all_luts_cost_same && !fast_mode)
|
//if (all_luts_cost_same && !fast_mode)
|
||||||
// abc_script += "; lutpack {S}";
|
// abc9_script += "; lutpack {S}";
|
||||||
} else
|
} else
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
||||||
//if (script_file.empty() && !delay_target.empty())
|
//if (script_file.empty() && !delay_target.empty())
|
||||||
// for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
|
// for (size_t pos = abc9_script.find("dretime;"); pos != std::string::npos; pos = abc9_script.find("dretime;", pos+1))
|
||||||
// abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
|
// abc9_script = abc9_script.substr(0, pos) + "dretime; retime -o {D};" + abc9_script.substr(pos+8);
|
||||||
|
|
||||||
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
|
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
|
||||||
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
|
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
//for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
|
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
|
||||||
// abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
|
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
for (size_t pos = abc_script.find("{W}"); pos != std::string::npos; pos = abc_script.find("{W}", pos))
|
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
|
||||||
abc_script = abc_script.substr(0, pos) + wire_delay + abc_script.substr(pos+3);
|
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
|
||||||
|
|
||||||
abc_script += stringf("; &write %s/output.aig", tempdir_name.c_str());
|
if (nomfs)
|
||||||
abc_script = add_echos_to_abc_cmd(abc_script);
|
for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos))
|
||||||
|
abc9_script = abc9_script.erase(pos, strlen("&mfs"));
|
||||||
|
|
||||||
for (size_t i = 0; i+1 < abc_script.size(); i++)
|
abc9_script += stringf("; &write %s/output.aig", tempdir_name.c_str());
|
||||||
if (abc_script[i] == ';' && abc_script[i+1] == ' ')
|
abc9_script = add_echos_to_abc9_cmd(abc9_script);
|
||||||
abc_script[i+1] = '\n';
|
|
||||||
|
for (size_t i = 0; i+1 < abc9_script.size(); i++)
|
||||||
|
if (abc9_script[i] == ';' && abc9_script[i+1] == ' ')
|
||||||
|
abc9_script[i+1] = '\n';
|
||||||
|
|
||||||
FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
|
FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
|
||||||
fprintf(f, "%s\n", abc_script.c_str());
|
fprintf(f, "%s\n", abc9_script.c_str());
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (dff_mode || !clk_str.empty())
|
if (dff_mode || !clk_str.empty())
|
||||||
|
@ -420,7 +424,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
// the expose operation -- remove them from PO/PI
|
// the expose operation -- remove them from PO/PI
|
||||||
// and re-connecting them back together
|
// and re-connecting them back together
|
||||||
for (auto wire : module->wires()) {
|
for (auto wire : module->wires()) {
|
||||||
auto it = wire->attributes.find(ID(abc_scc_break));
|
auto it = wire->attributes.find(ID(abc9_scc_break));
|
||||||
if (it != wire->attributes.end()) {
|
if (it != wire->attributes.end()) {
|
||||||
wire->attributes.erase(it);
|
wire->attributes.erase(it);
|
||||||
log_assert(wire->port_output);
|
log_assert(wire->port_output);
|
||||||
|
@ -450,28 +454,28 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
|
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
|
||||||
|
|
||||||
#ifndef YOSYS_LINK_ABC
|
#ifndef YOSYS_LINK_ABC
|
||||||
abc_output_filter filt(tempdir_name, show_tempdir);
|
abc9_output_filter filt(tempdir_name, show_tempdir);
|
||||||
int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
|
int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
|
||||||
#else
|
#else
|
||||||
// These needs to be mutable, supposedly due to getopt
|
// These needs to be mutable, supposedly due to getopt
|
||||||
char *abc_argv[5];
|
char *abc9_argv[5];
|
||||||
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
|
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
|
||||||
abc_argv[0] = strdup(exe_file.c_str());
|
abc9_argv[0] = strdup(exe_file.c_str());
|
||||||
abc_argv[1] = strdup("-s");
|
abc9_argv[1] = strdup("-s");
|
||||||
abc_argv[2] = strdup("-f");
|
abc9_argv[2] = strdup("-f");
|
||||||
abc_argv[3] = strdup(tmp_script_name.c_str());
|
abc9_argv[3] = strdup(tmp_script_name.c_str());
|
||||||
abc_argv[4] = 0;
|
abc9_argv[4] = 0;
|
||||||
int ret = Abc_RealMain(4, abc_argv);
|
int ret = Abc_RealMain(4, abc9_argv);
|
||||||
free(abc_argv[0]);
|
free(abc9_argv[0]);
|
||||||
free(abc_argv[1]);
|
free(abc9_argv[1]);
|
||||||
free(abc_argv[2]);
|
free(abc9_argv[2]);
|
||||||
free(abc_argv[3]);
|
free(abc9_argv[3]);
|
||||||
#endif
|
#endif
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
|
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
|
||||||
|
|
||||||
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
|
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
|
||||||
ifs.open(buffer);
|
ifs.open(buffer, std::ifstream::binary);
|
||||||
if (ifs.fail())
|
if (ifs.fail())
|
||||||
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
|
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
|
||||||
|
|
||||||
|
@ -513,7 +517,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
signal = std::move(bits);
|
signal = std::move(bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
dict<IdString, bool> abc_box;
|
dict<IdString, bool> abc9_box;
|
||||||
vector<RTLIL::Cell*> boxes;
|
vector<RTLIL::Cell*> boxes;
|
||||||
for (const auto &it : module->cells_) {
|
for (const auto &it : module->cells_) {
|
||||||
auto cell = it.second;
|
auto cell = it.second;
|
||||||
|
@ -521,10 +525,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto jt = abc_box.find(cell->type);
|
auto jt = abc9_box.find(cell->type);
|
||||||
if (jt == abc_box.end()) {
|
if (jt == abc9_box.end()) {
|
||||||
RTLIL::Module* box_module = design->module(cell->type);
|
RTLIL::Module* box_module = design->module(cell->type);
|
||||||
jt = abc_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc_box_id)))).first;
|
jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first;
|
||||||
}
|
}
|
||||||
if (jt->second)
|
if (jt->second)
|
||||||
boxes.emplace_back(cell);
|
boxes.emplace_back(cell);
|
||||||
|
@ -606,7 +610,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
existing_cell = module->cell(c->name);
|
existing_cell = module->cell(c->name);
|
||||||
log_assert(existing_cell);
|
log_assert(existing_cell);
|
||||||
cell = module->addCell(remap_name(c->name), c->type);
|
cell = module->addCell(remap_name(c->name), c->type);
|
||||||
module->swap_names(cell, existing_cell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
|
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
|
||||||
|
@ -642,8 +645,22 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cell : boxes)
|
for (auto existing_cell : boxes) {
|
||||||
module->remove(cell);
|
Cell *cell = module->cell(remap_name(existing_cell->name));
|
||||||
|
if (cell) {
|
||||||
|
for (auto &conn : existing_cell->connections()) {
|
||||||
|
if (!conn.second.is_wire())
|
||||||
|
continue;
|
||||||
|
Wire *wire = conn.second.as_wire();
|
||||||
|
if (!wire->get_bool_attribute(ID(abc9_padding)))
|
||||||
|
continue;
|
||||||
|
cell->unsetPort(conn.first);
|
||||||
|
log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second));
|
||||||
|
}
|
||||||
|
module->swap_names(cell, existing_cell);
|
||||||
|
}
|
||||||
|
module->remove(existing_cell);
|
||||||
|
}
|
||||||
|
|
||||||
// Copy connections (and rename) from mapped_mod to module
|
// Copy connections (and rename) from mapped_mod to module
|
||||||
for (auto conn : mapped_mod->connections()) {
|
for (auto conn : mapped_mod->connections()) {
|
||||||
|
@ -814,17 +831,17 @@ struct Abc9Pass : public Pass {
|
||||||
log(" if no -script parameter is given, the following scripts are used:\n");
|
log(" if no -script parameter is given, the following scripts are used:\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" for -lut/-luts (only one LUT size):\n");
|
log(" for -lut/-luts (only one LUT size):\n");
|
||||||
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str());
|
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str());
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" for -lut/-luts (different LUT sizes):\n");
|
log(" for -lut/-luts (different LUT sizes):\n");
|
||||||
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str());
|
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str());
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -fast\n");
|
log(" -fast\n");
|
||||||
log(" use different default scripts that are slightly faster (at the cost\n");
|
log(" use different default scripts that are slightly faster (at the cost\n");
|
||||||
log(" of output quality):\n");
|
log(" of output quality):\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" for -lut/-luts:\n");
|
log(" for -lut/-luts:\n");
|
||||||
log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str());
|
log("%s\n", fold_abc9_cmd(ABC_FAST_COMMAND_LUT).c_str());
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -D <picoseconds>\n");
|
log(" -D <picoseconds>\n");
|
||||||
log(" set delay target. the string {D} in the default scripts above is\n");
|
log(" set delay target. the string {D} in the default scripts above is\n");
|
||||||
|
@ -908,6 +925,7 @@ struct Abc9Pass : public Pass {
|
||||||
std::string delay_target, lutin_shared = "-S 1", wire_delay;
|
std::string delay_target, lutin_shared = "-S 1", wire_delay;
|
||||||
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
|
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
|
||||||
bool show_tempdir = false;
|
bool show_tempdir = false;
|
||||||
|
bool nomfs = false;
|
||||||
vector<int> lut_costs;
|
vector<int> lut_costs;
|
||||||
markgroups = false;
|
markgroups = false;
|
||||||
|
|
||||||
|
@ -1030,6 +1048,10 @@ struct Abc9Pass : public Pass {
|
||||||
wire_delay = "-W " + args[++argidx];
|
wire_delay = "-W " + args[++argidx];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-nomfs") {
|
||||||
|
nomfs = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -1044,7 +1066,7 @@ struct Abc9Pass : public Pass {
|
||||||
|
|
||||||
dict<int,IdString> box_lookup;
|
dict<int,IdString> box_lookup;
|
||||||
for (auto m : design->modules()) {
|
for (auto m : design->modules()) {
|
||||||
auto it = m->attributes.find(ID(abc_box_id));
|
auto it = m->attributes.find(ID(abc9_box_id));
|
||||||
if (it == m->attributes.end())
|
if (it == m->attributes.end())
|
||||||
continue;
|
continue;
|
||||||
if (m->name.begins_with("$paramod"))
|
if (m->name.begins_with("$paramod"))
|
||||||
|
@ -1052,7 +1074,7 @@ struct Abc9Pass : public Pass {
|
||||||
auto id = it->second.as_int();
|
auto id = it->second.as_int();
|
||||||
auto r = box_lookup.insert(std::make_pair(id, m->name));
|
auto r = box_lookup.insert(std::make_pair(id, m->name));
|
||||||
if (!r.second)
|
if (!r.second)
|
||||||
log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n",
|
log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n",
|
||||||
log_id(m), id, log_id(r.first->second));
|
log_id(m), id, log_id(r.first->second));
|
||||||
log_assert(r.second);
|
log_assert(r.second);
|
||||||
|
|
||||||
|
@ -1060,24 +1082,24 @@ struct Abc9Pass : public Pass {
|
||||||
for (auto p : m->ports) {
|
for (auto p : m->ports) {
|
||||||
auto w = m->wire(p);
|
auto w = m->wire(p);
|
||||||
log_assert(w);
|
log_assert(w);
|
||||||
if (w->attributes.count(ID(abc_carry))) {
|
if (w->attributes.count(ID(abc9_carry))) {
|
||||||
if (w->port_input) {
|
if (w->port_input) {
|
||||||
if (carry_in)
|
if (carry_in)
|
||||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
|
||||||
carry_in = w;
|
carry_in = w;
|
||||||
}
|
}
|
||||||
else if (w->port_output) {
|
else if (w->port_output) {
|
||||||
if (carry_out)
|
if (carry_out)
|
||||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
|
||||||
carry_out = w;
|
carry_out = w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (carry_in || carry_out) {
|
if (carry_in || carry_out) {
|
||||||
if (carry_in && !carry_out)
|
if (carry_in && !carry_out)
|
||||||
log_error("Module '%s' contains an 'abc_carry' input port but no output port.\n", log_id(m));
|
log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m));
|
||||||
if (!carry_in && carry_out)
|
if (!carry_in && carry_out)
|
||||||
log_error("Module '%s' contains an 'abc_carry' output port but no input port.\n", log_id(m));
|
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
|
||||||
// Make carry_in the last PI, and carry_out the last PO
|
// Make carry_in the last PI, and carry_out the last PO
|
||||||
// since ABC requires it this way
|
// since ABC requires it this way
|
||||||
auto &ports = m->ports;
|
auto &ports = m->ports;
|
||||||
|
@ -1105,7 +1127,7 @@ struct Abc9Pass : public Pass {
|
||||||
|
|
||||||
for (auto mod : design->selected_modules())
|
for (auto mod : design->selected_modules())
|
||||||
{
|
{
|
||||||
if (mod->attributes.count(ID(abc_box_id)))
|
if (mod->attributes.count(ID(abc9_box_id)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mod->processes.size() > 0) {
|
if (mod->processes.size() > 0) {
|
||||||
|
@ -1118,7 +1140,7 @@ struct Abc9Pass : public Pass {
|
||||||
if (!dff_mode || !clk_str.empty()) {
|
if (!dff_mode || !clk_str.empty()) {
|
||||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
|
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
|
||||||
delay_target, lutin_shared, fast_mode, show_tempdir,
|
delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||||
box_file, lut_file, wire_delay, box_lookup);
|
box_file, lut_file, wire_delay, box_lookup, nomfs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,7 +1286,7 @@ struct Abc9Pass : public Pass {
|
||||||
en_sig = assign_map(std::get<3>(it.first));
|
en_sig = assign_map(std::get<3>(it.first));
|
||||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
|
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
|
||||||
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
|
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||||
box_file, lut_file, wire_delay, box_lookup);
|
box_file, lut_file, wire_delay, box_lookup, nomfs);
|
||||||
assign_map.set(mod);
|
assign_map.set(mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ struct AigmapPass : public Pass {
|
||||||
AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { }
|
AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { }
|
||||||
void help() YS_OVERRIDE
|
void help() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" aigmap [options] [selection]\n");
|
log(" aigmap [options] [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -36,10 +37,15 @@ struct AigmapPass : public Pass {
|
||||||
log(" -nand\n");
|
log(" -nand\n");
|
||||||
log(" Enable creation of $_NAND_ cells\n");
|
log(" Enable creation of $_NAND_ cells\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -select\n");
|
||||||
|
log(" Overwrite replaced cells in the current selection with new $_AND_,\n");
|
||||||
|
log(" $_NOT_, and $_NAND_, cells\n");
|
||||||
|
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
bool nand_mode = false;
|
bool nand_mode = false, select_mode = false;
|
||||||
|
|
||||||
log_header(design, "Executing AIGMAP pass (map logic to AIG).\n");
|
log_header(design, "Executing AIGMAP pass (map logic to AIG).\n");
|
||||||
|
|
||||||
|
@ -50,6 +56,10 @@ struct AigmapPass : public Pass {
|
||||||
nand_mode = true;
|
nand_mode = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-select") {
|
||||||
|
select_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -62,6 +72,7 @@ struct AigmapPass : public Pass {
|
||||||
dict<IdString, int> stat_not_replaced;
|
dict<IdString, int> stat_not_replaced;
|
||||||
int orig_num_cells = GetSize(module->cells());
|
int orig_num_cells = GetSize(module->cells());
|
||||||
|
|
||||||
|
pool<IdString> new_sel;
|
||||||
for (auto cell : module->selected_cells())
|
for (auto cell : module->selected_cells())
|
||||||
{
|
{
|
||||||
Aig aig(cell);
|
Aig aig(cell);
|
||||||
|
@ -75,6 +86,8 @@ struct AigmapPass : public Pass {
|
||||||
if (aig.name.empty()) {
|
if (aig.name.empty()) {
|
||||||
not_replaced_count++;
|
not_replaced_count++;
|
||||||
stat_not_replaced[cell->type]++;
|
stat_not_replaced[cell->type]++;
|
||||||
|
if (select_mode)
|
||||||
|
new_sel.insert(cell->name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,19 +108,33 @@ struct AigmapPass : public Pass {
|
||||||
SigBit A = sigs.at(node.left_parent);
|
SigBit A = sigs.at(node.left_parent);
|
||||||
SigBit B = sigs.at(node.right_parent);
|
SigBit B = sigs.at(node.right_parent);
|
||||||
if (nand_mode && node.inverter) {
|
if (nand_mode && node.inverter) {
|
||||||
bit = module->NandGate(NEW_ID, A, B);
|
bit = module->addWire(NEW_ID);
|
||||||
|
auto gate = module->addNandGate(NEW_ID, A, B, bit);
|
||||||
|
if (select_mode)
|
||||||
|
new_sel.insert(gate->name);
|
||||||
|
|
||||||
goto skip_inverter;
|
goto skip_inverter;
|
||||||
} else {
|
} else {
|
||||||
pair<int, int> key(node.left_parent, node.right_parent);
|
pair<int, int> key(node.left_parent, node.right_parent);
|
||||||
if (and_cache.count(key))
|
if (and_cache.count(key))
|
||||||
bit = and_cache.at(key);
|
bit = and_cache.at(key);
|
||||||
else
|
else {
|
||||||
bit = module->AndGate(NEW_ID, A, B);
|
bit = module->addWire(NEW_ID);
|
||||||
|
auto gate = module->addAndGate(NEW_ID, A, B, bit);
|
||||||
|
if (select_mode)
|
||||||
|
new_sel.insert(gate->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.inverter)
|
if (node.inverter) {
|
||||||
bit = module->NotGate(NEW_ID, bit);
|
SigBit new_bit = module->addWire(NEW_ID);
|
||||||
|
auto gate = module->addNotGate(NEW_ID, bit, new_bit);
|
||||||
|
bit = new_bit;
|
||||||
|
if (select_mode)
|
||||||
|
new_sel.insert(gate->name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
skip_inverter:
|
skip_inverter:
|
||||||
for (auto &op : node.outports)
|
for (auto &op : node.outports)
|
||||||
|
@ -142,6 +169,13 @@ struct AigmapPass : public Pass {
|
||||||
|
|
||||||
for (auto cell : replaced_cells)
|
for (auto cell : replaced_cells)
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
|
|
||||||
|
if (select_mode) {
|
||||||
|
log_assert(!design->selection_stack.empty());
|
||||||
|
RTLIL::Selection& sel = design->selection_stack.back();
|
||||||
|
sel.selected_members[module->name] = std::move(new_sel);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} AigmapPass;
|
} AigmapPass;
|
||||||
|
|
|
@ -262,10 +262,14 @@ struct ExtractFaWorker
|
||||||
pool<SigBit> new_leaves = leaves;
|
pool<SigBit> new_leaves = leaves;
|
||||||
|
|
||||||
new_leaves.erase(bit);
|
new_leaves.erase(bit);
|
||||||
if (cell->hasPort(ID::A)) new_leaves.insert(sigmap(SigBit(cell->getPort(ID::A))));
|
for (auto port : {ID::A, ID::B, ID(C), ID(D)}) {
|
||||||
if (cell->hasPort(ID::B)) new_leaves.insert(sigmap(SigBit(cell->getPort(ID::B))));
|
if (!cell->hasPort(port))
|
||||||
if (cell->hasPort(ID(C))) new_leaves.insert(sigmap(SigBit(cell->getPort(ID(C)))));
|
continue;
|
||||||
if (cell->hasPort(ID(D))) new_leaves.insert(sigmap(SigBit(cell->getPort(ID(D)))));
|
auto bit = sigmap(SigBit(cell->getPort(port)));
|
||||||
|
if (!bit.wire)
|
||||||
|
continue;
|
||||||
|
new_leaves.insert(bit);
|
||||||
|
}
|
||||||
|
|
||||||
if (GetSize(new_leaves) > maxbreadth)
|
if (GetSize(new_leaves) > maxbreadth)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -394,7 +394,7 @@ struct FlowGraph
|
||||||
|
|
||||||
pair<pool<RTLIL::SigBit>, pool<RTLIL::SigBit>> edge_cut()
|
pair<pool<RTLIL::SigBit>, pool<RTLIL::SigBit>> edge_cut()
|
||||||
{
|
{
|
||||||
pool<RTLIL::SigBit> x, xi;
|
pool<RTLIL::SigBit> x = {source}, xi; // X and X̅ in the paper
|
||||||
|
|
||||||
NodePrime source_prime = {source, true};
|
NodePrime source_prime = {source, true};
|
||||||
pool<NodePrime> visited;
|
pool<NodePrime> visited;
|
||||||
|
@ -437,6 +437,7 @@ struct FlowGraph
|
||||||
for (auto collapsed_node : collapsed[sink])
|
for (auto collapsed_node : collapsed[sink])
|
||||||
xi.insert(collapsed_node);
|
xi.insert(collapsed_node);
|
||||||
|
|
||||||
|
log_assert(x[source] && !xi[source]);
|
||||||
log_assert(!x[sink] && xi[sink]);
|
log_assert(!x[sink] && xi[sink]);
|
||||||
return {x, xi};
|
return {x, xi};
|
||||||
}
|
}
|
||||||
|
@ -1050,7 +1051,7 @@ struct FlowmapWorker
|
||||||
|
|
||||||
auto cut_inputs = cut_lut_at_gate(lut, lut_gate);
|
auto cut_inputs = cut_lut_at_gate(lut, lut_gate);
|
||||||
pool<RTLIL::SigBit> gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second;
|
pool<RTLIL::SigBit> gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second;
|
||||||
if (gate_inputs.empty() && (int)other_inputs.size() == order)
|
if (gate_inputs.empty() && (int)other_inputs.size() >= order)
|
||||||
{
|
{
|
||||||
if (debug_relax)
|
if (debug_relax)
|
||||||
log(" Breaking would result in a (k+1)-LUT.\n");
|
log(" Breaking would result in a (k+1)-LUT.\n");
|
||||||
|
|
|
@ -257,6 +257,12 @@ struct TechmapWorker
|
||||||
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
||||||
}
|
}
|
||||||
design->select(module, w);
|
design->select(module, w);
|
||||||
|
|
||||||
|
if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) {
|
||||||
|
IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), it.second->name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
|
||||||
|
Wire *replace_w = module->addWire(replace_name, it.second);
|
||||||
|
module->connect(replace_w, w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SigMap tpl_sigmap(tpl);
|
SigMap tpl_sigmap(tpl);
|
||||||
|
@ -378,6 +384,8 @@ struct TechmapWorker
|
||||||
|
|
||||||
if (techmap_replace_cell)
|
if (techmap_replace_cell)
|
||||||
c_name = orig_cell_name;
|
c_name = orig_cell_name;
|
||||||
|
else if (it.second->name.begins_with("\\_TECHMAP_REPLACE_."))
|
||||||
|
c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
|
||||||
else
|
else
|
||||||
apply_prefix(cell->name, c_name);
|
apply_prefix(cell->name, c_name);
|
||||||
|
|
||||||
|
@ -1198,6 +1206,12 @@ struct TechmapPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n");
|
log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n");
|
||||||
log("and attributes of the cell that is being replaced.\n");
|
log("and attributes of the cell that is being replaced.\n");
|
||||||
|
log("A cell with a name of the form `_TECHMAP_REPLACE_.<suffix>` in the map file will\n");
|
||||||
|
log("be named thus but with the `_TECHMAP_REPLACE_' prefix substituted with the name\n");
|
||||||
|
log("of the cell being replaced.\n");
|
||||||
|
log("Similarly, a wire named in the form `_TECHMAP_REPLACE_.<suffix>` will cause a\n");
|
||||||
|
log("new wire alias to be created and named as above but with the `_TECHMAP_REPLACE_'\n");
|
||||||
|
log("prefix also substituted.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("See 'help extract' for a pass that does the opposite thing.\n");
|
log("See 'help extract' for a pass that does the opposite thing.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
|
|
@ -351,6 +351,11 @@ struct TestAutotbBackend : public Backend {
|
||||||
log(" -n <int>\n");
|
log(" -n <int>\n");
|
||||||
log(" number of iterations the test bench should run (default = 1000)\n");
|
log(" number of iterations the test bench should run (default = 1000)\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -seed <int>\n");
|
||||||
|
log(" seed used for pseudo-random number generation (default = 0).\n");
|
||||||
|
log(" a value of 0 will cause an arbitrary seed to be chosen, based on\n");
|
||||||
|
log(" the current system time.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,12 +9,12 @@ GENFILES += techlibs/common/simcells_help.inc
|
||||||
|
|
||||||
techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v
|
techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v
|
||||||
$(Q) mkdir -p techlibs/common
|
$(Q) mkdir -p techlibs/common
|
||||||
$(P) python3 $^ > $@.new
|
$(P) $(PYTHON_EXECUTABLE) $^ > $@.new
|
||||||
$(Q) mv $@.new $@
|
$(Q) mv $@.new $@
|
||||||
|
|
||||||
techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v
|
techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v
|
||||||
$(Q) mkdir -p techlibs/common
|
$(Q) mkdir -p techlibs/common
|
||||||
$(P) python3 $^ > $@.new
|
$(P) $(PYTHON_EXECUTABLE) $^ > $@.new
|
||||||
$(Q) mv $@.new $@
|
$(Q) mv $@.new $@
|
||||||
|
|
||||||
kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc
|
kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc
|
||||||
|
@ -28,4 +28,5 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/gate2lut.v))
|
$(eval $(call add_share_file,share,techlibs/common/gate2lut.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
||||||
|
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/dummy.box))
|
$(eval $(call add_share_file,share,techlibs/common/dummy.box))
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// with n <= k inputs should be techmapped in this way, because this shortens the critical path
|
// with n <= k inputs should be techmapped in this way, because this shortens the critical path
|
||||||
// from n to 1 by avoiding carry chains.
|
// from n to 1 by avoiding carry chains.
|
||||||
|
|
||||||
(* techmap_celltype = "$eq $ne $lt $le $gt $ge" *)
|
(* techmap_celltype = "$lt $le $gt $ge" *)
|
||||||
module _90_lut_cmp_ (A, B, Y);
|
module _90_lut_cmp_ (A, B, Y);
|
||||||
|
|
||||||
parameter A_SIGNED = 0;
|
parameter A_SIGNED = 0;
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||||
|
* 2019 David Shah <dave@ds0.me>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* Tech-mapping rules for decomposing arbitrarily-sized $mul cells
|
||||||
|
* into an equivalent collection of smaller `DSP_NAME cells (with the
|
||||||
|
* same interface as $mul) no larger than `DSP_[AB]_MAXWIDTH, attached
|
||||||
|
* to $shl and $add cells.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
`ifndef DSP_A_MAXWIDTH
|
||||||
|
$fatal(1, "Macro DSP_A_MAXWIDTH must be defined");
|
||||||
|
`endif
|
||||||
|
`ifndef DSP_B_MAXWIDTH
|
||||||
|
$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
|
||||||
|
`endif
|
||||||
|
`ifndef DSP_B_MAXWIDTH
|
||||||
|
$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
|
||||||
|
`endif
|
||||||
|
`ifndef DSP_A_MAXWIDTH_PARTIAL
|
||||||
|
`define DSP_A_MAXWIDTH_PARTIAL `DSP_A_MAXWIDTH
|
||||||
|
`endif
|
||||||
|
`ifndef DSP_B_MAXWIDTH_PARTIAL
|
||||||
|
`define DSP_B_MAXWIDTH_PARTIAL `DSP_B_MAXWIDTH
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef DSP_NAME
|
||||||
|
$fatal(1, "Macro DSP_NAME must be defined");
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`define MAX(a,b) (a > b ? a : b)
|
||||||
|
`define MIN(a,b) (a < b ? a : b)
|
||||||
|
|
||||||
|
(* techmap_celltype = "$mul $__mul" *)
|
||||||
|
module _80_mul (A, B, Y);
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
parameter A_WIDTH = 1;
|
||||||
|
parameter B_WIDTH = 1;
|
||||||
|
parameter Y_WIDTH = 1;
|
||||||
|
|
||||||
|
input [A_WIDTH-1:0] A;
|
||||||
|
input [B_WIDTH-1:0] B;
|
||||||
|
output [Y_WIDTH-1:0] Y;
|
||||||
|
|
||||||
|
parameter _TECHMAP_CELLTYPE_ = "";
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (0) begin end
|
||||||
|
`ifdef DSP_A_MINWIDTH
|
||||||
|
else if (A_WIDTH < `DSP_A_MINWIDTH)
|
||||||
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
`endif
|
||||||
|
`ifdef DSP_B_MINWIDTH
|
||||||
|
else if (B_WIDTH < `DSP_B_MINWIDTH)
|
||||||
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
`endif
|
||||||
|
`ifdef DSP_Y_MINWIDTH
|
||||||
|
else if (Y_WIDTH < `DSP_Y_MINWIDTH)
|
||||||
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
`endif
|
||||||
|
`ifdef DSP_SIGNEDONLY
|
||||||
|
else if (_TECHMAP_CELLTYPE_ == "$mul" && !A_SIGNED && !B_SIGNED)
|
||||||
|
\$mul #(
|
||||||
|
.A_SIGNED(1),
|
||||||
|
.B_SIGNED(1),
|
||||||
|
.A_WIDTH(A_WIDTH + 1),
|
||||||
|
.B_WIDTH(B_WIDTH + 1),
|
||||||
|
.Y_WIDTH(Y_WIDTH)
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A({1'b0, A}),
|
||||||
|
.B({1'b0, B}),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
`endif
|
||||||
|
else if (_TECHMAP_CELLTYPE_ == "$mul" && A_WIDTH < B_WIDTH)
|
||||||
|
\$mul #(
|
||||||
|
.A_SIGNED(B_SIGNED),
|
||||||
|
.B_SIGNED(A_SIGNED),
|
||||||
|
.A_WIDTH(B_WIDTH),
|
||||||
|
.B_WIDTH(A_WIDTH),
|
||||||
|
.Y_WIDTH(Y_WIDTH)
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A(B),
|
||||||
|
.B(A),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
else begin
|
||||||
|
wire [1023:0] _TECHMAP_DO_ = "proc; clean";
|
||||||
|
|
||||||
|
`ifdef DSP_SIGNEDONLY
|
||||||
|
localparam sign_headroom = 1;
|
||||||
|
`else
|
||||||
|
localparam sign_headroom = 0;
|
||||||
|
`endif
|
||||||
|
|
||||||
|
genvar i;
|
||||||
|
if (A_WIDTH > `DSP_A_MAXWIDTH) begin
|
||||||
|
localparam n = (A_WIDTH-`DSP_A_MAXWIDTH+`DSP_A_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
|
||||||
|
localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);
|
||||||
|
localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
|
||||||
|
localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
|
||||||
|
if (A_SIGNED && B_SIGNED) begin
|
||||||
|
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
|
||||||
|
wire signed [last_Y_WIDTH-1:0] last_partial;
|
||||||
|
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
|
||||||
|
wire [last_Y_WIDTH-1:0] last_partial;
|
||||||
|
wire [Y_WIDTH-1:0] partial_sum [n:0];
|
||||||
|
end
|
||||||
|
|
||||||
|
for (i = 0; i < n; i=i+1) begin:sliceA
|
||||||
|
\$__mul #(
|
||||||
|
.A_SIGNED(sign_headroom),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(`DSP_A_MAXWIDTH_PARTIAL),
|
||||||
|
.B_WIDTH(B_WIDTH),
|
||||||
|
.Y_WIDTH(partial_Y_WIDTH)
|
||||||
|
) mul (
|
||||||
|
.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
|
||||||
|
.B(B),
|
||||||
|
.Y(partial[i])
|
||||||
|
);
|
||||||
|
// TODO: Currently a 'cascade' approach to summing the partial
|
||||||
|
// products is taken here, but a more efficient 'binary
|
||||||
|
// reduction' approach also exists...
|
||||||
|
if (i == 0)
|
||||||
|
assign partial_sum[i] = partial[i];
|
||||||
|
else
|
||||||
|
assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
|
||||||
|
end
|
||||||
|
|
||||||
|
\$__mul #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(last_A_WIDTH),
|
||||||
|
.B_WIDTH(B_WIDTH),
|
||||||
|
.Y_WIDTH(last_Y_WIDTH)
|
||||||
|
) sliceA.last (
|
||||||
|
.A(A[A_WIDTH-1 -: last_A_WIDTH]),
|
||||||
|
.B(B),
|
||||||
|
.Y(last_partial)
|
||||||
|
);
|
||||||
|
assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
|
||||||
|
assign Y = partial_sum[n];
|
||||||
|
end
|
||||||
|
else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
|
||||||
|
localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
|
||||||
|
localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);
|
||||||
|
localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
|
||||||
|
localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
|
||||||
|
if (A_SIGNED && B_SIGNED) begin
|
||||||
|
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
|
||||||
|
wire signed [last_Y_WIDTH-1:0] last_partial;
|
||||||
|
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
|
||||||
|
wire [last_Y_WIDTH-1:0] last_partial;
|
||||||
|
wire [Y_WIDTH-1:0] partial_sum [n:0];
|
||||||
|
end
|
||||||
|
|
||||||
|
for (i = 0; i < n; i=i+1) begin:sliceB
|
||||||
|
\$__mul #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(sign_headroom),
|
||||||
|
.A_WIDTH(A_WIDTH),
|
||||||
|
.B_WIDTH(`DSP_B_MAXWIDTH_PARTIAL),
|
||||||
|
.Y_WIDTH(partial_Y_WIDTH)
|
||||||
|
) mul (
|
||||||
|
.A(A),
|
||||||
|
.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),
|
||||||
|
.Y(partial[i])
|
||||||
|
);
|
||||||
|
// TODO: Currently a 'cascade' approach to summing the partial
|
||||||
|
// products is taken here, but a more efficient 'binary
|
||||||
|
// reduction' approach also exists...
|
||||||
|
if (i == 0)
|
||||||
|
assign partial_sum[i] = partial[i];
|
||||||
|
else
|
||||||
|
assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
|
||||||
|
end
|
||||||
|
|
||||||
|
\$__mul #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(A_WIDTH),
|
||||||
|
.B_WIDTH(last_B_WIDTH),
|
||||||
|
.Y_WIDTH(last_Y_WIDTH)
|
||||||
|
) mul_sliceB_last (
|
||||||
|
.A(A),
|
||||||
|
.B(B[B_WIDTH-1 -: last_B_WIDTH]),
|
||||||
|
.Y(last_partial)
|
||||||
|
);
|
||||||
|
assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
|
||||||
|
assign Y = partial_sum[n];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
if (A_SIGNED)
|
||||||
|
wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
|
||||||
|
else
|
||||||
|
wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
|
||||||
|
if (B_SIGNED)
|
||||||
|
wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
|
||||||
|
else
|
||||||
|
wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
|
||||||
|
|
||||||
|
`DSP_NAME #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(`DSP_A_MAXWIDTH),
|
||||||
|
.B_WIDTH(`DSP_B_MAXWIDTH),
|
||||||
|
.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A(Aext),
|
||||||
|
.B(Bext),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* techmap_celltype = "$mul $__mul" *)
|
||||||
|
module _90_soft_mul (A, B, Y);
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
parameter A_WIDTH = 1;
|
||||||
|
parameter B_WIDTH = 1;
|
||||||
|
parameter Y_WIDTH = 1;
|
||||||
|
|
||||||
|
input [A_WIDTH-1:0] A;
|
||||||
|
input [B_WIDTH-1:0] B;
|
||||||
|
output [Y_WIDTH-1:0] Y;
|
||||||
|
|
||||||
|
// Indirection necessary since mapping
|
||||||
|
// back to $mul will cause recursion
|
||||||
|
generate
|
||||||
|
if (A_SIGNED && !B_SIGNED)
|
||||||
|
\$__soft_mul #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(1),
|
||||||
|
.A_WIDTH(A_WIDTH),
|
||||||
|
.B_WIDTH(B_WIDTH+1),
|
||||||
|
.Y_WIDTH(Y_WIDTH)
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A(A),
|
||||||
|
.B({1'b0,B}),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
else if (!A_SIGNED && B_SIGNED)
|
||||||
|
\$__soft_mul #(
|
||||||
|
.A_SIGNED(1),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(A_WIDTH+1),
|
||||||
|
.B_WIDTH(B_WIDTH),
|
||||||
|
.Y_WIDTH(Y_WIDTH)
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A({1'b0,A}),
|
||||||
|
.B(B),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
else
|
||||||
|
\$__soft_mul #(
|
||||||
|
.A_SIGNED(A_SIGNED),
|
||||||
|
.B_SIGNED(B_SIGNED),
|
||||||
|
.A_WIDTH(A_WIDTH),
|
||||||
|
.B_WIDTH(B_WIDTH),
|
||||||
|
.Y_WIDTH(Y_WIDTH)
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.A(A),
|
||||||
|
.B(B),
|
||||||
|
.Y(Y)
|
||||||
|
);
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
|
@ -6,4 +6,5 @@ bram_conn_2.vh
|
||||||
bram_conn_4.vh
|
bram_conn_4.vh
|
||||||
bram_conn_9.vh
|
bram_conn_9.vh
|
||||||
bram_conn_18.vh
|
bram_conn_18.vh
|
||||||
|
bram_conn_36.vh
|
||||||
brams_connect.mk
|
brams_connect.mk
|
||||||
|
|
|
@ -13,25 +13,26 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
||||||
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))
|
||||||
|
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_map.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_map.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_unmap.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_unmap.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_model.v))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_model.v))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g.box))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g.lut))
|
||||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut))
|
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc9_5g_nowide.lut))
|
||||||
|
|
||||||
EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
|
EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
|
||||||
.SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
|
.SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
|
||||||
|
|
||||||
techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py
|
techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py
|
||||||
$(Q) mkdir -p techlibs/ecp5
|
$(Q) mkdir -p techlibs/ecp5
|
||||||
$(P) python3 $<
|
$(P) $(PYTHON_EXECUTABLE) $<
|
||||||
$(Q) touch $@
|
$(Q) touch $@
|
||||||
|
|
||||||
techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py
|
techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py
|
||||||
$(Q) mkdir -p techlibs/ecp5
|
$(Q) mkdir -p techlibs/ecp5
|
||||||
$(P) python3 $<
|
$(P) $(PYTHON_EXECUTABLE) $<
|
||||||
$(Q) touch $@
|
$(Q) touch $@
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ techlibs/ecp5/bram_conn_2.vh: techlibs/ecp5/brams_connect.mk
|
||||||
techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk
|
techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk
|
||||||
techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk
|
techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk
|
||||||
techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk
|
techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk
|
||||||
|
techlibs/ecp5/bram_conn_36.vh: techlibs/ecp5/brams_connect.mk
|
||||||
|
|
||||||
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh))
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh))
|
||||||
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh))
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh))
|
||||||
|
@ -52,3 +54,4 @@ $(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_2.vh))
|
||||||
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh))
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh))
|
||||||
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh))
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh))
|
||||||
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh))
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh))
|
||||||
|
$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_36.vh))
|
||||||
|
|
|
@ -18,7 +18,7 @@ CCU2C 1 1 9 3
|
||||||
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
||||||
# Outputs: DO0, DO1, DO2, DO3
|
# Outputs: DO0, DO1, DO2, DO3
|
||||||
# name ID w/b ins outs
|
# name ID w/b ins outs
|
||||||
$__ABC_DPR16X4_COMB 2 0 8 4
|
$__ABC9_DPR16X4_COMB 2 0 8 4
|
||||||
|
|
||||||
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
|
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
|
||||||
0 0 0 0 141 379 275 379
|
0 0 0 0 141 379 275 379
|
|
@ -20,5 +20,5 @@ module TRELLIS_DPR16X4 (
|
||||||
.RAD(RAD), .DO(\$DO )
|
.RAD(RAD), .DO(\$DO )
|
||||||
);
|
);
|
||||||
|
|
||||||
\$__ABC_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
|
\$__ABC9_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
|
||||||
endmodule
|
endmodule
|
|
@ -0,0 +1,5 @@
|
||||||
|
// ---------------------------------------
|
||||||
|
|
||||||
|
(* abc9_box_id=2 *)
|
||||||
|
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||||
|
endmodule
|
|
@ -1,5 +1,5 @@
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||||
assign Y = A;
|
assign Y = A;
|
||||||
endmodule
|
endmodule
|
|
@ -1,5 +0,0 @@
|
||||||
// ---------------------------------------
|
|
||||||
|
|
||||||
(* abc_box_id=2 *)
|
|
||||||
module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
|
||||||
endmodule
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
bram $__ECP5_PDPW16KD
|
||||||
|
init 1
|
||||||
|
|
||||||
|
abits 9
|
||||||
|
dbits 36
|
||||||
|
|
||||||
|
groups 2
|
||||||
|
ports 1 1
|
||||||
|
wrmode 1 0
|
||||||
|
enable 4 1
|
||||||
|
transp 0 0
|
||||||
|
clocks 2 3
|
||||||
|
clkpol 2 3
|
||||||
|
endbram
|
||||||
|
|
||||||
bram $__ECP5_DP16KD
|
bram $__ECP5_DP16KD
|
||||||
init 1
|
init 1
|
||||||
|
|
||||||
|
@ -22,8 +37,16 @@ bram $__ECP5_DP16KD
|
||||||
clkpol 2 3
|
clkpol 2 3
|
||||||
endbram
|
endbram
|
||||||
|
|
||||||
|
match $__ECP5_PDPW16KD
|
||||||
|
min bits 2048
|
||||||
|
min efficiency 5
|
||||||
|
shuffle_enable A
|
||||||
|
make_transp
|
||||||
|
or_next_if_better
|
||||||
|
endmatch
|
||||||
|
|
||||||
match $__ECP5_DP16KD
|
match $__ECP5_DP16KD
|
||||||
min bits 2048
|
min bits 2048
|
||||||
min efficiency 5
|
min efficiency 5
|
||||||
shuffle_enable B
|
shuffle_enable A
|
||||||
endmatch
|
endmatch
|
||||||
|
|
|
@ -10,6 +10,18 @@ def write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits):
|
||||||
print(" %s," % ", ".join(dia_conn), file=f)
|
print(" %s," % ", ".join(dia_conn), file=f)
|
||||||
print(" %s," % ", ".join(dob_conn), file=f)
|
print(" %s," % ", ".join(dob_conn), file=f)
|
||||||
|
|
||||||
|
def write_bus_ports_pdp(f, adw_bits, adr_bits, di_bits, do_bits, be_bits):
|
||||||
|
adw_conn = [".ADW%d(%s)" % (i, adw_bits[i]) for i in range(len(adw_bits))]
|
||||||
|
adr_conn = [".ADR%d(%s)" % (i, adr_bits[i]) for i in range(len(adr_bits))]
|
||||||
|
di_conn = [".DI%d(%s)" % (i, di_bits[i]) for i in range(len(di_bits))]
|
||||||
|
do_conn = [".DO%d(%s)" % (i, do_bits[i]) for i in range(len(do_bits))]
|
||||||
|
be_conn = [".BE%d(%s)" % (i, be_bits[i]) for i in range(len(be_bits))]
|
||||||
|
print(" %s," % ", ".join(adw_conn), file=f)
|
||||||
|
print(" %s," % ", ".join(adr_conn), file=f)
|
||||||
|
print(" %s," % ", ".join(di_conn), file=f)
|
||||||
|
print(" %s," % ", ".join(do_conn), file=f)
|
||||||
|
print(" %s," % ", ".join(be_conn), file=f)
|
||||||
|
|
||||||
with open("techlibs/ecp5/bram_conn_1.vh", "w") as f:
|
with open("techlibs/ecp5/bram_conn_1.vh", "w") as f:
|
||||||
ada_bits = ["A1ADDR[%d]" % i for i in range(14)]
|
ada_bits = ["A1ADDR[%d]" % i for i in range(14)]
|
||||||
adb_bits = ["B1ADDR[%d]" % i for i in range(14)]
|
adb_bits = ["B1ADDR[%d]" % i for i in range(14)]
|
||||||
|
@ -44,3 +56,11 @@ with open("techlibs/ecp5/bram_conn_18.vh", "w") as f:
|
||||||
dia_bits = ["A1DATA[%d]" % i for i in range(18)]
|
dia_bits = ["A1DATA[%d]" % i for i in range(18)]
|
||||||
dob_bits = ["B1DATA[%d]" % i for i in range(18)]
|
dob_bits = ["B1DATA[%d]" % i for i in range(18)]
|
||||||
write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits)
|
write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits)
|
||||||
|
|
||||||
|
with open("techlibs/ecp5/bram_conn_36.vh", "w") as f:
|
||||||
|
adw_bits = ["A1ADDR[%d]" % i for i in range(9)]
|
||||||
|
adr_bits = ["1'b0", "1'b0", "1'b0", "1'b0", "1'b0"] + ["B1ADDR[%d]" % i for i in range(9)]
|
||||||
|
di_bits = ["A1DATA[%d]" % i for i in range(36)]
|
||||||
|
do_bits = ["B1DATA[%d]" % (i + 18) for i in range(18)] + ["B1DATA[%d]" % i for i in range(18)]
|
||||||
|
be_bits = ["A1EN[%d]" % i for i in range(4)]
|
||||||
|
write_bus_ports_pdp(f, adw_bits, adr_bits, di_bits, do_bits, be_bits)
|
||||||
|
|
|
@ -113,3 +113,45 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
|
||||||
wire TECHMAP_FAIL = 1'b1;
|
wire TECHMAP_FAIL = 1'b1;
|
||||||
end endgenerate
|
end endgenerate
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
module \$__ECP5_PDPW16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
|
||||||
|
parameter CFG_ABITS = 9;
|
||||||
|
parameter CFG_DBITS = 36;
|
||||||
|
parameter CFG_ENABLE_A = 4;
|
||||||
|
|
||||||
|
parameter CLKPOL2 = 1;
|
||||||
|
parameter CLKPOL3 = 1;
|
||||||
|
parameter [18431:0] INIT = 18432'bx;
|
||||||
|
|
||||||
|
input CLK2;
|
||||||
|
input CLK3;
|
||||||
|
|
||||||
|
input [CFG_ABITS-1:0] A1ADDR;
|
||||||
|
input [CFG_DBITS-1:0] A1DATA;
|
||||||
|
input [CFG_ENABLE_A-1:0] A1EN;
|
||||||
|
|
||||||
|
input [CFG_ABITS-1:0] B1ADDR;
|
||||||
|
output [CFG_DBITS-1:0] B1DATA;
|
||||||
|
input B1EN;
|
||||||
|
|
||||||
|
localparam CLKWMUX = CLKPOL2 ? "CLKA" : "INV";
|
||||||
|
localparam CLKRMUX = CLKPOL3 ? "CLKB" : "INV";
|
||||||
|
|
||||||
|
localparam WRITEMODE_A = TRANSP2 ? "WRITETHROUGH" : "READBEFOREWRITE";
|
||||||
|
|
||||||
|
PDPW16KD #(
|
||||||
|
`include "bram_init_9_18_36.vh"
|
||||||
|
.DATA_WIDTH_W(36),
|
||||||
|
.DATA_WIDTH_R(36),
|
||||||
|
.CLKWMUX(CLKWMUX),
|
||||||
|
.CLKRMUX(CLKRMUX),
|
||||||
|
.GSR("AUTO")
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
`include "bram_conn_36.vh"
|
||||||
|
.CLKW(CLK2), .CLKR(CLK3),
|
||||||
|
.CEW(1'b1),
|
||||||
|
.CER(B1EN), .OCER(1'b1),
|
||||||
|
.RST(1'b0)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -333,6 +333,13 @@ module ECLKSYNCB(
|
||||||
);
|
);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
(* blackbox *)
|
||||||
|
module ECLKBRIDGECS(
|
||||||
|
input CLK0, CLK1, SEL,
|
||||||
|
output ECSOUT
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
(* blackbox *)
|
(* blackbox *)
|
||||||
module DCCA(
|
module DCCA(
|
||||||
input CLKI, CE,
|
input CLKI, CE,
|
||||||
|
@ -684,3 +691,97 @@ module SGSR (
|
||||||
input GSR, CLK
|
input GSR, CLK
|
||||||
);
|
);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
|
(* blackbox *)
|
||||||
|
module PDPW16KD (
|
||||||
|
input DI35, DI34, DI33, DI32, DI31, DI30, DI29, DI28, DI27, DI26, DI25, DI24, DI23, DI22, DI21, DI20, DI19, DI18,
|
||||||
|
input DI17, DI16, DI15, DI14, DI13, DI12, DI11, DI10, DI9, DI8, DI7, DI6, DI5, DI4, DI3, DI2, DI1, DI0,
|
||||||
|
input ADW8, ADW7, ADW6, ADW5, ADW4, ADW3, ADW2, ADW1, ADW0,
|
||||||
|
input BE3, BE2, BE1, BE0, CEW, CLKW, CSW2, CSW1, CSW0,
|
||||||
|
input ADR13, ADR12, ADR11, ADR10, ADR9, ADR8, ADR7, ADR6, ADR5, ADR4, ADR3, ADR2, ADR1, ADR0,
|
||||||
|
input CER, OCER, CLKR, CSR2, CSR1, CSR0, RST,
|
||||||
|
output DO35, DO34, DO33, DO32, DO31, DO30, DO29, DO28, DO27, DO26, DO25, DO24, DO23, DO22, DO21, DO20, DO19, DO18,
|
||||||
|
output DO17, DO16, DO15, DO14, DO13, DO12, DO11, DO10, DO9, DO8, DO7, DO6, DO5, DO4, DO3, DO2, DO1, DO0
|
||||||
|
);
|
||||||
|
parameter DATA_WIDTH_W = 36;
|
||||||
|
parameter DATA_WIDTH_R = 36;
|
||||||
|
parameter GSR = "ENABLED";
|
||||||
|
|
||||||
|
parameter REGMODE = "NOREG";
|
||||||
|
|
||||||
|
parameter RESETMODE = "SYNC";
|
||||||
|
parameter ASYNC_RESET_RELEASE = "SYNC";
|
||||||
|
|
||||||
|
parameter CSDECODE_W = "0b000";
|
||||||
|
parameter CSDECODE_R = "0b000";
|
||||||
|
|
||||||
|
parameter INITVAL_00 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_01 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_02 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_03 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_04 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_05 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_06 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_07 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_08 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_09 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_0F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_10 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_11 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_12 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_13 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_14 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_15 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_16 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_17 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_18 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_19 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_1F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_20 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_21 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_22 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_23 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_24 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_25 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_26 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_27 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_28 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_29 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_2F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_30 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_31 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_32 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_33 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_34 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_35 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_36 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_37 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_38 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_39 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
parameter INIT_DATA = "STATIC";
|
||||||
|
parameter CLKWMUX = "CLKW";
|
||||||
|
parameter CLKRMUX = "CLKR";
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -23,15 +23,15 @@ module FD1S3JX(input PD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLI
|
||||||
// module FL1S3AY(); endmodule
|
// module FL1S3AY(); endmodule
|
||||||
|
|
||||||
// Diamond I/O registers
|
// Diamond I/O registers
|
||||||
module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="input" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
|
|
||||||
module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; (* syn_useioff, ioff_dir="output" *) TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
|
||||||
|
|
||||||
// TODO: Diamond I/O latches
|
// TODO: Diamond I/O latches
|
||||||
// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
|
// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
|
||||||
|
|
|
@ -9,19 +9,19 @@ module LUT4(input A, B, C, D, output Z);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
(* abc_box_id=4, lib_whitebox *)
|
(* abc9_box_id=4, lib_whitebox *)
|
||||||
module L6MUX21 (input D0, D1, SD, output Z);
|
module L6MUX21 (input D0, D1, SD, output Z);
|
||||||
assign Z = SD ? D1 : D0;
|
assign Z = SD ? D1 : D0;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
(* abc_box_id=1, lib_whitebox *)
|
(* abc9_box_id=1, lib_whitebox *)
|
||||||
module CCU2C(
|
module CCU2C(
|
||||||
(* abc_carry *)
|
(* abc9_carry *)
|
||||||
input CIN,
|
input CIN,
|
||||||
input A0, B0, C0, D0, A1, B1, C1, D1,
|
input A0, B0, C0, D0, A1, B1, C1, D1,
|
||||||
output S0, S1,
|
output S0, S1,
|
||||||
(* abc_carry *)
|
(* abc9_carry *)
|
||||||
output COUT
|
output COUT
|
||||||
);
|
);
|
||||||
parameter [15:0] INIT0 = 16'h0000;
|
parameter [15:0] INIT0 = 16'h0000;
|
||||||
|
@ -103,7 +103,7 @@ module TRELLIS_RAM16X2 (
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
(* abc_box_id=3, lib_whitebox *)
|
(* abc9_box_id=3, lib_whitebox *)
|
||||||
module PFUMX (input ALUT, BLUT, C0, output Z);
|
module PFUMX (input ALUT, BLUT, C0, output Z);
|
||||||
assign Z = C0 ? ALUT : BLUT;
|
assign Z = C0 ? ALUT : BLUT;
|
||||||
endmodule
|
endmodule
|
||||||
|
@ -115,7 +115,7 @@ module TRELLIS_DPR16X4 (
|
||||||
input WRE,
|
input WRE,
|
||||||
input WCK,
|
input WCK,
|
||||||
input [3:0] RAD,
|
input [3:0] RAD,
|
||||||
/* (* abc_arrival=<TODO> *) */
|
/* (* abc9_arrival=<TODO> *) */
|
||||||
output [3:0] DO
|
output [3:0] DO
|
||||||
);
|
);
|
||||||
parameter WCKMUX = "WCK";
|
parameter WCKMUX = "WCK";
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y);
|
||||||
|
|
||||||
|
parameter A_WIDTH = 18;
|
||||||
|
parameter B_WIDTH = 18;
|
||||||
|
parameter Y_WIDTH = 36;
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
|
||||||
|
MULT18X18D _TECHMAP_REPLACE_ (
|
||||||
|
.A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]),
|
||||||
|
.B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]),
|
||||||
|
.C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0),
|
||||||
|
.SIGNEDA(A_SIGNED), .SIGNEDB(B_SIGNED), .SOURCEA(1'b0), .SOURCEB(1'b0),
|
||||||
|
|
||||||
|
.P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35])
|
||||||
|
);
|
||||||
|
endmodule
|
|
@ -89,6 +89,9 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
|
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
|
||||||
log(" (this feature is experimental and incomplete)\n");
|
log(" (this feature is experimental and incomplete)\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -nodsp\n");
|
||||||
|
log(" do not map multipliers to MULT18X18D\n");
|
||||||
|
log("\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("The following commands are executed by this synthesis command:\n");
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
help_script();
|
help_script();
|
||||||
|
@ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, blif_file, edif_file, json_file;
|
string top_opt, blif_file, edif_file, json_file;
|
||||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr;
|
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;
|
||||||
|
|
||||||
void clear_flags() YS_OVERRIDE
|
void clear_flags() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
@ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
abc2 = false;
|
abc2 = false;
|
||||||
vpr = false;
|
vpr = false;
|
||||||
abc9 = false;
|
abc9 = false;
|
||||||
|
nodsp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
abc9 = true;
|
abc9 = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-nodsp") {
|
||||||
|
nodsp = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -218,17 +226,34 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flatten && check_label("flatten", "(unless -noflatten)"))
|
if (check_label("coarse"))
|
||||||
{
|
{
|
||||||
run("proc");
|
run("proc");
|
||||||
|
if (flatten || help_mode)
|
||||||
run("flatten");
|
run("flatten");
|
||||||
run("tribuf -logic");
|
run("tribuf -logic");
|
||||||
run("deminout");
|
run("deminout");
|
||||||
|
run("opt_expr");
|
||||||
|
run("opt_clean");
|
||||||
|
run("check");
|
||||||
|
run("opt");
|
||||||
|
run("wreduce");
|
||||||
|
run("peepopt");
|
||||||
|
run("opt_clean");
|
||||||
|
run("share");
|
||||||
|
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||||
|
run("opt_expr");
|
||||||
|
run("opt_clean");
|
||||||
|
if (!nodsp) {
|
||||||
|
run("techmap -map +/mul2dsp.v -map +/ecp5/dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=$__MUL18X18", "(unless -nodsp)");
|
||||||
|
run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)");
|
||||||
}
|
}
|
||||||
|
run("alumacc");
|
||||||
if (check_label("coarse"))
|
run("opt");
|
||||||
{
|
run("fsm");
|
||||||
run("synth -run coarse");
|
run("opt -fast");
|
||||||
|
run("memory -nomap");
|
||||||
|
run("opt_clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nobram && check_label("map_bram", "(skip if -nobram)"))
|
if (!nobram && check_label("map_bram", "(skip if -nobram)"))
|
||||||
|
@ -272,6 +297,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
run("simplemap");
|
run("simplemap");
|
||||||
run("ecp5_ffinit");
|
run("ecp5_ffinit");
|
||||||
run("ecp5_gsr");
|
run("ecp5_gsr");
|
||||||
|
run("attrmvcp -copy -attr syn_useioff");
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,15 +308,16 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
}
|
}
|
||||||
std::string techmap_args = "-map +/ecp5/latches_map.v";
|
std::string techmap_args = "-map +/ecp5/latches_map.v";
|
||||||
if (abc9)
|
if (abc9)
|
||||||
techmap_args += " -map +/ecp5/abc_map.v -max_iter 1";
|
techmap_args += " -map +/ecp5/abc9_map.v -max_iter 1";
|
||||||
run("techmap " + techmap_args);
|
run("techmap " + techmap_args);
|
||||||
|
|
||||||
if (abc9) {
|
if (abc9) {
|
||||||
|
run("read_verilog -icells -lib +/ecp5/abc9_model.v");
|
||||||
if (nowidelut)
|
if (nowidelut)
|
||||||
run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200");
|
run("abc9 -lut +/ecp5/abc9_5g_nowide.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
|
||||||
else
|
else
|
||||||
run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200");
|
run("abc9 -lut +/ecp5/abc9_5g.lut -box +/ecp5/abc9_5g.box -W 200 -nomfs");
|
||||||
run("techmap -map +/ecp5/abc_unmap.v");
|
run("techmap -map +/ecp5/abc9_unmap.v");
|
||||||
} else {
|
} else {
|
||||||
if (nowidelut)
|
if (nowidelut)
|
||||||
run("abc -lut 4 -dress");
|
run("abc -lut 4 -dress");
|
||||||
|
@ -312,6 +339,7 @@ struct SynthEcp5Pass : public ScriptPass
|
||||||
|
|
||||||
if (check_label("check"))
|
if (check_label("check"))
|
||||||
{
|
{
|
||||||
|
run("autoname");
|
||||||
run("hierarchy -check");
|
run("hierarchy -check");
|
||||||
run("stat");
|
run("stat");
|
||||||
run("check -noinit");
|
run("check -noinit");
|
||||||
|
|
|
@ -17,6 +17,18 @@ module \$_DFF_NP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE
|
||||||
module \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
|
module \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
|
||||||
module \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
|
module \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
|
||||||
|
|
||||||
|
module \$_DLATCH_N_ (E, D, Q);
|
||||||
|
wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
|
||||||
|
input E, D;
|
||||||
|
output Q = !E ? D : Q;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module \$_DLATCH_P_ (E, D, Q);
|
||||||
|
wire [1023:0] _TECHMAP_DO_ = "simplemap; opt";
|
||||||
|
input E, D;
|
||||||
|
output Q = E ? D : Q;
|
||||||
|
endmodule
|
||||||
|
|
||||||
`ifndef NO_LUT
|
`ifndef NO_LUT
|
||||||
module \$lut (A, Y);
|
module \$lut (A, Y);
|
||||||
parameter WIDTH = 0;
|
parameter WIDTH = 0;
|
||||||
|
|
|
@ -60,6 +60,8 @@ module EFX_FF(
|
||||||
assign sr = SR_POLARITY ? SR : ~SR;
|
assign sr = SR_POLARITY ? SR : ~SR;
|
||||||
assign d = D_POLARITY ? D : ~D;
|
assign d = D_POLARITY ? D : ~D;
|
||||||
|
|
||||||
|
initial Q = 1'b0;
|
||||||
|
|
||||||
generate
|
generate
|
||||||
if (SR_SYNC == 1)
|
if (SR_SYNC == 1)
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
brams_init.mk
|
||||||
|
bram_init_*.vh
|
|
@ -15,3 +15,13 @@ $(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
|
||||||
|
|
||||||
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_init3.vh))
|
$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_init3.vh))
|
||||||
|
|
||||||
|
EXTRA_OBJS += techlibs/gowin/brams_init.mk
|
||||||
|
.SECONDARY: techlibs/gowin/brams_init.mk
|
||||||
|
|
||||||
|
techlibs/gowin/brams_init.mk: techlibs/gowin/brams_init.py
|
||||||
|
$(Q) mkdir -p techlibs/gowin
|
||||||
|
$(P) python3 $<
|
||||||
|
$(Q) touch $@
|
||||||
|
|
||||||
|
techlibs/gowin/bram_init_16.vh: techlibs/gowin/brams_init.mk
|
||||||
|
$(eval $(call add_gen_share_file,share/gowin,techlibs/gowin/bram_init_16.vh))
|
||||||
|
|
|
@ -40,15 +40,15 @@ module _80_gw1n_alu(A, B, CI, BI, X, Y, CO);
|
||||||
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
|
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
|
||||||
|
|
||||||
wire [Y_WIDTH-1:0] AA = A_buf;
|
wire [Y_WIDTH-1:0] AA = A_buf;
|
||||||
wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
|
wire [Y_WIDTH-1:0] BB = B_buf;
|
||||||
wire [Y_WIDTH-1:0] C = {CO, CI};
|
wire [Y_WIDTH-1:0] C = {CO, CI};
|
||||||
|
|
||||||
genvar i;
|
genvar i;
|
||||||
generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
|
generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
|
||||||
ALU #(.ALU_MODE(32'b0))
|
ALU #(.ALU_MODE(2)) // ADDSUB I3 ? add : sub
|
||||||
alu(.I0(AA[i]),
|
alu(.I0(AA[i]),
|
||||||
.I1(BB[i]),
|
.I1(BB[i]),
|
||||||
.I3(1'b0),
|
.I3(~BI),
|
||||||
.CIN(C[i]),
|
.CIN(C[i]),
|
||||||
.COUT(CO[i]),
|
.COUT(CO[i]),
|
||||||
.SUM(Y[i])
|
.SUM(Y[i])
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
bram $__GW1NR_SDP
|
bram $__GW1NR_SDP
|
||||||
# uncomment when done
|
init 1
|
||||||
# init 1
|
abits 9 @a9d36
|
||||||
|
dbits 32 @a9d36
|
||||||
abits 10 @a10d18
|
abits 10 @a10d18
|
||||||
dbits 16 @a10d18
|
dbits 16 @a10d18
|
||||||
abits 11 @a11d9
|
abits 11 @a11d9
|
||||||
|
@ -14,7 +15,8 @@ bram $__GW1NR_SDP
|
||||||
groups 2
|
groups 2
|
||||||
ports 1 1
|
ports 1 1
|
||||||
wrmode 1 0
|
wrmode 1 0
|
||||||
enable 1 1 @a10d18
|
enable 4 1 @a9d36
|
||||||
|
enable 2 1 @a10d18
|
||||||
enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
|
enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
|
||||||
transp 0 0
|
transp 0 0
|
||||||
clocks 2 3
|
clocks 2 3
|
||||||
|
@ -24,6 +26,6 @@ endbram
|
||||||
match $__GW1NR_SDP
|
match $__GW1NR_SDP
|
||||||
min bits 2048
|
min bits 2048
|
||||||
min efficiency 5
|
min efficiency 5
|
||||||
shuffle_enable B
|
shuffle_enable A
|
||||||
make_transp
|
make_transp
|
||||||
endmatch
|
endmatch
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
with open("techlibs/gowin/bram_init_16.vh", "w") as f:
|
||||||
|
for i in range(0, 0x40):
|
||||||
|
low = i << 8
|
||||||
|
hi = ((i+1) << 8)-1
|
||||||
|
snippet = "INIT[%d:%d]" % (hi, low)
|
||||||
|
print(".INIT_RAM_%02X({%s})," % (i, snippet), file=f)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue