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.