SystemVerilog Procedural Constructs and Function Enhancements

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.

Tags: SystemVerilog proceduralblocks functions tasks synthesis

Posted on Wed, 29 Jul 2026 16:41:57 +0000 by ehmer