Fix #1317: Variable written in an always_ff should not driven by other process

This commit is contained in:
Cookie 2026-05-27 13:41:07 +08:00
parent 311c22e4de
commit aaa1945549
7 changed files with 150 additions and 5 deletions

View File

@ -35,6 +35,8 @@
# include <iostream>
# include <sstream>
# include <list>
# include <map>
# include <set>
# include "pform.h"
# include "PClass.h"
# include "PEvent.h"
@ -7672,6 +7674,89 @@ static void check_ff_sensitivity(const NetProc* statement)
}
}
bool Design::check_always_ff_lvals()
{
bool result = false;
map<const Nexus*, const NetProcTop*> always_ff_lvals;
for (NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
if (pr->type() != IVL_PR_ALWAYS_FF)
continue;
NexusSet outputs;
pr->statement()->nex_output(outputs);
set<const Nexus*> seen_outputs;
for (unsigned idx = 0 ; idx < outputs.size() ; idx += 1) {
const NexusSet::elem_t&output = outputs[idx];
const Nexus*nexus = output.lnk.nexus();
if (! seen_outputs.insert(nexus).second)
continue;
auto cur = always_ff_lvals.find(nexus);
if (cur == always_ff_lvals.end()) {
always_ff_lvals[nexus] = pr;
continue;
}
if (cur->second == pr)
continue;
const char*name = nexus->name();
cerr << cur->second->get_fileline() << ": error: "
"variable '" << name << "' written in an "
"always_ff process may not be written by any "
"other process." << endl;
cerr << pr->get_fileline() << ": : "
"process also writes variable '" << name
<< "'." << endl;
result = true;
errors += 1;
}
}
if (always_ff_lvals.empty())
return result;
for (NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
if (pr->type() == IVL_PR_ALWAYS_FF)
continue;
if ((pr->type() == IVL_PR_INITIAL) &&
(pr->attribute(perm_string::literal("_ivl_schedule_init"))
.as_ulong() != 0))
continue;
NexusSet outputs;
pr->statement()->nex_output(outputs);
set<const Nexus*> seen_outputs;
for (unsigned idx = 0 ; idx < outputs.size() ; idx += 1) {
const NexusSet::elem_t&output = outputs[idx];
const Nexus*nexus = output.lnk.nexus();
if (! seen_outputs.insert(nexus).second)
continue;
auto cur = always_ff_lvals.find(nexus);
if (cur == always_ff_lvals.end())
continue;
const char*name = nexus->name();
cerr << cur->second->get_fileline() << ": error: "
"variable '" << name << "' written in an "
"always_ff process may not be written by any "
"other process." << endl;
cerr << pr->get_fileline() << ": : "
"process also writes variable '" << name
<< "'." << endl;
result = true;
errors += 1;
}
}
return result;
}
/*
* Check to see if the always_* processes only contain synthesizable
* constructs.
@ -8098,6 +8183,10 @@ Design* elaborate(list<perm_string>roots)
// synthesizable constructs
has_failure |= des->check_proc_synth();
// Check that always_ff l-values are not also written by another
// process.
has_failure |= des->check_always_ff_lvals();
if (debug_elaborate) {
cerr << "<toplevel>" << ": debug: "
<< " finishing with "

View File

@ -0,0 +1,7 @@
ivltests/always_ff_lval_fail.v:33: error: variable 'ff_ff_driver.q' written in an always_ff process may not be written by any other process.
ivltests/always_ff_lval_fail.v:29: : process also writes variable 'ff_ff_driver.q'.
ivltests/always_ff_lval_fail.v:6: error: variable 'ff_initial_driver.a' written in an always_ff process may not be written by any other process.
ivltests/always_ff_lval_fail.v:4: : process also writes variable 'ff_initial_driver.a'.
ivltests/always_ff_lval_fail.v:19: error: variable 'ff_always_driver.q' written in an always_ff process may not be written by any other process.
ivltests/always_ff_lval_fail.v:15: : process also writes variable 'ff_always_driver.q'.
Elaboration failed

View File

@ -0,0 +1,36 @@
module ff_initial_driver;
logic a;
initial a = 1'b0;
always_ff @(posedge a) begin
a <= 1'b1;
end
endmodule
module ff_always_driver;
logic clk;
logic q;
always @* begin
q = clk;
end
always_ff @(posedge clk) begin
q <= 1'b1;
end
endmodule
module ff_ff_driver;
logic clk1;
logic clk2;
logic q;
always_ff @(posedge clk1) begin
q <= 1'b0;
end
always_ff @(posedge clk2) begin
q <= 1'b1;
end
endmodule

View File

@ -1,19 +1,24 @@
module top;
logic [9:0] pipe = 0;
logic [9:0] pipe;
logic [4:0] i;
logic clk = 0;
logic rst = 1;
always #1 clk = ~clk;
always_ff @(posedge clk) begin
for (i=0; i<9; i++) begin
pipe[i+1] <= pipe[i];
if (rst) begin
pipe <= 10'b0000000001;
end else begin
for (i=0; i<9; i++) begin
pipe[i+1] <= pipe[i];
end
pipe[0] <= pipe[9];
end
pipe[0] <= pipe[9];
end
initial begin
pipe[0] = 1'b1;
#2 rst = 0;
for (int j=0; j<10; j++) begin
$display(pipe[9]);

View File

@ -2,6 +2,7 @@
# Test list files are a list of test names and the json that
# describes the test.
always_ff_lval_fail vvp_tests/always_ff_lval_fail.json
always4A vvp_tests/always4A.json
always4B vvp_tests/always4B.json
analog1 vvp_tests/analog1.json

View File

@ -0,0 +1,6 @@
{
"type" : "CE",
"source" : "always_ff_lval_fail.v",
"iverilog-args" : [ "-g2005-sv" ],
"gold" : "always_ff_lval_fail"
}

View File

@ -5236,6 +5236,7 @@ class Design {
void delete_process(NetProcTop*);
bool check_proc_delay() const;
bool check_proc_synth() const;
bool check_always_ff_lvals();
NetNet* find_discipline_reference(ivl_discipline_t dis, NetScope*scope);