diff --git a/docs/source/using_yosys/verilog.rst b/docs/source/using_yosys/verilog.rst index ef52bfc25..c503bd182 100644 --- a/docs/source/using_yosys/verilog.rst +++ b/docs/source/using_yosys/verilog.rst @@ -172,7 +172,10 @@ Verilog Attributes and non-standard features - The frontend sets attributes ``always_comb``, ``always_latch`` and ``always_ff`` on processes derived from SystemVerilog style always blocks according to the type of the always. These are checked for correctness in - ``proc_dlatch``. + ``proc_dlatch``. Latches inferred from ``always_latch`` processes are exempt + from the ``proc -latches `` reporting policy, and the + generated latch cells carry the ``always_latch`` attribute, which also + exempts them from `check` ``-nolatches``. - The cell attribute ``wildcard_port_conns`` represents wildcard port connections (SystemVerilog ``.*``). These are resolved to concrete connections diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 178653c41..15d7583fc 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -63,7 +63,8 @@ struct CheckPass : public Pass { log(" -nolatches\n"); log(" also check for latch cells ($dlatch, $adlatch, $dlatchsr and their\n"); log(" $_DLATCH_*/$_DLATCHSR_* mappings) remaining in the design. Use this\n"); - log(" before techmapping in flows that must not emit latches.\n"); + log(" before techmapping in flows that must not emit latches. Cells marked\n"); + log(" with the 'always_latch' attribute are not reported.\n"); log("\n"); log(" -latchonly\n"); log(" check only for latch cells (as listed under -nolatches), skipping all\n"); @@ -142,10 +143,9 @@ struct CheckPass : public Pass { // latch-only mode only flags latches, skipping the (potentially false-positive mid-flow) undriven/driver/loop checks below if (latchonly) { for (auto cell : module->cells()) - if ( + if (!cell->get_bool_attribute(ID::always_latch) && ( cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr)) || - cell->type.begins_with("$_DLATCH_") || cell->type.begins_with("$_DLATCHSR_") - ) { + cell->type.begins_with("$_DLATCH_") || cell->type.begins_with("$_DLATCHSR_"))) { log_warning("Cell %s.%s is a latch of type %s.\n", module, cell, cell->type.unescape()); counter++; } @@ -298,7 +298,7 @@ struct CheckPass : public Pass { } if ( - nolatches && ( + nolatches && !cell->get_bool_attribute(ID::always_latch) && ( cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.begins_with("$_DLATCH_") || cell->type.begins_with("$_DLATCHSR_")) ) { diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index d781d9047..1f25c5608 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -69,7 +69,7 @@ struct ProcPass : public Pass { log(" -noopt\n"); log(" Will omit the opt_expr pass.\n"); log("\n"); - log(" -latches \n"); + log(" -latches \n"); log(" controls how the inference of a latch is reported.\n"); log("\n"); } diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 1f5f5b9c6..72a9f0f6b 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -546,11 +546,16 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc, LatchPolicy policy) else cell = db.module->addDlatch(NEW_ID, en, rhs, lhs); cell->set_src_attribute(src); + if (proc->get_bool_attribute(ID::always_latch)) + cell->set_bool_attribute(ID::always_latch); db.generated_dlatches.insert(cell); if (proc->get_bool_attribute(ID::always_comb)) log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n", db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + else if (proc->get_bool_attribute(ID::always_latch)) + log("Latch inferred for signal `%s.%s' from always_latch process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), cell); else if (policy == POLICY_ERROR) log_error("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), cell); @@ -580,6 +585,9 @@ struct ProcDlatchPass : public Pass { log(" -latches \n"); log(" controls how the inference of a latch is reported. Alternatively, one\n"); log(" can use the 'proc.latches' scratchpad variable. Defaults to 'warn'.\n"); + log(" Latches requested explicitly with 'always_latch' processes are exempt\n"); + log(" from this policy and always reported at info level. The generated\n"); + log(" latch cells carry the 'always_latch' attribute.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override diff --git a/techlibs/analogdevices/synth_analogdevices.cc b/techlibs/analogdevices/synth_analogdevices.cc index aa27c78b6..8653fbf32 100644 --- a/techlibs/analogdevices/synth_analogdevices.cc +++ b/techlibs/analogdevices/synth_analogdevices.cc @@ -108,13 +108,20 @@ struct SynthAnalogDevicesPass : public ScriptPass log(" -noabc9\n"); log(" disable use of new ABC9 flow\n"); log("\n"); + log(" -latches \n"); + log(" select the behaviour for latches that cannot be mapped to a\n"); + log(" dedicated hardware primitive and are implemented using LUTs\n"); + log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); + log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - std::string top_opt, edif_file, json_file, tech, tech_param; + std::string top_opt, edif_file, json_file, tech, tech_param, latches; bool flatten, retime, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp; bool abc9, dff; bool flatten_before_abc; @@ -127,6 +134,7 @@ struct SynthAnalogDevicesPass : public ScriptPass edif_file.clear(); tech = "t16ffc"; tech_param = " -D IS_T16FFC"; + latches = "error"; flatten = true; retime = false; noiopad = false; @@ -244,10 +252,17 @@ struct SynthAnalogDevicesPass : public ScriptPass json_file = args[++argidx]; continue; } + if (args[argidx] == "-latches" && argidx+1 < args.size()) { + latches = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); + if (latches != "info" && latches != "warn" && latches != "error") + log_cmd_error("Invalid value '%s' for -latches (expected info, warn or error)\n", latches.c_str()); + if (!(tech == "t16ffc" || tech == "t40lp")) log_cmd_error("Invalid ADI -tech setting: '%s'.\n", tech); @@ -276,7 +291,7 @@ struct SynthAnalogDevicesPass : public ScriptPass } if (check_label("prepare")) { - run("proc"); + run("proc -latches " + latches); if (flatten || help_mode) { run("check"); run("flatten", "(with '-flatten')"); @@ -439,6 +454,8 @@ struct SynthAnalogDevicesPass : public ScriptPass } if (check_label("map_ffs")) { + if (latches == "error" || help_mode) + run("check -latchonly -assert", "(only if -latches error, the default)"); run("dfflegalize -cell $_DFFE_?P?P_ r -cell $_SDFFE_?P?P_ r"); if (abc9 || help_mode) { if (dff || help_mode) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 31986f25b..cfe8e1b8d 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -78,7 +78,7 @@ struct SynthPass : public ScriptPass { log(" -nordff\n"); log(" passed to 'memory'. prohibits merging of FFs into memory read ports\n"); log("\n"); - log(" -latches \n"); + log(" -latches \n"); log(" controls how the inference of a latch is reported.\n"); log("\n"); log(" -noshare\n"); diff --git a/techlibs/efinix/synth_efinix.cc b/techlibs/efinix/synth_efinix.cc index 5bb517112..a35d1d7af 100644 --- a/techlibs/efinix/synth_efinix.cc +++ b/techlibs/efinix/synth_efinix.cc @@ -68,6 +68,7 @@ struct SynthEfinixPass : public ScriptPass log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); @@ -160,7 +161,7 @@ struct SynthEfinixPass : public ScriptPass if (flatten && check_label("flatten", "(unless -noflatten)")) { - run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); + run("proc -latches " + latches); run("check"); run("flatten"); run("tribuf -logic"); diff --git a/techlibs/fabulous/synth_fabulous.cc b/techlibs/fabulous/synth_fabulous.cc index 49f84294e..7185aafe5 100644 --- a/techlibs/fabulous/synth_fabulous.cc +++ b/techlibs/fabulous/synth_fabulous.cc @@ -115,6 +115,7 @@ struct SynthPass : public ScriptPass { log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); @@ -289,7 +290,7 @@ struct SynthPass : public ScriptPass { run("hierarchy -check"); } else run(stringf("hierarchy -check -top %s", top_module)); - run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); + run("proc -latches " + latches); } if (check_label("flatten", "(unless -noflatten)")) { diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 68db38c51..c01c3deb0 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -119,6 +119,7 @@ struct SynthIce40Pass : public ScriptPass log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); @@ -304,7 +305,7 @@ struct SynthIce40Pass : public ScriptPass { run("read_verilog " + define + " -lib -specify +/ice40/cells_sim.v"); run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt)); - run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); + run("proc -latches " + latches); } if (check_label("flatten", "(unless -noflatten)")) diff --git a/techlibs/intel_alm/synth_intel_alm.cc b/techlibs/intel_alm/synth_intel_alm.cc index 95dbb6e35..869399f42 100644 --- a/techlibs/intel_alm/synth_intel_alm.cc +++ b/techlibs/intel_alm/synth_intel_alm.cc @@ -72,12 +72,19 @@ struct SynthIntelALMPass : public ScriptPass { log(" -noclkbuf\n"); log(" do not insert global clock buffers\n"); log("\n"); + log(" -latches \n"); + log(" select the behaviour for latches that cannot be mapped to a\n"); + log(" dedicated hardware primitive and are implemented using LUTs\n"); + log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); + log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); + log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - string top_opt, family_opt, bram_type; + string top_opt, family_opt, bram_type, latches; bool flatten, nolutram, nobram, dff, nodsp, noiopad, noclkbuf; void clear_flags() override @@ -85,6 +92,7 @@ struct SynthIntelALMPass : public ScriptPass { top_opt = "-auto-top"; family_opt = "cyclonev"; bram_type = "m10k"; + latches = "error"; flatten = true; nolutram = false; nobram = false; @@ -145,10 +153,17 @@ struct SynthIntelALMPass : public ScriptPass { noclkbuf = true; continue; } + if (args[argidx] == "-latches" && argidx + 1 < args.size()) { + latches = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); + if (latches != "info" && latches != "warn" && latches != "error") + log_cmd_error("Invalid value '%s' for -latches (expected info, warn or error)\n", latches.c_str()); + if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); @@ -183,7 +198,7 @@ struct SynthIntelALMPass : public ScriptPass { } if (check_label("coarse")) { - run("proc"); + run("proc -latches " + latches); if (flatten || help_mode) { run("check"); run("flatten", "(skip if -noflatten)"); @@ -241,6 +256,8 @@ struct SynthIntelALMPass : public ScriptPass { } if (check_label("map_ffs")) { + if (latches == "error" || help_mode) + run("check -latchonly -assert", "(only if -latches error, the default)"); run("techmap"); run("dfflegalize -cell $_DFFE_PN0P_ 0 -cell $_SDFFCE_PP0P_ 0"); run("techmap -map +/intel_alm/common/dff_map.v"); diff --git a/techlibs/lattice/synth_lattice.cc b/techlibs/lattice/synth_lattice.cc index a6daf8f85..d1f26ecdf 100644 --- a/techlibs/lattice/synth_lattice.cc +++ b/techlibs/lattice/synth_lattice.cc @@ -161,6 +161,7 @@ struct SynthLatticePass : public ScriptPass log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log(" (ignored with -asyncprld, which has a latch primitive)\n"); log("\n"); log("\n"); @@ -416,7 +417,7 @@ struct SynthLatticePass : public ScriptPass if (check_label("coarse")) { - run("proc -latches " + ((asyncprld || latches == "info") ? std::string("info") : std::string("warn"))); + run("proc -latches " + (asyncprld ? std::string("info") : latches)); if (flatten || help_mode) { run("check"); run("flatten"); diff --git a/techlibs/nanoxplore/synth_nanoxplore.cc b/techlibs/nanoxplore/synth_nanoxplore.cc index 20919bead..3cc97f8a4 100644 --- a/techlibs/nanoxplore/synth_nanoxplore.cc +++ b/techlibs/nanoxplore/synth_nanoxplore.cc @@ -102,6 +102,7 @@ struct SynthNanoXplorePass : public ScriptPass log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); @@ -263,7 +264,7 @@ struct SynthNanoXplorePass : public ScriptPass if (check_label("coarse")) { - run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); + run("proc -latches " + latches); if (flatten || help_mode) { run("check"); run("flatten", "(skip if -noflatten)"); diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 8dddff14c..ca38a6f78 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -77,6 +77,7 @@ struct SynthQuickLogicPass : public ScriptPass { log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" instead. 'error' (the default) aborts synthesis, 'warn' only\n"); log(" prints a warning, and 'info' permits them with an info-level message.\n"); + log(" Latches explicitly requested with 'always_latch' are always permitted.\n"); log(" (only applies to the pp3 family)\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); @@ -226,7 +227,7 @@ struct SynthQuickLogicPass : public ScriptPass { } if (check_label("prepare")) { - run("proc -latches " + ((family == "pp3" && latches != "info") ? std::string("warn") : std::string("info"))); + run("proc -latches " + (family == "pp3" ? latches : std::string("info"))); if (flatten) { run("check"); run("flatten", "(unless -noflatten)"); diff --git a/tests/proc/proc_latches.ys b/tests/proc/proc_latches.ys index 91749b6a7..ba22bf879 100644 --- a/tests/proc/proc_latches.ys +++ b/tests/proc/proc_latches.ys @@ -22,6 +22,20 @@ logger -check-expected design -reset +# always_latch is exempt +read_verilog -sv <