Skip to content

4.6 Designing with registers, adders and muxes

Why this matters

A circuit with memory is easier to design from a picture than from code. The picture shows what is remembered, what is computed in one clock cycle and where the special cases go, and the code then only has to say the same thing in text. Designers sketch these block diagrams constantly.

  • Draw a block diagram from a description, using registers, adders, multiplexers, constants and comparators.
  • Choose a bus width for a register that must count up to a limit.
  • Explain how a synchronous wrap works and why the register holds the limit for a full cycle.
  • Translate a block diagram into SystemVerilog, block by block.
  • Work out the waveform of a small circuit from its diagram.

Intuition

Think of a sequential circuit as a machine with a whiteboard. At every clock tick the machine looks at the number on the whiteboard, computes a new number from it with some arithmetic and a few choices, wipes the board and writes the new number. The whiteboard is the register, the arithmetic is the adders and comparators, the choices are multiplexers, and the clock is the tick.

So a design starts with three questions. What number must be remembered? What is the new number as a function of the old one and the inputs? And what is special, so that the new number is something else: at reset, at a limit, when an input says so? The first question gives the registers, the second gives the logic between a register's output and its input, and each special case becomes a multiplexer that chooses between the usual new number and the special one.

Definition 4.6.1(block diagram).

A block diagram draws a circuit as labeled parts joined by wires. The parts are registers, adders, multiplexers, constants and other blocks, and a wire that carries several bits is a bus, drawn with a slash and its width. A block diagram says which parts exist and how they connect. It leaves out what is inside a part that is not the point of the design.

Definition 4.6.2(black box).

A black box is a block that a diagram uses by what it does, without saying how it does it. A comparator that gives 1 when two numbers are equal is a black box: the design needs the result, and how the comparator is built from gates does not matter for the design.

Constants are drawn as the number written at the end of a wire. A multiplexer is a trapezoid, with the number of each data input written inside it and its select on the slanted side. An operator that has no special symbol, such as a comparator, is a labeled box.

Example 4.6.1(count from 0 up to a limit, then start over).

Build a counter that holds 0 after reset, goes up by 1 on every clock cycle, and after reaching a limit LAST goes back to 0. With LAST equal to 3, the sequence is 0, 1, 2, 3, 0, 1, 2, 3 and so on.

Start with the whiteboard questions. The remembered number is the count, so there is a register q. The usual new number is q plus 1, so there is an adder with the constant 1. The special case is the limit: when q equals LAST, the new number is 0 and not q plus 1. That needs a comparator, a black box that says whether q equals LAST, and a multiplexer that picks 0 when the comparator says yes. Reset is a second special case: when reset is 1, the register should capture 0, so another multiplexer with reset as its select goes between the first multiplexer and the register's input.

Illustration a block diagram that explains an idea; its behavior is not checked

Schematic ch04/count-todq+abs1010010== LASTxat_lastresetclkq444444444

A block diagram with inputs reset and clk and output q (4 bits).

Constant 1 drives n2.

Adder add: adds n2 and q, 4 bits, to give n3.

Constant 0 drives n4.

2-to-1 multiplexer wrap: selects among n3 as input 0 and n4 as input 1 using n6; output n5.

Block "== LAST" cmp: x is q, at_last is n6.

Constant 0 drives n7.

2-to-1 multiplexer rst: selects among n5 as input 0 and n7 as input 1 using reset; output n1.

4-bit register r: d is n1, q is q, clocked by clk.

This diagram is illustrative: its behavior is not checked.

Description: A register q, clocked by clk, feeds an adder with the constant 1 and a comparator box labeled equals LAST. The adder's output goes to input 0 of a first multiplexer whose input 1 is the constant 0 and whose select is the comparator's output at_last. That multiplexer's output goes to input 0 of a second multiplexer whose input 1 is the constant 0 and whose select is reset. The second multiplexer's output goes to the register's input.

Figure 4.6.1. The block diagram of the wrapping counter. The comparator is a black box. The adder and the two multiplexers are in the order they were found: the sum, the wrap choice, the reset choice, the register.

How wide must q be? It must hold every value from 0 to LAST, so a 2-bit register is enough for LAST equal to 3. This diagram uses 4 bits, which is enough for any LAST up to 15 and keeps the same drawing for other limits.

Each block of the diagram is one statement of the module, and the order of the statements follows the diagram. The limit is a parameter, so the same module counts to other limits without being rewritten. Section 4.2 introduced parameters. Here the parameter is a 4-bit number, so that comparing it with q involves two values of the same width.

count_to.sv
// A counter that counts from 0 up to LAST and then starts over at 0.
module count_to #(parameter [3:0] LAST = 4'd3) (input logic clk, reset, output logic [3:0] q);
logic [3:0] sum, next;
logic at_last;
// Block 1: the adder, always one more than the register.
assign sum = q + 4'd1;
// Block 2: the comparator, a black box in the diagram: is the register at its last value?
assign at_last = (q == LAST);
// Block 3: the wrap multiplexer. At the last value the next one is 0, otherwise it is the sum.
assign next = at_last ? 4'd0 : sum;
// Block 4: the register, with a synchronous reset that puts 0 at its input.
always_ff @(posedge clk)
if (reset) q <= 4'd0;
else q <= next;
endmodule

