This commit is contained in:
nella 2026-07-29 09:48:58 +02:00 committed by GitHub
commit a26e2adfe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 109 additions and 21 deletions

View File

@ -172,7 +172,10 @@ Verilog Attributes and non-standard features
- The frontend sets attributes ``always_comb``, ``always_latch`` and - The frontend sets attributes ``always_comb``, ``always_latch`` and
``always_ff`` on processes derived from SystemVerilog style always blocks ``always_ff`` on processes derived from SystemVerilog style always blocks
according to the type of the always. These are checked for correctness in 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 <info|warn|error>`` 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 - The cell attribute ``wildcard_port_conns`` represents wildcard port
connections (SystemVerilog ``.*``). These are resolved to concrete connections connections (SystemVerilog ``.*``). These are resolved to concrete connections

View File

@ -63,7 +63,8 @@ struct CheckPass : public Pass {
log(" -nolatches\n"); log(" -nolatches\n");
log(" also check for latch cells ($dlatch, $adlatch, $dlatchsr and their\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(" $_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("\n");
log(" -latchonly\n"); log(" -latchonly\n");
log(" check only for latch cells (as listed under -nolatches), skipping all\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 // latch-only mode only flags latches, skipping the (potentially false-positive mid-flow) undriven/driver/loop checks below
if (latchonly) { if (latchonly) {
for (auto cell : module->cells()) 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.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()); log_warning("Cell %s.%s is a latch of type %s.\n", module, cell, cell->type.unescape());
counter++; counter++;
} }
@ -298,7 +298,7 @@ struct CheckPass : public Pass {
} }
if ( if (
nolatches && ( nolatches && !cell->get_bool_attribute(ID::always_latch) && (
cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr)) || 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_"))
) { ) {

View File

@ -69,7 +69,7 @@ struct ProcPass : public Pass {
log(" -noopt\n"); log(" -noopt\n");
log(" Will omit the opt_expr pass.\n"); log(" Will omit the opt_expr pass.\n");
log("\n"); log("\n");
log(" -latches <auto|warn|error>\n"); log(" -latches <info|warn|error>\n");
log(" controls how the inference of a latch is reported.\n"); log(" controls how the inference of a latch is reported.\n");
log("\n"); log("\n");
} }

View File

@ -546,11 +546,16 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc, LatchPolicy policy)
else else
cell = db.module->addDlatch(NEW_ID, en, rhs, lhs); cell = db.module->addDlatch(NEW_ID, en, rhs, lhs);
cell->set_src_attribute(src); 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); db.generated_dlatches.insert(cell);
if (proc->get_bool_attribute(ID::always_comb)) if (proc->get_bool_attribute(ID::always_comb))
log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n", 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()); 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) else if (policy == POLICY_ERROR)
log_error("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", 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); 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 <info|warn|error>\n"); log(" -latches <info|warn|error>\n");
log(" controls how the inference of a latch is reported. Alternatively, one\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(" 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"); log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override

View File

@ -108,13 +108,20 @@ struct SynthAnalogDevicesPass : public ScriptPass
log(" -noabc9\n"); log(" -noabc9\n");
log(" disable use of new ABC9 flow\n"); log(" disable use of new ABC9 flow\n");
log("\n"); log("\n");
log(" -latches <info|warn|error>\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("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
help_script(); help_script();
log("\n"); 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 flatten, retime, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp;
bool abc9, dff; bool abc9, dff;
bool flatten_before_abc; bool flatten_before_abc;
@ -127,6 +134,7 @@ struct SynthAnalogDevicesPass : public ScriptPass
edif_file.clear(); edif_file.clear();
tech = "t16ffc"; tech = "t16ffc";
tech_param = " -D IS_T16FFC"; tech_param = " -D IS_T16FFC";
latches = "error";
flatten = true; flatten = true;
retime = false; retime = false;
noiopad = false; noiopad = false;
@ -244,10 +252,17 @@ struct SynthAnalogDevicesPass : public ScriptPass
json_file = args[++argidx]; json_file = args[++argidx];
continue; continue;
} }
if (args[argidx] == "-latches" && argidx+1 < args.size()) {
latches = args[++argidx];
continue;
}
break; break;
} }
extra_args(args, argidx, design); 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")) if (!(tech == "t16ffc" || tech == "t40lp"))
log_cmd_error("Invalid ADI -tech setting: '%s'.\n", tech); log_cmd_error("Invalid ADI -tech setting: '%s'.\n", tech);
@ -276,7 +291,7 @@ struct SynthAnalogDevicesPass : public ScriptPass
} }
if (check_label("prepare")) { if (check_label("prepare")) {
run("proc"); run("proc -latches " + latches);
if (flatten || help_mode) { if (flatten || help_mode) {
run("check"); run("check");
run("flatten", "(with '-flatten')"); run("flatten", "(with '-flatten')");
@ -439,6 +454,8 @@ struct SynthAnalogDevicesPass : public ScriptPass
} }
if (check_label("map_ffs")) { 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"); run("dfflegalize -cell $_DFFE_?P?P_ r -cell $_SDFFE_?P?P_ r");
if (abc9 || help_mode) { if (abc9 || help_mode) {
if (dff || help_mode) if (dff || help_mode)

View File

@ -78,7 +78,7 @@ struct SynthPass : public ScriptPass {
log(" -nordff\n"); log(" -nordff\n");
log(" passed to 'memory'. prohibits merging of FFs into memory read ports\n"); log(" passed to 'memory'. prohibits merging of FFs into memory read ports\n");
log("\n"); log("\n");
log(" -latches <auto|warn|error>\n"); log(" -latches <info|warn|error>\n");
log(" controls how the inference of a latch is reported.\n"); log(" controls how the inference of a latch is reported.\n");
log("\n"); log("\n");
log(" -noshare\n"); log(" -noshare\n");

View File

@ -68,6 +68,7 @@ struct SynthEfinixPass : public ScriptPass
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
@ -160,7 +161,7 @@ struct SynthEfinixPass : public ScriptPass
if (flatten && check_label("flatten", "(unless -noflatten)")) 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("check");
run("flatten"); run("flatten");
run("tribuf -logic"); run("tribuf -logic");

View File

@ -115,6 +115,7 @@ struct SynthPass : public ScriptPass {
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
@ -289,7 +290,7 @@ struct SynthPass : public ScriptPass {
run("hierarchy -check"); run("hierarchy -check");
} else } else
run(stringf("hierarchy -check -top %s", top_module)); 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)")) { if (check_label("flatten", "(unless -noflatten)")) {

View File

@ -119,6 +119,7 @@ struct SynthIce40Pass : public ScriptPass
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
@ -304,7 +305,7 @@ struct SynthIce40Pass : public ScriptPass
{ {
run("read_verilog " + define + " -lib -specify +/ice40/cells_sim.v"); run("read_verilog " + define + " -lib -specify +/ice40/cells_sim.v");
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt)); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt));
run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); run("proc -latches " + latches);
} }
if (check_label("flatten", "(unless -noflatten)")) if (check_label("flatten", "(unless -noflatten)"))

