Shift Register Waveform

Building A Configurable Shift Register

Today I am back to building out a set of basic circuits that I can use in future projects. We already have a configurable counter circuit. Another useful component to have is a configurable shift register. A shift register is used in many situations, particularly for serial communications protocols like RS-232, USB, I2C and SPI. The latter is on the list of future topics since some of the Pmod devices use SPI for communications. Since different serial protocols have different numbers of bits in their messages I want my shift register to be configurable.

What is a shift register?

A shift register reads in a bit each clock cycle and appends that to an internal register holding the list of bits previously received. An 8-bit shift register has an internal register that can hold the last 8 bits that have been clocked in. Often, a shift register will provide the current state of the internal register as an output. In that configuration we have a serial-in, parallel-out device (SIPO). Another useful configuration would be a parallel-in, serial-out device. For this project I will focus on the former.

The complete Verilog code for a shift register is shown below. There is not a lot in this code that we haven’t already seen before. The most interesting part takes place at line 22. Here I use the concatenation operator { } to both add in the new bit d as well as shift previous bits to the right. We first encountered the concatenation operator in the post on switches and the leds.

Notice how powerful the concatenation operator is, with it we can concatenate, or “glue” together, the new bit d with a bit-range (or slice) of the previous shift register contents. In this case we use rShiftReg[BITS – 2 : 0] as our sub-range. You will see the concatenation operator used frequently in Verilog.

module shift_reg
    #(parameter BITS = 8)
    (
    input clk,
    input rst,
    input d,
    output [BITS - 1 : 0] q
    );
    
    // internal signals
    reg [BITS - 1 : 0] rShiftReg;
    wire [BITS -1 : 0] output_next;
    
    // circuit is activated on clk or rst
    always @(posedge clk, posedge rst)
        if (rst)
            rShiftReg <= 0;
        else
            rShiftReg <= output_next;
            
    // update the shift register contents by shifting left
    assign output_next = { rShiftReg[BITS - 2 : 0], d };

    // update our output 
    assign q = rShiftReg;
                          
endmodule

Why does the upper range of the slice go from BITS – 2 to 0? That is because, in shifting in a new bit, we are going to drop the top bit (BITS – 1) to make room for the new bit at the bottom of our shift register. So the second highest bit (BITS – 2) becomes the new top bit (BITS – 1) and so on ending with bit 0 becoming the new bit 1. You may also have noticed that the new bit is added to the right, or bottom, of the shift register. That is because I’ve chosen to design this shift register to expect to receive the most significant bits first.

Does this design work?

To find out if it works I will write up a quick test bench and then run a behavioral simulation. In the test I will instantiate an 8-bit shift register. As mentioned in To ensure we start the test with the circuit in a known state I reset the circuit at the start. Then I clock in 8 bits of data with an input of 1. And then for good measure I will clock in 2 zero bits. Here is the test bench code.

module shift_test();

    localparam T=20;
    localparam BITS = 8;
    
    reg clk, rst;
    reg data_in;
    wire [BITS - 1 : 0] data_out;
    
    shift_reg #(.BITS(BITS)) uut(.clk(clk), .rst(rst), .d(data_in), .q(data_out));

    // generate the clock signal    
    always
    begin
        clk = 1'b1;
        #(T/2);
        clk = 1'b0;
        #(T/2);
    end

    // reset the device at start
    initial
    begin
        rst = 1'b1;
        #(T/2);
        rst = 1'b0;
    end
    
    initial
    begin
        data_in = 1'b0;
        @(negedge rst);
        
        // set input to 1
        data_in = 1'b1;
        
        // shift in the input BITS times
        repeat(BITS) @(negedge clk);

        // set the input to 0
        data_in = 1'b0;

        repeat(2) @(negedge clk);
        
        // end simulation                
        $stop; 
    end
        
endmodule

When we run the simulation we should expect to see the 1 bits flow through the output bits until all output bits are 1. And then we should see two 0 bits shift in to the output bits. Let’s run the simulation and see what happens. The resulting simulation waveform is shown below. In the waveform we can see, starting from the top, the clk signal repeating once every 20ns. Next we can see the rst signal for half a clock cycle, or 10ns.

Then we see the data input of 1 starting at the first negative edge of the clock. And finally we have the output bits 0 through 7. Notice how the 1 bit flows through the output bits all the way to 170ns when the input changes to 0. And how those zero values start to fill the output starting at 180ns, which is the next rising clock edge after the input changes. This is exactly the behavior we are looking for in a shift register.

shift register waveform
Shift register waveform

Yes, but does it work for real on the hardware? I haven’t yet run it that way. But that is not because I was lazy. No, if I ran it at full speed connected to, say, the LEDs you would not be able to see the change. At one bit-shift per FPGA clock the LEDs would change 100,000,000 times per second! We need some way to slow down the updates to the shift register so that we can see what is happening. I will leave that as a project for an upcoming post!

The source code for this project is here. If you have feedback, questions or suggestions regarding this post, please leave a comment!

Discover more from FPGA Coding

Subscribe now to keep reading and get access to the full archive.

Continue reading