The adder is sum = q + 4'd1. The comparator is the == that sets at_last. The wrap multiplexer is the conditional operator that picks 0 when at_last is 1, and sum otherwise. The reset multiplexer is the if (reset) inside the register's always_ff block, which gives the register 0 when reset is 1 and next otherwise. Section 4.8 explains why the block uses <=.

Its testbench follows the clocked-testbench pattern that Section 4.8 explains in full: a clock made in an initial block, inputs changed with <= at the rising edges, and one @(posedge clk) per cycle. With the default LAST of 3, it resets, then lets the counter run through two trips from 0 to 3 and back. Each // expect: comment shows the value just before the edge that ends its line, which is what the previous edge produced.

count_to_tb.sv
// Testbench for count_to with its default LAST of 3: reset, then two full trips from 0 to LAST and back to 0.
module count_to_tb();
logic clk, reset;
logic [3:0] q;
count_to dut(.*);
parameter CLOCK_PERIOD = 100;
initial begin
clk <= 0;
forever #(CLOCK_PERIOD / 2) clk <= ~clk;
end
initial begin
reset <= 1; @(posedge clk);
reset <= 0; @(posedge clk); // expect: q=0
@(posedge clk); // expect: q=1
@(posedge clk); // expect: q=2
@(posedge clk); // expect: q=3
@(posedge clk); // expect: q=0
@(posedge clk); // expect: q=1
// q holds the value it took two edges after it first showed 3.
$display("DD q_two_after_last=%0d'd%0d", $bits(q), q);
@(posedge clk); // expect: q=2
@(posedge clk); // expect: q=3
@(posedge clk); // expect: q=0
$stop;
end
endmodule
Waveform ch04/count-to-trace: clk, reset, dut.at_last, dut.next, qclkresetdut.at_lastdut.next (dec)123012q (dec)012301050100150200250300350400450500550time (ps)Waveform ch04/count-to-trace: clk, reset, dut.at_last, dut.next, qclkresetdut.at_lastdut.next (dec)123012q (dec)012301050100150200250300350400450500550time (ps)Waveform ch04/count-to-trace: clk, reset, dut.at_last, dut.next, qclkresetdut.at_lastdut.next (dec)123012q (dec)0123010100200300400500time (ps)Waveform ch04/count-to-trace: clk, reset, dut.at_last, dut.next, qclkresetdut.at_lastdut.next (dec)123012q (dec)0123010100200300400500time (ps)Waveform ch04/count-to-trace: clk, reset, dut.at_last, dut.next, qclkresetdut.at_lastdut.next (dec)123012q (dec)0123010100200300400500time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/count-to-trace: time in ps from 0 to 600, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (50, 150, 250, ...).

reset: 1 at 0, then 0 at 50.

dut.at_last: unknown at 0, then 0 at 50, 1 at 350, 0 at 450.

dut.next (4 bits, dec): unknown at 0, then 1 at 50, 2 at 150, 3 at 250, 0 at 350, 1 at 450, 2 at 550.

q (4 bits, dec): unknown at 0, then 0 at 50, 1 at 150, 2 at 250, 3 at 350, 0 at 450, 1 at 550.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, at_last, next and q. q is unknown, drawn hatched, before the first rising edge, and then is 0, 1, 2, 3, 0, 1 at consecutive rising edges. at_last is high only while q is 3. next is one above q, except while q is 3, when it is 0.

Figure 4.6.2. The wrapping counter from reset. at_last is the comparator's output and next is the output of the wrap multiplexer.

Look at Figure 4.6.2 from left to right. Before the first rising edge q is unknown, drawn hatched, and so is at_last. At the first rising edge reset is 1, so the register captures 0. From the second edge on, q is 1, 2 and 3. At the edge where q becomes 3, at_last rises, because the comparator looks at the register's output, and next falls to 0 at the same instant. But q is not 0 yet: it stays 3 for the whole cycle, and at the next rising edge it takes the 0 that next was showing. Then at_last falls, and the count starts again at 0.

In the wrapping counter with LAST equal to 3, q becomes 3 at a rising edge. When does at_last become 1, and when does q next change?

This is what a synchronous wrap means: the decision to wrap is made from the register's present value, during one cycle, and takes effect at the edge that ends it. As a result q holds the limit for a full cycle, and a counter that wraps at LAST goes through LAST plus 1 different values.

Example 4.6.2(a circuit that outputs four fixed numbers in turn).

Build a circuit whose 8-bit output is 21, then 34, then 55, then 89, and then 21 again, one value per clock cycle, forever.

The numbers are fixed, so they can be constants wired into a multiplexer, and the only thing that changes is which one the multiplexer passes. So the remembered number is a position, 0 to 3, and not an 8-bit value. A 2-bit counter is the select of a four-input multiplexer. It needs no comparator and no wrap multiplexer, because a 2-bit register holds only four values, so adding 1 to 3 gives 0 by itself. Reset is the register's own reset input in this drawing, which makes the register capture 0 at an edge where reset is 1.

