aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--README.md4
-rw-r--r--backends/edif/edif.cc43
-rw-r--r--backends/json/json.cc38
-rw-r--r--backends/smt2/smtio.py6
-rw-r--r--frontends/aiger/aigerparse.cc2
-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.cc18
-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--kernel/rtlil.h2
-rw-r--r--manual/CHAPTER_CellLib.tex101
-rw-r--r--misc/py_wrap_generator.py13
-rw-r--r--passes/cmds/show.cc2
-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/ice40_wrapcarry.cc15
-rw-r--r--passes/pmgen/xilinx_dsp.cc7
-rw-r--r--passes/sat/sat.cc3
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/tribuf.cc6
-rw-r--r--techlibs/ecp5/cells_map.v132
-rw-r--r--techlibs/ecp5/synth_ecp5.cc1
-rw-r--r--techlibs/gowin/synth_gowin.cc1
-rw-r--r--techlibs/ice40/abc9_model.v4
-rw-r--r--techlibs/ice40/abc9_u.box3
-rw-r--r--techlibs/ice40/arith_map.v5
-rw-r--r--techlibs/ice40/cells_map.v13
-rw-r--r--techlibs/ice40/cells_sim.v1
-rw-r--r--techlibs/ice40/ice40_opt.cc4
-rw-r--r--techlibs/intel/synth_intel.cc2
-rw-r--r--techlibs/xilinx/cells_sim.v230
-rw-r--r--techlibs/xilinx/cells_xtra.py2
-rw-r--r--techlibs/xilinx/cells_xtra.v43
-rw-r--r--techlibs/xilinx/lut_map.v99
-rw-r--r--techlibs/xilinx/lutrams.txt4
-rw-r--r--techlibs/xilinx/synth_xilinx.cc15
-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/opt_lut_ins.ys32
-rw-r--r--tests/arch/efinix/mux.ys2
-rw-r--r--tests/arch/gowin/mux.ys8
-rw-r--r--tests/arch/ice40/bug1597.ys72
-rw-r--r--tests/arch/ice40/ice40_opt.ys57
-rw-r--r--tests/arch/xilinx/opt_lut_ins.ys25
-rw-r--r--tests/arch/xilinx/tribuf.sh5
-rw-r--r--tests/opt/opt_lut_ins.ys23
-rw-r--r--tests/sat/initval.ys11
-rw-r--r--tests/various/help.ys2
-rw-r--r--tests/various/sformatf.ys12
-rwxr-xr-xtests/various/sv_implicit_ports.sh124
60 files changed, 1766 insertions, 383 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 09429cbe0..e4301e7bf 100644
--- a/README.md
+++ b/README.md
@@ -392,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/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/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 1df996aa7..34bf7ef38 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -304,7 +304,11 @@ class SmtIo:
def p_open(self):
assert self.p is None
- self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ try:
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ except FileNotFoundError:
+ print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True)
+ sys.exit(1)
running_solvers[self.p_index] = self.p
self.p_running = True
self.p_next = None
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 418fd722c..a42569301 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -422,6 +422,8 @@ void AigerReader::parse_xaiger()
log_assert(wire);
input_sig.append(wire);
}
+ // Reverse input order as fastest input is returned first
+ input_sig.reverse();
// TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
ce.clear();
ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
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 f0d495b46..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");
@@ -317,8 +320,14 @@ int main(int argc, char **argv)
exit(0);
}
+ if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")))
+ {
+ printf("%s\n", yosys_version_str);
+ exit(0);
+ }
+
int opt;
- while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
+ while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1)
{
switch (opt)
{
@@ -449,6 +458,9 @@ int main(int argc, char **argv)
case 'E':
depsfile = optarg;
break;
+ case 'x':
+ log_experimentals_ignored.insert(optarg);
+ break;
default:
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
exit(1);
@@ -561,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/kernel/rtlil.h b/kernel/rtlil.h
index 6251d265d..58c5d9674 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -851,6 +851,8 @@ public:
RTLIL::SigSpec repeat(int num) const;
+ void reverse() { inline_unpack(); std::reverse(bits_.begin(), bits_.end()); }
+
bool operator <(const RTLIL::SigSpec &other) const;
bool operator ==(const RTLIL::SigSpec &other) const;
inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); }
diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex
index 00a88cc82..55abd9b96 100644
--- a/manual/CHAPTER_CellLib.tex
+++ b/manual/CHAPTER_CellLib.tex
@@ -198,8 +198,8 @@ calculated signal and a constant zero with an {\tt \$and} gate).
\subsection{Registers}
-D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
-an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff
+D-type flip-flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK},
+an input port \B{D} and an output port \B{Q}. The following parameters are available for {\tt \$dff}
cells:
\begin{itemize}
@@ -211,13 +211,23 @@ Clock is active on the positive edge if this parameter has the value {\tt 1'b1}
edge if this parameter is {\tt 1'b0}.
\end{itemize}
-D-Type Flip-Flops with asynchronous resets are represented by {\tt \$adff} cells. As the {\tt \$dff}
+D-type flip-flops with enable are represented by {\tt \$dffe} cells. As the {\tt \$dff}
+cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{EN}
+input port for the enable pin and the following parameter:
+
+\begin{itemize}
+\item \B{EN\_POLARITY} \\
+The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low
+if this parameter is {\tt 1'b0}.
+\end{itemize}
+
+D-type flip-flops with asynchronous reset are represented by {\tt \$adff} cells. As the {\tt \$dff}
cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST}
input port for the reset pin and the following additional two parameters:
\begin{itemize}
\item \B{ARST\_POLARITY} \\
-The asynchronous reset is high-active if this parameter has the value {\tt 1'b1} and low-active
+The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low
if this parameter is {\tt 1'b0}.
\item \B{ARST\_VALUE} \\
@@ -231,8 +241,27 @@ Usually these cells are generated by the {\tt proc} pass using the information
in the designs RTLIL::Process objects.
\end{sloppypar}
+D-type flip-flops with asynchronous set and reset are represented by {\tt \$dffsr} cells.
+As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have
+a single-bit \B{SET} input port for the set pin, a single-bit \B{CLR} input port for the reset pin,
+and the following two parameters:
+
+\begin{itemize}
+\item \B{SET\_POLARITY} \\
+The set input is active-high if this parameter has the value {\tt 1'b1} and active-low
+if this parameter is {\tt 1'b0}.
+
+\item \B{CLR\_POLARITY} \\
+The reset input is active-high if this parameter has the value {\tt 1'b1} and active-low
+if this parameter is {\tt 1'b0}.
+\end{itemize}
+
+When both the set and reset inputs of a {\tt \$dffsr} cell are active, the reset input takes
+precedence.
+
\begin{fixme}
-Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches.
+Add information about {\tt \$sr} cells (set-reset flip-flops), {\tt \$dlatch} cells (d-type latches),
+and {\tt \$dlatchsr} cells (d-type latches with set/reset).
\end{fixme}
\subsection{Memories}
@@ -451,6 +480,30 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\
\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\
\end{tabular}
+% FIXME: the layout of this is broken and I have no idea how to fix it
+\hfil
+\begin{tabular}[t]{lll}
+$ClkEdge$ & $EnLvl$ & Cell Type \\
+\hline
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP\_} \\
+\end{tabular}
+% FIXME: the layout of this is broken too
+\hfil
+\begin{tabular}[t]{llll}
+$ClkEdge$ & $SetLvl$ & $RstLvl$ & Cell Type \\
+\hline
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NNN\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NNP\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NPN\_} \\
+\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NPP\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PNN\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PNP\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PPN\_} \\
+\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PPP\_} \\
+\end{tabular}
\caption{Cell types for gate level logic networks}
\label{tab:CellLib_gates}
\end{table}
@@ -459,11 +512,22 @@ Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. Th
{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_},
{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic.
The cell type {\tt \$\_TBUF\_} is used to model tristate logic.
+
The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops.
+The cell types {\tt \$\_DFFE\_NN\_}, {\tt \$\_DFFE\_NP\_}, {\tt \$\_DFFE\_PN\_} and {\tt \$\_DFFE\_PP\_}
+implement d-type flip-flops with enable. The values in the table for these cell types relate to the
+following Verilog code template.
+
+\begin{lstlisting}[mathescape,language=Verilog]
+ always @($ClkEdge$ C)
+ if (EN == $EnLvl$)
+ Q <= D;
+\end{lstlisting}
+
The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_},
{\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement
-d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the
+d-type flip-flops with asynchronous reset. The values in the table for these cell types relate to the
following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge;
otherwise.
@@ -476,6 +540,25 @@ otherwise.
Q <= D;
\end{lstlisting}
+The cell types {\tt \$\_DFFSR\_NNN\_}, {\tt \$\_DFFSR\_NNP\_}, {\tt \$\_DFFSR\_NPN\_}, {\tt \$\_DFFSR\_NPP\_},
+{\tt \$\_DFFSR\_PNN\_}, {\tt \$\_DFFSR\_PNP\_}, {\tt \$\_DFFSR\_PPN\_} and {\tt \$\_DFFSR\_PPP\_} implement
+d-type flip-flops with asynchronous set and reset. The values in the table for these cell types relate to the
+following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge;
+if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge;
+otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge;
+if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge;
+otherwise.
+
+\begin{lstlisting}[mathescape,language=Verilog]
+ always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S)
+ if (R == $RstLvl$)
+ Q <= 0;
+ else if (S == $SetLvl$)
+ Q <= 1;
+ else
+ Q <= D;
+\end{lstlisting}
+
In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells
from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap}
pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC}
@@ -507,11 +590,7 @@ Add information about {\tt \$ff} and {\tt \$\_FF\_} cells.
\end{fixme}
\begin{fixme}
-Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells.
-\end{fixme}
-
-\begin{fixme}
-Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells.
+Add information about {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells.
\end{fixme}
\begin{fixme}
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/cmds/show.cc b/passes/cmds/show.cc
index a3e969ef1..eeef24bde 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -873,7 +873,7 @@ struct ShowPass : public Pass {
#ifdef __APPLE__
std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
#else
- std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
+ std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
#endif
log("Exec: %s\n", cmd.c_str());
if (run_command(cmd) != 0)
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/ice40_wrapcarry.cc b/passes/pmgen/ice40_wrapcarry.cc
index 6e154147f..0053c8872 100644
--- a/passes/pmgen/ice40_wrapcarry.cc
+++ b/passes/pmgen/ice40_wrapcarry.cc
@@ -42,11 +42,19 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
cell->setPort("\\A", st.carry->getPort("\\I0"));
cell->setPort("\\B", st.carry->getPort("\\I1"));
- cell->setPort("\\CI", st.carry->getPort("\\CI"));
+ auto CI = st.carry->getPort("\\CI");
+ cell->setPort("\\CI", CI);
cell->setPort("\\CO", st.carry->getPort("\\CO"));
cell->setPort("\\I0", st.lut->getPort("\\I0"));
- cell->setPort("\\I3", st.lut->getPort("\\I3"));
+ auto I3 = st.lut->getPort("\\I3");
+ if (pm.sigmap(CI) == pm.sigmap(I3)) {
+ cell->setParam("\\I3_IS_CI", State::S1);
+ I3 = State::Sx;
+ }
+ else
+ cell->setParam("\\I3_IS_CI", State::S0);
+ cell->setPort("\\I3", I3);
cell->setPort("\\O", st.lut->getPort("\\O"));
cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT"));
@@ -118,7 +126,8 @@ struct Ice40WrapCarryPass : public Pass {
auto lut = module->addCell(lut_name, ID($lut));
lut->setParam(ID(WIDTH), 4);
lut->setParam(ID(LUT), cell->getParam(ID(LUT)));
- lut->setPort(ID(A), {cell->getPort(ID(I0)), cell->getPort(ID(A)), cell->getPort(ID(B)), cell->getPort(ID(I3)) });
+ auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3));
+ lut->setPort(ID(A), { I3, cell->getPort(ID(B)), cell->getPort(ID(A)), cell->getPort(ID(I0)) });
lut->setPort(ID(Y), cell->getPort(ID(O)));
Const src;
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/sat.cc b/passes/sat/sat.cc
index 430bba1e8..436ac1b01 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -269,7 +269,8 @@ struct SatHelper
for (int i = 0; i < lhs.size(); i++) {
RTLIL::SigSpec bit = lhs.extract(i, 1);
if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
- removed_bits.append(bit);
+ if (rhs[i] != State::Sx)
+ removed_bits.append(bit);
lhs.remove(i, 1);
rhs.remove(i, 1);
i--;
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/tribuf.cc b/passes/techmap/tribuf.cc
index 41fdc8f3d..decf9a202 100644
--- a/passes/techmap/tribuf.cc
+++ b/passes/techmap/tribuf.cc
@@ -86,6 +86,7 @@ struct TribufWorker {
cell->unsetPort(ID(S));
cell->type = tri_type;
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
continue;
}
@@ -95,6 +96,7 @@ struct TribufWorker {
cell->unsetPort(ID(S));
cell->type = tri_type;
tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
continue;
}
}
@@ -130,8 +132,10 @@ struct TribufWorker {
if (no_tribuf)
module->connect(it.first, muxout);
- else
+ else {
module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
+ }
}
}
}
diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v
index 10e89a3e0..c031703a9 100644
--- a/techlibs/ecp5/cells_map.v
+++ b/techlibs/ecp5/cells_map.v
@@ -73,102 +73,80 @@ module \$lut (A, Y);
input [WIDTH-1:0] A;
output Y;
- // Need to swap input ordering, and fix init accordingly,
- // to match ABC's expectation of LUT inputs in non-decreasing
- // delay order
- localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH;
- function [P_WIDTH-1:0] permute_index;
- input [P_WIDTH-1:0] i;
- integer j;
- begin
- permute_index = 0;
- for (j = 0; j < P_WIDTH; j = j + 1)
- permute_index[P_WIDTH-1 - j] = i[j];
- end
- endfunction
-
- function [2**P_WIDTH-1:0] permute_init;
- integer i;
- begin
- permute_init = 0;
- for (i = 0; i < 2**P_WIDTH; i = i + 1)
- permute_init[i] = LUT[permute_index(i)];
- end
- endfunction
-
- parameter [2**P_WIDTH-1:0] P_LUT = permute_init();
-
generate
if (WIDTH == 1) begin
- LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}};
+ LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
.A(1'b0), .B(1'b0), .C(1'b0), .D(A[0]));
end else
if (WIDTH == 2) begin
- LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0]));
+ localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}};
+ LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(1'b0), .B(1'b0), .C(A[0]), .D(A[1]));
end else
if (WIDTH == 3) begin
- LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0]));
+ localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}};
+ LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(1'b0), .B(A[0]), .C(A[1]), .D(A[2]));
end else
if (WIDTH == 4) begin
- LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0]));
+ LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
`ifndef NO_PFUMUX
end else
if (WIDTH == 5) begin
wire f0, f1;
- LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
- .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
- LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
- .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
- PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y));
+ LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y));
end else
if (WIDTH == 6) begin
wire f0, f1, f2, f3, g0, g1;
- LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
- .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
- LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
- .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
-
- LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
- .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
- LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
- .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
-
- PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0));
- PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1));
- L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y));
+ LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
+ PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
+ L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y));
end else
if (WIDTH == 7) begin
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
- LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
- LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
-
- LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
- LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
-
- LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
- LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
-
- LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
- LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7),
- .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
-
- PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0));
- PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1));
- PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2));
- PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3));
- L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0));
- L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1));
- L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y));
+ LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7),
+ .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+
+ PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
+ PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
+ PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2));
+ PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3));
+ L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0));
+ L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1));
+ L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y));
`endif
end else begin
wire _TECHMAP_FAIL_ = 1;
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/ice40/abc9_model.v b/techlibs/ice40/abc9_model.v
index 26cf6cc22..a5e5f4372 100644
--- a/techlibs/ice40/abc9_model.v
+++ b/techlibs/ice40/abc9_model.v
@@ -9,6 +9,8 @@ module \$__ICE40_CARRY_WRAPPER (
input I0, I3
);
parameter LUT = 0;
+ parameter I3_IS_CI = 0;
+ wire I3_OR_CI = I3_IS_CI ? CI : I3;
SB_CARRY carry (
.I0(A),
.I1(B),
@@ -21,7 +23,7 @@ module \$__ICE40_CARRY_WRAPPER (
.I0(I0),
.I1(A),
.I2(B),
- .I3(I3),
+ .I3(I3_OR_CI),
.O(O)
);
endmodule
diff --git a/techlibs/ice40/abc9_u.box b/techlibs/ice40/abc9_u.box
index 48a51463e..3d4b93834 100644
--- a/techlibs/ice40/abc9_u.box
+++ b/techlibs/ice40/abc9_u.box
@@ -6,13 +6,12 @@
# Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
# SB_LUT4+SB_CARRY)
-# Outputs: O, CO
# (Exception: carry chain input/output must be the
# last input and output and the entire bus has been
# moved there overriding the otherwise
# alphabetical ordering)
# name ID w/b ins outs
$__ICE40_CARRY_WRAPPER 1 1 5 2
-#A B I0 I3 CI
+#A B I0 I3 CI
1231 1205 1285 874 874 # O
675 609 - - 278 # CO
diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v
index 00a07247b..ed4140e44 100644
--- a/techlibs/ice40/arith_map.v
+++ b/techlibs/ice40/arith_map.v
@@ -49,13 +49,14 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO);
// A[1]: 1100 1100 1100 1100
// A[2]: 1111 0000 1111 0000
// A[3]: 1111 1111 0000 0000
- .LUT(16'b 0110_1001_1001_0110)
+ .LUT(16'b 0110_1001_1001_0110),
+ .I3_IS_CI(1'b1)
) carry (
.A(AA[i]),
.B(BB[i]),
.CI(C[i]),
.I0(1'b0),
- .I3(C[i]),
+ .I3(1'bx),
.CO(CO[i]),
.O(Y[i])
);
diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v
index 759549e30..d5362eb83 100644
--- a/techlibs/ice40/cells_map.v
+++ b/techlibs/ice40/cells_map.v
@@ -42,19 +42,18 @@ module \$lut (A, Y);
.I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
end else
if (WIDTH == 2) begin
- localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}};
+ localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}};
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0]));
+ .I0(1'b0), .I1(1'b0), .I2(A[0]), .I3(A[1]));
end else
if (WIDTH == 3) begin
- localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}};
+ localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}};
SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0]));
+ .I0(1'b0), .I1(A[0]), .I2(A[1]), .I3(A[2]));
end else
if (WIDTH == 4) begin
- localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]};
- SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0]));
+ SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
end else begin
wire _TECHMAP_FAIL_ = 1;
end
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
index 7d1b37fd6..50eab5dde 100644
--- a/techlibs/ice40/cells_sim.v
+++ b/techlibs/ice40/cells_sim.v
@@ -1126,6 +1126,7 @@ module SB_SPRAM256KA (
input [15:0] DATAIN,
input [3:0] MASKWREN,
input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF,
+ `ABC9_ARRIVAL_U(1821) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13207
output reg [15:0] DATAOUT
);
`ifndef BLACKBOX
diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc
index 9bee0444b..925ab31bb 100644
--- a/techlibs/ice40/ice40_opt.cc
+++ b/techlibs/ice40/ice40_opt.cc
@@ -139,7 +139,8 @@ static void run_ice40_opts(Module *module)
log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
log_id(module), log_id(cell), log_signal(replacement_output));
cell->type = "$lut";
- cell->setPort("\\A", { cell->getPort("\\I0"), inbit[0], inbit[1], cell->getPort("\\I3") });
+ auto I3 = get_bit_or_zero(cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3)));
+ cell->setPort("\\A", { I3, inbit[1], inbit[0], get_bit_or_zero(cell->getPort("\\I0")) });
cell->setPort("\\Y", cell->getPort("\\O"));
cell->unsetPort("\\B");
cell->unsetPort("\\CI");
@@ -148,6 +149,7 @@ static void run_ice40_opts(Module *module)
cell->unsetPort("\\CO");
cell->unsetPort("\\O");
cell->setParam("\\WIDTH", 4);
+ cell->unsetParam("\\I3_IS_CI");
}
continue;
}
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/cells_sim.v b/techlibs/xilinx/cells_sim.v
index c8cbf8522..4692eba33 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -2309,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).
diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py
index cb873a044..75646f594 100644
--- a/techlibs/xilinx/cells_xtra.py
+++ b/techlibs/xilinx/cells_xtra.py
@@ -249,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 beed07e0a..e87f4ec76 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -5492,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/lut_map.v b/techlibs/xilinx/lut_map.v
index 62d501632..718ec42f1 100644
--- a/techlibs/xilinx/lut_map.v
+++ b/techlibs/xilinx/lut_map.v
@@ -29,90 +29,65 @@ module \$lut (A, Y);
input [WIDTH-1:0] A;
output Y;
- // Need to swap input ordering, and fix init accordingly,
- // to match ABC's expectation of LUT inputs in non-decreasing
- // delay order
- function [WIDTH-1:0] permute_index;
- input [WIDTH-1:0] i;
- integer j;
- begin
- permute_index = 0;
- for (j = 0; j < WIDTH; j = j + 1)
- permute_index[WIDTH-1 - j] = i[j];
- end
- endfunction
-
- function [2**WIDTH-1:0] permute_init;
- input [2**WIDTH-1:0] orig;
- integer i;
- begin
- permute_init = 0;
- for (i = 0; i < 2**WIDTH; i = i + 1)
- permute_init[i] = orig[permute_index(i)];
- end
- endfunction
-
- parameter [2**WIDTH-1:0] P_LUT = permute_init(LUT);
-
generate
if (WIDTH == 1) begin
- if (P_LUT == 2'b01) begin
+ if (LUT == 2'b01) begin
INV _TECHMAP_REPLACE_ (.O(Y), .I(A[0]));
end else begin
- LUT1 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
.I0(A[0]));
end
end else
if (WIDTH == 2) begin
- LUT2 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[1]), .I1(A[0]));
+ LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]));
end else
if (WIDTH == 3) begin
- LUT3 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[2]), .I1(A[1]), .I2(A[0]));
+ LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]));
end else
if (WIDTH == 4) begin
- LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[3]), .I1(A[2]), .I2(A[1]),
- .I3(A[0]));
+ LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]));
end else
if (WIDTH == 5) begin
- LUT5 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[4]), .I1(A[3]), .I2(A[2]),
- .I3(A[1]), .I4(A[0]));
+ LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]));
end else
if (WIDTH == 6) begin
- LUT6 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[5]), .I1(A[4]), .I2(A[3]),
- .I3(A[2]), .I4(A[1]), .I5(A[0]));
+ LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
end else
if (WIDTH == 7) begin
wire T0, T1;
- LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0),
- .I0(A[6]), .I1(A[5]), .I2(A[4]),
- .I3(A[3]), .I4(A[2]), .I5(A[1]));
- LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1),
- .I0(A[6]), .I1(A[5]), .I2(A[4]),
- .I3(A[3]), .I4(A[2]), .I5(A[1]));
- MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[0]));
+ LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[6]));
end else
if (WIDTH == 8) begin
wire T0, T1, T2, T3, T4, T5;
- LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0),
- .I0(A[7]), .I1(A[6]), .I2(A[5]),
- .I3(A[4]), .I4(A[3]), .I5(A[2]));
- LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1),
- .I0(A[7]), .I1(A[6]), .I2(A[5]),
- .I3(A[4]), .I4(A[3]), .I5(A[2]));
- LUT6 #(.INIT(P_LUT[191:128])) fpga_lut_2 (.O(T2),
- .I0(A[7]), .I1(A[6]), .I2(A[5]),
- .I3(A[4]), .I4(A[3]), .I5(A[2]));
- LUT6 #(.INIT(P_LUT[255:192])) fpga_lut_3 (.O(T3),
- .I0(A[7]), .I1(A[6]), .I2(A[5]),
- .I3(A[4]), .I4(A[3]), .I5(A[2]));
- MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[1]));
- MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[1]));
- MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[0]));
+ LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[191:128])) fpga_lut_2 (.O(T2),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ LUT6 #(.INIT(LUT[255:192])) fpga_lut_3 (.O(T3),
+ .I0(A[0]), .I1(A[1]), .I2(A[2]),
+ .I3(A[3]), .I4(A[4]), .I5(A[5]));
+ MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[6]));
+ MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[6]));
+ MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[7]));
end else begin
wire _TECHMAP_FAIL_ = 1;
end
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 5c3b5179d..5a28bb139 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -316,7 +316,11 @@ struct SynthXilinxPass : public ScriptPass
run("proc");
if (flatten || help_mode)
run("flatten", "(with '-flatten')");
+ if (active_design)
+ active_design->scratchpad_unset("tribuf.added_something");
run("tribuf -logic");
+ 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");
run("opt_clean");
@@ -526,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);
@@ -589,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/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..a5ab80d8b 100644
--- a/tests/arch/efinix/mux.ys
+++ b/tests/arch/efinix/mux.ys
@@ -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/ice40/bug1597.ys b/tests/arch/ice40/bug1597.ys
new file mode 100644
index 000000000..b7983cfa4
--- /dev/null
+++ b/tests/arch/ice40/bug1597.ys
@@ -0,0 +1,72 @@
+read_verilog <<EOT
+module top (
+ input CLK, PIN_1, PIN_2, PIN_3, PIN_4, PIN_5,
+ PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, PIN_11, PIN_12, PIN_13, PIN_25,
+ output USBPU, PIN_14, PIN_15, PIN_16, PIN_17, PIN_18,
+ PIN_19, PIN_20, PIN_21, PIN_22, PIN_23, PIN_24,
+);
+ assign USBPU = 0;
+
+ wire[5:0] parOut;
+ wire[5:0] chrg;
+
+ assign PIN_14 = parOut[0];
+ assign PIN_15 = parOut[1];
+ assign PIN_16 = parOut[2];
+ assign PIN_17 = parOut[3];
+ assign PIN_18 = parOut[4];
+ assign PIN_19 = parOut[5];
+ assign chrg[0] = PIN_3;
+ assign chrg[1] = PIN_4;
+ assign chrg[2] = PIN_5;
+ assign chrg[3] = PIN_6;
+ assign chrg[4] = PIN_7;
+ assign chrg[5] = PIN_8;
+
+ SSCounter6o sc6(PIN_1, CLK, PIN_2, PIN_9, chrg, parOut);
+
+endmodule
+
+module SSCounter6 (input wire rst, clk, adv, jmp, input wire [5:0] in, output reg[5:0] out);
+ always @(posedge clk, posedge rst)
+ if (rst) out <= 0;
+ else if (adv || jmp) out <= jmp ? in : out + 1;
+endmodule
+
+// Optimized 6 bit counter, it should takes 7 cells.
+/* b[5:1] /* b[0]
+1010101010101010 in 1010101010101010 in
+1100110011001100 jmp 1100110011001100 jmp
+1111000011110000 loop 1111000011110000 loop
+1111111100000000 carry 1111111100000000 -
+---------------------- ----------------------
+1000101110111000 out 1000101110001011 out
+ 8 B B 8 8 B 8 B
+*/
+module SSCounter6o (input wire rst, clk, adv, jmp, input wire [5:0] in, output wire[5:0] out);
+ wire[4:0] co;
+ wire[5:0] lo;
+ wire ien;
+ SB_LUT4 #(.LUT_INIT(16'hFFF0)) lien (ien, 0, 0, adv, jmp);
+ SB_CARRY c0 (co[0], jmp, out[0], 1),
+ c1 (co[1], jmp, out[1], co[0]),
+ c2 (co[2], jmp, out[2], co[1]),
+ c3 (co[3], jmp, out[3], co[2]),
+ c4 (co[4], jmp, out[4], co[3]);
+ SB_DFFER d0 (out[0], clk, ien, rst, lo[0]),
+ d1 (out[1], clk, ien, rst, lo[1]),
+ d2 (out[2], clk, ien, rst, lo[2]),
+ d3 (out[3], clk, ien, rst, lo[3]),
+ d4 (out[4], clk, ien, rst, lo[4]),
+ d5 (out[5], clk, ien, rst, lo[5]);
+ SB_LUT4 #(.LUT_INIT(16'h8B8B)) l0 (lo[0], in[0], jmp, out[0], 0);
+ SB_LUT4 #(.LUT_INIT(16'h8BB8)) l1 (lo[1], in[1], jmp, out[1], co[0]);
+ SB_LUT4 #(.LUT_INIT(16'h8BB8)) l2 (lo[2], in[2], jmp, out[2], co[1]);
+ SB_LUT4 #(.LUT_INIT(16'h8BB8)) l3 (lo[3], in[3], jmp, out[3], co[2]);
+ SB_LUT4 #(.LUT_INIT(16'h8BB8)) l4 (lo[4], in[4], jmp, out[4], co[3]);
+ SB_LUT4 #(.LUT_INIT(16'h8BB8)) l5 (lo[5], in[5], jmp, out[5], co[4]);
+endmodule
+EOT
+hierarchy -top top
+flatten
+equiv_opt -multiclock -map +/ice40/cells_sim.v synth_ice40
diff --git a/tests/arch/ice40/ice40_opt.ys b/tests/arch/ice40/ice40_opt.ys
index 5186d4800..71b68431e 100644
--- a/tests/arch/ice40/ice40_opt.ys
+++ b/tests/arch/ice40/ice40_opt.ys
@@ -1,24 +1,4 @@
read_verilog -icells -formal <<EOT
-module \$__ICE40_CARRY_WRAPPER (output CO, O, input A, B, CI, I0, I3);
- parameter LUT = 0;
- SB_CARRY carry (
- .I0(A),
- .I1(B),
- .CI(CI),
- .CO(CO)
- );
- \$lut #(
- .WIDTH(4),
- .LUT(LUT)
- ) lut (
- .A({I0,A,B,I3}),
- .Y(O)
- );
-endmodule
-EOT
-design -stash unmap
-
-read_verilog -icells -formal <<EOT
module top(input CI, I0, output [1:0] CO, output O);
wire A = 1'b0, B = 1'b0;
\$__ICE40_CARRY_WRAPPER #(
@@ -26,13 +6,14 @@ module top(input CI, I0, output [1:0] CO, output O);
// A[1]: 1100 1100 1100 1100
// A[2]: 1111 0000 1111 0000
// A[3]: 1111 1111 0000 0000
- .LUT(~16'b 0110_1001_1001_0110)
+ .LUT(~16'b 0110_1001_1001_0110),
+ .I3_IS_CI(1'b1)
) u0 (
.A(A),
.B(B),
.CI(CI),
.I0(I0),
- .I3(CI),
+ .I3(1'bx),
.CO(CO[0]),
.O(O)
);
@@ -40,7 +21,7 @@ module top(input CI, I0, output [1:0] CO, output O);
endmodule
EOT
-equiv_opt -assert -map %unmap -map +/ice40/cells_sim.v ice40_opt
+equiv_opt -assert -map +/ice40/abc9_model.v -map +/ice40/cells_sim.v ice40_opt
design -load postopt
select -assert-count 1 t:*
select -assert-count 1 t:$lut
@@ -105,3 +86,33 @@ select -assert-count 1 t:SB_LUT4
select -assert-count 1 t:SB_CARRY
select -assert-count 1 t:SB_CARRY a:keep %i
select -assert-count 1 t:SB_CARRY c:carry %i
+
+
+design -reset
+read_verilog -icells <<EOT
+module top(input I3, I2, I1, I0, output O, O2);
+ SB_LUT4 #(
+ .LUT_INIT(8'b 1001_0110)
+ ) u0 (
+ .I0(I0),
+ .I1(I1),
+ .I2(I2),
+ .I3(),
+ .O(O)
+ );
+ wire CO;
+ \$__ICE40_CARRY_WRAPPER #(
+ .LUT(~8'b 1001_0110),
+ .I3_IS_CI(1'b0)
+ ) u1 (
+ .A(1'b0),
+ .B(1'b0),
+ .CI(1'b0),
+ .I0(),
+ .I3(),
+ .CO(CO),
+ .O(O2)
+ );
+endmodule
+EOT
+ice40_opt
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/arch/xilinx/tribuf.sh b/tests/arch/xilinx/tribuf.sh
new file mode 100644
index 000000000..636aed12a
--- /dev/null
+++ b/tests/arch/xilinx/tribuf.sh
@@ -0,0 +1,5 @@
+! ../../../yosys ../common/tribuf.v -qp "synth_xilinx"
+../../../yosys ../common/tribuf.v -qp "synth_xilinx -iopad; \
+select -assert-count 2 t:IBUF; \
+select -assert-count 1 t:INV; \
+select -assert-count 1 t:OBUFT"
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/initval.ys b/tests/sat/initval.ys
index 2079d2f34..1436724b0 100644
--- a/tests/sat/initval.ys
+++ b/tests/sat/initval.ys
@@ -2,3 +2,14 @@ read_verilog -sv initval.v
proc;;
sat -seq 10 -prove-asserts
+
+design -reset
+read_verilog -icells <<EOT
+module top(input clk, i, output [1:0] o);
+(* init = 2'bx0 *)
+wire [1:0] o;
+assign o[1] = o[0];
+$_DFF_P_ dff (.C(clk), .D(i), .Q(o[0]));
+endmodule
+EOT
+sat -seq 1
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