Flip-Flops and Latches#

Source

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.

Exercise 114

Most digital circuits employ some memory. Is it possible to create a useful digital circuit without employing memory? How would it work?

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#

Exercise 115

A Flip-flop and a latch are bistable circuits. What does bistable mean?

Exercise 116

What is the difference between a flip-flop and a latch?

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#

Source

Exercise 117

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#

Exercise 118

What does SR in an SR-latch mean?

Exercise 119

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

Exercise 120

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.

Exercise 121

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:

  1. if we deactivate first S then R?

  2. … first R then S?

  3. if we deactivate S and R at the same time?

D-Latch#

Exercise 122

What does D in D-latch stand for?

Exercise 123

How can we create

  1. a D latch from an SR latch?

  2. a D latch with enable input using the last latch?

Flip-flops#

Source

Note

In this section the author refers to edge-triggered flip-flops.

Exercise 124

How do we have to modify the level-triggered D flip-flop to get a positive edge-triggered flip-flop?

Registers#

Source

Exercise 125

What is a register in context of digital circuits?

Exercise 126

Which register architectures exist?

Exercise 127

Where is a SIPO useful?

Verilog for Latches, Flip-Flops, and Registers#

Source

Exercise 128

What is the difference between a continuous and procedural assignment in respect to how they work?

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 keyword logic is a more descriptive term. logic and reg denote the same type.

Exercise 129

A procedural block typically contains a condition when it should be triggered. How is this block called?

Note

You may have noticed that you may use an always statement without a begin and end pair. The standard differentiates between:

  1. Structured procedures

  2. Block statements

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:

  1. (a) We set and reset at the same time, then (b) toggle S and R in the next cycle. Due to (a) both q and qn will contain the same value and due to (b) the gates will be in inverter mode and the q and qn will toggle.

  2. 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#

Button as clock input#

Solution
module pipo_with_mux_clk_as_button
	#(parameter WIDTH=8)(
	input [WIDTH-1:0] i,
	input clk, sel,
	output [WIDTH-1:0] o
);

logic [WIDTH-1:0] d, q, mux_o;
logic [WIDTH-1:0] mux_i [1:0];

assign
	d = i,
	mux_i[0] = i,
	mux_i[1] = q,
	o = mux_o;

// PIPO
always @(posedge clk)
	q = d;

// Mux
// There are multiple ways to implement a mux
// TODO transfer this to the first mux implementation in projects
// 1) Continuous assignment and array indexing
assign mux_o = mux_i[sel];

// 2) Continuous assignment and ternary if
//assign
//	mux_o = (sel == 0) ? mux_i[0] : mux_i[1];

// 3) Procedural using case
//always @(*) begin
//	unique case (sel)
//		0: mux_o = mux_i[0];
//		1: mux_o = mux_i[1];
//	endcase
//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 {i[0]}]
set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports {i[1]}]
set_property -dict {PACKAGE_PIN U1 IOSTANDARD LVCMOS33} [get_ports {i[2]}]
set_property -dict {PACKAGE_PIN T2 IOSTANDARD LVCMOS33} [get_ports {i[3]}]
set_property -dict {PACKAGE_PIN T1 IOSTANDARD LVCMOS33} [get_ports {i[4]}]
set_property -dict {PACKAGE_PIN R2 IOSTANDARD LVCMOS33} [get_ports {i[5]}]
set_property -dict {PACKAGE_PIN R1 IOSTANDARD LVCMOS33} [get_ports {i[6]}]
set_property -dict {PACKAGE_PIN P2 IOSTANDARD LVCMOS33} [get_ports {i[7]}]

# On-board LEDs
set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {o[0]}]
set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {o[1]}]
set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {o[2]}]
set_property -dict {PACKAGE_PIN F2 IOSTANDARD LVCMOS33} [get_ports {o[3]}]
set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {o[4]}]
set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {o[5]}]
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {o[6]}]
set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports {o[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}];
set_property -dict {PACKAGE_PIN J5 IOSTANDARD LVCMOS33} [get_ports {sel}]