Schematic ch04/const-cycledqrst+abs12134558900011011clkresetout2222288888

A logic schematic with inputs clk and reset and output out (8 bits).

Constant 1 drives n3.

Adder add: adds n2 and n3, 2 bits, to give n1.

2-bit register idx: d is n1, q is n2, clocked by clk, synchronous reset reset.

Constant 21 drives n4.

Constant 34 drives n5.

Constant 55 drives n6.

Constant 89 drives n7.

4-to-1 multiplexer m: selects among n4 as input 0, n5 as input 1, n6 as input 2 and n7 as input 3 using n2; output out.

It is checked against the module hdl:ch04/const_cycle.

Description: A 2-bit register idx, with a reset input and a clock, feeds an adder with the constant 1, whose output goes back to the register. The register's output is also the select of a four-input multiplexer whose data inputs are the 8-bit constants 21, 34, 55 and 89 for inputs 0 to 3. The multiplexer's output is the 8-bit output out.

Figure 4.6.3. The circuit that steps through four constants. The register idx, 2 bits wide, counts 0, 1, 2, 3 and wraps by itself, and its value is the select of the multiplexer.

The drawing is simulated against the module below, so the diagram and the code cannot differ.

const_cycle.sv
// Steps through four 8-bit constants, one per clock cycle, over and over. A 2-bit counter picks which one the multiplexer passes.
module const_cycle(input logic clk, reset, output logic [7:0] out);
logic [1:0] idx;
// The counter: a 2-bit register that adds 1 each cycle and wraps from 3 to 0 by itself.
always_ff @(posedge clk)
if (reset) idx <= 2'd0;
else idx <= idx + 2'd1;
// The multiplexer: the register's value is the select, and each arm is one data input of the drawing.
// The default arm never acts in hardware. In simulation, before the first reset, it gives what the drawn multiplexer gives for an unknown select: bit 7 known (0 in all four constants), the rest unknown. It is there for the book's check of the drawing, not as a pattern to copy; in your own code write 8'bx.
always_comb
case (idx)
2'd0: out = 8'd21;
2'd1: out = 8'd34;
2'd2: out = 8'd55;
2'd3: out = 8'd89;
default: out = 8'b0xxxxxxx;
endcase
endmodule

The always_ff block is the register with its reset and the adder. The case is the multiplexer, written as a single case as in Section 2.7: idx is the select, and each arm is one data input of the drawing. The default arm can never act in hardware, since a 2-bit idx always holds one of the four values. It acts only in simulation, before the first reset, while idx is unknown. Its odd value, 8'b0xxxxxxx, copies what the simulator's model of the drawn multiplexer gives for an unknown select: a bit on which all four constants agree is known, and here only bit 7 is, because it is 0 in all four. The book's check compares the drawing with this module from the first cycle, so the two have to agree even then. It is there for that check, not as a pattern to copy. In your own code, default: out = 8'bx; (Section 2.7) describes the same hardware.

const_cycle_tb.sv
// Testbench for const_cycle: reset, then six cycles, which is one full round and the start of the next.
module const_cycle_tb();
logic clk, reset;
logic [7:0] out;
const_cycle dut(.*);
parameter CLOCK_PERIOD = 100;
initial begin
clk <= 0;
forever #(CLOCK_PERIOD / 2) clk <= ~clk;
end
initial begin
reset <= 1; @(posedge clk);
reset <= 0; @(posedge clk); // expect: out=21
@(posedge clk); // expect: out=34
@(posedge clk); // expect: out=55
@(posedge clk); // expect: out=89
@(posedge clk); // expect: out=21
// out holds the value it took four edges after it first showed 21.
$display("DD out_four_later=%0d'd%0d", $bits(out), out);
@(posedge clk); // expect: out=34
@(posedge clk); // expect: out=55
$stop;
end
endmodule
Waveform ch04/const-cycle-trace: clk, reset, dut.idx, outclkresetdut.idx (dec)012301out (dec)21345589213450100150200250300350400450500550600time (ps)Waveform ch04/const-cycle-trace: clk, reset, dut.idx, outclkresetdut.idx (dec)012301out (dec)21345589213450100150200250300350400450500550600time (ps)Waveform ch04/const-cycle-trace: clk, reset, dut.idx, outclkresetdut.idx (dec)012301out (dec)21345589213450150250350450550time (ps)Waveform ch04/const-cycle-trace: clk, reset, dut.idx, outclkresetdut.idx (dec)012301out (dec)21345589213450150250350450550time (ps)Waveform ch04/const-cycle-trace: clk, reset, dut.idx, outclkresetdut.idx (dec)012301out (dec)21345589213450150250350450550time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/const-cycle-trace: time in ps from 50 to 650, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 1, 5 rising edges in the window (150, 250, 350, ...).

reset: 0 at 50, no changes.

dut.idx (2 bits, dec): 0 at 50, then 1 at 150, 2 at 250, 3 at 350, 0 at 450, 1 at 550.

