aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--README.md17
-rw-r--r--backends/aiger/xaiger.cc59
-rw-r--r--backends/edif/edif.cc43
-rw-r--r--backends/json/json.cc38
-rw-r--r--frontends/ast/ast.h1
-rw-r--r--frontends/ast/simplify.cc202
-rw-r--r--frontends/verific/verific.cc30
-rw-r--r--frontends/verilog/verilog_lexer.l2
-rw-r--r--frontends/verilog/verilog_parser.y7
-rw-r--r--kernel/driver.cc12
-rw-r--r--kernel/log.cc15
-rw-r--r--kernel/log.h3
-rw-r--r--kernel/register.cc23
-rw-r--r--kernel/register.h5
-rw-r--r--misc/py_wrap_generator.py13
-rw-r--r--passes/hierarchy/hierarchy.cc76
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/opt_lut_ins.cc278
-rw-r--r--passes/opt/opt_reduce.cc7
-rw-r--r--passes/pmgen/xilinx_dsp.cc7
-rw-r--r--passes/sat/clk2fflogic.cc83
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc9.cc22
-rw-r--r--passes/techmap/abc9_exe.cc3
-rw-r--r--passes/techmap/abc9_ops.cc437
-rw-r--r--techlibs/common/Makefile.inc1
-rw-r--r--techlibs/common/dummy.box1
-rw-r--r--techlibs/common/techmap.v104
-rw-r--r--techlibs/ecp5/synth_ecp5.cc1
-rw-r--r--techlibs/gowin/synth_gowin.cc1
-rw-r--r--techlibs/intel/synth_intel.cc2
-rw-r--r--techlibs/xilinx/abc9_model.v5
-rw-r--r--techlibs/xilinx/abc9_xc7.box61
-rw-r--r--techlibs/xilinx/cells_sim.v625
-rw-r--r--techlibs/xilinx/cells_xtra.py42
-rw-r--r--techlibs/xilinx/cells_xtra.v59
-rw-r--r--techlibs/xilinx/lutrams.txt4
-rw-r--r--techlibs/xilinx/synth_xilinx.cc16
-rw-r--r--techlibs/xilinx/tests/.gitignore3
-rw-r--r--techlibs/xilinx/tests/test_dsp48_model.sh14
-rw-r--r--techlibs/xilinx/tests/test_dsp48_model.v287
-rw-r--r--tests/arch/ecp5/mux.ys6
-rw-r--r--tests/arch/ecp5/opt_lut_ins.ys32
-rw-r--r--tests/arch/efinix/mux.ys6
-rw-r--r--tests/arch/gowin/mux.ys8
-rw-r--r--tests/arch/xilinx/dffs.ys22
-rw-r--r--tests/arch/xilinx/opt_lut_ins.ys25
-rw-r--r--tests/opt/opt_lut_ins.ys23
-rw-r--r--tests/sat/clk2fflogic.ys66
-rw-r--r--tests/techmap/shiftx2mux.ys110
-rw-r--r--tests/various/help.ys2
-rw-r--r--tests/various/sformatf.ys12
-rwxr-xr-xtests/various/sv_implicit_ports.sh124
54 files changed, 2568 insertions, 481 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 481ba266e..ca7196091 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -55,10 +55,12 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "check -mapped"
- Added checking of SystemVerilog always block types (always_comb,
always_latch and always_ff)
+ - Added support for SystemVerilog wildcard port connections (.*)
- Added "xilinx_dffopt" pass
- Added "scratchpad" pass
- Added "abc9 -dff"
- Added "synth_xilinx -dff"
+ - Added "opt_lut_ins" pass
Yosys 0.8 .. Yosys 0.9
----------------------
diff --git a/README.md b/README.md
index 77e9410da..e4301e7bf 100644
--- a/README.md
+++ b/README.md
@@ -373,10 +373,15 @@ Verilog Attributes and non-standard features
`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 ``abc9_arrival`` specifies an integer (for output ports
- only) to be used as the arrival time of this sequential port. It can be used,
- for example, to specify the clk-to-Q delay of a flip-flop for consideration
- during `abc9` techmapping.
+- The output port attribute ``abc9_arrival`` specifies an integer, or a string
+ of space-separated integers to be used as the arrival time of this blackbox
+ port. It can be used, for example, to specify the clk-to-Q delay of a flip-
+ flop output for consideration during `abc9` techmapping.
+
+- The input port attribute ``abc9_required`` specifies an integer, or a string
+ of space-separated integers to be used as the required time of this blackbox
+ port. It can be used, for example, to specify the setup-time of a flip-flop
+ input for consideration during `abc9` techmapping.
- The module attribute ``abc9_flop`` is a boolean marking the module as a
flip-flop. This allows `abc9` to analyse its contents in order to perform
@@ -387,6 +392,10 @@ Verilog Attributes and non-standard features
according to the type of the always. These are checked for correctness in
``proc_dlatch``.
+- The cell attribute ``wildcard_port_conns`` represents wildcard port
+ connections (SystemVerilog ``.*``). These are resolved to concrete
+ connections to matching wires in ``hierarchy``.
+
- 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
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index b72dd6890..76b7efbfc 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -184,7 +184,7 @@ struct XAigerWriter
}
}
- dict<IdString,dict<IdString,int>> arrival_cache;
+ dict<IdString,dict<IdString,std::vector<int>>> arrivals_cache;
for (auto cell : module->cells()) {
RTLIL::Module* inst_module = module->design->module(cell->type);
if (!cell->has_keep_attr()) {
@@ -236,29 +236,50 @@ struct XAigerWriter
box_list.resize(abc9_box_seq+1);
box_list[abc9_box_seq] = cell;
// Only flop boxes may have arrival times
+ // (all others are combinatorial)
abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
if (!abc9_flop)
continue;
}
- auto &cell_arrivals = arrival_cache[cell->type];
+ auto &cell_arrivals = arrivals_cache[cell->type];
for (const auto &conn : cell->connections()) {
+ auto port_wire = inst_module->wire(conn.first);
+ if (!port_wire->port_output)
+ continue;
+
auto r = cell_arrivals.insert(conn.first);
- auto &arrival = r.first->second;
+ auto &arrivals = r.first->second;
if (r.second) {
- auto port_wire = inst_module->wire(conn.first);
- if (port_wire->port_output) {
- auto it = port_wire->attributes.find("\\abc9_arrival");
- if (it != port_wire->attributes.end()) {
- if (it->second.flags != 0)
- log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type));
- arrival = it->second.as_int();
- }
- }
+ auto it = port_wire->attributes.find("\\abc9_arrival");
+ if (it == port_wire->attributes.end())
+ continue;
+ if (it->second.flags == 0)
+ arrivals.emplace_back(it->second.as_int());
+ else
+ for (const auto &tok : split_tokens(it->second.decode_string()))
+ arrivals.push_back(atoi(tok.c_str()));
+ }
+
+ if (arrivals.empty())
+ continue;
+
+ if (GetSize(arrivals) > 1 && GetSize(arrivals) != GetSize(port_wire))
+ log_error("%s.%s is %d bits wide but abc9_arrival = %s has %d value(s)!\n", log_id(cell->type), log_id(conn.first),
+ GetSize(port_wire), log_signal(it->second), GetSize(arrivals));
+
+ auto jt = arrivals.begin();
+#ifndef NDEBUG
+ if (ys_debug(1)) {
+ static std::set<std::pair<IdString,IdString>> seen;
+ if (seen.emplace(cell->type, conn.first).second) log("%s.%s abc9_arrival = %d\n", log_id(cell->type), log_id(conn.first), *jt);
+ }
+#endif
+ for (auto bit : sigmap(conn.second)) {
+ arrival_times[bit] = *jt;
+ if (arrivals.size() > 1)
+ jt++;
}
- if (arrival)
- for (auto bit : sigmap(conn.second))
- arrival_times[bit] = arrival;
}
if (abc9_flop)
@@ -300,7 +321,7 @@ struct XAigerWriter
RTLIL::Module* box_module = module->design->module(cell->type);
log_assert(box_module);
- log_assert(box_module->attributes.count("\\abc9_box_id"));
+ log_assert(box_module->attributes.count("\\abc9_box_id") || box_module->get_bool_attribute("\\abc9_flop"));
auto r = box_ports.insert(cell->type);
if (r.second) {
@@ -579,7 +600,11 @@ struct XAigerWriter
RTLIL::Module* box_module = module->design->module(cell->type);
log_assert(box_module);
- auto r = cell_cache.insert(cell->type);
+ IdString derived_type = box_module->derive(box_module->design, cell->parameters);
+ box_module = box_module->design->module(derived_type);
+ log_assert(box_module);
+
+ auto r = cell_cache.insert(derived_type);
auto &v = r.first->second;
if (r.second) {
int box_inputs = 0, box_outputs = 0;
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 60a098917..616b754ce 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -326,7 +326,7 @@ struct EdifBackend : public Backend {
continue;
SigMap sigmap(module);
- std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db;
+ std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db;
*f << stringf(" (cell %s\n", EDIF_DEF(module->name));
*f << stringf(" (cellType GENERIC)\n");
@@ -349,7 +349,7 @@ struct EdifBackend : public Backend {
add_prop(p.first, p.second);
*f << ")\n";
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
- net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
+ net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input));
} else {
int b[2];
b[wire->upto ? 0 : 1] = wire->start_offset;
@@ -362,7 +362,7 @@ struct EdifBackend : public Backend {
*f << ")\n";
for (int i = 0; i < wire->width; i++) {
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
- net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
+ net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input));
}
}
}
@@ -391,7 +391,7 @@ struct EdifBackend : public Backend {
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
else if (sig.size() == 1)
- net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first)));
else {
int member_idx = GetSize(sig)-i-1;
auto m = design->module(cell->type);
@@ -400,8 +400,8 @@ struct EdifBackend : public Backend {
if (w)
member_idx = GetSize(w)-i-1;
}
- net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))",
- EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)));
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))",
+ EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first)));
}
}
}
@@ -410,13 +410,13 @@ struct EdifBackend : public Backend {
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
if (sig == RTLIL::State::Sx) {
for (auto &ref : it.second)
- log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
+ log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str());
sig = RTLIL::State::S0;
} else if (sig == RTLIL::State::Sz) {
continue;
} else {
for (auto &ref : it.second)
- log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
+ log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str());
log_abort();
}
}
@@ -433,7 +433,7 @@ struct EdifBackend : public Backend {
}
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
for (auto &ref : it.second)
- *f << stringf(" %s\n", ref.c_str());
+ *f << stringf(" %s\n", ref.first.c_str());
if (sig.wire == NULL) {
if (nogndvcc)
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
@@ -448,6 +448,31 @@ struct EdifBackend : public Backend {
add_prop(p.first, p.second);
*f << stringf("\n )\n");
}
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (!wire->get_bool_attribute(ID::keep))
+ continue;
+ for(int i = 0; i < wire->width; i++) {
+ SigBit raw_sig = RTLIL::SigSpec(wire, i);
+ SigBit mapped_sig = sigmap(raw_sig);
+ if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0)
+ continue;
+ std::string netname = log_signal(raw_sig);
+ for (size_t i = 0; i < netname.size(); i++)
+ if (netname[i] == ' ' || netname[i] == '\\')
+ netname.erase(netname.begin() + i--);
+ *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+ auto &refs = net_join_db.at(mapped_sig);
+ for (auto &ref : refs)
+ if (ref.second)
+ *f << stringf(" %s\n", ref.first.c_str());
+ *f << stringf(" )");
+ if (attr_properties && raw_sig.wire != NULL)
+ for (auto &p : raw_sig.wire->attributes)
+ add_prop(p.first, p.second);
+ *f << stringf("\n )\n");
+ }
+ }
*f << stringf(" )\n");
*f << stringf(" )\n");
*f << stringf(" )\n");
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 107009ee4..5c67cb857 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -33,6 +33,7 @@ struct JsonWriter
std::ostream &f;
bool use_selection;
bool aig_mode;
+ bool compat_int_mode;
Design *design;
Module *module;
@@ -42,8 +43,9 @@ struct JsonWriter
dict<SigBit, string> sigids;
pool<Aig> aig_models;
- JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) :
- f(f), use_selection(use_selection), aig_mode(aig_mode) { }
+ JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) :
+ f(f), use_selection(use_selection), aig_mode(aig_mode),
+ compat_int_mode(compat_int_mode) { }
string get_string(string str)
{
@@ -102,8 +104,7 @@ struct JsonWriter
if (state < 2)
str += " ";
f << get_string(str);
- } else
- if (GetSize(value) == 32 && value.is_fully_def()) {
+ } else if (compat_int_mode && GetSize(value) == 32 && value.is_fully_def()) {
if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0)
f << stringf("%d", value.as_int());
else
@@ -294,6 +295,10 @@ struct JsonBackend : public Backend {
log(" -aig\n");
log(" include AIG models for the different gate types\n");
log("\n");
+ log(" -compat-int\n");
+ log(" emit 32-bit fully-defined parameter values directly\n");
+ log(" as JSON numbers (for compatibility with old parsers)\n");
+ log("\n");
log("\n");
log("The general syntax of the JSON output created by this command is as follows:\n");
log("\n");
@@ -368,10 +373,9 @@ struct JsonBackend : public Backend {
log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n");
log("\"z\" instead of a number.\n");
log("\n");
- log("Numeric 32-bit parameter and attribute values are written as decimal values.\n");
- log("Bit verctors of different sizes, or ones containing 'x' or 'z' bits, are written\n");
- log("as string holding the binary representation of the value. Strings are written\n");
- log("as strings, with an appended blank in cases of strings of the form /[01xz]* */.\n");
+ log("Bit vectors (including integers) are written as string holding the binary");
+ log("representation of the value. Strings are written as strings, with an appended");
+ log("blank in cases of strings of the form /[01xz]* */.\n");
log("\n");
log("For example the following Verilog code:\n");
log("\n");
@@ -495,6 +499,7 @@ struct JsonBackend : public Backend {
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool aig_mode = false;
+ bool compat_int_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -503,13 +508,17 @@ struct JsonBackend : public Backend {
aig_mode = true;
continue;
}
+ if (args[argidx] == "-compat-int") {
+ compat_int_mode = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
log_header(design, "Executing JSON backend.\n");
- JsonWriter json_writer(*f, false, aig_mode);
+ JsonWriter json_writer(*f, false, aig_mode, compat_int_mode);
json_writer.write_design(design);
}
} JsonBackend;
@@ -530,6 +539,10 @@ struct JsonPass : public Pass {
log(" -aig\n");
log(" also include AIG models for the different gate types\n");
log("\n");
+ log(" -compat-int\n");
+ log(" emit 32-bit fully-defined parameter values directly\n");
+ log(" as JSON numbers (for compatibility with old parsers)\n");
+ log("\n");
log("See 'help write_json' for a description of the JSON format used.\n");
log("\n");
}
@@ -537,6 +550,7 @@ struct JsonPass : public Pass {
{
std::string filename;
bool aig_mode = false;
+ bool compat_int_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -549,6 +563,10 @@ struct JsonPass : public Pass {
aig_mode = true;
continue;
}
+ if (args[argidx] == "-compat-int") {
+ compat_int_mode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -569,7 +587,7 @@ struct JsonPass : public Pass {
f = &buf;
}
- JsonWriter json_writer(*f, true, aig_mode);
+ JsonWriter json_writer(*f, true, aig_mode, compat_int_mode);
json_writer.write_design(design);
if (!filename.empty()) {
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 918d178c7..14e1cec5e 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -244,6 +244,7 @@ namespace AST
void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
AstNode *eval_const_function(AstNode *fcall);
bool is_simple_const_expr();
+ std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint);
// create a human-readable text representation of the AST (for debugging)
void dumpAst(FILE *f, std::string indent) const;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index b94a8d710..8855d9954 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -41,6 +41,103 @@ YOSYS_NAMESPACE_BEGIN
using namespace AST;
using namespace AST_INTERNAL;
+// Process a format string and arguments for $display, $write, $sprintf, etc
+
+std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) {
+ // Other arguments are placeholders. Process the string as we go through it
+ std::string sout;
+ for (size_t i = 0; i < sformat.length(); i++)
+ {
+ // format specifier
+ if (sformat[i] == '%')
+ {
+ // If there's no next character, that's a problem
+ if (i+1 >= sformat.length())
+ log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str());
+
+ char cformat = sformat[++i];
+
+ // %% is special, does not need a matching argument
+ if (cformat == '%')
+ {
+ sout += '%';
+ continue;
+ }
+
+ // Simplify the argument
+ AstNode *node_arg = nullptr;
+
+ // Everything from here on depends on the format specifier
+ switch (cformat)
+ {
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ case 'x':
+ case 'X':
+ if (next_arg >= GetSize(children))
+ log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
+ cformat, str.c_str());
+
+ node_arg = children[next_arg++];
+ while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_arg->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
+ break;
+
+ case 'm':
+ case 'M':
+ break;
+
+ default:
+ log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
+ break;
+ }
+
+ switch (cformat)
+ {
+ case 's':
+ case 'S':
+ sout += node_arg->bitsAsConst().decode_string();
+ break;
+
+ case 'd':
+ case 'D':
+ {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int());
+ sout += tmp;
+ }
+ break;
+
+ case 'x':
+ case 'X':
+ {
+ char tmp[128];
+ snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
+ sout += tmp;
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ sout += log_id(current_module->name);
+ break;
+
+ default:
+ log_abort();
+ }
+ }
+
+ // not a format specifier
+ else
+ sout += sformat[i];
+ }
+ return sout;
+}
+
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -216,99 +313,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (node_string->type != AST_CONSTANT)
log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str());
std::string sformat = node_string->bitsAsConst().decode_string();
-
- // Other arguments are placeholders. Process the string as we go through it
- std::string sout;
- int next_arg = 1;
- for (size_t i = 0; i < sformat.length(); i++)
- {
- // format specifier
- if (sformat[i] == '%')
- {
- // If there's no next character, that's a problem
- if (i+1 >= sformat.length())
- log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str());
-
- char cformat = sformat[++i];
-
- // %% is special, does not need a matching argument
- if (cformat == '%')
- {
- sout += '%';
- continue;
- }
-
- // Simplify the argument
- AstNode *node_arg = nullptr;
-
- // Everything from here on depends on the format specifier
- switch (cformat)
- {
- case 's':
- case 'S':
- case 'd':
- case 'D':
- case 'x':
- case 'X':
- if (next_arg >= GetSize(children))
- log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
- cformat, str.c_str());
-
- node_arg = children[next_arg++];
- while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
- if (node_arg->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
- break;
-
- case 'm':
- case 'M':
- break;
-
- default:
- log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
- break;
- }
-
- switch (cformat)
- {
- case 's':
- case 'S':
- sout += node_arg->bitsAsConst().decode_string();
- break;
-
- case 'd':
- case 'D':
- {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int());
- sout += tmp;
- }
- break;
-
- case 'x':
- case 'X':
- {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int());
- sout += tmp;
- }
- break;
-
- case 'm':
- case 'M':
- sout += log_id(current_module->name);
- break;
-
- default:
- log_abort();
- }
- }
-
- // not a format specifier
- else
- sout += sformat[i];
- }
-
+ std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint);
// Finally, print the message (only include a \n for $display, not for $write)
log("%s", sout.c_str());
if (str == "$display")
@@ -2244,6 +2249,17 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
+ if (str == "\\$sformatf") {
+ AstNode *node_string = children[0];
+ while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (node_string->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str());
+ std::string sformat = node_string->bitsAsConst().decode_string();
+ std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint);
+ newNode = AstNode::mkconst_str(sout);
+ goto apply_newNode;
+ }
+
if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION)
{
AstNode *dpi_decl = current_scope[str];
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 9274cf5ca..ae5815f8e 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -539,6 +539,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == OPER_REDUCE_NAND) {
+ Wire *tmp = module->addWire(NEW_ID);
+ cell = module->addReduceAnd(inst_name, IN, tmp, SIGNED);
+ module->addNot(NEW_ID, tmp, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
+ return true;
+ }
+
if (inst->Type() == OPER_REDUCE_OR) {
cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
import_attributes(cell->attributes, inst);
@@ -1891,6 +1899,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
if (!verific_error_msg.empty())
log_error("%s\n", verific_error_msg.c_str());
+ for (auto nl : nl_todo)
+ nl->ChangePortBusStructures(1 /* hierarchical */);
+
VerificExtNets worker;
for (auto nl : nl_todo)
worker.run(nl);
@@ -2408,7 +2419,7 @@ struct VerificPass : public Pass {
else
{
if (argidx == GetSize(args))
- log_cmd_error("No top module specified.\n");
+ cmd_error(args, argidx, "No top module specified.\n");
Array veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++)
@@ -2470,6 +2481,9 @@ struct VerificPass : public Pass {
worker.run(nl);
}
+ for (auto nl : nl_todo)
+ nl->ChangePortBusStructures(1 /* hierarchical */);
+
if (!dumpfile.empty()) {
VeriWrite veri_writer;
veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign());
@@ -2495,7 +2509,7 @@ struct VerificPass : public Pass {
goto check_error;
}
- log_cmd_error("Missing or unsupported mode parameter.\n");
+ cmd_error(args, argidx, "Missing or unsupported mode parameter.\n");
check_error:
if (!verific_error_msg.empty())
@@ -2568,14 +2582,14 @@ struct ReadPass : public Pass {
static bool use_verific = verific_available;
if (args.size() < 2 || args[1][0] != '-')
- log_cmd_error("Missing mode parameter.\n");
+ cmd_error(args, 1, "Missing mode parameter.\n");
if (args[1] == "-verific" || args[1] == "-noverific") {
if (args.size() != 2)
- log_cmd_error("Additional arguments to -verific/-noverific.\n");
+ cmd_error(args, 1, "Additional arguments to -verific/-noverific.\n");
if (args[1] == "-verific") {
if (!verific_available)
- log_cmd_error("This version of Yosys is built without Verific support.\n");
+ cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
use_verific = true;
} else {
use_verific = false;
@@ -2584,7 +2598,7 @@ struct ReadPass : public Pass {
}
if (args.size() < 3)
- log_cmd_error("Missing file name parameter.\n");
+ cmd_error(args, 3, "Missing file name parameter.\n");
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
if (use_verific) {
@@ -2616,7 +2630,7 @@ struct ReadPass : public Pass {
args[0] = "verific";
Pass::call(design, args);
} else {
- log_cmd_error("This version of Yosys is built without Verific support.\n");
+ cmd_error(args, 1, "This version of Yosys is built without Verific support.\n");
}
return;
}
@@ -2663,7 +2677,7 @@ struct ReadPass : public Pass {
return;
}
- log_cmd_error("Missing or unsupported mode parameter.\n");
+ cmd_error(args, 1, "Missing or unsupported mode parameter.\n");
}
} ReadPass;
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index ca23df3e8..9b43c250e 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -431,6 +431,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
"+:" { return TOK_POS_INDEXED; }
"-:" { return TOK_NEG_INDEXED; }
+".*" { return TOK_WILDCARD_CONNECT; }
+
[-+]?[=*]> {
if (!specify_mode) REJECT;
frontend_verilog_yylval.string = new std::string(yytext);
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index a30935e0a..2c7304cc4 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -138,7 +138,7 @@ struct specify_rise_fall {
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
-%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
+%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH
@@ -1580,6 +1580,11 @@ cell_port:
node->children.back()->str = *$3;
delete $3;
free_attr($1);
+ } |
+ attr TOK_WILDCARD_CONNECT {
+ if (!sv_mode)
+ frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode.");
+ astbuf2->attributes[ID(wildcard_port_conns)] = AstNode::mkconst_int(1, false);
};
always_comb_or_latch:
diff --git a/kernel/driver.cc b/kernel/driver.cc
index 7fc81f589..9040408bc 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -295,6 +295,9 @@ int main(int argc, char **argv)
printf(" -E <depsfile>\n");
printf(" write a Makefile dependencies file with in- and output file names\n");
printf("\n");
+ printf(" -x <feature>\n");
+ printf(" do not print warnings for the specified experimental feature\n");
+ printf("\n");
printf(" -g\n");
printf(" globally enable debug log messages\n");
printf("\n");
@@ -324,7 +327,7 @@ int main(int argc, char **argv)
}
int opt;
- while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
+ while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1)
{
switch (opt)
{
@@ -455,6 +458,9 @@ int main(int argc, char **argv)
case 'E':
depsfile = optarg;
break;
+ case 'x':
+ log_experimentals_ignored.insert(optarg);
+ break;
default:
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
exit(1);
@@ -567,6 +573,10 @@ int main(int argc, char **argv)
if (log_warnings_count)
log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count);
+
+ if (!log_experimentals.empty())
+ log("Warnings: %d experimental features used (not excluded with -x).\n", GetSize(log_experimentals));
+
#ifdef _WIN32
log("End of script. Logfile hash: %s\n", hash.c_str());
#else
diff --git a/kernel/log.cc b/kernel/log.cc
index c5ba0d10d..f5d6c488e 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -42,7 +42,7 @@ std::vector<FILE*> log_files;
std::vector<std::ostream*> log_streams;
std::map<std::string, std::set<std::string>> log_hdump;
std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
-std::set<std::string> log_warnings;
+std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
int log_warnings_count = 0;
bool log_hdump_all = false;
FILE *log_errfile = NULL;
@@ -377,6 +377,19 @@ void log_warning(const char *format, ...)
va_end(ap);
}
+void log_experimental(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ string s = vstringf(format, ap);
+ va_end(ap);
+
+ if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) {
+ log_warning("Feature '%s' is experimental.\n", s.c_str());
+ log_experimentals.insert(s);
+ }
+}
+
void log_warning_noprefix(const char *format, ...)
{
va_list ap;
diff --git a/kernel/log.h b/kernel/log.h
index 1f15f3459..9db8efaa5 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -50,7 +50,7 @@ extern std::vector<FILE*> log_files;
extern std::vector<std::ostream*> log_streams;
extern std::map<std::string, std::set<std::string>> log_hdump;
extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
-extern std::set<std::string> log_warnings;
+extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
extern int log_warnings_count;
extern bool log_hdump_all;
extern FILE *log_errfile;
@@ -77,6 +77,7 @@ YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noretur
void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3));
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
+void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
// Log with filename to report a problem in a source file.
void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
diff --git a/kernel/register.cc b/kernel/register.cc
index 5d0fb3c8c..e59d59654 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -304,6 +304,9 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
if (pass_register.count(args[0]) == 0)
log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
+ if (pass_register[args[0]]->experimental_flag)
+ log_experimental("%s", args[0].c_str());
+
size_t orig_sel_stack_pos = design->selection_stack.size();
auto state = pass_register[args[0]]->pre_execute();
pass_register[args[0]]->execute(args, design);
@@ -824,6 +827,11 @@ struct HelpPass : public Pass {
log("=");
log("\n");
it.second->help();
+ if (it.second->experimental_flag) {
+ log("\n");
+ log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
+ log("\n");
+ }
}
}
else if (args[1] == "-cells") {
@@ -846,6 +854,11 @@ struct HelpPass : public Pass {
std::ostringstream buf;
log_streams.push_back(&buf);
it.second->help();
+ if (it.second->experimental_flag) {
+ log("\n");
+ log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
+ log("\n");
+ }
log_streams.pop_back();
write_tex(f, it.first, it.second->short_help, buf.str());
}
@@ -858,6 +871,11 @@ struct HelpPass : public Pass {
std::ostringstream buf;
log_streams.push_back(&buf);
it.second->help();
+ if (it.second->experimental_flag) {
+ log("\n");
+ log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
+ log("\n");
+ }
log_streams.pop_back();
write_html(f, it.first, it.second->short_help, buf.str());
}
@@ -865,6 +883,11 @@ struct HelpPass : public Pass {
}
else if (pass_register.count(args[1])) {
pass_register.at(args[1])->help();
+ if (pass_register.at(args[1])->experimental_flag) {
+ log("\n");
+ log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str());
+ log("\n");
+ }
}
else if (cell_help_messages.cell_help.count(args[1])) {
log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
diff --git a/kernel/register.h b/kernel/register.h
index 821faff3e..4622845b6 100644
--- a/kernel/register.h
+++ b/kernel/register.h
@@ -36,6 +36,11 @@ struct Pass
int call_counter;
int64_t runtime_ns;
+ bool experimental_flag = false;
+
+ void experimental() {
+ experimental_flag = true;
+ }
struct pre_post_exec_state_t {
Pass *parent_pass;
diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py
index c58c3f66a..9b4e644c0 100644
--- a/misc/py_wrap_generator.py
+++ b/misc/py_wrap_generator.py
@@ -1935,6 +1935,19 @@ def parse_header(source):
line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
ugly_line = unpretty_string(line)
+ # for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line
+ if 'union {' in line:
+ j = i+1
+ while j < len(source_text):
+ union_line = source_text[j]
+ if '};' in union_line:
+ source_text[j] = '\n'
+ break
+ j += 1
+ if j != len(source_text):
+ i += 1
+ continue
+
if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
namespace_name = ugly_line[10:].replace("{","").strip()
namespaces.append((namespace_name, ugly_line.count("{")))
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index d8a628448..fa4a8ea29 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -548,6 +548,19 @@ RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod)
return NULL;
}
+// Find a matching wire for an implicit port connection; traversing generate block scope
+RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port)
+{
+ const std::string &cellname = cell->name.str();
+ size_t idx = cellname.size();
+ while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) {
+ Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1));
+ if (found != nullptr)
+ return found;
+ }
+ return module->wire(port);
+}
+
struct HierarchyPass : public Pass {
HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
void help() YS_OVERRIDE
@@ -970,15 +983,71 @@ struct HierarchyPass : public Pass {
}
}
+ // Determine default values
+ dict<IdString, dict<IdString, Const>> defaults_db;
if (!nodefaults)
{
- dict<IdString, dict<IdString, Const>> defaults_db;
-
for (auto module : design->modules())
for (auto wire : module->wires())
if (wire->port_input && wire->attributes.count("\\defaultvalue"))
defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue");
+ }
+ // Process SV implicit wildcard port connections
+ std::set<Module*> blackbox_derivatives;
+ std::vector<Module*> design_modules = design->modules();
+ for (auto module : design_modules)
+ {
+ for (auto cell : module->cells())
+ {
+ if (!cell->get_bool_attribute(ID(wildcard_port_conns)))
+ continue;
+ Module *m = design->module(cell->type);
+
+ if (m == nullptr)
+ log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n",
+ RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+
+ // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths
+ if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
+ IdString new_m_name = m->derive(design, cell->parameters, true);
+ if (new_m_name.empty())
+ continue;
+ if (new_m_name != m->name) {
+ m = design->module(new_m_name);
+ blackbox_derivatives.insert(m);
+ }
+ }
+
+ auto old_connections = cell->connections();
+ for (auto wire : m->wires()) {
+ // Find ports of the module that aren't explicitly connected
+ if (!wire->port_input && !wire->port_output)
+ continue;
+ if (old_connections.count(wire->name))
+ continue;
+ // Make sure a wire of correct name exists in the parent
+ Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str());
+
+ // Missing wires are OK when a default value is set
+ if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name))
+ continue;
+
+ if (parent_wire == nullptr)
+ log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n",
+ RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ if (parent_wire->width != wire->width)
+ log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n",
+ parent_wire->width, wire->width,
+ RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ cell->setPort(wire->name, parent_wire);
+ }
+ cell->attributes.erase(ID(wildcard_port_conns));
+ }
+ }
+
+ if (!nodefaults)
+ {
for (auto module : design->modules())
for (auto cell : module->cells())
{
@@ -1000,9 +1069,6 @@ struct HierarchyPass : public Pass {
}
}
- std::set<Module*> blackbox_derivatives;
- std::vector<Module*> design_modules = design->modules();
-
for (auto module : design_modules)
{
pool<Wire*> wand_wor_index;
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 002c1a6a1..3133927bb 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -15,6 +15,7 @@ OBJS += passes/opt/wreduce.o
OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o
OBJS += passes/opt/opt_lut.o
+OBJS += passes/opt/opt_lut_ins.o
OBJS += passes/opt/pmux2shiftx.o
OBJS += passes/opt/muxpack.o
endif
diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc
new file mode 100644
index 000000000..cf5248ced
--- /dev/null
+++ b/passes/opt/opt_lut_ins.cc
@@ -0,0 +1,278 @@
+/*
+ * 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 OptLutInsPass : public Pass {
+ OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_lut_ins [options] [selection]\n");
+ log("\n");
+ log("This pass removes unused inputs from LUT cells (that is, inputs that can not\n");
+ log("influence the output signal given this LUT's value). While such LUTs cannot\n");
+ log("be directly emitted by ABC, they can be a result of various post-ABC\n");
+ log("transformations, such as mapping wide LUTs (not all sub-LUTs will use the\n");
+ log("full set of inputs) or optimizations such as xilinx_dffopt.\n");
+ log("\n");
+ log(" -tech <technology>\n");
+ log(" Instead of generic $lut cells, operate on LUT cells specific\n");
+ log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n");
+ string techname;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-tech" && argidx+1 < args.size()) {
+ techname = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (techname != "" && techname != "xilinx" && techname != "ecp5" && techname != "gowin")
+ log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
+
+ for (auto module : design->selected_modules())
+ {
+ log("Optimizing LUTs in %s.\n", log_id(module));
+
+ std::vector<Cell *> remove_cells;
+ // Gather LUTs.
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->get_bool_attribute(ID::keep))
+ continue;
+ Const lut;
+ std::vector<SigBit> inputs;
+ std::vector<SigBit> output;
+ bool ignore_const = false;
+ if (techname == "") {
+ if (cell->type != ID($lut))
+ continue;
+ inputs = cell->getPort(ID::A).bits();
+ output = cell->getPort(ID::Y);
+ lut = cell->getParam(ID(LUT));
+ } else if (techname == "xilinx" || techname == "gowin") {
+ if (cell->type == ID(LUT1)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ };
+ } else if (cell->type == ID(LUT2)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ cell->getPort(ID(I1)),
+ };
+ } else if (cell->type == ID(LUT3)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ cell->getPort(ID(I1)),
+ cell->getPort(ID(I2)),
+ };
+ } else if (cell->type == ID(LUT4)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ cell->getPort(ID(I1)),
+ cell->getPort(ID(I2)),
+ cell->getPort(ID(I3)),
+ };
+ } else if (cell->type == ID(LUT5)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ cell->getPort(ID(I1)),
+ cell->getPort(ID(I2)),
+ cell->getPort(ID(I3)),
+ cell->getPort(ID(I4)),
+ };
+ } else if (cell->type == ID(LUT6)) {
+ inputs = {
+ cell->getPort(ID(I0)),
+ cell->getPort(ID(I1)),
+ cell->getPort(ID(I2)),
+ cell->getPort(ID(I3)),
+ cell->getPort(ID(I4)),
+ cell->getPort(ID(I5)),
+ };
+ } else {
+ // Not a LUT.
+ continue;
+ }
+ lut = cell->getParam(ID(INIT));
+ if (techname == "xilinx")
+ output = cell->getPort(ID(O));
+ else
+ output = cell->getPort(ID(F));
+ } else if (techname == "ecp5") {
+ if (cell->type == ID(LUT4)) {
+ inputs = {
+ cell->getPort(ID::A),
+ cell->getPort(ID::B),
+ cell->getPort(ID(C)),
+ cell->getPort(ID(D)),
+ };
+ lut = cell->getParam(ID(INIT));
+ output = cell->getPort(ID(Z));
+ ignore_const = true;
+ } else {
+ // Not a LUT.
+ continue;
+ }
+ }
+ std::vector<int> swizzle;
+ std::vector<SigBit> new_inputs;
+ bool doit = false;
+ for (int i = 0; i < GetSize(inputs); i++) {
+ SigBit input = inputs[i];
+ if (!input.wire) {
+ if (input.data == State::S1)
+ swizzle.push_back(-2);
+ else
+ swizzle.push_back(-1);
+ // For ECP5, smaller LUTs are
+ // implemented as LUT4s with
+ // extra const inputs. Do not
+ // consider that to be a reason
+ // to redo a LUT.
+ if (!ignore_const)
+ doit = true;
+ } else {
+ bool redundant = true;
+ for (int j = 0; j < GetSize(lut); j++) {
+ if (lut[j] != lut[j ^ 1 << i])
+ redundant = false;
+ }
+ if (redundant) {
+ swizzle.push_back(-1);
+ doit = true;
+ } else {
+ swizzle.push_back(GetSize(new_inputs));
+ new_inputs.push_back(input);
+ }
+ }
+ }
+ if (!doit)
+ continue;
+ log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs));
+ if (techname == "ecp5") {
+ // Pad the LUT to 4 inputs, adding consts from the front.
+ int extra = 4 - GetSize(new_inputs);
+ log_assert(extra >= 0);
+ if (extra) {
+ for (int i = 0; i < extra; i++)
+ new_inputs.insert(new_inputs.begin(), State::S0);
+ for (auto &swz : swizzle)
+ if (swz >= 0)
+ swz += extra;
+ }
+ }
+ Const new_lut(0, 1 << GetSize(new_inputs));
+ for (int i = 0; i < GetSize(new_lut); i++) {
+ int lidx = 0;
+ for (int j = 0; j < GetSize(inputs); j++) {
+ int val;
+ if (swizzle[j] == -2) {
+ val = 1;
+ } else if (swizzle[j] == -1) {
+ val = 0;
+ } else {
+ val = (i >> swizzle[j]) & 1;
+ }
+ lidx |= val << j;
+ }
+ new_lut[i] = lut[lidx];
+ }
+ // For ecp5, do not replace with a const driver — the nextpnr
+ // packer requires a complete set of LUTs for wide LUT muxes.
+ if (new_inputs.empty() && techname != "ecp5") {
+ // const driver.
+ remove_cells.push_back(cell);
+ module->connect(output, new_lut[0]);
+ } else {
+ if (techname == "") {
+ cell->setParam(ID(LUT), new_lut);
+ cell->setParam(ID(WIDTH), GetSize(new_inputs));
+ cell->setPort(ID::A, new_inputs);
+ } else if (techname == "ecp5") {
+ log_assert(GetSize(new_inputs) == 4);
+ cell->setParam(ID(INIT), new_lut);
+ cell->setPort(ID::A, new_inputs[0]);
+ cell->setPort(ID::B, new_inputs[1]);
+ cell->setPort(ID(C), new_inputs[2]);
+ cell->setPort(ID(D), new_inputs[3]);
+ } else {
+ // xilinx, gowin
+ cell->setParam(ID(INIT), new_lut);
+ if (techname == "xilinx")
+ log_assert(GetSize(new_inputs) <= 6);
+ else
+ log_assert(GetSize(new_inputs) <= 4);
+ if (GetSize(new_inputs) == 1)
+ cell->type = ID(LUT1);
+ else if (GetSize(new_inputs) == 2)
+ cell->type = ID(LUT2);
+ else if (GetSize(new_inputs) == 3)
+ cell->type = ID(LUT3);
+ else if (GetSize(new_inputs) == 4)
+ cell->type = ID(LUT4);
+ else if (GetSize(new_inputs) == 5)
+ cell->type = ID(LUT5);
+ else if (GetSize(new_inputs) == 6)
+ cell->type = ID(LUT6);
+ else
+ log_assert(0);
+ cell->unsetPort(ID(I0));
+ cell->unsetPort(ID(I1));
+ cell->unsetPort(ID(I2));
+ cell->unsetPort(ID(I3));
+ cell->unsetPort(ID(I4));
+ cell->unsetPort(ID(I5));
+ cell->setPort(ID(I0), new_inputs[0]);
+ if (GetSize(new_inputs) >= 2)
+ cell->setPort(ID(I1), new_inputs[1]);
+ if (GetSize(new_inputs) >= 3)
+ cell->setPort(ID(I2), new_inputs[2]);
+ if (GetSize(new_inputs) >= 4)
+ cell->setPort(ID(I3), new_inputs[3]);
+ if (GetSize(new_inputs) >= 5)
+ cell->setPort(ID(I4), new_inputs[4]);
+ if (GetSize(new_inputs) >= 6)
+ cell->setPort(ID(I5), new_inputs[5]);
+ }
+ }
+ }
+ for (auto cell : remove_cells)
+ module->remove(cell);
+ }
+ }
+} XilinxDffOptPass;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index 6a8d8cabd..f74655d1c 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -44,9 +44,10 @@ struct OptReduceWorker
cells.erase(cell);
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ sig_a.sort_and_unify();
pool<RTLIL::SigBit> new_sig_a_bits;
- for (auto &bit : sig_a.to_sigbit_set())
+ for (auto &bit : sig_a)
{
if (bit == RTLIL::State::S0) {
if (cell->type == ID($reduce_and)) {
@@ -86,6 +87,7 @@ struct OptReduceWorker
}
RTLIL::SigSpec new_sig_a(new_sig_a_bits);
+ new_sig_a.sort_and_unify();
if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) {
log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
@@ -235,7 +237,6 @@ struct OptReduceWorker
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
- module->check();
did_something = true;
total_count++;
@@ -324,6 +325,8 @@ struct OptReduceWorker
opt_mux(cell);
}
}
+
+ module->check();
}
};
diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
index 81c3c57c4..ae7967d7c 100644
--- a/passes/pmgen/xilinx_dsp.cc
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -767,6 +767,9 @@ struct XilinxDspPass : public Pass {
log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n");
log("device.\n");
log("\n");
+ log("This pass is a no-op if the scratchpad variable 'xilinx_dsp.multonly' is set\n");
+ log("to 1.\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");
@@ -805,6 +808,10 @@ struct XilinxDspPass : public Pass {
family = "xcu";
for (auto module : design->selected_modules()) {
+
+ if (design->scratchpad_get_bool("xilinx_dsp.multonly"))
+ continue;
+
// Experimental feature: pack $add/$sub cells with
// (* use_dsp48="simd" *) into DSP48E1's using its
// SIMD feature
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
index 4bb4aa047..f9e7783a9 100644
--- a/passes/sat/clk2fflogic.cc
+++ b/passes/sat/clk2fflogic.cc
@@ -214,14 +214,38 @@ struct Clk2fflogicPass : public Pass {
continue;
}
- if (cell->type.in("$dff", "$adff", "$dffsr"))
+ bool word_dff = cell->type.in("$dff", "$adff", "$dffsr");
+ if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_),
+ ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
+ ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
{
- bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool clkpol;
+ SigSpec clk;
+ if (word_dff) {
+ clkpol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ clk = cell->getPort("\\CLK");
+ }
+ else {
+ if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_),
+ ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
+ clkpol = cell->type[6] == 'P';
+ else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
+ clkpol = cell->type[8] == 'P';
+ else log_abort();
+ clk = cell->getPort("\\C");
+ }
- SigSpec clk = cell->getPort("\\CLK");
Wire *past_clk = module->addWire(NEW_ID);
past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
- module->addFf(NEW_ID, clk, past_clk);
+
+ if (word_dff)
+ module->addFf(NEW_ID, clk, past_clk);
+ else
+ module->addFfGate(NEW_ID, clk, past_clk);
SigSpec sig_d = cell->getPort("\\D");
SigSpec sig_q = cell->getPort("\\Q");
@@ -244,8 +268,14 @@ struct Clk2fflogicPass : public Pass {
Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
- module->addFf(NEW_ID, sig_d, past_d);
- module->addFf(NEW_ID, sig_q, past_q);
+ if (word_dff) {
+ module->addFf(NEW_ID, sig_d, past_d);
+ module->addFf(NEW_ID, sig_q, past_q);
+ }
+ else {
+ module->addFfGate(NEW_ID, sig_d, past_d);
+ module->addFfGate(NEW_ID, sig_q, past_q);
+ }
if (cell->type == "$adff")
{
@@ -266,6 +296,26 @@ struct Clk2fflogicPass : public Pass {
module->addMux(NEW_ID, rstval, qval, arst, sig_q);
}
else
+ if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
+ {
+ SigSpec arst = cell->getPort("\\R");
+ SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
+ SigBit rstval = (cell->type[8] == '1');
+
+ Wire *past_arst = module->addWire(NEW_ID);
+ module->addFfGate(NEW_ID, arst, past_arst);
+ if (cell->type[7] == 'P')
+ arst = module->OrGate(NEW_ID, arst, past_arst);
+ else
+ arst = module->AndGate(NEW_ID, arst, past_arst);
+
+ if (cell->type[7] == 'P')
+ module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q);
+ else
+ module->addMuxGate(NEW_ID, rstval, qval, arst, sig_q);
+ }
+ else
if (cell->type == "$dffsr")
{
SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
@@ -282,9 +332,30 @@ struct Clk2fflogicPass : public Pass {
module->addAnd(NEW_ID, qval, clrval, sig_q);
}
else
+ if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
+ {
+ SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
+ SigSpec setval = cell->getPort("\\S");
+ SigSpec clrval = cell->getPort("\\R");
+
+ if (cell->type[9] != 'P')
+ setval = module->Not(NEW_ID, setval);
+
+ if (cell->type[10] == 'P')
+ clrval = module->Not(NEW_ID, clrval);
+
+ qval = module->OrGate(NEW_ID, qval, setval);
+ module->addAndGate(NEW_ID, qval, clrval, sig_q);
+ }
+ else if (cell->type == "$dff")
{
module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
}
+ else
+ {
+ module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q);
+ }
Const initval;
bool assign_initval = false;
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 369c9de64..c16db0d57 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -13,6 +13,7 @@ OBJS += passes/techmap/abc9_ops.o
ifneq ($(ABCEXTERNAL),)
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
+passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
endif
endif
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 2aeda16d6..5ae2fb22a 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -175,6 +175,7 @@ struct Abc9Pass : public ScriptPass
std::stringstream exe_cmd;
bool dff_mode, cleanup;
+ std::string box_file;
void clear_flags() YS_OVERRIDE
{
@@ -182,6 +183,7 @@ struct Abc9Pass : public ScriptPass
exe_cmd << "abc9_exe";
dff_mode = false;
cleanup = true;
+ box_file.clear();
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -203,7 +205,7 @@ struct Abc9Pass : public ScriptPass
std::string arg = args[argidx];
if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
/* arg == "-S" || */ arg == "-lut" || arg == "-luts" ||
- arg == "-box" || arg == "-W") &&
+ /*arg == "-box" ||*/ arg == "-W") &&
argidx+1 < args.size()) {
exe_cmd << " " << arg << " " << args[++argidx];
continue;
@@ -222,6 +224,10 @@ struct Abc9Pass : public ScriptPass
cleanup = false;
continue;
}
+ if (arg == "-box" && argidx+1 < args.size()) {
+ box_file = args[++argidx];
+ continue;
+ }
if (arg == "-run" && argidx+1 < args.size()) {
size_t pos = args[argidx+1].find(':');
if (pos == std::string::npos)
@@ -251,11 +257,12 @@ struct Abc9Pass : public ScriptPass
void script() YS_OVERRIDE
{
if (check_label("pre")) {
+ run("abc9_ops -check");
run("scc -set_attr abc9_scc_id {}");
if (help_mode)
- run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)");
+ run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)");
else
- run("abc9_ops -mark_scc -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)");
+ run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)");
run("select -set abc9_holes A:abc9_holes");
run("flatten -wb @abc9_holes");
run("techmap @abc9_holes");
@@ -269,8 +276,9 @@ struct Abc9Pass : public ScriptPass
if (check_label("map")) {
if (help_mode) {
run("foreach module in selection");
+ run(" abc9_ops -write_box [<value from -box>|(null)] <abc-temp-dir>/input.box");
run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig");
- run(" abc9_exe -cwd <abc-temp-dir> [options]");
+ run(" abc9_exe [options] -cwd <abc-temp-dir> -box <abc-temp-dir>/input.box");
run(" read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig");
run(" abc9_ops -reintegrate");
}
@@ -296,6 +304,10 @@ struct Abc9Pass : public ScriptPass
tempdir_name[0] = tempdir_name[4] = '_';
tempdir_name = make_temp_dir(tempdir_name);
+ if (box_file.empty())
+ run(stringf("abc9_ops -write_box (null) %s/input.box", tempdir_name.c_str()));
+ else
+ run(stringf("abc9_ops -write_box %s %s/input.box", box_file.c_str(), tempdir_name.c_str()));
run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs");
@@ -307,7 +319,7 @@ struct Abc9Pass : public ScriptPass
active_design->scratchpad_get_int("write_xaiger.num_inputs"),
num_outputs);
if (num_outputs) {
- run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str()));
+ run(stringf("%s -cwd %s -box %s/input.box", exe_cmd.str().c_str(), tempdir_name.c_str(), tempdir_name.c_str()));
run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str()));
run("abc9_ops -reintegrate");
}
diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc
index 01bf46539..71221951c 100644
--- a/passes/techmap/abc9_exe.cc
+++ b/passes/techmap/abc9_exe.cc
@@ -510,9 +510,8 @@ struct Abc9ExePass : public Pass {
}
}
- // ABC expects a box file for XAIG
if (box_file.empty())
- box_file = "+/dummy.box";
+ log_cmd_error("abc9_exe '-box' option is mandatory.\n");
rewrite_filename(box_file);
if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
index 9ad29a8f6..7071f0de4 100644
--- a/passes/techmap/abc9_ops.cc
+++ b/passes/techmap/abc9_ops.cc
@@ -23,6 +23,9 @@
#include "kernel/utils.h"
#include "kernel/celltypes.h"
+#define ABC9_FLOPS_BASE_ID 8000
+#define ABC9_DELAY_BASE_ID 9000
+
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -33,6 +36,110 @@ inline std::string remap_name(RTLIL::IdString abc9_name)
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
}
+void check(RTLIL::Design *design)
+{
+ dict<IdString,IdString> box_lookup;
+ for (auto m : design->modules()) {
+ if (m->name.begins_with("$paramod"))
+ continue;
+
+ auto flop = m->get_bool_attribute(ID(abc9_flop));
+ auto it = m->attributes.find(ID(abc9_box_id));
+ if (!flop) {
+ if (it == m->attributes.end())
+ continue;
+ auto id = it->second.as_int();
+ auto r = box_lookup.insert(std::make_pair(stringf("$__boxid%d", id), m->name));
+ if (!r.second)
+ log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n",
+ log_id(m), id, log_id(r.first->second));
+ }
+
+ // Make carry in the last PI, and carry out the last PO
+ // since ABC requires it this way
+ IdString carry_in, carry_out;
+ for (const auto &port_name : m->ports) {
+ auto w = m->wire(port_name);
+ log_assert(w);
+ if (w->get_bool_attribute("\\abc9_carry")) {
+ if (w->port_input) {
+ if (carry_in != IdString())
+ log_error("Module '%s' contains more than one (* abc9_carry *) input port.\n", log_id(m));
+ carry_in = port_name;
+ }
+ if (w->port_output) {
+ if (carry_out != IdString())
+ log_error("Module '%s' contains more than one (* abc9_carry *) output port.\n", log_id(m));
+ carry_out = port_name;
+ }
+ }
+
+ auto it = w->attributes.find("\\abc9_arrival");
+ if (it != w->attributes.end()) {
+ int count = 0;
+ if (it->second.flags == 0) {
+ if (it->second.as_int() < 0)
+ log_error("%s.%s has negative arrival value %d!\n", log_id(m), log_id(port_name),
+ it->second.as_int());
+ count++;
+ }
+ else
+ for (const auto &tok : split_tokens(it->second.decode_string())) {
+ if (tok.find_first_not_of("0123456789") != std::string::npos)
+ log_error("%s.%s has non-integer arrival value '%s'!\n", log_id(m), log_id(port_name),
+ tok.c_str());
+ if (atoi(tok.c_str()) < 0)
+ log_error("%s.%s has negative arrival value %s!\n", log_id(m), log_id(port_name),
+ tok.c_str());
+ count++;
+ }
+ if (count > 1 && count != GetSize(w))
+ log_error("%s.%s is %d bits wide but abc9_arrival = %s has %d value(s)!\n", log_id(m), log_id(port_name),
+ GetSize(w), log_signal(it->second), count);
+ }
+
+ it = w->attributes.find("\\abc9_required");
+ if (it != w->attributes.end()) {
+ int count = 0;
+ if (it->second.flags == 0) {
+ if (it->second.as_int() < 0)
+ log_error("%s.%s has negative required value %d!\n", log_id(m), log_id(port_name),
+ it->second.as_int());
+ count++;
+ }
+ else
+ for (const auto &tok : split_tokens(it->second.decode_string())) {
+ if (tok.find_first_not_of("0123456789") != std::string::npos)
+ log_error("%s.%s has non-integer required value '%s'!\n", log_id(m), log_id(port_name),
+ tok.c_str());
+ if (atoi(tok.c_str()) < 0)
+ log_error("%s.%s has negative required value %s!\n", log_id(m), log_id(port_name),
+ tok.c_str());
+ count++;
+ }
+ if (count > 1 && count != GetSize(w))
+ log_error("%s.%s is %d bits wide but abc9_required = %s has %d value(s)!\n", log_id(m), log_id(port_name),
+ GetSize(w), log_signal(it->second), count);
+ }
+ }
+
+ if (carry_in != IdString() && carry_out == IdString())
+ log_error("Module '%s' contains an (* abc9_carry *) input port but no output port.\n", log_id(m));
+ if (carry_in == IdString() && carry_out != IdString())
+ log_error("Module '%s' contains an (* abc9_carry *) output port but no input port.\n", log_id(m));
+
+ if (flop) {
+ int num_outputs = 0;
+ for (auto port_name : m->ports) {
+ auto wire = m->wire(port_name);
+ if (wire->port_output) num_outputs++;
+ }
+ if (num_outputs != 1)
+ log_error("Module '%s' with (* abc9_flop *) has %d outputs (expect 1).\n", log_id(m), num_outputs);
+ }
+ }
+}
+
void mark_scc(RTLIL::Module *module)
{
// For every unique SCC found, (arbitrarily) find the first
@@ -169,13 +276,11 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
continue;
auto inst_module = module->design->module(cell->type);
- bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id");
- bool abc9_flop = false;
- if (abc9_box) {
- abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
- if (abc9_flop && !dff)
- continue;
+ bool abc9_flop = inst_module && inst_module->get_bool_attribute("\\abc9_flop");
+ if (abc9_flop && !dff)
+ continue;
+ if ((inst_module && inst_module->attributes.count("\\abc9_box_id")) || abc9_flop) {
auto r = box_ports.insert(cell->type);
if (r.second) {
// Make carry in the last PI, and carry out the last PO
@@ -185,25 +290,15 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
auto w = inst_module->wire(port_name);
log_assert(w);
if (w->get_bool_attribute("\\abc9_carry")) {
- if (w->port_input) {
- if (carry_in != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module));
+ log_assert(w->port_input != w->port_output);
+ if (w->port_input)
carry_in = port_name;
- }
- if (w->port_output) {
- if (carry_out != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module));
+ else if (w->port_output)
carry_out = port_name;
- }
}
else
r.first->second.push_back(port_name);
}
-
- if (carry_in != IdString() && carry_out == IdString())
- log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module));
- if (carry_in == IdString() && carry_out != IdString())
- log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module));
if (carry_in != IdString()) {
r.first->second.push_back(carry_in);
r.first->second.push_back(carry_out);
@@ -266,22 +361,25 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
log_assert(cell);
RTLIL::Module* box_module = design->module(cell->type);
- if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
+ if (!box_module || (!box_module->attributes.count("\\abc9_box_id") && !box_module->get_bool_attribute("\\abc9_flop")))
continue;
cell->attributes["\\abc9_box_seq"] = box_count++;
- IdString derived_name = box_module->derive(design, cell->parameters);
- box_module = design->module(derived_name);
+ IdString derived_type = box_module->derive(design, cell->parameters);
+ box_module = design->module(derived_type);
- auto r = cell_cache.insert(derived_name);
+ auto r = cell_cache.insert(derived_type);
auto &holes_cell = r.first->second;
if (r.second) {
if (box_module->has_processes())
Pass::call_on_module(design, box_module, "proc");
if (box_module->get_bool_attribute("\\whitebox")) {
- holes_cell = holes_module->addCell(cell->name, derived_name);
+ holes_cell = holes_module->addCell(cell->name, derived_type);
+
+ if (box_module->has_processes())
+ Pass::call_on_module(design, box_module, "proc");
int box_inputs = 0;
for (auto port_name : box_ports.at(cell->type)) {
@@ -303,7 +401,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
}
}
else if (w->port_output)
- conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w));
+ conn = holes_module->addWire(stringf("%s.%s", derived_type.c_str(), log_id(port_name)), GetSize(w));
}
// For flops only, create an extra 1-bit input that drives a new wire
@@ -342,6 +440,169 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
}
}
+void prep_delays(RTLIL::Design *design)
+{
+ std::set<int> delays;
+ pool<Module*> flops;
+ std::vector<Cell*> cells;
+ dict<IdString,dict<IdString,std::vector<int>>> requireds_cache;
+ for (auto module : design->selected_modules()) {
+ if (module->processes.size() > 0) {
+ log("Skipping module %s as it contains processes.\n", log_id(module));
+ continue;
+ }
+
+ cells.clear();
+ for (auto cell : module->cells()) {
+ if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_), ID($__ABC9_DELAY)))
+ continue;
+
+ RTLIL::Module* inst_module = module->design->module(cell->type);
+ if (!inst_module)
+ continue;
+ if (!inst_module->get_blackbox_attribute())
+ continue;
+ if (inst_module->get_bool_attribute(ID(abc9_flop))) {
+ IdString derived_type = inst_module->derive(design, cell->parameters);
+ inst_module = design->module(derived_type);
+ log_assert(inst_module);
+ flops.insert(inst_module);
+ continue; // because all flop required times
+ // will be captured in the flop box
+ }
+ if (inst_module->attributes.count(ID(abc9_box_id)))
+ continue;
+ cells.emplace_back(cell);
+ }
+
+ delays.clear();
+ for (auto cell : cells) {
+ RTLIL::Module* inst_module = module->design->module(cell->type);
+ log_assert(inst_module);
+ auto &cell_requireds = requireds_cache[cell->type];
+ for (auto &conn : cell->connections_) {
+ auto port_wire = inst_module->wire(conn.first);
+ if (!port_wire->port_input)
+ continue;
+
+ auto r = cell_requireds.insert(conn.first);
+ auto &requireds = r.first->second;
+ if (r.second) {
+ auto it = port_wire->attributes.find("\\abc9_required");
+ if (it == port_wire->attributes.end())
+ continue;
+ if (it->second.flags == 0) {
+ int delay = it->second.as_int();
+ delays.insert(delay);
+ requireds.emplace_back(delay);
+ }
+ else
+ for (const auto &tok : split_tokens(it->second.decode_string())) {
+ int delay = atoi(tok.c_str());
+ delays.insert(delay);
+ requireds.push_back(delay);
+ }
+ }
+
+ if (requireds.empty())
+ continue;
+
+ SigSpec O = module->addWire(NEW_ID, GetSize(conn.second));
+ auto it = requireds.begin();
+ for (int i = 0; i < GetSize(conn.second); ++i) {
+#ifndef NDEBUG
+ if (ys_debug(1)) {
+ static std::set<std::pair<IdString,IdString>> seen;
+ if (seen.emplace(cell->type, conn.first).second) log("%s.%s abc9_required = %d\n", log_id(cell->type), log_id(conn.first), requireds[i]);
+ }
+#endif
+ auto box = module->addCell(NEW_ID, ID($__ABC9_DELAY));
+ box->setPort(ID(I), conn.second[i]);
+ box->setPort(ID(O), O[i]);
+ box->setParam(ID(DELAY), *it);
+ if (requireds.size() > 1)
+ it++;
+ conn.second[i] = O[i];
+ }
+ }
+ }
+
+ std::stringstream ss;
+ bool first = true;
+ for (auto d : delays) {
+ if (first)
+ first = false;
+ else
+ ss << " ";
+ ss << d;
+ }
+ module->attributes[ID(abc9_delays)] = ss.str();
+ }
+
+ int flops_id = ABC9_FLOPS_BASE_ID;
+ std::stringstream ss;
+ for (auto flop_module : flops) {
+ int num_inputs = 0, num_outputs = 0;
+ for (auto port_name : flop_module->ports) {
+ auto wire = flop_module->wire(port_name);
+ if (wire->port_input) num_inputs++;
+ if (wire->port_output) num_outputs++;
+ }
+ log_assert(num_outputs == 1);
+
+ auto r = flop_module->attributes.insert(ID(abc9_box_id));
+ if (r.second)
+ r.first->second = flops_id++;
+
+ ss << log_id(flop_module) << " " << r.first->second.as_int();
+ ss << " 1 " << num_inputs+1 << " " << num_outputs << std::endl;
+ bool first = true;
+ for (auto port_name : flop_module->ports) {
+ auto wire = flop_module->wire(port_name);
+ if (!wire->port_input)
+ continue;
+ if (first)
+ first = false;
+ else
+ ss << " ";
+ ss << wire->attributes.at("\\abc9_required", 0).as_int();
+ }
+ // Last input is 'abc9_ff.Q'
+ ss << " 0" << std::endl << std::endl;
+ }
+ design->scratchpad_set_string("abc9_ops.box.flops", ss.str());
+}
+
+void write_box(RTLIL::Module *module, const std::string &src, const std::string &dst) {
+ std::ofstream ofs(dst);
+ log_assert(ofs.is_open());
+
+ // Since ABC can only accept one box file, we have to copy
+ // over the existing box file
+ if (src != "(null)") {
+ std::ifstream ifs(src);
+ ofs << ifs.rdbuf() << std::endl;
+ ifs.close();
+ }
+
+ ofs << module->design->scratchpad_get_string("abc9_ops.box.flops");
+
+ auto it = module->attributes.find(ID(abc9_delays));
+ if (it != module->attributes.end()) {
+ for (const auto &tok : split_tokens(it->second.decode_string())) {
+ int d = atoi(tok.c_str());
+ ofs << "$__ABC9_DELAY@" << d << " " << ABC9_DELAY_BASE_ID + d << " 0 1 1" << std::endl;
+ ofs << d << std::endl;
+ }
+ module->attributes.erase(it);
+ }
+
+ if (ofs.tellp() == 0)
+ ofs << "(dummy) 1 0 0 0";
+
+ ofs.close();
+}
+
void reintegrate(RTLIL::Module *module)
{
auto design = module->design;
@@ -363,37 +624,29 @@ void reintegrate(RTLIL::Module *module)
continue;
auto r = box_ports.insert(m->name);
- if (r.second) {
- // Make carry in the last PI, and carry out the last PO
- // since ABC requires it this way
- IdString carry_in, carry_out;
- for (const auto &port_name : m->ports) {
- auto w = m->wire(port_name);
- log_assert(w);
- if (w->get_bool_attribute("\\abc9_carry")) {
- if (w->port_input) {
- if (carry_in != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
- carry_in = port_name;
- }
- if (w->port_output) {
- if (carry_out != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m));
- carry_out = port_name;
- }
- }
- else
- r.first->second.push_back(port_name);
- }
+ if (!r.second)
+ continue;
- if (carry_in != IdString() && carry_out == IdString())
- log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m));
- if (carry_in == IdString() && carry_out != IdString())
- log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
- if (carry_in != IdString()) {
- r.first->second.push_back(carry_in);
- r.first->second.push_back(carry_out);
+ // Make carry in the last PI, and carry out the last PO
+ // since ABC requires it this way
+ IdString carry_in, carry_out;
+ for (const auto &port_name : m->ports) {
+ auto w = m->wire(port_name);
+ log_assert(w);
+ if (w->get_bool_attribute("\\abc9_carry")) {
+ log_assert(w->port_input != w->port_output);
+ if (w->port_input)
+ carry_in = port_name;
+ else if (w->port_output)
+ carry_out = port_name;
}
+ else
+ r.first->second.push_back(port_name);
+ }
+
+ if (carry_in != IdString()) {
+ r.first->second.push_back(carry_in);
+ r.first->second.push_back(carry_out);
}
}
@@ -465,16 +718,6 @@ void reintegrate(RTLIL::Module *module)
}
if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
- // Convert buffer into direct connection
- if (mapped_cell->type == ID($lut) &&
- GetSize(mapped_cell->getPort(ID::A)) == 1 &&
- mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) {
- SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name));
- SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name));
- module->connect(my_y, my_a);
- log_abort();
- continue;
- }
RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
cell->parameters = mapped_cell->parameters;
cell->attributes = mapped_cell->attributes;
@@ -506,12 +749,27 @@ void reintegrate(RTLIL::Module *module)
}
else {
RTLIL::Cell *existing_cell = module->cell(mapped_cell->name);
- log_assert(existing_cell);
+ if (!existing_cell)
+ log_error("Cannot find existing box cell with name '%s' in original design.\n", log_id(mapped_cell));
+
+ if (existing_cell->type == ID($__ABC9_DELAY)) {
+ SigBit I = mapped_cell->getPort(ID(i));
+ SigBit O = mapped_cell->getPort(ID(o));
+ if (I.wire)
+ I.wire = module->wires_.at(remap_name(I.wire->name));
+ log_assert(O.wire);
+ O.wire = module->wires_.at(remap_name(O.wire->name));
+ module->connect(O, I);
+ continue;
+ }
+#ifndef NDEBUG
RTLIL::Module* box_module = design->module(existing_cell->type);
- auto it = box_module->attributes.find(ID(abc9_box_id));
- log_assert(it != box_module->attributes.end());
- log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int()));
+ IdString derived_type = box_module->derive(design, existing_cell->parameters);
+ RTLIL::Module* derived_module = design->module(derived_type);
+ log_assert(derived_module);
+ log_assert(mapped_cell->type == stringf("$__boxid%d", derived_module->attributes.at("\\abc9_box_id").as_int()));
+#endif
mapped_cell->type = existing_cell->type;
RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
@@ -539,7 +797,7 @@ void reintegrate(RTLIL::Module *module)
}
int input_count = 0, output_count = 0;
- for (const auto &port_name : box_ports.at(cell->type)) {
+ for (const auto &port_name : box_ports.at(derived_type)) {
RTLIL::Wire *w = box_module->wire(port_name);
log_assert(w);
@@ -729,6 +987,14 @@ struct Abc9OpsPass : public Pass {
log("mapping, and is expected to be called in conjunction with other operations from\n");
log("the `abc9' script pass. Only fully-selected modules are supported.\n");
log("\n");
+ log(" -check\n");
+ log(" check that the design is valid, e.g. (* abc9_box_id *) values are unique,\n");
+ log(" (* abc9_carry *) is only given for one input/output port, etc.\n");
+ log("\n");
+ log(" -prep_delays\n");
+ log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n");
+ log(" certain delays, e.g. (* abc9_required *) values.\n");
+ log("\n");
log(" -mark_scc\n");
log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n");
log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n");
@@ -749,6 +1015,10 @@ struct Abc9OpsPass : public Pass {
log(" compute the clock domain and initial value of each flop in the design.\n");
log(" process the '$holes' module to support clock-enable functionality.\n");
log("\n");
+ log(" -write_box (<src>|(null)) <dst>\n");
+ log(" copy the existing box file from <src> (skip if '(null)') and append any\n");
+ log(" new box definitions.\n");
+ log("\n");
log(" -reintegrate\n");
log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n");
log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n");
@@ -759,15 +1029,22 @@ struct Abc9OpsPass : public Pass {
{
log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n");
+ bool check_mode = false;
+ bool prep_delays_mode = false;
bool mark_scc_mode = false;
bool prep_dff_mode = false;
bool prep_xaiger_mode = false;
bool reintegrate_mode = false;
bool dff_mode = false;
+ std::string write_box_src, write_box_dst;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
+ if (arg == "-check") {
+ check_mode = true;
+ continue;
+ }
if (arg == "-mark_scc") {
mark_scc_mode = true;
continue;
@@ -780,6 +1057,17 @@ struct Abc9OpsPass : public Pass {
prep_xaiger_mode = true;
continue;
}
+ if (arg == "-prep_delays") {
+ prep_delays_mode = true;
+ continue;
+ }
+ if (arg == "-write_box" && argidx+2 < args.size()) {
+ write_box_src = args[++argidx];
+ write_box_dst = args[++argidx];
+ rewrite_filename(write_box_src);
+ rewrite_filename(write_box_dst);
+ continue;
+ }
if (arg == "-reintegrate") {
reintegrate_mode = true;
continue;
@@ -792,12 +1080,17 @@ struct Abc9OpsPass : public Pass {
}
extra_args(args, argidx, design);
- if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode))
- log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n");
+ if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || !write_box_src.empty() || reintegrate_mode))
+ log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff}, -write_box, -reintegrate must be specified.\n");
if (dff_mode && !prep_xaiger_mode)
log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n");
+ if (check_mode)
+ check(design);
+ if (prep_delays_mode)
+ prep_delays(design);
+
for (auto mod : design->selected_modules()) {
if (mod->get_bool_attribute("\\abc9_holes"))
continue;
@@ -810,6 +1103,8 @@ struct Abc9OpsPass : public Pass {
if (!design->selected_whole_module(mod))
log_error("Can't handle partially selected module %s!\n", log_id(mod));
+ if (!write_box_src.empty())
+ write_box(mod, write_box_src, write_box_dst);
if (mark_scc_mode)
mark_scc(mod);
if (prep_dff_mode)
diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc
index a42f63128..42f1068ad 100644
--- a/techlibs/common/Makefile.inc
+++ b/techlibs/common/Makefile.inc
@@ -29,4 +29,3 @@ $(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))
diff --git a/techlibs/common/dummy.box b/techlibs/common/dummy.box
deleted file mode 100644
index 0c18070a0..000000000
--- a/techlibs/common/dummy.box
+++ /dev/null
@@ -1 +0,0 @@
-(dummy) 1 0 0 0
diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v
index d7ec3947e..75a51e55e 100644
--- a/techlibs/common/techmap.v
+++ b/techlibs/common/techmap.v
@@ -129,47 +129,83 @@ module _90_shift_shiftx (A, B, Y);
input [B_WIDTH-1:0] B;
output [Y_WIDTH-1:0] Y;
- localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH);
- localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0);
-
parameter _TECHMAP_CELLTYPE_ = "";
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
+
localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx;
- wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
- wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";
+ generate
+`ifndef NO_LSB_FIRST_SHIFT_SHIFTX
+ // If $shift/$shiftx only shifts in units of Y_WIDTH
+ // (a common pattern created by pmux2shiftx)
+ // which is checked by ensuring that all that
+ // the appropriate LSBs of B are constant zero,
+ // then we can decompose LSB first instead of
+ // MSB first
+ localparam CLOG2_Y_WIDTH = $clog2(Y_WIDTH);
+ if (B_WIDTH > CLOG2_Y_WIDTH+1 &&
+ _TECHMAP_CONSTMSK_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b1}} &&
+ _TECHMAP_CONSTVAL_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b0}}) begin
+ // Halve the size of $shift/$shiftx by $mux-ing A according to
+ // the LSB of B, after discarding the zeroed bits
+ localparam len = 2**(B_WIDTH-1);
+ localparam Y_WIDTH2 = 2**CLOG2_Y_WIDTH;
+ wire [len-1:0] T, F, AA;
+ genvar i;
+ for (i = 0; i < A_WIDTH; i=i+Y_WIDTH2*2) begin
+ assign F[i/2 +: Y_WIDTH2] = A[i +: Y_WIDTH2];
+ assign T[i/2 +: Y_WIDTH2] = (i + Y_WIDTH2 < A_WIDTH) ? A[i+Y_WIDTH2 +: Y_WIDTH2] : {Y_WIDTH2{extbit}};
+ assign AA[i/2 +: Y_WIDTH2] = B[CLOG2_Y_WIDTH] ? T[i/2 +: Y_WIDTH2] : F[i/2 +: Y_WIDTH2];
+ end
+ wire [B_WIDTH-2:0] BB = {B[B_WIDTH-1:CLOG2_Y_WIDTH+1], {CLOG2_Y_WIDTH{1'b0}}};
+ if (_TECHMAP_CELLTYPE_ == "$shift")
+ $shift #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y));
+ else
+ $shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y));
+ end
+ else
+`endif
+ begin
+ localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH);
+ localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0);
- integer i;
- reg [WIDTH-1:0] buffer;
- reg overflow;
+ wire [1023:0] _TECHMAP_DO_00_ = "proc;;";
+ wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;";
- always @* begin
- overflow = 0;
- buffer = {WIDTH{extbit}};
- buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A;
-
- if (B_WIDTH > BB_WIDTH) begin
- if (B_SIGNED) begin
- for (i = BB_WIDTH; i < B_WIDTH; i = i+1)
- if (B[i] != B[BB_WIDTH-1])
- overflow = 1;
- end else
- overflow = |B[B_WIDTH-1:BB_WIDTH];
- if (overflow)
- buffer = {WIDTH{extbit}};
- end
+ integer i;
+ reg [WIDTH-1:0] buffer;
+ reg overflow;
- for (i = BB_WIDTH-1; i >= 0; i = i-1)
- if (B[i]) begin
- if (B_SIGNED && i == BB_WIDTH-1)
- buffer = {buffer, {2**i{extbit}}};
- else if (2**i < WIDTH)
- buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]};
- else
- buffer = {WIDTH{extbit}};
+ always @* begin
+ overflow = 0;
+ buffer = {WIDTH{extbit}};
+ buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A;
+
+ if (B_WIDTH > BB_WIDTH) begin
+ if (B_SIGNED) begin
+ for (i = BB_WIDTH; i < B_WIDTH; i = i+1)
+ if (B[i] != B[BB_WIDTH-1])
+ overflow = 1;
+ end else
+ overflow = |B[B_WIDTH-1:BB_WIDTH];
+ if (overflow)
+ buffer = {WIDTH{extbit}};
+ end
+
+ for (i = BB_WIDTH-1; i >= 0; i = i-1)
+ if (B[i]) begin
+ if (B_SIGNED && i == BB_WIDTH-1)
+ buffer = {buffer, {2**i{extbit}}};
+ else if (2**i < WIDTH)
+ buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]};
+ else
+ buffer = {WIDTH{extbit}};
+ end
end
- end
-
- assign Y = buffer;
+ assign Y = buffer;
+ end
+ endgenerate
endmodule
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 6583f43fd..bce20f604 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -343,6 +343,7 @@ struct SynthEcp5Pass : public ScriptPass
else
run("techmap -map +/ecp5/cells_map.v", "(with -D NO_LUT in vpr mode)");
+ run("opt_lut_ins -tech ecp5");
run("clean");
}
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
index c5b41b503..99dd3834b 100644
--- a/techlibs/gowin/synth_gowin.cc
+++ b/techlibs/gowin/synth_gowin.cc
@@ -246,6 +246,7 @@ struct SynthGowinPass : public ScriptPass
if (check_label("map_cells"))
{
run("techmap -map +/gowin/cells_map.v");
+ run("opt_lut_ins -tech gowin");
run("setundef -undriven -params -zero");
run("hilomap -singleton -hicell VCC V -locell GND G");
if (!noiopads || help_mode)
diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc
index 2ebb8bf50..3689df70e 100644
--- a/techlibs/intel/synth_intel.cc
+++ b/techlibs/intel/synth_intel.cc
@@ -26,7 +26,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct SynthIntelPass : public ScriptPass {
- SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") {}
+ SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") { experimental(); }
void help() YS_OVERRIDE
{
diff --git a/techlibs/xilinx/abc9_model.v b/techlibs/xilinx/abc9_model.v
index 15d12c89f..782c53ab6 100644
--- a/techlibs/xilinx/abc9_model.v
+++ b/techlibs/xilinx/abc9_model.v
@@ -33,6 +33,11 @@ endmodule
module \$__ABC9_FF_ (input D, output Q);
endmodule
+(* abc9_box_id = (9000+DELAY) *)
+module \$__ABC9_DELAY (input I, output O);
+ parameter DELAY = 0;
+endmodule
+
// Box to emulate async behaviour of FDC*
(* abc9_box_id = 1000, lib_whitebox *)
module \$__ABC9_ASYNC0 (input A, S, output Y);
diff --git a/techlibs/xilinx/abc9_xc7.box b/techlibs/xilinx/abc9_xc7.box
index 13f4f0e61..48d492801 100644
--- a/techlibs/xilinx/abc9_xc7.box
+++ b/techlibs/xilinx/abc9_xc7.box
@@ -62,67 +62,6 @@ $__ABC9_ASYNC1 1001 1 2 1
#A S
0 764 # Y
-# Flop boxes:
-# * Max delays from https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf#L237-L251
-# https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf#L265-L277
-# * Exception: $abc9_currQ is a special input (located last) necessary for clock-enable functionality
-
-# Box 1100 : FDRE
-# name ID w/b ins outs
-FDRE 1100 1 5 1
-#C CE D R $abc9_currQ
-#0 109 -46 404 0
-0 109 0 404 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1101 : FDRE_1
-# name ID w/b ins outs
-FDRE_1 1101 1 5 1
-#C CE D R $abc9_currQ
-#0 109 -46 404 0
-0 109 0 404 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1102 : FDSE
-# name ID w/b ins outs
-FDSE 1102 1 5 1
-#C CE D R $abc9_currQ
-#0 109 -46 404 0
-0 109 0 404 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1103 : FDSE_1
-# name ID w/b ins outs
-FDSE_1 1103 1 5 1
-#C CE D R $abc9_currQ
-#0 109 -46 404 0
-0 109 0 404 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1104 : FDCE
-# name ID w/b ins outs
-FDCE 1104 1 5 1
-#C CE CLR D $abc9_currQ
-#0 109 764 -46 0
-0 109 764 0 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1105 : FDCE_1
-# name ID w/b ins outs
-FDCE_1 1105 1 5 1
-#C CE CLR D $abc9_currQ
-#0 109 764 -46 0
-0 109 764 0 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1106 : FDPE
-# name ID w/b ins outs
-FDPE 1106 1 5 1
-#C CE D PRE $abc9_currQ
-#0 109 -46 764 0
-0 109 0 764 0 # Q (-46ps Tsu clamped to 0)
-
-# Box 1107 : FDPE_1
-# name ID w/b ins outs
-FDPE_1 1107 1 5 1
-#C CE D PRE $abc9_currQ
-#0 109 -46 764 0
-0 109 0 764 0 # Q (-46ps Tsu clamped to 0)
-
# Box 2000 : $__ABC9_LUT6
# (private cell to emulate async behaviour of LUTRAMs)
# SLICEM/A6LUT
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 22dca3c47..4692eba33 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -325,17 +325,20 @@ endmodule
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250
-(* abc9_box_id=1100, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDRE (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
(* invertible_pin = "IS_C_INVERTED" *)
input C,
+ (* abc9_required=109 *)
input CE,
(* invertible_pin = "IS_D_INVERTED" *)
+ //(* abc9_required=-46 *) // Negative required times not currently supported
input D,
(* invertible_pin = "IS_R_INVERTED" *)
+ (* abc9_required=404 *)
input R
);
parameter [0:0] INIT = 1'b0;
@@ -349,30 +352,38 @@ module FDRE (
endcase endgenerate
endmodule
-(* abc9_box_id=1101, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDRE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
- input CE, D, R
+ (* abc9_required=109 *)
+ input CE,
+ //(* abc9_required=-46 *) // Negative required times not currently supported
+ input D,
+ (* abc9_required=404 *)
+ input R
);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C) if (R) Q <= 1'b0; else if (CE) Q <= D;
endmodule
-(* abc9_box_id=1102, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDSE (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
(* invertible_pin = "IS_C_INVERTED" *)
input C,
+ (* abc9_required=109 *)
input CE,
(* invertible_pin = "IS_D_INVERTED" *)
+ //(* abc9_required=-46 *) // Negative required times not currently supported
input D,
(* invertible_pin = "IS_S_INVERTED" *)
+ (* abc9_required=404 *)
input S
);
parameter [0:0] INIT = 1'b1;
@@ -386,13 +397,18 @@ module FDSE (
endcase endgenerate
endmodule
-(* abc9_box_id=1103, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDSE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
- input CE, D, S
+ (* abc9_required=109 *)
+ input CE,
+ //(* abc9_required=-46 *) // Negative required times not currently supported
+ input D,
+ (* abc9_required=404 *)
+ input S
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
@@ -405,6 +421,7 @@ module FDRSE (
(* invertible_pin = "IS_C_INVERTED" *)
input C,
(* invertible_pin = "IS_CE_INVERTED" *)
+ (* abc9_required=109 *)
input CE,
(* invertible_pin = "IS_D_INVERTED" *)
input D,
@@ -434,17 +451,20 @@ module FDRSE (
Q <= d;
endmodule
-(* abc9_box_id=1104, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDCE (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
(* invertible_pin = "IS_C_INVERTED" *)
input C,
+ (* abc9_required=109 *)
input CE,
(* invertible_pin = "IS_CLR_INVERTED" *)
+ (* abc9_required=764 *)
input CLR,
(* invertible_pin = "IS_D_INVERTED" *)
+ //(* abc9_required=-46 *) // Negative required times not currently supported
input D
);
parameter [0:0] INIT = 1'b0;
@@ -460,30 +480,38 @@ module FDCE (
endcase endgenerate
endmodule
-(* abc9_box_id=1105, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDCE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
- input CE, D, CLR
+ (* abc9_required=109 *)
+ input CE,
+ (* abc9_required=764 *)
+ input CLR,
+ //(* abc9_required=-46 *) // Negative required times not currently supported
+ input D
);
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
-(* abc9_box_id=1106, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDPE (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
(* invertible_pin = "IS_C_INVERTED" *)
input C,
+ (* abc9_required=109 *)
input CE,
(* invertible_pin = "IS_D_INVERTED" *)
+ //(* abc9_required=-46 *) // Negative required times not currently supported
input D,
(* invertible_pin = "IS_PRE_INVERTED" *)
+ (* abc9_required=764 *)
input PRE
);
parameter [0:0] INIT = 1'b1;
@@ -499,13 +527,18 @@ module FDPE (
endcase endgenerate
endmodule
-(* abc9_box_id=1107, lib_whitebox, abc9_flop *)
+(* abc9_flop, lib_whitebox *)
module FDPE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
- input CE, D, PRE
+ (* abc9_required=109 *)
+ input CE,
+ //(* abc9_required=-46 *) // Negative required times not currently supported
+ input D,
+ (* abc9_required=764 *)
+ input PRE
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
@@ -1120,15 +1153,33 @@ module RAM16X1D_1 (
endmodule
module RAM32X1D (
- // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857
- (* abc9_arrival=1188 *)
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981
+ (* abc9_arrival=1153 *)
output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986
+ (* abc9_required=453 *)
input D,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
input WE,
- input A0, A1, A2, A3, A4,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L800
+ (* abc9_required=245 *)
+ input A0,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/clBLM_R.sdf#L798
+ (* abc9_required=208 *)
+ input A1,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L796
+ (* abc9_required=147 *)
+ input A2,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L794
+ (* abc9_required=68 *)
+ input A3,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792
+ (* abc9_required=66 *)
+ input A4,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
);
parameter INIT = 32'h0;
@@ -1143,15 +1194,33 @@ module RAM32X1D (
endmodule
module RAM32X1D_1 (
- // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857
- (* abc9_arrival=1188 *)
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981
+ (* abc9_arrival=1153 *)
output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986
+ (* abc9_required=453 *)
input D,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
input WE,
- input A0, A1, A2, A3, A4,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L800
+ (* abc9_required=245 *)
+ input A0,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/clBLM_R.sdf#L798
+ (* abc9_required=208 *)
+ input A1,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L796
+ (* abc9_required=147 *)
+ input A2,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L794
+ (* abc9_required=68 *)
+ input A3,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792
+ (* abc9_required=66 *)
+ input A4,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
);
parameter INIT = 32'h0;
@@ -1166,15 +1235,36 @@ module RAM32X1D_1 (
endmodule
module RAM64X1D (
- // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981
(* abc9_arrival=1153 *)
output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986
+ (* abc9_required=453 *)
input D,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
input WE,
- input A0, A1, A2, A3, A4, A5,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L828
+ (* abc9_required=362 *)
+ input A0,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L826
+ (* abc9_required=245 *)
+ input A1,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L824
+ (* abc9_required=208 *)
+ input A2,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L822
+ (* abc9_required=147 *)
+ input A3,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L820
+ (* abc9_required=68 *)
+ input A4,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818
+ (* abc9_required=66 *)
+ input A5,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
);
parameter INIT = 64'h0;
@@ -1189,15 +1279,36 @@ module RAM64X1D (
endmodule
module RAM64X1D_1 (
- // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981
(* abc9_arrival=1153 *)
output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986
+ (* abc9_required=453 *)
input D,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
input WE,
- input A0, A1, A2, A3, A4, A5,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L828
+ (* abc9_required=362 *)
+ input A0,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L826
+ (* abc9_required=245 *)
+ input A1,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L824
+ (* abc9_required=208 *)
+ input A2,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L822
+ (* abc9_required=147 *)
+ input A3,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L820
+ (* abc9_required=68 *)
+ input A4,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818
+ (* abc9_required=66 *)
+ input A5,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
);
parameter INIT = 64'h0;
@@ -1212,16 +1323,23 @@ module RAM64X1D_1 (
endmodule
module RAM128X1D (
- // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889
- // plus 204ps to cross MUXF7
- (* abc9_arrival=1357 *)
- output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981
+ // plus 208ps to cross MUXF7
+ (* abc9_arrival=1359 *)
+ output DPO, SPO,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986
+ (* abc9_required=453 *)
input D,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
input WE,
- input [6:0] A, DPRA
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818-830
+ (* abc9_required="616 362 245 208 147 68 66" *)
+ input [6:0] A,
+ input [6:0] DPRA
);
parameter INIT = 128'h0;
parameter IS_WCLK_INVERTED = 1'b0;
@@ -1253,24 +1371,44 @@ endmodule
// Multi port.
module RAM32M (
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857
- (* abc9_arrival=1188 *)
+ (* abc9_arrival="1153 1188" *)
output [1:0] DOA,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L925
- (* abc9_arrival=1187 *)
+ (* abc9_arrival="1161 1187" *)
output [1:0] DOB,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1025
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L993
- (* abc9_arrival=1180 *)
+ (* abc9_arrival="1158 1180" *)
output [1:0] DOC,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1093
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1061
- (* abc9_arrival=1190 *)
+ (* abc9_arrival="1163 1190" *)
output [1:0] DOD,
- input [4:0] ADDRA, ADDRB, ADDRC, ADDRD,
- input [1:0] DIA, DIB, DIC, DID,
+ input [4:0] ADDRA, ADDRB, ADDRC,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792-L802
+ (* abc9_required="245 208 147 68 66" *)
+ input [4:0] ADDRD,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986-L988
+ (* abc9_required="453 384" *)
+ input [1:0] DIA,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1054-L1056
+ (* abc9_required="461 354" *)
+ input [1:0] DIB,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1122-L1124
+ (* abc9_required="457 375" *)
+ input [1:0] DIC,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1190-L1192
+ (* abc9_required="310 334" *)
+ input [1:0] DID,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
- input WCLK,
- input WE
+ input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
+ input WE
);
parameter [63:0] INIT_A = 64'h0000000000000000;
parameter [63:0] INIT_B = 64'h0000000000000000;
@@ -1367,22 +1505,38 @@ endmodule
module RAM64M (
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889
(* abc9_arrival=1153 *)
- output DOA,
+ output DOA,
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
(* abc9_arrival=1161 *)
- output DOB,
+ output DOB,
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1025
(* abc9_arrival=1158 *)
- output DOC,
+ output DOC,
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1093
(* abc9_arrival=1163 *)
- output DOD,
- input [5:0] ADDRA, ADDRB, ADDRC, ADDRD,
- input DIA, DIB, DIC, DID,
+ output DOD,
+ input [5:0] ADDRA, ADDRB, ADDRC,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818-L830
+ (* abc9_required="362 245 208 147 68 66" *)
+ input [5:0] ADDRD,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986-L988
+ (* abc9_required=384 *)
+ input DIA,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1054-L1056
+ (* abc9_required=354 *)
+ input DIB,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1122-L1124
+ (* abc9_required=375 *)
+ input DIC,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1190-L1192
+ (* abc9_required=310 *)
+ input DID,
(* clkbuf_sink *)
(* invertible_pin = "IS_WCLK_INVERTED" *)
- input WCLK,
- input WE
+ input WCLK,
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834
+ (* abc9_required=654 *)
+ input WE
);
parameter [63:0] INIT_A = 64'h0000000000000000;
parameter [63:0] INIT_B = 64'h0000000000000000;
@@ -2155,7 +2309,235 @@ assign PCOUT = P;
endmodule
-// TODO: DSP48 (Virtex 4).
+module DSP48 (
+ input signed [17:0] A,
+ input signed [17:0] B,
+ input signed [47:0] C,
+ input signed [17:0] BCIN,
+ input signed [47:0] PCIN,
+ input CARRYIN,
+ input [6:0] OPMODE,
+ input SUBTRACT,
+ input [1:0] CARRYINSEL,
+ output signed [47:0] P,
+ output signed [17:0] BCOUT,
+ output signed [47:0] PCOUT,
+ (* clkbuf_sink *)
+ input CLK,
+ input CEA,
+ input CEB,
+ input CEC,
+ input CEM,
+ input CECARRYIN,
+ input CECINSUB,
+ input CECTRL,
+ input CEP,
+ input RSTA,
+ input RSTB,
+ input RSTC,
+ input RSTM,
+ input RSTCARRYIN,
+ input RSTCTRL,
+ input RSTP
+);
+
+parameter integer AREG = 1;
+parameter integer BREG = 1;
+parameter integer CREG = 1;
+parameter integer MREG = 1;
+parameter integer PREG = 1;
+parameter integer CARRYINREG = 1;
+parameter integer CARRYINSELREG = 1;
+parameter integer OPMODEREG = 1;
+parameter integer SUBTRACTREG = 1;
+parameter B_INPUT = "DIRECT";
+parameter LEGACY_MODE = "MULT18X18S";
+
+wire signed [17:0] A_OUT;
+wire signed [17:0] B_OUT;
+wire signed [47:0] C_OUT;
+wire signed [35:0] M_MULT;
+wire signed [35:0] M_OUT;
+wire signed [47:0] P_IN;
+wire [6:0] OPMODE_OUT;
+wire [1:0] CARRYINSEL_OUT;
+wire CARRYIN_OUT;
+wire SUBTRACT_OUT;
+reg INT_CARRYIN_XY;
+reg INT_CARRYIN_Z;
+reg signed [47:0] XMUX;
+reg signed [47:0] YMUX;
+wire signed [47:0] XYMUX;
+reg signed [47:0] ZMUX;
+reg CIN;
+
+// The B input multiplexer.
+wire signed [17:0] B_MUX;
+assign B_MUX = (B_INPUT == "DIRECT") ? B : BCIN;
+
+// The cascade output.
+assign BCOUT = B_OUT;
+assign PCOUT = P;
+
+// The registers.
+reg signed [17:0] A0_REG;
+reg signed [17:0] A1_REG;
+reg signed [17:0] B0_REG;
+reg signed [17:0] B1_REG;
+reg signed [47:0] C_REG;
+reg signed [35:0] M_REG;
+reg signed [47:0] P_REG;
+reg [6:0] OPMODE_REG;
+reg [1:0] CARRYINSEL_REG;
+reg SUBTRACT_REG;
+reg CARRYIN_REG;
+reg INT_CARRYIN_XY_REG;
+
+initial begin
+ A0_REG = 0;
+ A1_REG = 0;
+ B0_REG = 0;
+ B1_REG = 0;
+ C_REG = 0;
+ M_REG = 0;
+ P_REG = 0;
+ OPMODE_REG = 0;
+ CARRYINSEL_REG = 0;
+ SUBTRACT_REG = 0;
+ CARRYIN_REG = 0;
+ INT_CARRYIN_XY_REG = 0;
+end
+
+always @(posedge CLK) begin
+ if (RSTA) begin
+ A0_REG <= 0;
+ A1_REG <= 0;
+ end else if (CEA) begin
+ A0_REG <= A;
+ A1_REG <= A0_REG;
+ end
+ if (RSTB) begin
+ B0_REG <= 0;
+ B1_REG <= 0;
+ end else if (CEB) begin
+ B0_REG <= B_MUX;
+ B1_REG <= B0_REG;
+ end
+ if (RSTC) begin
+ C_REG <= 0;
+ end else if (CEC) begin
+ C_REG <= C;
+ end
+ if (RSTM) begin
+ M_REG <= 0;
+ end else if (CEM) begin
+ M_REG <= M_MULT;
+ end
+ if (RSTP) begin
+ P_REG <= 0;
+ end else if (CEP) begin
+ P_REG <= P_IN;
+ end
+ if (RSTCTRL) begin
+ OPMODE_REG <= 0;
+ CARRYINSEL_REG <= 0;
+ SUBTRACT_REG <= 0;
+ end else begin
+ if (CECTRL) begin
+ OPMODE_REG <= OPMODE;
+ CARRYINSEL_REG <= CARRYINSEL;
+ end
+ if (CECINSUB)
+ SUBTRACT_REG <= SUBTRACT;
+ end
+ if (RSTCARRYIN) begin
+ CARRYIN_REG <= 0;
+ INT_CARRYIN_XY_REG <= 0;
+ end else begin
+ if (CECINSUB)
+ CARRYIN_REG <= CARRYIN;
+ if (CECARRYIN)
+ INT_CARRYIN_XY_REG <= INT_CARRYIN_XY;
+ end
+end
+
+// The register enables.
+assign A_OUT = (AREG == 2) ? A1_REG : (AREG == 1) ? A0_REG : A;
+assign B_OUT = (BREG == 2) ? B1_REG : (BREG == 1) ? B0_REG : B_MUX;
+assign C_OUT = (CREG == 1) ? C_REG : C;
+assign M_OUT = (MREG == 1) ? M_REG : M_MULT;
+assign P = (PREG == 1) ? P_REG : P_IN;
+assign OPMODE_OUT = (OPMODEREG == 1) ? OPMODE_REG : OPMODE;
+assign SUBTRACT_OUT = (SUBTRACTREG == 1) ? SUBTRACT_REG : SUBTRACT;
+assign CARRYINSEL_OUT = (CARRYINSELREG == 1) ? CARRYINSEL_REG : CARRYINSEL;
+assign CARRYIN_OUT = (CARRYINREG == 1) ? CARRYIN_REG : CARRYIN;
+
+// The multiplier.
+assign M_MULT = A_OUT * B_OUT;
+
+// The post-adder inputs.
+always @* begin
+ case (OPMODE_OUT[1:0])
+ 2'b00: XMUX <= 0;
+ 2'b10: XMUX <= P;
+ 2'b11: XMUX <= {{12{A_OUT[17]}}, A_OUT, B_OUT};
+ default: XMUX <= 48'hxxxxxxxxxxxx;
+ endcase
+ case (OPMODE_OUT[1:0])
+ 2'b01: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17];
+ 2'b11: INT_CARRYIN_XY <= ~A_OUT[17];
+ // TODO: not tested in hardware.
+ default: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17];
+ endcase
+end
+
+always @* begin
+ case (OPMODE_OUT[3:2])
+ 2'b00: YMUX <= 0;
+ 2'b11: YMUX <= C_OUT;
+ default: YMUX <= 48'hxxxxxxxxxxxx;
+ endcase
+end
+
+assign XYMUX = (OPMODE_OUT[3:0] == 4'b0101) ? M_OUT : (XMUX + YMUX);
+
+always @* begin
+ case (OPMODE_OUT[6:4])
+ 3'b000: ZMUX <= 0;
+ 3'b001: ZMUX <= PCIN;
+ 3'b010: ZMUX <= P;
+ 3'b011: ZMUX <= C_OUT;
+ 3'b101: ZMUX <= {{17{PCIN[47]}}, PCIN[47:17]};
+ 3'b110: ZMUX <= {{17{P[47]}}, P[47:17]};
+ default: ZMUX <= 48'hxxxxxxxxxxxx;
+ endcase
+ // TODO: check how all this works on actual hw.
+ if (OPMODE_OUT[1:0] == 2'b10)
+ INT_CARRYIN_Z <= ~P[47];
+ else
+ case (OPMODE_OUT[6:4])
+ 3'b001: INT_CARRYIN_Z <= ~PCIN[47];
+ 3'b010: INT_CARRYIN_Z <= ~P[47];
+ 3'b101: INT_CARRYIN_Z <= ~PCIN[47];
+ 3'b110: INT_CARRYIN_Z <= ~P[47];
+ default: INT_CARRYIN_Z <= 1'bx;
+ endcase
+end
+
+always @* begin
+ case (CARRYINSEL_OUT)
+ 2'b00: CIN <= CARRYIN_OUT;
+ 2'b01: CIN <= INT_CARRYIN_Z;
+ 2'b10: CIN <= INT_CARRYIN_XY;
+ 2'b11: CIN <= INT_CARRYIN_XY_REG;
+ default: CIN <= 1'bx;
+ endcase
+end
+
+// The post-adder.
+assign P_IN = SUBTRACT_OUT ? (ZMUX - (XYMUX + CIN)) : (ZMUX + XYMUX + CIN);
+
+endmodule
// TODO: DSP48E (Virtex 5).
@@ -2169,21 +2551,30 @@ module DSP48E1 (
output reg MULTSIGNOUT,
output OVERFLOW,
`ifdef YOSYS
- (* abc9_arrival = \DSP48E1.P_arrival () *)
+ (* abc9_arrival = \P.abc9_arrival () *)
`endif
output reg signed [47:0] P,
output reg PATTERNBDETECT,
output reg PATTERNDETECT,
`ifdef YOSYS
- (* abc9_arrival = \DSP48E1.PCOUT_arrival () *)
+ (* abc9_arrival = \PCOUT.abc9_arrival () *)
`endif
output [47:0] PCOUT,
output UNDERFLOW,
+`ifdef YOSYS
+ (* abc9_required = \A.abc9_required () *)
+`endif
input signed [29:0] A,
input [29:0] ACIN,
input [3:0] ALUMODE,
+`ifdef YOSYS
+ (* abc9_required = \B.abc9_required () *)
+`endif
input signed [17:0] B,
input [17:0] BCIN,
+`ifdef YOSYS
+ (* abc9_required = \C.abc9_required () *)
+`endif
input [47:0] C,
input CARRYCASCIN,
input CARRYIN,
@@ -2202,10 +2593,16 @@ module DSP48E1 (
input CEM,
input CEP,
(* clkbuf_sink *) input CLK,
+`ifdef YOSYS
+ (* abc9_required = \D.abc9_required () *)
+`endif
input [24:0] D,
input [4:0] INMODE,
input MULTSIGNIN,
input [6:0] OPMODE,
+`ifdef YOSYS
+ (* abc9_required = \PCIN.abc9_required () *)
+`endif
input [47:0] PCIN,
input RSTA,
input RSTALLCARRYIN,
@@ -2250,69 +2647,133 @@ module DSP48E1 (
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
`ifdef YOSYS
- function integer \DSP48E1.P_arrival ;
+ function integer \A.abc9_required ;
+ begin
+ \A.abc9_required = 0;
+ if (AREG != 0) \A.abc9_required = 254;
+ else if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
+ if (MREG != 0) \A.abc9_required = 1416;
+ else if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 3030 : 2739) ;
+ end
+ else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
+ // Worst-case from ADREG and MREG
+ if (MREG != 0) \A.abc9_required = 2400;
+ else if (ADREG != 0) \A.abc9_required = 1283;
+ else if (PREG != 0) \A.abc9_required = 3723;
+ else if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 4014 : 3723) ;
+ end
+ else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
+ if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1730 : 1441) ;
+ end
+ end
+ endfunction
+ function integer \B.abc9_required ;
+ begin
+ \B.abc9_required = 0;
+ if (BREG != 0) \B.abc9_required = 324;
+ else if (MREG != 0) \B.abc9_required = 1285;
+ else if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
+ if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 2898 : 2608) ;
+ end
+ else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
+ if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 2898 : 2608) ;
+ end
+ else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
+ if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1718 : 1428) ;
+ end
+ end
+ endfunction
+ function integer \C.abc9_required ;
+ begin
+ \C.abc9_required = 0;
+ if (CREG != 0) \C.abc9_required = 168;
+ else if (PREG != 0) \C.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1534 : 1244) ;
+ end
+ endfunction
+ function integer \D.abc9_required ;
+ begin
+ \D.abc9_required = 0;
+ if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
+ end
+ else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
+ if (DREG != 0) \D.abc9_required = 248;
+ else if (ADREG != 0) \D.abc9_required = 1195;
+ else if (MREG != 0) \D.abc9_required = 2310;
+ else if (PREG != 0) \D.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 3925 : 3635) ;
+ end
+ else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
+ end
+ end
+ endfunction
+ function integer \PCIN.abc9_required ;
+ begin
+ \PCIN.abc9_required = 0;
+ if (PREG != 0) \PCIN.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1315 : 1025) ;
+ end
+ endfunction
+ function integer \P.abc9_arrival ;
begin
- \DSP48E1.P_arrival = 0;
+ \P.abc9_arrival = 0;
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
- if (PREG != 0) \DSP48E1.P_arrival = 329;
+ if (PREG != 0) \P.abc9_arrival = 329;
// Worst-case from CREG and MREG
- else if (CREG != 0) \DSP48E1.P_arrival = 1687;
- else if (MREG != 0) \DSP48E1.P_arrival = 1671;
+ else if (CREG != 0) \P.abc9_arrival = 1687;
+ else if (MREG != 0) \P.abc9_arrival = 1671;
// Worst-case from AREG and BREG
- else if (AREG != 0) \DSP48E1.P_arrival = 2952;
- else if (BREG != 0) \DSP48E1.P_arrival = 2813;
+ else if (AREG != 0) \P.abc9_arrival = 2952;
+ else if (BREG != 0) \P.abc9_arrival = 2813;
end
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
- if (PREG != 0) \DSP48E1.P_arrival = 329;
+ if (PREG != 0) \P.abc9_arrival = 329;
// Worst-case from CREG and MREG
- else if (CREG != 0) \DSP48E1.P_arrival = 1687;
- else if (MREG != 0) \DSP48E1.P_arrival = 1671;
+ else if (CREG != 0) \P.abc9_arrival = 1687;
+ else if (MREG != 0) \P.abc9_arrival = 1671;
// Worst-case from AREG, ADREG, BREG, DREG
- else if (AREG != 0) \DSP48E1.P_arrival = 3935;
- else if (DREG != 0) \DSP48E1.P_arrival = 3908;
- else if (ADREG != 0) \DSP48E1.P_arrival = 2958;
- else if (BREG != 0) \DSP48E1.P_arrival = 2813;
+ else if (AREG != 0) \P.abc9_arrival = 3935;
+ else if (DREG != 0) \P.abc9_arrival = 3908;
+ else if (ADREG != 0) \P.abc9_arrival = 2958;
+ else if (BREG != 0) \P.abc9_arrival = 2813;
end
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
- if (PREG != 0) \DSP48E1.P_arrival = 329;
+ if (PREG != 0) \P.abc9_arrival = 329;
// Worst-case from AREG, BREG, CREG
- else if (CREG != 0) \DSP48E1.P_arrival = 1687;
- else if (AREG != 0) \DSP48E1.P_arrival = 1632;
- else if (BREG != 0) \DSP48E1.P_arrival = 1616;
+ else if (CREG != 0) \P.abc9_arrival = 1687;
+ else if (AREG != 0) \P.abc9_arrival = 1632;
+ else if (BREG != 0) \P.abc9_arrival = 1616;
end
//else
// $error("Invalid DSP48E1 configuration");
end
endfunction
- function integer \DSP48E1.PCOUT_arrival ;
+ function integer \PCOUT.abc9_arrival ;
begin
- \DSP48E1.PCOUT_arrival = 0;
+ \PCOUT.abc9_arrival = 0;
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
- if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
+ if (PREG != 0) \PCOUT.abc9_arrival = 435;
// Worst-case from CREG and MREG
- else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
- else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819;
+ else if (CREG != 0) \PCOUT.abc9_arrival = 1835;
+ else if (MREG != 0) \PCOUT.abc9_arrival = 1819;
// Worst-case from AREG and BREG
- else if (AREG != 0) \DSP48E1.PCOUT_arrival = 3098;
- else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960;
+ else if (AREG != 0) \PCOUT.abc9_arrival = 3098;
+ else if (BREG != 0) \PCOUT.abc9_arrival = 2960;
end
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
- if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
+ if (PREG != 0) \PCOUT.abc9_arrival = 435;
// Worst-case from CREG and MREG
- else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
- else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819;
+ else if (CREG != 0) \PCOUT.abc9_arrival = 1835;
+ else if (MREG != 0) \PCOUT.abc9_arrival = 1819;
// Worst-case from AREG, ADREG, BREG, DREG
- else if (AREG != 0) \DSP48E1.PCOUT_arrival = 4083;
- else if (DREG != 0) \DSP48E1.PCOUT_arrival = 4056;
- else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960;
- else if (ADREG != 0) \DSP48E1.PCOUT_arrival = 2859;
+ else if (AREG != 0) \PCOUT.abc9_arrival = 4083;
+ else if (DREG != 0) \PCOUT.abc9_arrival = 4056;
+ else if (BREG != 0) \PCOUT.abc9_arrival = 2960;
+ else if (ADREG != 0) \PCOUT.abc9_arrival = 2859;
end
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
- if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
+ if (PREG != 0) \PCOUT.abc9_arrival = 435;
// Worst-case from AREG, BREG, CREG
- else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
- else if (AREG != 0) \DSP48E1.PCOUT_arrival = 1780;
- else if (BREG != 0) \DSP48E1.PCOUT_arrival = 1765;
+ else if (CREG != 0) \PCOUT.abc9_arrival = 1835;
+ else if (AREG != 0) \PCOUT.abc9_arrival = 1780;
+ else if (BREG != 0) \PCOUT.abc9_arrival = 1765;
end
//else
// $error("Invalid DSP48E1 configuration");
diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py
index d5c58c5d7..75646f594 100644
--- a/techlibs/xilinx/cells_xtra.py
+++ b/techlibs/xilinx/cells_xtra.py
@@ -180,18 +180,58 @@ CELLS = [
Cell('RAMB18E1', port_attrs={
'CLKARDCLK': ['clkbuf_sink'],
'CLKBWRCLK': ['clkbuf_sink'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L143
'DOADO': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L163
'DOBDO': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L144
'DOPADOP': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L164
'DOPBDOP': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L13
+ 'ADDRARDADDR': ['abc9_required=566'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L17
+ 'ADDRBWRADDR': ['abc9_required=566'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L19
+ 'WEA': ['abc9_required=532'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L21
+ 'WEBWE': ['abc9_required=532'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L123
+ 'DIADI': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L133
+ 'DIBDI': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L125
+ 'DIPADIP': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L135
+ 'DIPBDIP': ['abc9_required=737'],
}),
Cell('RAMB36E1', port_attrs={
'CLKARDCLK': ['clkbuf_sink'],
'CLKBWRCLK': ['clkbuf_sink'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L143
'DOADO': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L163
'DOBDO': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L144
'DOPADOP': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L164
'DOPBDOP': ['abc9_arrival=2454'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L13
+ 'ADDRARDADDR': ['abc9_required=566'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L17
+ 'ADDRBWRADDR': ['abc9_required=566'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L19
+ 'WEA': ['abc9_required=532'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L21
+ 'WEBWE': ['abc9_required=532'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L123
+ 'DIADI': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L133
+ 'DIBDI': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L125
+ 'DIPADIP': ['abc9_required=737'],
+ # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L135
+ 'DIPBDIP': ['abc9_required=737'],
}),
# Ultrascale.
Cell('FIFO18E2', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
@@ -209,7 +249,7 @@ CELLS = [
# Cell('MULT18X18SIO', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3E
# Cell('DSP48A', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3A DSP
# Cell('DSP48A1', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 6
- Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4
+ # Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4
Cell('DSP48E', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 5
#Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 6 / Series 7
Cell('DSP48E2', port_attrs={'CLK': ['clkbuf_sink']}), # Ultrascale
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
index c3e5c72f9..e87f4ec76 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -4518,13 +4518,21 @@ module RAMB18E1 (...);
input RSTREGARSTREG;
(* invertible_pin = "IS_RSTREGB_INVERTED" *)
input RSTREGB;
+ (* abc9_required=566 *)
input [13:0] ADDRARDADDR;
+ (* abc9_required=566 *)
input [13:0] ADDRBWRADDR;
+ (* abc9_required=737 *)
input [15:0] DIADI;
+ (* abc9_required=737 *)
input [15:0] DIBDI;
+ (* abc9_required=737 *)
input [1:0] DIPADIP;
+ (* abc9_required=737 *)
input [1:0] DIPBDIP;
+ (* abc9_required=532 *)
input [1:0] WEA;
+ (* abc9_required=532 *)
input [3:0] WEBWE;
endmodule
@@ -4742,13 +4750,21 @@ module RAMB36E1 (...);
input REGCEB;
input INJECTDBITERR;
input INJECTSBITERR;
+ (* abc9_required=566 *)
input [15:0] ADDRARDADDR;
+ (* abc9_required=566 *)
input [15:0] ADDRBWRADDR;
+ (* abc9_required=737 *)
input [31:0] DIADI;
+ (* abc9_required=737 *)
input [31:0] DIBDI;
+ (* abc9_required=737 *)
input [3:0] DIPADIP;
+ (* abc9_required=737 *)
input [3:0] DIPBDIP;
+ (* abc9_required=532 *)
input [3:0] WEA;
+ (* abc9_required=532 *)
input [7:0] WEBWE;
endmodule
@@ -5476,49 +5492,6 @@ module URAM288_BASE (...);
input SLEEP;
endmodule
-module DSP48 (...);
- parameter integer AREG = 1;
- parameter integer BREG = 1;
- parameter B_INPUT = "DIRECT";
- parameter integer CARRYINREG = 1;
- parameter integer CARRYINSELREG = 1;
- parameter integer CREG = 1;
- parameter LEGACY_MODE = "MULT18X18S";
- parameter integer MREG = 1;
- parameter integer OPMODEREG = 1;
- parameter integer PREG = 1;
- parameter integer SUBTRACTREG = 1;
- output [17:0] BCOUT;
- output [47:0] P;
- output [47:0] PCOUT;
- input [17:0] A;
- input [17:0] B;
- input [17:0] BCIN;
- input [47:0] C;
- input CARRYIN;
- input [1:0] CARRYINSEL;
- input CEA;
- input CEB;
- input CEC;
- input CECARRYIN;
- input CECINSUB;
- input CECTRL;
- input CEM;
- input CEP;
- (* clkbuf_sink *)
- input CLK;
- input [6:0] OPMODE;
- input [47:0] PCIN;
- input RSTA;
- input RSTB;
- input RSTC;
- input RSTCARRYIN;
- input RSTCTRL;
- input RSTM;
- input RSTP;
- input SUBTRACT;
-endmodule
-
module DSP48E (...);
parameter SIM_MODE = "SAFE";
parameter integer ACASCREG = 1;
diff --git a/techlibs/xilinx/lutrams.txt b/techlibs/xilinx/lutrams.txt
index 29f6b05cc..faf66bc18 100644
--- a/techlibs/xilinx/lutrams.txt
+++ b/techlibs/xilinx/lutrams.txt
@@ -153,7 +153,7 @@ endmatch
match $__XILINX_RAM32X2Q
min bits 5
- min rports 3
+ min rports 2
min wports 1
make_outreg
or_next_if_better
@@ -161,7 +161,7 @@ endmatch
match $__XILINX_RAM64X1Q
min bits 5
- min rports 3
+ min rports 2
min wports 1
make_outreg
endmatch
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 3c5599e4e..5a28bb139 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -316,9 +316,10 @@ struct SynthXilinxPass : public ScriptPass
run("proc");
if (flatten || help_mode)
run("flatten", "(with '-flatten')");
- active_design->scratchpad_unset("tribuf.added_something");
+ if (active_design)
+ active_design->scratchpad_unset("tribuf.added_something");
run("tribuf -logic");
- if (noiopad && active_design->scratchpad_get_bool("tribuf.added_something"))
+ if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something"))
log_error("Tristate buffers are unsupported without the '-iopad' option.\n");
run("deminout");
run("opt_expr");
@@ -529,7 +530,7 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("map_cells")) {
// Needs to be done before logic optimization, so that inverters (OE vs T) are handled.
if (help_mode || !noiopad)
- run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if not '-noiopad')");
+ run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(skip if '-noiopad')");
std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v";
if (widemux > 0)
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux);
@@ -592,17 +593,16 @@ struct SynthXilinxPass : public ScriptPass
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 += stringf("[-map %s]", ff_map_file.c_str());
- else if (!abc9)
+ if (help_mode || !abc9)
techmap_args += stringf(" -map %s", ff_map_file.c_str());
- run("techmap " + techmap_args, "(only if '-abc9')");
+ run("techmap " + techmap_args);
run("xilinx_dffopt");
+ run("opt_lut_ins -tech xilinx");
}
if (check_label("finalize")) {
if (help_mode || !noclkbuf)
- run("clkbufmap -buf BUFG O:I ", "(skip if '-noclkbuf')");
+ run("clkbufmap -buf BUFG O:I", "(skip if '-noclkbuf')");
if (help_mode || ise)
run("extractinv -inv INV O:I", "(only if '-ise')");
run("clean");
diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore
index 848f88d53..0d9c28fde 100644
--- a/techlibs/xilinx/tests/.gitignore
+++ b/techlibs/xilinx/tests/.gitignore
@@ -12,4 +12,7 @@ test_dsp48a_model_ref.v
test_dsp48a1_model_ref.v
test_dsp48a1_model_uut.v
test_dsp48a1_model
+test_dsp48_model_ref.v
+test_dsp48_model_uut.v
+test_dsp48_model
*.vcd
diff --git a/techlibs/xilinx/tests/test_dsp48_model.sh b/techlibs/xilinx/tests/test_dsp48_model.sh
new file mode 100644
index 000000000..9a73f9b0c
--- /dev/null
+++ b/techlibs/xilinx/tests/test_dsp48_model.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -ex
+if [ -z $ISE_DIR ]; then
+ ISE_DIR=/opt/Xilinx/ISE/14.7
+fi
+sed 's/DSP48 /DSP48_UUT /; /DSP48_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp48_model_uut.v
+if [ ! -f "test_dsp48_model_ref.v" ]; then
+ cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48.v test_dsp48_model_ref.v
+fi
+for tb in mult_allreg mult_noreg mult_inreg
+do
+ iverilog -s $tb -s glbl -o test_dsp48_model test_dsp48_model.v test_dsp48_model_uut.v test_dsp48_model_ref.v $ISE_DIR/ISE_DS/ISE/verilog/src/glbl.v
+ vvp -N ./test_dsp48_model
+done
diff --git a/techlibs/xilinx/tests/test_dsp48_model.v b/techlibs/xilinx/tests/test_dsp48_model.v
new file mode 100644
index 000000000..d69c00e93
--- /dev/null
+++ b/techlibs/xilinx/tests/test_dsp48_model.v
@@ -0,0 +1,287 @@
+`timescale 1ns / 1ps
+
+module testbench;
+ parameter integer AREG = 1;
+ parameter integer BREG = 1;
+ parameter integer CREG = 1;
+ parameter integer MREG = 1;
+ parameter integer PREG = 1;
+ parameter integer CARRYINREG = 1;
+ parameter integer CARRYINSELREG = 1;
+ parameter integer OPMODEREG = 1;
+ parameter integer SUBTRACTREG = 1;
+ parameter B_INPUT = "DIRECT";
+ parameter LEGACY_MODE = "NONE";
+
+ reg CLK;
+ reg CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL;
+ reg RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL;
+ reg [17:0] A;
+ reg [17:0] B;
+ reg [47:0] C;
+ reg [17:0] BCIN;
+ reg [47:0] PCIN;
+ reg CARRYIN;
+ reg [6:0] OPMODE;
+ reg SUBTRACT;
+ reg [1:0] CARRYINSEL;
+
+ output [47:0] P, REF_P;
+ output [17:0] BCOUT, REF_BCOUT;
+ output [47:0] PCOUT, REF_PCOUT;
+
+ integer errcount = 0;
+
+ reg ERROR_FLAG = 0;
+
+ task clkcycle;
+ begin
+ #5;
+ CLK = ~CLK;
+ #10;
+ CLK = ~CLK;
+ #2;
+ ERROR_FLAG = 0;
+ if (REF_BCOUT !== BCOUT) begin
+ $display("ERROR at %1t: REF_BCOUT=%b UUT_BCOUT=%b DIFF=%b", $time, REF_BCOUT, BCOUT, REF_BCOUT ^ BCOUT);
+ errcount = errcount + 1;
+ ERROR_FLAG = 1;
+ end
+ 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_PCOUT !== PCOUT) begin
+ $display("ERROR at %1t: REF_PCOUT=%b UUT_PCOUT=%b DIFF=%b", $time, REF_PCOUT, PCOUT, REF_PCOUT ^ PCOUT);
+ errcount = errcount + 1;
+ ERROR_FLAG = 1;
+ end
+ #3;
+ end
+ endtask
+
+ reg config_valid = 0;
+ task drc;
+ begin
+ config_valid = 1;
+
+ if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0;
+ if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b10) config_valid = 0;
+ if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b10) config_valid = 0;
+ if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b11) config_valid = 0;
+ if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b11) config_valid = 0;
+ if (OPMODE[3:2] == 2'b10) 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) config_valid = 0;
+ if (OPMODE[6:4] == 3'b111) config_valid = 0;
+ if (OPMODE[6:4] == 3'b000 && CARRYINSEL == 2'b01) config_valid = 0;
+ if (OPMODE[6:4] == 3'b011 && CARRYINSEL == 2'b01) config_valid = 0;
+
+ // Xilinx models consider these combinations invalid for an unknown reason.
+ if (CARRYINSEL == 2'b01 && OPMODE[3:2] == 2'b00) config_valid = 0;
+ if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000011) config_valid = 0;
+ if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000101) config_valid = 0;
+ if (CARRYINSEL == 2'b10 && OPMODE == 7'b0100011) config_valid = 0;
+ if (CARRYINSEL == 2'b10 && OPMODE == 7'b0111111) config_valid = 0;
+ if (CARRYINSEL == 2'b10 && OPMODE == 7'b1100011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000101) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0011111) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0010011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100101) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0101111) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0110011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b0111111) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b1010011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b1011111) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100011) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100101) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE == 7'b1101111) config_valid = 0;
+
+ if (CARRYINSEL == 2'b10 && OPMODE[3:0] == 4'b0101 && MREG == 1) config_valid = 0;
+ if (CARRYINSEL == 2'b11 && OPMODE[3:0] == 4'b0101 && MREG == 0) config_valid = 0;
+ end
+ endtask
+
+ initial begin
+ $dumpfile("test_dsp48_model.vcd");
+ $dumpvars(0, testbench);
+
+ #2;
+ CLK = 1'b0;
+ {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = 8'b11111111;
+ {A, B, C, PCIN, OPMODE, SUBTRACT, CARRYIN, CARRYINSEL} = 0;
+ {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 7'b1111111;
+ repeat (10) begin
+ #10;
+ CLK = 1'b1;
+ #10;
+ CLK = 1'b0;
+ #10;
+ CLK = 1'b1;
+ #10;
+ CLK = 1'b0;
+ end
+ {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 0;
+
+ repeat (100000) begin
+ clkcycle;
+ config_valid = 0;
+ while (!config_valid) begin
+ A = $urandom;
+ B = $urandom;
+ C = {$urandom, $urandom};
+ BCIN = $urandom;
+ PCIN = {$urandom, $urandom};
+
+ {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = $urandom | $urandom | $urandom;
+ {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom;
+ {CARRYIN, CARRYINSEL, OPMODE, SUBTRACT} = $urandom;
+
+ drc;
+ end
+ end
+
+ if (errcount == 0) begin
+ $display("All tests passed.");
+ $finish;
+ end else begin
+ $display("Caught %1d errors.", errcount);
+ $stop;
+ end
+ end
+
+ DSP48 #(
+ .AREG (AREG),
+ .BREG (BREG),
+ .CREG (CREG),
+ .MREG (MREG),
+ .PREG (PREG),
+ .CARRYINREG (CARRYINREG),
+ .CARRYINSELREG (CARRYINSELREG),
+ .OPMODEREG (OPMODEREG),
+ .SUBTRACTREG (SUBTRACTREG),
+ .B_INPUT (B_INPUT),
+ .LEGACY_MODE (LEGACY_MODE)
+ ) ref (
+ .A (A),
+ .B (B),
+ .C (C),
+ .BCIN (BCIN),
+ .PCIN (PCIN),
+ .CARRYIN (CARRYIN),
+ .OPMODE (OPMODE),
+ .SUBTRACT (SUBTRACT),
+ .CARRYINSEL (CARRYINSEL),
+ .BCOUT (REF_BCOUT),
+ .P (REF_P),
+ .PCOUT (REF_PCOUT),
+ .CEA (CEA),
+ .CEB (CEB),
+ .CEC (CEC),
+ .CEM (CEM),
+ .CEP (CEP),
+ .CECARRYIN (CECARRYIN),
+ .CECINSUB (CECINSUB),
+ .CECTRL (CECTRL),
+ .CLK (CLK),
+ .RSTA (RSTA),
+ .RSTB (RSTB),
+ .RSTC (RSTC),
+ .RSTM (RSTM),
+ .RSTP (RSTP),
+ .RSTCARRYIN (RSTCARRYIN),
+ .RSTCTRL (RSTCTRL)
+ );
+
+ DSP48_UUT #(
+ .AREG (AREG),
+ .BREG (BREG),
+ .CREG (CREG),
+ .MREG (MREG),
+ .PREG (PREG),
+ .CARRYINREG (CARRYINREG),
+ .CARRYINSELREG (CARRYINSELREG),
+ .OPMODEREG (OPMODEREG),
+ .SUBTRACTREG (SUBTRACTREG),
+ .B_INPUT (B_INPUT),
+ .LEGACY_MODE (LEGACY_MODE)
+ ) uut (
+ .A (A),
+ .B (B),
+ .C (C),
+ .BCIN (BCIN),
+ .PCIN (PCIN),
+ .CARRYIN (CARRYIN),
+ .OPMODE (OPMODE),
+ .SUBTRACT (SUBTRACT),
+ .CARRYINSEL (CARRYINSEL),
+ .BCOUT (BCOUT),
+ .P (P),
+ .PCOUT (PCOUT),
+ .CEA (CEA),
+ .CEB (CEB),
+ .CEC (CEC),
+ .CEM (CEM),
+ .CEP (CEP),
+ .CECARRYIN (CECARRYIN),
+ .CECINSUB (CECINSUB),
+ .CECTRL (CECTRL),
+ .CLK (CLK),
+ .RSTA (RSTA),
+ .RSTB (RSTB),
+ .RSTC (RSTC),
+ .RSTM (RSTM),
+ .RSTP (RSTP),
+ .RSTCARRYIN (RSTCARRYIN),
+ .RSTCTRL (RSTCTRL)
+ );
+endmodule
+
+module mult_noreg;
+ testbench #(
+ .AREG (0),
+ .BREG (0),
+ .CREG (0),
+ .MREG (0),
+ .PREG (0),
+ .CARRYINREG (0),
+ .CARRYINSELREG (0),
+ .OPMODEREG (0),
+ .SUBTRACTREG (0),
+ .B_INPUT ("DIRECT")
+ ) testbench ();
+endmodule
+
+module mult_allreg;
+ testbench #(
+ .AREG (1),
+ .BREG (1),
+ .CREG (1),
+ .MREG (1),
+ .PREG (1),
+ .CARRYINREG (1),
+ .CARRYINSELREG (1),
+ .OPMODEREG (1),
+ .SUBTRACTREG (1),
+ .B_INPUT ("CASCADE")
+ ) testbench ();
+endmodule
+
+module mult_inreg;
+ testbench #(
+ .AREG (1),
+ .BREG (1),
+ .CREG (1),
+ .MREG (0),
+ .PREG (0),
+ .CARRYINREG (1),
+ .CARRYINSELREG (0),
+ .OPMODEREG (0),
+ .SUBTRACTREG (0),
+ .B_INPUT ("DIRECT")
+ ) testbench ();
+endmodule
diff --git a/tests/arch/ecp5/mux.ys b/tests/arch/ecp5/mux.ys
index 22866832d..92463aa32 100644
--- a/tests/arch/ecp5/mux.ys
+++ b/tests/arch/ecp5/mux.ys
@@ -39,8 +39,8 @@ proc
equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module
-select -assert-count 12 t:L6MUX21
-select -assert-count 34 t:LUT4
-select -assert-count 17 t:PFUMX
+select -assert-count 8 t:L6MUX21
+select -assert-count 26 t:LUT4
+select -assert-count 12 t:PFUMX
select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D
diff --git a/tests/arch/ecp5/opt_lut_ins.ys b/tests/arch/ecp5/opt_lut_ins.ys
new file mode 100644
index 000000000..2bc546912
--- /dev/null
+++ b/tests/arch/ecp5/opt_lut_ins.ys
@@ -0,0 +1,32 @@
+read_ilang << EOF
+
+module \top
+
+ wire input 1 \A
+ wire input 2 \B
+ wire input 3 \C
+ wire input 4 \D
+
+ wire output 5 \Z
+
+ cell \LUT4 $0
+ parameter \INIT 16'1111110011000000
+ connect \A \A
+ connect \B \B
+ connect \C \C
+ connect \D \D
+ connect \Z \Z
+ end
+end
+
+EOF
+
+read_verilog -lib +/ecp5/cells_sim.v
+
+equiv_opt -assert -map +/ecp5/cells_sim.v opt_lut_ins -tech ecp5
+
+design -load postopt
+
+select -assert-count 1 top/t:LUT4
+select -assert-count 0 top/w:A %co top/t:LUT4 %i
+select -assert-count 1 top/w:B %co top/t:LUT4 %i
diff --git a/tests/arch/efinix/mux.ys b/tests/arch/efinix/mux.ys
index b46f641e1..67006b6f2 100644
--- a/tests/arch/efinix/mux.ys
+++ b/tests/arch/efinix/mux.ys
@@ -16,7 +16,7 @@ proc
equiv_opt -assert -map +/efinix/cells_sim.v synth_efinix # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux4 # Constrain all select calls below inside the top module
-select -assert-count 2 t:EFX_LUT4
+#select -assert-count 2 t:EFX_LUT4
select -assert-none t:EFX_LUT4 %% t:* %D
@@ -26,7 +26,7 @@ proc
equiv_opt -assert -map +/efinix/cells_sim.v synth_efinix # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux8 # Constrain all select calls below inside the top module
-select -assert-count 5 t:EFX_LUT4
+#select -assert-count 5 t:EFX_LUT4
select -assert-none t:EFX_LUT4 %% t:* %D
@@ -36,6 +36,6 @@ proc
equiv_opt -assert -map +/efinix/cells_sim.v synth_efinix # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module
-select -assert-count 12 t:EFX_LUT4
+select -assert-max 12 t:EFX_LUT4
select -assert-none t:EFX_LUT4 %% t:* %D
diff --git a/tests/arch/gowin/mux.ys b/tests/arch/gowin/mux.ys
index afad29a89..33b092284 100644
--- a/tests/arch/gowin/mux.ys
+++ b/tests/arch/gowin/mux.ys
@@ -18,13 +18,13 @@ proc
equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux4 # Constrain all select calls below inside the top module
-select -assert-count 4 t:LUT4
+select -assert-count 4 t:LUT*
select -assert-count 2 t:MUX2_LUT5
select -assert-count 1 t:MUX2_LUT6
select -assert-count 6 t:IBUF
select -assert-count 1 t:OBUF
-select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D
+select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D
design -load read
hierarchy -top mux8
@@ -35,7 +35,7 @@ cd mux8 # Constrain all select calls below inside the top module
select -assert-count 11 t:IBUF
select -assert-count 1 t:OBUF
-select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D
+select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D
design -load read
hierarchy -top mux16
@@ -46,4 +46,4 @@ cd mux16 # Constrain all select calls below inside the top module
select -assert-count 20 t:IBUF
select -assert-count 1 t:OBUF
-select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D
+select -assert-none t:GND t:VCC t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D
diff --git a/tests/arch/xilinx/dffs.ys b/tests/arch/xilinx/dffs.ys
index dc764b033..deaf16bd6 100644
--- a/tests/arch/xilinx/dffs.ys
+++ b/tests/arch/xilinx/dffs.ys
@@ -8,7 +8,6 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd dff # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG
select -assert-count 1 t:FDRE
-
select -assert-none t:BUFG t:FDRE %% t:* %D
@@ -20,6 +19,27 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd dffe # Constrain all select calls below inside the top module
select -assert-count 1 t:BUFG
select -assert-count 1 t:FDRE
+select -assert-none t:BUFG t:FDRE %% t:* %D
+
+
+design -load read
+hierarchy -top dff
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd dff # Constrain all select calls below inside the top module
+select -assert-count 1 t:BUFG
+select -assert-count 1 t:FDRE
+select -assert-none t:BUFG t:FDRE %% t:* %D
+
+design -load read
+hierarchy -top dffe
+proc
+equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd dffe # Constrain all select calls below inside the top module
+select -assert-count 1 t:BUFG
+select -assert-count 1 t:FDRE
select -assert-none t:BUFG t:FDRE %% t:* %D
diff --git a/tests/arch/xilinx/opt_lut_ins.ys b/tests/arch/xilinx/opt_lut_ins.ys
new file mode 100644
index 000000000..a01d02179
--- /dev/null
+++ b/tests/arch/xilinx/opt_lut_ins.ys
@@ -0,0 +1,25 @@
+read_ilang << EOF
+
+module \top
+
+ wire width 4 input 1 \A
+
+ wire output 2 \O
+
+ cell \LUT4 $0
+ parameter \INIT 16'1111110011000000
+ connect \I0 \A [0]
+ connect \I1 \A [1]
+ connect \I2 \A [2]
+ connect \I3 \A [3]
+ connect \O \O
+ end
+end
+
+EOF
+
+equiv_opt -assert -map +/xilinx/cells_sim.v opt_lut_ins -tech xilinx
+
+design -load postopt
+
+select -assert-count 1 t:LUT3
diff --git a/tests/opt/opt_lut_ins.ys b/tests/opt/opt_lut_ins.ys
new file mode 100644
index 000000000..82460b164
--- /dev/null
+++ b/tests/opt/opt_lut_ins.ys
@@ -0,0 +1,23 @@
+read_ilang << EOF
+
+module \top
+
+ wire width 4 input 1 \A
+
+ wire output 2 \Y
+
+ cell $lut \lut
+ parameter \LUT 16'1111110011000000
+ parameter \WIDTH 4
+ connect \A \A
+ connect \Y \Y
+ end
+end
+
+EOF
+
+equiv_opt -assert opt_lut_ins
+
+design -load postopt
+
+select -assert-count 1 t:$lut r:WIDTH=3 %i
diff --git a/tests/sat/clk2fflogic.ys b/tests/sat/clk2fflogic.ys
new file mode 100644
index 000000000..6d6d9e490
--- /dev/null
+++ b/tests/sat/clk2fflogic.ys
@@ -0,0 +1,66 @@
+read_verilog -icells <<EOT
+module top(input clk, d, s, r, output reg [17:0] q);
+always @(posedge clk or posedge s) if ( s) q[ 0] <= 1'b1; else q[ 0] <= d;
+always @(posedge clk or negedge s) if (!s) q[ 1] <= 1'b1; else q[ 1] <= d;
+always @(posedge clk or posedge r) if ( r) q[ 2] <= 1'b0; else q[ 2] <= d;
+always @(posedge clk or negedge r) if (!r) q[ 3] <= 1'b0; else q[ 3] <= d;
+always @(negedge clk or posedge s) if ( s) q[ 4] <= 1'b1; else q[ 4] <= d;
+always @(negedge clk or negedge s) if (!s) q[ 5] <= 1'b1; else q[ 5] <= d;
+always @(negedge clk or posedge r) if ( r) q[ 6] <= 1'b0; else q[ 6] <= d;
+always @(negedge clk or negedge r) if (!r) q[ 7] <= 1'b0; else q[ 7] <= d;
+
+// Seems like proc_dlatch always sets {SET,CLR}_POLARITY to true
+always @(posedge clk or posedge s or posedge r) if ( r) q[ 8] <= 1'b0; else if ( s) q[ 8] <= 1'b1; else q[ 8] <= d;
+//always @(posedge clk or posedge s or negedge r) if (!r) q[ 9] <= 1'b0; else if ( s) q[ 9] <= 1'b1; else q[ 9] <= d;
+//always @(posedge clk or negedge s or posedge r) if ( r) q[10] <= 1'b0; else if (!s) q[10] <= 1'b1; else q[10] <= d;
+//always @(posedge clk or negedge s or negedge r) if (!r) q[11] <= 1'b0; else if (!s) q[11] <= 1'b1; else q[11] <= d;
+$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h1), .WIDTH(32'd1)) ppn (.CLK(clk), .CLR(r), .D(d), .Q(q[ 9]), .SET(s));
+$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h1), .SET_POLARITY(1'h0), .WIDTH(32'd1)) pnp (.CLK(clk), .CLR(r), .D(d), .Q(q[10]), .SET(s));
+$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h0), .WIDTH(32'd1)) pnn (.CLK(clk), .CLR(r), .D(d), .Q(q[11]), .SET(s));
+
+always @(negedge clk or posedge s or posedge r) if ( r) q[12] <= 1'b0; else if ( s) q[12] <= 1'b1; else q[12] <= d;
+//always @(negedge clk or posedge s or negedge r) if (!r) q[13] <= 1'b0; else if ( s) q[13] <= 1'b1; else q[13] <= d;
+//always @(negedge clk or negedge s or posedge r) if ( r) q[14] <= 1'b0; else if (!s) q[14] <= 1'b1; else q[14] <= d;
+//always @(negedge clk or negedge s or negedge r) if (!r) q[15] <= 1'b0; else if (!s) q[15] <= 1'b1; else q[15] <= d;
+$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h1), .WIDTH(32'd1)) npn (.CLK(clk), .CLR(r), .D(d), .Q(q[13]), .SET(s));
+$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h1), .SET_POLARITY(1'h0), .WIDTH(32'd1)) nnp (.CLK(clk), .CLR(r), .D(d), .Q(q[14]), .SET(s));
+$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h0), .WIDTH(32'd1)) nnn (.CLK(clk), .CLR(r), .D(d), .Q(q[15]), .SET(s));
+
+always @(posedge clk) q[16] <= d;
+always @(negedge clk) q[17] <= d;
+endmodule
+EOT
+proc
+select -assert-count 8 t:$adff
+select -assert-count 8 t:$dffsr
+select -assert-count 2 t:$dff
+design -save gold
+
+simplemap
+select -assert-count 1 t:$_DFF_NN0_
+select -assert-count 1 t:$_DFF_NN1_
+select -assert-count 1 t:$_DFF_NP0_
+select -assert-count 1 t:$_DFF_NP1_
+select -assert-count 1 t:$_DFF_PN0_
+select -assert-count 1 t:$_DFF_PN1_
+select -assert-count 1 t:$_DFF_PP0_
+select -assert-count 1 t:$_DFF_PP1_
+stat
+select -assert-count 1 t:$_DFFSR_NNN_
+select -assert-count 1 t:$_DFFSR_NNP_
+select -assert-count 1 t:$_DFFSR_NPN_
+select -assert-count 1 t:$_DFFSR_NPP_
+select -assert-count 1 t:$_DFFSR_PNN_
+select -assert-count 1 t:$_DFFSR_PNP_
+select -assert-count 1 t:$_DFFSR_PPN_
+select -assert-count 1 t:$_DFFSR_PPP_
+select -assert-count 1 t:$_DFF_N_
+select -assert-count 1 t:$_DFF_P_
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+clk2fflogic
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports -set-init-undef -seq 10 miter
diff --git a/tests/techmap/shiftx2mux.ys b/tests/techmap/shiftx2mux.ys
new file mode 100644
index 000000000..c13b5f600
--- /dev/null
+++ b/tests/techmap/shiftx2mux.ys
@@ -0,0 +1,110 @@
+read_verilog <<EOT
+module sc1 (i1 ,
+ i2 ,
+ i3 ,
+ i4 ,
+ i5 ,
+ i6 ,
+ i7 ,
+ i8 ,
+ i9 ,
+ i10,
+ i11,
+ i12,
+ i13,
+ i14,
+ i15,
+ binary_out,
+ encoder_in,
+ enable
+);
+
+input [3:0] i1 ;
+input [3:0] i2 ;
+input [3:0] i3 ;
+input [3:0] i4 ;
+input [3:0] i5 ;
+input [3:0] i6 ;
+input [3:0] i7 ;
+input [3:0] i8 ;
+input [3:0] i9 ;
+input [3:0] i10 ;
+input [3:0] i11 ;
+input [3:0] i12 ;
+input [3:0] i13 ;
+input [3:0] i14 ;
+input [3:0] i15 ;
+
+output reg [3:0] binary_out ;
+
+input [3:0] encoder_in ;
+input enable ;
+
+
+
+always @ (*)
+begin
+ binary_out = 0;
+ if (enable) begin
+ case (encoder_in)
+ 4'h1 : binary_out = i1;
+ 4'h2 : binary_out = i2;
+ 4'h3 : binary_out = i3;
+ 4'h4 : binary_out = i4;
+ 4'h5 : binary_out = i5;
+ 4'h6 : binary_out = i6;
+ 4'h7 : binary_out = i7;
+ 4'h8 : binary_out = i8;
+ 4'h9 : binary_out = i9;
+ 4'ha : binary_out = i10;
+ 4'hb : binary_out = i11;/*
+ 4'hc : binary_out = i12;
+ 4'hd : binary_out = i13;
+ 4'he : binary_out = i14;
+ 4'hf : binary_out = i15;*/
+ endcase
+ end
+end
+endmodule
+EOT
+
+proc
+pmux2shiftx
+design -save gold
+
+
+design -load gold
+techmap -D NO_LSB_FIRST_SHIFT_SHIFTX
+abc -lut 6
+select -assert-min 17 t:$lut
+
+
+design -load gold
+techmap
+abc -lut 6
+select -assert-count 16 t:$lut
+
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+
+design -load gold
+techmap -D NO_LSB_FIRST_SHIFT_SHIFTX
+abc9 -lut 6
+select -assert-min 17 t:$lut
+
+
+design -load gold
+techmap
+abc9 -lut 6
+select -assert-count 16 t:$lut
+
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
diff --git a/tests/various/help.ys b/tests/various/help.ys
new file mode 100644
index 000000000..9283ce8f1
--- /dev/null
+++ b/tests/various/help.ys
@@ -0,0 +1,2 @@
+help -all
+help -celltypes
diff --git a/tests/various/sformatf.ys b/tests/various/sformatf.ys
new file mode 100644
index 000000000..66d6b0dbe
--- /dev/null
+++ b/tests/various/sformatf.ys
@@ -0,0 +1,12 @@
+read_verilog <<EOT
+
+module top;
+ localparam a = $sformatf("0x%x", 8'h5A);
+ localparam b = $sformatf("%d", 4'b011);
+ generate
+ if (a != "0x5a") $error("a incorrect!");
+ if (b != "3") $error("b incorrect!");
+ endgenerate
+endmodule
+
+EOT
diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh
new file mode 100755
index 000000000..9a01447f7
--- /dev/null
+++ b/tests/various/sv_implicit_ports.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+
+trap 'echo "ERROR in sv_implicit_ports.sh" >&2; exit 1' ERR
+
+# Simple case
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+ assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ wire [7:0] b = 8'd42;
+ add add_i(.*);
+endmodule
+EOT
+
+# Generate block
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ generate
+ if (1) begin:ablock
+ wire [7:0] b = 8'd42;
+ add add_i(.*);
+ end
+ endgenerate
+endmodule
+EOT
+
+# Missing wire
+((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+ assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ add add_i(.*);
+endmodule
+EOT
+) 2>&1 | grep -F "ERROR: No matching wire for implicit port connection \`b' of cell top.add_i (add)." > /dev/null
+
+# Incorrectly sized wire
+((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+ assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ wire [6:0] b = 6'd42;
+ add add_i(.*);
+endmodule
+EOT
+) 2>&1 | grep -F "ERROR: Width mismatch between wire (7 bits) and port (8 bits) for implicit port connection \`b' of cell top.add_i (add)." > /dev/null
+
+# Defaults
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add(input [7:0] a = 8'd00, input [7:0] b = 8'd01, output [7:0] q);
+assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ add add_i(.*);
+endmodule
+EOT
+
+# Parameterised module
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add #(parameter N=3) (input [N-1:0] a = 8'd00, input [N-1:0] b = 8'd01, output [N-1:0] q);
+assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ add #(.N(8)) add_i(.*);
+endmodule
+EOT
+
+# Parameterised blackbox module
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - <<EOT
+(* blackbox *)
+module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q);
+endmodule
+
+module top(input [7:0] a, b, output [7:0] q);
+ add #(.N(8)) add_i(.*);
+endmodule
+EOT
+
+# Parameterised blackbox module - incorrect width
+((../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - || true) <<EOT
+(* blackbox *)
+module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q);
+endmodule
+
+module top(input [7:0] a, b, output [7:0] q);
+ add #(.N(6)) add_i(.*);
+endmodule
+EOT
+) 2>&1 | grep -F "ERROR: Width mismatch between wire (8 bits) and port (6 bits) for implicit port connection \`q' of cell top.add_i (add)." > /dev/null
+
+# Mixed implicit and explicit 1
+../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+ assign q = a + b;
+endmodule
+
+module top(input [7:0] a, output [7:0] q);
+ add add_i(.b(8'd42), .*);
+endmodule
+EOT
+
+# Mixed implicit and explicit 2
+(../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT
+module add(input [7:0] a, input [7:0] b, output [7:0] q);
+ assign q = a + b;
+endmodule
+
+module top(input [7:0] a, input [9:0] b, output [7:0] q);
+ add add_i(.b, .*);
+endmodule
+EOT
+) 2>&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null