SystemVerilog Insights: always @* and always_comb are not the same !!
Posted by: Trent McClements | Posted on: February 3rd, 2015 | 8 Comments
Always_comb is one of a number of new process types added to SystemVerilog to give developers more control and improve code readability. On the surface always_comb seems like a convenient short-hand for always @*, but, surprisingly, that’s not actually the case. always_comb is actually quite different than always @*, and you should be aware of how if you plan on using it!
Before we dig too deeply into the details there is one thing about always_comb that I would like to point out. Much to my dismay, always_combdoes not prevent accidental latch inference (something I’m sure we’ve all done with the Verilog always procedure). The SystemVerilog LRM does not declare latch inference within always_comb procedures as illegal. All that is suggested, but not required, of your SystemVerilog parser is to warn the user in the case of latch inference within an always_comb procedure. It’s an interesting choice, especially given that SystemVerilog brought in the always_latch and always_ff procedures too!
With that detail out of the way, let’s take a look at the differences between always @* and always_comb. In generally, these fall into four areas — time, sensitivity, content, and exclusivity of variables.
When looking at the always_comb‘s execution time keep in mind the procedures intent — to model combinational logic. As such, the always_comb procedure, unlike always @*, executes at time zero. This makes sense as the outputs of a combinational logic model should have an initial value that depends on the values of the input signals. This is not possible with the always @* procedure as it’s outputs values are slave to the sensitivity list and thus require a delta on a sensitivity list signal to get updated.
Both always_comb and always @* have inferred sensitivity lists. The inference, however, is different between the two procedures. always_comb‘s sensitivity goes a little deeper than always @*. always_comb is sensitive to changes inside a function as well as expressions in immediate assertions within functions. always @* is not sensitive to either. Both procedures are sensitive to changes to the inputs of functions and to expressions in assertions used directly within the procedures. For example:
In the above code the always_comb procedure is sensitive to comb_in0, comb_in1, comb_in2, comb_sensitive and func_sensitive. The always_comb is not sensitive to comb_ignored because it is not in the immediate assertion statement but rather in the action block of the assertion. If the above always_comb procedure was changed to always @* then the procedure’s implicit sensitivity list would only include comb_in0, comb_in1, comb_in2 and comb_sensitive.
The SystemVerilog LRM restricts the type of statements allowed within an always_comb procedure. This also makes sense in the context of a model for combinational logic. Blocking statements, fork-join statements, and statements with blocking timing or event controls are forbidden within an always_comb procedure.
One very interesting aspect of always_comb is that the procedure owns the variables assigned with itself. That is, no other process is allowed to assign to a variable that an always_comb procedure assigns to. always @* has no such exclusivity. With that said, always_comb does not own assignment to the complete variable, only the portions it assigns to. So, the following is legal:
In the end, the always @* and always_comb are more like close siblings than identical twins. They both have similar abilities but are forced to live under close, but not identical, rules. It’s important to be aware of these differences, especially with respect to procedure sensitivity and execution time, as they can have surprising impacts on your simulations!
Have you had any experience with always_comb or perhaps useful “rule of thumb” guidelines on when to use always @* instead of always_comb (or vise-versa)? We’d love to hear about it in the comments!
LRM says: The always_ff procedure imposes the restriction that it contains one and only one event control and no blocking timing controls. Variables on the left-hand side of assignments within an always_ff procedure, including variables from the contents of a called function, shall not be written to by any other process.
Software tools should perform additional checks to warn if the behavior within an always_ff procedure does not represent sequential logic.
A blocking assignment is not a blocking timing control. Blocking timing controls are #, @, wait, and task calls. Thus,
always_ff @(posedge clk) begin // LEGAL
temp=a && b; // local evaluation, for later usage
if(temp) c <= d;
always_ff @(posedge clk) begin // ILLEGAL
#1 // <<<< ILLEGAL
temp=a && b; // local evaluation, for later usage
if(temp) e <= d;
Ben Cohen (831) 345-1759
* SystemVerilog Assertions Handbook, 2nd Edition, 2010 ISBN 878-0-9705394-8-7
* A Pragmatic Approach to VMM Adoption 2006 ISBN 0-9705394-9-5
* Using PSL/SUGAR for Formal and Dynamic Verification 2nd Edition, 2004, ISBN 0-9705394-6-0
* Real Chip Design and Verification Using Verilog and VHDL, 2002 isbn 0-9705394-2-8
* Component Design by Example, 2001 ISBN 0-9705394-0-1
* VHDL Coding Styles and Methodologies, 2nd Edition, 1999 ISBN 0-7923-8474-1
* VHDL Answers to Frequently Asked Questions, 2nd Edition ISBN 0-7923-8115