out (8 bits, dec): 21 at 50, then 34 at 150, 55 at 250, 89 at 350, 21 at 450, 34 at 550.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, idx and out, starting at the first rising edge, at 50. reset is 0 throughout. idx counts 0, 1, 2, 3, 0, 1 at consecutive rising edges, and out shows 21, 34, 55, 89, 21, 34 in step with idx.

Figure 4.6.4. The four constants in turn, in decimal.

In Figure 4.6.4 out takes each constant at the same rising edge at which idx takes the matching number. The figure starts at the first rising edge, the one that sees reset at 1, so that its numbers fit the column. The testbench lowers reset at that same edge, so reset is drawn low from the start. Before that edge idx is unknown, and so is most of out.

Example 4.6.3(flip a bit when the input is 1).

Here is a circuit with a 1-bit register q and an XOR gate. The register's output goes back to the gate through a reset multiplexer, so that b is 0 while reset is 1 and q otherwise. The gate's other input is din, and its output sout goes to the register. The XOR of din and b is 1 exactly when the two differ, so each rising edge keeps q when din is 0 and flips it when din is 1.

Schematic ch04/xor-accdq001dinresetclksoutq

A logic schematic with inputs din, reset and clk and outputs sout and q.

Constant 0 drives n2.

2-to-1 multiplexer m: selects among q as input 0 and n2 as input 1 using reset; output n1.

XOR gate x1: inputs din and n1; output sout.

Flip-flop r: d is sout, q is q, clocked by clk.

It is checked against the module hdl:ch04/xor_acc.

Description: A 1-bit register q feeds input 0 of a multiplexer whose input 1 is the constant 0 and whose select is reset. The multiplexer's output b and the input din go into an XOR gate. The gate's output, sout, goes to the register's input and is also an output.

Figure 4.6.5. A 1-bit register with XOR feedback. The reset multiplexer is in front of the XOR gate, as in the timer of Section 4.5.
xor_acc.sv
// A 1-bit register with XOR feedback: each cycle q is flipped when din is 1 and kept when din is 0. A mux in front of the XOR gate puts 0 into it while reset is 1.
module xor_acc(input logic clk, reset, din, output logic sout, q);
logic b;
assign b = reset ? 1'b0 : q;
assign sout = din ^ b;
always_ff @(posedge clk)
q <= sout;
endmodule
xor_acc_tb.sv
// Testbench for xor_acc: reset with din = 1 at the first edge, then a run of values on din, and a second reset in the middle.
module xor_acc_tb();
logic clk, reset, din;
logic sout, q;
xor_acc dut(.*);
parameter CLOCK_PERIOD = 100;
initial begin
clk <= 0;
forever #(CLOCK_PERIOD / 2) clk <= ~clk;
end
initial begin
reset <= 1; din <= 1; @(posedge clk);
reset <= 0; din <= 0; @(posedge clk); // expect: q=1
// q holds what the first edge captured: the XOR of din = 1 and the 0 that reset put in.
$display("DD q_after_reset=%0d'd%0d", $bits(q), q);
din <= 1; @(posedge clk); // expect: q=1
din <= 1; @(posedge clk); // expect: q=0
din <= 0; @(posedge clk); // expect: q=1
din <= 1; @(posedge clk); // expect: q=1
din <= 0; @(posedge clk); // expect: q=0
// Reset is 1 at the next edge, with din = 0: q takes din, which is 0.
reset <= 1; din <= 0; @(posedge clk); // expect: q=0
reset <= 0; din <= 1; @(posedge clk); // expect: q=0
din <= 1; @(posedge clk); // expect: q=1
din <= 0; @(posedge clk); // expect: q=0
din <= 1; @(posedge clk); // expect: q=0
din <= 1; @(posedge clk); // expect: q=1
@(posedge clk); // expect: q=0
$stop;
end
endmodule
Waveform ch04/xor-acc-trace: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq050100150200250300350400450500550time (ps)Waveform ch04/xor-acc-trace: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq050100150200250300350400450500550time (ps)Waveform ch04/xor-acc-trace: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq0100200300400500time (ps)Waveform ch04/xor-acc-trace: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq0100200300400500time (ps)Waveform ch04/xor-acc-trace: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq0100200300400500time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/xor-acc-trace: time in ps from 0 to 600, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (50, 150, 250, ...).

reset: 1 at 0, then 0 at 50.

din: 1 at 0, then 0 at 50, 1 at 150, 0 at 350, 1 at 450, 0 at 550.

dut.b: 0 at 0, then 1 at 50, 0 at 250, 1 at 350, 0 at 550.

sout: 1 at 0, then 0 at 150, 1 at 250, 0 at 450.

q: unknown at 0, then 1 at 50, 0 at 250, 1 at 350, 0 at 550.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, din, b, sout and q. q is unknown before the first rising edge. din is 1 at the first rising edge, 0 and 1 and 1 at the next ones. q becomes 1 at the first edge and stays 1 at the second edge, becomes 0 at the third edge, and becomes 1 at the fourth.

Figure 4.6.6. The XOR register from reset, with a few values of din.

