diff --git a/elaborate.cc b/elaborate.cc index 988110c7f..c54fa37f6 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -35,6 +35,8 @@ # include # include # include +# include +# include # 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 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 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 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(listroots) // 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 << "" << ": debug: " << " finishing with " diff --git a/ivtest/gold/always_ff_lval_fail-iverilog-stderr.gold b/ivtest/gold/always_ff_lval_fail-iverilog-stderr.gold new file mode 100644 index 000000000..6b87d27d4 --- /dev/null +++ b/ivtest/gold/always_ff_lval_fail-iverilog-stderr.gold @@ -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 diff --git a/ivtest/ivltests/always_ff_lval_fail.v b/ivtest/ivltests/always_ff_lval_fail.v new file mode 100644 index 000000000..3e5325891 --- /dev/null +++ b/ivtest/ivltests/always_ff_lval_fail.v @@ -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 diff --git a/ivtest/ivltests/br_gh687.v b/ivtest/ivltests/br_gh687.v index e4a1682c6..5584ca93d 100644 --- a/ivtest/ivltests/br_gh687.v +++ b/ivtest/ivltests/br_gh687.v @@ -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]); diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 09f0498cf..6ac0b0bee 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -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 diff --git a/ivtest/vvp_tests/always_ff_lval_fail.json b/ivtest/vvp_tests/always_ff_lval_fail.json new file mode 100644 index 000000000..e2839467f --- /dev/null +++ b/ivtest/vvp_tests/always_ff_lval_fail.json @@ -0,0 +1,6 @@ +{ + "type" : "CE", + "source" : "always_ff_lval_fail.v", + "iverilog-args" : [ "-g2005-sv" ], + "gold" : "always_ff_lval_fail" +} diff --git a/netlist.h b/netlist.h index 86bfb9b73..ba0615701 100644 --- a/netlist.h +++ b/netlist.h @@ -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);