View File

@ -72,12 +72,19 @@ struct SynthIntelALMPass : public ScriptPass {
log(" -noclkbuf\n"); log(" -noclkbuf\n");
log(" do not insert global clock buffers\n"); log(" do not insert global clock buffers\n");
log("\n"); log("\n");
log(" -latches <info|warn|error>\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"); log("The following commands are executed by this synthesis command:\n");
help_script(); help_script();
log("\n"); 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; bool flatten, nolutram, nobram, dff, nodsp, noiopad, noclkbuf;
void clear_flags() override void clear_flags() override
@ -85,6 +92,7 @@ struct SynthIntelALMPass : public ScriptPass {
top_opt = "-auto-top"; top_opt = "-auto-top";
family_opt = "cyclonev"; family_opt = "cyclonev";
bram_type = "m10k"; bram_type = "m10k";
latches = "error";
flatten = true; flatten = true;
nolutram = false; nolutram = false;
nobram = false; nobram = false;
@ -145,10 +153,17 @@ struct SynthIntelALMPass : public ScriptPass {
noclkbuf = true; noclkbuf = true;
continue; continue;
} }
if (args[argidx] == "-latches" && argidx + 1 < args.size()) {
latches = args[++argidx];
continue;
}
break; break;
} }
extra_args(args, argidx, design); 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()) if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n"); log_cmd_error("This command only operates on fully selected designs!\n");
@ -183,7 +198,7 @@ struct SynthIntelALMPass : public ScriptPass {
} }
if (check_label("coarse")) { if (check_label("coarse")) {
run("proc"); run("proc -latches " + latches);
if (flatten || help_mode) { if (flatten || help_mode) {
run("check"); run("check");
run("flatten", "(skip if -noflatten)"); run("flatten", "(skip if -noflatten)");
@ -241,6 +256,8 @@ struct SynthIntelALMPass : public ScriptPass {
} }
if (check_label("map_ffs")) { if (check_label("map_ffs")) {
if (latches == "error" || help_mode)
run("check -latchonly -assert", "(only if -latches error, the default)");
run("techmap"); run("techmap");
run("dfflegalize -cell $_DFFE_PN0P_ 0 -cell $_SDFFCE_PP0P_ 0"); run("dfflegalize -cell $_DFFE_PN0P_ 0 -cell $_SDFFCE_PP0P_ 0");
run("techmap -map +/intel_alm/common/dff_map.v"); run("techmap -map +/intel_alm/common/dff_map.v");

View File

@ -161,6 +161,7 @@ struct SynthLatticePass : public ScriptPass
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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(" (ignored with -asyncprld, which has a latch primitive)\n");
log("\n"); log("\n");
log("\n"); log("\n");
@ -416,7 +417,7 @@ struct SynthLatticePass : public ScriptPass
if (check_label("coarse")) 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) { if (flatten || help_mode) {
run("check"); run("check");
run("flatten"); run("flatten");

View File

@ -102,6 +102,7 @@ struct SynthNanoXplorePass : public ScriptPass
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
@ -263,7 +264,7 @@ struct SynthNanoXplorePass : public ScriptPass
if (check_label("coarse")) if (check_label("coarse"))
{ {
run("proc -latches " + (latches == "info" ? std::string("info") : std::string("warn"))); run("proc -latches " + latches);
if (flatten || help_mode) { if (flatten || help_mode) {
run("check"); run("check");
run("flatten", "(skip if -noflatten)"); run("flatten", "(skip if -noflatten)");

View File

@ -77,6 +77,7 @@ struct SynthQuickLogicPass : public ScriptPass {
log(" dedicated hardware primitive and are implemented using LUTs\n"); log(" dedicated hardware primitive and are implemented using LUTs\n");
log(" instead. 'error' (the default) aborts synthesis, 'warn' only\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(" 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(" (only applies to the pp3 family)\n");
log("\n"); log("\n");
log("The following commands are executed by this synthesis command:\n"); log("The following commands are executed by this synthesis command:\n");
@ -226,7 +227,7 @@ struct SynthQuickLogicPass : public ScriptPass {
} }
if (check_label("prepare")) { 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) { if (flatten) {
run("check"); run("check");
run("flatten", "(unless -noflatten)"); run("flatten", "(unless -noflatten)");

View File

@ -22,6 +22,20 @@ logger -check-expected
design -reset design -reset
# always_latch is exempt
read_verilog -sv <<EOT
module top(input g, d, output reg q);
always_latch if (g) q <= d;
endmodule
EOT
logger -expect-no-warnings
logger -expect log "Latch inferred for signal .* from always_latch process" 1
proc -latches error
logger -check-expected
select -assert-count 1 t:$dlatch a:always_latch %i
design -reset
# error # error
read_verilog <<EOT read_verilog <<EOT
module top(input g, rn, d, output reg q); module top(input g, rn, d, output reg q);

View File

@ -21,6 +21,18 @@ hierarchy -top top
proc proc
check -nolatches -assert check -nolatches -assert
design -reset
read_verilog -sv <<EOT
module top(input g, d, output reg q);
always_latch if (g) q <= d;
endmodule
EOT
hierarchy -top top
proc
select -assert-count 1 t:$dlatch a:always_latch %i
check -nolatches -assert
check -latchonly -assert
design -reset design -reset
read_verilog <<EOT read_verilog <<EOT
module top(input g, d, output reg q, output y); module top(input g, d, output reg q, output y);

View File

@ -14,7 +14,18 @@ design -load read
synth_ice40 -latches info synth_ice40 -latches info
select -assert-count 1 t:SB_LUT4 select -assert-count 1 t:SB_LUT4
design -load read # always_latch
logger -expect warning "Latch inferred for signal" 1 design -reset
logger -expect error "Found 1 problems in 'check -assert'" 1 read_verilog -sv <<EOT
module top(input d, en, output reg q);
always_latch if (en) q = d;
endmodule
EOT
logger -expect-no-warnings
synth_ice40
logger -check-expected
select -assert-count 1 t:SB_LUT4
design -load read
logger -expect error "Latch inferred for signal" 1
synth_ice40 synth_ice40