Take the edges in Figure 4.6.6 in turn. At the first edge reset is 1 and din is 1, so b is 0 and sout is 1: q becomes 1'd1, the value of din, and not 0. The testbench then sets reset to 0 and din to 0 at that same edge. b follows q and is now 1, so sout is 0 XOR 1, which is 1, and the register at the second edge keeps 1. At the second edge din becomes 1, so sout is 1 XOR 1, which is 0, and at the third edge q becomes 0. So with din high in a cycle q flips at the end of it, and with din low it stays.

How CSE 369 writes this

Block diagrams in CSE 369 mark a bus with a slash across the line (the book also writes its width there), draw a multiplexer as a trapezoid with its data inputs numbered, and draw an operator with no symbol of its own, such as a comparator, as a labeled box. In code, always_ff uses <= and always_comb uses =, never both in one block, and a variable is assigned in one block only.

Common pitfall

  • Writing the code before the diagram. Without the picture it is easy to forget where the reset or the wrap belongs, or to put two assignments to one register in different places.
  • Expecting the counter to wrap as soon as it equals the limit. The comparator sees the limit during one whole cycle, and the register takes the 0 at the end of it, so the limit is held for a full cycle.
  • Choosing a register that is too narrow. Counting from 0 to N takes N plus 1 different values, so when N is a power of two, such as 64, the register needs one more bit than the exponent suggests: 7 bits, not 6.
  • Assuming every circuit is 0 after reset. A synchronous reset loads whatever the design chooses (Section 4.7), and where the reset multiplexer sits decides what the register holds after the reset edge. In front of the adder, the register holds the adder's result for 0. In front of the register, it holds the value on the multiplexer's reset input, which is 0, START or any other value the design needs.
Take the quiz (4 questions)

In the wrapping counter with LAST equal to 3, what does q show right after the second rising edge following the one at which it first showed 3?

In the circuit that steps through four constants, out first shows 21 right after some rising edge. What does it show right after the fourth edge after that one?

In the XOR register, the first rising edge has reset at 1 and din at 1. What does q hold right after it?

Why does a block diagram treat the comparator in the wrapping counter as a black box?

Exercise 4.6.1

How many bits does the register of a counter need, so that it can count from 0 up to N, for each of these N?

  1. N = 5
  2. N = 64
  3. N = 127
  4. N = 128
Hint
The register must hold N plus 1 different values, from 0 to N. How many different values do ww bits hold?
Solution

A register of ww bits holds 2w2^w different values, so it needs the smallest ww with 2w2^w at least N plus 1.

  1. Six values, from 0 to 5. Two bits hold 4 and three bits hold 8, so 3 bits.
  2. 65 values. Six bits hold only 64, one too few, so 7 bits. A power of two as N is where the count goes wrong.
  3. 128 values. Seven bits hold exactly 128, so 7 bits.
  4. 129 values. Seven bits hold 128, so 8 bits.

Tags: Warm-upRegisters and countersNumber representation

Exercise 4.6.2

The figure shows the clock, reset and din of the XOR register for a later stretch of its run. At the start q is 0. In this simulation an input that changes at an edge is drawn changing at that edge, and the simulator counts it as changing just after it, so the register sees the value the input had before the edge (Section 4.7). Work out b, sout and q.

Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700750800850900950100010501100115012001250time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq7009001100time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/xor-acc-second: time in ps from 700 to 1300, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (750, 850, 950, ...).

reset: 1 at 700, then 0 at 750.

din: 0 at 700, then 1 at 750, 0 at 950, 1 at 1050.

Empty lanes for the reader to complete: dut.b, sout, q.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset and din, with lanes for b, sout and q left empty.

Figure 4.6.7. The question: the clock, reset and din.
Hint
b is 0 while reset is 1, and q otherwise. sout is din XOR b, and q takes sout at each rising edge, using the value it had just before the edge.
Solution

Take the rising edges one at a time, using the value each signal had just before the edge.

  • Before 750: reset is 1, din is 0 and q is 0. So b is 0 and sout is 0.
  • Edge at 750: sout was 0, so q stays 0. At this edge reset falls and din rises, so b is q = 0 and sout is 1 XOR 0, which is 1.
  • Edge at 850: sout was 1, so q becomes 1. Now b is 1 and sout is 1 XOR 1, which is 0.
  • Edge at 950: sout was 0, so q becomes 0. din falls at this edge, so b is 0 and sout is 0 XOR 0, which is 0.
  • Edge at 1050: sout was 0, so q stays 0. din rises, so sout is 1 XOR 0, which is 1.
  • Edge at 1150: sout was 1, so q becomes 1. b is 1, and sout is 1 XOR 1, which is 0.
  • Edge at 1250: sout was 0, so q becomes 0. b is 0 and, with din still 1, sout is 1.
Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700750800850900950100010501100115012001250time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq700800900100011001200time (ps)Waveform ch04/xor-acc-second: clk, reset, din, dut.b, sout, qclkresetdindut.bsoutq7009001100time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/xor-acc-second: time in ps from 700 to 1300, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (750, 850, 950, ...).

reset: 1 at 700, then 0 at 750.

