Craig
Craig

Reputation: 928

Why would a simulation/synthesis mismatch occur with non-blocking initialization of signals?

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

Answers (2)

Mikef
Mikef

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

toolic
toolic

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

Related Questions