A universal shift register is one W-bit register that can hold, shift right, shift left or load a whole word, chosen each clock by a 2-bit mode.
mode[1:0] | Operation | Next value of the register |
|---|---|---|
| Hold |
|
| Shift right |
|
| Shift left |
|
| Parallel load |
|
Each flip-flop takes its D input from a 4:1 multiplexer. The choices are its own output, each neighbour and the parallel input.
Read the slice left to right: four candidates enter the mux, mode picks one, and the clock edge copies it into the flop. The whole register is W copies of this slice.
Shift right (01) takes the bit from the flop above, q[i+1]. The top flop has no neighbour above, so it takes sin_r.
Shift left (10) takes the bit from the flop below, q[i-1]. Flop 0 takes sin_l instead.
Hold (00) feeds the flop its own output, so nothing changes at the edge.
The same block serves as a delay line, serializer, deserializer and bidirectional shifter.
In RTL the four mux inputs are the four branches of one case, inside the clocked block.
// snippet
unique case (mode)
2'b00: q <= q; // hold
2'b01: q <= {sin_r, q[W-1:1]}; // shift right: new bit enters at the MSB
2'b10: q <= {q[W-2:0], sin_l}; // shift left: new bit enters at the LSB
2'b11: q <= d; // parallel load
endcaseStart from q = 1001_0110 (W = 8) and apply one mode per edge. Each row is the value after that edge.
Edge | mode | Serial or parallel input | q after the edge |
|---|---|---|---|
1 |
| — |
|
2 |
|
|
|
3 |
|
|
|
4 |
|
|
|
At edge 2 the top bit 1 falls off the left end and sin_l enters at bit 0. At edge 3 bit 0 falls off the right end and sin_r enters at bit 7. A load ignores the old value completely.
The shift-add multiplier uses exactly two of these modes: it loads its operands once, then shifts the multiplier right one bit per step so the bit under test is always bit 0.
Bidirectional shifters in arithmetic units (shift-and-add multiply, shift-and-subtract divide).
Scan registers and test logic.
Small serializers where one block must both send and receive.
8'b1001_0110, what do you get after one shift left with sin_l = 1, and after one shift right with sin_r = 0 applied to the *original* value?Shift left with sin_l = 1: 0010_1101. Shift right with sin_r = 0: 0100_1011.
The test compares the register with an arithmetic reference over 1000 random mode trials. Each failure identifies the operation, previous reference value, inputs and expected/actual output, and any failure ends with $fatal. It sets 1 ns time units and 1 ps precision, drives inputs on falling edges, and checks updated Q 1 ns after each rising edge. The module requires W >= 2 (with W = 1 the selects become the reversed q[0:1] and the out of range q[-1:0], which are illegal); an elaboration-time guard reports a clear error, though some tools may still diagnose the invalid select.
module univ_shift #(parameter int W = 8) ( // W >= 2
input logic clk, rst_n,
input logic [1:0] mode,
input logic sin_l, sin_r,
input logic [W-1:0] d,
output logic [W-1:0] q
);
if (W < 2) begin : g_w_check
$error("univ_shift: W must be at least 2");
end
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) q <= '0;
else begin
unique case (mode)
2'b00: q <= q;
2'b01: q <= {sin_r, q[W-1:1]}; // shift right: new bit enters at the MSB
2'b10: q <= {q[W-2:0], sin_l}; // shift left: new bit enters at the LSB
2'b11: q <= d;
endcase
end
endmodule