din: 0 at 700, then 1 at 750, 0 at 950, 1 at 1050.

dut.b: 0 at 700, then 1 at 850, 0 at 950, 1 at 1150, 0 at 1250.

sout: 0 at 700, then 1 at 750, 0 at 850, 1 at 1050, 0 at 1150, 1 at 1250.

q: 0 at 700, then 1 at 850, 0 at 950, 1 at 1150, 0 at 1250.

Dashed vertical guides mark the rising clock edges.

Description: The full timing diagram of clk, reset, din, b, sout and q for the stretch of the run, with the three empty lanes filled in.

Figure 4.6.8. The answer, from the simulation.

Tags: StandardWaveformsSequential logic

Exercise 4.6.3

Change the wrapping counter so that it counts from 0 to LAST and then stays at LAST until reset. Which block of the diagram changes, and what does the module look like?

Hint
At the limit, the wrap multiplexer passes 0. What should it pass instead, so that the register keeps its value?
Solution

Only the wrap multiplexer's second input changes. At the limit, the register must capture what it already holds, so that input is q itself and not the constant 0. The adder, the comparator and the reset multiplexer stay as they are.

count_hold.sv
// A counter that counts from 0 up to LAST and then stays at LAST until reset.
module count_hold #(parameter [3:0] LAST = 4'd3) (input logic clk, reset, output logic [3:0] q);
logic [3:0] sum, next;
logic at_last;
assign sum = q + 4'd1;
assign at_last = (q == LAST);
// At the last value the multiplexer passes q itself, so the register keeps what it holds.
assign next = at_last ? q : sum;
always_ff @(posedge clk)
if (reset) q <= 4'd0;
else q <= next;
endmodule

The simulation shows q reaching 3 and staying there, with at_last high from then on.

Waveform ch04/count-hold-trace: clk, reset, dut.at_last, qclkresetdut.at_lastq (dec)0123050100150200250300350400450500550time (ps)Waveform ch04/count-hold-trace: clk, reset, dut.at_last, qclkresetdut.at_lastq (dec)0123050100150200250300350400450500550time (ps)Waveform ch04/count-hold-trace: clk, reset, dut.at_last, qclkresetdut.at_lastq (dec)01230100200300400500time (ps)Waveform ch04/count-hold-trace: clk, reset, dut.at_last, qclkresetdut.at_lastq (dec)01230100200300400500time (ps)Waveform ch04/count-hold-trace: clk, reset, dut.at_last, qclkresetdut.at_lastq (dec)01230100200300400500time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/count-hold-trace: time in ps from 0 to 600, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (50, 150, 250, ...).

reset: 1 at 0, then 0 at 50.

dut.at_last: unknown at 0, then 0 at 50, 1 at 350.

q (4 bits, dec): unknown at 0, then 0 at 50, 1 at 150, 2 at 250, 3 at 350.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, at_last and q. q is unknown before the first edge, then is 0, 1, 2 and 3 at consecutive rising edges, and stays 3. at_last rises when q becomes 3 and stays high. reset is 1 until the first rising edge and then stays 0.

Figure 4.6.9. The counter that stops at LAST.

Tags: StandardSequential logicSystemVerilog

Exercise 4.6.4

Design a counter that counts down. After reset it holds START, and each clock cycle it goes down by 1. After 0 it goes back to START. With START equal to 4 the sequence is 4, 3, 2, 1, 0, 4, 3, and so on. Say which blocks you need, then write the module.

Hint
What does a down counter use instead of the adder? What does the comparator compare q with now, and what does the wrap multiplexer pass when the comparison is true?
Solution

The remembered number is still the count, so there is one register. The usual new number is q minus 1, so the adder becomes a subtractor with the constant 1. The special case is 0: when q is 0, the new number is START and not q minus 1, so the comparator checks q against 0, and the wrap multiplexer passes START when it says yes. Reset puts START into the register, so the reset multiplexer's constant is START and not 0.

