In Verilog and SystemVerilog, always blocks describe hardware behaviour sequentially, similar to how software is written. Statements are evaluated in the order that they’re written. Take this module, describing a multiplexer.

// ...
output reg f;
always @(x,y,s) begin // like a {
	if (s == 0)
		f = x;
	else
		f = y;
end // like a }
// ...

All signals assigned a value in always must be of type reg, but cannot be assigned outside of the block. In SystemVerilog, we can define signals of a type logic, which don’t have this restriction.

If we have multiple statements in a block, then we must use the keywords begin and end. We cannot use wire within always. Modules can only be instantiated outside always blocks, not inside. A workaround is to call always within the sub-module.1 Or to use generate statements.

Sensitivity list

Whatever parameters are in the always statement describe a “sensitivity list”, i.e., where the code in the always block is sensitive to changes in the signals. We can also instead put an asterisk in the sensitivity list — what this means is that we let Verilog figure out the sensitivity list. For combinational circuits, we should only pass in an asterisk.

For sequential circuits, we want to be able to pass in a clock edge. Oftentimes we explicitly put in:

always@(posedge clock, negedge reset_n)

In SystemVerilog, it’s best practice to specify combinational logic with always_comb (no sensitivity list), and edge-sensitive logic with always_ff (sensitivity list requires edges).

Enabled features

Certain language constructs are only enabled within always blocks.

  • Regular conditionals, like if and else statements.
  • case statements, analogous to switch statements

Footnotes

  1. There’s a really good explanation on StackOverflow on other possible things you can do.