Flip-Flops and Latches#
We already answered some questions about integrating sequential behavior in a digital circuit in chapter Structural Verilog for Combinational Circuits. Also take a look there.
Most digital circuits employ some memory. Is it possible to create a useful digital circuit without employing memory? How would it work?
Solution to Exercise 114
Without any memory, the digital circuit would have to create its output only using its inputs. In other words its output will be based on a combination of its inputs (hence combinational) which will be limited compared sequential circuits which in return can use the information about the past using memory.
Learning Goals#
Understand the cause of metastability
Understand the operation of latches and flip-flops
Be able to describe memory circuits in behavioral Verilog.
Background#
A Flip-flop and a latch are bistable circuits. What does bistable mean?
Solution to Exercise 115
A bistable system can only rest in two states. Flip-flops and latches are also known as bistable circuits because they are the well-known devices to create states using electronics.
What is the difference between a flip-flop and a latch?
Solution to Exercise 116
From flip-flop (electronics) – Wikipedia
Flip-flops can be either level-triggered (asynchronous, transparent or opaque) or edge-triggered (synchronous, or clocked). The term flip-flop has historically referred generically to both level-triggered and edge-triggered circuits that store a single bit of data using gates. Recently, some authors reserve the term flip-flop exclusively for discussing clocked circuits; the simple ones are commonly called transparent latches.12 Using this terminology, a level-sensitive flip-flop is called a transparent latch, whereas an edge-triggered flip-flop is simply called a flip-flop. Using either terminology, the term “flip-flop” refers to a device that stores a single bit of data, but the term “latch” may also refer to a device that stores any number of bits of data using a single trigger. The terms “edge-triggered”, and “level-triggered” may be used to avoid ambiguity.3
In summary, we can use the name flip-flop for latches, but we should also incorporate the adjectives edge-triggered or level-triggered to avoid ambiguity.
Note
The author uses the name latch for a level-triggered flip-flop and flip-flop for an edge-triggered flip-flop.
SR-Latch and D-Latch#
How does a typical latch work?
Solution to Exercise 117
A latch outputs the value at its input even when the input value is not being input anymore. A latch typically employs an enable input to differentiate between the cases
a valid value is currently input
no value is currently input
SR-Latch#
What does SR in an SR-latch mean?
Solution to Exercise 118
S stands for set and sets the value stored in the latch to 1, R stands for reset and sets the value to 0.
How can we implement the SR latch described by the following truth table using two NOR gates?
S |
R |
Q |
---|---|---|
0 |
0 |
Hold |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
Solution to Exercise 119
Probably there are many approaches to this task. An idea is to first create a circuit which sets the output when the input is active and holds the output otherwise. The OR gate works very similar, right?
Warning
The drawing library does not support feedback connections, so the line between the output o
and the input o
could not be drawn.
The resulting truth table:
i |
\(o_{-1}\) |
\(o_0\) |
remark |
---|---|---|---|
0 |
0 |
0 |
hold |
0 |
1 |
1 |
hold |
1 |
0 |
1 |
i sets to 1 |
1 |
1 |
1 |
i sets to 1 |
The \(o_{-1}\) is the previous value of \(o_0\).
The i
pin has the functionality of the S
pin. Now we need the R
functionality which can reset the output to 0. 0 in AND works very similar – if one of the inputs is 0 in AND, then then the output will be 0. Let us create a circuit using AND:
The resulting truth table:
i |
\(o_{-1}\) |
\(o_0\) |
remark |
---|---|---|---|
0 |
0 |
0 |
i sets to 0 |
0 |
1 |
0 |
i sets to 0 |
1 |
0 |
0 |
hold |
1 |
1 |
1 |
hold |
In the specification the output is reset if R
is 1, so we have to additionally negate the input:
But the rest fulfills what we expect from R
.
How do we combine the two circuits? By combining their outputs! At the end both circuits operate on the value of o
. So we could input o
of the AND circuit to the OR circuit and vice-versa.
Let us rename the outputs of OR and AND as o1
and o2
respectively:
Let us check what our circuit does by creating the truth table. This time we do not have to look at previous values, as we analyzed them already in the previous truth tables.
S |
R |
o1 |
o2 |
remark |
---|---|---|---|---|
0 |
0 |
0 |
0 |
S holds o2, R holds o1 |
0 |
0 |
1 |
1 |
S holds o2, R holds o1 |
0 |
1 |
0 |
0 |
S holds o2, R sets to 0 => o1=o2=0 |
1 |
0 |
0 |
0 |
S sets to 1, R holds o1 => o1=o2=1 |
1 |
1 |
1 |
0 |
S sets to 1, R sets to 0 |
Only the last line outputs two different values. In this case we have to choose either o1
or o2
as the output. When we look at the specification we see that the output must be 0
. So we choose o2
as the output.
One last step is missing, we have to use NOR gates. If we place two bubbles at the end of the NOR gate, and push one of the bubbles to the AND, we get two NOR gates. A side-effect is that the o1
will be negated, so we rename it as ON
. In the literature the output is typically named as Q
probably because Q
is more distinctive than O
(and the exercise ask for that), so let us take the names QN
and Q
for the outputs.
Final check:
S |
R |
\(Q_{-1}\) |
\(QN_0\) |
\(Q_0\) |
remark |
---|---|---|---|---|---|
0 |
0 |
0 |
1 |
0 |
hold |
0 |
0 |
1 |
0 |
1 |
hold |
0 |
1 |
0 |
1 |
0 |
R sets to 0 |
0 |
1 |
1 |
0 |
0 |
R sets to 0 |
1 |
0 |
0 |
0 |
1 |
S sets to 1 |
1 |
0 |
1 |
0 |
1 |
S sets to 1 |
1 |
1 |
0 |
0 |
0 |
Q stays at 0 |
1 |
1 |
1 |
0 |
0 |
Q will be 0 |
This is what the specification wanted 🎉.
We implemented an SR latch using NOR gates in Exercise 119. How can we use NAND gates instead of NOR?
Hint: S
and R
are now negated. This means that the output is set if S
is 0 and reset if R
is 0.
Solution to Exercise 120
We can convert the NOR gates to NAND gates by pushing the bubbles at the end of the NOR gates to the inputs. Pushing the bubbles negates the QN
and Q
outputs, so we get Q
and QN
, respectively. Then we push the bubbles further through the OR gates which in turn are converted to AND gates with the S
and R
inputs negated. Their outputs get a bubble again, so we get only NAND
s but with negated S
and R
inputs. So the user must supply the negated values to get the same functionality.
In
the truth table above about combining S and R circuits
we observe that the gates override their outputs o1
and o2
to different values if S
and R
are both set. What happens with the output Q
:
if we deactivate first
S
thenR
?… first
R
thenS
?if we deactivate
S
andR
at the same time?
Solution to Exercise 121
R
will be active, so the output will be set to0
.S
will be active, so the output will be set to1
.One of the outputs will be
1
and the other one0
which are fed to gates. Both gates will be in hold mode, so they will try to keep the value which is input, so the outputQ
will oscillate between0
and1
. This oscillation behavior is also called metastability.
On real hardware we cannot deactivate both the inputs S
and R
at the same time and the propagation delays of the gates won’t be equal either. Deactivating both inputs in a short time interval will most likely cause an oscillation which will be resolved after a while though.
D-Latch#
What does D in D-latch stand for?
Solution to Exercise 122
According to flip-flop types – Wikipedia D stands for data or delay.
How can we create
a D latch from an SR latch?
a D latch with enable input using the last latch?
Solution to Exercise 123
D latch holds the data which is input at
D
. Essentially we want to reduceS
andR
toD
. This can be done by connectingD
toS
and~D
toR
. We need an additional inverter for the latter.If enable input (
E
) is active, thenS
andR
should be forwarded. So we introduce twoAND
gates in front ofS
andR
. TheD
and~D
are connected to theAND
gates:Warning
While building the D latch with enable, do not make the mistake of gating the
D
directly. This will lead toR
being0
, so the value 0 will be stored also in case ifE
is not active. We have to gateS
andR
individually.We can also create a pure
NAND
version by using the same bubble-pushing approach we used in Exercise 120. Let us begin with theNOR
version above. If we push the bubbles ofS
andR
inputs to theAND
gates, we getNAND
s. Additionally we can get rid of the inverter forD
, because aNAND
gate acts like an inverter if one of the inputs is1
which is the case when the enable input is 1. We can then reuse this output as an input to the R enable NAND gate.:Note that the NAND version is vertically mirrored compared to the NOR version. The NAND version uses the active high D in the top inputs.
Note
The enabling of a signal is also called gating. D latch with enable is also called gated D-latch
Flip-flops#
Note
In this section the author refers to edge-triggered flip-flops.
How do we have to modify the level-triggered D flip-flop to get a positive edge-triggered flip-flop?
Solution to Exercise 124
Instead of storing the value when the enable is high (level-trigger) we want to store at the rising edge. We can do this by introducing an input layer consisting of two level-triggered D latches additional to our level-triggered ~SR latch which
settles the next values for
S
andR
deactivates the
S
andR
inputs to the ~SR latch
while the clock signal is low. Only after the clock signal is high (rising edge) \(S_\mathrm{next}\) and \(R_\mathrm{next}\) are propagated to the ~SR latch.
We also introduce a feedback from the ~S
signal to the lower left D latch to block the ~R
signal when after D had a falling edge. This avoids a temporary metastability in the output. The resulting circuit is:
A detailed analysis follows:
There are also other implementations. Refer to D flip-flop – Wikipedia
A simulation of the flip-flop above can be found on edge-triggered D flip-flop – falstad.com. To visualize the advantage of the ~S
signal feedback, remove this net and create a falling edge on D
in when the clock signal is high.
Registers#
What is a register in context of digital circuits?
Solution to Exercise 125
A set of flip-flops that share a common clock.
Which register architectures exist?
Solution to Exercise 126
parallel in parallel out (PIPO)
serial in parallel out (SIPO)
parallel in serial out (PISO)
serial in serial out (SISO)
With the exception of PIPO, every of them are shift registers.
Where is a SIPO useful?
Solution to Exercise 127
When data is sent over a transmission channel, then the data must be typically serialized. When it is received then the serialized data must be organized as bytes and words again. This can be done with a SIPO.
Verilog for Latches, Flip-Flops, and Registers#
What is the difference between a continuous and procedural assignment in respect to how they work?
Solution to Exercise 128
A continuous assignment is a constant assignment to a signal and can also be used to model combinational logic, e.g.:
assign x = y & z;
Also multiple assign statements can be combined. Example 3 from The continuous assignment statement – Systemverilog 2017:
...
assign
data = (s == 0) ? bus0 : Zee,
data = (s == 1) ? bus1 : Zee,
data = (s == 2) ? bus2 : Zee,
data = (s == 3) ? bus3 : Zee;
A procedural assignment occurs in an always
, initial
, final
, task
or function
block. Compared to the continuous assignments, the procedural assignments are triggered by a condition like if
, case
or a loop.
Warning
In literature you may see many signals assigned in procedural blocks declared as reg
which stands for register. This fact does not mean that we can only model flip-flops using procedural statements. Always statements can also model combinational logic.
The section 2-state (two-value) and 4-state (four-value) data types from Systemverilog 2017 recommends using logic
:
The keyword
reg
does not always accurately describe user intent, as it could be perceived to imply a hardware register. The keywordlogic
is a more descriptive term.logic
andreg
denote the same type.
A procedural block typically contains a condition when it should be triggered. How is this block called?
Solution to Exercise 129
The Systemverilog 2017 standard calls the triggering of the procedural blocks as event control. But also uses the term sensitivity list:
From implicit always_comb sensitivities – Systemverilog 2017:
The implicit sensitivity list of an always_comb includes the expansions of the longest static prefix of each variable or …
Note
You may have noticed that you may use an always
statement without a begin
and end
pair. The standard differentiates between:
Structured procedures contain a single statement. Block statements use begin
and end
to group multiple statements. Excerpt from Block statements:
Block statements are a means of grouping statements together so that they act syntactically like a single statement. …
Requirements#
Implement and simulate a NAND basic cell#
Solution
module sr_latch_nand_with_delays(
input sn, rn,
output q, qn
);
// Internal signals to use the outputs as feedback
logic q_int, qn_int;
assign
q = q_int,
qn = qn_int;
assign #1
q_int = sn ~& qn_int, // Set
qn_int = rn ~& q_int; // Reset
endmodule
module sr_latch_nand_with_delays_tb;
logic sn, rn, q, qn;
sr_latch_nand_with_delays dut(sn, rn, q, qn);
initial begin
$dumpfile("signals.vcd");
$dumpvars();
sn = 1; rn = 1;
#10 sn = 0;
#10 sn = 1;
#10 rn = 0;
#10 rn = 1;
#10 sn = 0; rn = 0;
#10 sn = 1; rn = 1;
#10 sn = 0; rn = 0;
#10;
end
endmodule
We have seen the SR-latch in a metastable state. How and when does this happen?
SR latch is driven into a metastable state if we set or reset but do not wait for the two gates (NAND or NOR) to propagate (in other words store) the value being stored. In other words, both two halves of the latch, the q
and qn
must contain the opposite values so that the store operation is complete.
The metastability can be caused in two ways:
(a) We set and reset at the same time, then (b) toggle S and R in the next cycle. Due to (a) both
q
andqn
will contain the same value and due to (b) the gates will be in inverter mode and theq
andqn
will toggle.SR latch has the stored value 0, so
q
=0,qn
=1. We activate the S signal only for as long as the propagation time of one of the gates (NOR or NAND) and then deactivate S.
In case of NAND-based latch q
will become 1 in the next cycle, but qn
will stay at 1 and wait for the next cycle to be overwritten by the propagated new value of q
=1. In the next cycle the both gates will be in inverter mode and this corresponds to the case in (1).
We can achieve the same effect by starting with a latch that has the stored value of 1 and also with a NOR-based latch which we see in the next exercise.
Implement and simulate a NOR basic cell#
Solution
We drove the NAND-based SR latch into a metastable state. We can achieve the same effect also with a NOR-based latch.
module sr_latch_nor_with_delays(
input s, r,
output q, qn
);
// Internal signals to use the outputs as feedback
logic q_int, qn_int;
assign
q = q_int,
qn = qn_int;
assign #1
qn_int = s ~| q_int, // Set
q_int = r ~| qn_int; // Reset
endmodule
module sr_latch_nor_with_delays_tb;
logic s, r, q, qn;
sr_latch_nor_with_delays dut(s, r, q, qn);
initial begin
$dumpfile("signals.vcd");
$dumpvars();
s = 0; r = 0;
#10 s = 1;
#10 s = 0;
#10 r = 1;
#10 r = 0;
#10 s = 1; r = 1;
#10 s = 0; r = 0;
#10 s = 1; r = 1;
#10;
end
endmodule
Design and simulate a D-Latch#
Solution
We have seen the SR latch in a metastable state. Our goal is now to drive the latch into a metastable state. How can we do this?
Our goal is the same as in SR latch, because D latch includes an SR latch. But in contrast to the SR latch we cannot directly access the S and R signals. We have to create the same effect using d
and e
signals by paying attention to the imbalance between D~S and D~R paths.
The testbench contains a detailed description of the steps.
module d_latch_nand_with_delays(
input d, e,
output q, qn
);
// Internal signals to use the outputs as feedback
logic q_int, qn_int;
assign
q = q_int,
qn = qn_int;
// Other intermediate signals
wire d_gated, d_inv, d_inv_gated;
assign #1 // Every gate has the same delay
d_gated = d ~& e, // D enable
d_inv = ~d, // ~D
d_inv_gated = d_inv ~& e, // ~D enable
q_int = d_gated ~& qn_int, // Set_n
qn_int = d_inv_gated ~& q_int; // Reset_n
endmodule
module d_latch_nand_with_delays_tb;
logic d, e, q, qn;
d_latch_nand_with_delays dut(d, e, q, qn);
reg [2:0] test_case;
initial begin
$dumpfile("signals.vcd");
$dumpvars();
// Test the functionality
test_case = 0;
e = 0;
#10 d = 1;
#10 d = 0;
#10;
e = 1;
#10 d = 1;
#10 d = 0;
#10 e = 0;
#10;
// Drive into a metastable state
//
// In the following one cycle corresponds to a single gate delay
//
// Stimulus for the explained cases:
//
// (1) Set & reset at the same time, then deactivate them at the same time
//
// The R path takes more time so we first activate R by setting `d`=0.
// R needs two cycles to arrive to the SR latch, so after one cycle we can
// issue an S activation signal on the shorter S path. One cycle after we
// activated the S signal we deactivate `e` to deactivate S and R signals
// at the same time, because `e` has the same delay to the SR latch
// inputs.
test_case = 1;
e = 1; d = 0; #1;
d = 1; #1;
e = 0;
#10; // Wait long enough to observe the metastability
e = 1; #2 e = 0; // End the metastable state
#10;
// (2a) Set only for a single cycle while the latch stores a `0`:
//
// We set for one cycle, then deactivate `e`.
test_case = 2;
// Store 0 first
// Storing a value takes two cycles after R is at the SR latch
e = 1; d = 0; #3;
e = 0; #1; // Now R is stored
#5; // Wait to differentiate the metastability steps
// Issue S
e = 1; d = 1; #1; // Wait only a single cycle
e = 0; // Issue `e` before 1 is stored
#10; // Wait long enough to observe the metastability
e = 1; #2 e = 0; // End the metastable state
#10;
// (2b) Reset only for a single cycle while the latch stores a `1`:
//
// We reset for one cycle, then deactivate `e`.
test_case = 3;
// Latch already stores `1`
// Issue R
e = 1; d = 0; #2; // Wait two cycles for R to arrive at SR latch
e = 0; // Deactivate `e` before R is stored.
#10; // Wait long enough to observe the metastability
e = 1; #2 e = 0; // End the metastable state
#10;
$finish;
end
endmodule
We observe that if we issue a set or reset operation before the opposite operation is complete, then we drive the latch into a metastable state. The correct way is to assert the S or R signals at least for two cycles so that the value is completely stored, in other words q
and qn
have the right values.
Parallel In Parallel Out (PIPO) Shift Register#
Parallel In Serial Out (PISO) Shift Register#
Solution
In PISO data can be loaded in parallel and data is output serially on q. The exercise asks to visualize the contents of all registers, so the content of the registers is output on an additional port.
// Parallel in serial out circular shift register
module piso_shift_reg_circular
#(parameter SIZE=16, COUNTER_SIZE=26)(
input clk,
input [1:0] en, // Enable for MSB: higher half, LSB: lower half
input [SIZE/2-1:0] pi, // Parallel in
output so, // Serial out
output [SIZE-1:1] other_piso_regs // Rest of the register bits
);
// Slow clock counter
logic [COUNTER_SIZE-1:0] cntr;
logic slow_clk;
assign slow_clk = cntr[$left(cntr)];
always @(posedge clk)
cntr += 1;
// Circular Shift Register (wrap around MSB into LSB) with two enable signals
// If enable is active, then feed data from the inputs, otherwise from the
// less significant bit
logic [SIZE-1:0] d, q;
always @(posedge slow_clk) begin
q[SIZE-1:SIZE/2] <=
(en[1] == 1) ? pi : q[SIZE-2:SIZE/2-1];
q[SIZE/2-1:0] <=
(en[0] == 1) ? pi : {q[SIZE/2-2:0], q[SIZE-1]};
end
// Routing
assign
other_piso_regs = q[$left(q):1],
so = q[0]
;
endmodule
// Set the counter width to a small number to the changes early. Otherwise we
// have to wait 2^26 cycles for a single slow clock cycle.
module tb
#(parameter SIZE=16, COUNTER_SIZE=1)
(input clk);
piso_shift_reg_circular
#(.SIZE(SIZE), .COUNTER_SIZE(COUNTER_SIZE))
dut(clk, en, pi, so, other_piso_regs);
logic so;
logic [1:0] en;
logic [SIZE/2-1:0] pi;
logic [SIZE-1:1] other_piso_regs;
integer cycle = 0;
initial begin
$dumpfile("signals.fst");
$dumpvars();
end
always @(posedge clk) begin
case (cycle)
0:
begin
pi = 8'b10101010;
en = 2'b10;
end
1:
en = 2'b00;
// Wait at least 16 slow clock cycles (32 normal clock cycles to observe a wraparound
34:
$finish;
endcase
cycle += 1;
end
endmodule
# Set Bank 0 voltage
set_property CFGBVS VCCO [current_design]
# Configuration bank voltage select
set_property CONFIG_VOLTAGE 3.3 [current_design]
# These attributes help Vivado to spot for errors
# More info: https://support.xilinx.com/s/article/55660
# Clock 100 MHz
set_property -dict {PACKAGE_PIN F14 IOSTANDARD LVCMOS33} [get_ports {clk}]
# On-board Slide Switches
set_property -dict {PACKAGE_PIN V2 IOSTANDARD LVCMOS33} [get_ports {pi[0]}]
set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports {pi[1]}]
set_property -dict {PACKAGE_PIN U1 IOSTANDARD LVCMOS33} [get_ports {pi[2]}]
set_property -dict {PACKAGE_PIN T2 IOSTANDARD LVCMOS33} [get_ports {pi[3]}]
set_property -dict {PACKAGE_PIN T1 IOSTANDARD LVCMOS33} [get_ports {pi[4]}]
set_property -dict {PACKAGE_PIN R2 IOSTANDARD LVCMOS33} [get_ports {pi[5]}]
set_property -dict {PACKAGE_PIN R1 IOSTANDARD LVCMOS33} [get_ports {pi[6]}]
set_property -dict {PACKAGE_PIN P2 IOSTANDARD LVCMOS33} [get_ports {pi[7]}]
# On-board LEDs
set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {so}]
set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[1]}]
set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[2]}]
set_property -dict {PACKAGE_PIN F2 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[3]}]
set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[4]}]
set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[5]}]
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[6]}]
set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[7]}]
set_property -dict {PACKAGE_PIN E6 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[8]}]
set_property -dict {PACKAGE_PIN C3 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[9]}]
set_property -dict {PACKAGE_PIN B2 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[10]}]
set_property -dict {PACKAGE_PIN A2 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[11]}]
set_property -dict {PACKAGE_PIN B3 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[12]}]
set_property -dict {PACKAGE_PIN A3 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[13]}]
set_property -dict {PACKAGE_PIN B4 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[14]}]
set_property -dict {PACKAGE_PIN A4 IOSTANDARD LVCMOS33} [get_ports {other_piso_regs[15]}]
# On-board Buttons
set_property -dict {PACKAGE_PIN J2 IOSTANDARD LVCMOS33} [get_ports {en[0]}]
set_property -dict {PACKAGE_PIN J5 IOSTANDARD LVCMOS33} [get_ports {en[1]}]
Warning
Using blocking assignments in the shift register implementation can lead to unintended behavior.
The following excerpt which describes the shift register using blocking assignments has a problem. Stop and take some time to analyze what the always block describes:
...
logic [SIZE-1:0] d, q;
always @(posedge slow_clk) begin
q[SIZE-1:SIZE/2] =
(en[1] == 1) ? pi : q[SIZE-2:SIZE/2-1];
q[SIZE/2-1:0] =
(en[0] == 1) ? pi : {q[SIZE/2-2:0], q[SIZE-1]};
end
...
Our intention was to describe a shift register which has SIZE
flip-flops, but this description duplicates one bit (SIZE-1
th and 0
th bit) leading to a shift register with SIZE-1
bits.
The reason for this unintended behavior lies behind the blocking assignment semantics. At every rising edge we execute two assignments. The first one deals with the left half and the second one with the right half of the shift register. We shift the left half first and the blocking assignment changes the left-hand side of the assignment (q[SIZE-1:SIZE/2]
) immediately (compared to the non-blocking assignment). So the second assignment which deals with the right half observes an already shifted first half – the bit q[SIZE-1]
will contain the bit q[SIZE-2]
, which was already shifted by the first assignment. This behavior leads to the same bit in q[SIZE-1]
and q[0]
, which is practically a SIZE-1
wide register.
This behavior induces the perception that we cannot describe a shift register using two separate blocking assignments, or in general more than one blocking assignment. Is this true?
The problem with the blocking assignment is that it throws away the most-significant bit q[SIZE-1]
right after it is executed. We do not have access to it anymore, unless we buffer it in a temporary variable:
...
logic [SIZE-1:0] d, q;
logic tmpbit;
always @(posedge slow_clk) begin
tmpbit = q[SIZE-1];
q[SIZE-1:SIZE/2] =
(en[1] == 1) ? pi : q[SIZE-2:SIZE/2-1];
q[SIZE/2-1:0] =
(en[0] == 1) ? pi : {q[SIZE/2-2:0], tmpbit};
end
...
In summary we see that we can describe the shift register using blocking assignments but this leads to a more expressive description. Non-blocking assignments here are better and this example emphasizes the superpower of non-blocking assignments which assign in parallel even we write the assignments in a traditional sequential text.
We observe after programming nothing, because we have to load some values first. Our clock is slow, so we have to press a button long enough so that enable is registered. After loading the slide switch values, the values start circulating.
In our implementation the circulation for the half stops which is the enable is currently activated for, but the other half continues rotating and the value will vanish as the other half is continuously loaded with slide switch values.
Serial In Parallel Out (SIPO) Shift Register#
Solution
The data is input in to the MSB of the register using a slide switch and in every clock cycle the data is shifted right. We use a button to clock the circuit.
// Serial in parallel out shift register
module sipo_shift_reg
#(parameter SIZE=8)(
input clk,
input si, // Serial in
output logic [SIZE-1:0] q // Parallel out
);
// Shift register rotating right, feeding from MSB
logic [SIZE-1:0] d;
always @(posedge clk) begin
q = {si, q[SIZE-1:1]};
end
endmodule
module sipo_shift_reg_tb
#(parameter SIZE=8)
(input clk);
sipo_shift_reg
#(.SIZE(SIZE))
dut(clk, si, q);
logic si;
logic [SIZE-1:0] q;
integer cycle = 0;
initial begin
$dumpfile("signals.fst");
$dumpvars();
end
always @(posedge clk) begin
case (cycle)
0:
begin
si = 0;
end
5:
si = 1;
7:
si = 0;
10:
$finish;
endcase
cycle += 1;
end
endmodule
# Set Bank 0 voltage
set_property CFGBVS VCCO [current_design]
# Configuration bank voltage select
set_property CONFIG_VOLTAGE 3.3 [current_design]
# These attributes help Vivado to spot for errors
# More info: https://support.xilinx.com/s/article/55660
# On-board Slide Switches
set_property -dict {PACKAGE_PIN V2 IOSTANDARD LVCMOS33} [get_ports {si}]
# On-board LEDs
set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {q[0]}]
set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {q[1]}]
set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {q[2]}]
set_property -dict {PACKAGE_PIN F2 IOSTANDARD LVCMOS33} [get_ports {q[3]}]
set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {q[4]}]
set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {q[5]}]
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {q[6]}]
set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports {q[7]}]
# On-board Buttons
set_property -dict {PACKAGE_PIN J2 IOSTANDARD LVCMOS33} [get_ports {clk}]
## Enforce clock input from a signal which is not dedicated for clocks
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {clk}];
We observe after programming nothing, because we have to load some values first. Our clock is slow, so we have to press a button long enough so that enable is registered. After loading the slide switch values, the values start circulating.
In our implementation the circulation for the half stops which is the enable is currently activated for, but the other half continues rotating and the value will vanish as the other half is continuously loaded with slide switch values.