Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Digital Design & Verification (Verilog, SystemVerilog, VLSI)  Module 2 – Verilog and SystemVerilog for design: the language core

Sequential logic, always_ff and nonblocking

Model registers with always_ff and nonblocking assignments, write synchronous and asynchronous resets and enables, and see what synthesis builds from each.

  • Beginner
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Model flip-flops and registers with always_ff and nonblocking assignments
  • Explain why blocking assignments in a clocked block change the hardware
  • Write synchronous and asynchronous resets and clock enables correctly
  • Read a waveform of registered logic, edge by edge
  • Predict which flip-flop cells synthesis builds for each register style

Before you start

What you’ll learn · Before you start

What you will learn

  • Model flip-flops and registers with always_ff and nonblocking assignments
  • Explain why blocking assignments in a clocked block change the hardware
  • Write synchronous and asynchronous resets and clock enables correctly
  • Read a waveform of registered logic, edge by edge
  • Predict which flip-flop cells synthesis builds for each register style

Before you start

On this page

Four lines in a clocked block, s1 <= d; s2 <= s1; s3 <= s2; q <= s3;, build four flip-flops. Change <= to = and the same four lines build one, which Yosys counts for you below. A clocked block describes registers, and the kind of assignment decides how many there are and when each one changes.

You will model registers with always_ff and nonblocking assignments, see what blocking assignments do to them, write synchronous and asynchronous resets and enables, read a waveform edge by edge, and predict the flip-flop cells that synthesis picks. The simulations come from Icarus Verilog 13.0 and Verilator 5.052, and the cell counts from Yosys 0.69.

Nonblocking assignments make a clocked block a chain of flip-flops

always_ff @(posedge clk) runs its body at each rising edge of clk, and each variable it writes becomes a flip-flop: A storage element that takes the value of its input at a clock edge and holds it until the next edge. A register is a row of flip-flops clocked together.: it takes a new value at the edge and holds it until the next one. Real flip-flops clocked by one edge all capture their inputs as they were just before it, and a nonblocking assignment: An assignment written with <= that reads its value at once but applies it only after every process woken at the same time has run, so all of them read the old values. More in “Thinking in hardware: concurrency and 4-state logic”, <=, gives the simulation the same rule: every right side is read first, and the updates land together afterwards. shift4.sv uses it for a shift register: A chain of flip-flops clocked together, each loading the previous one's value at every edge, so that data moves one stage per clock cycle. of four stages:

shift4.sv: four stages, written with <= SV · hdl/shift4.sv
// A 4-stage shift register: each clock edge moves every bit one stage on.
`default_nettype none

module shift4 (
  input  logic clk,
  input  logic d,
  output logic q
);
  logic s1, s2, s3;

  always_ff @(posedge clk) begin
    s1 <= d;
    s2 <= s1;
    s3 <= s2;
    q  <= s3;
  end
endmodule

`default_nettype wire

The order of reading and writing is the whole idea, so watch it on one edge. The program below models the four stages as a list holding a 1 in s1, applies one edge with d = 0, first the blocking way and then the nonblocking way, and lets you step through every line:

Interactive · line by line One clock edge of a 4-stage shift register, written both ways Step through the program. Watch b lose its 1 line by line, then watch nb take every value from old.

Program · edge/one_edge.py

# One rising edge of a 4-stage
# shift register, with d = 0.
stages = [1, 0, 0, 0]  # s1 s2 s3 q
d = 0

# Blocking: each line reads what
# the line above has just written.
b = stages.copy()
b[0] = d
b[1] = b[0]
b[2] = b[1]
b[3] = b[2]
print("blocking:   ", b)

# Nonblocking: read every old
# value, then write them together.
old = stages.copy()
nb = [d, old[0], old[1], old[2]]
print("nonblocking:", nb)

Variables

After line 19: stages is [1, 0, 0, 0]; d is 0; b is [0, 0, 0, 0]; old is [1, 0, 0, 0]; nb is [0, 1, 0, 0].stages10010203d0b00010203old10010203nb00110203
  • changed in this step

Printed so far

blocking:    [0, 0, 0, 0]
nonblocking: [0, 1, 0, 0]

Every step was recorded from a run with Python 3.14.8 on macOS 26 arm64.

The drawing shows the variables when the program ends, and the program's 12 steps are listed below.

