Load, clear and priority
Read the priority of a clocked block from its statement order, write a register whose clear, load and hold follow the brief, and see the priority in the netlist.
- Lesson
- R2.6
- Time
- 20 min
- Runs on
- Your browser
- Check
- 5 questions
A register had a load and a synchronous clear, and its brief said clear wins. The RTL had two plain if statements, clear first and load second. In the one cycle where both were high, the register loaded instead of clearing, because the last assignment in the block wins.
- Cycle 0
Edge 2: load alone.
qtakes 5a. - Cycle 2
Edge 4: clear and load are both 1. The block schedules 0 for
q, then schedules 33. The later one is the one that lands:qis 33. - Cycle 4
Edge 6: clear alone works,
qis 0. The bug shows only in the cycle where both are high.
Measured on the lab bench. With clear first in an if-else chain, edge 4 gives 00.
The last assignment wins
In a clocked block, each nonblocking assignment schedules a new value for its variable. All of them land after the block has run, in the order they were made. So when two statements assign q in the same edge, the later one is what q holds. Statement order is priority.
That gives two correct ways to write a priority. An if ... else if chain puts the highest priority first, because the first true test wins and the rest are skipped. A sequence of plain if statements puts the highest priority last, because the last assignment wins. The hook mixed them: it wrote clear first, as in a chain, but with plain ifs.
Two plain ifs, clear first
else begin
if (clear) q <= '0;
if (load) q <= d; // last: wins
end
Load wins when both are 1. Edge 4 gives 33.
One chain, clear first
else if (clear) q <= '0;
else if (load) q <= d;
// neither: q holds
Clear wins when both are 1. Edge 4 gives 00.
- Hold
With no input active, the next value is the old one. The synthesizer turns this into the flop's enable.
- Load
Load replaces the held value with
d. - Clear
Clear sits closest to the flop, so it replaces whatever load chose. That is the brief's priority.
- Reset
The asynchronous reset does not wait for an edge. It is the first test in the block and the strongest input of the flop.
Read the diagram as text
- q (hold): the default: keep the value
- load ? d : ...: overrides hold
- clear ? 0 : ...: overrides load
- flop q: 8 flops with enable
- rst_n: asynchronous: overrides everything
- q (hold) → load ? d : ...
- load ? d : ... → clear ? 0 : ...
- clear ? 0 : ... → flop q: next q
- rst_n → flop q: reset pin
Read the chain from the flop outward: the nearer a step, the higher its priority.
Default first, then override
Plain ifs in increasing priority are a common idiom, not a mistake. Write the default first, then let later statements override it for special cases. Counters and state machines use it all the time.
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) q <= '0;
else begin
if (load) q <= d; // lower priority first
if (clear) q <= '0; // highest priority last: it wins
endBoth idioms are correct. A block that uses both at once is the bug, because the reader cannot tell which rule applies. Pick the chain for registers with a few controls; it reads top to bottom in priority order.
| Priority | Input | In a chain | Typical reason |
|---|---|---|---|
| 1 | reset | first test, in the sensitivity list if asynchronous | nothing may stop it |
| 2 | clear | first else if | a known state on command |
| 3 | load | next else if | new data |
| 4 | count enable | next else if | the normal work |
| 5 | hold | no branch at all | the flop keeps its value |
The common order. The brief states it; the code should make it visible in one structure.
What the netlist shows
Synthesize both versions and the cell counts match: 17 cells each, measured on this flow. Each has 8 flops with an asynchronous reset and an enable, and one OR gate making the enable from clear and load.
The difference is the 8 gates in front of the flops. With load winning they are AND gates, d & load: when the flop is enabled, it takes d if load is 1, else 0. With clear winning they are AND-NOT gates, d & ~clear: it takes 0 if clear is 1, else d. Same size, opposite priority, one gate type apart.
| Port | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | clock |
| rst_n | input | 1 | reset, asynchronous, active-low |
| clear | input | 1 | q becomes 0 |
| load | input | 1 | q becomes d |
| d | input | [7:0] | the value load writes |
| q | output | [7:0] | clear: q becomes 0; load: q becomes d; neither: q holds |
- Latency
- clear and load act at the edge; one cycle of latency
- Corner cases
- clear and load in the same cycle; clear alone; load right after a clear. Priority: reset, then clear, then load, then hold
The lab ships the two plain ifs. Run it and find edge 4: q=33. Rewrite the block as one chain with clear first and run again: q=00 at edge 4, held at edge 5, every other line the same. Synthesize both and compare the gate types.
Asked in interviews
Two nonblocking assignments to the same register in one block: which wins?
The last one executed. Both are scheduled in order and the later update lands last. That is why statement order inside a clocked block is priority, and why a default-then-override style works.
How do you code a register with clear, load and enable with a defined priority?
One if-else-if chain in the order of the brief: reset first, then clear, then load, then enable, with no final else so the register holds. Each later branch only runs when every earlier test was false.
Where does the priority show in the netlist?
In the data logic in front of the flops. The control with the last word sits closest to the flop input. Swapping the priority of clear and load here kept 17 cells and changed AND gates into AND-NOT gates.
Try it
Edge 4 is the only cycle with both inputs high, and q reads 33: the load, written last, won. Every other line matches the brief.
Rewrite the block as if (!rst_n) ... else if (clear) ... else if (load) ... and run again: edge 4 reads q=00, edge 5 holds it, and the other seven lines are unchanged.
Synthesize the design to measure its cells and inspect the mapped gates.
e0 clear=0 load=0 d=00 q=00 e1 clear=0 load=0 d=00 q=00 e2 clear=0 load=1 d=5a q=5a e3 clear=0 load=0 d=5a q=5a e4 clear=1 load=1 d=33 q=33 e5 clear=0 load=0 d=33 q=33 e6 clear=1 load=0 d=33 q=00 e7 clear=0 load=1 d=77 q=77 e8 clear=0 load=0 d=77 q=77Original design · 17 cells · 8 flip-flops · 0 latches · 1 logic levels. Synthesis uses Yosys; counts depend on the toolchain.
Read included file: tb.sv
`timescale 1ns/1ps
module tb;
logic clk = 0;
always #5 clk = ~clk; // rising edges at 5, 15, 25, ...
logic rst_n = 1; // falls at 1 ns: a real edge, so an asynchronous reset acts before any clock
int edge_n = -1;
always @(posedge clk) edge_n = edge_n + 1;
logic clear = 0, load = 0;
logic [7:0] d = 0;
logic [7:0] q;
reg8 dut (.clk(clk), .rst_n(rst_n), .clear(clear), .load(load), .d(d), .q(q));
// Advance to the next rising edge, wait for the design to settle, then print one line.
task automatic tick();
@(posedge clk); #1;
$display("e%0d clear=%b load=%b d=%h q=%h", edge_n, clear, load, d, q);
endtask
initial begin
#1 rst_n = 0; tick(); tick(); rst_n = 1; // asserted at 1 ns, edges 0 and 1 in reset, released 1 ns after edge 1
load = 1; d = 8'h5a; tick(); // load alone
load = 0; tick(); // hold
clear = 1; load = 1; d = 8'h33; tick(); // both in the same cycle: the brief says clear wins
clear = 0; load = 0; tick(); // hold
clear = 1; tick(); // clear alone
clear = 0; load = 1; d = 8'h77; tick(); // load alone
load = 0; tick(); // hold
$finish(0); // quiet: no "$finish called" line
end
endmodule
Check yourself
Remember
- In one clocked block the last nonblocking assignment to a variable is the one that lands, so statement order is priority.
- An if-else-if chain puts the highest priority first; plain ifs put it last. Use one style per block.
- Write reset first, then the brief's order, and leave out the final else so the register holds.
- The netlist shows priority in the gates before the flop: here AND with load or AND-NOT with clear, 17 cells either way.