What is a Shift Register
Create delays, convert serial to parallel data in FPGAs
Shift registers are a common FPGA building block. They are created by cascading Flip-Flops (Registers) in a chain. All registers must share the same clock, and the output of one register must be connected to the input of the next register in the chain. Shift registers are mainly used to accomplish one of three goals:
- Delaying data by some number of clock cycles
- Converting serial data to parallel data
- Converting parallel data to serial data
Creating a Shift Register for Delay
Creating delay in an FPGA is the most common use of a shift register. The delay is often used to align data in time. The figure below shows this simple type of shift register. The number of Flip-Flops in the delay chain dictates how many clock cycles it will take for the data on the input to propagate to the data on the output. So in the picture below, it will take four clock cycles for an input on D on the first Flip-Flop to be seen on the output Q of the last Flip-Flop. Read about Processes in VHDL or Always Blocks in Verilog for a tutorial on how to create a shift register in your HDL of choice.
-- VHDL Example of Shift Register for Delay: signal r_Shift : std_logic_vector(3 downto 0); process (i_clock) begin if rising_edge(i_clock) then r_Shift(3 downto 1) <= r_Shift(2 downto 0); -- Shift Left r_Shift(0) <= i_Data_To_Delay; -- Bit 3 of r_Shift has been delayed by 4 clock cycles end if; end process;
// Verilog Example of Shift Register for Delay: reg [3:0] r_Shift; always @ (posedge i_clock) begin r_Shift[3:1] <= r_Shift[2:0]; // Shift Left r_Shift[0] <= i_Data_To_Delay; // Bit 3 of r_Shift has been delayed by 4 clock cycles end
The code above demonstrates creating delay by passing i_Data_To_Delay to the least significant bit of r_Shift. Then r_Shift is continuously shifted to the left on each clock cycle. This might be useful for example if you get some data in from one module, but don’t want to act on it right away. In the code above, any bit from r_Shift can be used to precisely control how much delay is applied, bit 0 has 1 bit of delay on it, and bit 3 has 4 bits of delay.
Converting serial data to parallel data
Converting from serial data to parallel data is another common use of shift registers. This occurs when interfacing to off-chip signals that transmit data serially such as a UART Receiver. When data comes in over a UART, it need to be converted from serial data 1-bit wide to a parallel byte that the FPGA can look at.
signal r_RX_Data : std_logic := '0'; signal r_Bit_Index : integer range 0 to 7 := 0; -- 8 Bits Total signal r_RX_Byte : std_logic_vector(7 downto 0) := (others => '0'); p_UART_RX : process (i_Clk) begin if rising_edge(i_Clk) then r_Rx_Byte[7] <= r_Rx_Data; -- Data is sent least-significant byte first r_Rx_Byte[6:0] <= r_Rx_Byte[7:1]; -- shift right -- ABOVE DOES SAME THING AS: -- r_RX_Byte(r_Bit_Index) <= r_RX_Data; -- Check if we have sent out all bits if r_Bit_Index < 7 then r_Bit_Index <= r_Bit_Index + 1; r_SM_Main <= s_RX_Data_Bits; else r_Bit_Index <= 0; -- RECEIVE OF A UART BYTE COMPLETE HERE
reg r_Rx_Data = 1'b1; // Received UART Data reg [2:0] r_Bit_Index = 0; //8 bits total reg [7:0] r_Rx_Byte = 0; always @(posedge i_Clock) begin // SNIPPET: r_Rx_Byte[7] <= r_Rx_Data; // Data is sent least-significant byte first, so shift right r_Rx_Byte[6:0] <= r_Rx_Byte[7:1]; // ABOVE DOES SAME THING AS: // r_Rx_Byte[r_Bit_Index] <= r_Rx_Data; // Check if we have received all bits if (r_Bit_Index < 7) r_Bit_Index <= r_Bit_Index + 1; else r_Bit_Index <= 0; // RECEIVE OF A UART BYTE COMPLETE HERE
Converting parallel data to serial data
This is the opposite of the above and is used in UART Transmitter. When you want to transmit a byte over UART, it must first be serialized and sent out over the single UART line. A shift register can be used for this purpose.
signal r_Bit_Index : integer range 0 to 7 := 0; -- 8 Bits Total signal r_TX_Data : std_logic_vector(7 downto 0) := (others => '0'); begin p_UART_TX : process (i_Clk) begin if rising_edge(i_Clk) then -- SNIPPET: o_TX_Serial <= r_TX_Data(0); -- Data is shifted out least-significant bit first. r_TX_Data(6 downto 0) <= r_TX_Data(7 downto 1); -- Shift next bit into place. -- ABOVE DOES SAME THING AS: -- o_TX_Serial <= r_TX_Data(r_Bit_Index); -- Check if we have sent out all bits if r_Bit_Index < 7 then r_Bit_Index <= r_Bit_Index + 1; else r_Bit_Index <= 0; // TRANSMIT OF A UART BYTE COMPLETE HERE end if;
reg [2:0] r_Bit_Index = 0; reg [7:0] r_Tx_Data = 0; always @(posedge i_Clock) begin // SNIPPET: o_TX_Serial <= r_TX_Data[0]; // Data is shifted out least-significant bit first. r_TX_Data[6:0] <= r_TX_Data[7:1]; // Shift next bit into place. // ABOVE DOES SAME THING AS: // o_Tx_Serial <= r_Tx_Data[r_Bit_Index]; // Check if we have sent out all bits if (r_Bit_Index < 7) begin r_Bit_Index <= r_Bit_Index + 1; end else begin r_Bit_Index <= 0; end end
Hopefully this article has given you a basic understanding of what a shift register is in an FPGA or ASIC. To see how to create your own shift register in either Verilog or VHDL, see the links below.
This website is a goldmine. Thank you for writing all of this!