Line run 19
Next line end
Every step as text
  1. Start. No line has run yet, so there are no variables. Python starts with line 3.
  2. Line 3. stages = [1, 0, 0, 0] makes stages a list of 4 items.
  3. Line 4. d = 0 sets d to 0.
  4. Line 8. b = stages.copy() sets b to a new list with the same items as stages, [1, 0, 0, 0].
  5. Line 9. b[0] = d changes b: it is now [0, 0, 0, 0].
  6. Line 10. b[1] = b[0] leaves b as it was. The bug b[1] reads b[0], which the line above has just set to 0. The 1 that s1 held before the edge is already gone, and so is the shift.
  7. Line 11. b[2] = b[1] leaves b as it was.
  8. Line 12. b[3] = b[2] leaves b as it was.
  9. Line 13. print("blocking: ", b) prints blocking: [0, 0, 0, 0].
  10. Line 17. old = stages.copy() sets old to a new list with the same items as stages, [1, 0, 0, 0].
  11. Line 18. nb = [d, old[0], old[1], old[2]] makes nb a list of 4 items. Key point Every value on the right comes from old, the stages as they were before the edge, so the 1 moves from s1 to s2, as a flip-flop chain does.
  12. Line 19. print("nonblocking:", nb) prints nonblocking: [0, 1, 0, 0]. That was the last line, so the program ends. Blocking wiped the register to [0, 0, 0, 0]; nonblocking gave [0, 1, 0, 0], the 1 one stage on, which is what the four flip-flops of shift4.sv do at an edge.

The nonblocking version moved the 1 from s1 to s2, one stage per edge. tb_shift4.sv feeds a single 1 into shift4.sv and the blocking variant beside it, then prints both outputs at every falling edge, half a period after each rising edge:

A single 1 through both shift registers

hdl/shift4_blocking.sv

// The same four lines with blocking assignments: each stage copies the value the line above has just written.
`default_nettype none

module shift4_blocking (
  input  logic clk,
  input  logic d,
  output logic q
);
  logic s1, s2, s3;

  always_ff @(posedge clk) begin
    s1 = d;
    s2 = s1;
    s3 = s2;
    q  = s3;
  end
endmodule

`default_nettype wire

hdl/tb_shift4.sv

// Feeds a single 1 into both shift registers, then 0s, and prints both outputs at every falling edge.
module tb_shift4;
  logic clk, d, q, q_blocking;

  shift4          u_nb (.clk(clk), .d(d), .q(q));
  shift4_blocking u_b  (.clk(clk), .d(d), .q(q_blocking));

  initial clk = 1'b0;
  always #5 clk = ~clk;

  initial begin
    d = 1'b1;
    #10 d = 1'b0;
    repeat (5) @(negedge clk) $display("t = %0d: q = %b, q_blocking = %b", $time, q, q_blocking);
    $finish;
  end
endmodule

Commands · hdl/shift_session.py

from session import run

# Both shift registers on both simulators. Verilator's -Wall reports the blocking assignments; -Wno-fatal lets the
# build go on.
run("""
iverilog -g2012 -Wall -o sim.vvp tb_shift4.sv shift4.sv shift4_blocking.sv
vvp -n sim.vvp
verilator --binary -j 0 -Wall --quiet -Wno-fatal tb_shift4.sv shift4.sv shift4_blocking.sv
obj_dir/Vtb_shift4 +verilator+quiet
""")

Output

# Icarus Verilog 13.0 (stable) · Verilator 5.052
$ iverilog -g2012 -Wall -o sim.vvp tb_shift4.sv shift4.sv shift4_blocking.sv
$ vvp -n sim.vvp
t = 10: q = x, q_blocking = 1
t = 20: q = x, q_blocking = 0
t = 30: q = x, q_blocking = 0
t = 40: q = 1, q_blocking = 0
t = 50: q = 0, q_blocking = 0
tb_shift4.sv:15: $finish called at 50 (1s)
$ verilator --binary -j 0 -Wall --quiet -Wno-fatal tb_shift4.sv shift4.sv shift4_blocking.sv
%Warning-BLKSEQ: shift4_blocking.sv:12:8: Blocking assignment '=' in sequential logic process
                                        : ... note: In instance 'tb_shift4.u_b'
                                        : ... Suggest using delayed assignment '<='
   12 |     s1 = d;
      |        ^
                 ... For warning description see https://verilator.org/warn/BLKSEQ?v=5.052
                 ... Use "/* verilator lint_off BLKSEQ */" and lint_on around source to disable this message.
%Warning-BLKSEQ: shift4_blocking.sv:13:8: Blocking assignment '=' in sequential logic process
                                        : ... note: In instance 'tb_shift4.u_b'
                                        : ... Suggest using delayed assignment '<='
   13 |     s2 = s1;
      |        ^
%Warning-BLKSEQ: shift4_blocking.sv:14:8: Blocking assignment '=' in sequential logic process
                                        : ... note: In instance 'tb_shift4.u_b'
                                        : ... Suggest using delayed assignment '<='
   14 |     s3 = s2;
      |        ^
%Warning-BLKSEQ: shift4_blocking.sv:15:8: Blocking assignment '=' in sequential logic process
                                        : ... note: In instance 'tb_shift4.u_b'
                                        : ... Suggest using delayed assignment '<='
   15 |     q  = s3;
      |        ^
$ obj_dir/Vtb_shift4 +verilator+quiet
t = 10: q = 0, q_blocking = 1
t = 20: q = 0, q_blocking = 0
t = 30: q = 0, q_blocking = 0
t = 40: q = 1, q_blocking = 0
t = 50: q = 0, q_blocking = 0
- tb_shift4.sv:15: Verilog $finish

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shift_session.py

Read the q column as a waveform. The 1 enters s1 at the edge at 5 and reaches q at the edge at 35, the fourth edge, so q first shows it at t = 40 and drops back to 0 at t = 50. Before that, Icarus Verilog prints x: the stages have no reset, so q holds nothing known until data arrives. Verilator, mostly a two-state simulator, starts them at 0 instead.

Checkpoint

Check: Flip-flops from always_ff and <=

  1. Question 1 of 2 In tb_shift4.sv the 1 enters s1 at the rising edge at time 5, and the edges follow at 15, 25 and 35. At which edge time does it reach q of the nonblocking shift4?

    Type a number.

    A hint

    Each edge moves every bit one stage on, and q is the fourth stage.

    Show the answer: Question 1 of 2

    Answer: 35

    The edge at 5 puts the 1 into s1, 15 moves it to s2, 25 to s3 and 35 to q. The testbench, reading at the falling edges, printed q = 1 first at t = 40, just after the edge at 35, and q = 0 again at 50.

  2. Question 2 of 2 In the nonblocking shift register, which value does s2 <= s1 read at a rising edge?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: s1 as it was just before the edge

    Nonblocking assignments read every right side before any left side changes, which is what flip-flops clocked by one edge do: each captures its input as it was just before the edge.

Blocking assignments in a clocked block collapse the chain

The q_blocking column shows the 1 at t = 10, after a single edge. With =, s1 = d changes s1 at once, s2 = s1 reads the s1 just written, and so on down the chain, exactly as the traced program’s first half did: every stage holds d after one edge, so only q matters, and q is one flip-flop. Verilator saw it coming, with a BLKSEQ warning for each line, “Blocking assignment ‘=’ in sequential logic process”. Synthesis agrees with the simulation:

The flip-flops Yosys builds for each shift register Python · hdl/stat_session.py
from session import run

# What synthesis builds from each shift register: Yosys writes its cell statistics to a file, and grep shows the
# flip-flops.
run("""
yosys -q -p "read_verilog -sv shift4.sv; synth -top shift4; tee -q -o shift4.txt stat"
yosys -q -p "read_verilog -sv shift4_blocking.sv; synth -top shift4_blocking; tee -q -o shift4_blocking.txt stat"
grep DFF shift4.txt shift4_blocking.txt
""")

Output

# Yosys 0.69
$ yosys -q -p "read_verilog -sv shift4.sv; synth -top shift4; tee -q -o shift4.txt stat"
$ yosys -q -p "read_verilog -sv shift4_blocking.sv; synth -top shift4_blocking; tee -q -o shift4_blocking.txt stat"
$ grep DFF shift4.txt shift4_blocking.txt
shift4.txt:        4   $_DFF_P_
shift4_blocking.txt:        1   $_DFF_P_

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 stat_session.py

Four $_DFF_P_ cells, flip-flops on the rising edge, for shift4.sv, and one for shift4_blocking.sv. A design that needed four cycles of delay, to line data up with a slower path, would get one, and everything downstream would be three cycles early. The bug is easy to make because the code looks the same, and the rule that avoids it is short.

Key idea

In always_ff, write every register with <=: each one then reads its inputs from before the edge, and the hardware is the same whatever the order of the lines.

Checkpoint

Check: Blocking assignments in a clocked block

  1. Question 1 of 2 Why did q_blocking show the 1 after a single clock edge, where q needed four?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Each blocking line copied the value the line above had just written, so d went through all four lines in one edge

    With =, a later line reads an earlier line's new value at once, so the chain collapses into one stage. With <=, every stage reads its old input, and the chain behaves as four flip-flops.

  2. Question 2 of 2 How many flip-flops did Yosys build for shift4_blocking.sv?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 1 flip-flops

    All four blocking assignments end with d, so s1, s2 and s3 are no more than names on the way: Yosys counted one $_DFF_P_ for shift4_blocking and four for shift4.

A reset either waits for the clock or acts at once

