mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'master' into SergeyDegtyar/anlogic
This commit is contained in:
commit
d99b1e3261
1
Brewfile
1
Brewfile
|
@ -7,3 +7,4 @@ brew "graphviz"
|
|||
brew "pkg-config"
|
||||
brew "python3"
|
||||
brew "tcl-tk"
|
||||
brew "xdot"
|
||||
|
|
12
CHANGELOG
12
CHANGELOG
|
@ -27,6 +27,7 @@ Yosys 0.9 .. Yosys 0.9-dev
|
|||
- Improve attribute and parameter encoding in JSON to avoid ambiguities between
|
||||
bit vectors and strings containing [01xz]*
|
||||
- Added "clkbufmap" pass
|
||||
- Added "extractinv" pass and "invertible_pin" attribute
|
||||
- Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental)
|
||||
- Added "synth_xilinx -ise" (experimental)
|
||||
- Added "synth_xilinx -iopad"
|
||||
|
@ -38,6 +39,17 @@ Yosys 0.9 .. Yosys 0.9-dev
|
|||
- Improvements in pmgen: slices, choices, define, generate
|
||||
- Added "xilinx_srl" for Xilinx shift register extraction
|
||||
- Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
|
||||
- Added "_TECHMAP_WIREINIT_*_" attribute and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass
|
||||
- Added "-match-init" option to "dff2dffs" pass
|
||||
- Added "techmap_autopurge" support to techmap
|
||||
- Added "add -mod <modname[s]>"
|
||||
- Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones
|
||||
- Added "ice40_dsp" for Lattice iCE40 DSP packing
|
||||
- Added "xilinx_dsp" for Xilinx DSP packing
|
||||
- "synth_xilinx" to now infer DSP blocks (-nodsp to disable)
|
||||
- "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental)
|
||||
- "synth_ice40 -dsp" to infer DSP blocks
|
||||
- Added latch support to synth_xilinx
|
||||
|
||||
Yosys 0.8 .. Yosys 0.9
|
||||
----------------------
|
||||
|
|
12
Makefile
12
Makefile
|
@ -88,7 +88,7 @@ ifeq ($(OS), Darwin)
|
|||
PLUGIN_LDFLAGS += -undefined dynamic_lookup
|
||||
|
||||
# homebrew search paths
|
||||
ifneq ($(shell which brew),)
|
||||
ifneq ($(shell :; command -v brew),)
|
||||
BREW_PREFIX := $(shell brew --prefix)/opt
|
||||
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
|
@ -102,8 +102,8 @@ PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
|||
export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH)
|
||||
|
||||
# macports search paths
|
||||
else ifneq ($(shell which port),)
|
||||
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port))
|
||||
else ifneq ($(shell :; command -v port),)
|
||||
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port))
|
||||
CXXFLAGS += -I$(PORT_PREFIX)/include
|
||||
LDFLAGS += -L$(PORT_PREFIX)/lib
|
||||
PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
|
@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
|
|||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.9+36
|
||||
YOSYS_VER := 0.9+899
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
@ -528,6 +528,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
|
|||
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
||||
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
||||
$(eval $(call add_include_file,libs/json11/json11.hpp))
|
||||
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
||||
$(eval $(call add_include_file,frontends/ast/ast.h))
|
||||
$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
|
||||
|
@ -545,6 +546,8 @@ OBJS += libs/sha1/sha1.o
|
|||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/json11/json11.o
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
||||
OBJS += libs/ezsat/ezsat.o
|
||||
|
@ -710,6 +713,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/aiger && bash run-test.sh $(ABCOPT)
|
||||
+cd tests/arch && bash run-test.sh
|
||||
+cd tests/ice40 && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/rpc && bash run-test.sh
|
||||
+cd tests/anlogic && bash run-test.sh $(SEEDOPT)
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
|
|
23
README.md
23
README.md
|
@ -332,6 +332,10 @@ Verilog Attributes and non-standard features
|
|||
that represent module parameters or localparams (when the HDL front-end
|
||||
is run in ``-pwires`` mode).
|
||||
|
||||
- Wires marked with the ``hierconn`` attribute are connected to wires with the
|
||||
same name (format ``cell_name.identifier``) when they are imported from
|
||||
sub-modules by ``flatten``.
|
||||
|
||||
- The ``clkbuf_driver`` attribute can be set on an output port of a blackbox
|
||||
module to mark it as a clock buffer output, and thus prevent ``clkbufmap``
|
||||
from inserting another clock buffer on a net driven by such output.
|
||||
|
@ -343,6 +347,12 @@ Verilog Attributes and non-standard features
|
|||
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
|
||||
overridden by providing a custom selection to ``clkbufmap``.
|
||||
|
||||
- The ``invertible_pin`` attribute can be set on a port to mark it as
|
||||
invertible via a cell parameter. The name of the inversion parameter
|
||||
is specified as the value of this attribute. The value of the inversion
|
||||
parameter must be of the same width as the port, with 1 indicating
|
||||
an inverted bit and 0 indicating a non-inverted bit.
|
||||
|
||||
- The ``iopad_external_pin`` attribute on a blackbox module's port marks
|
||||
it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
|
||||
from inserting another pad cell on it.
|
||||
|
@ -351,19 +361,16 @@ Verilog Attributes and non-standard features
|
|||
blackbox or whitebox definition to a corresponding entry in a `abc9`
|
||||
box-file.
|
||||
|
||||
- The port attribute ``abc_scc_break`` indicates a module input port that will
|
||||
be treated as a primary output during `abc9` techmapping. Doing so eliminates
|
||||
the possibility of a strongly-connected component (i.e. a combinatorial loop)
|
||||
existing. Typically, this is specified for sequential inputs on otherwise
|
||||
combinatorial boxes -- for example, applying ``abc_scc_break`` onto the `D`
|
||||
port of a LUTRAM cell prevents `abc9` from interpreting any `Q` -> `D` paths
|
||||
as a combinatorial loop.
|
||||
|
||||
- The port attribute ``abc_carry`` marks the carry-in (if an input port) and
|
||||
carry-out (if output port) ports of a box. This information is necessary for
|
||||
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
|
||||
onto a bus port will affect only its most significant bit.
|
||||
|
||||
- The port attribute ``abc_arrival`` specifies an integer (for output ports
|
||||
only) to be used as the arrival time of this sequential port. It can be used,
|
||||
for example, to specify the clk-to-Q delay of a flip-flop for consideration
|
||||
during techmapping.
|
||||
|
||||
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
|
||||
the non-standard ``{* ... *}`` attribute syntax to set default attributes
|
||||
for everything that comes after the ``{* ... *}`` statement. (Reset
|
||||
|
|
|
@ -101,7 +101,7 @@ struct AigerWriter
|
|||
return a;
|
||||
}
|
||||
|
||||
AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
|
||||
AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
|
||||
{
|
||||
pool<SigBit> undriven_bits;
|
||||
pool<SigBit> unused_bits;
|
||||
|
@ -367,6 +367,12 @@ struct AigerWriter
|
|||
aig_latchin.push_back(a);
|
||||
}
|
||||
|
||||
if (lmode && aig_l == 0) {
|
||||
aig_m++, aig_l++;
|
||||
aig_latchinit.push_back(0);
|
||||
aig_latchin.push_back(0);
|
||||
}
|
||||
|
||||
if (!initstate_bits.empty() || !init_inputs.empty())
|
||||
aig_latchin.push_back(1);
|
||||
|
||||
|
@ -704,9 +710,9 @@ struct AigerBackend : public Backend {
|
|||
log(" -vmap <filename>\n");
|
||||
log(" like -map, but more verbose\n");
|
||||
log("\n");
|
||||
log(" -I, -O, -B\n");
|
||||
log(" If the design contains no input/output/assert then create one\n");
|
||||
log(" dummy input/output/bad_state pin to make the tools reading the\n");
|
||||
log(" -I, -O, -B, -L\n");
|
||||
log(" If the design contains no input/output/assert/flip-flop then create one\n");
|
||||
log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n");
|
||||
log(" AIGER file happy.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
@ -720,6 +726,7 @@ struct AigerBackend : public Backend {
|
|||
bool imode = false;
|
||||
bool omode = false;
|
||||
bool bmode = false;
|
||||
bool lmode = false;
|
||||
std::string map_filename;
|
||||
|
||||
log_header(design, "Executing AIGER backend.\n");
|
||||
|
@ -764,16 +771,20 @@ struct AigerBackend : public Backend {
|
|||
bmode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-L") {
|
||||
lmode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||
|
||||
Module *top_module = design->top_module();
|
||||
|
||||
if (top_module == nullptr)
|
||||
log_error("Can't find top module in current design!\n");
|
||||
|
||||
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode);
|
||||
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
|
||||
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
|
||||
|
||||
if (!map_filename.empty()) {
|
||||
|
|
|
@ -83,6 +83,7 @@ struct XAigerWriter
|
|||
dict<SigBit, pair<SigBit, SigBit>> and_map;
|
||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
|
||||
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
|
||||
dict<SigBit, float> arrival_times;
|
||||
|
||||
vector<pair<int, int>> aig_gates;
|
||||
vector<int> aig_outputs;
|
||||
|
@ -247,14 +248,15 @@ struct XAigerWriter
|
|||
if (!holes_mode) {
|
||||
toposort.node(cell->name);
|
||||
for (const auto &conn : cell->connections()) {
|
||||
if (cell->input(conn.first)) {
|
||||
auto port_wire = inst_module->wire(conn.first);
|
||||
if (port_wire->port_input) {
|
||||
// Ignore inout for the sake of topographical ordering
|
||||
if (cell->output(conn.first)) continue;
|
||||
if (port_wire->port_output) continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_users[bit].insert(cell->name);
|
||||
}
|
||||
|
||||
if (cell->output(conn.first))
|
||||
if (port_wire->port_output)
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_drivers[bit].insert(cell->name);
|
||||
}
|
||||
|
@ -271,7 +273,7 @@ struct XAigerWriter
|
|||
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
|
||||
|
||||
if (is_input) {
|
||||
for (auto b : c.second.bits()) {
|
||||
for (auto b : c.second) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
if (!w->port_output || !cell_known) {
|
||||
|
@ -287,7 +289,17 @@ struct XAigerWriter
|
|||
}
|
||||
}
|
||||
if (is_output) {
|
||||
for (auto b : c.second.bits()) {
|
||||
int arrival = 0;
|
||||
if (port_wire) {
|
||||
auto it = port_wire->attributes.find("\\abc_arrival");
|
||||
if (it != port_wire->attributes.end()) {
|
||||
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));
|
||||
arrival = it->second.as_int();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto b : c.second) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
input_bits.insert(b);
|
||||
|
@ -295,6 +307,9 @@ struct XAigerWriter
|
|||
if (O != b)
|
||||
alias_map[O] = b;
|
||||
undriven_bits.erase(O);
|
||||
|
||||
if (arrival)
|
||||
arrival_times[b] = arrival;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,6 +350,8 @@ struct XAigerWriter
|
|||
if (!box_module || !box_module->attributes.count("\\abc_box_id"))
|
||||
continue;
|
||||
|
||||
bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
|
||||
|
||||
// Fully pad all unused input connections of this box cell with S0
|
||||
// Fully pad all undriven output connections of this box cell with anonymous wires
|
||||
// NB: Assume box_module->ports are sorted alphabetically
|
||||
|
@ -379,7 +396,10 @@ struct XAigerWriter
|
|||
rhs = it->second;
|
||||
}
|
||||
else {
|
||||
rhs = module->addWire(NEW_ID, GetSize(w));
|
||||
Wire *wire = module->addWire(NEW_ID, GetSize(w));
|
||||
if (blackbox)
|
||||
wire->set_bool_attribute(ID(abc_padding));
|
||||
rhs = wire;
|
||||
cell->setPort(port_name, rhs);
|
||||
}
|
||||
|
||||
|
@ -390,12 +410,7 @@ struct XAigerWriter
|
|||
if (O != b)
|
||||
alias_map[O] = b;
|
||||
undriven_bits.erase(O);
|
||||
|
||||
auto jt = input_bits.find(b);
|
||||
if (jt != input_bits.end()) {
|
||||
log_assert(keep_bits.count(O));
|
||||
input_bits.erase(b);
|
||||
}
|
||||
input_bits.erase(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +429,7 @@ struct XAigerWriter
|
|||
// inherit existing inout's drivers
|
||||
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|
||||
|| keep_bits.count(bit)) {
|
||||
RTLIL::IdString wire_name = wire->name.str() + "$inout.out";
|
||||
RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str());
|
||||
RTLIL::Wire *new_wire = module->wire(wire_name);
|
||||
if (!new_wire)
|
||||
new_wire = module->addWire(wire_name, GetSize(wire));
|
||||
|
@ -489,16 +504,16 @@ struct XAigerWriter
|
|||
aig_outputs.push_back(bit2aig(bit));
|
||||
}
|
||||
|
||||
if (output_bits.empty()) {
|
||||
output_bits.insert(State::S0);
|
||||
omode = true;
|
||||
}
|
||||
|
||||
for (auto bit : output_bits) {
|
||||
ordered_outputs[bit] = aig_o++;
|
||||
aig_outputs.push_back(bit2aig(bit));
|
||||
}
|
||||
|
||||
if (output_bits.empty()) {
|
||||
aig_o++;
|
||||
aig_outputs.push_back(0);
|
||||
omode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void write_aiger(std::ostream &f, bool ascii_mode)
|
||||
|
@ -560,26 +575,38 @@ struct XAigerWriter
|
|||
|
||||
f << "c";
|
||||
|
||||
log_assert(!output_bits.empty());
|
||||
auto write_buffer = [](std::stringstream &buffer, int i32) {
|
||||
int32_t i32_be = to_big_endian(i32);
|
||||
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
|
||||
};
|
||||
std::stringstream h_buffer;
|
||||
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
|
||||
write_h_buffer(1);
|
||||
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits));
|
||||
write_h_buffer(input_bits.size() + ci_bits.size());
|
||||
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits));
|
||||
write_h_buffer(output_bits.size() + GetSize(co_bits));
|
||||
log_debug("piNum = %d\n", GetSize(input_bits));
|
||||
write_h_buffer(input_bits.size());
|
||||
log_debug("poNum = %d\n", GetSize(output_bits));
|
||||
write_h_buffer(output_bits.size());
|
||||
log_debug("boxNum = %d\n", GetSize(box_list));
|
||||
write_h_buffer(box_list.size());
|
||||
|
||||
auto write_buffer_float = [](std::stringstream &buffer, float f32) {
|
||||
buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32));
|
||||
};
|
||||
std::stringstream i_buffer;
|
||||
auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1);
|
||||
for (auto bit : input_bits)
|
||||
write_i_buffer(arrival_times.at(bit, 0));
|
||||
//std::stringstream o_buffer;
|
||||
//auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1);
|
||||
//for (auto bit : output_bits)
|
||||
// write_o_buffer(0);
|
||||
|
||||
if (!box_list.empty()) {
|
||||
auto write_buffer = [](std::stringstream &buffer, int i32) {
|
||||
int32_t i32_be = to_big_endian(i32);
|
||||
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
|
||||
};
|
||||
|
||||
std::stringstream h_buffer;
|
||||
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
|
||||
write_h_buffer(1);
|
||||
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits));
|
||||
write_h_buffer(input_bits.size() + ci_bits.size());
|
||||
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits));
|
||||
write_h_buffer(output_bits.size() + co_bits.size());
|
||||
log_debug("piNum = %d\n", GetSize(input_bits));
|
||||
write_h_buffer(input_bits.size());
|
||||
log_debug("poNum = %d\n", GetSize(output_bits));
|
||||
write_h_buffer(output_bits.size());
|
||||
log_debug("boxNum = %d\n", GetSize(box_list));
|
||||
write_h_buffer(box_list.size());
|
||||
|
||||
RTLIL::Module *holes_module = module->design->addModule("$__holes__");
|
||||
log_assert(holes_module);
|
||||
|
||||
|
@ -643,19 +670,12 @@ struct XAigerWriter
|
|||
write_h_buffer(box_count++);
|
||||
}
|
||||
|
||||
f << "h";
|
||||
std::string buffer_str = h_buffer.str();
|
||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
std::stringstream r_buffer;
|
||||
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
|
||||
write_r_buffer(0);
|
||||
|
||||
f << "r";
|
||||
buffer_str = r_buffer.str();
|
||||
buffer_size_be = to_big_endian(buffer_str.size());
|
||||
std::string buffer_str = r_buffer.str();
|
||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
|
@ -709,6 +729,23 @@ struct XAigerWriter
|
|||
}
|
||||
}
|
||||
|
||||
f << "h";
|
||||
std::string buffer_str = h_buffer.str();
|
||||
int32_t buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
f << "i";
|
||||
buffer_str = i_buffer.str();
|
||||
buffer_size_be = to_big_endian(buffer_str.size());
|
||||
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
f.write(buffer_str.data(), buffer_str.size());
|
||||
//f << "o";
|
||||
//buffer_str = o_buffer.str();
|
||||
//buffer_size_be = to_big_endian(buffer_str.size());
|
||||
//f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
|
||||
//f.write(buffer_str.data(), buffer_str.size());
|
||||
|
||||
f << stringf("Generated by %s\n", yosys_version_str);
|
||||
}
|
||||
|
||||
|
@ -760,11 +797,11 @@ struct XAigerWriter
|
|||
f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
|
||||
|
||||
output_lines.sort();
|
||||
if (omode)
|
||||
output_lines[State::S0] = "output 0 0 $__dummy__\n";
|
||||
for (auto &it : output_lines)
|
||||
f << it.second;
|
||||
log_assert(output_lines.size() == output_bits.size());
|
||||
if (omode && output_bits.empty())
|
||||
f << "output " << output_lines.size() << " 0 $__dummy__\n";
|
||||
|
||||
wire_lines.sort();
|
||||
for (auto &it : wire_lines)
|
||||
|
@ -819,7 +856,7 @@ struct XAigerBackend : public Backend {
|
|||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
extra_args(f, filename, args, argidx, !ascii_mode);
|
||||
|
||||
Module *top_module = design->top_module();
|
||||
|
||||
|
|
|
@ -685,7 +685,7 @@ struct BtorWorker
|
|||
}
|
||||
else
|
||||
{
|
||||
int nid_init_val = next_nid++;
|
||||
nid_init_val = next_nid++;
|
||||
btorf("%d state %d\n", nid_init_val, sid);
|
||||
|
||||
for (int i = 0; i < nwords; i++) {
|
||||
|
@ -897,9 +897,12 @@ struct BtorWorker
|
|||
|
||||
int sid = get_bv_sid(GetSize(s));
|
||||
int nid = next_nid++;
|
||||
btorf("%d input %d %s\n", nid, sid);
|
||||
btorf("%d input %d\n", nid, sid);
|
||||
nid_width[nid] = GetSize(s);
|
||||
|
||||
for (int j = 0; j < GetSize(s); j++)
|
||||
nidbits.push_back(make_pair(nid, j));
|
||||
|
||||
i += GetSize(s)-1;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ struct ProtobufBackend : public Backend {
|
|||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
extra_args(f, filename, args, argidx, !text_mode);
|
||||
|
||||
log_header(design, "Executing Protobuf backend.\n");
|
||||
|
||||
|
@ -338,7 +338,7 @@ struct ProtobufPass : public Pass {
|
|||
if (!filename.empty()) {
|
||||
rewrite_filename(filename);
|
||||
std::ofstream *ff = new std::ofstream;
|
||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
||||
ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary));
|
||||
if (ff->fail()) {
|
||||
delete ff;
|
||||
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||
|
|
|
@ -16,7 +16,7 @@ yosys-smtbmc-script.py: backends/smt2/smtbmc.py
|
|||
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
|
||||
|
||||
yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
|
||||
$(P) gcc -DGUI=0 -O -s -o $@ $<
|
||||
$(P) $(CXX) -DGUI=0 -O -s -o $@ $<
|
||||
# Other targets
|
||||
else
|
||||
TARGETS += yosys-smtbmc
|
||||
|
|
|
@ -285,6 +285,8 @@ end_of_header:
|
|||
}
|
||||
else if (c == 'c') {
|
||||
f.ignore(1);
|
||||
if (f.peek() == '\r')
|
||||
f.ignore(1);
|
||||
if (f.peek() == '\n')
|
||||
break;
|
||||
// Else constraint (TODO)
|
||||
|
@ -430,6 +432,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
|
|||
else if (c == 'r') {
|
||||
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
|
||||
flopNum = parse_xaiger_literal(f);
|
||||
log_debug("flopNum: %u\n", flopNum);
|
||||
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
|
||||
f.ignore(flopNum * sizeof(uint32_t));
|
||||
}
|
||||
|
@ -496,8 +499,7 @@ void AigerReader::parse_aiger_ascii()
|
|||
|
||||
// Parse latches
|
||||
RTLIL::Wire *clk_wire = nullptr;
|
||||
if (L > 0) {
|
||||
log_assert(clk_name != "");
|
||||
if (L > 0 && !clk_name.empty()) {
|
||||
clk_wire = module->wire(clk_name);
|
||||
log_assert(!clk_wire);
|
||||
log_debug2("Creating %s\n", clk_name.c_str());
|
||||
|
@ -513,7 +515,10 @@ void AigerReader::parse_aiger_ascii()
|
|||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
if (clk_wire)
|
||||
module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
else
|
||||
module->addFfGate(NEW_ID, d_wire, q_wire);
|
||||
|
||||
// Reset logic is optional in AIGER 1.9
|
||||
if (f.peek() == ' ') {
|
||||
|
@ -621,8 +626,7 @@ void AigerReader::parse_aiger_binary()
|
|||
|
||||
// Parse latches
|
||||
RTLIL::Wire *clk_wire = nullptr;
|
||||
if (L > 0) {
|
||||
log_assert(clk_name != "");
|
||||
if (L > 0 && !clk_name.empty()) {
|
||||
clk_wire = module->wire(clk_name);
|
||||
log_assert(!clk_wire);
|
||||
log_debug2("Creating %s\n", clk_name.c_str());
|
||||
|
@ -638,7 +642,10 @@ void AigerReader::parse_aiger_binary()
|
|||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
if (clk_wire)
|
||||
module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
else
|
||||
module->addFf(NEW_ID, d_wire, q_wire);
|
||||
|
||||
// Reset logic is optional in AIGER 1.9
|
||||
if (f.peek() == ' ') {
|
||||
|
@ -776,19 +783,19 @@ void AigerReader::post_process()
|
|||
// NB: Assume box_module->ports are sorted alphabetically
|
||||
// (as RTLIL::Module::fixup_ports() would do)
|
||||
for (auto port_name : box_module->ports) {
|
||||
RTLIL::Wire* w = box_module->wire(port_name);
|
||||
log_assert(w);
|
||||
RTLIL::Wire* port = box_module->wire(port_name);
|
||||
log_assert(port);
|
||||
RTLIL::SigSpec rhs;
|
||||
RTLIL::Wire* wire = nullptr;
|
||||
for (int i = 0; i < GetSize(w); i++) {
|
||||
if (w->port_input) {
|
||||
for (int i = 0; i < GetSize(port); i++) {
|
||||
RTLIL::Wire* wire = nullptr;
|
||||
if (port->port_input) {
|
||||
log_assert(co_count < outputs.size());
|
||||
wire = outputs[co_count++];
|
||||
log_assert(wire);
|
||||
log_assert(wire->port_output);
|
||||
wire->port_output = false;
|
||||
}
|
||||
if (w->port_output) {
|
||||
if (port->port_output) {
|
||||
log_assert((piNum + ci_count) < inputs.size());
|
||||
wire = inputs[piNum + ci_count++];
|
||||
log_assert(wire);
|
||||
|
@ -797,6 +804,7 @@ void AigerReader::post_process()
|
|||
}
|
||||
rhs.append(wire);
|
||||
}
|
||||
|
||||
cell->setPort(port_name, rhs);
|
||||
}
|
||||
}
|
||||
|
@ -814,6 +822,7 @@ void AigerReader::post_process()
|
|||
RTLIL::Wire* wire = inputs[variable];
|
||||
log_assert(wire);
|
||||
log_assert(wire->port_input);
|
||||
log_debug("Renaming input %s", log_id(wire));
|
||||
|
||||
if (index == 0) {
|
||||
// Cope with the fact that a CI might be identical
|
||||
|
@ -840,6 +849,7 @@ void AigerReader::post_process()
|
|||
wire->port_input = false;
|
||||
}
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(wire));
|
||||
}
|
||||
else if (type == "output") {
|
||||
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
|
||||
|
@ -850,6 +860,7 @@ void AigerReader::post_process()
|
|||
wire->port_output = false;
|
||||
continue;
|
||||
}
|
||||
log_debug("Renaming output %s", log_id(wire));
|
||||
|
||||
if (index == 0) {
|
||||
// Cope with the fact that a CO might be identical
|
||||
|
@ -859,7 +870,7 @@ void AigerReader::post_process()
|
|||
if (!existing) {
|
||||
if (escaped_s.ends_with("$inout.out")) {
|
||||
wire->port_output = false;
|
||||
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10));
|
||||
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11));
|
||||
log_assert(in_wire);
|
||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||
in_wire->port_output = true;
|
||||
|
@ -871,6 +882,7 @@ void AigerReader::post_process()
|
|||
else {
|
||||
wire->port_output = false;
|
||||
module->connect(wire, existing);
|
||||
wire = existing;
|
||||
}
|
||||
}
|
||||
else if (index > 0) {
|
||||
|
@ -879,7 +891,7 @@ void AigerReader::post_process()
|
|||
if (!existing) {
|
||||
if (escaped_s.ends_with("$inout.out")) {
|
||||
wire->port_output = false;
|
||||
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index));
|
||||
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index));
|
||||
log_assert(in_wire);
|
||||
log_assert(in_wire->port_input && !in_wire->port_output);
|
||||
in_wire->port_output = true;
|
||||
|
@ -896,6 +908,7 @@ void AigerReader::post_process()
|
|||
wire->port_output = false;
|
||||
}
|
||||
}
|
||||
log_debug(" -> %s\n", log_id(wire));
|
||||
}
|
||||
else if (type == "box") {
|
||||
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
|
||||
|
@ -974,7 +987,7 @@ void AigerReader::post_process()
|
|||
// operate (and run checks on) this one module
|
||||
RTLIL::Design *mapped_design = new RTLIL::Design;
|
||||
mapped_design->add(module);
|
||||
Pass::call(mapped_design, "clean -purge");
|
||||
Pass::call(mapped_design, "clean");
|
||||
mapped_design->modules_.erase(module->name);
|
||||
delete mapped_design;
|
||||
|
||||
|
@ -1004,8 +1017,8 @@ struct AigerFrontend : public Frontend {
|
|||
log(" Name of module to be created (default: <filename>)\n");
|
||||
log("\n");
|
||||
log(" -clk_name <wire_name>\n");
|
||||
log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
|
||||
log(" this name (default: clk)\n");
|
||||
log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
|
||||
log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n");
|
||||
log("\n");
|
||||
log(" -map <filename>\n");
|
||||
log(" read file with port and latch symbols\n");
|
||||
|
@ -1045,13 +1058,15 @@ struct AigerFrontend : public Frontend {
|
|||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
extra_args(f, filename, args, argidx, true);
|
||||
|
||||
if (module_name.empty()) {
|
||||
#ifdef _WIN32
|
||||
char fname[_MAX_FNAME];
|
||||
_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */);
|
||||
module_name = fname;
|
||||
char* bn = strdup(fname);
|
||||
module_name = RTLIL::escape_id(bn);
|
||||
free(bn);
|
||||
#else
|
||||
char* bn = strdup(filename.c_str());
|
||||
module_name = RTLIL::escape_id(bn);
|
||||
|
|
|
@ -158,6 +158,11 @@ std::string AST::type2str(AstNodeType type)
|
|||
X(AST_POSEDGE)
|
||||
X(AST_NEGEDGE)
|
||||
X(AST_EDGE)
|
||||
X(AST_INTERFACE)
|
||||
X(AST_INTERFACEPORT)
|
||||
X(AST_INTERFACEPORTTYPE)
|
||||
X(AST_MODPORT)
|
||||
X(AST_MODPORTMEMBER)
|
||||
X(AST_PACKAGE)
|
||||
#undef X
|
||||
default:
|
||||
|
@ -1099,6 +1104,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
|
|||
|
||||
ignoreThisSignalsInInitial = RTLIL::SigSpec();
|
||||
}
|
||||
else {
|
||||
for (auto &attr : ast->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
continue;
|
||||
current_module->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
}
|
||||
|
||||
if (ast->type == AST_INTERFACE)
|
||||
current_module->set_bool_attribute("\\is_interface");
|
||||
|
@ -1284,6 +1296,8 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
|
|||
// from AST. The interface members are copied into the AST module with the prefix of the interface.
|
||||
void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
|
||||
{
|
||||
loadconfig();
|
||||
|
||||
bool is_top = false;
|
||||
AstNode *new_ast = ast->clone();
|
||||
for (auto &intf : local_interfaces) {
|
||||
|
@ -1467,24 +1481,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
|
|||
stripped_name = stripped_name.substr(9);
|
||||
|
||||
log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
|
||||
|
||||
current_ast = NULL;
|
||||
flag_dump_ast1 = false;
|
||||
flag_dump_ast2 = false;
|
||||
flag_dump_vlog1 = false;
|
||||
flag_dump_vlog2 = false;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomeminit = nomeminit;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
flag_mem2reg = mem2reg;
|
||||
flag_noblackbox = noblackbox;
|
||||
flag_lib = lib;
|
||||
flag_nowb = nowb;
|
||||
flag_noopt = noopt;
|
||||
flag_icells = icells;
|
||||
flag_pwires = pwires;
|
||||
flag_autowire = autowire;
|
||||
use_internal_line_num();
|
||||
loadconfig();
|
||||
|
||||
std::string para_info;
|
||||
AstNode *new_ast = ast->clone();
|
||||
|
@ -1565,6 +1562,27 @@ RTLIL::Module *AstModule::clone() const
|
|||
return new_mod;
|
||||
}
|
||||
|
||||
void AstModule::loadconfig() const
|
||||
{
|
||||
current_ast = NULL;
|
||||
flag_dump_ast1 = false;
|
||||
flag_dump_ast2 = false;
|
||||
flag_dump_vlog1 = false;
|
||||
flag_dump_vlog2 = false;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomeminit = nomeminit;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
flag_mem2reg = mem2reg;
|
||||
flag_noblackbox = noblackbox;
|
||||
flag_lib = lib;
|
||||
flag_nowb = nowb;
|
||||
flag_noopt = noopt;
|
||||
flag_icells = icells;
|
||||
flag_pwires = pwires;
|
||||
flag_autowire = autowire;
|
||||
use_internal_line_num();
|
||||
}
|
||||
|
||||
// internal dummy line number callbacks
|
||||
namespace {
|
||||
int internal_line_num;
|
||||
|
|
|
@ -299,6 +299,7 @@ namespace AST
|
|||
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
|
||||
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
|
||||
RTLIL::Module *clone() const YS_OVERRIDE;
|
||||
void loadconfig() const;
|
||||
};
|
||||
|
||||
// this must be set by the language frontend before parsing the sources
|
||||
|
|
|
@ -1530,10 +1530,16 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
current_scope[wire_en->str] = wire_en;
|
||||
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
|
||||
|
||||
std::vector<RTLIL::State> x_bit;
|
||||
x_bit.push_back(RTLIL::State::Sx);
|
||||
AstNode *check_defval;
|
||||
if (type == AST_LIVE || type == AST_FAIR) {
|
||||
check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone());
|
||||
} else {
|
||||
std::vector<RTLIL::State> x_bit;
|
||||
x_bit.push_back(RTLIL::State::Sx);
|
||||
check_defval = mkconst_bits(x_bit, false);
|
||||
}
|
||||
|
||||
AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false));
|
||||
AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval);
|
||||
assign_check->children[0]->str = id_check;
|
||||
assign_check->children[0]->was_checked = true;
|
||||
|
||||
|
@ -1546,9 +1552,13 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
default_signals->children.push_back(assign_en);
|
||||
current_top_block->children.insert(current_top_block->children.begin(), default_signals);
|
||||
|
||||
assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
|
||||
assign_check->children[0]->str = id_check;
|
||||
assign_check->children[0]->was_checked = true;
|
||||
if (type == AST_LIVE || type == AST_FAIR) {
|
||||
assign_check = nullptr;
|
||||
} else {
|
||||
assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
|
||||
assign_check->children[0]->str = id_check;
|
||||
assign_check->children[0]->was_checked = true;
|
||||
}
|
||||
|
||||
if (current_always == nullptr || current_always->type != AST_INITIAL) {
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
|
||||
|
@ -1560,7 +1570,8 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
assign_en->children[0]->was_checked = true;
|
||||
|
||||
newNode = new AstNode(AST_BLOCK);
|
||||
newNode->children.push_back(assign_check);
|
||||
if (assign_check != nullptr)
|
||||
newNode->children.push_back(assign_check);
|
||||
newNode->children.push_back(assign_en);
|
||||
|
||||
AstNode *assertnode = new AstNode(type);
|
||||
|
@ -2884,8 +2895,15 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
|
|||
void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map)
|
||||
{
|
||||
if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) {
|
||||
current_scope[index_var]->children[0]->cloneInto(this);
|
||||
return;
|
||||
if (children.empty()) {
|
||||
current_scope[index_var]->children[0]->cloneInto(this);
|
||||
} else {
|
||||
AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone());
|
||||
p->str = stringf("$genval$%d", autoidx++);
|
||||
current_ast_mod->children.push_back(p);
|
||||
str = p->str;
|
||||
id2ast = p;
|
||||
}
|
||||
}
|
||||
|
||||
if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
OBJS += frontends/rpc/rpc_frontend.o
|
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
* 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>
|
||||
#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)
|
||||
|
||||
struct HandleRpcServer : RpcServer {
|
||||
HANDLE hsend, hrecv;
|
||||
|
||||
HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv)
|
||||
: RpcServer(name), hsend(hsend), hrecv(hrecv) { }
|
||||
|
||||
void write(const std::string &data) YS_OVERRIDE {
|
||||
log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
|
||||
ssize_t offset = 0;
|
||||
do {
|
||||
DWORD data_written;
|
||||
if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL))
|
||||
log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str());
|
||||
offset += data_written;
|
||||
} while(offset < (ssize_t)data.length());
|
||||
}
|
||||
|
||||
std::string read() YS_OVERRIDE {
|
||||
std::string data;
|
||||
ssize_t offset = 0;
|
||||
while (data.length() == 0 || data[data.length() - 1] != '\n') {
|
||||
data.resize(data.length() + 1024);
|
||||
DWORD data_read;
|
||||
if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL))
|
||||
log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str());
|
||||
offset += data_read;
|
||||
data.resize(offset);
|
||||
size_t term_pos = data.find('\n', offset);
|
||||
if (term_pos != data.length() - 1 && term_pos != std::string::npos)
|
||||
log_cmd_error("read failed: more than one response\n");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
~HandleRpcServer() {
|
||||
CloseHandle(hsend);
|
||||
if (hrecv != hsend)
|
||||
CloseHandle(hrecv);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct FdRpcServer : RpcServer {
|
||||
int fdsend, fdrecv;
|
||||
pid_t pid;
|
||||
|
||||
FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1)
|
||||
: RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { }
|
||||
|
||||
void check_pid() {
|
||||
if (pid == -1) return;
|
||||
// If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE.
|
||||
pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
|
||||
if (wait_result == -1)
|
||||
log_cmd_error("waitpid failed: %s\n", strerror(errno));
|
||||
if (wait_result == pid)
|
||||
log_cmd_error("RPC frontend terminated unexpectedly\n");
|
||||
}
|
||||
|
||||
void write(const std::string &data) YS_OVERRIDE {
|
||||
log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
|
||||
ssize_t offset = 0;
|
||||
do {
|
||||
check_pid();
|
||||
ssize_t result = ::write(fdsend, &data[offset], data.length() - offset);
|
||||
if (result == -1)
|
||||
log_cmd_error("write failed: %s\n", strerror(errno));
|
||||
offset += result;
|
||||
} while(offset < (ssize_t)data.length());
|
||||
}
|
||||
|
||||
std::string read() YS_OVERRIDE {
|
||||
std::string data;
|
||||
ssize_t offset = 0;
|
||||
while (data.length() == 0 || data[data.length() - 1] != '\n') {
|
||||
data.resize(data.length() + 1024);
|
||||
check_pid();
|
||||
ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset);
|
||||
if (result == -1)
|
||||
log_cmd_error("read failed: %s\n", strerror(errno));
|
||||
offset += result;
|
||||
data.resize(offset);
|
||||
size_t term_pos = data.find('\n', offset);
|
||||
if (term_pos != data.length() - 1 && term_pos != std::string::npos)
|
||||
log_cmd_error("read failed: more than one response\n");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
~FdRpcServer() {
|
||||
close(fdsend);
|
||||
if (fdrecv != fdsend)
|
||||
close(fdrecv);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// RpcFrontend does not inherit from Frontend since it does not read files.
|
||||
struct RpcFrontend : public Pass {
|
||||
RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" connect_rpc -exec <command> [args...]\n");
|
||||
log(" connect_rpc -path <path>\n");
|
||||
log("\n");
|
||||
log("Load modules using an out-of-process frontend.\n");
|
||||
log("\n");
|
||||
log(" -exec <command> [args...]\n");
|
||||
log(" run <command> with arguments [args...]. send requests on stdin, read\n");
|
||||
log(" responses from stdout.\n");
|
||||
log("\n");
|
||||
log(" -path <path>\n");
|
||||
log(" connect to Unix domain socket at <path>. (Unix)\n");
|
||||
log(" connect to bidirectional byte-type named pipe at <path>. (Windows)\n");
|
||||
log("\n");
|
||||
log("A simple JSON-based, newline-delimited protocol is used for communicating with\n");
|
||||
log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n");
|
||||
log("of JSON. Frontend responds with data or error message by replying with exactly\n");
|
||||
log("1 line of JSON as well.\n");
|
||||
log("\n");
|
||||
log(" -> {\"method\": \"modules\"}\n");
|
||||
log(" <- {\"modules\": [\"<module-name>\", ...]}\n");
|
||||
log(" <- {\"error\": \"<error-message>\"}\n");
|
||||
log(" request for the list of modules that can be derived by this frontend.\n");
|
||||
log(" the 'hierarchy' command will call back into this frontend if a cell\n");
|
||||
log(" with type <module-name> is instantiated in the design.\n");
|
||||
log("\n");
|
||||
log(" -> {\"method\": \"derive\", \"module\": \"<module-name\">, \"parameters\": {\n");
|
||||
log(" \"<param-name>\": {\"type\": \"[unsigned|signed|string|real]\",\n");
|
||||
log(" \"value\": \"<param-value>\"}, ...}}\n");
|
||||
log(" <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"<source>\"}}\n");
|
||||
log(" <- {\"error\": \"<error-message>\"}\n");
|
||||
log(" request for the module <module-name> to be derived for a specific set of\n");
|
||||
log(" parameters. <param-name> starts with \\ for named parameters, and with $\n");
|
||||
log(" for unnamed parameters, which are numbered starting at 1.<param-value>\n");
|
||||
log(" for integer parameters is always specified as a binary string of unlimited\n");
|
||||
log(" precision. the <source> returned by the frontend is hygienically parsed\n");
|
||||
log(" by a built-in Yosys <frontend>, allowing the RPC frontend to return any\n");
|
||||
log(" convenient representation of the module. the derived module is cached,\n");
|
||||
log(" so the response should be the same whenever the same set of parameters\n");
|
||||
log(" is provided.\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Connecting to RPC frontend.\n");
|
||||
|
||||
std::vector<std::string> command;
|
||||
std::string path;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-exec" && argidx+1 < args.size()) {
|
||||
command.insert(command.begin(), args.begin() + argidx + 1, args.end());
|
||||
continue;
|
||||
}
|
||||
if (arg == "-path" && argidx+1 < args.size()) {
|
||||
path = args[argidx+1];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if ((!command.empty()) + (!path.empty()) != 1)
|
||||
log_cmd_error("Exactly one of -exec, -unix must be specified.\n");
|
||||
|
||||
std::shared_ptr<RpcServer> server;
|
||||
if (!command.empty()) {
|
||||
std::string command_line;
|
||||
bool first = true;
|
||||
for (auto &arg : command) {
|
||||
if (!first) command_line += ' ';
|
||||
command_line += arg;
|
||||
first = false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::wstring command_w = str2wstr(command[0]);
|
||||
std::wstring command_path_w;
|
||||
std::wstring command_line_w = str2wstr(command_line);
|
||||
DWORD command_path_len_w;
|
||||
SECURITY_ATTRIBUTES pipe_attr = {};
|
||||
HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL;
|
||||
STARTUPINFOW startup_info = {};
|
||||
PROCESS_INFORMATION proc_info = {};
|
||||
|
||||
command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL);
|
||||
if (command_path_len_w == 0) {
|
||||
log_error("SearchPathW failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
command_path_w.resize(command_path_len_w - 1);
|
||||
command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL);
|
||||
log_assert(command_path_len_w == command_path_w.length());
|
||||
|
||||
pipe_attr.nLength = sizeof(pipe_attr);
|
||||
pipe_attr.bInheritHandle = TRUE;
|
||||
pipe_attr.lpSecurityDescriptor = NULL;
|
||||
if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) {
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) {
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) {
|
||||
log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) {
|
||||
log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
startup_info.hStdInput = send_r;
|
||||
startup_info.hStdOutput = recv_w;
|
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) {
|
||||
log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_exec;
|
||||
}
|
||||
CloseHandle(proc_info.hProcess);
|
||||
CloseHandle(proc_info.hThread);
|
||||
|
||||
server = std::make_shared<HandleRpcServer>(path, send_w, recv_r);
|
||||
send_w = NULL;
|
||||
recv_r = NULL;
|
||||
|
||||
cleanup_exec:
|
||||
if (send_r != NULL) CloseHandle(send_r);
|
||||
if (send_w != NULL) CloseHandle(send_w);
|
||||
if (recv_r != NULL) CloseHandle(recv_r);
|
||||
if (recv_w != NULL) CloseHandle(recv_w);
|
||||
#else
|
||||
std::vector<char *> argv;
|
||||
int send[2] = {-1,-1}, recv[2] = {-1,-1};
|
||||
posix_spawn_file_actions_t file_actions, *file_actions_p = NULL;
|
||||
pid_t pid;
|
||||
|
||||
for (auto &arg : command)
|
||||
argv.push_back(&arg[0]);
|
||||
argv.push_back(nullptr);
|
||||
|
||||
if (pipe(send) != 0) {
|
||||
log_error("pipe failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (pipe(recv) != 0) {
|
||||
log_error("pipe failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
|
||||
if (posix_spawn_file_actions_init(&file_actions) != 0) {
|
||||
log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
file_actions_p = &file_actions;
|
||||
if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) {
|
||||
log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) {
|
||||
log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) {
|
||||
log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) {
|
||||
log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
|
||||
if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) {
|
||||
log_error("posix_spawnp failed: %s\n", strerror(errno));
|
||||
goto cleanup_exec;
|
||||
}
|
||||
|
||||
server = std::make_shared<FdRpcServer>(command_line, send[1], recv[0], pid);
|
||||
send[1] = -1;
|
||||
recv[0] = -1;
|
||||
|
||||
cleanup_exec:
|
||||
if (send[0] != -1) close(send[0]);
|
||||
if (send[1] != -1) close(send[1]);
|
||||
if (recv[0] != -1) close(recv[0]);
|
||||
if (recv[1] != -1) close(recv[1]);
|
||||
if (file_actions_p != NULL)
|
||||
posix_spawn_file_actions_destroy(file_actions_p);
|
||||
#endif
|
||||
} else if (!path.empty()) {
|
||||
#ifdef _WIN32
|
||||
std::wstring path_w = str2wstr(path);
|
||||
HANDLE h;
|
||||
|
||||
h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
log_error("CreateFileW failed: %s\n", get_last_error_str().c_str());
|
||||
goto cleanup_path;
|
||||
}
|
||||
|
||||
server = std::make_shared<HandleRpcServer>(path, h, h);
|
||||
|
||||
cleanup_path:
|
||||
;
|
||||
#else
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
log_error("socket failed: %s\n", strerror(errno));
|
||||
goto cleanup_path;
|
||||
}
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
log_error("connect failed: %s\n", strerror(errno));
|
||||
goto cleanup_path;
|
||||
}
|
||||
|
||||
server = std::make_shared<FdRpcServer>(path, fd, fd);
|
||||
fd = -1;
|
||||
|
||||
cleanup_path:
|
||||
if (fd != -1) close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!server)
|
||||
log_cmd_error("Failed to connect to RPC frontend.\n");
|
||||
|
||||
for (auto &module_name : server->get_module_names()) {
|
||||
log("Linking module `%s'.\n", module_name.c_str());
|
||||
RpcModule *module = new RpcModule;
|
||||
module->name = "$abstract\\" + module_name;
|
||||
module->server = server;
|
||||
design->add(module);
|
||||
}
|
||||
}
|
||||
} RpcFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
|
@ -85,10 +85,8 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
|
|||
digits.push_back(10 + *str - 'A');
|
||||
else if (*str == 'x' || *str == 'X')
|
||||
digits.push_back(0xf0);
|
||||
else if (*str == 'z' || *str == 'Z')
|
||||
else if (*str == 'z' || *str == 'Z' || *str == '?')
|
||||
digits.push_back(0xf1);
|
||||
else if (*str == '?')
|
||||
digits.push_back(0xf2);
|
||||
str++;
|
||||
}
|
||||
|
||||
|
@ -112,8 +110,6 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
|
|||
data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
|
||||
else if (*it == 0xf1)
|
||||
data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
|
||||
else if (*it == 0xf2)
|
||||
data.push_back(RTLIL::Sa);
|
||||
else
|
||||
data.push_back((*it & bitmask) ? State::S1 : State::S0);
|
||||
}
|
||||
|
@ -199,13 +195,13 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
|
|||
if (str == endptr)
|
||||
len_in_bits = -1;
|
||||
|
||||
// The "<bits>'s?[bodhBODH]<digits>" syntax
|
||||
// The "<bits>'[sS]?[bodhBODH]<digits>" syntax
|
||||
if (*endptr == '\'')
|
||||
{
|
||||
std::vector<RTLIL::State> data;
|
||||
bool is_signed = false;
|
||||
bool is_unsized = len_in_bits < 0;
|
||||
if (*(endptr+1) == 's') {
|
||||
if (*(endptr+1) == 's' || *(endptr+1) == 'S') {
|
||||
is_signed = true;
|
||||
endptr++;
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ YOSYS_NAMESPACE_END
|
|||
return TOK_CONSTVAL;
|
||||
}
|
||||
|
||||
[0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
|
||||
[0-9]*[ \t]*\'[sS]?[bodhBODH]?[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_CONSTVAL;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ using zlib to write gzip-compressed data every time the stream is flushed.
|
|||
*/
|
||||
class gzip_ostream : public std::ostream {
|
||||
public:
|
||||
gzip_ostream()
|
||||
gzip_ostream() : std::ostream(nullptr)
|
||||
{
|
||||
rdbuf(&outbuf);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ private:
|
|||
str("");
|
||||
return 0;
|
||||
}
|
||||
~gzip_streambuf()
|
||||
virtual ~gzip_streambuf()
|
||||
{
|
||||
sync();
|
||||
gzclose(gzf);
|
||||
|
@ -439,7 +439,7 @@ void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
|||
FILE *Frontend::current_script_file = NULL;
|
||||
std::string Frontend::last_here_document;
|
||||
|
||||
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
||||
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input)
|
||||
{
|
||||
bool called_with_fp = f != NULL;
|
||||
|
||||
|
@ -489,7 +489,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
|
|||
next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
|
||||
}
|
||||
std::ifstream *ff = new std::ifstream;
|
||||
ff->open(filename.c_str());
|
||||
ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in);
|
||||
yosys_input_files.insert(filename);
|
||||
if (ff->fail())
|
||||
delete ff;
|
||||
|
@ -498,7 +498,15 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
|
|||
if (f != NULL) {
|
||||
// Check for gzip magic
|
||||
unsigned char magic[3];
|
||||
int n = readsome(*ff, reinterpret_cast<char*>(magic), 3);
|
||||
int n = 0;
|
||||
while (n < 3)
|
||||
{
|
||||
int c = ff->get();
|
||||
if (c != EOF) {
|
||||
magic[n] = (unsigned char) c;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) {
|
||||
#ifdef YOSYS_ENABLE_ZLIB
|
||||
log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str());
|
||||
|
@ -604,7 +612,7 @@ void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
|||
delete f;
|
||||
}
|
||||
|
||||
void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
||||
void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output)
|
||||
{
|
||||
bool called_with_fp = f != NULL;
|
||||
|
||||
|
@ -639,7 +647,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st
|
|||
#endif
|
||||
} else {
|
||||
std::ofstream *ff = new std::ofstream;
|
||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
||||
ff->open(filename.c_str(), bin_output ? (std::ofstream::trunc | std::ofstream::binary) : std::ofstream::trunc);
|
||||
yosys_output_files.insert(filename);
|
||||
if (ff->fail()) {
|
||||
delete ff;
|
||||
|
|
|
@ -94,7 +94,7 @@ struct Frontend : Pass
|
|||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
static std::vector<std::string> next_args;
|
||||
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
||||
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input = false);
|
||||
|
||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command);
|
||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args);
|
||||
|
@ -109,7 +109,7 @@ struct Backend : Pass
|
|||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL;
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
||||
void extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output = false);
|
||||
|
||||
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command);
|
||||
static void backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args);
|
||||
|
|
|
@ -1528,7 +1528,7 @@ std::vector<RTLIL::Wire*> RTLIL::Module::selected_wires() const
|
|||
std::vector<RTLIL::Cell*> RTLIL::Module::selected_cells() const
|
||||
{
|
||||
std::vector<RTLIL::Cell*> result;
|
||||
result.reserve(wires_.size());
|
||||
result.reserve(cells_.size());
|
||||
for (auto &it : cells_)
|
||||
if (design->selected(this, it.second))
|
||||
result.push_back(it.second);
|
||||
|
@ -3083,6 +3083,7 @@ void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules, RT
|
|||
log_assert(other != NULL);
|
||||
log_assert(width_ == other->width_);
|
||||
|
||||
if (rules.empty()) return;
|
||||
unpack();
|
||||
other->unpack();
|
||||
|
||||
|
@ -3107,6 +3108,7 @@ void RTLIL::SigSpec::replace(const std::map<RTLIL::SigBit, RTLIL::SigBit> &rules
|
|||
log_assert(other != NULL);
|
||||
log_assert(width_ == other->width_);
|
||||
|
||||
if (rules.empty()) return;
|
||||
unpack();
|
||||
other->unpack();
|
||||
|
||||
|
|
|
@ -135,9 +135,11 @@ struct SigPool
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T, class Compare = std::less<T>>
|
||||
template <typename T, class Compare = void>
|
||||
struct SigSet
|
||||
{
|
||||
static_assert(!std::is_same<Compare,void>::value, "Default value for `Compare' class not found for SigSet<T>. Please specify.");
|
||||
|
||||
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
|
||||
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
|
||||
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
|
||||
|
@ -220,6 +222,13 @@ struct SigSet
|
|||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SigSet<T, typename std::enable_if<!std::is_pointer<T>::value>::type> : public SigSet<T, std::less<T>> {};
|
||||
template<typename T>
|
||||
using sort_by_name_id_guard = typename std::enable_if<std::is_same<T,RTLIL::Cell*>::value>::type;
|
||||
template<typename T>
|
||||
class SigSet<T, sort_by_name_id_guard<T>> : public SigSet<T, RTLIL::sort_by_name_id<typename std::remove_pointer<T>::type>> {};
|
||||
|
||||
struct SigMap
|
||||
{
|
||||
mfp<SigBit> database;
|
||||
|
|
|
@ -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
|
|
@ -65,7 +65,7 @@ SOFTWARE. */
|
|||
|
||||
int child_pid=0;
|
||||
|
||||
int fail(char *format, char *data) {
|
||||
int fail(const char *format, const char *data) {
|
||||
/* Print error message to stderr and return 2 */
|
||||
fprintf(stderr, format, data);
|
||||
return 2;
|
||||
|
@ -76,7 +76,7 @@ char *quoted(char *data) {
|
|||
|
||||
/* We allocate twice as much space as needed to deal with worse-case
|
||||
of having to escape everything. */
|
||||
char *result = calloc(ln*2+3, sizeof(char));
|
||||
char *result = (char *)calloc(ln*2+3, sizeof(char));
|
||||
char *presult = result;
|
||||
|
||||
*presult++ = '"';
|
||||
|
@ -120,7 +120,7 @@ char *loadable_exe(char *exename) {
|
|||
if (!hPython) return NULL; */
|
||||
|
||||
/* Return the absolute filename for spawnv */
|
||||
result = calloc(MAX_PATH, sizeof(char));
|
||||
result = (char *)calloc(MAX_PATH, sizeof(char));
|
||||
strncpy(result, exename, MAX_PATH);
|
||||
/*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
|
||||
|
||||
|
@ -158,7 +158,7 @@ char **parse_argv(char *cmdline, int *argc)
|
|||
{
|
||||
/* Parse a command line in-place using MS C rules */
|
||||
|
||||
char **result = calloc(strlen(cmdline), sizeof(char *));
|
||||
char **result = (char **)calloc(strlen(cmdline), sizeof(char *));
|
||||
char *output = cmdline;
|
||||
char c;
|
||||
int nb = 0;
|
||||
|
|
|
@ -1081,6 +1081,8 @@ class WConstructor:
|
|||
con.args = []
|
||||
con.duplicate = False
|
||||
con.protected = protected
|
||||
if str.startswith(str_def, "inline "):
|
||||
str_def = str_def[7:]
|
||||
if not str.startswith(str_def, class_.name + "("):
|
||||
return None
|
||||
str_def = str_def[len(class_.name)+1:]
|
||||
|
|
|
@ -25,6 +25,7 @@ OBJS += passes/cmds/plugin.o
|
|||
OBJS += passes/cmds/check.o
|
||||
OBJS += passes/cmds/qwp.o
|
||||
OBJS += passes/cmds/edgetypes.o
|
||||
OBJS += passes/cmds/portlist.o
|
||||
OBJS += passes/cmds/chformal.o
|
||||
OBJS += passes/cmds/chtype.o
|
||||
OBJS += passes/cmds/blackbox.o
|
||||
|
|
|
@ -105,6 +105,11 @@ struct AddPass : public Pass {
|
|||
log("Like 'add -input', but also connect the signal between instances of the\n");
|
||||
log("selected modules.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" add -mod <name[s]>\n");
|
||||
log("\n");
|
||||
log("Add module[s] with the specified name[s].\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
|
@ -113,6 +118,7 @@ struct AddPass : public Pass {
|
|||
bool arg_flag_input = false;
|
||||
bool arg_flag_output = false;
|
||||
bool arg_flag_global = false;
|
||||
bool mod_mode = false;
|
||||
int arg_width = 0;
|
||||
|
||||
size_t argidx;
|
||||
|
@ -133,8 +139,20 @@ struct AddPass : public Pass {
|
|||
arg_width = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (arg == "-mod") {
|
||||
mod_mode = true;
|
||||
argidx++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mod_mode) {
|
||||
for (; argidx < args.size(); argidx++)
|
||||
design->addModule(RTLIL::escape_id(args[argidx]));
|
||||
return;
|
||||
}
|
||||
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto &mod : design->modules_)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct PortlistPass : public Pass {
|
||||
PortlistPass() : Pass("portlist", "list (top-level) ports") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" portlist [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command lists all module ports found in the selected modules.\n");
|
||||
log("\n");
|
||||
log("If no selection is provided then it lists the ports on the top module.\n");
|
||||
log("\n");
|
||||
log(" -m\n");
|
||||
log(" print verilog blackbox module definitions instead of port lists\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool m_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-m") {
|
||||
m_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bool first_module = true;
|
||||
|
||||
auto handle_module = [&](RTLIL::Module *module) {
|
||||
vector<string> ports;
|
||||
if (first_module)
|
||||
first_module = false;
|
||||
else
|
||||
log("\n");
|
||||
for (auto port : module->ports) {
|
||||
auto *w = module->wire(port);
|
||||
ports.push_back(stringf("%s [%d:%d] %s", w->port_input ? w->port_output ? "inout" : "input" : "output",
|
||||
w->upto ? w->start_offset : w->start_offset + w->width - 1,
|
||||
w->upto ? w->start_offset + w->width - 1 : w->start_offset,
|
||||
log_id(w)));
|
||||
}
|
||||
log("module %s%s\n", log_id(module), m_mode ? " (" : "");
|
||||
for (int i = 0; i < GetSize(ports); i++)
|
||||
log("%s%s\n", ports[i].c_str(), m_mode && i+1 < GetSize(ports) ? "," : "");
|
||||
if (m_mode)
|
||||
log(");\nendmodule\n");
|
||||
};
|
||||
|
||||
if (argidx == args.size())
|
||||
{
|
||||
auto *top = design->top_module();
|
||||
if (top == nullptr)
|
||||
log_cmd_error("Can't find top module in current design!\n");
|
||||
handle_module(top);
|
||||
}
|
||||
else
|
||||
{
|
||||
extra_args(args, argidx, design);
|
||||
for (auto module : design->selected_modules())
|
||||
handle_module(module);
|
||||
}
|
||||
}
|
||||
} PortlistPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -26,6 +26,10 @@
|
|||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef YOSYS_ENABLE_READLINE
|
||||
# include <readline/readline.h>
|
||||
#endif
|
||||
|
@ -866,7 +870,11 @@ struct ShowPass : public Pass {
|
|||
log_cmd_error("Shell command failed!\n");
|
||||
} else
|
||||
if (format.empty()) {
|
||||
#ifdef __APPLE__
|
||||
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
|
||||
#else
|
||||
std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
|
||||
#endif
|
||||
log("Exec: %s\n", cmd.c_str());
|
||||
if (run_command(cmd) != 0)
|
||||
log_cmd_error("Shell command failed!\n");
|
||||
|
|
|
@ -32,7 +32,8 @@ struct EquivOptPass:public ScriptPass
|
|||
log("\n");
|
||||
log(" equiv_opt [options] [command]\n");
|
||||
log("\n");
|
||||
log("This command checks circuit equivalence before and after an optimization pass.\n");
|
||||
log("This command uses temporal induction to check circuit equivalence before and\n");
|
||||
log("after an optimization pass.\n");
|
||||
log("\n");
|
||||
log(" -run <from_label>:<to_label>\n");
|
||||
log(" only run the commands between the labels (see below). an empty\n");
|
||||
|
@ -46,6 +47,9 @@ struct EquivOptPass:public ScriptPass
|
|||
log(" -assert\n");
|
||||
log(" produce an error if the circuits are not equivalent.\n");
|
||||
log("\n");
|
||||
log(" -multiclock\n");
|
||||
log(" run clk2fflogic before equivalence checking.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states during equiv_induct.\n");
|
||||
log("\n");
|
||||
|
@ -55,7 +59,7 @@ struct EquivOptPass:public ScriptPass
|
|||
}
|
||||
|
||||
std::string command, techmap_opts;
|
||||
bool assert, undef;
|
||||
bool assert, undef, multiclock;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -63,6 +67,7 @@ struct EquivOptPass:public ScriptPass
|
|||
techmap_opts = "";
|
||||
assert = false;
|
||||
undef = false;
|
||||
multiclock = false;
|
||||
}
|
||||
|
||||
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
|
||||
|
@ -92,6 +97,10 @@ struct EquivOptPass:public ScriptPass
|
|||
undef = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-multiclock") {
|
||||
multiclock = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -146,6 +155,10 @@ struct EquivOptPass:public ScriptPass
|
|||
}
|
||||
|
||||
if (check_label("prove")) {
|
||||
if (multiclock || help_mode)
|
||||
run("clk2fflogic", "(only with -multiclock)");
|
||||
if (!multiclock || help_mode)
|
||||
run("async2sync", "(only without -multiclock)");
|
||||
run("equiv_make gold gate equiv");
|
||||
if (help_mode)
|
||||
run("equiv_induct [-undef] equiv");
|
||||
|
|
|
@ -808,6 +808,30 @@ struct HierarchyPass : public Pass {
|
|||
if (mod_it.second->get_bool_attribute("\\top"))
|
||||
top_mod = mod_it.second;
|
||||
|
||||
if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) {
|
||||
IdString top_name = top_mod->name.substr(strlen("$abstract"));
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Const> top_parameters;
|
||||
for (auto ¶ : parameters) {
|
||||
SigSpec sig_value;
|
||||
if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
|
||||
log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
|
||||
top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
|
||||
}
|
||||
|
||||
top_mod = design->module(top_mod->derive(design, top_parameters));
|
||||
|
||||
if (top_mod != nullptr && top_mod->name != top_name) {
|
||||
Module *m = top_mod->clone();
|
||||
m->name = top_name;
|
||||
Module *old_mod = design->module(top_name);
|
||||
if (old_mod)
|
||||
design->remove(old_mod);
|
||||
design->add(m);
|
||||
top_mod = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (top_mod == nullptr && auto_top_mode) {
|
||||
log_header(design, "Finding top of design hierarchy..\n");
|
||||
dict<Module*, int> db;
|
||||
|
|
|
@ -953,6 +953,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
}
|
||||
|
||||
if (b.is_fully_const()) {
|
||||
if (b.is_fully_undef()) {
|
||||
RTLIL::SigSpec input = b;
|
||||
ACTION_DO(ID::Y, Const(State::Sx, GetSize(cell->getPort(ID::Y))));
|
||||
} else
|
||||
if (b.as_bool() == (cell->type == ID($eq))) {
|
||||
RTLIL::SigSpec input = b;
|
||||
ACTION_DO(ID::Y, cell->getPort(ID::A));
|
||||
|
|
|
@ -108,12 +108,13 @@ bool cell_supported(RTLIL::Cell *cell)
|
|||
return false;
|
||||
}
|
||||
|
||||
std::map<IdString, IdString> mergeable_type_map{
|
||||
{ID($sub), ID($add)},
|
||||
};
|
||||
std::map<IdString, IdString> mergeable_type_map;
|
||||
|
||||
bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
|
||||
{
|
||||
if (mergeable_type_map.empty()) {
|
||||
mergeable_type_map.insert({ID($sub), ID($add)});
|
||||
}
|
||||
auto a_type = a->type;
|
||||
if (mergeable_type_map.count(a_type))
|
||||
a_type = mergeable_type_map.at(a_type);
|
||||
|
|
|
@ -1 +1 @@
|
|||
/*_pm.h
|
||||
/*_pm.h
|
||||
|
|
|
@ -21,12 +21,21 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
|
|||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/xilinx_dsp.o
|
||||
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
|
||||
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
OBJS += passes/pmgen/peepopt.o
|
||||
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
|
||||
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
|
||||
|
||||
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
|
||||
|
||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
|
||||
|
|
|
@ -352,7 +352,7 @@ state variables used to pass arguments.
|
|||
subpattern tail
|
||||
...
|
||||
|
||||
Subpatterns cann be called recursively.
|
||||
Subpatterns can be called recursively.
|
||||
|
||||
If a `subpattern` statement is preceded by a `fallthrough` statement, this is
|
||||
equivalent to calling the subpattern at the end of the preceding block.
|
||||
|
|
|
@ -29,19 +29,19 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
|||
{
|
||||
auto &st = pm.st_ice40_dsp;
|
||||
|
||||
#if 0
|
||||
log("\n");
|
||||
log("ffA: %s\n", log_id(st.ffA, "--"));
|
||||
log("ffB: %s\n", log_id(st.ffB, "--"));
|
||||
log("mul: %s\n", log_id(st.mul, "--"));
|
||||
log("ffY: %s\n", log_id(st.ffY, "--"));
|
||||
log("addAB: %s\n", log_id(st.addAB, "--"));
|
||||
log("muxAB: %s\n", log_id(st.muxAB, "--"));
|
||||
log("ffS: %s\n", log_id(st.ffS, "--"));
|
||||
#endif
|
||||
|
||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
|
||||
|
||||
log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
|
||||
log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
|
||||
log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
|
||||
log_debug("mul: %s\n", log_id(st.mul, "--"));
|
||||
log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
|
||||
log_debug("ffH: %s\n", log_id(st.ffH, "--"));
|
||||
log_debug("add: %s\n", log_id(st.add, "--"));
|
||||
log_debug("mux: %s\n", log_id(st.mux, "--"));
|
||||
log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
|
||||
log_debug("\n");
|
||||
|
||||
if (GetSize(st.sigA) > 16) {
|
||||
log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
|
||||
return;
|
||||
|
@ -52,59 +52,85 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
|||
return;
|
||||
}
|
||||
|
||||
if (GetSize(st.sigS) > 32) {
|
||||
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
|
||||
if (GetSize(st.sigO) > 33) {
|
||||
log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSize(st.sigY) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
|
||||
if (GetSize(st.sigH) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH));
|
||||
return;
|
||||
}
|
||||
|
||||
bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
|
||||
Cell *cell = st.mul;
|
||||
if (cell->type == ID($mul)) {
|
||||
log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type));
|
||||
|
||||
log(" replacing $mul with SB_MAC16 cell.\n");
|
||||
|
||||
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
|
||||
pm.module->swap_names(cell, st.mul);
|
||||
cell = pm.module->addCell(NEW_ID, ID(SB_MAC16));
|
||||
pm.module->swap_names(cell, st.mul);
|
||||
}
|
||||
else log_assert(cell->type == ID(SB_MAC16));
|
||||
|
||||
// SB_MAC16 Input Interface
|
||||
|
||||
SigSpec A = st.sigA;
|
||||
A.extend_u0(16, mul_signed);
|
||||
A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||
log_assert(GetSize(A) == 16);
|
||||
|
||||
SigSpec B = st.sigB;
|
||||
B.extend_u0(16, mul_signed);
|
||||
B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
log_assert(GetSize(B) == 16);
|
||||
|
||||
SigSpec CD;
|
||||
if (st.muxA)
|
||||
CD = st.muxA->getPort("\\B");
|
||||
if (st.muxB)
|
||||
CD = st.muxB->getPort("\\A");
|
||||
CD.extend_u0(32, mul_signed);
|
||||
SigSpec CD = st.sigCD;
|
||||
if (CD.empty())
|
||||
CD = RTLIL::Const(0, 32);
|
||||
else
|
||||
log_assert(GetSize(CD) == 32);
|
||||
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\C", CD.extract(0, 16));
|
||||
cell->setPort("\\D", CD.extract(16, 16));
|
||||
cell->setPort(ID::A, A);
|
||||
cell->setPort(ID::B, B);
|
||||
cell->setPort(ID(C), CD.extract(16, 16));
|
||||
cell->setPort(ID(D), CD.extract(0, 16));
|
||||
|
||||
cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
|
||||
cell->setParam(ID(A_REG), st.ffA ? State::S1 : State::S0);
|
||||
cell->setParam(ID(B_REG), st.ffB ? State::S1 : State::S0);
|
||||
cell->setParam(ID(C_REG), st.ffCD ? State::S1 : State::S0);
|
||||
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
|
||||
|
||||
cell->setPort("\\AHOLD", State::S0);
|
||||
cell->setPort("\\BHOLD", State::S0);
|
||||
cell->setPort("\\CHOLD", State::S0);
|
||||
cell->setPort("\\DHOLD", State::S0);
|
||||
SigSpec AHOLD, BHOLD, CDHOLD;
|
||||
if (st.ffAholdmux)
|
||||
AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S)));
|
||||
else
|
||||
AHOLD = State::S0;
|
||||
if (st.ffBholdmux)
|
||||
BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID(S)));
|
||||
else
|
||||
BHOLD = State::S0;
|
||||
if (st.ffCDholdmux)
|
||||
CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID(S)));
|
||||
else
|
||||
CDHOLD = State::S0;
|
||||
cell->setPort(ID(AHOLD), AHOLD);
|
||||
cell->setPort(ID(BHOLD), BHOLD);
|
||||
cell->setPort(ID(CHOLD), CDHOLD);
|
||||
cell->setPort(ID(DHOLD), CDHOLD);
|
||||
|
||||
cell->setPort("\\IRSTTOP", State::S0);
|
||||
cell->setPort("\\IRSTBOT", State::S0);
|
||||
SigSpec IRSTTOP, IRSTBOT;
|
||||
if (st.ffArstmux)
|
||||
IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID(S)));
|
||||
else
|
||||
IRSTTOP = State::S0;
|
||||
if (st.ffBrstmux)
|
||||
IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID(S)));
|
||||
else
|
||||
IRSTBOT = State::S0;
|
||||
cell->setPort(ID(IRSTTOP), IRSTTOP);
|
||||
cell->setPort(ID(IRSTBOT), IRSTBOT);
|
||||
|
||||
if (st.clock_vld)
|
||||
if (st.clock != SigBit())
|
||||
{
|
||||
cell->setPort("\\CLK", st.clock);
|
||||
cell->setPort("\\CE", State::S1);
|
||||
cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
|
||||
cell->setPort(ID(CLK), st.clock);
|
||||
cell->setPort(ID(CE), State::S1);
|
||||
cell->setParam(ID(NEG_TRIGGER), st.clock_pol ? State::S0 : State::S1);
|
||||
|
||||
log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
|
||||
|
||||
|
@ -114,91 +140,137 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
|
|||
if (st.ffB)
|
||||
log(" ffB:%s", log_id(st.ffB));
|
||||
|
||||
if (st.ffY)
|
||||
log(" ffY:%s", log_id(st.ffY));
|
||||
if (st.ffCD)
|
||||
log(" ffCD:%s", log_id(st.ffCD));
|
||||
|
||||
if (st.ffS)
|
||||
log(" ffS:%s", log_id(st.ffS));
|
||||
if (st.ffFJKG)
|
||||
log(" ffFJKG:%s", log_id(st.ffFJKG));
|
||||
|
||||
if (st.ffH)
|
||||
log(" ffH:%s", log_id(st.ffH));
|
||||
|
||||
if (st.ffO)
|
||||
log(" ffO:%s", log_id(st.ffO));
|
||||
|
||||
log("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
cell->setPort("\\CLK", State::S0);
|
||||
cell->setPort("\\CE", State::S0);
|
||||
cell->setParam("\\NEG_TRIGGER", State::S0);
|
||||
cell->setPort(ID(CLK), State::S0);
|
||||
cell->setPort(ID(CE), State::S0);
|
||||
cell->setParam(ID(NEG_TRIGGER), State::S0);
|
||||
}
|
||||
|
||||
// SB_MAC16 Cascade Interface
|
||||
|
||||
cell->setPort("\\SIGNEXTIN", State::Sx);
|
||||
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
|
||||
cell->setPort(ID(SIGNEXTIN), State::Sx);
|
||||
cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort("\\CI", State::Sx);
|
||||
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
|
||||
cell->setPort(ID(CI), State::Sx);
|
||||
|
||||
cell->setPort("\\ACCUMCI", State::Sx);
|
||||
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
|
||||
cell->setPort(ID(ACCUMCI), State::Sx);
|
||||
cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID));
|
||||
|
||||
// SB_MAC16 Output Interface
|
||||
|
||||
SigSpec O = st.ffS ? st.sigS : st.sigY;
|
||||
SigSpec O = st.sigO;
|
||||
int O_width = GetSize(O);
|
||||
if (O_width == 33) {
|
||||
log_assert(st.add);
|
||||
// If we have a signed multiply-add, then perform sign extension
|
||||
if (st.add->getParam(ID(A_SIGNED)).as_bool() && st.add->getParam(ID(B_SIGNED)).as_bool())
|
||||
pm.module->connect(O[32], O[31]);
|
||||
else
|
||||
cell->setPort(ID(CO), O[32]);
|
||||
O.remove(O_width-1);
|
||||
}
|
||||
else
|
||||
cell->setPort(ID(CO), pm.module->addWire(NEW_ID));
|
||||
log_assert(GetSize(O) <= 32);
|
||||
if (GetSize(O) < 32)
|
||||
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
|
||||
|
||||
cell->setPort("\\O", O);
|
||||
cell->setPort(ID(O), O);
|
||||
|
||||
if (st.addAB) {
|
||||
log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
|
||||
cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
|
||||
bool accum = false;
|
||||
if (st.add) {
|
||||
accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO);
|
||||
if (accum)
|
||||
log(" accumulator %s (%s)\n", log_id(st.add), log_id(st.add->type));
|
||||
else
|
||||
log(" adder %s (%s)\n", log_id(st.add), log_id(st.add->type));
|
||||
cell->setPort(ID(ADDSUBTOP), st.add->type == ID($add) ? State::S0 : State::S1);
|
||||
cell->setPort(ID(ADDSUBBOT), st.add->type == ID($add) ? State::S0 : State::S1);
|
||||
} else {
|
||||
cell->setPort("\\ADDSUBTOP", State::S0);
|
||||
cell->setPort("\\ADDSUBBOT", State::S0);
|
||||
cell->setPort(ID(ADDSUBTOP), State::S0);
|
||||
cell->setPort(ID(ADDSUBBOT), State::S0);
|
||||
}
|
||||
|
||||
cell->setPort("\\ORSTTOP", State::S0);
|
||||
cell->setPort("\\ORSTBOT", State::S0);
|
||||
SigSpec OHOLD;
|
||||
if (st.ffOholdmux)
|
||||
OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID(S)));
|
||||
else
|
||||
OHOLD = State::S0;
|
||||
cell->setPort(ID(OHOLDTOP), OHOLD);
|
||||
cell->setPort(ID(OHOLDBOT), OHOLD);
|
||||
|
||||
cell->setPort("\\OHOLDTOP", State::S0);
|
||||
cell->setPort("\\OHOLDBOT", State::S0);
|
||||
SigSpec ORST;
|
||||
if (st.ffOrstmux)
|
||||
ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID(S)));
|
||||
else
|
||||
ORST = State::S0;
|
||||
cell->setPort(ID(ORSTTOP), ORST);
|
||||
cell->setPort(ID(ORSTBOT), ORST);
|
||||
|
||||
SigSpec acc_reset = State::S0;
|
||||
if (st.muxA)
|
||||
acc_reset = st.muxA->getPort("\\S");
|
||||
if (st.muxB)
|
||||
acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
|
||||
|
||||
cell->setPort("\\OLOADTOP", acc_reset);
|
||||
cell->setPort("\\OLOADBOT", acc_reset);
|
||||
if (st.mux) {
|
||||
if (st.muxAB == ID::A)
|
||||
acc_reset = st.mux->getPort(ID(S));
|
||||
else
|
||||
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S)));
|
||||
}
|
||||
cell->setPort(ID(OLOADTOP), acc_reset);
|
||||
cell->setPort(ID(OLOADBOT), acc_reset);
|
||||
|
||||
// SB_MAC16 Remaining Parameters
|
||||
|
||||
cell->setParam("\\C_REG", State::S0);
|
||||
cell->setParam("\\D_REG", State::S0);
|
||||
cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
|
||||
cell->setParam(ID(BOT_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
|
||||
cell->setParam(ID(PIPELINE_16x16_MULT_REG1), st.ffFJKG ? State::S1 : State::S0);
|
||||
cell->setParam(ID(PIPELINE_16x16_MULT_REG2), st.ffH ? State::S1 : State::S0);
|
||||
|
||||
cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
|
||||
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
|
||||
cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2));
|
||||
cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
|
||||
cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2));
|
||||
|
||||
cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
|
||||
cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2));
|
||||
cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
|
||||
cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
|
||||
|
||||
cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
|
||||
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
|
||||
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
|
||||
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
|
||||
cell->setParam(ID(MODE_8x8), State::S0);
|
||||
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool());
|
||||
cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
|
||||
cell->setParam("\\MODE_8x8", State::S0);
|
||||
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
|
||||
if (st.ffO) {
|
||||
if (st.o_lo)
|
||||
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||
else
|
||||
cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2));
|
||||
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.ffY);
|
||||
pm.autoremove(st.ffS);
|
||||
st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O)));
|
||||
cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2));
|
||||
}
|
||||
else {
|
||||
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||
cell->setParam(ID(BOTOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
|
||||
}
|
||||
|
||||
if (cell != st.mul)
|
||||
pm.autoremove(st.mul);
|
||||
else
|
||||
pm.blacklist(st.mul);
|
||||
pm.autoremove(st.ffFJKG);
|
||||
pm.autoremove(st.add);
|
||||
}
|
||||
|
||||
struct Ice40DspPass : public Pass {
|
||||
|
@ -209,7 +281,17 @@ struct Ice40DspPass : public Pass {
|
|||
log("\n");
|
||||
log(" ice40_dsp [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
|
||||
log("Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n");
|
||||
log("cells into iCE40 DSP resources.\n");
|
||||
log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n");
|
||||
log("\n");
|
||||
log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n");
|
||||
log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n");
|
||||
log("optional hold), and post-adder into into the SB_MAC16 resource.\n");
|
||||
log("\n");
|
||||
log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n");
|
||||
log("input will be folded into the DSP. In this scenario only, resetting the\n");
|
||||
log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
|
|
@ -1,163 +1,574 @@
|
|||
pattern ice40_dsp
|
||||
|
||||
state <SigBit> clock
|
||||
state <bool> clock_pol clock_vld
|
||||
state <SigSpec> sigA sigB sigY sigS
|
||||
state <Cell*> addAB muxAB
|
||||
state <bool> clock_pol cd_signed o_lo
|
||||
state <SigSpec> sigA sigB sigCD sigH sigO
|
||||
state <Cell*> add mux
|
||||
state <IdString> addAB muxAB
|
||||
|
||||
state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
|
||||
state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
|
||||
|
||||
state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
|
||||
state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
|
||||
|
||||
// subpattern
|
||||
state <SigSpec> argQ argD
|
||||
state <bool> ffholdpol ffrstpol
|
||||
state <int> ffoffset
|
||||
udata <SigSpec> dffD dffQ
|
||||
udata <SigBit> dffclock
|
||||
udata <Cell*> dff dffholdmux dffrstmux
|
||||
udata <bool> dffholdpol dffrstpol dffclock_pol
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
select mul->type.in($mul, \SB_MAC16)
|
||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||
select GetSize(mul->getPort(\Y)) > 10
|
||||
endmatch
|
||||
|
||||
match ffA
|
||||
select ffA->type.in($dff)
|
||||
// select nusers(port(ffA, \Q)) == 2
|
||||
index <SigSpec> port(ffA, \Q) === port(mul, \A)
|
||||
optional
|
||||
endmatch
|
||||
code sigA sigB sigH
|
||||
auto unextend = [](const SigSpec &sig) {
|
||||
int i;
|
||||
for (i = GetSize(sig)-1; i > 0; i--)
|
||||
if (sig[i] != sig[i-1])
|
||||
break;
|
||||
// Do not remove non-const sign bit
|
||||
if (sig[i].wire)
|
||||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
sigA = unextend(port(mul, \A));
|
||||
sigB = unextend(port(mul, \B));
|
||||
|
||||
code sigA clock clock_pol clock_vld
|
||||
sigA = port(mul, \A);
|
||||
SigSpec O;
|
||||
if (mul->type == $mul)
|
||||
O = mul->getPort(\Y);
|
||||
else if (mul->type == \SB_MAC16)
|
||||
O = mul->getPort(\O);
|
||||
else log_abort();
|
||||
if (GetSize(O) <= 10)
|
||||
reject;
|
||||
|
||||
if (ffA) {
|
||||
sigA = port(ffA, \D);
|
||||
// Only care about those bits that are used
|
||||
int i;
|
||||
for (i = 0; i < GetSize(O); i++) {
|
||||
if (nusers(O[i]) <= 1)
|
||||
break;
|
||||
sigH.append(O[i]);
|
||||
}
|
||||
log_assert(nusers(O.extract_end(i)) <= 1);
|
||||
endcode
|
||||
|
||||
clock = port(ffA, \CLK).as_bit();
|
||||
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
|
||||
clock_vld = true;
|
||||
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
|
||||
argQ = sigA;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
if (dffrstmux) {
|
||||
ffArstmux = dffrstmux;
|
||||
ffArstpol = dffrstpol;
|
||||
}
|
||||
if (dffholdmux) {
|
||||
ffAholdmux = dffholdmux;
|
||||
ffAholdpol = dffholdpol;
|
||||
}
|
||||
sigA = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffB
|
||||
select ffB->type.in($dff)
|
||||
// select nusers(port(ffB, \Q)) == 2
|
||||
index <SigSpec> port(ffB, \Q) === port(mul, \B)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigB clock clock_pol clock_vld
|
||||
sigB = port(mul, \B);
|
||||
|
||||
if (ffB) {
|
||||
sigB = port(ffB, \D);
|
||||
SigBit c = port(ffB, \CLK).as_bit();
|
||||
bool cp = param(ffB, \CLK_POLARITY).as_bool();
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
|
||||
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
|
||||
argQ = sigB;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
if (dffrstmux) {
|
||||
ffBrstmux = dffrstmux;
|
||||
ffBrstpol = dffrstpol;
|
||||
}
|
||||
if (dffholdmux) {
|
||||
ffBholdmux = dffholdmux;
|
||||
ffBholdpol = dffholdpol;
|
||||
}
|
||||
sigB = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffY
|
||||
select ffY->type.in($dff)
|
||||
select nusers(port(ffY, \D)) == 2
|
||||
index <SigSpec> port(ffY, \D) === port(mul, \Y)
|
||||
optional
|
||||
endmatch
|
||||
code argD ffFJKG sigH clock clock_pol
|
||||
if (nusers(sigH) == 2 &&
|
||||
(mul->type != \SB_MAC16 ||
|
||||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
|
||||
argD = sigH;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
// F/J/K/G do not have a CE-like (hold) input
|
||||
if (dffholdmux)
|
||||
goto reject_ffFJKG;
|
||||
|
||||
code sigY clock clock_pol clock_vld
|
||||
sigY = port(mul, \Y);
|
||||
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
|
||||
// shared with A and B
|
||||
if ((ffArstmux != NULL) != (dffrstmux != NULL))
|
||||
goto reject_ffFJKG;
|
||||
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||
goto reject_ffFJKG;
|
||||
if (ffArstmux) {
|
||||
if (port(ffArstmux, \S) != port(dffrstmux, \S))
|
||||
goto reject_ffFJKG;
|
||||
if (ffArstpol != dffrstpol)
|
||||
goto reject_ffFJKG;
|
||||
}
|
||||
if (ffBrstmux) {
|
||||
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||
goto reject_ffFJKG;
|
||||
if (ffBrstpol != dffrstpol)
|
||||
goto reject_ffFJKG;
|
||||
}
|
||||
|
||||
if (ffY) {
|
||||
sigY = port(ffY, \Q);
|
||||
SigBit c = port(ffY, \CLK).as_bit();
|
||||
bool cp = param(ffY, \CLK_POLARITY).as_bool();
|
||||
ffFJKG = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
sigH = dffQ;
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
reject_ffFJKG: ;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
match addA
|
||||
select addA->type.in($add)
|
||||
select nusers(port(addA, \A)) == 2
|
||||
index <SigSpec> port(addA, \A) === sigY
|
||||
code argD ffH sigH sigO clock clock_pol
|
||||
if (ffFJKG && nusers(sigH) == 2 &&
|
||||
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
|
||||
argD = sigH;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
// H does not have a CE-like (hold) input
|
||||
if (dffholdmux)
|
||||
goto reject_ffH;
|
||||
|
||||
// Reset signal of H (IRSTBOT) shared with B
|
||||
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||
goto reject_ffH;
|
||||
if (ffBrstmux) {
|
||||
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||
goto reject_ffH;
|
||||
if (ffBrstpol != dffrstpol)
|
||||
goto reject_ffH;
|
||||
}
|
||||
|
||||
ffH = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
sigH = dffQ;
|
||||
|
||||
reject_ffH: ;
|
||||
}
|
||||
}
|
||||
|
||||
sigO = sigH;
|
||||
endcode
|
||||
|
||||
match add
|
||||
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
|
||||
|
||||
select add->type.in($add)
|
||||
choice <IdString> AB {\A, \B}
|
||||
select nusers(port(add, AB)) == 2
|
||||
|
||||
index <SigBit> port(add, AB)[0] === sigH[0]
|
||||
filter GetSize(port(add, AB)) <= GetSize(sigH)
|
||||
filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
|
||||
filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
|
||||
set addAB AB
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match addB
|
||||
if !addA
|
||||
select addB->type.in($add, $sub)
|
||||
select nusers(port(addB, \B)) == 2
|
||||
index <SigSpec> port(addB, \B) === sigY
|
||||
optional
|
||||
endmatch
|
||||
code sigCD sigO cd_signed
|
||||
if (add) {
|
||||
sigCD = port(add, addAB == \A ? \B : \A);
|
||||
cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
|
||||
|
||||
code addAB sigS
|
||||
if (addA) {
|
||||
addAB = addA;
|
||||
sigS = port(addA, \B);
|
||||
}
|
||||
if (addB) {
|
||||
addAB = addB;
|
||||
sigS = port(addB, \A);
|
||||
}
|
||||
if (addAB) {
|
||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||
int actual_mul_width = GetSize(sigY);
|
||||
int actual_acc_width = GetSize(sigS);
|
||||
int actual_mul_width = GetSize(sigH);
|
||||
int actual_acc_width = GetSize(sigCD);
|
||||
|
||||
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
|
||||
reject;
|
||||
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
|
||||
// If accumulator, check adder width and signedness
|
||||
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
|
||||
reject;
|
||||
|
||||
sigO = port(add, \Y);
|
||||
}
|
||||
endcode
|
||||
|
||||
match muxA
|
||||
if addAB
|
||||
select muxA->type.in($mux)
|
||||
select nusers(port(muxA, \A)) == 2
|
||||
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
|
||||
match mux
|
||||
select mux->type == $mux
|
||||
choice <IdString> AB {\A, \B}
|
||||
select nusers(port(mux, AB)) == 2
|
||||
index <SigSpec> port(mux, AB) === sigO
|
||||
set muxAB AB
|
||||
optional
|
||||
endmatch
|
||||
|
||||
match muxB
|
||||
if addAB
|
||||
if !muxA
|
||||
select muxB->type.in($mux)
|
||||
select nusers(port(muxB, \B)) == 2
|
||||
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code muxAB
|
||||
muxAB = addAB;
|
||||
if (muxA)
|
||||
muxAB = muxA;
|
||||
if (muxB)
|
||||
muxAB = muxB;
|
||||
code sigO
|
||||
if (mux)
|
||||
sigO = port(mux, \Y);
|
||||
endcode
|
||||
|
||||
match ffS
|
||||
if muxAB
|
||||
select ffS->type.in($dff)
|
||||
select nusers(port(ffS, \D)) == 2
|
||||
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
|
||||
index <SigSpec> port(ffS, \Q) === sigS
|
||||
endmatch
|
||||
code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
|
||||
if (mul->type != \SB_MAC16 ||
|
||||
// Ensure that register is not already used
|
||||
((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) &&
|
||||
// Ensure that OLOADTOP/OLOADBOT is unused or zero
|
||||
(port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
|
||||
|
||||
code clock clock_pol clock_vld
|
||||
if (ffS) {
|
||||
SigBit c = port(ffS, \CLK).as_bit();
|
||||
bool cp = param(ffS, \CLK_POLARITY).as_bool();
|
||||
dff = nullptr;
|
||||
|
||||
if (clock_vld && (c != clock || cp != clock_pol))
|
||||
reject;
|
||||
// First try entire sigO
|
||||
if (nusers(sigO) == 2) {
|
||||
argD = sigO;
|
||||
subpattern(out_dffe);
|
||||
}
|
||||
|
||||
clock = c;
|
||||
clock_pol = cp;
|
||||
clock_vld = true;
|
||||
// Otherwise try just its least significant 16 bits
|
||||
if (!dff && GetSize(sigO) > 16) {
|
||||
argD = sigO.extract(0, 16);
|
||||
if (nusers(argD) == 2) {
|
||||
subpattern(out_dffe);
|
||||
o_lo = dff;
|
||||
}
|
||||
}
|
||||
|
||||
if (dff) {
|
||||
ffO = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
if (dffrstmux) {
|
||||
ffOrstmux = dffrstmux;
|
||||
ffOrstpol = dffrstpol;
|
||||
}
|
||||
if (dffholdmux) {
|
||||
ffOholdmux = dffholdmux;
|
||||
ffOholdpol = dffholdpol;
|
||||
}
|
||||
|
||||
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
|
||||
}
|
||||
|
||||
// Loading value into output register is not
|
||||
// supported unless using accumulator
|
||||
if (mux) {
|
||||
if (sigCD != sigO)
|
||||
reject;
|
||||
sigCD = port(mux, muxAB == \B ? \A : \B);
|
||||
|
||||
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
|
||||
if (!sigCD.empty() && sigCD != sigO &&
|
||||
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
|
||||
argQ = sigCD;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (dffholdmux) {
|
||||
ffCDholdmux = dffholdmux;
|
||||
ffCDholdpol = dffholdpol;
|
||||
}
|
||||
|
||||
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
|
||||
// shared with A and B
|
||||
if ((ffArstmux != NULL) != (dffrstmux != NULL))
|
||||
goto reject_ffCD;
|
||||
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
|
||||
goto reject_ffCD;
|
||||
if (ffArstmux) {
|
||||
if (port(ffArstmux, \S) != port(dffrstmux, \S))
|
||||
goto reject_ffCD;
|
||||
if (ffArstpol != dffrstpol)
|
||||
goto reject_ffCD;
|
||||
}
|
||||
if (ffBrstmux) {
|
||||
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
|
||||
goto reject_ffCD;
|
||||
if (ffBrstpol != dffrstpol)
|
||||
goto reject_ffCD;
|
||||
}
|
||||
|
||||
ffCD = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
sigCD = dffD;
|
||||
|
||||
reject_ffCD: ;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
code sigCD
|
||||
sigCD.extend_u0(32, cd_signed);
|
||||
endcode
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
subpattern in_dffe
|
||||
arg argD argQ clock clock_pol
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argQ.chunks()) {
|
||||
if (!c.wire)
|
||||
reject;
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
Const init = c.wire->attributes.at(\init, State::Sx);
|
||||
if (!init.is_fully_undef() && !init.is_fully_zero())
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff)
|
||||
// DSP48E1 does not support clock inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||
|
||||
// Check that the rest of argQ is present
|
||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||
|
||||
set ffoffset offset
|
||||
endmatch
|
||||
|
||||
code argQ argD
|
||||
{
|
||||
if (clock != SigBit()) {
|
||||
if (port(ff, \CLK) != clock)
|
||||
reject;
|
||||
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||
reject;
|
||||
}
|
||||
|
||||
SigSpec Q = port(ff, \Q);
|
||||
dff = ff;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
|
||||
dffD = argQ;
|
||||
argD = port(ff, \D);
|
||||
argQ = Q;
|
||||
dffD.replace(argQ, argD);
|
||||
// Only search for ffrstmux if dffD only
|
||||
// has two (ff, ffrstmux) users
|
||||
if (nusers(dffD) > 2)
|
||||
argD = SigSpec();
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffrstmux
|
||||
if false /* TODO: ice40 resets are actually async */
|
||||
|
||||
if !argD.empty()
|
||||
select ffrstmux->type.in($mux)
|
||||
index <SigSpec> port(ffrstmux, \Y) === argD
|
||||
|
||||
choice <IdString> BA {\B, \A}
|
||||
// DSP48E1 only supports reset to zero
|
||||
select port(ffrstmux, BA).is_fully_zero()
|
||||
|
||||
define <bool> pol (BA == \B)
|
||||
set ffrstpol pol
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
if (ffrstmux) {
|
||||
dffrstmux = ffrstmux;
|
||||
dffrstpol = ffrstpol;
|
||||
argD = port(ffrstmux, ffrstpol ? \A : \B);
|
||||
dffD.replace(port(ffrstmux, \Y), argD);
|
||||
|
||||
// Only search for ffholdmux if argQ has at
|
||||
// least 3 users (ff, <upstream>, ffrstmux) and
|
||||
// dffD only has two (ff, ffrstmux)
|
||||
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
|
||||
argD = SigSpec();
|
||||
}
|
||||
else
|
||||
dffrstmux = nullptr;
|
||||
endcode
|
||||
|
||||
match ffholdmux
|
||||
if !argD.empty()
|
||||
select ffholdmux->type.in($mux)
|
||||
index <SigSpec> port(ffholdmux, \Y) === argD
|
||||
choice <IdString> BA {\B, \A}
|
||||
index <SigSpec> port(ffholdmux, BA) === argQ
|
||||
define <bool> pol (BA == \B)
|
||||
set ffholdpol pol
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
if (ffholdmux) {
|
||||
dffholdmux = ffholdmux;
|
||||
dffholdpol = ffholdpol;
|
||||
argD = port(ffholdmux, ffholdpol ? \A : \B);
|
||||
dffD.replace(port(ffholdmux, \Y), argD);
|
||||
}
|
||||
else
|
||||
dffholdmux = nullptr;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
subpattern out_dffe
|
||||
arg argD argQ clock clock_pol
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argD.chunks())
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
endcode
|
||||
|
||||
match ffholdmux
|
||||
select ffholdmux->type.in($mux)
|
||||
// ffholdmux output must have two users: ffholdmux and ff.D
|
||||
select nusers(port(ffholdmux, \Y)) == 2
|
||||
|
||||
choice <IdString> BA {\B, \A}
|
||||
// keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
|
||||
select nusers(port(ffholdmux, BA)) >= 3
|
||||
|
||||
slice offset GetSize(port(ffholdmux, \Y))
|
||||
define <IdString> AB (BA == \B ? \A : \B)
|
||||
index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
|
||||
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
|
||||
filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
|
||||
|
||||
set ffoffset offset
|
||||
define <bool> pol (BA == \B)
|
||||
set ffholdpol pol
|
||||
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD argQ
|
||||
dffholdmux = ffholdmux;
|
||||
if (ffholdmux) {
|
||||
SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
|
||||
SigSpec Y = port(ffholdmux, \Y);
|
||||
argQ = argD;
|
||||
argD.replace(AB, Y);
|
||||
argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
|
||||
|
||||
dffholdmux = ffholdmux;
|
||||
dffholdpol = ffholdpol;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ffrstmux
|
||||
if false /* TODO: ice40 resets are actually async */
|
||||
|
||||
select ffrstmux->type.in($mux)
|
||||
// ffrstmux output must have two users: ffrstmux and ff.D
|
||||
select nusers(port(ffrstmux, \Y)) == 2
|
||||
|
||||
choice <IdString> BA {\B, \A}
|
||||
// DSP48E1 only supports reset to zero
|
||||
select port(ffrstmux, BA).is_fully_zero()
|
||||
|
||||
slice offset GetSize(port(ffrstmux, \Y))
|
||||
define <IdString> AB (BA == \B ? \A : \B)
|
||||
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
|
||||
|
||||
// Check that offset is consistent
|
||||
filter !ffholdmux || ffoffset == offset
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
|
||||
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
|
||||
|
||||
set ffoffset offset
|
||||
define <bool> pol (AB == \A)
|
||||
set ffrstpol pol
|
||||
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD argQ
|
||||
dffrstmux = ffrstmux;
|
||||
if (ffrstmux) {
|
||||
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
|
||||
SigSpec Y = port(ffrstmux, \Y);
|
||||
argD.replace(AB, Y);
|
||||
|
||||
dffrstmux = ffrstmux;
|
||||
dffrstpol = ffrstpol;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff)
|
||||
// DSP48E1 does not support clock inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \D)[offset] === argD[0]
|
||||
|
||||
// Check that offset is consistent
|
||||
filter (!ffholdmux && !ffrstmux) || ffoffset == offset
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||
// Check that FF.Q is connected to CE-mux
|
||||
filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||
|
||||
set ffoffset offset
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
if (ff) {
|
||||
if (clock != SigBit()) {
|
||||
if (port(ff, \CLK) != clock)
|
||||
reject;
|
||||
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||
reject;
|
||||
}
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
if (!ffholdmux) {
|
||||
argQ = argD;
|
||||
argQ.replace(D, Q);
|
||||
}
|
||||
|
||||
for (auto c : argQ.chunks()) {
|
||||
Const init = c.wire->attributes.at(\init, State::Sx);
|
||||
if (!init.is_fully_undef() && !init.is_fully_zero())
|
||||
reject;
|
||||
}
|
||||
|
||||
dff = ff;
|
||||
dffQ = argQ;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
|
||||
}
|
||||
// No enable/reset mux possible without flop
|
||||
else if (dffholdmux || dffrstmux)
|
||||
reject;
|
||||
endcode
|
||||
|
|
|
@ -60,6 +60,7 @@ struct PeepoptPass : public Pass {
|
|||
peepopt_pm pm(module, module->selected_cells());
|
||||
pm.run_shiftmul();
|
||||
pm.run_muldiv();
|
||||
pm.run_dffmux();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
pattern dffmux
|
||||
|
||||
state <IdString> cemuxAB rstmuxBA
|
||||
state <SigSpec> sigD
|
||||
|
||||
match dff
|
||||
select dff->type == $dff
|
||||
select GetSize(port(dff, \D)) > 1
|
||||
endmatch
|
||||
|
||||
match rstmux
|
||||
select rstmux->type == $mux
|
||||
select GetSize(port(rstmux, \Y)) > 1
|
||||
index <SigSpec> port(rstmux, \Y) === port(dff, \D)
|
||||
choice <IdString> BA {\B, \A}
|
||||
select port(rstmux, BA).is_fully_const()
|
||||
set rstmuxBA BA
|
||||
optional
|
||||
endmatch
|
||||
|
||||
code sigD
|
||||
if (rstmux)
|
||||
sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
|
||||
else
|
||||
sigD = port(dff, \D);
|
||||
endcode
|
||||
|
||||
match cemux
|
||||
select cemux->type == $mux
|
||||
select GetSize(port(cemux, \Y)) > 1
|
||||
index <SigSpec> port(cemux, \Y) === sigD
|
||||
choice <IdString> AB {\A, \B}
|
||||
index <SigSpec> port(cemux, AB) === port(dff, \Q)
|
||||
set cemuxAB AB
|
||||
endmatch
|
||||
|
||||
code
|
||||
SigSpec D = port(cemux, cemuxAB == \A ? \B : \A);
|
||||
SigSpec Q = port(dff, \Q);
|
||||
Const rst;
|
||||
if (rstmux)
|
||||
rst = port(rstmux, rstmuxBA).as_const();
|
||||
int width = GetSize(D);
|
||||
|
||||
SigSpec &ceA = cemux->connections_.at(\A);
|
||||
SigSpec &ceB = cemux->connections_.at(\B);
|
||||
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]) {
|
||||
did_something = true;
|
||||
|
||||
SigBit sign = D[width-1];
|
||||
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 {
|
||||
module->connect(Q[i], Q[i-1]);
|
||||
if (D[i-2] != sign || (rst.size() && rst[i-1] != rst[width-1]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ceA.remove(i, width-i);
|
||||
ceB.remove(i, width-i);
|
||||
ceY.remove(i, width-i);
|
||||
cemux->fixup_parameters();
|
||||
dffD.remove(i, width-i);
|
||||
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 {
|
||||
int count = 0;
|
||||
for (int i = width-1; i >= 0; i--) {
|
||||
if (D[i].wire)
|
||||
continue;
|
||||
Wire *w = Q[i].wire;
|
||||
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++;
|
||||
module->connect(Q[i], D[i]);
|
||||
ceA.remove(i);
|
||||
ceB.remove(i);
|
||||
ceY.remove(i);
|
||||
dffD.remove(i);
|
||||
dffQ.remove(i);
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
did_something = true;
|
||||
cemux->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);
|
||||
}
|
||||
|
||||
accept;
|
||||
}
|
||||
endcode
|
|
@ -286,7 +286,7 @@ def process_pmgfile(f, filename):
|
|||
block["gencode"].append(rewrite_cpp(l.rstrip()))
|
||||
break
|
||||
|
||||
assert False
|
||||
raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr))
|
||||
|
||||
if block["optional"]:
|
||||
assert not block["semioptional"]
|
||||
|
@ -305,7 +305,8 @@ def process_pmgfile(f, filename):
|
|||
block["states"] = set()
|
||||
|
||||
for s in line.split()[1:]:
|
||||
assert s in state_types[current_pattern]
|
||||
if s not in state_types[current_pattern]:
|
||||
raise RuntimeError("'%s' not in state_types" % s)
|
||||
block["states"].add(s)
|
||||
|
||||
codetype = "code"
|
||||
|
@ -327,7 +328,7 @@ def process_pmgfile(f, filename):
|
|||
blocks.append(block)
|
||||
continue
|
||||
|
||||
assert False
|
||||
raise RuntimeError("'%s' command not recognised" % cmd)
|
||||
|
||||
for fn in pmgfiles:
|
||||
with open(fn, "r") as f:
|
||||
|
@ -452,11 +453,19 @@ with open(outfile, "w") as f:
|
|||
print(" return sigmap(cell->getPort(portname));", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
print(" SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f)
|
||||
print(" return sigmap(cell->connections_.at(portname, defval));", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" Const param(Cell *cell, IdString paramname) {", file=f)
|
||||
print(" return cell->getParam(paramname);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
print(" Const param(Cell *cell, IdString paramname, const Const& defval) {", file=f)
|
||||
print(" return cell->parameters.at(paramname, defval);", file=f)
|
||||
print(" }", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(" int nusers(const SigSpec &sig) {", file=f)
|
||||
print(" pool<Cell*> users;", file=f)
|
||||
|
|
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
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()) {
|
||||
xilinx_simd_pack(module, module->selected_cells());
|
||||
|
||||
{
|
||||
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 CREG into a
|
||||
// downstream DSP that should have otherwise been a
|
||||
// PREG of an upstream DSP that had not been pattern
|
||||
// matched yet
|
||||
{
|
||||
xilinx_dsp_CREG_pm pm(module, module->selected_cells());
|
||||
pm.run_xilinx_dsp_packC(xilinx_dsp_packC);
|
||||
}
|
||||
{
|
||||
xilinx_dsp_cascade_pm pm(module, module->selected_cells());
|
||||
pm.run_xilinx_dsp_cascade();
|
||||
}
|
||||
}
|
||||
}
|
||||
} XilinxDspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,587 @@
|
|||
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
|
||||
|
||||
// subpattern
|
||||
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
|
||||
|
||||
match dsp
|
||||
select dsp->type.in(\DSP48E1)
|
||||
endmatch
|
||||
|
||||
code sigA sigB sigC sigD sigM clock
|
||||
auto unextend = [](const SigSpec &sig) {
|
||||
int i;
|
||||
for (i = GetSize(sig)-1; i > 0; i--)
|
||||
if (sig[i] != sig[i-1])
|
||||
break;
|
||||
// Do not remove non-const sign bit
|
||||
if (sig[i].wire)
|
||||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
sigA = unextend(port(dsp, \A));
|
||||
sigB = unextend(port(dsp, \B));
|
||||
|
||||
sigC = port(dsp, \C, SigSpec());
|
||||
sigD = port(dsp, \D, SigSpec());
|
||||
|
||||
SigSpec P = port(dsp, \P);
|
||||
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||
// Only care about those bits that are used
|
||||
int i;
|
||||
for (i = 0; i < GetSize(P); i++) {
|
||||
if (nusers(P[i]) <= 1)
|
||||
break;
|
||||
sigM.append(P[i]);
|
||||
}
|
||||
log_assert(nusers(P.extract_end(i)) <= 1);
|
||||
}
|
||||
else
|
||||
sigM = P;
|
||||
// This sigM could have no users if downstream $add
|
||||
// is narrower than $mul result, for example
|
||||
if (sigM.empty())
|
||||
reject;
|
||||
|
||||
clock = port(dsp, \CLK, SigBit());
|
||||
endcode
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
if (GetSize(sigA) < GetSize(sigD))
|
||||
std::swap(sigA, sigD);
|
||||
}
|
||||
endcode
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 in_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
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() && port(ff, \CLK) != clock)
|
||||
reject;
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 out_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argD.chunks())
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
endcode
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
set ffoffset offset
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
if (ff) {
|
||||
if (clock != SigBit() && port(ff, \CLK) != clock)
|
||||
reject;
|
||||
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
if (!ffcemux) {
|
||||
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);
|
||||
}
|
||||
// No enable/reset mux possible without flop
|
||||
else if (dffcemux || dffrstmux)
|
||||
reject;
|
||||
endcode
|
|
@ -0,0 +1,181 @@
|
|||
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
|
||||
|
||||
// subpattern
|
||||
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
|
||||
|
||||
match dsp
|
||||
select dsp->type.in(\DSP48E1)
|
||||
select param(dsp, \CREG, 1).as_int() == 0
|
||||
select nusers(port(dsp, \C, SigSpec())) > 1
|
||||
endmatch
|
||||
|
||||
code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC sigP clock
|
||||
unextend = [](const SigSpec &sig) {
|
||||
int i;
|
||||
for (i = GetSize(sig)-1; i > 0; i--)
|
||||
if (sig[i] != sig[i-1])
|
||||
break;
|
||||
// Do not remove non-const sign bit
|
||||
if (sig[i].wire)
|
||||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
sigC = unextend(port(dsp, \C, SigSpec()));
|
||||
|
||||
SigSpec P = port(dsp, \P);
|
||||
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
|
||||
// Only care about those bits that are used
|
||||
int i;
|
||||
for (i = 0; i < GetSize(P); i++) {
|
||||
if (nusers(P[i]) <= 1)
|
||||
break;
|
||||
sigP.append(P[i]);
|
||||
}
|
||||
log_assert(nusers(P.extract_end(i)) <= 1);
|
||||
}
|
||||
else
|
||||
sigP = P;
|
||||
|
||||
if (sigC == sigP)
|
||||
reject;
|
||||
|
||||
clock = port(dsp, \CLK, SigBit());
|
||||
|
||||
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 in_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
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() && port(ff, \CLK) != clock)
|
||||
reject;
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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,336 @@
|
|||
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
|
||||
|
||||
// subpattern
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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" &&
|
||||
port(next, \ACIN, SigSpec()).is_fully_zero() &&
|
||||
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
|
||||
argQ = unextend(port(next, \A));
|
||||
clock = port(prev, \CLK);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
|
||||
goto reject_AREG;
|
||||
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
|
||||
goto reject_AREG;
|
||||
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0)
|
||||
goto reject_AREG;
|
||||
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0))
|
||||
goto reject_AREG;
|
||||
if (dffD == unextend(port(prev, \A)))
|
||||
AREG = 1;
|
||||
reject_AREG: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
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
|
||||
|
||||
code
|
||||
if (next) {
|
||||
chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
|
||||
|
||||
SigSpec sigC = unextend(port(next, \C));
|
||||
|
||||
// TODO: Cannot use 'reject' since semioptional
|
||||
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 in_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
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() && port(ff, \CLK) != clock)
|
||||
reject;
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
match ffcemux
|
||||
if !argD.empty()
|
||||
select ffcemux->type.in($mux)
|
||||
index <SigSpec> port(ffcemux, \Y) === argD
|
||||
choice <IdString> AB {\A, \B}
|
||||
index <SigSpec> port(ffcemux, AB) === argQ
|
||||
define <bool> pol (AB == \A)
|
||||
set ffcepol pol
|
||||
semioptional
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
if (ffcemux) {
|
||||
dffcemux = ffcemux;
|
||||
dffcepol = ffcepol;
|
||||
argD = port(ffcemux, ffcepol ? \B : \A);
|
||||
dffD.replace(port(ffcemux, \Y), argD);
|
||||
}
|
||||
else
|
||||
dffcemux = nullptr;
|
||||
endcode
|
|
@ -13,9 +13,9 @@ endcode
|
|||
match first
|
||||
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
||||
select !first->has_keep_attr()
|
||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
||||
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
|
||||
filter !non_first_cells.count(first)
|
||||
generate
|
||||
SigSpec C = module->addWire(NEW_ID);
|
||||
|
@ -84,9 +84,9 @@ arg en_port
|
|||
match first
|
||||
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
|
||||
select !first->has_keep_attr()
|
||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
||||
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
|
||||
endmatch
|
||||
|
||||
code clk_port en_port
|
||||
|
@ -111,10 +111,10 @@ match next
|
|||
index <SigBit> port(next, \Q) === port(first, \D)
|
||||
filter port(next, clk_port) == port(first, clk_port)
|
||||
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
|
||||
endmatch
|
||||
|
||||
code
|
||||
|
@ -138,10 +138,10 @@ match next
|
|||
index <SigBit> port(next, \Q) === port(chain.back(), \D)
|
||||
filter port(next, clk_port) == port(first, clk_port)
|
||||
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
|
||||
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
|
||||
generate
|
||||
Cell *cell = module->addCell(NEW_ID, chain.back()->type);
|
||||
cell->setPort(\C, chain.back()->getPort(\C));
|
||||
|
@ -149,7 +149,7 @@ generate
|
|||
cell->setPort(\Q, chain.back()->getPort(\D));
|
||||
if (cell->type == \FDRE) {
|
||||
if (rng(2) == 0)
|
||||
cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
|
||||
cell->setPort(\R, port(chain.back(), \R, State::S0));
|
||||
cell->setPort(\CE, chain.back()->getPort(\CE));
|
||||
}
|
||||
else if (cell->type.begins_with("$_DFFE_"))
|
||||
|
|
|
@ -198,6 +198,7 @@ struct Async2syncPass : public Pass {
|
|||
module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", sig_q);
|
||||
cell->setPort("\\Q", new_q);
|
||||
cell->unsetPort("\\EN");
|
||||
cell->unsetParam("\\EN_POLARITY");
|
||||
|
|
|
@ -40,6 +40,7 @@ OBJS += passes/techmap/attrmap.o
|
|||
OBJS += passes/techmap/zinit.o
|
||||
OBJS += passes/techmap/dff2dffs.o
|
||||
OBJS += passes/techmap/flowmap.o
|
||||
OBJS += passes/techmap/extractinv.o
|
||||
endif
|
||||
|
||||
GENFILES += passes/techmap/techmap.inc
|
||||
|
|
|
@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name)
|
|||
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
|
||||
}
|
||||
|
||||
void handle_loops(RTLIL::Design *design,
|
||||
const dict<IdString,pool<IdString>> &scc_break_inputs)
|
||||
void handle_loops(RTLIL::Design *design)
|
||||
{
|
||||
Pass::call(design, "scc -set_attr abc_scc_id {}");
|
||||
|
||||
|
@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design,
|
|||
}
|
||||
cell->attributes.erase(it);
|
||||
}
|
||||
|
||||
auto jt = scc_break_inputs.find(cell->type);
|
||||
if (jt != scc_break_inputs.end())
|
||||
for (auto port_name : jt->second) {
|
||||
RTLIL::SigSpec sig;
|
||||
auto &rhs = cell->connections_.at(port_name);
|
||||
for (auto b : rhs) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
w->port_output = true;
|
||||
w->set_bool_attribute(ID(abc_scc_break));
|
||||
w = module->wire(stringf("%s.abci", w->name.c_str()));
|
||||
if (!w) {
|
||||
w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
|
||||
w->port_input = true;
|
||||
}
|
||||
else {
|
||||
log_assert(b.offset < GetSize(w));
|
||||
log_assert(w->port_input);
|
||||
}
|
||||
sig.append(RTLIL::SigBit(w, b.offset));
|
||||
}
|
||||
rhs = sig;
|
||||
}
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
|
@ -272,8 +247,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
|
||||
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
|
||||
bool show_tempdir, std::string box_file, std::string lut_file,
|
||||
std::string wire_delay, const dict<int,IdString> &box_lookup,
|
||||
const dict<IdString,pool<IdString>> &scc_break_inputs
|
||||
std::string wire_delay, const dict<int,IdString> &box_lookup
|
||||
)
|
||||
{
|
||||
module = current_module;
|
||||
|
@ -413,7 +387,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
RTLIL::Selection& sel = design->selection_stack.back();
|
||||
sel.select(module);
|
||||
|
||||
handle_loops(design, scc_break_inputs);
|
||||
handle_loops(design);
|
||||
|
||||
Pass::call(design, "aigmap");
|
||||
|
||||
|
@ -497,7 +471,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
|
||||
|
||||
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
|
||||
ifs.open(buffer);
|
||||
ifs.open(buffer, std::ifstream::binary);
|
||||
if (ifs.fail())
|
||||
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
|
||||
|
||||
|
@ -632,7 +606,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
existing_cell = module->cell(c->name);
|
||||
log_assert(existing_cell);
|
||||
cell = module->addCell(remap_name(c->name), c->type);
|
||||
module->swap_names(cell, existing_cell);
|
||||
}
|
||||
|
||||
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
|
||||
|
@ -668,8 +641,22 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
}
|
||||
}
|
||||
|
||||
for (auto cell : boxes)
|
||||
module->remove(cell);
|
||||
for (auto existing_cell : boxes) {
|
||||
Cell *cell = module->cell(remap_name(existing_cell->name));
|
||||
if (cell) {
|
||||
for (auto &conn : existing_cell->connections()) {
|
||||
if (!conn.second.is_wire())
|
||||
continue;
|
||||
Wire *wire = conn.second.as_wire();
|
||||
if (!wire->get_bool_attribute(ID(abc_padding)))
|
||||
continue;
|
||||
cell->unsetPort(conn.first);
|
||||
log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second));
|
||||
}
|
||||
module->swap_names(cell, existing_cell);
|
||||
}
|
||||
module->remove(existing_cell);
|
||||
}
|
||||
|
||||
// Copy connections (and rename) from mapped_mod to module
|
||||
for (auto conn : mapped_mod->connections()) {
|
||||
|
@ -1050,9 +1037,6 @@ struct Abc9Pass : public Pass {
|
|||
}
|
||||
if (arg == "-box" && argidx+1 < args.size()) {
|
||||
box_file = args[++argidx];
|
||||
rewrite_filename(box_file);
|
||||
if (!box_file.empty() && !is_absolute_path(box_file))
|
||||
box_file = std::string(pwd) + "/" + box_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-W" && argidx+1 < args.size()) {
|
||||
|
@ -1063,8 +1047,15 @@ struct Abc9Pass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
// ABC expects a box file for XAIG
|
||||
if (box_file.empty())
|
||||
box_file = "+/dummy.box";
|
||||
|
||||
rewrite_filename(box_file);
|
||||
if (!box_file.empty() && !is_absolute_path(box_file))
|
||||
box_file = std::string(pwd) + "/" + box_file;
|
||||
|
||||
dict<int,IdString> box_lookup;
|
||||
dict<IdString,pool<IdString>> scc_break_inputs;
|
||||
for (auto m : design->modules()) {
|
||||
auto it = m->attributes.find(ID(abc_box_id));
|
||||
if (it == m->attributes.end())
|
||||
|
@ -1082,17 +1073,13 @@ struct Abc9Pass : public Pass {
|
|||
for (auto p : m->ports) {
|
||||
auto w = m->wire(p);
|
||||
log_assert(w);
|
||||
if (w->port_input) {
|
||||
if (w->attributes.count(ID(abc_scc_break)))
|
||||
scc_break_inputs[m->name].insert(p);
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
if (w->port_input) {
|
||||
if (carry_in)
|
||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
||||
carry_in = w;
|
||||
}
|
||||
}
|
||||
if (w->port_output) {
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
else if (w->port_output) {
|
||||
if (carry_out)
|
||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
||||
carry_out = w;
|
||||
|
@ -1144,7 +1131,7 @@ struct Abc9Pass : public Pass {
|
|||
if (!dff_mode || !clk_str.empty()) {
|
||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
|
||||
delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||
box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
|
||||
box_file, lut_file, wire_delay, box_lookup);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1290,7 +1277,7 @@ struct Abc9Pass : public Pass {
|
|||
en_sig = assign_map(std::get<3>(it.first));
|
||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
|
||||
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||
box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
|
||||
box_file, lut_file, wire_delay, box_lookup);
|
||||
assign_map.set(mod);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,14 +48,25 @@ struct AlumaccWorker
|
|||
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
|
||||
|
||||
RTLIL::SigSpec get_lt() {
|
||||
if (GetSize(cached_lt) == 0)
|
||||
cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf();
|
||||
if (GetSize(cached_lt) == 0) {
|
||||
if (is_signed) {
|
||||
get_of();
|
||||
get_sf();
|
||||
cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
|
||||
}
|
||||
else
|
||||
cached_lt = get_cf();
|
||||
}
|
||||
return cached_lt;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec get_gt() {
|
||||
if (GetSize(cached_gt) == 0)
|
||||
cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute());
|
||||
if (GetSize(cached_gt) == 0) {
|
||||
get_lt();
|
||||
get_eq();
|
||||
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
|
||||
cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
|
||||
}
|
||||
return cached_gt;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,11 +34,16 @@ struct Dff2dffsPass : public Pass {
|
|||
log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n");
|
||||
log("dff2dffe for SR over CE priority.\n");
|
||||
log("\n");
|
||||
log(" -match-init\n");
|
||||
log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
|
||||
log(" output wire's init attribute (if any).\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
|
||||
|
||||
bool match_init = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
|
@ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass {
|
|||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-match-init") {
|
||||
match_init = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass {
|
|||
SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
|
||||
SigBit bit_s = sigmap(mux_cell->getPort(ID(S)));
|
||||
|
||||
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
|
||||
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
|
||||
|
||||
SigBit sr_val, sr_sig;
|
||||
bool invert_sr;
|
||||
sr_sig = bit_s;
|
||||
|
@ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass {
|
|||
invert_sr = false;
|
||||
}
|
||||
|
||||
if (match_init) {
|
||||
SigBit bit_q = cell->getPort(ID(Q));
|
||||
if (bit_q.wire) {
|
||||
auto it = bit_q.wire->attributes.find(ID(init));
|
||||
if (it != bit_q.wire->attributes.end()) {
|
||||
auto init_val = it->second[bit_q.offset];
|
||||
if (init_val == State::S1 && sr_val != State::S1)
|
||||
continue;
|
||||
if (init_val == State::S0 && sr_val != State::S0)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
|
||||
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
|
||||
|
||||
if (sr_val == State::S1) {
|
||||
if (cell->type == ID($_DFF_N_)) {
|
||||
if (invert_sr) cell->type = ID($__DFFS_NN1_);
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void split_portname_pair(std::string &port1, std::string &port2)
|
||||
{
|
||||
size_t pos = port1.find_first_of(':');
|
||||
if (pos != std::string::npos) {
|
||||
port2 = port1.substr(pos+1);
|
||||
port1 = port1.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtractinvPass : public Pass {
|
||||
ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" extractinv [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Searches the design for all cells with invertible pins controlled by a cell\n");
|
||||
log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n");
|
||||
log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n");
|
||||
log("the pin instead. Normally used for output to ISE, which does not support the\n");
|
||||
log("inversion parameters.\n");
|
||||
log("\n");
|
||||
log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n");
|
||||
log("on the wire in the blackbox module. The parameter value should have\n");
|
||||
log("the same width as the port, and will be effectively XORed with it.\n");
|
||||
log("\n");
|
||||
log(" -inv <celltype> <portname_out>:<portname_in>\n");
|
||||
log(" Specifies the cell type to use for the inverters and its port names.\n");
|
||||
log(" This option is required.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n");
|
||||
|
||||
std::string inv_celltype, inv_portname, inv_portname2;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-inv" && argidx+2 < args.size()) {
|
||||
inv_celltype = args[++argidx];
|
||||
inv_portname = args[++argidx];
|
||||
split_portname_pair(inv_portname, inv_portname2);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (inv_celltype.empty())
|
||||
log_error("The -inv option is required.\n");
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
for (auto cell : module->selected_cells())
|
||||
for (auto port : cell->connections()) {
|
||||
auto cell_module = design->module(cell->type);
|
||||
if (!cell_module)
|
||||
continue;
|
||||
auto cell_wire = cell_module->wire(port.first);
|
||||
if (!cell_wire)
|
||||
continue;
|
||||
auto it = cell_wire->attributes.find("\\invertible_pin");
|
||||
if (it == cell_wire->attributes.end())
|
||||
continue;
|
||||
IdString param_name = RTLIL::escape_id(it->second.decode_string());
|
||||
auto it2 = cell->parameters.find(param_name);
|
||||
// Inversion not used -- skip.
|
||||
if (it2 == cell->parameters.end())
|
||||
continue;
|
||||
SigSpec sig = port.second;
|
||||
if (it2->second.size() != sig.size())
|
||||
log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name));
|
||||
RTLIL::Const invmask = it2->second;
|
||||
cell->parameters.erase(param_name);
|
||||
if (invmask.is_fully_zero())
|
||||
continue;
|
||||
Wire *iwire = module->addWire(NEW_ID, sig.size());
|
||||
for (int i = 0; i < sig.size(); i++)
|
||||
if (invmask[i] == State::S1) {
|
||||
RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
|
||||
icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
|
||||
icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
|
||||
log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
|
||||
sig[i] = SigBit(iwire, i);
|
||||
}
|
||||
cell->setPort(port.first, sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
} ExtractinvPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -205,20 +205,57 @@ struct TechmapWorker
|
|||
}
|
||||
|
||||
std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
|
||||
dict<Wire*, IdString> temp_renamed_wires;
|
||||
pool<SigBit> autopurge_tpl_bits;
|
||||
|
||||
for (auto &it : tpl->wires_) {
|
||||
for (auto &it : tpl->wires_)
|
||||
{
|
||||
if (it.second->port_id > 0)
|
||||
positional_ports[stringf("$%d", it.second->port_id)] = it.first;
|
||||
{
|
||||
IdString posportname = stringf("$%d", it.second->port_id);
|
||||
positional_ports[posportname] = it.first;
|
||||
|
||||
if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) &&
|
||||
(!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) &&
|
||||
(!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname))))
|
||||
{
|
||||
if (sigmaps.count(tpl) == 0)
|
||||
sigmaps[tpl].set(tpl);
|
||||
|
||||
for (auto bit : sigmaps.at(tpl)(it.second))
|
||||
if (bit.wire != nullptr)
|
||||
autopurge_tpl_bits.insert(bit);
|
||||
}
|
||||
}
|
||||
IdString w_name = it.second->name;
|
||||
apply_prefix(cell->name, w_name);
|
||||
RTLIL::Wire *w = module->addWire(w_name, it.second);
|
||||
w->port_input = false;
|
||||
w->port_output = false;
|
||||
w->port_id = 0;
|
||||
if (it.second->get_bool_attribute(ID(_techmap_special_)))
|
||||
w->attributes.clear();
|
||||
if (w->attributes.count(ID(src)))
|
||||
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
||||
RTLIL::Wire *w = module->wire(w_name);
|
||||
if (w != nullptr) {
|
||||
if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) {
|
||||
temp_renamed_wires[w] = w->name;
|
||||
module->rename(w, NEW_ID);
|
||||
w = nullptr;
|
||||
} else {
|
||||
w->attributes.erase(ID(hierconn));
|
||||
if (GetSize(w) < GetSize(it.second)) {
|
||||
log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
|
||||
log_id(tpl), log_id(it.second), log_id(module), log_id(cell));
|
||||
w->width = GetSize(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (w == nullptr) {
|
||||
w = module->addWire(w_name, it.second);
|
||||
w->port_input = false;
|
||||
w->port_output = false;
|
||||
w->port_id = 0;
|
||||
if (!flatten_mode)
|
||||
w->attributes.erase(ID(techmap_autopurge));
|
||||
if (it.second->get_bool_attribute(ID(_techmap_special_)))
|
||||
w->attributes.clear();
|
||||
if (w->attributes.count(ID(src)))
|
||||
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
||||
}
|
||||
design->select(module, w);
|
||||
}
|
||||
|
||||
|
@ -322,6 +359,12 @@ struct TechmapWorker
|
|||
for (auto &attr : w->attributes) {
|
||||
if (attr.first == ID(src))
|
||||
continue;
|
||||
auto lhs = GetSize(extra_connect.first);
|
||||
auto rhs = GetSize(extra_connect.second);
|
||||
if (lhs > rhs)
|
||||
extra_connect.first.remove(rhs, lhs-rhs);
|
||||
else if (rhs > lhs)
|
||||
extra_connect.second.remove(lhs, rhs-lhs);
|
||||
module->connect(extra_connect);
|
||||
break;
|
||||
}
|
||||
|
@ -344,11 +387,31 @@ struct TechmapWorker
|
|||
if (!flatten_mode && c->type.begins_with("\\$"))
|
||||
c->type = c->type.substr(1);
|
||||
|
||||
for (auto &it2 : c->connections_) {
|
||||
apply_prefix(cell->name, it2.second, module);
|
||||
port_signal_map.apply(it2.second);
|
||||
vector<IdString> autopurge_ports;
|
||||
|
||||
for (auto &it2 : c->connections_)
|
||||
{
|
||||
bool autopurge = false;
|
||||
if (!autopurge_tpl_bits.empty()) {
|
||||
autopurge = GetSize(it2.second) != 0;
|
||||
for (auto &bit : sigmaps.at(tpl)(it2.second))
|
||||
if (!autopurge_tpl_bits.count(bit)) {
|
||||
autopurge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (autopurge) {
|
||||
autopurge_ports.push_back(it2.first);
|
||||
} else {
|
||||
apply_prefix(cell->name, it2.second, module);
|
||||
port_signal_map.apply(it2.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it2 : autopurge_ports)
|
||||
c->unsetPort(it2);
|
||||
|
||||
if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
|
||||
IdString memid = c->getParam(ID(MEMID)).decode_string();
|
||||
log_assert(memory_renames.count(memid) != 0);
|
||||
|
@ -380,6 +443,16 @@ struct TechmapWorker
|
|||
}
|
||||
|
||||
module->remove(cell);
|
||||
|
||||
for (auto &it : temp_renamed_wires)
|
||||
{
|
||||
Wire *w = it.first;
|
||||
IdString name = it.second;
|
||||
IdString altname = module->uniquify(name);
|
||||
Wire *other_w = module->wire(name);
|
||||
module->rename(other_w, altname);
|
||||
module->rename(w, name);
|
||||
}
|
||||
}
|
||||
|
||||
bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
|
||||
|
@ -396,6 +469,18 @@ struct TechmapWorker
|
|||
|
||||
SigMap sigmap(module);
|
||||
|
||||
dict<SigBit, State> init_bits;
|
||||
pool<SigBit> remove_init_bits;
|
||||
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->attributes.count("\\init")) {
|
||||
Const value = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
|
||||
if (value[i] != State::Sx)
|
||||
init_bits[sigmap(SigBit(wire, i))] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
|
||||
std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
|
||||
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
|
||||
|
@ -633,6 +718,17 @@ struct TechmapWorker
|
|||
bit = RTLIL::SigBit(RTLIL::State::Sx);
|
||||
parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
|
||||
}
|
||||
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
|
||||
auto sig = sigmap(conn.second);
|
||||
RTLIL::Const value(State::Sx, sig.size());
|
||||
for (int i = 0; i < sig.size(); i++) {
|
||||
auto it = init_bits.find(sig[i]);
|
||||
if (it != init_bits.end()) {
|
||||
value[i] = it->second;
|
||||
}
|
||||
}
|
||||
parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
|
||||
}
|
||||
}
|
||||
|
||||
int unique_bit_id_counter = 0;
|
||||
|
@ -833,7 +929,7 @@ struct TechmapWorker
|
|||
|
||||
TechmapWires twd = techmap_find_special_wires(tpl);
|
||||
for (auto &it : twd) {
|
||||
if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
|
||||
if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
|
||||
log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
|
||||
if (techmap_do_cache[tpl])
|
||||
for (auto &it2 : it.second)
|
||||
|
@ -864,6 +960,23 @@ struct TechmapWorker
|
|||
mkdebug.off();
|
||||
}
|
||||
|
||||
TechmapWires twd = techmap_find_special_wires(tpl);
|
||||
for (auto &it : twd) {
|
||||
if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") {
|
||||
for (auto &it2 : it.second) {
|
||||
auto val = it2.value.as_const();
|
||||
auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
|
||||
auto it = cell->connections().find(wirename);
|
||||
if (it != cell->connections().end()) {
|
||||
auto sig = sigmap(it->second);
|
||||
for (int i = 0; i < sig.size(); i++)
|
||||
if (val[i] == State::S1)
|
||||
remove_init_bits.insert(sig[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extern_mode && !in_recursion)
|
||||
{
|
||||
std::string m_name = stringf("$extern:%s", log_id(tpl));
|
||||
|
@ -907,6 +1020,25 @@ struct TechmapWorker
|
|||
handled_cells.insert(cell);
|
||||
}
|
||||
|
||||
if (!remove_init_bits.empty()) {
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count("\\init")) {
|
||||
Const &value = wire->attributes.at("\\init");
|
||||
bool do_cleanup = true;
|
||||
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
|
||||
SigBit bit = sigmap(SigBit(wire, i));
|
||||
if (remove_init_bits.count(bit))
|
||||
value[i] = State::Sx;
|
||||
else if (value[i] != State::Sx)
|
||||
do_cleanup = false;
|
||||
}
|
||||
if (do_cleanup) {
|
||||
log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
|
||||
wire->attributes.erase("\\init");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (log_continue) {
|
||||
log_header(design, "Continuing TECHMAP pass.\n");
|
||||
log_continue = false;
|
||||
|
@ -981,6 +1113,11 @@ struct TechmapPass : public Pass {
|
|||
log("will create a wrapper for the cell and then run the command string that the\n");
|
||||
log("attribute is set to on the wrapper module.\n");
|
||||
log("\n");
|
||||
log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n");
|
||||
log("set, and that port is not connected in the instantiation that is mapped, then\n");
|
||||
log("then a cell port connected only to such wires will be omitted in the mapped\n");
|
||||
log("version of the circuit.\n");
|
||||
log("\n");
|
||||
log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
|
||||
log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
|
||||
log("the mapping module to the techmap command. At the moment the following special\n");
|
||||
|
@ -1019,6 +1156,13 @@ struct TechmapPass : public Pass {
|
|||
log("\n");
|
||||
log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_REMOVEINIT_<port-name>_\n");
|
||||
log(" When this wire is set to a constant value, the init attribute of the wire(s)\n");
|
||||
log(" connected to this port will be consumed. This wire must have the same\n");
|
||||
log(" width as the given port, and for every bit that is set to 1 in the value,\n");
|
||||
log(" the corresponding init attribute bit will be changed to 1'bx. If all\n");
|
||||
log(" bits of an init attribute are left as x, it will be removed.\n");
|
||||
log("\n");
|
||||
log("In addition to this special wires, techmap also supports special parameters in\n");
|
||||
log("modules in the map file:\n");
|
||||
log("\n");
|
||||
|
@ -1032,6 +1176,13 @@ struct TechmapPass : public Pass {
|
|||
log(" former has a 1-bit for each constant input bit and the latter has the\n");
|
||||
log(" value for this bit. The unused bits of the latter are set to undef (x).\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_WIREINIT_<port-name>_\n");
|
||||
log(" When a parameter with this name exists, it will be set to the initial\n");
|
||||
log(" value of the wire(s) connected to the given port, as specified by the init\n");
|
||||
log(" attribute. If the attribute doesn't exist, x will be filled for the\n");
|
||||
log(" missing bits. To remove the init attribute bits used, use the\n");
|
||||
log(" _TECHMAP_REMOVEINIT_*_ wires.\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_BITS_CONNMAP_\n");
|
||||
log(" _TECHMAP_CONNMAP_<port-name>_\n");
|
||||
log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
|
||||
|
|
|
@ -345,9 +345,17 @@ struct TestAutotbBackend : public Backend {
|
|||
log("value after initialization. This can e.g. be used to force a reset signal\n");
|
||||
log("low in order to explore more inner states in a state machine.\n");
|
||||
log("\n");
|
||||
log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n");
|
||||
log("generation.\n");
|
||||
log("\n");
|
||||
log(" -n <int>\n");
|
||||
log(" number of iterations the test bench should run (default = 1000)\n");
|
||||
log("\n");
|
||||
log(" -seed <int>\n");
|
||||
log(" seed used for pseudo-random number generation (default = 0).\n");
|
||||
log(" a value of 0 will cause an arbitrary seed to be chosen, based on\n");
|
||||
log(" the current system time.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module AL_MAP_SEQ (
|
||||
output q,
|
||||
output reg q,
|
||||
input ce,
|
||||
input clk,
|
||||
input sr,
|
||||
|
@ -9,6 +9,71 @@ module AL_MAP_SEQ (
|
|||
parameter REGSET = "RESET"; //RESET/SET
|
||||
parameter SRMUX = "SR"; //SR/INV
|
||||
parameter SRMODE = "SYNC"; //SYNC/ASYNC
|
||||
|
||||
wire clk_ce;
|
||||
assign clk_ce = ce ? clk : 1'b0;
|
||||
|
||||
wire srmux;
|
||||
generate
|
||||
case (SRMUX)
|
||||
"SR": assign srmux = sr;
|
||||
"INV": assign srmux = ~sr;
|
||||
default: assign srmux = sr;
|
||||
endcase
|
||||
endgenerate
|
||||
|
||||
wire regset;
|
||||
generate
|
||||
case (REGSET)
|
||||
"RESET": assign regset = 1'b0;
|
||||
"SET": assign regset = 1'b1;
|
||||
default: assign regset = 1'b0;
|
||||
endcase
|
||||
endgenerate
|
||||
|
||||
initial q = regset;
|
||||
|
||||
generate
|
||||
if (DFFMODE == "FF")
|
||||
begin
|
||||
if (SRMODE == "ASYNC")
|
||||
begin
|
||||
always @(posedge clk_ce, posedge srmux)
|
||||
if (srmux)
|
||||
q <= regset;
|
||||
else
|
||||
q <= d;
|
||||
end
|
||||
else
|
||||
begin
|
||||
always @(posedge clk_ce)
|
||||
if (srmux)
|
||||
q <= regset;
|
||||
else
|
||||
q <= d;
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
// DFFMODE == "LATCH"
|
||||
if (SRMODE == "ASYNC")
|
||||
begin
|
||||
always @(clk_ce, srmux)
|
||||
if (srmux)
|
||||
q <= regset;
|
||||
else
|
||||
q <= d;
|
||||
end
|
||||
else
|
||||
begin
|
||||
always @(clk_ce)
|
||||
if (srmux)
|
||||
q <= regset;
|
||||
else
|
||||
q <= d;
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
module AL_MAP_LUT1 (
|
||||
|
@ -17,7 +82,8 @@ module AL_MAP_LUT1 (
|
|||
);
|
||||
parameter [1:0] INIT = 2'h0;
|
||||
parameter EQN = "(A)";
|
||||
assign o = INIT >> a;
|
||||
|
||||
assign o = a ? INIT[1] : INIT[0];
|
||||
endmodule
|
||||
|
||||
module AL_MAP_LUT2 (
|
||||
|
@ -27,7 +93,9 @@ module AL_MAP_LUT2 (
|
|||
);
|
||||
parameter [3:0] INIT = 4'h0;
|
||||
parameter EQN = "(A)";
|
||||
assign o = INIT >> {b, a};
|
||||
|
||||
wire [1:0] s1 = b ? INIT[ 3:2] : INIT[1:0];
|
||||
assign o = a ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
module AL_MAP_LUT3 (
|
||||
|
@ -38,7 +106,10 @@ module AL_MAP_LUT3 (
|
|||
);
|
||||
parameter [7:0] INIT = 8'h0;
|
||||
parameter EQN = "(A)";
|
||||
assign o = INIT >> {c, b, a};
|
||||
|
||||
wire [3:0] s2 = c ? INIT[ 7:4] : INIT[3:0];
|
||||
wire [1:0] s1 = b ? s2[ 3:2] : s2[1:0];
|
||||
assign o = a ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
module AL_MAP_LUT4 (
|
||||
|
@ -50,7 +121,11 @@ module AL_MAP_LUT4 (
|
|||
);
|
||||
parameter [15:0] INIT = 16'h0;
|
||||
parameter EQN = "(A)";
|
||||
assign o = INIT >> {d, c, b, a};
|
||||
|
||||
wire [7:0] s3 = d ? INIT[15:8] : INIT[7:0];
|
||||
wire [3:0] s2 = c ? s3[ 7:4] : s3[3:0];
|
||||
wire [1:0] s1 = b ? s2[ 3:2] : s2[1:0];
|
||||
assign o = a ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
module AL_MAP_LUT5 (
|
||||
|
@ -100,4 +175,18 @@ module AL_MAP_ADDER (
|
|||
output [1:0] o
|
||||
);
|
||||
parameter ALUTYPE = "ADD";
|
||||
|
||||
generate
|
||||
case (ALUTYPE)
|
||||
"ADD": assign o = a + b + c;
|
||||
"SUB": assign o = a - b - c;
|
||||
"A_LE_B": assign o = a - b - c;
|
||||
|
||||
"ADD_CARRY": assign o = { a, 1'b0 };
|
||||
"SUB_CARRY": assign o = { ~a, 1'b0 };
|
||||
"A_LE_B_CARRY": assign o = { a, 1'b0 };
|
||||
default: assign o = a + b + c;
|
||||
endcase
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -28,3 +28,5 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v))
|
|||
$(eval $(call add_share_file,share,techlibs/common/gate2lut.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
||||
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/dummy.box))
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
(dummy) 1 0 0 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
|
|
@ -13,7 +13,11 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v))
|
|||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))
|
||||
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_map.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_unmap.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_model.v))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut))
|
||||
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut))
|
||||
|
|
|
@ -15,16 +15,16 @@ CCU2C 1 1 9 3
|
|||
630 379 630 379 526 275 392 141 273
|
||||
516 516 516 516 412 412 278 278 43
|
||||
|
||||
# Box 2 : TRELLIS_DPR16X4 (16x4 dist ram)
|
||||
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
|
||||
# Outputs: DO0, DO1, DO2, DO3
|
||||
# name ID w/b ins outs
|
||||
TRELLIS_DPR16X4 2 0 14 4
|
||||
# name ID w/b ins outs
|
||||
$__ABC_DPR16X4_COMB 2 0 8 4
|
||||
|
||||
#DI0 DI1 DI2 DI3 RAD0 RAD1 RAD2 RAD3 WAD0 WAD1 WAD2 WAD3 WCK WRE
|
||||
- - - - 141 379 275 379 - - - - - -
|
||||
- - - - 141 379 275 379 - - - - - -
|
||||
- - - - 141 379 275 379 - - - - - -
|
||||
- - - - 141 379 275 379 - - - - - -
|
||||
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
0 0 0 0 141 379 275 379
|
||||
|
||||
# Box 3 : PFUMX (MUX2)
|
||||
# Outputs: Z
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// ---------------------------------------
|
||||
|
||||
module TRELLIS_DPR16X4 (
|
||||
input [3:0] DI,
|
||||
input [3:0] WAD,
|
||||
input WRE,
|
||||
input WCK,
|
||||
input [3:0] RAD,
|
||||
output [3:0] DO
|
||||
);
|
||||
parameter WCKMUX = "WCK";
|
||||
parameter WREMUX = "WRE";
|
||||
parameter [63:0] INITVAL = 64'h0000000000000000;
|
||||
wire [3:0] \$DO ;
|
||||
|
||||
TRELLIS_DPR16X4 #(
|
||||
.WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK),
|
||||
.RAD(RAD), .DO(\$DO )
|
||||
);
|
||||
|
||||
\$__ABC_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
// ---------------------------------------
|
||||
|
||||
(* abc_box_id=2 *)
|
||||
module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
// ---------------------------------------
|
||||
|
||||
module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
|
||||
assign Y = A;
|
||||
endmodule
|
|
@ -109,16 +109,13 @@ module PFUMX (input ALUT, BLUT, C0, output Z);
|
|||
endmodule
|
||||
|
||||
// ---------------------------------------
|
||||
//(* abc_box_id=2 *)
|
||||
module TRELLIS_DPR16X4 (
|
||||
(* abc_scc_break *)
|
||||
input [3:0] DI,
|
||||
(* abc_scc_break *)
|
||||
input [3:0] WAD,
|
||||
(* abc_scc_break *)
|
||||
input WRE,
|
||||
input WCK,
|
||||
input [3:0] RAD,
|
||||
/* (* abc_arrival=<TODO> *) */
|
||||
output [3:0] DO
|
||||
);
|
||||
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(" (this feature is experimental and incomplete)\n");
|
||||
log("\n");
|
||||
log(" -nodsp\n");
|
||||
log(" do not map multipliers to MULT18X18D\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
help_script();
|
||||
|
@ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, json_file;
|
||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr;
|
||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
|
@ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
abc2 = false;
|
||||
vpr = false;
|
||||
abc9 = false;
|
||||
nodsp = false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
abc9 = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodsp") {
|
||||
nodsp = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -218,17 +226,34 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
}
|
||||
|
||||
if (flatten && check_label("flatten", "(unless -noflatten)"))
|
||||
{
|
||||
run("proc");
|
||||
run("flatten");
|
||||
run("tribuf -logic");
|
||||
run("deminout");
|
||||
}
|
||||
|
||||
if (check_label("coarse"))
|
||||
{
|
||||
run("synth -run coarse");
|
||||
run("proc");
|
||||
if (flatten || help_mode)
|
||||
run("flatten");
|
||||
run("tribuf -logic");
|
||||
run("deminout");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
run("check");
|
||||
run("opt");
|
||||
run("wreduce");
|
||||
run("peepopt");
|
||||
run("opt_clean");
|
||||
run("share");
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
if (!nodsp) {
|
||||
run("techmap -map +/mul2dsp.v -map +/ecp5/dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=$__MUL18X18", "(unless -nodsp)");
|
||||
run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)");
|
||||
}
|
||||
run("alumacc");
|
||||
run("opt");
|
||||
run("fsm");
|
||||
run("opt -fast");
|
||||
run("memory -nomap");
|
||||
run("opt_clean");
|
||||
}
|
||||
|
||||
if (!nobram && check_label("map_bram", "(skip if -nobram)"))
|
||||
|
@ -280,12 +305,17 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
if (abc2 || help_mode) {
|
||||
run("abc", " (only if -abc2)");
|
||||
}
|
||||
run("techmap -map +/ecp5/latches_map.v");
|
||||
std::string techmap_args = "-map +/ecp5/latches_map.v";
|
||||
if (abc9)
|
||||
techmap_args += " -map +/ecp5/abc_map.v -max_iter 1";
|
||||
run("techmap " + techmap_args);
|
||||
|
||||
if (abc9) {
|
||||
if (nowidelut)
|
||||
run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200");
|
||||
else
|
||||
run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200");
|
||||
run("techmap -map +/ecp5/abc_unmap.v");
|
||||
} else {
|
||||
if (nowidelut)
|
||||
run("abc -lut 4 -dress");
|
||||
|
|
|
@ -5,7 +5,12 @@ module EFX_LUT4(
|
|||
input I2,
|
||||
input I3
|
||||
);
|
||||
parameter LUTMASK = 16'h0000;
|
||||
parameter LUTMASK = 16'h0000;
|
||||
|
||||
wire [7:0] s3 = I3 ? LUTMASK[15:8] : LUTMASK[7:0];
|
||||
wire [3:0] s2 = I2 ? s3[ 7:4] : s3[3:0];
|
||||
wire [1:0] s1 = I1 ? s2[ 3:2] : s2[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
module EFX_ADD(
|
||||
|
@ -17,10 +22,18 @@ module EFX_ADD(
|
|||
);
|
||||
parameter I0_POLARITY = 1;
|
||||
parameter I1_POLARITY = 1;
|
||||
|
||||
wire i0;
|
||||
wire i1;
|
||||
|
||||
assign i0 = I0_POLARITY ? I0 : ~I0;
|
||||
assign i1 = I1_POLARITY ? I1 : ~I1;
|
||||
|
||||
assign {CO, O} = i0 + i1 + CI;
|
||||
endmodule
|
||||
|
||||
module EFX_FF(
|
||||
output Q,
|
||||
output reg Q,
|
||||
input D,
|
||||
input CE,
|
||||
input CLK,
|
||||
|
@ -33,6 +46,53 @@ module EFX_FF(
|
|||
parameter SR_VALUE = 0;
|
||||
parameter SR_SYNC_PRIORITY = 0;
|
||||
parameter D_POLARITY = 1;
|
||||
|
||||
wire clk;
|
||||
wire ce;
|
||||
wire sr;
|
||||
wire d;
|
||||
wire prio;
|
||||
wire sync;
|
||||
wire async;
|
||||
|
||||
assign clk = CLK_POLARITY ? CLK : ~CLK;
|
||||
assign ce = CE_POLARITY ? CE : ~CE;
|
||||
assign sr = SR_POLARITY ? SR : ~SR;
|
||||
assign d = D_POLARITY ? D : ~D;
|
||||
|
||||
generate
|
||||
if (SR_SYNC == 1)
|
||||
begin
|
||||
if (SR_SYNC_PRIORITY == 1)
|
||||
begin
|
||||
always @(posedge clk)
|
||||
if (sr)
|
||||
Q <= SR_VALUE;
|
||||
else if (ce)
|
||||
Q <= d;
|
||||
end
|
||||
else
|
||||
begin
|
||||
always @(posedge clk)
|
||||
if (ce)
|
||||
begin
|
||||
if (sr)
|
||||
Q <= SR_VALUE;
|
||||
else
|
||||
Q <= d;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
always @(posedge clk or posedge sr)
|
||||
if (sr)
|
||||
Q <= SR_VALUE;
|
||||
else if (ce)
|
||||
Q <= d;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
module EFX_GBUFCE(
|
||||
|
@ -41,6 +101,12 @@ module EFX_GBUFCE(
|
|||
output O
|
||||
);
|
||||
parameter CE_POLARITY = 1'b1;
|
||||
|
||||
wire ce;
|
||||
assign ce = CE_POLARITY ? CE : ~CE;
|
||||
|
||||
assign O = I & ce;
|
||||
|
||||
endmodule
|
||||
|
||||
module EFX_RAM_5K(
|
||||
|
|
|
@ -27,6 +27,7 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v))
|
|||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box))
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
`define SB_DFF_REG reg Q = 0
|
||||
// `define SB_DFF_REG reg Q
|
||||
|
||||
`define ABC_ARRIVAL_HX(TIME) `ifdef ICE40_HX (* abc_arrival=TIME *) `endif
|
||||
`define ABC_ARRIVAL_LP(TIME) `ifdef ICE40_LP (* abc_arrival=TIME *) `endif
|
||||
`define ABC_ARRIVAL_U(TIME) `ifdef ICE40_U (* abc_arrival=TIME *) `endif
|
||||
|
||||
// SiliconBlue IO Cells
|
||||
|
||||
module SB_IO (
|
||||
|
@ -169,20 +173,42 @@ module \$__ICE40_CARRY_WRAPPER (
|
|||
);
|
||||
endmodule
|
||||
|
||||
// Max delay from: https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L90
|
||||
// https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L90
|
||||
// https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L102
|
||||
|
||||
// Positive Edge SiliconBlue FF Cells
|
||||
|
||||
module SB_DFF (output `SB_DFF_REG, input C, D);
|
||||
module SB_DFF (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, D
|
||||
);
|
||||
always @(posedge C)
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFE (output `SB_DFF_REG, input C, E, D);
|
||||
module SB_DFFE (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, D
|
||||
);
|
||||
always @(posedge C)
|
||||
if (E)
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFSR (output `SB_DFF_REG, input C, R, D);
|
||||
module SB_DFFSR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, R, D
|
||||
);
|
||||
always @(posedge C)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -190,7 +216,13 @@ module SB_DFFSR (output `SB_DFF_REG, input C, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFR (output `SB_DFF_REG, input C, R, D);
|
||||
module SB_DFFR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, R, D
|
||||
);
|
||||
always @(posedge C, posedge R)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -198,7 +230,13 @@ module SB_DFFR (output `SB_DFF_REG, input C, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFSS (output `SB_DFF_REG, input C, S, D);
|
||||
module SB_DFFSS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, S, D
|
||||
);
|
||||
always @(posedge C)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -206,7 +244,13 @@ module SB_DFFSS (output `SB_DFF_REG, input C, S, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFS (output `SB_DFF_REG, input C, S, D);
|
||||
module SB_DFFS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, S, D
|
||||
);
|
||||
always @(posedge C, posedge S)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -214,7 +258,13 @@ module SB_DFFS (output `SB_DFF_REG, input C, S, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D);
|
||||
module SB_DFFESR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, R, D
|
||||
);
|
||||
always @(posedge C)
|
||||
if (E) begin
|
||||
if (R)
|
||||
|
@ -224,7 +274,13 @@ module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D);
|
|||
end
|
||||
endmodule
|
||||
|
||||
module SB_DFFER (output `SB_DFF_REG, input C, E, R, D);
|
||||
module SB_DFFER (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, R, D
|
||||
);
|
||||
always @(posedge C, posedge R)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -232,7 +288,13 @@ module SB_DFFER (output `SB_DFF_REG, input C, E, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D);
|
||||
module SB_DFFESS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, S, D
|
||||
);
|
||||
always @(posedge C)
|
||||
if (E) begin
|
||||
if (S)
|
||||
|
@ -242,7 +304,13 @@ module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D);
|
|||
end
|
||||
endmodule
|
||||
|
||||
module SB_DFFES (output `SB_DFF_REG, input C, E, S, D);
|
||||
module SB_DFFES (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, S, D
|
||||
);
|
||||
always @(posedge C, posedge S)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -252,18 +320,36 @@ endmodule
|
|||
|
||||
// Negative Edge SiliconBlue FF Cells
|
||||
|
||||
module SB_DFFN (output `SB_DFF_REG, input C, D);
|
||||
module SB_DFFN (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, D
|
||||
);
|
||||
always @(negedge C)
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNE (output `SB_DFF_REG, input C, E, D);
|
||||
module SB_DFFNE (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, D
|
||||
);
|
||||
always @(negedge C)
|
||||
if (E)
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNSR (output `SB_DFF_REG, input C, R, D);
|
||||
module SB_DFFNSR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, R, D
|
||||
);
|
||||
always @(negedge C)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -271,7 +357,13 @@ module SB_DFFNSR (output `SB_DFF_REG, input C, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNR (output `SB_DFF_REG, input C, R, D);
|
||||
module SB_DFFNR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, R, D
|
||||
);
|
||||
always @(negedge C, posedge R)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -279,7 +371,13 @@ module SB_DFFNR (output `SB_DFF_REG, input C, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNSS (output `SB_DFF_REG, input C, S, D);
|
||||
module SB_DFFNSS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, S, D
|
||||
);
|
||||
always @(negedge C)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -287,7 +385,13 @@ module SB_DFFNSS (output `SB_DFF_REG, input C, S, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNS (output `SB_DFF_REG, input C, S, D);
|
||||
module SB_DFFNS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, S, D
|
||||
);
|
||||
always @(negedge C, posedge S)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -295,7 +399,13 @@ module SB_DFFNS (output `SB_DFF_REG, input C, S, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D);
|
||||
module SB_DFFNESR (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, R, D
|
||||
);
|
||||
always @(negedge C)
|
||||
if (E) begin
|
||||
if (R)
|
||||
|
@ -305,7 +415,13 @@ module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D);
|
|||
end
|
||||
endmodule
|
||||
|
||||
module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D);
|
||||
module SB_DFFNER (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, R, D
|
||||
);
|
||||
always @(negedge C, posedge R)
|
||||
if (R)
|
||||
Q <= 0;
|
||||
|
@ -313,7 +429,13 @@ module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D);
|
|||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D);
|
||||
module SB_DFFNESS (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, S, D
|
||||
);
|
||||
always @(negedge C)
|
||||
if (E) begin
|
||||
if (S)
|
||||
|
@ -323,7 +445,13 @@ module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D);
|
|||
end
|
||||
endmodule
|
||||
|
||||
module SB_DFFNES (output `SB_DFF_REG, input C, E, S, D);
|
||||
module SB_DFFNES (
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output `SB_DFF_REG,
|
||||
input C, E, S, D
|
||||
);
|
||||
always @(negedge C, posedge S)
|
||||
if (S)
|
||||
Q <= 1;
|
||||
|
@ -334,6 +462,9 @@ endmodule
|
|||
// SiliconBlue RAM Cells
|
||||
|
||||
module SB_RAM40_4K (
|
||||
`ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401
|
||||
`ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401
|
||||
`ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026
|
||||
output [15:0] RDATA,
|
||||
input RCLK, RCLKE, RE,
|
||||
input [10:0] RADDR,
|
||||
|
@ -502,6 +633,9 @@ module SB_RAM40_4K (
|
|||
endmodule
|
||||
|
||||
module SB_RAM40_4KNR (
|
||||
`ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401
|
||||
`ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401
|
||||
`ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026
|
||||
output [15:0] RDATA,
|
||||
input RCLKN, RCLKE, RE,
|
||||
input [10:0] RADDR,
|
||||
|
@ -567,6 +701,9 @@ module SB_RAM40_4KNR (
|
|||
endmodule
|
||||
|
||||
module SB_RAM40_4KNW (
|
||||
`ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401
|
||||
`ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401
|
||||
`ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026
|
||||
output [15:0] RDATA,
|
||||
input RCLK, RCLKE, RE,
|
||||
input [10:0] RADDR,
|
||||
|
@ -632,6 +769,9 @@ module SB_RAM40_4KNW (
|
|||
endmodule
|
||||
|
||||
module SB_RAM40_4KNRNW (
|
||||
`ABC_ARRIVAL_HX(2146) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_hx1k.txt#L401
|
||||
`ABC_ARRIVAL_LP(3163) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_lp1k.txt#L401
|
||||
`ABC_ARRIVAL_U(1179) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026
|
||||
output [15:0] RDATA,
|
||||
input RCLKN, RCLKE, RE,
|
||||
input [10:0] RADDR,
|
||||
|
@ -700,7 +840,12 @@ endmodule
|
|||
|
||||
module ICESTORM_LC (
|
||||
input I0, I1, I2, I3, CIN, CLK, CEN, SR,
|
||||
output LO, O, COUT
|
||||
output LO,
|
||||
`ABC_ARRIVAL_HX(540)
|
||||
`ABC_ARRIVAL_LP(796)
|
||||
`ABC_ARRIVAL_U(1391)
|
||||
output O,
|
||||
output COUT
|
||||
);
|
||||
parameter [15:0] LUT_INIT = 0;
|
||||
|
||||
|
@ -1300,6 +1445,7 @@ module SB_MAC16 (
|
|||
input ADDSUBTOP, ADDSUBBOT,
|
||||
input OHOLDTOP, OHOLDBOT,
|
||||
input CI, ACCUMCI, SIGNEXTIN,
|
||||
//`ABC_ARRIVAL_U(1984) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13026
|
||||
output [31:0] O,
|
||||
output CO, ACCUMCO, SIGNEXTOUT
|
||||
);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
module \$__MUL16X16 (input [15:0] A, input [15:0] B, output [31:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
SB_MAC16 #(
|
||||
.NEG_TRIGGER(1'b0),
|
||||
.C_REG(1'b0),
|
||||
.A_REG(1'b0),
|
||||
.B_REG(1'b0),
|
||||
.D_REG(1'b0),
|
||||
.TOP_8x8_MULT_REG(1'b0),
|
||||
.BOT_8x8_MULT_REG(1'b0),
|
||||
.PIPELINE_16x16_MULT_REG1(1'b0),
|
||||
.PIPELINE_16x16_MULT_REG2(1'b0),
|
||||
.TOPOUTPUT_SELECT(2'b11),
|
||||
.TOPADDSUB_LOWERINPUT(2'b0),
|
||||
.TOPADDSUB_UPPERINPUT(1'b0),
|
||||
.TOPADDSUB_CARRYSELECT(2'b0),
|
||||
.BOTOUTPUT_SELECT(2'b11),
|
||||
.BOTADDSUB_LOWERINPUT(2'b0),
|
||||
.BOTADDSUB_UPPERINPUT(1'b0),
|
||||
.BOTADDSUB_CARRYSELECT(2'b0),
|
||||
.MODE_8x8(1'b0),
|
||||
.A_SIGNED(A_SIGNED),
|
||||
.B_SIGNED(B_SIGNED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.A(A),
|
||||
.B(B),
|
||||
.O(Y),
|
||||
);
|
||||
endmodule
|
|
@ -238,7 +238,14 @@ struct SynthIce40Pass : public ScriptPass
|
|||
{
|
||||
if (check_label("begin"))
|
||||
{
|
||||
run("read_verilog -icells -lib +/ice40/cells_sim.v");
|
||||
std::string define;
|
||||
if (device_opt == "lp")
|
||||
define = "-D ICE40_LP";
|
||||
else if (device_opt == "u")
|
||||
define = "-D ICE40_U";
|
||||
else
|
||||
define = "-D ICE40_HX";
|
||||
run("read_verilog -icells " + define + " -lib +/ice40/cells_sim.v");
|
||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
run("proc");
|
||||
}
|
||||
|
@ -265,8 +272,18 @@ struct SynthIce40Pass : public ScriptPass
|
|||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
if (help_mode || dsp)
|
||||
run("ice40_dsp", "(if -dsp)");
|
||||
if (help_mode || dsp) {
|
||||
run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 "
|
||||
"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 "
|
||||
"-D DSP_NAME=$__MUL16X16", "(if -dsp)");
|
||||
run("select a:mul2dsp", " (if -dsp)");
|
||||
run("setattr -unset mul2dsp", " (if -dsp)");
|
||||
run("opt_expr -fine", " (if -dsp)");
|
||||
run("wreduce", " (if -dsp)");
|
||||
run("select -clear", " (if -dsp)");
|
||||
run("ice40_dsp", " (if -dsp)");
|
||||
run("chtype -set $mul t:$__soft_mul", "(if -dsp)");
|
||||
}
|
||||
run("alumacc");
|
||||
run("opt");
|
||||
run("fsm");
|
||||
|
|
|
@ -25,7 +25,10 @@ techlibs/xilinx/brams_init_8.vh: techlibs/xilinx/brams_init.mk
|
|||
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_cells_xtra.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6v_cells_xtra.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_cells_xtra.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcu_cells_xtra.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v))
|
||||
|
@ -35,10 +38,15 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_bb.v))
|
|||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams.txt))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_ff_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_ff_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/dsp_map.v))
|
||||
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_map.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_unmap.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_model.v))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut))
|
||||
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7_nowide.lut))
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
|
||||
module RAM32X1D (
|
||||
output DPO, SPO,
|
||||
(* techmap_autopurge *) input D,
|
||||
(* techmap_autopurge *) input WCLK,
|
||||
(* techmap_autopurge *) input WE,
|
||||
(* techmap_autopurge *) input A0, A1, A2, A3, A4,
|
||||
(* techmap_autopurge *) input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
|
||||
);
|
||||
parameter INIT = 32'h0;
|
||||
parameter IS_WCLK_INVERTED = 1'b0;
|
||||
wire \$DPO , \$SPO ;
|
||||
RAM32X1D #(
|
||||
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.DPO(\$DPO ), .SPO(\$SPO ),
|
||||
.D(D), .WCLK(WCLK), .WE(WE),
|
||||
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4),
|
||||
.DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4)
|
||||
);
|
||||
\$__ABC_LUT6 dpo (.A(\$DPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(DPO));
|
||||
\$__ABC_LUT6 spo (.A(\$SPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(SPO));
|
||||
endmodule
|
||||
|
||||
module RAM64X1D (
|
||||
output DPO, SPO,
|
||||
(* techmap_autopurge *) input D,
|
||||
(* techmap_autopurge *) input WCLK,
|
||||
(* techmap_autopurge *) input WE,
|
||||
(* techmap_autopurge *) input A0, A1, A2, A3, A4, A5,
|
||||
(* techmap_autopurge *) input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
|
||||
);
|
||||
parameter INIT = 64'h0;
|
||||
parameter IS_WCLK_INVERTED = 1'b0;
|
||||
wire \$DPO , \$SPO ;
|
||||
RAM64X1D #(
|
||||
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.DPO(\$DPO ), .SPO(\$SPO ),
|
||||
.D(D), .WCLK(WCLK), .WE(WE),
|
||||
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A5(A5),
|
||||
.DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4), .DPRA5(DPRA5)
|
||||
);
|
||||
\$__ABC_LUT6 dpo (.A(\$DPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(DPO));
|
||||
\$__ABC_LUT6 spo (.A(\$SPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(SPO));
|
||||
endmodule
|
||||
|
||||
module RAM128X1D (
|
||||
output DPO, SPO,
|
||||
(* techmap_autopurge *) input D,
|
||||
(* techmap_autopurge *) input WCLK,
|
||||
(* techmap_autopurge *) input WE,
|
||||
(* techmap_autopurge *) input [6:0] A, DPRA
|
||||
);
|
||||
parameter INIT = 128'h0;
|
||||
parameter IS_WCLK_INVERTED = 1'b0;
|
||||
wire \$DPO , \$SPO ;
|
||||
RAM128X1D #(
|
||||
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.DPO(\$DPO ), .SPO(\$SPO ),
|
||||
.D(D), .WCLK(WCLK), .WE(WE),
|
||||
.A(A),
|
||||
.DPRA(DPRA)
|
||||
);
|
||||
\$__ABC_LUT7 dpo (.A(\$DPO ), .S(A), .Y(DPO));
|
||||
\$__ABC_LUT7 spo (.A(\$SPO ), .S(A), .Y(SPO));
|
||||
endmodule
|
||||
|
||||
module SRL16E (
|
||||
output Q,
|
||||
(* techmap_autopurge *) input A0, A1, A2, A3, CE, CLK, D
|
||||
);
|
||||
parameter [15:0] INIT = 16'h0000;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
wire \$Q ;
|
||||
SRL16E #(
|
||||
.INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.Q(\$Q ),
|
||||
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .CE(CE), .CLK(CLK), .D(D)
|
||||
);
|
||||
\$__ABC_LUT6 q (.A(\$Q ), .S({1'b1, A0, A1, A2, A3, 1'b1}), .Y(Q));
|
||||
endmodule
|
||||
|
||||
module SRLC32E (
|
||||
output Q,
|
||||
output Q31,
|
||||
(* techmap_autopurge *) input [4:0] A,
|
||||
(* techmap_autopurge *) input CE, CLK, D
|
||||
);
|
||||
parameter [31:0] INIT = 32'h00000000;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
wire \$Q ;
|
||||
SRLC32E #(
|
||||
.INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.Q(\$Q ), .Q31(Q31),
|
||||
.A(A), .CE(CE), .CLK(CLK), .D(D)
|
||||
);
|
||||
\$__ABC_LUT6 q (.A(\$Q ), .S({1'b1, A}), .Y(Q));
|
||||
endmodule
|
||||
|
||||
module DSP48E1 (
|
||||
(* techmap_autopurge *) output [29:0] ACOUT,
|
||||
(* techmap_autopurge *) output [17:0] BCOUT,
|
||||
(* techmap_autopurge *) output reg CARRYCASCOUT,
|
||||
(* techmap_autopurge *) output reg [3:0] CARRYOUT,
|
||||
(* techmap_autopurge *) output reg MULTSIGNOUT,
|
||||
(* techmap_autopurge *) output OVERFLOW,
|
||||
(* techmap_autopurge *) output reg signed [47:0] P,
|
||||
(* techmap_autopurge *) output PATTERNBDETECT,
|
||||
(* techmap_autopurge *) output PATTERNDETECT,
|
||||
(* techmap_autopurge *) output [47:0] PCOUT,
|
||||
(* techmap_autopurge *) output UNDERFLOW,
|
||||
(* techmap_autopurge *) input signed [29:0] A,
|
||||
(* techmap_autopurge *) input [29:0] ACIN,
|
||||
(* techmap_autopurge *) input [3:0] ALUMODE,
|
||||
(* techmap_autopurge *) input signed [17:0] B,
|
||||
(* techmap_autopurge *) input [17:0] BCIN,
|
||||
(* techmap_autopurge *) input [47:0] C,
|
||||
(* techmap_autopurge *) input CARRYCASCIN,
|
||||
(* techmap_autopurge *) input CARRYIN,
|
||||
(* techmap_autopurge *) input [2:0] CARRYINSEL,
|
||||
(* techmap_autopurge *) input CEA1,
|
||||
(* techmap_autopurge *) input CEA2,
|
||||
(* techmap_autopurge *) input CEAD,
|
||||
(* techmap_autopurge *) input CEALUMODE,
|
||||
(* techmap_autopurge *) input CEB1,
|
||||
(* techmap_autopurge *) input CEB2,
|
||||
(* techmap_autopurge *) input CEC,
|
||||
(* techmap_autopurge *) input CECARRYIN,
|
||||
(* techmap_autopurge *) input CECTRL,
|
||||
(* techmap_autopurge *) input CED,
|
||||
(* techmap_autopurge *) input CEINMODE,
|
||||
(* techmap_autopurge *) input CEM,
|
||||
(* techmap_autopurge *) input CEP,
|
||||
(* techmap_autopurge *) input CLK,
|
||||
(* techmap_autopurge *) input [24:0] D,
|
||||
(* techmap_autopurge *) input [4:0] INMODE,
|
||||
(* techmap_autopurge *) input MULTSIGNIN,
|
||||
(* techmap_autopurge *) input [6:0] OPMODE,
|
||||
(* techmap_autopurge *) input [47:0] PCIN,
|
||||
(* techmap_autopurge *) input RSTA,
|
||||
(* techmap_autopurge *) input RSTALLCARRYIN,
|
||||
(* techmap_autopurge *) input RSTALUMODE,
|
||||
(* techmap_autopurge *) input RSTB,
|
||||
(* techmap_autopurge *) input RSTC,
|
||||
(* techmap_autopurge *) input RSTCTRL,
|
||||
(* techmap_autopurge *) input RSTD,
|
||||
(* techmap_autopurge *) input RSTINMODE,
|
||||
(* techmap_autopurge *) input RSTM,
|
||||
(* techmap_autopurge *) input RSTP
|
||||
);
|
||||
parameter integer ACASCREG = 1;
|
||||
parameter integer ADREG = 1;
|
||||
parameter integer ALUMODEREG = 1;
|
||||
parameter integer AREG = 1;
|
||||
parameter AUTORESET_PATDET = "NO_RESET";
|
||||
parameter A_INPUT = "DIRECT";
|
||||
parameter integer BCASCREG = 1;
|
||||
parameter integer BREG = 1;
|
||||
parameter B_INPUT = "DIRECT";
|
||||
parameter integer CARRYINREG = 1;
|
||||
parameter integer CARRYINSELREG = 1;
|
||||
parameter integer CREG = 1;
|
||||
parameter integer DREG = 1;
|
||||
parameter integer INMODEREG = 1;
|
||||
parameter integer MREG = 1;
|
||||
parameter integer OPMODEREG = 1;
|
||||
parameter integer PREG = 1;
|
||||
parameter SEL_MASK = "MASK";
|
||||
parameter SEL_PATTERN = "PATTERN";
|
||||
parameter USE_DPORT = "FALSE";
|
||||
parameter USE_MULT = "MULTIPLY";
|
||||
parameter USE_PATTERN_DETECT = "NO_PATDET";
|
||||
parameter USE_SIMD = "ONE48";
|
||||
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
|
||||
parameter [47:0] PATTERN = 48'h000000000000;
|
||||
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
|
||||
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
|
||||
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
|
||||
|
||||
parameter _TECHMAP_CELLTYPE_ = "";
|
||||
localparam techmap_guard = (_TECHMAP_CELLTYPE_ != "");
|
||||
|
||||
`define DSP48E1_INST(__CELL__) """
|
||||
__CELL__ #(
|
||||
.ACASCREG(ACASCREG),
|
||||
.ADREG(ADREG),
|
||||
.ALUMODEREG(ALUMODEREG),
|
||||
.AREG(AREG),
|
||||
.AUTORESET_PATDET(AUTORESET_PATDET),
|
||||
.A_INPUT(A_INPUT),
|
||||
.BCASCREG(BCASCREG),
|
||||
.BREG(BREG),
|
||||
.B_INPUT(B_INPUT),
|
||||
.CARRYINREG(CARRYINREG),
|
||||
.CARRYINSELREG(CARRYINSELREG),
|
||||
.CREG(CREG),
|
||||
.DREG(DREG),
|
||||
.INMODEREG(INMODEREG),
|
||||
.MREG(MREG),
|
||||
.OPMODEREG(OPMODEREG),
|
||||
.PREG(PREG),
|
||||
.SEL_MASK(SEL_MASK),
|
||||
.SEL_PATTERN(SEL_PATTERN),
|
||||
.USE_DPORT(USE_DPORT),
|
||||
.USE_MULT(USE_MULT),
|
||||
.USE_PATTERN_DETECT(USE_PATTERN_DETECT),
|
||||
.USE_SIMD(USE_SIMD),
|
||||
.MASK(MASK),
|
||||
.PATTERN(PATTERN),
|
||||
.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED),
|
||||
.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED),
|
||||
.IS_CLK_INVERTED(IS_CLK_INVERTED),
|
||||
.IS_INMODE_INVERTED(IS_INMODE_INVERTED),
|
||||
.IS_OPMODE_INVERTED(IS_OPMODE_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.ACOUT(ACOUT),
|
||||
.BCOUT(BCOUT),
|
||||
.CARRYCASCOUT(CARRYCASCOUT),
|
||||
.CARRYOUT(CARRYOUT),
|
||||
.MULTSIGNOUT(MULTSIGNOUT),
|
||||
.OVERFLOW(OVERFLOW),
|
||||
.P(oP),
|
||||
.PATTERNBDETECT(PATTERNBDETECT),
|
||||
.PATTERNDETECT(PATTERNDETECT),
|
||||
.PCOUT(oPCOUT),
|
||||
.UNDERFLOW(UNDERFLOW),
|
||||
.A(iA),
|
||||
.ACIN(ACIN),
|
||||
.ALUMODE(ALUMODE),
|
||||
.B(iB),
|
||||
.BCIN(BCIN),
|
||||
.C(iC),
|
||||
.CARRYCASCIN(CARRYCASCIN),
|
||||
.CARRYIN(CARRYIN),
|
||||
.CARRYINSEL(CARRYINSEL),
|
||||
.CEA1(CEA1),
|
||||
.CEA2(CEA2),
|
||||
.CEAD(CEAD),
|
||||
.CEALUMODE(CEALUMODE),
|
||||
.CEB1(CEB1),
|
||||
.CEB2(CEB2),
|
||||
.CEC(CEC),
|
||||
.CECARRYIN(CECARRYIN),
|
||||
.CECTRL(CECTRL),
|
||||
.CED(CED),
|
||||
.CEINMODE(CEINMODE),
|
||||
.CEM(CEM),
|
||||
.CEP(CEP),
|
||||
.CLK(CLK),
|
||||
.D(iD),
|
||||
.INMODE(INMODE),
|
||||
.MULTSIGNIN(MULTSIGNIN),
|
||||
.OPMODE(OPMODE),
|
||||
.PCIN(PCIN),
|
||||
.RSTA(RSTA),
|
||||
.RSTALLCARRYIN(RSTALLCARRYIN),
|
||||
.RSTALUMODE(RSTALUMODE),
|
||||
.RSTB(RSTB),
|
||||
.RSTC(RSTC),
|
||||
.RSTCTRL(RSTCTRL),
|
||||
.RSTD(RSTD),
|
||||
.RSTINMODE(RSTINMODE),
|
||||
.RSTM(RSTM),
|
||||
.RSTP(RSTP)
|
||||
);
|
||||
"""
|
||||
|
||||
wire [29:0] iA;
|
||||
wire [17:0] iB;
|
||||
wire [47:0] iC;
|
||||
wire [24:0] iD;
|
||||
|
||||
wire pA, pB, pC, pD, pAD, pM, pP;
|
||||
wire [47:0] oP, mP;
|
||||
wire [47:0] oPCOUT, mPCOUT;
|
||||
|
||||
generate
|
||||
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
|
||||
// Disconnect the A-input if MREG is enabled, since
|
||||
// combinatorial path is broken
|
||||
if (AREG == 0 && MREG == 0 && PREG == 0)
|
||||
assign iA = A, pA = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
|
||||
if (BREG == 0 && MREG == 0 && PREG == 0)
|
||||
assign iB = B, pB = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
|
||||
if (CREG == 0 && PREG == 0)
|
||||
assign iC = C, pC = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
|
||||
if (DREG == 0)
|
||||
assign iD = D;
|
||||
else if (techmap_guard)
|
||||
$error("Invalid DSP48E1 configuration: DREG enabled but USE_DPORT == \"FALSE\"");
|
||||
assign pD = 1'bx;
|
||||
if (ADREG == 1 && techmap_guard)
|
||||
$error("Invalid DSP48E1 configuration: ADREG enabled but USE_DPORT == \"FALSE\"");
|
||||
assign pAD = 1'bx;
|
||||
if (PREG == 0) begin
|
||||
if (MREG == 1)
|
||||
\$__ABC_REG rM (.Q(pM));
|
||||
else
|
||||
assign pM = 1'bx;
|
||||
assign pP = 1'bx;
|
||||
end else begin
|
||||
assign pM = 1'bx;
|
||||
\$__ABC_REG rP (.Q(pP));
|
||||
end
|
||||
|
||||
if (MREG == 0 && PREG == 0)
|
||||
assign mP = oP, mPCOUT = oPCOUT;
|
||||
else
|
||||
assign mP = 1'bx, mPCOUT = 1'bx;
|
||||
\$__ABC_DSP48E1_MULT_P_MUX muxP (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P)
|
||||
);
|
||||
\$__ABC_DSP48E1_MULT_PCOUT_MUX muxPCOUT (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
|
||||
);
|
||||
|
||||
`DSP48E1_INST(\$__ABC_DSP48E1_MULT )
|
||||
end
|
||||
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
|
||||
// Disconnect the A-input if MREG is enabled, since
|
||||
// combinatorial path is broken
|
||||
if (AREG == 0 && ADREG == 0 && MREG == 0 && PREG == 0)
|
||||
assign iA = A, pA = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
|
||||
if (BREG == 0 && MREG == 0 && PREG == 0)
|
||||
assign iB = B, pB = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
|
||||
if (CREG == 0 && PREG == 0)
|
||||
assign iC = C, pC = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
|
||||
if (DREG == 0 && ADREG == 0)
|
||||
assign iD = D, pD = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(25)) rD (.I(D), .O(iD), .Q(pD));
|
||||
if (PREG == 0) begin
|
||||
if (MREG == 1) begin
|
||||
assign pAD = 1'bx;
|
||||
\$__ABC_REG rM (.Q(pM));
|
||||
end else begin
|
||||
if (ADREG == 1)
|
||||
\$__ABC_REG rAD (.Q(pAD));
|
||||
else
|
||||
assign pAD = 1'bx;
|
||||
assign pM = 1'bx;
|
||||
end
|
||||
assign pP = 1'bx;
|
||||
end else begin
|
||||
assign pAD = 1'bx, pM = 1'bx;
|
||||
\$__ABC_REG rP (.Q(pP));
|
||||
end
|
||||
|
||||
if (MREG == 0 && PREG == 0)
|
||||
assign mP = oP, mPCOUT = oPCOUT;
|
||||
else
|
||||
assign mP = 1'bx, mPCOUT = 1'bx;
|
||||
\$__ABC_DSP48E1_MULT_DPORT_P_MUX muxP (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P)
|
||||
);
|
||||
\$__ABC_DSP48E1_MULT_DPORT_PCOUT_MUX muxPCOUT (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
|
||||
);
|
||||
|
||||
`DSP48E1_INST(\$__ABC_DSP48E1_MULT_DPORT )
|
||||
end
|
||||
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
|
||||
// Disconnect the A-input if MREG is enabled, since
|
||||
// combinatorial path is broken
|
||||
if (AREG == 0 && PREG == 0)
|
||||
assign iA = A, pA = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
|
||||
if (BREG == 0 && PREG == 0)
|
||||
assign iB = B, pB = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
|
||||
if (CREG == 0 && PREG == 0)
|
||||
assign iC = C, pC = 1'bx;
|
||||
else
|
||||
\$__ABC_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
|
||||
if (DREG == 1 && techmap_guard)
|
||||
$error("Invalid DSP48E1 configuration: DREG enabled but USE_DPORT == \"FALSE\"");
|
||||
assign pD = 1'bx;
|
||||
if (ADREG == 1 && techmap_guard)
|
||||
$error("Invalid DSP48E1 configuration: ADREG enabled but USE_DPORT == \"FALSE\"");
|
||||
assign pAD = 1'bx;
|
||||
if (MREG == 1 && techmap_guard)
|
||||
$error("Invalid DSP48E1 configuration: MREG enabled but USE_MULT == \"NONE\"");
|
||||
assign pM = 1'bx;
|
||||
if (PREG == 1)
|
||||
\$__ABC_REG rP (.Q(pP));
|
||||
else
|
||||
assign pP = 1'bx;
|
||||
|
||||
if (MREG == 0 && PREG == 0)
|
||||
assign mP = oP, mPCOUT = oPCOUT;
|
||||
else
|
||||
assign mP = 1'bx, mPCOUT = 1'bx;
|
||||
\$__ABC_DSP48E1_P_MUX muxP (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P)
|
||||
);
|
||||
\$__ABC_DSP48E1_PCOUT_MUX muxPCOUT (
|
||||
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
|
||||
);
|
||||
|
||||
`DSP48E1_INST(\$__ABC_DSP48E1 )
|
||||
end
|
||||
else
|
||||
$error("Invalid DSP48E1 configuration");
|
||||
endgenerate
|
||||
`undef DSP48E1_INST
|
||||
endmodule
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Box containing MUXF7.[AB] + MUXF8,
|
||||
// Necessary to make these an atomic unit so that
|
||||
// ABC cannot optimise just one of the MUXF7 away
|
||||
// and expect to save on its delay
|
||||
(* abc_box_id = 3, lib_whitebox *)
|
||||
module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
|
||||
assign O = S1 ? (S0 ? I3 : I2)
|
||||
: (S0 ? I1 : I0);
|
||||
endmodule
|
||||
|
||||
// Box to emulate comb/seq behaviour of RAMD{32,64} and SRL{16,32}
|
||||
// Necessary since RAMD* and SRL* have both combinatorial (i.e.
|
||||
// same-cycle read operation) and sequential (write operation
|
||||
// is only committed on the next clock edge).
|
||||
// To model the combinatorial path, such cells have to be split
|
||||
// into comb and seq parts, with this box modelling only the former.
|
||||
(* abc_box_id=2000 *)
|
||||
module \$__ABC_LUT6 (input A, input [5:0] S, output Y);
|
||||
endmodule
|
||||
// Box to emulate comb/seq behaviour of RAMD128
|
||||
(* abc_box_id=2001 *)
|
||||
module \$__ABC_LUT7 (input A, input [6:0] S, output Y);
|
||||
endmodule
|
||||
|
||||
|
||||
// Modules used to model the comb/seq behaviour of DSP48E1
|
||||
// With abc_map.v responsible for splicing the below modules
|
||||
// between the combinatorial DSP48E1 box (e.g. disconnecting
|
||||
// A when AREG, MREG or PREG is enabled and splicing in the
|
||||
// "$__ABC_DSP48E1_REG" blackbox as "REG" in the diagram below)
|
||||
// this acts to first disables the combinatorial path (as there
|
||||
// is no connectivity through REG), and secondly, since this is
|
||||
// blackbox a new PI will be introduced with an arrival time of
|
||||
// zero.
|
||||
// Note: Since these "$__ABC_DSP48E1_REG" modules are of a
|
||||
// sequential nature, they are not passed as a box to ABC and
|
||||
// (desirably) represented as PO/PIs.
|
||||
//
|
||||
// At the DSP output, we place a blackbox mux ("M" in the diagram
|
||||
// below) to capture the fact that the critical-path could come
|
||||
// from any one of its inputs.
|
||||
// In contrast to "REG", the "$__ABC_DSP48E1_*_MUX" modules are
|
||||
// combinatorial blackboxes that do get passed to ABC.
|
||||
// The propagation delay through this box (specified in the box
|
||||
// file) captures the arrival time of the register (i.e.
|
||||
// propagation from AREG to P after clock edge), or zero delay
|
||||
// for the combinatorial path from the DSP.
|
||||
//
|
||||
// Doing so should means that ABC is able to analyse the
|
||||
// worst-case delay through to P, regardless of if it was
|
||||
// through any combinatorial paths (e.g. B, below) or an
|
||||
// internal register (A2REG).
|
||||
// However, the true value of being as complete as this is
|
||||
// questionable since if AREG=1 and BREG=0 (as below)
|
||||
// then the worse-case path would very likely be through B
|
||||
// and very unlikely to be through AREG.Q...?
|
||||
//
|
||||
// In graphical form:
|
||||
//
|
||||
// +-----+
|
||||
// +------>> REG >>----+
|
||||
// | +-----+ |
|
||||
// | |
|
||||
// | +---------+ | __
|
||||
// A >>-+X X-| | +--| \
|
||||
// | DSP48E1 |P | M |--->> P
|
||||
// | AREG=1 |-------|__/
|
||||
// B >>------| |
|
||||
// +---------+
|
||||
//
|
||||
`define ABC_DSP48E1_MUX(__NAME__) """
|
||||
module __NAME__ (input Aq, ADq, Bq, Cq, Dq, input [47:0] I, input Mq, input [47:0] P, input Pq, output [47:0] O);
|
||||
endmodule
|
||||
"""
|
||||
(* abc_box_id=2100 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_MULT_P_MUX )
|
||||
(* abc_box_id=2101 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_MULT_PCOUT_MUX )
|
||||
(* abc_box_id=2102 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_MULT_DPORT_P_MUX )
|
||||
(* abc_box_id=2103 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_MULT_DPORT_PCOUT_MUX )
|
||||
(* abc_box_id=2104 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_P_MUX )
|
||||
(* abc_box_id=2105 *) `ABC_DSP48E1_MUX(\$__ABC_DSP48E1_PCOUT_MUX )
|
||||
|
||||
`define ABC_DSP48E1(__NAME__) """
|
||||
module __NAME__ (
|
||||
output [29:0] ACOUT,
|
||||
output [17:0] BCOUT,
|
||||
output reg CARRYCASCOUT,
|
||||
output reg [3:0] CARRYOUT,
|
||||
output reg MULTSIGNOUT,
|
||||
output OVERFLOW,
|
||||
output reg signed [47:0] P,
|
||||
output PATTERNBDETECT,
|
||||
output PATTERNDETECT,
|
||||
output [47:0] PCOUT,
|
||||
output UNDERFLOW,
|
||||
input signed [29:0] A,
|
||||
input [29:0] ACIN,
|
||||
input [3:0] ALUMODE,
|
||||
input signed [17:0] B,
|
||||
input [17:0] BCIN,
|
||||
input [47:0] C,
|
||||
input CARRYCASCIN,
|
||||
input CARRYIN,
|
||||
input [2:0] CARRYINSEL,
|
||||
input CEA1,
|
||||
input CEA2,
|
||||
input CEAD,
|
||||
input CEALUMODE,
|
||||
input CEB1,
|
||||
input CEB2,
|
||||
input CEC,
|
||||
input CECARRYIN,
|
||||
input CECTRL,
|
||||
input CED,
|
||||
input CEINMODE,
|
||||
input CEM,
|
||||
input CEP,
|
||||
input CLK,
|
||||
input [24:0] D,
|
||||
input [4:0] INMODE,
|
||||
input MULTSIGNIN,
|
||||
input [6:0] OPMODE,
|
||||
input [47:0] PCIN,
|
||||
input RSTA,
|
||||
input RSTALLCARRYIN,
|
||||
input RSTALUMODE,
|
||||
input RSTB,
|
||||
input RSTC,
|
||||
input RSTCTRL,
|
||||
input RSTD,
|
||||
input RSTINMODE,
|
||||
input RSTM,
|
||||
input RSTP
|
||||
);
|
||||
parameter integer ACASCREG = 1;
|
||||
parameter integer ADREG = 1;
|
||||
parameter integer ALUMODEREG = 1;
|
||||
parameter integer AREG = 1;
|
||||
parameter AUTORESET_PATDET = "NO_RESET";
|
||||
parameter A_INPUT = "DIRECT";
|
||||
parameter integer BCASCREG = 1;
|
||||
parameter integer BREG = 1;
|
||||
parameter B_INPUT = "DIRECT";
|
||||
parameter integer CARRYINREG = 1;
|
||||
parameter integer CARRYINSELREG = 1;
|
||||
parameter integer CREG = 1;
|
||||
parameter integer DREG = 1;
|
||||
parameter integer INMODEREG = 1;
|
||||
parameter integer MREG = 1;
|
||||
parameter integer OPMODEREG = 1;
|
||||
parameter integer PREG = 1;
|
||||
parameter SEL_MASK = "MASK";
|
||||
parameter SEL_PATTERN = "PATTERN";
|
||||
parameter USE_DPORT = "FALSE";
|
||||
parameter USE_MULT = "MULTIPLY";
|
||||
parameter USE_PATTERN_DETECT = "NO_PATDET";
|
||||
parameter USE_SIMD = "ONE48";
|
||||
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
|
||||
parameter [47:0] PATTERN = 48'h000000000000;
|
||||
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
|
||||
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
|
||||
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
|
||||
endmodule
|
||||
"""
|
||||
(* abc_box_id=3000 *) `ABC_DSP48E1(\$__ABC_DSP48E1_MULT )
|
||||
(* abc_box_id=3001 *) `ABC_DSP48E1(\$__ABC_DSP48E1_MULT_DPORT )
|
||||
(* abc_box_id=3002 *) `ABC_DSP48E1(\$__ABC_DSP48E1 )
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
|
||||
module \$__ABC_LUT6 (input A, input [5:0] S, output Y);
|
||||
assign Y = A;
|
||||
endmodule
|
||||
module \$__ABC_LUT7 (input A, input [6:0] S, output Y);
|
||||
assign Y = A;
|
||||
endmodule
|
||||
|
||||
module \$__ABC_REG (input [WIDTH-1:0] I, output [WIDTH-1:0] O, output Q);
|
||||
parameter WIDTH = 1;
|
||||
assign O = I;
|
||||
endmodule
|
||||
(* techmap_celltype = "$__ABC_DSP48E1_MULT_P_MUX $__ABC_DSP48E1_MULT_PCOUT_MUX $__ABC_DSP48E1_MULT_DPORT_P_MUX $__ABC_DSP48E1_MULT_DPORT_PCOUT_MUX $__ABC_DSP48E1_P_MUX $__ABC_DSP48E1_PCOUT_MUX" *)
|
||||
module \$__ABC_DSP48E1_MUX (
|
||||
input Aq, Bq, Cq, Dq, ADq,
|
||||
input [47:0] I,
|
||||
input Mq,
|
||||
input [47:0] P,
|
||||
input Pq,
|
||||
output [47:0] O
|
||||
);
|
||||
assign O = I;
|
||||
endmodule
|
||||
|
||||
(* techmap_celltype = "$__ABC_DSP48E1_MULT $__ABC_DSP48E1_MULT_DPORT $__ABC_DSP48E1" *)
|
||||
module \$__ABC_DSP48E1 (
|
||||
(* techmap_autopurge *) output [29:0] ACOUT,
|
||||
(* techmap_autopurge *) output [17:0] BCOUT,
|
||||
(* techmap_autopurge *) output reg CARRYCASCOUT,
|
||||
(* techmap_autopurge *) output reg [3:0] CARRYOUT,
|
||||
(* techmap_autopurge *) output reg MULTSIGNOUT,
|
||||
(* techmap_autopurge *) output OVERFLOW,
|
||||
(* techmap_autopurge *) output reg signed [47:0] P,
|
||||
(* techmap_autopurge *) output PATTERNBDETECT,
|
||||
(* techmap_autopurge *) output PATTERNDETECT,
|
||||
(* techmap_autopurge *) output [47:0] PCOUT,
|
||||
(* techmap_autopurge *) output UNDERFLOW,
|
||||
(* techmap_autopurge *) input signed [29:0] A,
|
||||
(* techmap_autopurge *) input [29:0] ACIN,
|
||||
(* techmap_autopurge *) input [3:0] ALUMODE,
|
||||
(* techmap_autopurge *) input signed [17:0] B,
|
||||
(* techmap_autopurge *) input [17:0] BCIN,
|
||||
(* techmap_autopurge *) input [47:0] C,
|
||||
(* techmap_autopurge *) input CARRYCASCIN,
|
||||
(* techmap_autopurge *) input CARRYIN,
|
||||
(* techmap_autopurge *) input [2:0] CARRYINSEL,
|
||||
(* techmap_autopurge *) input CEA1,
|
||||
(* techmap_autopurge *) input CEA2,
|
||||
(* techmap_autopurge *) input CEAD,
|
||||
(* techmap_autopurge *) input CEALUMODE,
|
||||
(* techmap_autopurge *) input CEB1,
|
||||
(* techmap_autopurge *) input CEB2,
|
||||
(* techmap_autopurge *) input CEC,
|
||||
(* techmap_autopurge *) input CECARRYIN,
|
||||
(* techmap_autopurge *) input CECTRL,
|
||||
(* techmap_autopurge *) input CED,
|
||||
(* techmap_autopurge *) input CEINMODE,
|
||||
(* techmap_autopurge *) input CEM,
|
||||
(* techmap_autopurge *) input CEP,
|
||||
(* techmap_autopurge *) input CLK,
|
||||
(* techmap_autopurge *) input [24:0] D,
|
||||
(* techmap_autopurge *) input [4:0] INMODE,
|
||||
(* techmap_autopurge *) input MULTSIGNIN,
|
||||
(* techmap_autopurge *) input [6:0] OPMODE,
|
||||
(* techmap_autopurge *) input [47:0] PCIN,
|
||||
(* techmap_autopurge *) input RSTA,
|
||||
(* techmap_autopurge *) input RSTALLCARRYIN,
|
||||
(* techmap_autopurge *) input RSTALUMODE,
|
||||
(* techmap_autopurge *) input RSTB,
|
||||
(* techmap_autopurge *) input RSTC,
|
||||
(* techmap_autopurge *) input RSTCTRL,
|
||||
(* techmap_autopurge *) input RSTD,
|
||||
(* techmap_autopurge *) input RSTINMODE,
|
||||
(* techmap_autopurge *) input RSTM,
|
||||
(* techmap_autopurge *) input RSTP
|
||||
);
|
||||
parameter integer ACASCREG = 1;
|
||||
parameter integer ADREG = 1;
|
||||
parameter integer ALUMODEREG = 1;
|
||||
parameter integer AREG = 1;
|
||||
parameter AUTORESET_PATDET = "NO_RESET";
|
||||
parameter A_INPUT = "DIRECT";
|
||||
parameter integer BCASCREG = 1;
|
||||
parameter integer BREG = 1;
|
||||
parameter B_INPUT = "DIRECT";
|
||||
parameter integer CARRYINREG = 1;
|
||||
parameter integer CARRYINSELREG = 1;
|
||||
parameter integer CREG = 1;
|
||||
parameter integer DREG = 1;
|
||||
parameter integer INMODEREG = 1;
|
||||
parameter integer MREG = 1;
|
||||
parameter integer OPMODEREG = 1;
|
||||
parameter integer PREG = 1;
|
||||
parameter SEL_MASK = "MASK";
|
||||
parameter SEL_PATTERN = "PATTERN";
|
||||
parameter USE_DPORT = "FALSE";
|
||||
parameter USE_MULT = "MULTIPLY";
|
||||
parameter USE_PATTERN_DETECT = "NO_PATDET";
|
||||
parameter USE_SIMD = "ONE48";
|
||||
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
|
||||
parameter [47:0] PATTERN = 48'h000000000000;
|
||||
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
|
||||
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
|
||||
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
|
||||
|
||||
DSP48E1 #(
|
||||
.ACASCREG(ACASCREG),
|
||||
.ADREG(ADREG),
|
||||
.ALUMODEREG(ALUMODEREG),
|
||||
.AREG(AREG),
|
||||
.AUTORESET_PATDET(AUTORESET_PATDET),
|
||||
.A_INPUT(A_INPUT),
|
||||
.BCASCREG(BCASCREG),
|
||||
.BREG(BREG),
|
||||
.B_INPUT(B_INPUT),
|
||||
.CARRYINREG(CARRYINREG),
|
||||
.CARRYINSELREG(CARRYINSELREG),
|
||||
.CREG(CREG),
|
||||
.DREG(DREG),
|
||||
.INMODEREG(INMODEREG),
|
||||
.MREG(MREG),
|
||||
.OPMODEREG(OPMODEREG),
|
||||
.PREG(PREG),
|
||||
.SEL_MASK(SEL_MASK),
|
||||
.SEL_PATTERN(SEL_PATTERN),
|
||||
.USE_DPORT(USE_DPORT),
|
||||
.USE_MULT(USE_MULT),
|
||||
.USE_PATTERN_DETECT(USE_PATTERN_DETECT),
|
||||
.USE_SIMD(USE_SIMD),
|
||||
.MASK(MASK),
|
||||
.PATTERN(PATTERN),
|
||||
.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED),
|
||||
.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED),
|
||||
.IS_CLK_INVERTED(IS_CLK_INVERTED),
|
||||
.IS_INMODE_INVERTED(IS_INMODE_INVERTED),
|
||||
.IS_OPMODE_INVERTED(IS_OPMODE_INVERTED)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.ACOUT(ACOUT),
|
||||
.BCOUT(BCOUT),
|
||||
.CARRYCASCOUT(CARRYCASCOUT),
|
||||
.CARRYOUT(CARRYOUT),
|
||||
.MULTSIGNOUT(MULTSIGNOUT),
|
||||
.OVERFLOW(OVERFLOW),
|
||||
.P(P),
|
||||
.PATTERNBDETECT(PATTERNBDETECT),
|
||||
.PATTERNDETECT(PATTERNDETECT),
|
||||
.PCOUT(PCOUT),
|
||||
.UNDERFLOW(UNDERFLOW),
|
||||
.A(A),
|
||||
.ACIN(ACIN),
|
||||
.ALUMODE(ALUMODE),
|
||||
.B(B),
|
||||
.BCIN(BCIN),
|
||||
.C(C),
|
||||
.CARRYCASCIN(CARRYCASCIN),
|
||||
.CARRYIN(CARRYIN),
|
||||
.CARRYINSEL(CARRYINSEL),
|
||||
.CEA1(CEA1),
|
||||
.CEA2(CEA2),
|
||||
.CEAD(CEAD),
|
||||
.CEALUMODE(CEALUMODE),
|
||||
.CEB1(CEB1),
|
||||
.CEB2(CEB2),
|
||||
.CEC(CEC),
|
||||
.CECARRYIN(CECARRYIN),
|
||||
.CECTRL(CECTRL),
|
||||
.CED(CED),
|
||||
.CEINMODE(CEINMODE),
|
||||
.CEM(CEM),
|
||||
.CEP(CEP),
|
||||
.CLK(CLK),
|
||||
.D(D),
|
||||
.INMODE(INMODE),
|
||||
.MULTSIGNIN(MULTSIGNIN),
|
||||
.OPMODE(OPMODE),
|
||||
.PCIN(PCIN),
|
||||
.RSTA(RSTA),
|
||||
.RSTALLCARRYIN(RSTALLCARRYIN),
|
||||
.RSTALUMODE(RSTALUMODE),
|
||||
.RSTB(RSTB),
|
||||
.RSTC(RSTC),
|
||||
.RSTCTRL(RSTCTRL),
|
||||
.RSTD(RSTD),
|
||||
.RSTINMODE(RSTINMODE),
|
||||
.RSTM(RSTM),
|
||||
.RSTP(RSTP)
|
||||
);
|
||||
endmodule
|
File diff suppressed because it is too large
Load Diff
|
@ -331,7 +331,6 @@ module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y)
|
|||
endmodule
|
||||
`endif
|
||||
|
||||
`ifndef _ABC
|
||||
module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
|
||||
output O;
|
||||
input I0, I1, I2, I3, S0, S1;
|
||||
|
@ -364,4 +363,3 @@ module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
|
|||
else
|
||||
MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O));
|
||||
endmodule
|
||||
`endif
|
||||
|
|
|
@ -60,9 +60,18 @@ module BUFGCTRL(
|
|||
(* clkbuf_driver *)
|
||||
output O,
|
||||
input I0, input I1,
|
||||
input S0, input S1,
|
||||
input CE0, input CE1,
|
||||
input IGNORE0, input IGNORE1);
|
||||
(* invertible_pin = "IS_S0_INVERTED" *)
|
||||
input S0,
|
||||
(* invertible_pin = "IS_S1_INVERTED" *)
|
||||
input S1,
|
||||
(* invertible_pin = "IS_CE0_INVERTED" *)
|
||||
input CE0,
|
||||
(* invertible_pin = "IS_CE1_INVERTED" *)
|
||||
input CE1,
|
||||
(* invertible_pin = "IS_IGNORE0_INVERTED" *)
|
||||
input IGNORE0,
|
||||
(* invertible_pin = "IS_IGNORE1_INVERTED" *)
|
||||
input IGNORE1);
|
||||
|
||||
parameter [0:0] INIT_OUT = 1'b0;
|
||||
parameter PRESELECT_I0 = "FALSE";
|
||||
|
@ -87,6 +96,7 @@ module BUFHCE(
|
|||
(* clkbuf_driver *)
|
||||
output O,
|
||||
input I,
|
||||
(* invertible_pin = "IS_CE_INVERTED" *)
|
||||
input CE);
|
||||
|
||||
parameter [0:0] INIT_OUT = 1'b0;
|
||||
|
@ -184,14 +194,6 @@ module MUXF8(output O, input I0, I1, S);
|
|||
assign O = S ? I1 : I0;
|
||||
endmodule
|
||||
|
||||
`ifdef _ABC
|
||||
(* abc_box_id = 3, lib_whitebox *)
|
||||
module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
|
||||
assign O = S1 ? (S0 ? I3 : I2)
|
||||
: (S0 ? I1 : I0);
|
||||
endmodule
|
||||
`endif
|
||||
|
||||
module XORCY(output O, input CI, LI);
|
||||
assign O = CI ^ LI;
|
||||
endmodule
|
||||
|
@ -236,7 +238,20 @@ endmodule
|
|||
|
||||
`endif
|
||||
|
||||
module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250
|
||||
|
||||
module FDRE (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_C_INVERTED" *)
|
||||
input C,
|
||||
input CE,
|
||||
(* invertible_pin = "IS_D_INVERTED" *)
|
||||
input D,
|
||||
(* invertible_pin = "IS_R_INVERTED" *)
|
||||
input R
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_C_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_D_INVERTED = 1'b0;
|
||||
|
@ -248,7 +263,18 @@ module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
|
|||
endcase endgenerate
|
||||
endmodule
|
||||
|
||||
module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
|
||||
module FDSE (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_C_INVERTED" *)
|
||||
input C,
|
||||
input CE,
|
||||
(* invertible_pin = "IS_D_INVERTED" *)
|
||||
input D,
|
||||
(* invertible_pin = "IS_S_INVERTED" *)
|
||||
input S
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_C_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_D_INVERTED = 1'b0;
|
||||
|
@ -260,7 +286,18 @@ module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
|
|||
endcase endgenerate
|
||||
endmodule
|
||||
|
||||
module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
|
||||
module FDCE (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_C_INVERTED" *)
|
||||
input C,
|
||||
input CE,
|
||||
(* invertible_pin = "IS_D_INVERTED" *)
|
||||
input D,
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_C_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_D_INVERTED = 1'b0;
|
||||
|
@ -274,7 +311,18 @@ module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
|
|||
endcase endgenerate
|
||||
endmodule
|
||||
|
||||
module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
|
||||
module FDPE (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_C_INVERTED" *)
|
||||
input C,
|
||||
input CE,
|
||||
(* invertible_pin = "IS_D_INVERTED" *)
|
||||
input D,
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_C_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_D_INVERTED = 1'b0;
|
||||
|
@ -288,38 +336,106 @@ module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
|
|||
endcase endgenerate
|
||||
endmodule
|
||||
|
||||
module FDRE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
|
||||
module FDRE_1 (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
input C,
|
||||
input CE, D, R
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
initial Q <= INIT;
|
||||
always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D;
|
||||
endmodule
|
||||
|
||||
module FDSE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
|
||||
module FDSE_1 (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
input C,
|
||||
input CE, D, S
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
initial Q <= INIT;
|
||||
always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D;
|
||||
endmodule
|
||||
|
||||
module FDCE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
|
||||
module FDCE_1 (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
input C,
|
||||
input CE, D, CLR
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
initial Q <= INIT;
|
||||
always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D;
|
||||
endmodule
|
||||
|
||||
module FDPE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
|
||||
module FDPE_1 (
|
||||
(* abc_arrival=303 *)
|
||||
output reg Q,
|
||||
(* clkbuf_sink *)
|
||||
input C,
|
||||
input CE, D, PRE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
initial Q <= INIT;
|
||||
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
|
||||
endmodule
|
||||
|
||||
(* abc_box_id = 5 *)
|
||||
module LDCE (
|
||||
output reg Q,
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR,
|
||||
input D,
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G,
|
||||
input GE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
initial Q = INIT;
|
||||
wire clr = CLR ^ IS_CLR_INVERTED;
|
||||
wire g = G ^ IS_G_INVERTED;
|
||||
always @*
|
||||
if (clr) Q = 1'b0;
|
||||
else if (GE && g) Q = D;
|
||||
endmodule
|
||||
|
||||
module LDPE (
|
||||
output reg Q,
|
||||
input D,
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G,
|
||||
input GE,
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
initial Q = INIT;
|
||||
wire g = G ^ IS_G_INVERTED;
|
||||
wire pre = PRE ^ IS_PRE_INVERTED;
|
||||
always @*
|
||||
if (pre) Q = 1'b1;
|
||||
else if (GE && g) Q = D;
|
||||
endmodule
|
||||
|
||||
module RAM32X1D (
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
|
||||
(* abc_arrival=1153 *)
|
||||
output DPO, SPO,
|
||||
(* abc_scc_break *)
|
||||
input D,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_WCLK_INVERTED" *)
|
||||
input WCLK,
|
||||
(* abc_scc_break *)
|
||||
input WE,
|
||||
input A0, A1, A2, A3, A4,
|
||||
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
|
||||
|
@ -335,14 +451,14 @@ module RAM32X1D (
|
|||
always @(posedge clk) if (WE) mem[a] <= D;
|
||||
endmodule
|
||||
|
||||
(* abc_box_id = 6 *)
|
||||
module RAM64X1D (
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
|
||||
(* abc_arrival=1153 *)
|
||||
output DPO, SPO,
|
||||
(* abc_scc_break *)
|
||||
input D,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_WCLK_INVERTED" *)
|
||||
input WCLK,
|
||||
(* abc_scc_break *)
|
||||
input WE,
|
||||
input A0, A1, A2, A3, A4, A5,
|
||||
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
|
||||
|
@ -358,14 +474,14 @@ module RAM64X1D (
|
|||
always @(posedge clk) if (WE) mem[a] <= D;
|
||||
endmodule
|
||||
|
||||
(* abc_box_id = 7 *)
|
||||
module RAM128X1D (
|
||||
output DPO, SPO,
|
||||
(* abc_scc_break *)
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
|
||||
(* abc_arrival=1153 *)
|
||||
output DPO, SPO,
|
||||
input D,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_WCLK_INVERTED" *)
|
||||
input WCLK,
|
||||
(* abc_scc_break *)
|
||||
input WE,
|
||||
input [6:0] A, DPRA
|
||||
);
|
||||
|
@ -379,9 +495,12 @@ module RAM128X1D (
|
|||
endmodule
|
||||
|
||||
module SRL16E (
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905
|
||||
(* abc_arrival=1472 *)
|
||||
output Q,
|
||||
input A0, A1, A2, A3, CE,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLK_INVERTED" *)
|
||||
input CLK,
|
||||
input D
|
||||
);
|
||||
|
@ -404,6 +523,7 @@ module SRLC16E (
|
|||
output Q15,
|
||||
input A0, A1, A2, A3, CE,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLK_INVERTED" *)
|
||||
input CLK,
|
||||
input D
|
||||
);
|
||||
|
@ -423,11 +543,15 @@ module SRLC16E (
|
|||
endmodule
|
||||
|
||||
module SRLC32E (
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905
|
||||
(* abc_arrival=1472 *)
|
||||
output Q,
|
||||
(* abc_arrival=1114 *)
|
||||
output Q31,
|
||||
input [4:0] A,
|
||||
input CE,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLK_INVERTED" *)
|
||||
input CLK,
|
||||
input D
|
||||
);
|
||||
|
@ -445,3 +569,466 @@ module SRLC32E (
|
|||
always @(posedge CLK) if (CE) r <= { r[30:0], D };
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
module DSP48E1 (
|
||||
output [29:0] ACOUT,
|
||||
output [17:0] BCOUT,
|
||||
output reg CARRYCASCOUT,
|
||||
output reg [3:0] CARRYOUT,
|
||||
output reg MULTSIGNOUT,
|
||||
output OVERFLOW,
|
||||
output reg signed [47:0] P,
|
||||
output reg PATTERNBDETECT,
|
||||
output reg PATTERNDETECT,
|
||||
output [47:0] PCOUT,
|
||||
output UNDERFLOW,
|
||||
input signed [29:0] A,
|
||||
input [29:0] ACIN,
|
||||
input [3:0] ALUMODE,
|
||||
input signed [17:0] B,
|
||||
input [17:0] BCIN,
|
||||
input [47:0] C,
|
||||
input CARRYCASCIN,
|
||||
input CARRYIN,
|
||||
input [2:0] CARRYINSEL,
|
||||
input CEA1,
|
||||
input CEA2,
|
||||
input CEAD,
|
||||
input CEALUMODE,
|
||||
input CEB1,
|
||||
input CEB2,
|
||||
input CEC,
|
||||
input CECARRYIN,
|
||||
input CECTRL,
|
||||
input CED,
|
||||
input CEINMODE,
|
||||
input CEM,
|
||||
input CEP,
|
||||
(* clkbuf_sink *) input CLK,
|
||||
input [24:0] D,
|
||||
input [4:0] INMODE,
|
||||
input MULTSIGNIN,
|
||||
input [6:0] OPMODE,
|
||||
input [47:0] PCIN,
|
||||
input RSTA,
|
||||
input RSTALLCARRYIN,
|
||||
input RSTALUMODE,
|
||||
input RSTB,
|
||||
input RSTC,
|
||||
input RSTCTRL,
|
||||
input RSTD,
|
||||
input RSTINMODE,
|
||||
input RSTM,
|
||||
input RSTP
|
||||
);
|
||||
parameter integer ACASCREG = 1;
|
||||
parameter integer ADREG = 1;
|
||||
parameter integer ALUMODEREG = 1;
|
||||
parameter integer AREG = 1;
|
||||
parameter AUTORESET_PATDET = "NO_RESET";
|
||||
parameter A_INPUT = "DIRECT";
|
||||
parameter integer BCASCREG = 1;
|
||||
parameter integer BREG = 1;
|
||||
parameter B_INPUT = "DIRECT";
|
||||
parameter integer CARRYINREG = 1;
|
||||
parameter integer CARRYINSELREG = 1;
|
||||
parameter integer CREG = 1;
|
||||
parameter integer DREG = 1;
|
||||
parameter integer INMODEREG = 1;
|
||||
parameter integer MREG = 1;
|
||||
parameter integer OPMODEREG = 1;
|
||||
parameter integer PREG = 1;
|
||||
parameter SEL_MASK = "MASK";
|
||||
parameter SEL_PATTERN = "PATTERN";
|
||||
parameter USE_DPORT = "FALSE";
|
||||
parameter USE_MULT = "MULTIPLY";
|
||||
parameter USE_PATTERN_DETECT = "NO_PATDET";
|
||||
parameter USE_SIMD = "ONE48";
|
||||
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
|
||||
parameter [47:0] PATTERN = 48'h000000000000;
|
||||
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
|
||||
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
|
||||
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
|
||||
|
||||
initial begin
|
||||
`ifdef __ICARUS__
|
||||
if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value");
|
||||
if (SEL_MASK != "MASK") $fatal(1, "Unsupported SEL_MASK value");
|
||||
if (SEL_PATTERN != "PATTERN") $fatal(1, "Unsupported SEL_PATTERN value");
|
||||
if (USE_SIMD != "ONE48" && USE_SIMD != "TWO24" && USE_SIMD != "FOUR12") $fatal(1, "Unsupported USE_SIMD value");
|
||||
if (IS_ALUMODE_INVERTED != 4'b0) $fatal(1, "Unsupported IS_ALUMODE_INVERTED value");
|
||||
if (IS_CARRYIN_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CARRYIN_INVERTED value");
|
||||
if (IS_CLK_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CLK_INVERTED value");
|
||||
if (IS_INMODE_INVERTED != 5'b0) $fatal(1, "Unsupported IS_INMODE_INVERTED value");
|
||||
if (IS_OPMODE_INVERTED != 7'b0) $fatal(1, "Unsupported IS_OPMODE_INVERTED value");
|
||||
`endif
|
||||
end
|
||||
|
||||
wire signed [29:0] A_muxed;
|
||||
wire signed [17:0] B_muxed;
|
||||
|
||||
generate
|
||||
if (A_INPUT == "CASCADE") assign A_muxed = ACIN;
|
||||
else assign A_muxed = A;
|
||||
|
||||
if (B_INPUT == "CASCADE") assign B_muxed = BCIN;
|
||||
else assign B_muxed = B;
|
||||
endgenerate
|
||||
|
||||
reg signed [29:0] Ar1, Ar2;
|
||||
reg signed [24:0] Dr;
|
||||
reg signed [17:0] Br1, Br2;
|
||||
reg signed [47:0] Cr;
|
||||
reg [4:0] INMODEr = 5'b0;
|
||||
reg [6:0] OPMODEr = 7'b0;
|
||||
reg [3:0] ALUMODEr = 4'b0;
|
||||
reg [2:0] CARRYINSELr = 3'b0;
|
||||
|
||||
generate
|
||||
// Configurable A register
|
||||
if (AREG == 2) begin
|
||||
initial Ar1 = 30'b0;
|
||||
initial Ar2 = 30'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTA) begin
|
||||
Ar1 <= 30'b0;
|
||||
Ar2 <= 30'b0;
|
||||
end else begin
|
||||
if (CEA1) Ar1 <= A_muxed;
|
||||
if (CEA2) Ar2 <= Ar1;
|
||||
end
|
||||
end else if (AREG == 1) begin
|
||||
//initial Ar1 = 30'b0;
|
||||
initial Ar2 = 30'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTA) begin
|
||||
Ar1 <= 30'b0;
|
||||
Ar2 <= 30'b0;
|
||||
end else begin
|
||||
if (CEA1) Ar1 <= A_muxed;
|
||||
if (CEA2) Ar2 <= A_muxed;
|
||||
end
|
||||
end else begin
|
||||
always @* Ar1 <= A_muxed;
|
||||
always @* Ar2 <= A_muxed;
|
||||
end
|
||||
|
||||
// Configurable B register
|
||||
if (BREG == 2) begin
|
||||
initial Br1 = 25'b0;
|
||||
initial Br2 = 25'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTB) begin
|
||||
Br1 <= 18'b0;
|
||||
Br2 <= 18'b0;
|
||||
end else begin
|
||||
if (CEB1) Br1 <= B_muxed;
|
||||
if (CEB2) Br2 <= Br1;
|
||||
end
|
||||
end else if (BREG == 1) begin
|
||||
//initial Br1 = 25'b0;
|
||||
initial Br2 = 25'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTB) begin
|
||||
Br1 <= 18'b0;
|
||||
Br2 <= 18'b0;
|
||||
end else begin
|
||||
if (CEB1) Br1 <= B_muxed;
|
||||
if (CEB2) Br2 <= B_muxed;
|
||||
end
|
||||
end else begin
|
||||
always @* Br1 <= B_muxed;
|
||||
always @* Br2 <= B_muxed;
|
||||
end
|
||||
|
||||
// C and D registers
|
||||
if (CREG == 1) initial Cr = 48'b0;
|
||||
if (CREG == 1) begin always @(posedge CLK) if (RSTC) Cr <= 48'b0; else if (CEC) Cr <= C; end
|
||||
else always @* Cr <= C;
|
||||
|
||||
if (CREG == 1) initial Dr = 25'b0;
|
||||
if (DREG == 1) begin always @(posedge CLK) if (RSTD) Dr <= 25'b0; else if (CED) Dr <= D; end
|
||||
else always @* Dr <= D;
|
||||
|
||||
// Control registers
|
||||
if (INMODEREG == 1) initial INMODEr = 5'b0;
|
||||
if (INMODEREG == 1) begin always @(posedge CLK) if (RSTINMODE) INMODEr <= 5'b0; else if (CEINMODE) INMODEr <= INMODE; end
|
||||
else always @* INMODEr <= INMODE;
|
||||
if (OPMODEREG == 1) initial OPMODEr = 7'b0;
|
||||
if (OPMODEREG == 1) begin always @(posedge CLK) if (RSTCTRL) OPMODEr <= 7'b0; else if (CECTRL) OPMODEr <= OPMODE; end
|
||||
else always @* OPMODEr <= OPMODE;
|
||||
if (ALUMODEREG == 1) initial ALUMODEr = 4'b0;
|
||||
if (ALUMODEREG == 1) begin always @(posedge CLK) if (RSTALUMODE) ALUMODEr <= 4'b0; else if (CEALUMODE) ALUMODEr <= ALUMODE; end
|
||||
else always @* ALUMODEr <= ALUMODE;
|
||||
if (CARRYINSELREG == 1) initial CARRYINSELr = 3'b0;
|
||||
if (CARRYINSELREG == 1) begin always @(posedge CLK) if (RSTCTRL) CARRYINSELr <= 3'b0; else if (CECTRL) CARRYINSELr <= CARRYINSEL; end
|
||||
else always @* CARRYINSELr <= CARRYINSEL;
|
||||
endgenerate
|
||||
|
||||
// A and B cascade
|
||||
generate
|
||||
if (ACASCREG == 1 && AREG == 2) assign ACOUT = Ar1;
|
||||
else assign ACOUT = Ar2;
|
||||
if (BCASCREG == 1 && BREG == 2) assign BCOUT = Br1;
|
||||
else assign BCOUT = Br2;
|
||||
endgenerate
|
||||
|
||||
// A/D input selection and pre-adder
|
||||
wire signed [29:0] Ar12_muxed = INMODEr[0] ? Ar1 : Ar2;
|
||||
wire signed [24:0] Ar12_gated = INMODEr[1] ? 25'b0 : Ar12_muxed;
|
||||
wire signed [24:0] Dr_gated = INMODEr[2] ? Dr : 25'b0;
|
||||
wire signed [24:0] AD_result = INMODEr[3] ? (Dr_gated - Ar12_gated) : (Dr_gated + Ar12_gated);
|
||||
reg signed [24:0] ADr;
|
||||
|
||||
generate
|
||||
if (ADREG == 1) initial ADr = 25'b0;
|
||||
if (ADREG == 1) begin always @(posedge CLK) if (RSTD) ADr <= 25'b0; else if (CEAD) ADr <= AD_result; end
|
||||
else always @* ADr <= AD_result;
|
||||
endgenerate
|
||||
|
||||
// 25x18 multiplier
|
||||
wire signed [24:0] A_MULT;
|
||||
wire signed [17:0] B_MULT = INMODEr[4] ? Br1 : Br2;
|
||||
generate
|
||||
if (USE_DPORT == "TRUE") assign A_MULT = ADr;
|
||||
else assign A_MULT = Ar12_gated;
|
||||
endgenerate
|
||||
|
||||
wire signed [42:0] M = A_MULT * B_MULT;
|
||||
wire signed [42:0] Mx = (CARRYINSEL == 3'b010) ? 43'bx : M;
|
||||
reg signed [42:0] Mr = 43'b0;
|
||||
|
||||
// Multiplier result register
|
||||
generate
|
||||
if (MREG == 1) begin always @(posedge CLK) if (RSTM) Mr <= 43'b0; else if (CEM) Mr <= Mx; end
|
||||
else always @* Mr <= Mx;
|
||||
endgenerate
|
||||
|
||||
wire signed [42:0] Mrx = (CARRYINSELr == 3'b010) ? 43'bx : Mr;
|
||||
|
||||
// X, Y and Z ALU inputs
|
||||
reg signed [47:0] X, Y, Z;
|
||||
|
||||
always @* begin
|
||||
// X multiplexer
|
||||
case (OPMODEr[1:0])
|
||||
2'b00: X = 48'b0;
|
||||
2'b01: begin X = $signed(Mrx);
|
||||
`ifdef __ICARUS__
|
||||
if (OPMODEr[3:2] != 2'b01) $fatal(1, "OPMODEr[3:2] must be 2'b01 when OPMODEr[1:0] is 2'b01");
|
||||
`endif
|
||||
end
|
||||
2'b10: begin X = P;
|
||||
`ifdef __ICARUS__
|
||||
if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[1:0] is 2'b10");
|
||||
`endif
|
||||
end
|
||||
2'b11: X = $signed({Ar2, Br2});
|
||||
default: X = 48'bx;
|
||||
endcase
|
||||
|
||||
// Y multiplexer
|
||||
case (OPMODEr[3:2])
|
||||
2'b00: Y = 48'b0;
|
||||
2'b01: begin Y = 48'b0; // FIXME: more accurate partial product modelling?
|
||||
`ifdef __ICARUS__
|
||||
if (OPMODEr[1:0] != 2'b01) $fatal(1, "OPMODEr[1:0] must be 2'b01 when OPMODEr[3:2] is 2'b01");
|
||||
`endif
|
||||
end
|
||||
2'b10: Y = {48{1'b1}};
|
||||
2'b11: Y = Cr;
|
||||
default: Y = 48'bx;
|
||||
endcase
|
||||
|
||||
// Z multiplexer
|
||||
case (OPMODEr[6:4])
|
||||
3'b000: Z = 48'b0;
|
||||
3'b001: Z = PCIN;
|
||||
3'b010: begin Z = P;
|
||||
`ifdef __ICARUS__
|
||||
if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] i0s 3'b010");
|
||||
`endif
|
||||
end
|
||||
3'b011: Z = Cr;
|
||||
3'b100: begin Z = P;
|
||||
`ifdef __ICARUS__
|
||||
if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] is 3'b100");
|
||||
if (OPMODEr[3:0] != 4'b1000) $fatal(1, "OPMODEr[3:0] must be 4'b1000 when OPMODEr[6:4] i0s 3'b100");
|
||||
`endif
|
||||
end
|
||||
3'b101: Z = $signed(PCIN[47:17]);
|
||||
3'b110: Z = $signed(P[47:17]);
|
||||
default: Z = 48'bx;
|
||||
endcase
|
||||
end
|
||||
|
||||
// Carry in
|
||||
wire A24_xnor_B17d = A_MULT[24] ~^ B_MULT[17];
|
||||
reg CARRYINr = 1'b0, A24_xnor_B17 = 1'b0;
|
||||
generate
|
||||
if (CARRYINREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) CARRYINr <= 1'b0; else if (CECARRYIN) CARRYINr <= CARRYIN; end
|
||||
else always @* CARRYINr = CARRYIN;
|
||||
|
||||
if (MREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) A24_xnor_B17 <= 1'b0; else if (CEM) A24_xnor_B17 <= A24_xnor_B17d; end
|
||||
else always @* A24_xnor_B17 = A24_xnor_B17d;
|
||||
endgenerate
|
||||
|
||||
reg cin_muxed;
|
||||
|
||||
always @(*) begin
|
||||
case (CARRYINSELr)
|
||||
3'b000: cin_muxed = CARRYINr;
|
||||
3'b001: cin_muxed = ~PCIN[47];
|
||||
3'b010: cin_muxed = CARRYCASCIN;
|
||||
3'b011: cin_muxed = PCIN[47];
|
||||
3'b100: cin_muxed = CARRYCASCOUT;
|
||||
3'b101: cin_muxed = ~P[47];
|
||||
3'b110: cin_muxed = A24_xnor_B17;
|
||||
3'b111: cin_muxed = P[47];
|
||||
default: cin_muxed = 1'bx;
|
||||
endcase
|
||||
end
|
||||
|
||||
wire alu_cin = (ALUMODEr[3] || ALUMODEr[2]) ? 1'b0 : cin_muxed;
|
||||
|
||||
// ALU core
|
||||
wire [47:0] Z_muxinv = ALUMODEr[0] ? ~Z : Z;
|
||||
wire [47:0] xor_xyz = X ^ Y ^ Z_muxinv;
|
||||
wire [47:0] maj_xyz = (X & Y) | (X & Z_muxinv) | (Y & Z_muxinv);
|
||||
|
||||
wire [47:0] xor_xyz_muxed = ALUMODEr[3] ? maj_xyz : xor_xyz;
|
||||
wire [47:0] maj_xyz_gated = ALUMODEr[2] ? 48'b0 : maj_xyz;
|
||||
|
||||
wire [48:0] maj_xyz_simd_gated;
|
||||
wire [3:0] int_carry_in, int_carry_out, ext_carry_out;
|
||||
wire [47:0] alu_sum;
|
||||
assign int_carry_in[0] = 1'b0;
|
||||
wire [3:0] carryout_reset;
|
||||
|
||||
generate
|
||||
if (USE_SIMD == "FOUR12") begin
|
||||
assign maj_xyz_simd_gated = {
|
||||
maj_xyz_gated[47:36],
|
||||
1'b0, maj_xyz_gated[34:24],
|
||||
1'b0, maj_xyz_gated[22:12],
|
||||
1'b0, maj_xyz_gated[10:0],
|
||||
alu_cin
|
||||
};
|
||||
assign int_carry_in[3:1] = 3'b000;
|
||||
assign ext_carry_out = {
|
||||
int_carry_out[3],
|
||||
maj_xyz_gated[35] ^ int_carry_out[2],
|
||||
maj_xyz_gated[23] ^ int_carry_out[1],
|
||||
maj_xyz_gated[11] ^ int_carry_out[0]
|
||||
};
|
||||
assign carryout_reset = 4'b0000;
|
||||
end else if (USE_SIMD == "TWO24") begin
|
||||
assign maj_xyz_simd_gated = {
|
||||
maj_xyz_gated[47:24],
|
||||
1'b0, maj_xyz_gated[22:0],
|
||||
alu_cin
|
||||
};
|
||||
assign int_carry_in[3:1] = {int_carry_out[2], 1'b0, int_carry_out[0]};
|
||||
assign ext_carry_out = {
|
||||
int_carry_out[3],
|
||||
1'bx,
|
||||
maj_xyz_gated[23] ^ int_carry_out[1],
|
||||
1'bx
|
||||
};
|
||||
assign carryout_reset = 4'b0x0x;
|
||||
end else begin
|
||||
assign maj_xyz_simd_gated = {maj_xyz_gated, alu_cin};
|
||||
assign int_carry_in[3:1] = int_carry_out[2:0];
|
||||
assign ext_carry_out = {
|
||||
int_carry_out[3],
|
||||
3'bxxx
|
||||
};
|
||||
assign carryout_reset = 4'b0xxx;
|
||||
end
|
||||
|
||||
genvar i;
|
||||
for (i = 0; i < 4; i = i + 1)
|
||||
assign {int_carry_out[i], alu_sum[i*12 +: 12]} = {1'b0, maj_xyz_simd_gated[i*12 +: ((i == 3) ? 13 : 12)]}
|
||||
+ xor_xyz_muxed[i*12 +: 12] + int_carry_in[i];
|
||||
endgenerate
|
||||
|
||||
wire signed [47:0] Pd = ALUMODEr[1] ? ~alu_sum : alu_sum;
|
||||
wire [3:0] CARRYOUTd = (OPMODEr[3:0] == 4'b0101 || ALUMODEr[3:2] != 2'b00) ? 4'bxxxx :
|
||||
((ALUMODEr[0] & ALUMODEr[1]) ? ~ext_carry_out : ext_carry_out);
|
||||
wire CARRYCASCOUTd = ext_carry_out[3];
|
||||
wire MULTSIGNOUTd = Mrx[42];
|
||||
|
||||
generate
|
||||
if (PREG == 1) begin
|
||||
initial P = 48'b0;
|
||||
initial CARRYOUT = carryout_reset;
|
||||
initial CARRYCASCOUT = 1'b0;
|
||||
initial MULTSIGNOUT = 1'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTP) begin
|
||||
P <= 48'b0;
|
||||
CARRYOUT <= carryout_reset;
|
||||
CARRYCASCOUT <= 1'b0;
|
||||
MULTSIGNOUT <= 1'b0;
|
||||
end else if (CEP) begin
|
||||
P <= Pd;
|
||||
CARRYOUT <= CARRYOUTd;
|
||||
CARRYCASCOUT <= CARRYCASCOUTd;
|
||||
MULTSIGNOUT <= MULTSIGNOUTd;
|
||||
end
|
||||
end else begin
|
||||
always @* begin
|
||||
P = Pd;
|
||||
CARRYOUT = CARRYOUTd;
|
||||
CARRYCASCOUT = CARRYCASCOUTd;
|
||||
MULTSIGNOUT = MULTSIGNOUTd;
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign PCOUT = P;
|
||||
|
||||
generate
|
||||
wire PATTERNDETECTd, PATTERNBDETECTd;
|
||||
|
||||
if (USE_PATTERN_DETECT == "PATDET") begin
|
||||
// TODO: Support SEL_PATTERN != "PATTERN" and SEL_MASK != "MASK
|
||||
assign PATTERNDETECTd = &(~(Pd ^ PATTERN) | MASK);
|
||||
assign PATTERNBDETECTd = &((Pd ^ PATTERN) | MASK);
|
||||
end else begin
|
||||
assign PATTERNDETECTd = 1'b1;
|
||||
assign PATTERNBDETECTd = 1'b1;
|
||||
end
|
||||
|
||||
if (PREG == 1) begin
|
||||
reg PATTERNDETECTPAST, PATTERNBDETECTPAST;
|
||||
initial PATTERNDETECT = 1'b0;
|
||||
initial PATTERNBDETECT = 1'b0;
|
||||
initial PATTERNDETECTPAST = 1'b0;
|
||||
initial PATTERNBDETECTPAST = 1'b0;
|
||||
always @(posedge CLK)
|
||||
if (RSTP) begin
|
||||
PATTERNDETECT <= 1'b0;
|
||||
PATTERNBDETECT <= 1'b0;
|
||||
PATTERNDETECTPAST <= 1'b0;
|
||||
PATTERNBDETECTPAST <= 1'b0;
|
||||
end else if (CEP) begin
|
||||
PATTERNDETECT <= PATTERNDETECTd;
|
||||
PATTERNBDETECT <= PATTERNBDETECTd;
|
||||
PATTERNDETECTPAST <= PATTERNDETECT;
|
||||
PATTERNBDETECTPAST <= PATTERNBDETECT;
|
||||
end
|
||||
assign OVERFLOW = &{PATTERNDETECTPAST, ~PATTERNBDETECT, ~PATTERNDETECT};
|
||||
assign UNDERFLOW = &{PATTERNBDETECTPAST, ~PATTERNBDETECT, ~PATTERNDETECT};
|
||||
end else begin
|
||||
always @* begin
|
||||
PATTERNDETECT = PATTERNDETECTd;
|
||||
PATTERNBDETECT = PATTERNBDETECTd;
|
||||
end
|
||||
assign OVERFLOW = 1'bx, UNDERFLOW = 1'bx;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -5,6 +5,7 @@ from io import StringIO
|
|||
from enum import Enum, auto
|
||||
import os.path
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
class Cell:
|
||||
|
@ -14,9 +15,258 @@ class Cell:
|
|||
self.port_attrs = port_attrs
|
||||
|
||||
|
||||
CELLS = [
|
||||
# Design elements types listed in Xilinx UG953
|
||||
Cell('BSCANE2', keep=True),
|
||||
XC6S_CELLS = [
|
||||
# Design elements types listed in Xilinx UG615.
|
||||
|
||||
# Advanced.
|
||||
Cell('MCB'),
|
||||
Cell('PCIE_A1'),
|
||||
|
||||
# Arithmetic functions.
|
||||
Cell('DSP48A1', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# Clock components.
|
||||
# Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFH', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFIO2', port_attrs={'IOCLK': ['clkbuf_driver'], 'DIVCLK': ['clkbuf_driver']}),
|
||||
Cell('BUFIO2_2CLK', port_attrs={'IOCLK': ['clkbuf_driver'], 'DIVCLK': ['clkbuf_driver']}),
|
||||
Cell('BUFIO2FB', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFPLL_MCB', port_attrs={'IOCLK0': ['clkbuf_driver'], 'IOCLK1': ['clkbuf_driver']}),
|
||||
Cell('DCM_CLKGEN'),
|
||||
Cell('DCM_SP'),
|
||||
Cell('PLL_BASE'),
|
||||
|
||||
# Config/BSCAN components.
|
||||
Cell('BSCAN_SPARTAN6', keep=True),
|
||||
Cell('DNA_PORT'),
|
||||
Cell('ICAP_SPARTAN6', keep=True),
|
||||
Cell('POST_CRC_INTERNAL'),
|
||||
Cell('STARTUP_SPARTAN6', keep=True),
|
||||
Cell('SUSPEND_SYNC', keep=True),
|
||||
|
||||
# I/O components.
|
||||
Cell('GTPA1_DUAL'),
|
||||
# Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IODELAY2', port_attrs={'IOCLK0': ['clkbuf_sink'], 'IOCLK1': ['clkbuf_sink'], 'CLK': ['clkbuf_sink']}),
|
||||
Cell('IODRP2', port_attrs={'IOCLK0': ['clkbuf_sink'], 'IOCLK1': ['clkbuf_sink'], 'CLK': ['clkbuf_sink']}),
|
||||
Cell('IODRP2_MCB', port_attrs={'IOCLK0': ['clkbuf_sink'], 'IOCLK1': ['clkbuf_sink'], 'CLK': ['clkbuf_sink']}),
|
||||
Cell('ISERDES2', port_attrs={
|
||||
'CLK0': ['clkbuf_sink'],
|
||||
'CLK1': ['clkbuf_sink'],
|
||||
'CLKDIV': ['clkbuf_sink'],
|
||||
}),
|
||||
Cell('KEEPER'),
|
||||
# Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OSERDES2', port_attrs={
|
||||
'CLK0': ['clkbuf_sink'],
|
||||
'CLK1': ['clkbuf_sink'],
|
||||
'CLKDIV': ['clkbuf_sink'],
|
||||
}),
|
||||
Cell('PULLDOWN'),
|
||||
Cell('PULLUP'),
|
||||
|
||||
# RAM/ROM.
|
||||
#Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM32X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('RAM64X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB8BWER', port_attrs={'CLKAWRCLK': ['clkbuf_sink'], 'CLKBRDCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB16BWER', port_attrs={'CLKA': ['clkbuf_sink'], 'CLKB': ['clkbuf_sink']}),
|
||||
Cell('ROM128X1'),
|
||||
Cell('ROM256X1'),
|
||||
Cell('ROM32X1'),
|
||||
Cell('ROM64X1'),
|
||||
|
||||
# Registers/latches.
|
||||
# Cell('FDCE'),
|
||||
# Cell('FDPE'),
|
||||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('IDDR2', port_attrs={'C0': ['clkbuf_sink'], 'C1': ['clkbuf_sink']}),
|
||||
# Cell('LDCE'),
|
||||
# Cell('LDPE'),
|
||||
Cell('ODDR2', port_attrs={'C0': ['clkbuf_sink'], 'C1': ['clkbuf_sink']}),
|
||||
|
||||
# Slice/CLB primitives.
|
||||
# Cell('CARRY4'),
|
||||
Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('LUT1'),
|
||||
# Cell('LUT2'),
|
||||
# Cell('LUT3'),
|
||||
# Cell('LUT4'),
|
||||
# Cell('LUT5'),
|
||||
# Cell('LUT6'),
|
||||
# Cell('LUT6_2'),
|
||||
# Cell('MUXF7'),
|
||||
# Cell('MUXF8'),
|
||||
# Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
]
|
||||
|
||||
|
||||
XC6V_CELLS = [
|
||||
# Design elements types listed in Xilinx UG623.
|
||||
|
||||
# Advanced.
|
||||
Cell('PCIE_2_0'),
|
||||
Cell('SYSMON'),
|
||||
|
||||
# Arithmetic functions.
|
||||
Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# Clock components.
|
||||
# Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
#Cell('BUFGCTRL', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX_CTRL', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFH', port_attrs={'O': ['clkbuf_driver']}),
|
||||
#Cell('BUFHCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFIO', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFIODQS', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFR', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('IBUFDS_GTXE1', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('MMCM_ADV'),
|
||||
Cell('MMCM_BASE'),
|
||||
|
||||
# Config/BSCAN components.
|
||||
Cell('BSCAN_VIRTEX6', keep=True),
|
||||
Cell('CAPTURE_VIRTEX6', keep=True),
|
||||
Cell('DNA_PORT'),
|
||||
Cell('EFUSE_USR'),
|
||||
Cell('FRAME_ECC_VIRTEX6'),
|
||||
Cell('ICAP_VIRTEX6', keep=True),
|
||||
Cell('STARTUP_VIRTEX6', keep=True),
|
||||
Cell('USR_ACCESS_VIRTEX6'),
|
||||
|
||||
# I/O components.
|
||||
Cell('DCIRESET', keep=True),
|
||||
Cell('GTHE1_QUAD'),
|
||||
Cell('GTXE1'),
|
||||
# Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_GTHE1', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
|
||||
Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IODELAYE1', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('ISERDESE1', port_attrs={
|
||||
'CLK': ['clkbuf_sink'],
|
||||
'CLKB': ['clkbuf_sink'],
|
||||
'OCLK': ['clkbuf_sink'],
|
||||
'CLKDIV': ['clkbuf_sink'],
|
||||
}),
|
||||
Cell('KEEPER'),
|
||||
# Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OSERDESE1', port_attrs={'CLK': ['clkbuf_sink'], 'CLKDIV': ['clkbuf_sink']}),
|
||||
Cell('PULLDOWN'),
|
||||
Cell('PULLUP'),
|
||||
Cell('TEMAC_SINGLE'),
|
||||
|
||||
# RAM/ROM.
|
||||
Cell('FIFO18E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('FIFO36E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM32X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('RAM64X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB18E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB36E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
Cell('ROM128X1'),
|
||||
Cell('ROM256X1'),
|
||||
Cell('ROM32X1'),
|
||||
Cell('ROM64X1'),
|
||||
|
||||
# Registers/latches.
|
||||
# Cell('FDCE'),
|
||||
# Cell('FDPE'),
|
||||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('IDDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('IDDR_2CLK', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}),
|
||||
Cell('LDCE'),
|
||||
Cell('LDPE'),
|
||||
Cell('ODDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
|
||||
# Slice/CLB primitives.
|
||||
# Cell('CARRY4'),
|
||||
Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('LUT1'),
|
||||
# Cell('LUT2'),
|
||||
# Cell('LUT3'),
|
||||
# Cell('LUT4'),
|
||||
# Cell('LUT5'),
|
||||
# Cell('LUT6'),
|
||||
# Cell('LUT6_2'),
|
||||
# Cell('MUXF7'),
|
||||
# Cell('MUXF8'),
|
||||
# Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
]
|
||||
|
||||
|
||||
XC7_CELLS = [
|
||||
# Design elements types listed in Xilinx UG953.
|
||||
|
||||
# Advanced.
|
||||
Cell('GTHE2_CHANNEL'),
|
||||
Cell('GTHE2_COMMON'),
|
||||
Cell('GTPE2_CHANNEL'),
|
||||
Cell('GTPE2_COMMON'),
|
||||
Cell('GTXE2_CHANNEL'),
|
||||
Cell('GTXE2_COMMON'),
|
||||
Cell('PCIE_2_1'),
|
||||
Cell('PCIE_3_0'),
|
||||
Cell('XADC'),
|
||||
|
||||
# Arithmetic functions.
|
||||
Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# Clock components.
|
||||
# Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
|
@ -30,26 +280,23 @@ CELLS = [
|
|||
Cell('BUFMR', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFMRCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFR', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('MMCME2_ADV'),
|
||||
Cell('MMCME2_BASE'),
|
||||
Cell('PLLE2_ADV'),
|
||||
Cell('PLLE2_BASE'),
|
||||
|
||||
# Config/BSCAN components.
|
||||
Cell('BSCANE2', keep=True),
|
||||
Cell('CAPTUREE2', keep=True),
|
||||
# Cell('CARRY4'),
|
||||
Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('DCIRESET', keep=True),
|
||||
Cell('DNA_PORT'),
|
||||
Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('EFUSE_USR'),
|
||||
# Cell('FDCE'),
|
||||
# Cell('FDPE'),
|
||||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('FIFO18E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('FIFO36E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('FRAME_ECCE2'),
|
||||
Cell('GTHE2_CHANNEL'),
|
||||
Cell('GTHE2_COMMON'),
|
||||
Cell('GTPE2_CHANNEL'),
|
||||
Cell('GTPE2_COMMON'),
|
||||
Cell('GTXE2_CHANNEL'),
|
||||
Cell('GTXE2_COMMON'),
|
||||
Cell('ICAPE2', keep=True),
|
||||
Cell('STARTUPE2', keep=True),
|
||||
Cell('USR_ACCESSE2'),
|
||||
|
||||
# I/O components.
|
||||
Cell('DCIRESET', keep=True),
|
||||
# Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUF_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUF_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin']}),
|
||||
|
@ -63,9 +310,6 @@ CELLS = [
|
|||
Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('ICAPE2', keep=True),
|
||||
Cell('IDDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('IDDR_2CLK', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}),
|
||||
Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
|
||||
Cell('IDELAYE2', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('IN_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
|
@ -77,6 +321,7 @@ CELLS = [
|
|||
Cell('IOBUFDS_DIFF_OUT', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DIFF_OUT_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('ISERDESE2', port_attrs={
|
||||
'CLK': ['clkbuf_sink'],
|
||||
'CLKB': ['clkbuf_sink'],
|
||||
|
@ -86,24 +331,10 @@ CELLS = [
|
|||
'CLKDIVP': ['clkbuf_sink'],
|
||||
}),
|
||||
Cell('KEEPER'),
|
||||
Cell('LDCE'),
|
||||
Cell('LDPE'),
|
||||
# Cell('LUT1'),
|
||||
# Cell('LUT2'),
|
||||
# Cell('LUT3'),
|
||||
# Cell('LUT4'),
|
||||
# Cell('LUT5'),
|
||||
# Cell('LUT6'),
|
||||
#Cell('LUT6_2'),
|
||||
Cell('MMCME2_ADV'),
|
||||
Cell('MMCME2_BASE'),
|
||||
# Cell('MUXF7'),
|
||||
# Cell('MUXF8'),
|
||||
# Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('ODDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('ODELAYE2', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('OSERDESE2', port_attrs={'CLK': ['clkbuf_sink'], 'CLKDIV': ['clkbuf_sink']}),
|
||||
Cell('OUT_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
|
@ -113,11 +344,12 @@ CELLS = [
|
|||
Cell('PHASER_OUT_PHY'),
|
||||
Cell('PHASER_REF'),
|
||||
Cell('PHY_CONTROL'),
|
||||
Cell('PLLE2_ADV'),
|
||||
Cell('PLLE2_BASE'),
|
||||
Cell('PS7', keep=True),
|
||||
Cell('PULLDOWN'),
|
||||
Cell('PULLUP'),
|
||||
|
||||
# RAM/ROM.
|
||||
Cell('FIFO18E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('FIFO36E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
|
@ -130,6 +362,7 @@ CELLS = [
|
|||
#Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('RAM64X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB18E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
# Cell('RAMB36E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
|
@ -137,13 +370,207 @@ CELLS = [
|
|||
Cell('ROM256X1'),
|
||||
Cell('ROM32X1'),
|
||||
Cell('ROM64X1'),
|
||||
#Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
#Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('STARTUPE2', keep=True),
|
||||
Cell('USR_ACCESSE2'),
|
||||
Cell('XADC'),
|
||||
|
||||
# Registers/latches.
|
||||
# Cell('FDCE'),
|
||||
# Cell('FDPE'),
|
||||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('IDDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
Cell('IDDR_2CLK', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}),
|
||||
Cell('LDCE'),
|
||||
Cell('LDPE'),
|
||||
Cell('ODDR', port_attrs={'C': ['clkbuf_sink']}),
|
||||
|
||||
# Slice/CLB primitives.
|
||||
# Cell('CARRY4'),
|
||||
Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('LUT1'),
|
||||
# Cell('LUT2'),
|
||||
# Cell('LUT3'),
|
||||
# Cell('LUT4'),
|
||||
# Cell('LUT5'),
|
||||
# Cell('LUT6'),
|
||||
# Cell('LUT6_2'),
|
||||
# Cell('MUXF7'),
|
||||
# Cell('MUXF8'),
|
||||
# Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('PS7', keep=True),
|
||||
]
|
||||
|
||||
|
||||
XCU_CELLS = [
|
||||
# Design elements types listed in Xilinx UG974.
|
||||
|
||||
# Advanced.
|
||||
Cell('CMAC'),
|
||||
Cell('CMACE4'),
|
||||
Cell('GTHE3_CHANNEL'),
|
||||
Cell('GTHE3_COMMON'),
|
||||
Cell('GTHE4_CHANNEL'),
|
||||
Cell('GTHE4_COMMON'),
|
||||
Cell('GTYE3_CHANNEL'),
|
||||
Cell('GTYE3_COMMON'),
|
||||
Cell('GTYE4_CHANNEL'),
|
||||
Cell('GTYE4_COMMON'),
|
||||
Cell('IBUFDS_GTE3', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_GTE4', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('ILKN'),
|
||||
Cell('ILKNE4'),
|
||||
Cell('OBUFDS_GTE3', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS_GTE3_ADV', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS_GTE4', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS_GTE4_ADV', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('PCIE40E4'),
|
||||
Cell('PCIE_3_1'),
|
||||
Cell('SYSMONE1'),
|
||||
Cell('SYSMONE4'),
|
||||
|
||||
# Arithmetic functions.
|
||||
Cell('DSP48E2', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# Blockram.
|
||||
Cell('FIFO18E2', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('FIFO36E2', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
|
||||
Cell('RAMB18E2', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
Cell('RAMB36E2', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
|
||||
Cell('URAM288', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('URAM288_BASE', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# CLB.
|
||||
# Cell('LUT6_2'),
|
||||
#Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM256X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32M16', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM32X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM32X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM512X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64M', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64M8', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
#Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
|
||||
Cell('AND2B1L'),
|
||||
Cell('CARRY8'),
|
||||
Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('LUT1'),
|
||||
# Cell('LUT2'),
|
||||
# Cell('LUT3'),
|
||||
# Cell('LUT4'),
|
||||
# Cell('LUT5'),
|
||||
# Cell('LUT6'),
|
||||
# Cell('MUXF7'),
|
||||
# Cell('MUXF8'),
|
||||
Cell('MUXF9'),
|
||||
Cell('OR2L'),
|
||||
# Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
# Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
|
||||
# Clock.
|
||||
# Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFG_GT', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFG_GT_SYNC'),
|
||||
Cell('BUFG_PS', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGCE_DIV', port_attrs={'O': ['clkbuf_driver']}),
|
||||
#Cell('BUFGCTRL', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX_1', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('BUFGMUX_CTRL', port_attrs={'O': ['clkbuf_driver']}),
|
||||
Cell('MMCME3_ADV'),
|
||||
Cell('MMCME3_BASE'),
|
||||
Cell('MMCME4_ADV'),
|
||||
Cell('MMCME4_BASE'),
|
||||
Cell('PLLE3_ADV'),
|
||||
Cell('PLLE3_BASE'),
|
||||
Cell('PLLE4_ADV'),
|
||||
Cell('PLLE4_BASE'),
|
||||
|
||||
# Configuration.
|
||||
Cell('BSCANE2', keep=True),
|
||||
Cell('DNA_PORTE2'),
|
||||
Cell('EFUSE_USR'),
|
||||
Cell('FRAME_ECCE3'),
|
||||
Cell('ICAPE3', keep=True),
|
||||
Cell('MASTER_JTAG', keep=True),
|
||||
Cell('STARTUPE3', keep=True),
|
||||
Cell('USR_ACCESSE2'),
|
||||
|
||||
# I/O.
|
||||
Cell('BITSLICE_CONTROL', keep=True),
|
||||
Cell('DCIRESET', keep=True),
|
||||
Cell('HPIO_VREF'),
|
||||
# XXX
|
||||
# Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUF_ANALOG', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUF_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUF_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DIFF_OUT_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_DPHY', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDS_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFDSE3', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
|
||||
Cell('IBUFE3', port_attrs={'I': ['iopad_external_pin']}),
|
||||
Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
|
||||
Cell('IDELAYE3', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUF_DCIEN', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUF_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DIFF_OUT', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DIFF_OUT_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDS_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
|
||||
Cell('IOBUFDSE3', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('IOBUFE3', port_attrs={'IO': ['iopad_external_pin']}),
|
||||
Cell('ISERDESE3', port_attrs={
|
||||
'CLK': ['clkbuf_sink'],
|
||||
'CLK_B': ['clkbuf_sink'],
|
||||
'FIFO_RD_CLK': ['clkbuf_sink'],
|
||||
'CLKDIV': ['clkbuf_sink'],
|
||||
}),
|
||||
Cell('KEEPER'),
|
||||
# Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFDS_DPHY', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}),
|
||||
Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
|
||||
Cell('ODELAYE3', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('OSERDESE3', port_attrs={'CLK': ['clkbuf_sink'], 'CLKDIV': ['clkbuf_sink']}),
|
||||
Cell('PULLDOWN'),
|
||||
Cell('PULLUP'),
|
||||
Cell('RIU_OR'),
|
||||
Cell('RX_BITSLICE'),
|
||||
Cell('RXTX_BITSLICE'),
|
||||
Cell('TX_BITSLICE'),
|
||||
Cell('TX_BITSLICE_TRI'),
|
||||
|
||||
# Registers.
|
||||
# Cell('FDCE'),
|
||||
# Cell('FDPE'),
|
||||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('HARD_SYNC', port_attrs={'CLK': ['clkbuf_sink']}),
|
||||
Cell('IDDRE1', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}),
|
||||
Cell('LDCE'),
|
||||
Cell('LDPE'),
|
||||
Cell('ODDRE1', port_attrs={'C': ['clkbuf_sink']}),
|
||||
|
||||
# NOTE: not in the official library guide!
|
||||
Cell('PS8', keep=True),
|
||||
]
|
||||
|
||||
|
||||
class State(Enum):
|
||||
OUTSIDE = auto()
|
||||
IN_MODULE = auto()
|
||||
|
@ -159,6 +586,8 @@ def xtract_cell_decl(cell, dirs, outf):
|
|||
state = State.OUTSIDE
|
||||
found = False
|
||||
# Probably the most horrible Verilog "parser" ever written.
|
||||
module_ports = []
|
||||
invertible_ports = set()
|
||||
for l in f:
|
||||
l = l.partition('//')[0]
|
||||
l = l.strip()
|
||||
|
@ -193,6 +622,15 @@ def xtract_cell_decl(cell, dirs, outf):
|
|||
state = State.IN_MODULE
|
||||
elif l == 'endmodule':
|
||||
if state == State.IN_MODULE:
|
||||
for kind, rng, port in module_ports:
|
||||
for attr in cell.port_attrs.get(port, []):
|
||||
outf.write(' (* {} *)\n'.format(attr))
|
||||
if port in invertible_ports:
|
||||
outf.write(' (* invertible_pin = "IS_{}_INVERTED" *)\n'.format(port))
|
||||
if rng is None:
|
||||
outf.write(' {} {};\n'.format(kind, port))
|
||||
else:
|
||||
outf.write(' {} {} {};\n'.format(kind, rng, port))
|
||||
outf.write(l + '\n')
|
||||
outf.write('\n')
|
||||
elif state != State.IN_OTHER_MODULE:
|
||||
|
@ -208,9 +646,11 @@ def xtract_cell_decl(cell, dirs, outf):
|
|||
kind, _, ports = l.partition(' ')
|
||||
for port in ports.split(','):
|
||||
port = port.strip()
|
||||
for attr in cell.port_attrs.get(port, []):
|
||||
outf.write(' (* {} *)\n'.format(attr))
|
||||
outf.write(' {} {};\n'.format(kind, port))
|
||||
if port.startswith('['):
|
||||
rng, port = port.split()
|
||||
else:
|
||||
rng = None
|
||||
module_ports.append((kind, rng, port))
|
||||
elif l.startswith('parameter ') and state == State.IN_MODULE:
|
||||
if 'UNPLACED' in l:
|
||||
continue
|
||||
|
@ -222,6 +662,9 @@ def xtract_cell_decl(cell, dirs, outf):
|
|||
print('Weird parameter line in {} [{}].'.format(fname, l))
|
||||
sys.exit(1)
|
||||
outf.write(' {};\n'.format(l))
|
||||
match = re.search('IS_([a-zA-Z0-9_]+)_INVERTED', l)
|
||||
if match:
|
||||
invertible_ports.add(match[1])
|
||||
if state != State.OUTSIDE:
|
||||
print('endmodule not found in {}.'.format(fname))
|
||||
sys.exit(1)
|
||||
|
@ -235,23 +678,31 @@ def xtract_cell_decl(cell, dirs, outf):
|
|||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(description='Extract Xilinx blackbox cell definitions from Vivado.')
|
||||
parser = ArgumentParser(description='Extract Xilinx blackbox cell definitions from ISE and Vivado.')
|
||||
parser.add_argument('vivado_dir', nargs='?', default='/opt/Xilinx/Vivado/2018.1')
|
||||
parser.add_argument('ise_dir', nargs='?', default='/opt/Xilinx/ISE/14.7')
|
||||
args = parser.parse_args()
|
||||
|
||||
dirs = [
|
||||
os.path.join(args.vivado_dir, 'data/verilog/src/xeclib'),
|
||||
os.path.join(args.vivado_dir, 'data/verilog/src/retarget'),
|
||||
os.path.join(args.ise_dir, 'ISE_DS/ISE/verilog/xeclib/unisims'),
|
||||
]
|
||||
for dir in dirs:
|
||||
if not os.path.isdir(dir):
|
||||
print('{} is not a directory'.format(dir))
|
||||
|
||||
out = StringIO()
|
||||
for cell in CELLS:
|
||||
xtract_cell_decl(cell, dirs, out)
|
||||
for ofile, cells in [
|
||||
('xc6s_cells_xtra.v', XC6S_CELLS),
|
||||
('xc6v_cells_xtra.v', XC6V_CELLS),
|
||||
('xc7_cells_xtra.v', XC7_CELLS),
|
||||
('xcu_cells_xtra.v', XCU_CELLS),
|
||||
]:
|
||||
out = StringIO()
|
||||
for cell in cells:
|
||||
xtract_cell_decl(cell, dirs, out)
|
||||
|
||||
with open('cells_xtra.v', 'w') as f:
|
||||
f.write('// Created by cells_xtra.py from Xilinx models\n')
|
||||
f.write('\n')
|
||||
f.write(out.getvalue())
|
||||
with open(ofile, 'w') as f:
|
||||
f.write('// Created by cells_xtra.py from Xilinx models\n')
|
||||
f.write('\n')
|
||||
f.write(out.getvalue())
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
module \$__MUL25X18 (input [24:0] A, input [17:0] B, output [42:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
wire [47:0] P_48;
|
||||
DSP48E1 #(
|
||||
// Disable all registers
|
||||
.ACASCREG(0),
|
||||
.ADREG(0),
|
||||
.A_INPUT("DIRECT"),
|
||||
.ALUMODEREG(0),
|
||||
.AREG(0),
|
||||
.BCASCREG(0),
|
||||
.B_INPUT("DIRECT"),
|
||||
.BREG(0),
|
||||
.CARRYINREG(0),
|
||||
.CARRYINSELREG(0),
|
||||
.CREG(0),
|
||||
.DREG(0),
|
||||
.INMODEREG(0),
|
||||
.MREG(0),
|
||||
.OPMODEREG(0),
|
||||
.PREG(0),
|
||||
.USE_MULT("MULTIPLY"),
|
||||
.USE_SIMD("ONE48"),
|
||||
.USE_DPORT("FALSE")
|
||||
) _TECHMAP_REPLACE_ (
|
||||
//Data path
|
||||
.A({{5{A[24]}}, A}),
|
||||
.B(B),
|
||||
.C(48'b0),
|
||||
.D(25'b0),
|
||||
.P(P_48),
|
||||
|
||||
.INMODE(5'b00000),
|
||||
.ALUMODE(4'b0000),
|
||||
.OPMODE(7'b000101),
|
||||
.CARRYINSEL(3'b000),
|
||||
|
||||
.ACIN(30'b0),
|
||||
.BCIN(18'b0),
|
||||
.PCIN(48'b0),
|
||||
.CARRYIN(1'b0)
|
||||
);
|
||||
assign Y = P_48;
|
||||
endmodule
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// FF mapping
|
||||
|
||||
`ifndef _NO_FFS
|
||||
|
||||
module \$_DFF_N_ (input D, C, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
|
||||
module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
|
||||
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
|
||||
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
|
||||
|
||||
`endif
|
||||
|
|
@ -46,7 +46,7 @@ struct SynthXilinxPass : public ScriptPass
|
|||
log(" -top <module>\n");
|
||||
log(" use the specified module as top module\n");
|
||||
log("\n");
|
||||
log(" -family {xcup|xcu|xc7|xc6s}\n");
|
||||
log(" -family {xcup|xcu|xc7|xc6v|xc6s}\n");
|
||||
log(" run synthesis for the specified Xilinx architecture\n");
|
||||
log(" generate the synthesis netlist for the specified family.\n");
|
||||
log(" default: xc7\n");
|
||||
|
@ -81,6 +81,9 @@ struct SynthXilinxPass : public ScriptPass
|
|||
log(" -nowidelut\n");
|
||||
log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n");
|
||||
log("\n");
|
||||
log(" -nodsp\n");
|
||||
log(" do not use DSP48E1s to implement multipliers and associated logic\n");
|
||||
log("\n");
|
||||
log(" -iopad\n");
|
||||
log(" enable I/O buffer insertion (selected automatically by -ise)\n");
|
||||
log("\n");
|
||||
|
@ -116,7 +119,7 @@ struct SynthXilinxPass : public ScriptPass
|
|||
}
|
||||
|
||||
std::string top_opt, edif_file, blif_file, family;
|
||||
bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9;
|
||||
bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, abc9;
|
||||
bool flatten_before_abc;
|
||||
int widemux;
|
||||
|
||||
|
@ -139,6 +142,7 @@ struct SynthXilinxPass : public ScriptPass
|
|||
nosrl = false;
|
||||
nocarry = false;
|
||||
nowidelut = false;
|
||||
nodsp = false;
|
||||
abc9 = false;
|
||||
flatten_before_abc = false;
|
||||
widemux = 0;
|
||||
|
@ -240,11 +244,15 @@ struct SynthXilinxPass : public ScriptPass
|
|||
abc9 = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodsp") {
|
||||
nodsp = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6s")
|
||||
if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6v" && family != "xc6s")
|
||||
log_cmd_error("Invalid Xilinx -family setting: '%s'.\n", family.c_str());
|
||||
|
||||
if (widemux != 0 && widemux < 2)
|
||||
|
@ -266,29 +274,46 @@ struct SynthXilinxPass : public ScriptPass
|
|||
|
||||
void script() YS_OVERRIDE
|
||||
{
|
||||
std::string ff_map_file;
|
||||
if (help_mode)
|
||||
ff_map_file = "+/xilinx/{family}_ff_map.v";
|
||||
else if (family == "xc6s")
|
||||
ff_map_file = "+/xilinx/xc6s_ff_map.v";
|
||||
else
|
||||
ff_map_file = "+/xilinx/xc7_ff_map.v";
|
||||
|
||||
if (check_label("begin")) {
|
||||
if (vpr)
|
||||
run("read_verilog -lib -icells -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
|
||||
run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
|
||||
else
|
||||
run("read_verilog -lib -icells -D _ABC +/xilinx/cells_sim.v");
|
||||
run("read_verilog -lib +/xilinx/cells_sim.v");
|
||||
|
||||
run("read_verilog -lib +/xilinx/cells_xtra.v");
|
||||
if (help_mode)
|
||||
run("read_verilog -lib +/xilinx/{family}_cells_xtra.v");
|
||||
else if (family == "xc6s")
|
||||
run("read_verilog -lib +/xilinx/xc6s_cells_xtra.v");
|
||||
else if (family == "xc6v")
|
||||
run("read_verilog -lib +/xilinx/xc6v_cells_xtra.v");
|
||||
else if (family == "xc7")
|
||||
run("read_verilog -lib +/xilinx/xc7_cells_xtra.v");
|
||||
else if (family == "xcu" || family == "xcup")
|
||||
run("read_verilog -lib +/xilinx/xcu_cells_xtra.v");
|
||||
|
||||
if (help_mode) {
|
||||
run("read_verilog -lib +/xilinx/{family}_brams_bb.v");
|
||||
} else if (family == "xc6s") {
|
||||
run("read_verilog -lib +/xilinx/xc6s_brams_bb.v");
|
||||
} else if (family == "xc7") {
|
||||
} else if (family == "xc6v" || family == "xc7") {
|
||||
run("read_verilog -lib +/xilinx/xc7_brams_bb.v");
|
||||
}
|
||||
|
||||
run(stringf("hierarchy -check %s", top_opt.c_str()));
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
if (check_label("prepare")) {
|
||||
run("proc");
|
||||
if (help_mode || flatten)
|
||||
run("flatten", "(if -flatten)");
|
||||
if (flatten || help_mode)
|
||||
run("flatten", "(with '-flatten')");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
run("check");
|
||||
|
@ -312,6 +337,26 @@ struct SynthXilinxPass : public ScriptPass
|
|||
}
|
||||
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6");
|
||||
}
|
||||
|
||||
if (check_label("map_dsp"), "(skip if '-nodsp')") {
|
||||
if (!nodsp || help_mode) {
|
||||
// NB: Xilinx multipliers are signed only
|
||||
run("techmap -map +/mul2dsp.v -map +/xilinx/dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_A_MAXWIDTH_PARTIAL=18 -D DSP_B_MAXWIDTH=18 "
|
||||
"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers
|
||||
"-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller
|
||||
"-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18");
|
||||
run("select a:mul2dsp");
|
||||
run("setattr -unset mul2dsp");
|
||||
run("opt_expr -fine");
|
||||
run("wreduce");
|
||||
run("select -clear");
|
||||
run("xilinx_dsp");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
run("alumacc");
|
||||
run("share");
|
||||
run("opt");
|
||||
|
@ -329,7 +374,7 @@ struct SynthXilinxPass : public ScriptPass
|
|||
if (family == "xc6s") {
|
||||
run("memory_bram -rules +/xilinx/xc6s_brams.txt");
|
||||
run("techmap -map +/xilinx/xc6s_brams_map.v");
|
||||
} else if (family == "xc7") {
|
||||
} else if (family == "xc6v" || family == "xc7") {
|
||||
run("memory_bram -rules +/xilinx/xc7_brams.txt");
|
||||
run("techmap -map +/xilinx/xc7_brams_map.v");
|
||||
} else {
|
||||
|
@ -408,7 +453,7 @@ struct SynthXilinxPass : public ScriptPass
|
|||
}
|
||||
|
||||
if (check_label("map_cells")) {
|
||||
std::string techmap_args = "-map +/techmap.v -D _ABC -map +/xilinx/cells_map.v";
|
||||
std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v";
|
||||
if (widemux > 0)
|
||||
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux);
|
||||
run("techmap " + techmap_args);
|
||||
|
@ -416,11 +461,9 @@ struct SynthXilinxPass : public ScriptPass
|
|||
}
|
||||
|
||||
if (check_label("map_ffs")) {
|
||||
if (abc9 || help_mode) {
|
||||
run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)");
|
||||
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
|
||||
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "('-abc9' only)");
|
||||
}
|
||||
if (abc9 || help_mode) {
|
||||
run("techmap -map " + ff_map_file, "('-abc9' only)");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_luts")) {
|
||||
|
@ -428,10 +471,12 @@ struct SynthXilinxPass : public ScriptPass
|
|||
if (flatten_before_abc)
|
||||
run("flatten");
|
||||
if (help_mode)
|
||||
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')");
|
||||
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut'; option for '-retime')");
|
||||
else if (abc9) {
|
||||
if (family != "xc7")
|
||||
log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n");
|
||||
run("techmap -map +/xilinx/abc_map.v -max_iter 1");
|
||||
run("read_verilog -icells -lib +/xilinx/abc_model.v");
|
||||
if (nowidelut)
|
||||
run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY));
|
||||
else
|
||||
|
@ -449,16 +494,14 @@ struct SynthXilinxPass : public ScriptPass
|
|||
// has performed any necessary retiming
|
||||
if (!nosrl || help_mode)
|
||||
run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
|
||||
|
||||
std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v";
|
||||
if (help_mode)
|
||||
techmap_args += " [-map +/xilinx/ff_map.v]";
|
||||
else if (!abc9)
|
||||
techmap_args += " -map +/xilinx/ff_map.v";
|
||||
techmap_args += " [-map " + ff_map_file + "]";
|
||||
else if (abc9)
|
||||
techmap_args += " -map +/xilinx/abc_unmap.v";
|
||||
else
|
||||
techmap_args += " -map " + ff_map_file;
|
||||
run("techmap " + techmap_args);
|
||||
if (!abc9)
|
||||
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
|
||||
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "(without '-abc9' only)");
|
||||
run("clean");
|
||||
}
|
||||
|
||||
|
@ -470,8 +513,10 @@ struct SynthXilinxPass : public ScriptPass
|
|||
else
|
||||
run("clkbufmap -buf BUFG O:I");
|
||||
}
|
||||
if (do_iopad)
|
||||
if (help_mode || do_iopad)
|
||||
run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I A:top", "(only if '-iopad' or '-ise' and not '-noiopad')");
|
||||
if (help_mode || ise)
|
||||
run("extractinv -inv INV O:I", "(only if '-ise')");
|
||||
}
|
||||
|
||||
if (check_label("check")) {
|
||||
|
|
|
@ -4,3 +4,8 @@ bram1_[0-9]*/
|
|||
bram2.log
|
||||
bram2_syn.v
|
||||
bram2_tb
|
||||
dsp_work*/
|
||||
test_dsp_model_ref.v
|
||||
test_dsp_model_uut.v
|
||||
test_dsp_model
|
||||
*.vcd
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v
|
||||
if [ ! -f "test_dsp_model_ref.v" ]; then
|
||||
cat /opt/Xilinx/Vivado/2019.1/data/verilog/src/unisims/DSP48E1.v > test_dsp_model_ref.v
|
||||
fi
|
||||
for tb in macc_overflow_underflow \
|
||||
simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \
|
||||
mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc \
|
||||
mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc
|
||||
do
|
||||
iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v /opt/Xilinx/Vivado/2019.1/data/verilog/src/glbl.v
|
||||
vvp -N ./test_dsp_model
|
||||
done
|
|
@ -0,0 +1,652 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module testbench;
|
||||
parameter integer ACASCREG = 1;
|
||||
parameter integer ADREG = 1;
|
||||
parameter integer ALUMODEREG = 1;
|
||||
parameter integer AREG = 1;
|
||||
parameter AUTORESET_PATDET = "NO_RESET";
|
||||
parameter A_INPUT = "DIRECT";
|
||||
parameter integer BCASCREG = 1;
|
||||
parameter integer BREG = 1;
|
||||
parameter B_INPUT = "DIRECT";
|
||||
parameter integer CARRYINREG = 1;
|
||||
parameter integer CARRYINSELREG = 1;
|
||||
parameter integer CREG = 1;
|
||||
parameter integer DREG = 1;
|
||||
parameter integer INMODEREG = 1;
|
||||
parameter integer MREG = 1;
|
||||
parameter integer OPMODEREG = 1;
|
||||
parameter integer PREG = 1;
|
||||
parameter SEL_MASK = "MASK";
|
||||
parameter SEL_PATTERN = "PATTERN";
|
||||
parameter USE_DPORT = "FALSE";
|
||||
parameter USE_MULT = "MULTIPLY";
|
||||
parameter USE_PATTERN_DETECT = "NO_PATDET";
|
||||
parameter USE_SIMD = "ONE48";
|
||||
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
|
||||
parameter [47:0] PATTERN = 48'h000000000000;
|
||||
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
|
||||
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_CLK_INVERTED = 1'b0;
|
||||
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
|
||||
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
|
||||
|
||||
reg CLK;
|
||||
reg CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL;
|
||||
reg CED, CEINMODE, CEM, CEP;
|
||||
reg RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP;
|
||||
reg [29:0] A, ACIN;
|
||||
reg [17:0] B, BCIN;
|
||||
reg [47:0] C;
|
||||
reg [24:0] D;
|
||||
reg [47:0] PCIN;
|
||||
reg [3:0] ALUMODE;
|
||||
reg [2:0] CARRYINSEL;
|
||||
reg [4:0] INMODE;
|
||||
reg [6:0] OPMODE;
|
||||
reg CARRYCASCIN, CARRYIN, MULTSIGNIN;
|
||||
|
||||
output [29:0] ACOUT, REF_ACOUT;
|
||||
output [17:0] BCOUT, REF_BCOUT;
|
||||
output CARRYCASCOUT, REF_CARRYCASCOUT;
|
||||
output [3:0] CARRYOUT, REF_CARRYOUT;
|
||||
output MULTSIGNOUT, REF_MULTSIGNOUT;
|
||||
output OVERFLOW, REF_OVERFLOW;
|
||||
output [47:0] P, REF_P;
|
||||
output PATTERNBDETECT, REF_PATTERNBDETECT;
|
||||
output PATTERNDETECT, REF_PATTERNDETECT;
|
||||
output [47:0] PCOUT, REF_PCOUT;
|
||||
output UNDERFLOW, REF_UNDERFLOW;
|
||||
|
||||
integer errcount = 0;
|
||||
|
||||
reg ERROR_FLAG = 0;
|
||||
|
||||
task clkcycle;
|
||||
begin
|
||||
#5;
|
||||
CLK = ~CLK;
|
||||
#10;
|
||||
CLK = ~CLK;
|
||||
#2;
|
||||
ERROR_FLAG = 0;
|
||||
if (REF_P !== P) begin
|
||||
$display("ERROR at %1t: REF_P=%b UUT_P=%b DIFF=%b", $time, REF_P, P, REF_P ^ P);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
if (REF_CARRYOUT !== CARRYOUT) begin
|
||||
$display("ERROR at %1t: REF_CARRYOUT=%b UUT_CARRYOUT=%b", $time, REF_CARRYOUT, CARRYOUT);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
if (REF_PATTERNDETECT !== PATTERNDETECT) begin
|
||||
$display("ERROR at %1t: REF_PATTERNDETECT=%b UUT_PATTERNDETECT=%b DIFF=%b REF_P=%b P=%b", $time, REF_PATTERNDETECT, PATTERNDETECT, REF_PATTERNDETECT ^ PATTERNDETECT, REF_P, P);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
if (REF_PATTERNBDETECT !== PATTERNBDETECT) begin
|
||||
$display("ERROR at %1t: REF_PATTERNBDETECT=%b UUT_PATTERNBDETECT=%b DIFF=%b", $time, REF_PATTERNBDETECT, PATTERNBDETECT, REF_PATTERNBDETECT ^ PATTERNBDETECT);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
if (REF_OVERFLOW !== OVERFLOW) begin
|
||||
$display("ERROR at %1t: REF_OVERFLOW=%b UUT_OVERFLOW=%b DIFF=%b", $time, REF_OVERFLOW, OVERFLOW, REF_OVERFLOW ^ OVERFLOW);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
if (REF_UNDERFLOW !== UNDERFLOW) begin
|
||||
$display("ERROR at %1t: REF_UNDERFLOW=%b UUT_UNDERFLOW=%b DIFF=%b", $time, REF_UNDERFLOW, UNDERFLOW, REF_UNDERFLOW ^ UNDERFLOW);
|
||||
errcount = errcount + 1;
|
||||
ERROR_FLAG = 1;
|
||||
end
|
||||
#3;
|
||||
end
|
||||
endtask
|
||||
|
||||
reg config_valid = 0;
|
||||
task drc;
|
||||
begin
|
||||
config_valid = 1;
|
||||
if (AREG != 2 && INMODE[0]) config_valid = 0;
|
||||
if (BREG != 2 && INMODE[4]) config_valid = 0;
|
||||
|
||||
if (USE_SIMD != "ONE48" && OPMODE[3:0] == 4'b0101) config_valid = 0;
|
||||
|
||||
if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0;
|
||||
if ((OPMODE[3:2] == 2'b01) ^ (OPMODE[1:0] == 2'b01) == 1'b1) config_valid = 0;
|
||||
if ((OPMODE[6:4] == 3'b010 || OPMODE[6:4] == 3'b110) && PREG != 1) config_valid = 0;
|
||||
if ((OPMODE[6:4] == 3'b100) && (PREG != 1 || OPMODE[3:0] != 4'b1000 || ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11)) config_valid = 0;
|
||||
if ((CARRYINSEL == 3'b100 || CARRYINSEL == 3'b101 || CARRYINSEL == 3'b111) && (PREG != 1)) config_valid = 0;
|
||||
if (OPMODE[6:4] == 3'b111) config_valid = 0;
|
||||
if ((OPMODE[3:0] == 4'b0101) && CARRYINSEL == 3'b010) config_valid = 0;
|
||||
if (CARRYINSEL == 3'b000 && OPMODE == 7'b1001000) config_valid = 0;
|
||||
|
||||
if ((ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11) && OPMODE[3:2] != 2'b00 && OPMODE[3:2] != 2'b10) config_valid = 0;
|
||||
|
||||
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
$dumpfile("test_dsp_model.vcd");
|
||||
$dumpvars(0, testbench);
|
||||
|
||||
#2;
|
||||
CLK = 1'b0;
|
||||
{CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = 9'b111111111;
|
||||
{CED, CEINMODE, CEM, CEP} = 4'b1111;
|
||||
|
||||
{A, B, C, D} = 0;
|
||||
{ACIN, BCIN, PCIN} = 0;
|
||||
{ALUMODE, CARRYINSEL, INMODE} = 0;
|
||||
{OPMODE, CARRYCASCIN, CARRYIN, MULTSIGNIN} = 0;
|
||||
|
||||
{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = ~0;
|
||||
repeat (10) begin
|
||||
#10;
|
||||
CLK = 1'b1;
|
||||
#10;
|
||||
CLK = 1'b0;
|
||||
#10;
|
||||
CLK = 1'b1;
|
||||
#10;
|
||||
CLK = 1'b0;
|
||||
end
|
||||
{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = 0;
|
||||
|
||||
repeat (10000) begin
|
||||
clkcycle;
|
||||
config_valid = 0;
|
||||
while (!config_valid) begin
|
||||
A = $urandom;
|
||||
ACIN = $urandom;
|
||||
B = $urandom;
|
||||
BCIN = $urandom;
|
||||
C = {$urandom, $urandom};
|
||||
D = $urandom;
|
||||
PCIN = {$urandom, $urandom};
|
||||
|
||||
{CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = $urandom | $urandom | $urandom;
|
||||
{CED, CEINMODE, CEM, CEP} = $urandom | $urandom | $urandom | $urandom;
|
||||
|
||||
// Otherwise we can accidentally create illegal configs
|
||||
CEINMODE = CECTRL;
|
||||
CEALUMODE = CECTRL;
|
||||
|
||||
{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom;
|
||||
{ALUMODE, INMODE} = $urandom;
|
||||
CARRYINSEL = $urandom & $urandom & $urandom;
|
||||
OPMODE = $urandom;
|
||||
if ($urandom & 1'b1)
|
||||
OPMODE[3:0] = 4'b0101; // test multiply more than other modes
|
||||
{CARRYCASCIN, CARRYIN, MULTSIGNIN} = $urandom;
|
||||
|
||||
// So few valid options in these modes, just force one valid option
|
||||
if (CARRYINSEL == 3'b001) OPMODE = 7'b1010101;
|
||||
if (CARRYINSEL == 3'b010) OPMODE = 7'b0001010;
|
||||
if (CARRYINSEL == 3'b011) OPMODE = 7'b0011011;
|
||||
if (CARRYINSEL == 3'b100) OPMODE = 7'b0110011;
|
||||
if (CARRYINSEL == 3'b101) OPMODE = 7'b0011010;
|
||||
if (CARRYINSEL == 3'b110) OPMODE = 7'b0010101;
|
||||
if (CARRYINSEL == 3'b111) OPMODE = 7'b0100011;
|
||||
|
||||
drc;
|
||||
end
|
||||
end
|
||||
|
||||
if (errcount == 0) begin
|
||||
$display("All tests passed.");
|
||||
$finish;
|
||||
end else begin
|
||||
$display("Caught %1d errors.", errcount);
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
|
||||
DSP48E1 #(
|
||||
.ACASCREG (ACASCREG),
|
||||
.ADREG (ADREG),
|
||||
.ALUMODEREG (ALUMODEREG),
|
||||
.AREG (AREG),
|
||||
.AUTORESET_PATDET (AUTORESET_PATDET),
|
||||
.A_INPUT (A_INPUT),
|
||||
.BCASCREG (BCASCREG),
|
||||
.BREG (BREG),
|
||||
.B_INPUT (B_INPUT),
|
||||
.CARRYINREG (CARRYINREG),
|
||||
.CARRYINSELREG (CARRYINSELREG),
|
||||
.CREG (CREG),
|
||||
.DREG (DREG),
|
||||
.INMODEREG (INMODEREG),
|
||||
.MREG (MREG),
|
||||
.OPMODEREG (OPMODEREG),
|
||||
.PREG (PREG),
|
||||
.SEL_MASK (SEL_MASK),
|
||||
.SEL_PATTERN (SEL_PATTERN),
|
||||
.USE_DPORT (USE_DPORT),
|
||||
.USE_MULT (USE_MULT),
|
||||
.USE_PATTERN_DETECT (USE_PATTERN_DETECT),
|
||||
.USE_SIMD (USE_SIMD),
|
||||
.MASK (MASK),
|
||||
.PATTERN (PATTERN),
|
||||
.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED),
|
||||
.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED),
|
||||
.IS_CLK_INVERTED (IS_CLK_INVERTED),
|
||||
.IS_INMODE_INVERTED (IS_INMODE_INVERTED),
|
||||
.IS_OPMODE_INVERTED (IS_OPMODE_INVERTED)
|
||||
) ref (
|
||||
.ACOUT (REF_ACOUT),
|
||||
.BCOUT (REF_BCOUT),
|
||||
.CARRYCASCOUT (REF_CARRYCASCOUT),
|
||||
.CARRYOUT (REF_CARRYOUT),
|
||||
.MULTSIGNOUT (REF_MULTSIGNOUT),
|
||||
.OVERFLOW (REF_OVERFLOW),
|
||||
.P (REF_P),
|
||||
.PATTERNBDETECT(REF_PATTERNBDETECT),
|
||||
.PATTERNDETECT (REF_PATTERNDETECT),
|
||||
.PCOUT (REF_PCOUT),
|
||||
.UNDERFLOW (REF_UNDERFLOW),
|
||||
.A (A),
|
||||
.ACIN (ACIN),
|
||||
.ALUMODE (ALUMODE),
|
||||
.B (B),
|
||||
.BCIN (BCIN),
|
||||
.C (C),
|
||||
.CARRYCASCIN (CARRYCASCIN),
|
||||
.CARRYINSEL (CARRYINSEL),
|
||||
.CEA1 (CEA1),
|
||||
.CEA2 (CEA2),
|
||||
.CEAD (CEAD),
|
||||
.CEALUMODE (CEALUMODE),
|
||||
.CEB1 (CEB1),
|
||||
.CEB2 (CEB2),
|
||||
.CEC (CEC),
|
||||
.CECARRYIN (CECARRYIN),
|
||||
.CECTRL (CECTRL),
|
||||
.CED (CED),
|
||||
.CEINMODE (CEINMODE),
|
||||
.CEM (CEM),
|
||||
.CEP (CEP),
|
||||
.CLK (CLK),
|
||||
.D (D),
|
||||
.INMODE (INMODE),
|
||||
.MULTSIGNIN (MULTSIGNIN),
|
||||
.OPMODE (OPMODE),
|
||||
.PCIN (PCIN),
|
||||
.RSTA (RSTA),
|
||||
.RSTALLCARRYIN (RSTALLCARRYIN),
|
||||
.RSTALUMODE (RSTALUMODE),
|
||||
.RSTB (RSTB),
|
||||
.RSTC (RSTC),
|
||||
.RSTCTRL (RSTCTRL),
|
||||
.RSTD (RSTD),
|
||||
.RSTINMODE (RSTINMODE),
|
||||
.RSTM (RSTM),
|
||||
.RSTP (RSTP)
|
||||
);
|
||||
|
||||
DSP48E1_UUT #(
|
||||
.ACASCREG (ACASCREG),
|
||||
.ADREG (ADREG),
|
||||
.ALUMODEREG (ALUMODEREG),
|
||||
.AREG (AREG),
|
||||
.AUTORESET_PATDET (AUTORESET_PATDET),
|
||||
.A_INPUT (A_INPUT),
|
||||
.BCASCREG (BCASCREG),
|
||||
.BREG (BREG),
|
||||
.B_INPUT (B_INPUT),
|
||||
.CARRYINREG (CARRYINREG),
|
||||
.CARRYINSELREG (CARRYINSELREG),
|
||||
.CREG (CREG),
|
||||
.DREG (DREG),
|
||||
.INMODEREG (INMODEREG),
|
||||
.MREG (MREG),
|
||||
.OPMODEREG (OPMODEREG),
|
||||
.PREG (PREG),
|
||||
.SEL_MASK (SEL_MASK),
|
||||
.SEL_PATTERN (SEL_PATTERN),
|
||||
.USE_DPORT (USE_DPORT),
|
||||
.USE_MULT (USE_MULT),
|
||||
.USE_PATTERN_DETECT (USE_PATTERN_DETECT),
|
||||
.USE_SIMD (USE_SIMD),
|
||||
.MASK (MASK),
|
||||
.PATTERN (PATTERN),
|
||||
.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED),
|
||||
.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED),
|
||||
.IS_CLK_INVERTED (IS_CLK_INVERTED),
|
||||
.IS_INMODE_INVERTED (IS_INMODE_INVERTED),
|
||||
.IS_OPMODE_INVERTED (IS_OPMODE_INVERTED)
|
||||
) uut (
|
||||
.ACOUT (ACOUT),
|
||||
.BCOUT (BCOUT),
|
||||
.CARRYCASCOUT (CARRYCASCOUT),
|
||||
.CARRYOUT (CARRYOUT),
|
||||
.MULTSIGNOUT (MULTSIGNOUT),
|
||||
.OVERFLOW (OVERFLOW),
|
||||
.P (P),
|
||||
.PATTERNBDETECT(PATTERNBDETECT),
|
||||
.PATTERNDETECT (PATTERNDETECT),
|
||||
.PCOUT (PCOUT),
|
||||
.UNDERFLOW (UNDERFLOW),
|
||||
.A (A),
|
||||
.ACIN (ACIN),
|
||||
.ALUMODE (ALUMODE),
|
||||
.B (B),
|
||||
.BCIN (BCIN),
|
||||
.C (C),
|
||||
.CARRYCASCIN (CARRYCASCIN),
|
||||
.CARRYINSEL (CARRYINSEL),
|
||||
.CEA1 (CEA1),
|
||||
.CEA2 (CEA2),
|
||||
.CEAD (CEAD),
|
||||
.CEALUMODE (CEALUMODE),
|
||||
.CEB1 (CEB1),
|
||||
.CEB2 (CEB2),
|
||||
.CEC (CEC),
|
||||
.CECARRYIN (CECARRYIN),
|
||||
.CECTRL (CECTRL),
|
||||
.CED (CED),
|
||||
.CEINMODE (CEINMODE),
|
||||
.CEM (CEM),
|
||||
.CEP (CEP),
|
||||
.CLK (CLK),
|
||||
.D (D),
|
||||
.INMODE (INMODE),
|
||||
.MULTSIGNIN (MULTSIGNIN),
|
||||
.OPMODE (OPMODE),
|
||||
.PCIN (PCIN),
|
||||
.RSTA (RSTA),
|
||||
.RSTALLCARRYIN (RSTALLCARRYIN),
|
||||
.RSTALUMODE (RSTALUMODE),
|
||||
.RSTB (RSTB),
|
||||
.RSTC (RSTC),
|
||||
.RSTCTRL (RSTCTRL),
|
||||
.RSTD (RSTD),
|
||||
.RSTINMODE (RSTINMODE),
|
||||
.RSTM (RSTM),
|
||||
.RSTP (RSTP)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module mult_noreg_nopreadd_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (0),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (0),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (0),
|
||||
.BREG (0),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (0),
|
||||
.DREG (0),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (0),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("FALSE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module mult_allreg_nopreadd_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (1),
|
||||
.ADREG (1),
|
||||
.ALUMODEREG (1),
|
||||
.AREG (2),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (1),
|
||||
.BREG (2),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (1),
|
||||
.CARRYINSELREG (1),
|
||||
.CREG (1),
|
||||
.DREG (1),
|
||||
.INMODEREG (1),
|
||||
.MREG (1),
|
||||
.OPMODEREG (1),
|
||||
.PREG (1),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("FALSE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module mult_noreg_preadd_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (0),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (0),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (0),
|
||||
.BREG (0),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (0),
|
||||
.DREG (0),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (0),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("TRUE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module mult_allreg_preadd_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (1),
|
||||
.ADREG (1),
|
||||
.ALUMODEREG (1),
|
||||
.AREG (2),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (1),
|
||||
.BREG (2),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (1),
|
||||
.CARRYINSELREG (1),
|
||||
.CREG (1),
|
||||
.DREG (1),
|
||||
.INMODEREG (1),
|
||||
.MREG (1),
|
||||
.OPMODEREG (1),
|
||||
.PREG (1),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("TRUE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module mult_inreg_preadd_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (1),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (1),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (1),
|
||||
.BREG (1),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (1),
|
||||
.DREG (1),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (0),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("TRUE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module simd12_preadd_noreg_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (0),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (0),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (0),
|
||||
.BREG (0),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (0),
|
||||
.DREG (0),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (0),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("TRUE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("FOUR12"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
|
||||
module simd24_preadd_noreg_nocasc;
|
||||
testbench #(
|
||||
.ACASCREG (0),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (0),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (0),
|
||||
.BREG (0),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (0),
|
||||
.DREG (0),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (0),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("TRUE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("NO_PATDET"),
|
||||
.USE_SIMD ("TWO24"),
|
||||
.MASK (48'h3FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
||||
|
||||
module macc_overflow_underflow;
|
||||
testbench #(
|
||||
.ACASCREG (0),
|
||||
.ADREG (0),
|
||||
.ALUMODEREG (0),
|
||||
.AREG (0),
|
||||
.AUTORESET_PATDET ("NO_RESET"),
|
||||
.A_INPUT ("DIRECT"),
|
||||
.BCASCREG (0),
|
||||
.BREG (0),
|
||||
.B_INPUT ("DIRECT"),
|
||||
.CARRYINREG (0),
|
||||
.CARRYINSELREG (0),
|
||||
.CREG (0),
|
||||
.DREG (0),
|
||||
.INMODEREG (0),
|
||||
.MREG (0),
|
||||
.OPMODEREG (0),
|
||||
.PREG (1),
|
||||
.SEL_MASK ("MASK"),
|
||||
.SEL_PATTERN ("PATTERN"),
|
||||
.USE_DPORT ("FALSE"),
|
||||
.USE_MULT ("DYNAMIC"),
|
||||
.USE_PATTERN_DETECT ("PATDET"),
|
||||
.USE_SIMD ("ONE48"),
|
||||
.MASK (48'h1FFFFFFFFFFF),
|
||||
.PATTERN (48'h000000000000),
|
||||
.IS_ALUMODE_INVERTED(4'b0),
|
||||
.IS_CARRYIN_INVERTED(1'b0),
|
||||
.IS_CLK_INVERTED (1'b0),
|
||||
.IS_INMODE_INVERTED (5'b0),
|
||||
.IS_OPMODE_INVERTED (7'b0)
|
||||
) testbench ();
|
||||
endmodule
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// FF mapping for Spartan 6. The primitives used are the same as Series 7,
|
||||
// but with one major difference: the initial value is implied by the
|
||||
// primitive type used (FFs with reset pin must have INIT set to 0 or x, FFs
|
||||
// with set pin must have INIT set to 1 or x). For Yosys primitives without
|
||||
// set/reset, this means we have to pick the primitive type based on the INIT
|
||||
// value.
|
||||
|
||||
`ifndef _NO_FFS
|
||||
|
||||
module \$_DFF_N_ (input D, C, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
FDSE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .S(1'b0));
|
||||
else
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_P_ (input D, C, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
FDSE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .S(1'b0));
|
||||
else
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
FDSE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .S(1'b0));
|
||||
else
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
FDSE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .S(1'b0));
|
||||
else
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous reset initialized to 1");
|
||||
else
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous reset initialized to 1");
|
||||
else
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous reset initialized to 1");
|
||||
else
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous reset initialized to 1");
|
||||
else
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b0)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous set initialized to 0");
|
||||
else
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b0)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous set initialized to 0");
|
||||
else
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b0)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous set initialized to 0");
|
||||
else
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b0)
|
||||
$error("Spartan 6 doesn't support FFs with asynchronous set initialized to 0");
|
||||
else
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DLATCH_N_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
LDPE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .PRE(1'b0));
|
||||
else
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DLATCH_P_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
LDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .PRE(1'b0));
|
||||
else
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +1,25 @@
|
|||
// Max delays from https://github.com/SymbiFlow/prjxray-db/blob/f8e0364116b2983ac72a3dc8c509ea1cc79e2e3d/artix7/timings/BRAM_L.sdf#L138-L147
|
||||
|
||||
module RAMB18E1 (
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLKARDCLK_INVERTED" *)
|
||||
input CLKARDCLK,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLKBWRCLK_INVERTED" *)
|
||||
input CLKBWRCLK,
|
||||
(* invertible_pin = "IS_ENARDEN_INVERTED" *)
|
||||
input ENARDEN,
|
||||
(* invertible_pin = "IS_ENBWREN_INVERTED" *)
|
||||
input ENBWREN,
|
||||
input REGCEAREGCE,
|
||||
input REGCEB,
|
||||
(* invertible_pin = "IS_RSTRAMARSTRAM_INVERTED" *)
|
||||
input RSTRAMARSTRAM,
|
||||
(* invertible_pin = "IS_RSTRAMB_INVERTED" *)
|
||||
input RSTRAMB,
|
||||
(* invertible_pin = "IS_RSTREGARSTREG_INVERTED" *)
|
||||
input RSTREGARSTREG,
|
||||
(* invertible_pin = "IS_RSTREGB_INVERTED" *)
|
||||
input RSTREGB,
|
||||
|
||||
input [13:0] ADDRARDADDR,
|
||||
|
@ -21,9 +31,13 @@ module RAMB18E1 (
|
|||
input [1:0] WEA,
|
||||
input [3:0] WEBWE,
|
||||
|
||||
(* abc_arrival=2454 *)
|
||||
output [15:0] DOADO,
|
||||
(* abc_arrival=2454 *)
|
||||
output [15:0] DOBDO,
|
||||
(* abc_arrival=2454 *)
|
||||
output [1:0] DOPADOP,
|
||||
(* abc_arrival=2454 *)
|
||||
output [1:0] DOPBDOP
|
||||
);
|
||||
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
|
||||
|
@ -126,16 +140,24 @@ endmodule
|
|||
|
||||
module RAMB36E1 (
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLKARDCLK_INVERTED" *)
|
||||
input CLKARDCLK,
|
||||
(* clkbuf_sink *)
|
||||
(* invertible_pin = "IS_CLKBWRCLK_INVERTED" *)
|
||||
input CLKBWRCLK,
|
||||
(* invertible_pin = "IS_ENARDEN_INVERTED" *)
|
||||
input ENARDEN,
|
||||
(* invertible_pin = "IS_ENBWREN_INVERTED" *)
|
||||
input ENBWREN,
|
||||
input REGCEAREGCE,
|
||||
input REGCEB,
|
||||
(* invertible_pin = "IS_RSTRAMARSTRAM_INVERTED" *)
|
||||
input RSTRAMARSTRAM,
|
||||
(* invertible_pin = "IS_RSTRAMB_INVERTED" *)
|
||||
input RSTRAMB,
|
||||
(* invertible_pin = "IS_RSTREGARSTREG_INVERTED" *)
|
||||
input RSTREGARSTREG,
|
||||
(* invertible_pin = "IS_RSTREGB_INVERTED" *)
|
||||
input RSTREGB,
|
||||
|
||||
input [15:0] ADDRARDADDR,
|
||||
|
@ -147,9 +169,13 @@ module RAMB36E1 (
|
|||
input [3:0] WEA,
|
||||
input [7:0] WEBWE,
|
||||
|
||||
(* abc_arrival=2454 *)
|
||||
output [31:0] DOADO,
|
||||
(* abc_arrival=2454 *)
|
||||
output [31:0] DOBDO,
|
||||
(* abc_arrival=2454 *)
|
||||
output [3:0] DOPADOP,
|
||||
(* abc_arrival=2454 *)
|
||||
output [3:0] DOPBDOP
|
||||
);
|
||||
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// FF mapping for Virtex 6, Series 7 and Ultrascale. These families support
|
||||
// the following features:
|
||||
//
|
||||
// - a CLB flip-flop can be used as a latch or as a flip-flop
|
||||
// - a CLB flip-flop has the following pins:
|
||||
//
|
||||
// - data input
|
||||
// - clock (or gate for latches) (with optional inversion)
|
||||
// - clock enable (or gate enable, which is just ANDed with gate — unused by
|
||||
// synthesis)
|
||||
// - either a set or a reset input, which (for FFs) can be either
|
||||
// synchronous or asynchronous (with optional inversion)
|
||||
// - data output
|
||||
//
|
||||
// - a flip-flop also has an initial value, which is set at device
|
||||
// initialization (or whenever GSR is asserted)
|
||||
|
||||
`ifndef _NO_FFS
|
||||
|
||||
module \$_DFF_N_ (input D, C, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_P_ (input D, C, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DLATCH_N_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DLATCH_P_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -22,29 +22,25 @@ module adffn
|
|||
q <= d;
|
||||
endmodule
|
||||
|
||||
module dffsr
|
||||
module dffs
|
||||
( input d, clk, pre, clr, output reg q );
|
||||
initial begin
|
||||
q = 0;
|
||||
end
|
||||
always @( posedge clk, posedge pre, posedge clr )
|
||||
if ( clr )
|
||||
q <= 1'b0;
|
||||
else if ( pre )
|
||||
always @( posedge clk, posedge pre )
|
||||
if ( pre )
|
||||
q <= 1'b1;
|
||||
else
|
||||
q <= d;
|
||||
endmodule
|
||||
|
||||
module ndffnsnr
|
||||
module ndffnr
|
||||
( input d, clk, pre, clr, output reg q );
|
||||
initial begin
|
||||
q = 0;
|
||||
end
|
||||
always @( negedge clk, negedge pre, negedge clr )
|
||||
if ( !clr )
|
||||
q <= 1'b0;
|
||||
else if ( !pre )
|
||||
always @( negedge clk, negedge pre )
|
||||
if ( !pre )
|
||||
q <= 1'b1;
|
||||
else
|
||||
q <= d;
|
||||
|
@ -58,7 +54,7 @@ input a,
|
|||
output b,b1,b2,b3
|
||||
);
|
||||
|
||||
dffsr u_dffsr (
|
||||
dffs u_dffs (
|
||||
.clk (clk ),
|
||||
.clr (clr),
|
||||
.pre (pre),
|
||||
|
@ -66,7 +62,7 @@ dffsr u_dffsr (
|
|||
.q (b )
|
||||
);
|
||||
|
||||
ndffnsnr u_ndffnsnr (
|
||||
ndffnr u_ndffnr (
|
||||
.clk (clk ),
|
||||
.clr (clr),
|
||||
.pre (pre),
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
read_verilog adffs.v
|
||||
proc
|
||||
async2sync # converts async flops to a 'sync' variant clocked by a 'super'-clock
|
||||
flatten
|
||||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
equiv_opt -multiclock -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:SB_DFF
|
||||
select -assert-count 1 t:SB_DFFN
|
||||
select -assert-count 2 t:SB_DFFSR
|
||||
select -assert-count 7 t:SB_LUT4
|
||||
select -assert-none t:SB_DFF t:SB_DFFN t:SB_DFFSR t:SB_LUT4 %% t:* %D
|
||||
select -assert-count 1 t:SB_DFFNS
|
||||
select -assert-count 2 t:SB_DFFR
|
||||
select -assert-count 1 t:SB_DFFS
|
||||
select -assert-count 2 t:SB_LUT4
|
||||
select -assert-none t:SB_DFFNS t:SB_DFFR t:SB_DFFS t:SB_LUT4 %% t:* %D
|
||||
|
|
|
@ -4,6 +4,6 @@ flatten
|
|||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 62 t:SB_LUT4
|
||||
select -assert-count 59 t:SB_LUT4
|
||||
select -assert-count 41 t:SB_CARRY
|
||||
select -assert-none t:SB_LUT4 t:SB_CARRY %% t:* %D
|
||||
|
|
|
@ -13,13 +13,35 @@ reg [(A_WIDTH + B_WIDTH - 1):0] reg_tmp_c;
|
|||
assign c = reg_tmp_c;
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(set)
|
||||
begin
|
||||
reg_tmp_c <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
reg_tmp_c <= a * b + c;
|
||||
end
|
||||
if(set)
|
||||
begin
|
||||
reg_tmp_c <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
reg_tmp_c <= a * b + c;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module top2(clk,a,b,c,hold);
|
||||
parameter A_WIDTH = 6 /*4*/;
|
||||
parameter B_WIDTH = 6 /*3*/;
|
||||
input hold;
|
||||
input clk;
|
||||
input signed [(A_WIDTH - 1):0] a;
|
||||
input signed [(B_WIDTH - 1):0] b;
|
||||
output signed [(A_WIDTH + B_WIDTH - 1):0] c;
|
||||
reg signed [A_WIDTH-1:0] reg_a;
|
||||
reg signed [B_WIDTH-1:0] reg_b;
|
||||
reg [(A_WIDTH + B_WIDTH - 1):0] reg_tmp_c;
|
||||
assign c = reg_tmp_c;
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (!hold) begin
|
||||
reg_a <= a;
|
||||
reg_b <= b;
|
||||
reg_tmp_c <= reg_a * reg_b + c;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
read_verilog macc.v
|
||||
proc
|
||||
design -save read
|
||||
|
||||
hierarchy -top top
|
||||
#equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
|
||||
|
||||
equiv_opt -run :prove -map +/ice40/cells_sim.v synth_ice40 -dsp
|
||||
async2sync
|
||||
equiv_opt -run prove: -assert null
|
||||
|
||||
equiv_opt -assert -multiclock -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:SB_MAC16
|
||||
select -assert-none t:SB_MAC16 %% t:* %D
|
||||
|
||||
design -load read
|
||||
hierarchy -top top2
|
||||
|
||||
#equiv_opt -multiclock -assert -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
|
||||
|
||||
equiv_opt -run :prove -multiclock -assert -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
|
||||
clk2fflogic
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -set-init-zero -seq 4 -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top2 # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:SB_MAC16
|
||||
select -assert-none t:SB_MAC16 %% t:* %D
|
||||
|
|
|
@ -6,7 +6,7 @@ for x in *.ys; do
|
|||
echo "all:: run-$x"
|
||||
echo "run-$x:"
|
||||
echo " @echo 'Running $x..'"
|
||||
echo " @../../yosys -ql ${x%.ys}.log $x -w 'Yosys has only limited support for tri-state logic at the moment.'"
|
||||
echo " @../../yosys -ql ${x%.ys}.log -w 'Yosys has only limited support for tri-state logic at the moment.' $x"
|
||||
done
|
||||
for s in *.sh; do
|
||||
if [ "$s" != "run-test.sh" ]; then
|
||||
|
|
|
@ -204,7 +204,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr -fine
|
||||
equiv_opt -assert opt_expr -fine
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$alu r:A_WIDTH=4 r:B_WIDTH=4 r:Y_WIDTH=5 %i %i %i
|
||||
|
||||
|
@ -218,7 +218,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr -fine
|
||||
equiv_opt -assert opt_expr -fine
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$alu r:A_WIDTH=8 r:B_WIDTH=8 r:Y_WIDTH=9 %i %i %i
|
||||
|
||||
|
@ -232,7 +232,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr
|
||||
equiv_opt -assert opt_expr
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$shiftx r:A_WIDTH=3 %i
|
||||
|
||||
|
@ -246,7 +246,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr
|
||||
equiv_opt -assert opt_expr
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$shiftx r:A_WIDTH=12 %i
|
||||
|
||||
|
@ -260,7 +260,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr
|
||||
equiv_opt -assert opt_expr
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$shift r:A_WIDTH=3 %i
|
||||
|
||||
|
@ -274,7 +274,7 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr
|
||||
equiv_opt -assert opt_expr
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$shift r:A_WIDTH=10 %i
|
||||
|
||||
|
@ -288,6 +288,6 @@ endmodule
|
|||
EOT
|
||||
check
|
||||
|
||||
equiv_opt opt_expr -keepdc
|
||||
equiv_opt -assert opt_expr -keepdc
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$shift r:A_WIDTH=13 %i
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*.log
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue