1. always @(posedge clock) is the register
A combinational block runs whenever an input changes. A register changes only at a clock edge, and
Verilog says so in the sensitivity list: always @(posedge clock) runs its body once per rising
edge and at no other time. Whatever the body assigns is stored until the next edge. This is the
standard idiom synthesis tools turn into flip-flops; the course assumes a single clock and a
positive-edge synchronous design throughout.
Form
always @(posedge clock) begin
count <= count + 4'd1; // read the current value, store the next one
endThe right-hand side reads the current value, Q in the fundamentals. The assignment stores the next
value, D, and that value becomes visible only after the edge. count has to be declared reg; here
the keyword is finally paired with storage, but it is the edge in the sensitivity list that makes
it a register, not the keyword.
Note. Delays such as #5, system tasks such as $display, and an initial block that steps
through a test sequence exist for simulation; synthesis tools ignore or reject them, and which ones
they accept depends on the tool. @(posedge clock) is different: as the sensitivity of an always
block it is the standard way to describe a synthesizable register, which is exactly how Counter.v
below uses it. It is only in a testbench, as a wait in the middle of a sequence, that it is a
simulation device. Keep the DUT to the register and combinational idioms, and let the testbench use
any simulation construct it needs. This chapter runs everything in simulation with Icarus 11;
nothing here is synthesized.
2. Nonblocking <= reads now and updates after everything else at the edge
The nonblocking assignment <= evaluates its right-hand side immediately and postpones the update
of its target to a later step of the same simulation time, the nonblocking-update region. Every
<= triggered by one edge, in every block of the design, reads values from before the edge and
updates afterwards, together. That is the one-cycle rule you met in the fundamentals: in the cycle
after the edge, a register shows what was computed from the values before it.
| Assignment | Syntax | Right-hand side | Target changes | The next statement in the block sees |
|---|---|---|---|---|
| Blocking | target = expression; | evaluated now | immediately | the new value |
| Nonblocking | target <= expression; | evaluated now | in the nonblocking-update region, after the active statements of this time step | the old value |
reg [3:0] x;
initial begin
x = 1;
x <= 2;
$display("x=%0d", x); // 1: the <= update has not happened yet
#1 $display("x=%0d", x); // 2
endClocked logic means "every register computes its next value from the values before the edge", and
<= is the assignment that says exactly that. Reading a register in the same cycle gives its old
value because it is an edge-triggered register written with <=. It is not a law for every signal:
a wire, or a variable assigned with =, shows a new value at once.
3. The order of work inside one time step
3.1 Scheduling regions
The simulator advances time in steps and processes the work inside each step in a fixed order of regions. Three of them matter for testbenches.
| Region | What runs there |
|---|---|
| Active | Procedural statements that are ready, blocking = assignments, continuous-assignment updates, $display |
| Nonblocking update | The updates of every <= that ran in the active region |
| Postponed | $strobe and $monitor output, after everything else in the step has settled |
Two procedural blocks that wake up on the same rising edge both run in the active region, and the standard does not say which runs first. Most testbench races come from that undefined order. Follow these rules and the order stops mattering:
| Where | Rule |
|---|---|
| Clocked logic | Assign registers with <=, so every block reads the values from before the edge |
| Testbench inputs | Change them away from the sampling edge, for example on the falling edge |
| Reading outputs in the testbench | Either read at the edge knowing you see the old value, or read after the edge has settled |
3.2 $display, $strobe and $monitor
All three take the same format string; they differ in when they print.
Form
$display("format", value, ...);
$strobe("format", value, ...);
$monitor("format", value, ...);| Task | Prints when | How often |
|---|---|---|
$display | The moment the statement runs, in the active region | Once per call |
$strobe | At the end of the current time step, in the postponed region | Once per call |
$monitor | At the end of every time step in which one of its arguments changed | Called once, keeps printing |
reg [3:0] a = 0, b = 0;
initial $monitor("t=%0t a=%0d b=%0d", $time, a, b);
initial begin
#10 a = 1;
#10 b = 2;
#5 a = 3; b = 4; // two changes in the same time step
endHere $monitor prints one line each at times 0, 10, 20, and 25. At time 25 both a and b change,
but it prints only once, after that time step has finished.
4. Read a counter trace at the edge and after it
Counter.v is a 4-bit counter with a synchronous reset: at each rising edge, reset high stores 0,
otherwise the current value plus one. The reset is synchronous: it acts only at an edge, as
Reset, enable and priority works out.
// A 4-bit counter with a synchronous, active-high reset.
module counter (
input clock,
input reset,
output reg [3:0] count
);
always @(posedge clock) begin
if (reset) count <= 4'd0;
else count <= count + 4'd1;
end
endmoduleThe testbench makes a clock with always #5 clock = ~clock;, so rising edges fall at 5, 15, 25 and
so on. reset starts at 1 and is lowered on the falling edge at time 20. A second block prints two
lines at every rising edge: EDGE with $display, which runs at once, and AFTER with $strobe,
which prints at the very end of the same time step.
| Part | Behavior |
|---|---|
clock | Starts at 0 and inverts every 5 units |
reset | Starts at 1 |
always @(posedge clock) | At every rising edge, prints an EDGE line with $display and an AFTER line with $strobe |
initial | Waits two rising edges, lowers reset on the next falling edge, waits four more rising edges, and finishes one unit later |
module tb;
reg clock = 0;
reg reset = 1;
wire [3:0] count;
counter dut (.clock(clock), .reset(reset), .count(count));
// Period 10: rising edges at 5, 15, 25, ...
always #5 clock = ~clock;
// $display runs at the edge; $strobe waits until the edge's updates are done.
always @(posedge clock) begin
$display("EDGE t=%0t reset=%b count=%0d", $time, reset, count);
$strobe("AFTER t=%0t count=%0d", $time, count);
end
initial begin
repeat (2) @(posedge clock);
// Change inputs on the falling edge, half a period away from sampling.
@(negedge clock) reset = 0;
repeat (4) @(posedge clock);
#1 $finish;
end
endmodulePredict before you run.
- What does the first
EDGEline, at time 5, show forcount? - At time 25,
resetis low for the first time. Do theEDGEandAFTERlines show 0 or 1? - How many times does the counter increment before
$finish?
Check
What does the EDGE line at time 5, the first rising edge, show for count?
Check
At time 25 reset is 0 for the first time. What do the EDGE and AFTER lines show?
Check the result. Press Run and compare each pair of lines with this table.
| Time | Event | EDGE | AFTER |
|---|---|---|---|
| 5 | First rising edge, reset = 1 | x: nothing has assigned count yet | 0: reset applied |
| 15 | Second rising edge, reset = 1 | 0 | 0 |
| 20 | Falling edge: the testbench lowers reset | ||
| 25 | Rising edge, reset = 0 | 0 | 1 |
| 35, 45, 55 | Rising edges | 1, 2, 3 | 2, 3, 4 |
The EDGE line shows the value from before the edge for one precise reason: $display runs in the
active region of the time step, and the counter's <= updates in the nonblocking-update region that
follows. $strobe waits for the postponed region, so AFTER shows the value the edge produced. A
$display after a #1 would also show the new value. The x on the first line is why reset
matters: until the first edge with reset high, the register holds x.
Try it yourself. Change both <= in Counter.v to =. The counter counts in the same
sequence, but its update now happens in the active region, racing the testbench's $display. With
the Icarus version in this lab, the EDGE lines at times 35 and 55 print the new values 2 and 4,
while the one at 45 prints the old value 2. Nothing in the source decides that order. The AFTER
lines stay correct, because $strobe waits for the end of the time step.
Note. A testbench that reads values at the edge is only trustworthy when the design uses <=.
5. Two registers that swap, written twice
Swap.v holds two modules that differ by a single character. Both load x = 1, y = 2 while
load is high, then swap x and y at every rising edge: one with <=, one with =. Read the two
else branches side by side.
// Two registers that trade values on every rising edge.
module swap_nonblocking (
input clock,
input load,
output reg [3:0] x,
output reg [3:0] y
);
// <= reads both old values first, then updates both registers.
always @(posedge clock) begin
if (load) begin x <= 4'd1; y <= 4'd2; end
else begin x <= y; y <= x; end
end
endmodule
// The same text with = in place of <=.
module swap_blocking (
input clock,
input load,
output reg [3:0] x,
output reg [3:0] y
);
// = updates x at once, so the next line reads the new x.
always @(posedge clock) begin
if (load) begin x = 4'd1; y = 4'd2; end
else begin x = y; y = x; end
end
endmoduleTb.v instantiates both modules, keeps load high for the first edge, and prints both pairs at
every falling edge, half a period after the registers changed. Reading there avoids the race of
section 3, so each line shows the value after the edge.
module tb;
reg clock = 0;
reg load = 1;
wire [3:0] nx, ny, bx, by;
integer cycle = 0;
swap_nonblocking good (.clock(clock), .load(load), .x(nx), .y(ny));
swap_blocking bad (.clock(clock), .load(load), .x(bx), .y(by));
always #5 clock = ~clock;
// Sample on the falling edge, when every register has settled.
always @(negedge clock) begin
$display("CYCLE %0d load=%b nonblocking x=%0d y=%0d blocking x=%0d y=%0d",
cycle, load, nx, ny, bx, by);
cycle = cycle + 1;
load = 0;
if (cycle == 4) $finish;
end
endmodulePredict before you run. After the first swap edge, what are x and y in each module? What
happens after the second swap?
Check
After the first swap edge, what do the two modules hold?
Check the result. Press Run.
| Output line | nonblocking | blocking |
|---|---|---|
CYCLE 0 | x=1 y=2, right after loading | x=1 y=2 |
CYCLE 1 | x=2 y=1: both right-hand sides were read before either register changed | x=2 y=2: x = y made x 2 first, and y = x read the new x |
CYCLE 2, CYCLE 3 | Alternates between 1,2 and 2,1 | Stays at 2,2 |
The blocking result is not a simulator quirk. It describes different hardware, a circuit that copies
y into both registers, and a synthesis tool would build exactly that. With <= the order of the
two statements does not matter; with = it is the whole design.
6. Change one thing
In the blocking module, reverse the two statements to y = x; x = y; and predict before you run.
Check
With the blocking branch written y = x; x = y;, what do x and y settle to?
y takes the old x, 1, and x copies the new y, so both registers stick at 1,1. With =,
statement order changes the circuit; with <=, it does not. That is why clocked always blocks use
<=, and = belongs in combinational always @* blocks.
7. Registers in a row delay by one edge each
Two registers written with <= in one clocked block form a two-stage delay: s1 <= in; s2 <= s1;.
At each edge s2 takes the value s1 had before the edge, so a value on in sampled at one edge
reaches s2 after the next edge, one edge later than it reached s1. Fill in the table; the compare
button names the first cycle that differs from the model.
Predict, then compare · delay2
Fill in each cell before comparing. Empty cells count as a mismatch.
| Cycle | ininputs sampled at the edge | s1after the edge | s2after the edge |
|---|---|---|---|
| 0 | 1 | ||
| 1 | 0 | ||
| 2 | 1 | ||
| 3 | 1 | ||
| 4 | 0 | ||
| 5 | 0 |
Had both stages been written with blocking assignments, s1 = in; s2 = s1;, s2 would read the
new s1, and the two stages would print the same value on every line, as if there were one
register. Changing only the second assignment to = would still read the old s1, because the
first assignment's <= update has not happened yet.
Register stages, observation and
waveforms looks at such stages in a waveform, and the Practice problem delay-line of
Recording waveforms asks you to repair exactly this
defect.
8. Common mistakes
8.1 Changing inputs on the sampling edge
// Wrong: changes the input on the edge where the DUT reads it
@(posedge clock) add = 1;
// Right: change it on the falling edge, half a period away
@(negedge clock) add = 1;When the testbench and the DUT both wake up on the rising edge, nothing says which runs first, so
whether the DUT sees the new input or the old one depends on the simulator's ordering. Change inputs
on the falling edge, as Tb.v of the counter trace does with reset.
9. Summary
| Element | Meaning |
|---|---|
always @(posedge clock) | the block runs once per rising edge: a register, and the idiom synthesis tools accept |
q <= expr; | reads now, updates in the nonblocking-update region of the same time step |
| active, nonblocking update, postponed | the order of work inside one time step; two blocks woken by the same edge run in the active region in no defined order |
$display at the edge | shows the value from before the edge |
$strobe, $monitor, #1, falling edge | show the value the edge produced; $monitor keeps printing whenever an argument changes |
= in a clocked block | the next statement reads the new value: a different circuit |
clocked logic uses <=, combinational always @* uses = | the one rule that makes a testbench reading at the edge trustworthy |
10. Check your understanding
-
In the counter trace, why does the
EDGEline at time 25 show 0 while theAFTERline shows 1?Show answer
The
$displayfor theEDGEline runs in the active region, so it reads 0, the value before the counter's<=update. The$strobefor theAFTERline prints in the postponed region, after the update, so it reads 1. -
What would the counter trace show if the testbench never raised
reset?Show answer
countstarts asx, andx + 1is stillx, so every line printsx. A register only gets a known value from a reset or an assignment. -
In
swap_blocking, why does reordering the two statements change the result, while inswap_nonblockingit does not?Show answer
=changes its target immediately, so the second statement reads the first one's result.<=evaluates both right-hand sides from the pre-edge values and updates both targets together, so either order reads the same values.
This chapter has no Practice problem; its checkpoint is running the two examples. Tasks, !==
comparisons, $fatal and the watchdog that make a testbench check itself are in
Testbench techniques.