We observe that the input signal will be registered when we press on BTN0. With BTN1 we can check which value was registered.

Note that if we press on BTN0 all the time, then a new input does not get registered. We have to release and press again to register a new value.

I did not observe any unwanted behavior by using a button as a clock signal in this design. Still this is unwanted as Vivado puts it:

WARNING: [DRC PLCK-12] Clock Placer Checks: Poor placement for routing between an IO pin and BUFG. Resolution: Poor placement of an IO pin and a BUFG has resulted in the router using a non-dedicated path between the two. There are several things that could trigger this DRC, each of which can cause unpredictable clock insertion delays that result in poor timing. This DRC could be caused by any of the following: (a) a clock port was placed on a pin that is not a CCIO-pin (b)the BUFG has not been placed in the same half of the device or SLR as the CCIO-pin (c) a single ended clock has been placed on the N-Side of a differential pair CCIO-pin. This is normally an ERROR but the CLOCK_DEDICATED_ROUTE constraint is set to FALSE allowing your design to continue. The use of this override is highly discouraged as it may lead to very poor timing results. It is recommended that this error condition be corrected in the design.

Button as Enable input#

Solution
module pipo_with_mux_enable_as_button
	#(parameter WIDTH=8)(
	input [WIDTH-1:0] i,
	input clk, sel, en,
	output [WIDTH-1:0] o
);

logic [WIDTH-1:0] d, q, mux_o;
logic [WIDTH-1:0] mux_i [1:0];

assign
	d = i,
	mux_i[0] = i,
	mux_i[1] = q,
	o = mux_o;

// PIPO
always @(posedge clk)
	q = (en == 1) ? d : q;

// Mux
assign mux_o = mux_i[sel];

endmodule
module pipo_with_mux_enable_as_button_tb
	#(parameter WIDTH=8);

pipo_with_mux_enable_as_button #(.WIDTH(WIDTH)) dut(i, clk, sel, en, o);

logic [WIDTH-1:0] i, o;
logic clk = 0, sel, en;

initial begin
	$dumpfile("signals.vcd");
	$dumpvars();

	sel = 0;
	en = 0;
	i = 0;
	#10

	// Store
	i = 8'b10101010;
	en = 1; #2  // Assert at least two cycles, because clock's period is #2
	en = 0; #10

	// Change input
	i = ~i; #10

	// Output register's value
	sel = 1; #10
	$finish;
end

always #1 clk = ~clk;

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 {i[0]}]
set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports {i[1]}]
set_property -dict {PACKAGE_PIN U1 IOSTANDARD LVCMOS33} [get_ports {i[2]}]
set_property -dict {PACKAGE_PIN T2 IOSTANDARD LVCMOS33} [get_ports {i[3]}]
set_property -dict {PACKAGE_PIN T1 IOSTANDARD LVCMOS33} [get_ports {i[4]}]
set_property -dict {PACKAGE_PIN R2 IOSTANDARD LVCMOS33} [get_ports {i[5]}]
set_property -dict {PACKAGE_PIN R1 IOSTANDARD LVCMOS33} [get_ports {i[6]}]
set_property -dict {PACKAGE_PIN P2 IOSTANDARD LVCMOS33} [get_ports {i[7]}]

# On-board LEDs
set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {o[0]}]
set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {o[1]}]
set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {o[2]}]
set_property -dict {PACKAGE_PIN F2 IOSTANDARD LVCMOS33} [get_ports {o[3]}]
set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {o[4]}]
set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {o[5]}]
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {o[6]}]
set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports {o[7]}]

# On-board Buttons
set_property -dict {PACKAGE_PIN J2 IOSTANDARD LVCMOS33} [get_ports {en}]
set_property -dict {PACKAGE_PIN J5 IOSTANDARD LVCMOS33} [get_ports {sel}]

We observe the same behavior as in the previous PIPO design where the enable button was used as the clock source

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-1th and 0th 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.