A reset brings registers to a known value. A synchronous reset: A reset that a flip-flop samples at the clock edge like any other input, so it takes effect only at an edge. is one more input of the flip-flop, sampled at the clock edge: count_sync.sv tests !rst_n inside always_ff @(posedge clk). An asynchronous reset: A reset that acts as soon as it is asserted, without waiting for a clock edge. In RTL its edge is named in the event control, as in @(posedge clk or negedge rst_n). acts as soon as it is asserted: count_async.sv names the reset’s falling edge in its event control, always_ff @(posedge clk or negedge rst_n), and tests it first, so that the reset’s edge clears the register instead of counting. tb_reset.sv runs both counters and pulls the reset low between two clock edges; $monitor prints a line whenever a value changes, a waveform as text:

A synchronous and an asynchronous reset, side by side

hdl/count_sync.sv

// A 4-bit counter with a synchronous, active-low reset: the reset is one more input sampled at the clock edge.
`default_nettype none

module count_sync (
  input  logic       clk,
  input  logic       rst_n,
  output logic [3:0] q
);
  always_ff @(posedge clk) begin
    if (!rst_n) q <= '0;
    else        q <= q + 4'd1;
  end
endmodule

`default_nettype wire

hdl/count_async.sv

// The same counter with an asynchronous, active-low reset: the falling edge of rst_n wakes the block too.
`default_nettype none

module count_async (
  input  logic       clk,
  input  logic       rst_n,
  output logic [3:0] q
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) q <= '0;
    else        q <= q + 4'd1;
  end
endmodule

`default_nettype wire

hdl/tb_reset.sv

// Both counters count from reset; then rst_n falls at 38, between two rising edges, and rises again at 48.
module tb_reset;
  logic       clk, rst_n;
  logic [3:0] q_sync, q_async;

  count_sync  u_sync  (.clk(clk), .rst_n(rst_n), .q(q_sync));
  count_async u_async (.clk(clk), .rst_n(rst_n), .q(q_async));

  initial clk = 1'b0;
  always #5 clk = ~clk;

  initial begin
    rst_n = 1'b0;
    #12 rst_n = 1'b1;
    #26 rst_n = 1'b0;
    #10 rst_n = 1'b1;
    #10 $finish;
  end

  initial $monitor("t = %2d: clk %b rst_n %b | q_sync %2d  q_async %2d", $time, clk, rst_n, q_sync, q_async);
endmodule

Commands · hdl/reset_session.py

from session import run

# $monitor prints a line whenever one of its values changes: a waveform as text.
run("""
iverilog -g2012 -Wall -o sim.vvp tb_reset.sv count_sync.sv count_async.sv
vvp -n sim.vvp
""")

Output

# Icarus Verilog 13.0 (stable)
$ iverilog -g2012 -Wall -o sim.vvp tb_reset.sv count_sync.sv count_async.sv
$ vvp -n sim.vvp
t =  0: clk 0 rst_n 0 | q_sync  x  q_async  0
t =  5: clk 1 rst_n 0 | q_sync  0  q_async  0
t = 10: clk 0 rst_n 0 | q_sync  0  q_async  0
t = 12: clk 0 rst_n 1 | q_sync  0  q_async  0
t = 15: clk 1 rst_n 1 | q_sync  1  q_async  1
t = 20: clk 0 rst_n 1 | q_sync  1  q_async  1
t = 25: clk 1 rst_n 1 | q_sync  2  q_async  2
t = 30: clk 0 rst_n 1 | q_sync  2  q_async  2
t = 35: clk 1 rst_n 1 | q_sync  3  q_async  3
t = 38: clk 1 rst_n 0 | q_sync  3  q_async  0
t = 40: clk 0 rst_n 0 | q_sync  3  q_async  0
t = 45: clk 1 rst_n 0 | q_sync  0  q_async  0
t = 48: clk 1 rst_n 1 | q_sync  0  q_async  0
t = 50: clk 0 rst_n 1 | q_sync  0  q_async  0
t = 55: clk 1 rst_n 1 | q_sync  1  q_async  1
tb_reset.sv:17: $finish called at 58 (1s)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 reset_session.py

Find the lines at 38 and 45. At 38 the reset falls with no rising clock edge near; q_async drops to 0 on that very line, while q_sync still shows 3 until the edge at 45 samples rst_n and clears it. At the start the same thing happens in miniature: q_async is 0 at time 0, q_sync is x until the first edge at 5. Step through the reset around those lines:

Interactive · message by message Two counters and one reset that falls between clock edges Step through the run of tb_reset.sv and watch when each counter reacts to rst_n.
Two counters and one reset that falls between clock edges, step 7 of 7. Messages so far: Clock and rst_n to count_async: rst_n: 1 -> 0 at 38; Clock and rst_n to count_sync: posedge clk at 45.Clock and rst_nfrom the testbenchcount_syncq_sync = 1 at 55count_asyncq_async = 1 at 55rst_n: 1 -> 0 at 38posedge clk at 45

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. Reset from time 0. rst_n is 0 from the start. The asynchronous counter clears at once, because its block also wakes on the falling edge of rst_n. The synchronous one stays x until the first rising edge, at 5, samples rst_n = 0.
  2. Counting from 15. rst_n rises at 12. The rising edges at 15, 25 and 35 count both counters to 1, 2 and 3, in step.
  3. rst_n falls at 38. The reset falls between the edges at 35 and 45. Only a block that waits for this change can react to it now.
  4. The asynchronous counter clears. count_async wakes on negedge rst_n, its first test, if (!rst_n), is true, and q_async becomes 0 at time 38, with no clock edge.
  5. The synchronous one waits. Nothing wakes count_sync until a rising clock edge, so q_sync still shows 3 for seven more time units.
  6. The edge at 45. The rising edge at 45 samples rst_n = 0, and q_sync becomes 0. The asynchronous counter stays at 0, because its reset is still held.
  7. Release at 48, count at 55. rst_n rises at 48, between edges, which changes nothing by itself. The edge at 55 counts both to 1, together again.

Both styles are in use, and the choice between them, with the timing of a reset’s release, belongs to the reset lesson of the clocks module. Here the point is what each one describes. A start value written in a declaration, such as logic [3:0] q = '0;, is not a reset at all: an FPGA’s bitstream can load it at power-up, but a chip’s flip-flops start in whatever state they settle into, so a design for silicon needs a reset.

Checkpoint

Check: Synchronous and asynchronous resets

  1. Question 1 of 2 rst_n fell at time 38, between the rising edges at 35 and 45. When did each counter become 0?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: q_async at 38, at once; q_sync at the edge at 45

    An asynchronous reset is in the event control, so it acts the moment it falls. A synchronous reset is one more input sampled at the clock edge, so it waits for the next edge.

  2. Question 2 of 2 Why was q_sync x at time 0 on Icarus Verilog, while q_async was already 0?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: No rising edge had sampled the reset yet, and a register holds x until something writes it

    A synchronous reset needs a clock edge to act. Until the first edge, a four-state simulator shows the register as x, which is also what real flip-flops are worth before their reset: unknown.

An enable becomes a multiplexer in front of the flip-flop

A register that should load only on some edges gets a clock enable: An input that decides whether a register loads a new value at a clock edge or keeps its value. Synthesis builds it as a multiplexer in front of the flip-flop, or a flip-flop with an enable pin.: if (en) q <= d;. When en is 0 nothing is written at the edge, and in a clocked block that simply means the flip-flop keeps its value; there is no latch, because a flip-flop holds its value between edges anyway. reg_styles.sv writes four 1-bit registers, one in each style, and Yosys reports which flip-flop cell it built for each:

Four register styles and the cells synthesis picks

hdl/reg_styles.sv

// Four 1-bit registers, one per style: plain, with an enable, with a synchronous reset, with an asynchronous reset.
`default_nettype none

module reg_styles (
  input  logic clk,
  input  logic en,
  input  logic srst_n,  // synchronous reset, active low
  input  logic arst_n,  // asynchronous reset, active low
  input  logic d,
  output logic q_plain,
  output logic q_en,
  output logic q_sync,
  output logic q_async
);
  always_ff @(posedge clk) q_plain <= d;

  always_ff @(posedge clk) begin
    if (en) q_en <= d;
  end

  always_ff @(posedge clk) begin
    if (!srst_n) q_sync <= 1'b0;
    else         q_sync <= d;
  end

  always_ff @(posedge clk or negedge arst_n) begin
    if (!arst_n) q_async <= 1'b0;
    else         q_async <= d;
  end
endmodule

`default_nettype wire

Commands · hdl/styles_session.py

from session import run

# Lint, then the flip-flop cells Yosys picks for each style.
run("""
verilator --lint-only -Wall --quiet reg_styles.sv
yosys -q -p "read_verilog -sv reg_styles.sv; synth -top reg_styles; tee -q -o cells.txt stat"
grep DFF cells.txt
""")

Output

# Verilator 5.052 · Yosys 0.69
$ verilator --lint-only -Wall --quiet reg_styles.sv
$ yosys -q -p "read_verilog -sv reg_styles.sv; synth -top reg_styles; tee -q -o cells.txt stat"
$ grep DFF cells.txt
        1   $_DFFE_PP_
        1   $_DFF_PN0_
        1   $_DFF_P_
        1   $_SDFF_PN0_

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 styles_session.py

The names follow the Yosys cell library, one letter or digit per property:

Register Cell What the name says
q_plain $_DFF_P_ a flip-flop on the rising (P) clock edge
q_en $_DFFE_PP_ with an enable (E), active when it is 1 (P)
q_sync $_SDFF_PN0_ with a synchronous reset (S), active low (N), to 0
q_async $_DFF_PN0_ with a reset that does not wait for the clock, active low (N), to 0

Yosys used a flip-flop with an enable pin for q_en; where a target has no such cell, the same code becomes a multiplexer that feeds the flip-flop its own output when en is 0. Either way the clock keeps running. Turning the clock itself off, by gating it with logic, saves power but puts logic on the clock path, which brings glitches and timing problems; the clocking lesson of this track covers when and how chips do it safely.

Checkpoint

Check: Enables and the flip-flops synthesis builds

  1. Question 1 of 2 Which cell did Yosys build for q_en, the register written only when en is 1?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: $_DFFE_PP_, a flip-flop with an enable

    An enable means "load at this edge, or keep the value". Synthesis can build that as a multiplexer feeding the flip-flop its own output, or, as Yosys did, as a flip-flop cell with an enable pin.

  2. Question 2 of 2 What does the cell name $_SDFF_PN0_ say?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A flip-flop on the rising clock edge with a synchronous reset that is active at 0 and resets to 0

    The Yosys cell names spell out each flip-flop: P or N for the clock edge, E for an enable, S for a synchronous reset, then the reset's polarity and its value. q_sync became $_SDFF_PN0_ and q_async $_DFF_PN0_.

Practise: build a counter in two steps

The lab models a counter’s register in Python, one rising edge per call, in the order of an always_ff block’s tests. The first step adds a synchronous reset, an enable and counting in both directions; the second adds a parallel load with the priorities reset, then load, then enable:

Code lab · 2 steps

Build an up/down counter

Model a counter with a reset, an enable and wrap-around, then add a parallel load that only the reset can beat.

  1. Model an up/down counter with a reset and an enable To do
  2. Add a parallel load, with the priorities of an always_ff block To do

Exercise · Easy · Python

Model an up/down counter with a reset and an enable

Model the register of a width-bit counter the way an always_ff @(posedge clk) block describes it: nothing happens between clock edges, and edge() is one rising edge.

  • Counter(width) starts with value 0.
  • edge(rst=0, en=0, up=1) applies one rising clock edge. A synchronous reset comes first: with rst 1 the value becomes 0. Otherwise, with en 1 the counter counts up by 1 when up is 1 and down by 1 when up is 0; with en 0 it keeps its value.
  • The counter wraps like a width-bit register: up from the largest value, 2 to the power width minus 1, gives 0, and down from 0 gives the largest value.

The sample tests use a 4-bit counter, whose values run from 0 to 15.

Starter code · counter.py

class Counter:
    """A width-bit up/down counter with a synchronous reset and an enable, one rising clock edge at a time."""

    def __init__(self, width):
        self.width = width
        self.value = 0

    def edge(self, rst=0, en=0, up=1):
        """One rising clock edge: rst clears the counter; otherwise en counts up or down, wrapping."""
The sample tests · test_counter.py
from counter import Counter


def test_counts_up_when_enabled():
    """counts up by one on every edge with en = 1"""
    c = Counter(4)
    for expected in (1, 2, 3):
        c.edge(en=1)
        assert c.value == expected


def test_holds_without_enable():
    """keeps its value on an edge with en = 0"""
    c = Counter(4)
    c.edge(en=1)
    c.edge(en=0)
    c.edge()
    assert c.value == 1


def test_counts_down_and_wraps():
    """counts down with up = 0, and wraps from 0 to 15"""
    c = Counter(4)
    c.edge(en=1, up=0)
    assert c.value == 15
    c.edge(en=1, up=0)
    assert c.value == 14


def test_wraps_up():
    """wraps from 15 to 0 when counting up"""
    c = Counter(4)
    for _ in range(16):
        c.edge(en=1)
    assert c.value == 0
    c.edge(en=1)
    assert c.value == 1


def test_reset_wins():
    """clears on rst = 1, even when en is 1"""
    c = Counter(4)
    for _ in range(5):
        c.edge(en=1)
    c.edge(rst=1, en=1)
    assert c.value == 0


def test_other_widths():
    """wraps at the counter's own width"""
    c = Counter(3)
    c.edge(en=1, up=0)
    assert c.value == 7
    d = Counter(8)
    d.edge(en=1, up=0)
    assert d.value == 255
A hint

Write edge() as the if, else if and else of the always_ff block: if rst, then elif en, then nothing. Count with self.value + 1 or self.value - 1, then keep the low width bits with % (1 << self.width), which also turns −1 into the largest value.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Exercise · Medium · Python

Add a parallel load, with the priorities of an always_ff block

Extend the counter of the first step with a parallel load: edge(rst=0, load=0, d=0, en=0, up=1).

The order of the tests in an always_ff block sets the priorities, and this counter's block reads if (rst) … else if (load) … else if (en) …:

  1. rst 1 clears the counter, whatever the other inputs say.
  2. Otherwise load 1 stores d, cut to the counter's width as a width-bit register would cut it: loading 20 into a 4-bit counter stores 4.
  3. Otherwise en 1 counts up or down, wrapping as before.
  4. Otherwise the value stays.

