Reputation: 928
In the VHDL world, it is common in a clocked process to initialize a signal to a default value and then override it later (via IF or CASE statements) as necessary. I want to use the same technique in Verilog, but have been told it could be problematic.
Consider this example code:
always_ff @(posedge sys_ref_clk or negedge rst_n)
if (~rst_n) begin
mstate <= STATE1;
mysig <= '0;
end else begin
mysig <= '0;
case (mstate)
STATE1 : begin
if (go) begin
mstate <= STATE2;
end
end
STATE2 : begin
mysig <= '1;
mstate <= STATE3;
end
STATE3 : begin
mstate <= STATE1;
end
endcase
end
Here, I want STATE2 to cause mysig
to become 1, and all other states cause mysig
to become 0. I do not want to have to type mysig <= '0;
in STATE1 and STATE3, so I just set a default and only override it in STATE2.
In VHDL, the above is a technique I used successfully for many years. But upon switching to Verilog and to a new linter, I get lint errors such as the following:
[211] OverridenAssignment error
/path/to/file/myfile.sv 14 5 Flipflop 'mysig' is assigned over the same signal in an always construct for sequential circuits (at line '6')
I believe this is an overzealous lint error, but I admittedly don't know Verilog as well as I know VHDL. When I asked the person in charge of our lint tool about it, I was told that in Verilog, this might be dangerous -- specifically because non-blocking assignments can lead to inefficient or incorrect hardware structures, especially when used for initialization -- also that non-blocking assignments may be interpreted differently between synthesis and simulation tools.
Is this really the case that the above initialization of mysig
is dangerous Verilog? It simulates just fine. What other way could a synthesis tool interpret this besides the (obvious, IMO) way it was intended?
Upvotes: 3
Views: 321
Reputation: 2508
I don't see a problem with the posted code. "The last assignment wins" when referring to the behavior in a process is common to VHDL & Verilog. As long as you use nb assignments for synchronous processes, & blocking for combinational you should be fine. I have used the state machine style with the driver before the case statement, so that you don't need to specify a value in all states. Worked with no errors or warnings in Vivado.
This issue has been written about, probably in more than one place. Stu Sutherland discusses this style and has a section titled "Pros & Cons of pre-case assignments before decision statements" in his 2017 book "RTL Modeling with SyntemVerilog for Simulation & Synthesis".
Sutherland states "A disadvantage of this coding style is that in can be more difficult to see what is assigned to all the variables used by the procedure by looking at a specific case item branch. It is necessary to look at the pre-case assignment and the case item assignments to see all the values assigned. In a large, complex decoder, these assignments could be separated by many lines of code."
I find the pre-case assignment overall easier to read, and harder to make a mistake. If I have to put the same foo = 1; in each arm of the case statement, tomorrow I will add a state and forget to put foo = 1; in for the new state, and now have something to debug. I will be lucky to find it myself tomorrow the same day I put the bug in.
Verilog has an initial
process block which may or may not be used for setting initial values depending on the synthesis tool and exactly what you are trying to do. Consult the synthesis guide for the particular family of part you are targeting for things like what it will do with initial
blocks.
For edgy constructs like assignments in an Verilog initial
block (referring to synthesis) The problem could become one of Vendor portability. One vendor might support the construct where the other may not. So there is risk in coding using that style.
Upvotes: 3
Reputation: 62082
Using this Verilog coding style is guaranteed to simulate as you expect (for simulation software that adheres to the current IEEE Std 1800). The last nonblocking assignment wins.
I have seen synthesis tools and RTL linting tools generate warning messages along the lines of "multiple nonblocking assignments". I don't recall the specific reasons, but the guidance was to change the code to avoid this situation. You could pessimistically assume the synthesis tool will have a problem with this coding style if you see such a warning.
I recommend that you avoid this practice in Verilog code.
It is not a common practice for sequential logic, and as such, it could make the design harder for other people to understand. If I saw the 1st assignment before the case
, I would not expect to see another assignment inside the case
. Note: it is a more common approach for combinational logic always
blocks, but that is not what you are asking about.
A lot of people fall into the trap of trying to cram too much logic into a single sequential always
block. In your case, I think the design would be clearer as 2 always
blocks. Remove mysig
from your always
block into its own block:
always_ff @(posedge sys_ref_clk or negedge rst_n)
if (~rst_n) begin
mysig <= '0;
end else begin
mysig <= (mstate == STATE2);
end
Upvotes: 1