Module 5: Behavioral Verilog for Synthesis ยท 5.1 Procedural Blocks: always and initial

always and initial

Every file in a project is a design file or a testbench. Design files become transistors. Testbenches drive the design and check it. Same language, different jobs, and the clearest line between them is always against initial.

The rule

always describes hardware. It runs forever, starting again from the top each time it finishes, and every synthesisable always block becomes gates or flip-flops.

initial runs once, at time zero. It has no hardware meaning and synthesis tools ignore it. Not “reject it”: most of them skip it quietly, build everything else, and hand you a circuit that differs from what you simulated. That is the simulation-synthesis mismatch of section 4.1 again, from a different door.

Three forms of always

always @(*) begin
    y = a & b;                 // combinational: an AND gate
end

always @(posedge clk) begin
    q <= d;                    // sequential: a flip-flop
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;             // a flip-flop with asynchronous reset
    else
        q <= d;
end

The first runs whenever any signal it reads changes. The second runs on the rising clock edge only. The third runs on the clock edge or the falling edge of the reset, and maps to the flip-flop cell with an asynchronous clear pin. All three describe something that exists continuously once the power is on.

What initial looks like

initial begin
    $dumpfile("tb.vcd");
    $dumpvars(0, tb_name);

    clk = 0;
    rst = 1;
    d   = 0;

    #20 rst = 0;
    #10 d   = 1;
    #50 $finish;
end

This is the heartbeat of a testbench: set the inputs, wait, change them, stop. No circuit starts at time zero, holds a pin for 20 ns and then releases it forever. Real hardware powers up into whatever state its reset logic gives it. ASIC flows forbid initial in RTL outright and the lint rules treat one as an error.

Side by side

// dff.v: the design
`timescale 1ns / 1ps
`default_nettype none

module dff (
    input  wire clk,
    input  wire rst,
    input  wire d,
    output reg  q
);
    always @(posedge clk) begin
        if (rst)
            q <= 1'b0;
        else
            q <= d;
    end

endmodule
// tb_dff.v: the testbench
`timescale 1ns / 1ps
`default_nettype none

module tb_dff;

    reg  clk, rst, d;
    wire q;

    dff uut (
        .clk(clk),
        .rst(rst),
        .d  (d),
        .q  (q)
    );

    always #5 clk = ~clk;    // 10 ns period; testbench only

    initial begin
        $dumpfile("tb.vcd");
        $dumpvars(0, tb_dff);

        clk = 0;
        rst = 1;
        d   = 0;

        @(posedge clk); @(posedge clk);
        rst = 0;

        @(posedge clk); d = 1;
        @(posedge clk); d = 0;
        @(posedge clk); d = 1;

        #50 $finish;
    end

endmodule

The clock generator is an always with no sensitivity list: it toggles, waits 5 ns, and goes round again. That is fine in a testbench and meaningless in a design, where the synthesiser will reject it.

The classic mistake

// wrong, in a design file
module counter (
    input  wire       clk,
    output reg  [3:0] count
);
    initial begin
        count = 4'd0;
    end

    always @(posedge clk) begin
        count <= count + 1'b1;
    end

endmodule

In simulation this is perfect: the count starts at 0 and climbs. In an ASIC flow the initial is dropped and the counter powers up at whatever the flip-flops happen to hold, 7, or 13, or x in a gate-level simulation. FPGA tools are the exception: the configuration bitstream sets every flip-flop and memory cell when the device is programmed, so Vivado and Quartus do honour initial values and declaration initialisers. That is a documented, technology-specific behaviour and not a property of Verilog, and code that depends on it does not port.

The right way

`timescale 1ns / 1ps
`default_nettype none

module counter (
    input  wire       clk,
    input  wire       rst,        // synchronous, active high
    output reg  [3:0] count
);
    always @(posedge clk) begin
        if (rst)
            count <= 4'd0;
        else
            count <= count + 1'b1;
    end

endmodule

The testbench holds rst high for a few cycles and releases it. The reset becomes a mux in front of each D input, or the flip-flop’s clear pin if it is asynchronous, and either way it is real hardware that behaves the same in simulation and in silicon.

The one place initial earns its keep in a design file is memory contents on an FPGA: an array with an initial block, or $readmemh, becomes block RAM with those contents in the bitstream. Even then, a reset sequence or a file is the more portable choice. When you see initial in a design file, the question is “why”, and the honest answer is usually that someone learned to simulate before they learned to synthesise.

Practice Exercise

This loadable register has two problems from this article and the next. Find them and fix them.

`timescale 1ns / 1ps
`default_nettype none

module loadable_reg (
    input  wire       clk,
    input  wire       load,
    input  wire [3:0] data_in,
    output reg  [3:0] data_out
);
    initial begin
        data_out = 4'hF;
    end

    always @(posedge clk) begin
        if (load) begin
            data_out = data_in;
        end
    end

endmodule
Solution

The initial block sets a starting value that synthesis will not provide; replace it with a reset. The clocked block uses a blocking assignment; a clocked block uses <=.

`timescale 1ns / 1ps
`default_nettype none

module loadable_reg (
    input  wire       clk,
    input  wire       rst,
    input  wire       load,
    input  wire [3:0] data_in,
    output reg  [3:0] data_out
);
    always @(posedge clk) begin
        if (rst)
            data_out <= 4'hF;
        else if (load)
            data_out <= data_in;
    end

endmodule

The next article is about that second problem: what = actually does inside a block, where it is the right tool, and where it makes a race.

โ† Everything Runs at Once Blocking Assignments โ†’