The sample tests cover each priority: a reset during a load, a load while enabled, and wrap-around in both directions.

Starter code · counter.py

class Counter:
    """A width-bit up/down counter with a synchronous reset, a parallel load and an enable."""

    def __init__(self, width):
        self.width = width
        self.value = 0

    def edge(self, rst=0, load=0, d=0, en=0, up=1):
        """One rising clock edge, with the priorities rst, then load, then en."""
The sample tests · test_counter.py
from counter import Counter


def test_load_stores_d():
    """stores d on an edge with load = 1"""
    c = Counter(4)
    c.edge(load=1, d=9)
    assert c.value == 9


def test_load_cuts_to_width():
    """keeps only the low bits of a value wider than the counter"""
    c = Counter(4)
    c.edge(load=1, d=20)
    assert c.value == 4


def test_reset_beats_load():
    """clears on rst = 1 even during a load"""
    c = Counter(4)
    c.edge(load=1, d=9)
    c.edge(rst=1, load=1, d=7)
    assert c.value == 0


def test_load_beats_enable():
    """loads instead of counting when load and en are both 1"""
    c = Counter(4)
    c.edge(load=1, d=5, en=1)
    assert c.value == 5


def test_counts_after_load():
    """counts on from a loaded value, and wraps in both directions"""
    c = Counter(4)
    c.edge(load=1, d=15)
    c.edge(en=1)
    assert c.value == 0
    c.edge(en=1, up=0)
    assert c.value == 15
    c.edge(en=1, up=0)
    assert c.value == 14


def test_holds_when_idle():
    """keeps its value with every input at 0"""
    c = Counter(4)
    c.edge(load=1, d=3)
    c.edge()
    assert c.value == 3
A hint

Keep the structure of the first step and add one branch, elif load:, between the reset and the enable. Cutting d to the width is the same % (1 << self.width) as for counting.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then try the self-check on flip-flops, blocking assignments, resets and enables. It starts each time with the idea you have the fewest right answers on:

Practice that adapts to you

Practise this lesson

10 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 Which assignment belongs in an always_ff block?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: The nonblocking assignment, <=

    always_ff with <= is the track's rule for every register. Verilator's BLKSEQ warning flags a = in a sequential block.

  2. Question 2 of 10 Why did q print x for the first three lines on Icarus Verilog, but 0 on Verilator?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: The shift register has no reset, so its stages start unknown; Verilator, mostly two-state, starts them at 0

    A register without a reset is unknown until data reaches it. A four-state simulator shows that as x; a two-state one shows a constant, which can hide a missing reset.

  3. Question 3 of 10 A 6-stage version of shift4 is written the same way, with nonblocking assignments. How many rising edges does a bit take to go from d to q?

    Type a number.

    Show the answer: Question 3 of 10

    Answer: 6 edges

    Each nonblocking stage moves the bit one place per edge, so six stages take six edges: the latency of a pipeline equals its number of registers.

  4. Question 4 of 10 What did Verilator's BLKSEQ warning say about shift4_blocking.sv?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: That a blocking assignment is used in a sequential logic process, and that <= is suggested

    Lint catches the rule before synthesis does. With -Wno-fatal the build went on, and the run then showed the collapsed register.

  5. Question 5 of 10 Someone writes the four blocking lines in reverse order: q = s3; s3 = s2; s2 = s1; s1 = d;. What does the block now behave like?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: A correct 4-stage shift register, because each line now reads its stage before the line below overwrites it

    With blocking assignments the result depends on the order of the lines, which works until someone reorders them. Nonblocking assignments give the same hardware in any order, which is why they are the rule.

  6. Question 6 of 10 Which event control describes a register with an asynchronous, active-low reset?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: always_ff @(posedge clk or negedge rst_n)

    Naming the reset's asserting edge in the event control makes the reset asynchronous: the block runs the moment the reset falls, without waiting for the clock.

  7. Question 7 of 10 Why must the reset test come first in always_ff @(posedge clk or negedge rst_n)?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: The block also wakes on the reset's edge, and only a reset test first stops it from acting as if the clock had risen

    The block cannot tell which event woke it except by testing values. With if (!rst_n) first, the reset's edge clears the register, and the clock's edge, with rst_n high, takes the else branch.

  8. Question 8 of 10 logic [3:0] q = 4'd0; gives a register a start value in the code. Where can you rely on it?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: In an FPGA, whose bitstream can load start values into flip-flops; a chip needs a reset instead

    A start value describes power-up, which an FPGA's configuration can provide and a chip's flip-flops cannot. Design for silicon with a reset; on an FPGA a start value can save one.

  9. Question 9 of 10 What does always_ff @(posedge clk) if (en) q <= d; build?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: A flip-flop that keeps its value when en is 0, as a multiplexer in front of it or a flip-flop with an enable pin

    An enable keeps one clock for the whole design and decides, register by register, whether to load. The cell list of reg_styles.sv shows it as its own kind of flip-flop, $_DFFE_PP_.

  10. Question 10 of 10 Why not save power with assign gclk = clk & en; and clock the register with gclk instead of using an enable?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: Logic on the clock path can glitch and makes a second, delayed clock, which timing tools and other registers must deal with

    Keep one clock and use enables in RTL. Clock gating, when power needs it, uses gating cells that the clocking module of this track covers.

