6.1 General Purpose Always Block in Verilog
The always procedural block creates an infinite loop executing its contained statements repeatedly. Simulation advancement requires time controls or event controls such as delays (#), wait conditions, or event triggers (@).
always @(a, b) begin
sum = a + b;
diff = a - b;
prod = a * b;
end
Edge-sensitive event controls at block start act as sensitivity lists, making the entire block responsive to signal changes.
Verilog's always block serves multiple logic types: combinational, latched, and sequential. Tools must deduce hardware type from block contents, which can lead to synthesis errors when intent is unclear.
always @(posedge clock) begin
wait (!resetN)
if (mode) q1 = a + b;
else q1 = a - b;
q2 <= q1 | (q2 << 2);
q2++;
end
Synthesis guidelines mandate specific patterns for combinational, latched, and sequential logic modeling:
- Combinational Logic: Edge-sensitive event control without posedge/negedge, all inputs in sensitivity list, no other event controls
- Latched Logic: Same as combinational but with some variables not updated for all conditions
- Sequential Logic: All signals qualified with posedge/negedge, no other event controls
6.2 Specialized Procedural Blocks in SystemVerilog
SystemVerilog provides always_comb, always_latch, and always_ff for precise logic modeling:
6.2.1 Combinational Logic Blocks
The always_comb block explicitly models combinational logic without requiring sensitivity lists:
always_comb
if (!mode)
y = a + b;
else
y = a - b;
Tools automatically infer sensitivity lists including external signals read by the block. Temporary variables within the block aren't included in the list.
Variables assigned in always_comb cennot be written by other blocks, ensuring proper combinational behavior.
Always_comb executes once at simulation time zero, guaranteeing output consistency with initial inputs:
module fsm_example (
input clk, resetN,
input [2:0] state,
output [2:0] next_state
);
always_comb begin
case (state)
3'b000: next_state = 3'b001;
3'b001: next_state = 3'b010;
3'b010: next_state = 3'b000;
endcase
end
endmodule
6.2.2 Latched Logic Blocks
always_latch models latched-based logic:
always_latch
if (enable) q <= d;
Similar semantics to always_comb, with automatic sensitivity list inference and time-zero execution.
Tools validate that latched logic contains appropriate storage behavior:
module latch_example (
input clk, enable,
input [4:0] d,
output logic [4:0] q
);
logic internal_enable;
always_latch begin
if (!resetN)
internal_enable <= 0;
else if (enable)
internal_enable <= 1;
else if (overflow)
internal_enable <= 0;
end
always @(posedge clk, negedge resetN) begin
if (!resetN)
{overflow, q} <= 0;
else if (internal_enable)
{overflow, q} <= q + 1;
end
endmodule
6.2.3 Sequential Logic Blocks
always_ff models sequential logic with explicit sensitivity lists:
always_ff @(posedge clock, negedge resetN)
if (!resetN)
q <= 0;
else
q <= d;
All sensitivity list signals must be qualified with posedge or negedge for synthesis compliance.
6.3 Function and Task Enhancements
6.3.1 Statement Grouping
SystemVerilog eliminates mandatory begin/end grouping for multiple statements:
function states_t next_state(states_t current_state);
next_state = current_state;
case (current_state)
WAITE: if (start) next_state = LOAD;
LOAD: if (done) next_state = STORE;
STORE: next_state = WAITE;
endcase
endfunction
6.3.2 Return Value Handling
SystemVerilog functions can use explicit return statements:
function int add_and_inc(input int a, b);
return a + b + 1;
endfunction
6.3.3 Early Exit Capability
Return statements enable immediate function/task termination:
function automatic int log2(input int n);
if (n <= 1) return 1;
log2 = 0;
while (n > 1) begin
n = n / 2;
log2++;
end
endfunction
6.3.4 Void Functions
Void functions provide no return value:
function void fill_packet(
input logic [63:0] data_in,
output packet_t data_out);
data_out.data = data_in;
for (int i = 0; i <= 7; i++)
data_out.check[i] = ^data_in[(8*i)+:8];
data_out.valid = 1;
endfunction
6.3.5 Named Arguments
Arguments can be passed by name:
always @(posedge clock)
result <= divide(.denominator(b), .numerator(a));
6.3.6 Enhanced Formal Arguments
Functions can have input/output/inout parameters:
function [63:0] add(input [63:0] a, b, output overflow);
{overflow, add} = a + b;
endfunction
6.3.7 Default Argument Values
Optional default values simplify function calls:
function int incrementer(int count=0, step=1);
incrementer = count + step;
endfunction
6.3.8 Reference Arguments
Reference arguments pass by alias instead of copy:
function automatic void fill_packet(
ref logic [7:0] data_in [0:7],
ref packet_t data_out);
for (int i=0; i<=7; i++) begin
data_out.data[(8*i)+:8] = data_in[i];
data_out.check[i] = ^data_in[i];
end
data_out.valid = 1;
endfunction
6.3.9 Named Block Ends
Named end statements improve code readability:
function int add_and_inc(int a, b);
return a + b + 1;
endfunction : add_and_inc
6.4 Summary
SystemVerilog's specialized procedural blocks enhance clarity and tool compatibility for hardware modeling. The always_comb, always_latch, and always_ff constructs provide unambiguous design intent while enabling better simulation and synthesis results. Function enhancements streamline complex design development through improved syntax and capabilities.