count_down.sv
// A counter that counts down from START to 0 and then starts over at START.
module count_down #(parameter [3:0] START = 4'd4) (input logic clk, reset, output logic [3:0] q);
logic [3:0] less, next;
logic at_zero;
assign less = q - 4'd1;
assign at_zero = (q == 4'd0);
assign next = at_zero ? START : less;
always_ff @(posedge clk)
if (reset) q <= START;
else q <= next;
endmodule
Waveform ch04/count-down-trace: clk, reset, dut.at_zero, qclkresetdut.at_zeroq (dec)432104050100150200250300350400450500550time (ps)Waveform ch04/count-down-trace: clk, reset, dut.at_zero, qclkresetdut.at_zeroq (dec)432104050100150200250300350400450500550time (ps)Waveform ch04/count-down-trace: clk, reset, dut.at_zero, qclkresetdut.at_zeroq (dec)4321040100200300400500time (ps)Waveform ch04/count-down-trace: clk, reset, dut.at_zero, qclkresetdut.at_zeroq (dec)4321040100200300400500time (ps)Waveform ch04/count-down-trace: clk, reset, dut.at_zero, qclkresetdut.at_zeroq (dec)4321040100200300400500time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/count-down-trace: time in ps from 0 to 600, 12 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 6 rising edges in the window (50, 150, 250, ...).

reset: 1 at 0, then 0 at 50.

dut.at_zero: unknown at 0, then 0 at 50, 1 at 450, 0 at 550.

q (4 bits, dec): unknown at 0, then 4 at 50, 3 at 150, 2 at 250, 1 at 350, 0 at 450, 4 at 550.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, at_zero and q. q is unknown before the first rising edge, then is 4, 3, 2, 1, 0, 4 at consecutive rising edges. at_zero is high only while q is 0.

Figure 4.6.10. The down counter with START equal to 4, from reset.

As in the up counter, q stays at 0 for one whole cycle, and takes START at the edge that ends it.

Tags: ChallengeSequential logicSystemVerilog

Exercise 4.6.5

Design a circuit with an 8-bit output cur that shows 0, 1, 1, 2, 3, 5, 8, 13 and so on after reset, one number per clock cycle, each number being the sum of the two before it. It needs two registers, and each one reads the other.

  1. Say what each register holds and what each loads at a rising edge, including the edge at which reset is 1. Draw the block diagram.
  2. Write the module.
  3. The numbers grow until the next one no longer fits in 8 bits. What does cur show then?
Hint
cur is the older of two neighbors in the sequence, and the other register holds the number after it. At an edge, the older one takes the newer one, and the newer one takes the sum of the two, both computed from the values before the edge. What must the two hold right after reset for the first two numbers to be 0 and 1?
Solution

The registers. cur holds the number being shown and nxt holds the one after it. At a rising edge the pair (a,b)(a, b) becomes (b,a+b)(b, a + b): cur takes nxt, and nxt takes cur plus nxt, each from the values before the edge. At the edge where reset is 1 the registers load the values the design chooses for reset, 0 into cur and 1 into nxt. They are not both 0, as a synchronous reset need not load 0 (Section 4.7).

In the drawing, cur has the register's own reset, which loads 0. The register nxt has a multiplexer in front of it that passes the sum while reset is 0 and the constant 1 while reset is 1.

Schematic ch04/s06-fibdqdqrst+abs101clkresetcur8888888nxtcur

A logic schematic with inputs clk and reset and output cur (8 bits).

Adder add: adds n2 and cur, 8 bits, to give n3.

Constant 1 drives n4.

2-to-1 multiplexer m: selects among n3 as input 0 and n4 as input 1 using reset; output n1.

8-bit register rnxt: d is n1, q is n2, clocked by clk.

8-bit register rcur: d is n2, q is cur, clocked by clk, synchronous reset reset.

It is checked against the module hdl:ch04/fib.

Description: A block diagram. An adder takes the outputs of two 8-bit registers and its sum goes to input 0 of a multiplexer whose input 1 is the constant 1 and whose select is reset. The multiplexer's output goes to the input of the register nxt. The output of nxt goes to the input of the register cur and, along a wire over the top, back to the adder. The output of cur is the output cur of the circuit and also goes, along a longer wire over the top, back to the adder. Both registers are clocked by clk, and cur also has reset.

Figure 4.6.11. Two registers that read each other. The output of nxt goes to cur and to the adder, the output of cur goes to the output and to the adder, and the adder's sum goes through the reset multiplexer to nxt. This drawing is simulated against the module below.

The module. The two registers share one always_ff block, with two statements in each branch of the if, so each branch has begin and end. Both right-hand sides read the old values, which is what <= gives.

fib.sv
// Two registers that read each other. cur shows 0, 1, 1, 2, 3, 5, ...: at every edge cur takes nxt, and nxt takes the sum of the two.
module fib(input logic clk, reset, output logic [7:0] cur);
logic [7:0] nxt;
always_ff @(posedge clk)
if (reset) begin
cur <= 8'd0;
nxt <= 8'd1;
end else begin
cur <= nxt;
nxt <= cur + nxt;
end
endmodule
fib_tb.sv
// Testbench for fib: reset, then 14 more edges, which run past the point where the true Fibonacci number no longer fits in 8 bits.
module fib_tb();
logic clk, reset;
logic [7:0] cur;
fib dut(.*);
parameter CLOCK_PERIOD = 100;
initial begin
clk <= 0;
forever #(CLOCK_PERIOD / 2) clk <= ~clk;
end
initial begin
reset <= 1; @(posedge clk);
reset <= 0; @(posedge clk); // expect: cur=0, nxt=1
@(posedge clk); // expect: cur=1, nxt=1
@(posedge clk); // expect: cur=1, nxt=2
@(posedge clk); // expect: cur=2, nxt=3
@(posedge clk); // expect: cur=3, nxt=5
@(posedge clk); // expect: cur=5, nxt=8
@(posedge clk); // expect: cur=8, nxt=13
@(posedge clk); // expect: cur=13, nxt=21
@(posedge clk); // expect: cur=21, nxt=34
@(posedge clk); // expect: cur=34, nxt=55
@(posedge clk); // expect: cur=55, nxt=89
@(posedge clk); // expect: cur=89, nxt=144
@(posedge clk); // expect: cur=144, nxt=233
@(posedge clk); // expect: cur=233, nxt=121
$display("DD cur_last_true=%0d'd%0d", $bits(cur), cur);
@(posedge clk); // expect: cur=121, nxt=98
$display("DD cur_first_wrong=%0d'd%0d", $bits(cur), cur);
@(posedge clk); // expect: cur=98, nxt=219
$stop;
end
endmodule
Waveform ch04/fib-start: clk, reset, cur, dut.nxtclkresetcur (dec)012358dut.nxt (dec)1235813050100150200250300350400450500550600650time (ps)Waveform ch04/fib-start: clk, reset, cur, dut.nxtclkresetcur (dec)012358dut.nxt (dec)1235813050100150200250300350400450500550600650time (ps)Waveform ch04/fib-start: clk, reset, cur, dut.nxtclkresetcur (dec)012358dut.nxt (dec)12358130100200300400500600time (ps)Waveform ch04/fib-start: clk, reset, cur, dut.nxtclkresetcur (dec)012358dut.nxt (dec)12358130100200300400500600time (ps)Waveform ch04/fib-start: clk, reset, cur, dut.nxtclkresetcur (dec)012358dut.nxt (dec)12358130100200300400500600time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/fib-start: time in ps from 0 to 700, 14 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 7 rising edges in the window (50, 150, 250, ...).

reset: 1 at 0, then 0 at 50.

cur (8 bits, dec): unknown at 0, then 0 at 50, 1 at 150, 2 at 350, 3 at 450, 5 at 550, 8 at 650.

dut.nxt (8 bits, dec): unknown at 0, then 1 at 50, 2 at 250, 3 at 350, 5 at 450, 8 at 550, 13 at 650.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, cur and dut.nxt. reset is high until the first rising edge and cur and nxt are unknown before it. After that edge cur is 0 and nxt is 1. At each of the next rising edges the pair becomes 1 and 1, then 1 and 2, then 2 and 3, then 3 and 5, then 5 and 8, then 8 and 13.

Figure 4.6.12. The start of the run, in decimal. nxt is the register inside the module.

Read Figure 4.6.12 edge by edge. At the first edge, at 50, reset is 1, so cur becomes 0 and nxt becomes 1. At the edge at 150, cur takes the 1 that nxt held and nxt takes 0 plus 1, which is 1. At 250 cur takes 1 again and nxt takes 1 plus 1, which is 2. The shown numbers are 0, 1, 1, 2, 3, 5, 8, as required.

When the sum does not fit. The true next number after 144 and 233 is 377, which needs 9 bits. nxt has 8 bits and keeps only the low 8, which is 377−256=121377 - 256 = 121.

Waveform ch04/fib-wrap: clk, reset, cur, dut.nxtclkresetcur (dec)558914423312198dut.nxt (dec)891442331219821911001150120012501300135014001450150015501600time (ps)Waveform ch04/fib-wrap: clk, reset, cur, dut.nxtclkresetcur (dec)558914423312198dut.nxt (dec)8914423312198219110012001300140015001600time (ps)Waveform ch04/fib-wrap: clk, reset, cur, dut.nxtclkresetcur (dec)558914423312198dut.nxt (dec)8914423312198219110012001300140015001600time (ps)Waveform ch04/fib-wrap: clk, reset, cur, dut.nxtclkresetcur (dec)558914423312198dut.nxt (dec)8914423312198219110012001300140015001600time (ps)Waveform ch04/fib-wrap: clk, reset, cur, dut.nxtclkresetcur (dec)558914423312198dut.nxt (dec)8914423312198219110013001500time (ps)

Time in ps; one column is 50 ps.

Timing diagram from simulation, ch04/fib-wrap: time in ps from 1100 to 1650, 11 columns of 50 ps.

clk: clock, period 100 ps, starts 0, 5 rising edges in the window (1150, 1250, 1350, ...).

reset: 0 at 1100, no changes.

cur (8 bits, dec): 55 at 1100, then 89 at 1150, 144 at 1250, 233 at 1350, 121 at 1450, 98 at 1550.

dut.nxt (8 bits, dec): 89 at 1100, then 144 at 1150, 233 at 1250, 121 at 1350, 98 at 1450, 219 at 1550.

Dashed vertical guides mark the rising clock edges.

Description: A timing diagram of clk, reset, cur and dut.nxt. At the first rising edge shown cur is 89 and nxt is 144. At the next edge they are 144 and 233, then 233 and 121, then 121 and 98, then 98 and 219.

Figure 4.6.13. Later in the run, in decimal. The sum of 144 and 233 does not fit in 8 bits.

At the edge at 1350, nxt takes 121 where the true sum is 377, and that 121 reaches cur at the edge at 1450. So the last correct number that cur shows is 8'd233, and the first wrong one is 8'd121, where the true Fibonacci number is 377. From then on the circuit keeps adding modulo 256. Counting to a limit needs a register wide enough for the limit, as in Exercise 4.6.1, and so does this sequence.

Tags: ChallengeRegisters and countersSequential logicSystemVerilog