Interview questions

Warm-up (fresher): what is the difference between blocking and nonblocking assignments?

A blocking assignment, =, changes its variable at once, so every later statement reads the new value. A nonblocking assignment, <=, reads its right side at once but changes the variable only after every block woken at the same time has run, so they all read the old values. That is what flip-flops clocked by one edge do, so clocked logic uses <=: a chain of registers then behaves as a pipeline, whatever the order of the lines. Combinational logic uses =, so that each line can use the value the line above computed. Mixing them up changes the hardware: four blocking stages of a shift register synthesise to one flip-flop instead of four.

Two harder questions follow, on a reset that must work without a clock and on a pipeline that lost two stages. Plan your answer to each first, then check how your opening compares with the first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    A counter must clear even while its clock is stopped, for example before the clock source has started. Which reset style does that need, how do you write it, and what new problem does releasing the reset bring?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A 3-stage pipeline written in one always_ff block with blocking assignments passes a test that holds its input steady, then fails in the system, where results arrive two cycles early. Explain what was built, why the test missed it, and how you fix it and guard against it.

    Model answer, Pro. First step and follow-ups

Flashcards · 7 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. How many flip-flops do s1 <= d; s2 <= s1; s3 <= s2; q <= s3; build, and with = instead?

    Four with <=, a shift register. With = each line reads the value just written, every stage holds d after one edge, and Yosys built a single flip-flop.

  2. What does Verilator's BLKSEQ warning report?

    A blocking assignment, =, in a block woken by a clock edge: "Blocking assignment '=' in sequential logic process".

  3. How is an asynchronous active-low reset written in always_ff?

    Name the reset's falling edge in the event control, always_ff @(posedge clk or negedge rst_n), and test !rst_n first, so the reset clears the register at once.

  4. When does a synchronous reset clear a register?

    At the next clock edge, because it is one more input sampled at the edge. q_sync kept 3 from the reset at 38 until the edge at 45, while the asynchronous q_async cleared at 38.

  5. Does if (en) q <= d; in always_ff build a latch?

    No. When en is 0 the flip-flop keeps its value, as it does between edges anyway. Yosys built a flip-flop with an enable pin, $_DFFE_PP_; without such a cell it becomes a multiplexer feeding q back.

  6. What does the Yosys cell name $_SDFF_PN0_ say?

    A flip-flop on the rising clock edge (P) with a synchronous reset (S), active low (N), that resets to 0.

  7. What does a register without a reset show before data reaches it?

    x on Icarus Verilog, which keeps four values, and 0 on Verilator, which is mostly two-state.

Key takeaways

  • always_ff with nonblocking assignments describes flip-flops that all read their inputs from before the edge, so the 1 of tb_shift4.sv moved one stage per edge and reached q after four.
  • The same four lines with blocking assignments copy d through every stage in one edge: one flip-flop instead of four, as Yosys counted, with a BLKSEQ warning from Verilator for each line.
  • A synchronous reset waits for the clock, so q_sync kept 3 from 38 to 45, while an asynchronous reset, named in the event control and tested first, cleared q_async at 38.
  • A register without a reset shows x on Icarus Verilog and 0 on Verilator, and a start value in a declaration is not a reset on a chip.
  • An enable keeps one clock and decides whether to load, which Yosys built as $DFFE_PP; its cell names spell out the clock edge, the enable and the reset of every flip-flop.

Terms in this lesson

asynchronous reset
A reset that acts as soon as it is asserted, without waiting for a clock edge. In RTL its edge is named in the event control, as in @(posedge clk or negedge rst_n).
clock enable
An input that decides whether a register loads a new value at a clock edge or keeps its value. Synthesis builds it as a multiplexer in front of the flip-flop, or a flip-flop with an enable pin.
flip-flop
A storage element that takes the value of its input at a clock edge and holds it until the next edge. A register is a row of flip-flops clocked together.
shift register
A chain of flip-flops clocked together, each loading the previous one's value at every edge, so that data moves one stage per clock cycle.
synchronous reset
A reset that a flip-flop samples at the clock edge like any other input, so it takes effect only at an edge.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.