Oct 21, 2026

Verilog Code for FIFO to SPI Bridge

Verilog FIFO to SPI Bridge Design Documentation

Verilog Implementation: FIFO to SPI Bridge

This design functions as an efficient hardware bridge interfacing an upstream synchronous data FIFO with a downstream SPI peripheral device. The system automatically fetches data bytes from the FIFO when available, converts them from parallel to serial, and handles full transaction execution using standard SPI Mode 0 (CPOL=0, CPHA=0).

1. Architectural Block Diagram

  [ Upstream FIFO ]                     [ FIFO to SPI Bridge ]                   [ Downstream SPI ]
  ┌───────────────┐                    ┌──────────────────────┐                 ┌────────────────┐
  │         empty ├───────────────────>│ empty                │                 │                │
  │   rd_data[7:0]├───────────────────>│ fifo_data[7:0]       │                 │                │
  │        rd_en  │<───────────────────┤ fifo_rd_en       sclk├────────────────>│SCLK            │
  └───────────────┘                    │                  mosi├────────────────>│MOSI            │
                                       │                    ss├────────────────>│SS              │
                                       └──────────────────────┘                 └────────────────┘
    

2. Bridge RTL Source Code (fifo_to_spi_bridge.v)

// ============================================================================
// Module Name: fifo_to_spi_bridge
// Description: Bridge that reads 8-bit data from a FIFO and transmits it 
//              over a standard SPI interface (SPI Mode 0: CPOL=0, CPHA=0).
// ============================================================================

module fifo_to_spi_bridge #(
    parameter CLK_DIVIDE = 4  // System clock division factor for SCLK generation
)(
    // Clock and Reset
    input  wire       clk,          // System Clock
    input  wire       rst_n,        // Active Low Asynchronous Reset

    // FIFO Interface
    input  wire [7:0] fifo_data,    // 8-bit parallel data from FIFO
    input  wire       fifo_empty,   // Empty flag from upstream FIFO
    output reg        fifo_rd_en,   // Read enable strobe to FIFO

    // SPI Interface
    output reg        spi_sclk,     // SPI Serial Clock
    output reg        spi_mosi,     // SPI Master Out Slave In
    output reg        spi_ss        // SPI Slave Select / Chip Select (Active Low)
);

    // --- State Machine Definitions ---
    localparam STATE_IDLE      = 3'b000;
    localparam STATE_FETCH     = 3'b001;
    localparam STATE_WAIT_FIFO = 3'b010;
    localparam STATE_TX_BIT    = 3'b011;
    localparam STATE_DONE      = 3'b100;

    reg [2:0] current_state, next_state;

    // --- Internal Registers & Counters ---
    reg [7:0] shift_reg;            // Parallel-to-serial shift register
    reg [2:0] bit_counter;          // Tracks which of the 8 bits is transmitting
    reg [7:0] sclk_divider;         // Clock divider counter for SCLK generation
    reg       sclk_en;              // Enables internal SCLK ticking
    reg       sclk_edge_toggle;     // Toggles on every half-period of SCLK

    // --- Clock Divider logic for SPI SCLK ---
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            sclk_divider     <= 0;
            sclk_edge_toggle <= 0;
        end else if (sclk_en) begin
            if (sclk_divider == (CLK_DIVIDE/2) - 1) begin
                sclk_divider     <= 0;
                sclk_edge_toggle <= 1'b1;
            end else begin
                sclk_divider     <= sclk_divider + 1;
                sclk_edge_toggle <= 1'b0;
            end
        end else begin
            sclk_divider     <= 0;
            sclk_edge_toggle <= 1'b0;
        end
    end

    // --- State Machine Sequential Logic ---
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            current_state <= STATE_IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    // --- State Machine Combinational Logic ---
    always @(*) begin
        next_state = current_state;
        case (current_state)
            STATE_IDLE: begin
                if (!fifo_empty) next_state = STATE_FETCH;
            end
            STATE_FETCH:     next_state = STATE_WAIT_FIFO;
            STATE_WAIT_FIFO: next_state = STATE_TX_BIT;
            STATE_TX_BIT: begin
                if (bit_counter == 3'd7 && spi_sclk == 1'b1 && sclk_edge_toggle)
                    next_state = STATE_DONE;
            end
            STATE_DONE: begin
                if (sclk_edge_toggle) next_state = STATE_IDLE;
            end
            default: next_state = STATE_IDLE;
        endcase
    end

    // --- Datapath Control ---
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            fifo_rd_en  <= 1'b0;
            shift_reg   <= 8'h00;
            bit_counter <= 3'd0;
            sclk_en     <= 1'b0;
            spi_sclk    <= 1'b0;
            spi_mosi    <= 1'b0;
            spi_ss      <= 1'b1;
        end else begin
            case (current_state)
                STATE_IDLE: begin
                    fifo_rd_en  <= 1'b0;
                    bit_counter <= 3'd0;
                    sclk_en     <= 1'b0;
                    spi_sclk    <= 1'b0;
                    spi_mosi    <= 1'b0;
                    spi_ss      <= 1'b1;
                end
                STATE_FETCH: begin
                    fifo_rd_en  <= 1'b1;
                end
                STATE_WAIT_FIFO: begin
                    fifo_rd_en  <= 1'b0;
                    shift_reg   <= fifo_data;
                    spi_ss      <= 1'b0;
                end
                STATE_TX_BIT: begin
                    sclk_en <= 1'b1;
                    if (sclk_edge_toggle) begin
                        if (spi_sclk == 1'b0) begin
                            spi_sclk <= 1'b1;
                        end else begin
                            spi_sclk    <= 1'b0;
                            shift_reg   <= {shift_reg[6:0], 1'b0};
                            bit_counter <= bit_counter + 1;
                        end
                    end
                    spi_mosi <= shift_reg[7];
                end
                STATE_DONE: begin
                    if (sclk_edge_toggle) begin
                        sclk_en  <= 1'b0;
                        spi_ss   <= 1'b1;
                        spi_mosi <= 1'b0;
                    end
                end
            endcase
        end
    end
endmodule

3. Testbench Simulation (tb_fifo_to_spi_bridge.v)

`timescale 1ns / 1ps

module tb_fifo_to_spi_bridge;

    // Inputs
    reg clk;
    reg rst_n;
    reg [7:0] fifo_data;
    reg fifo_empty;

    // Outputs
    wire fifo_rd_en;
    wire spi_sclk;
    wire spi_mosi;
    wire spi_ss;

    // Instantiate Unit Under Test (UUT)
    fifo_to_spi_bridge #(
        .CLK_DIVIDE(4)
    ) uut (
        .clk(clk),
        .rst_n(rst_n),
        .fifo_data(fifo_data),
        .fifo_empty(fifo_empty),
        .fifo_rd_en(fifo_rd_en),
        .spi_sclk(spi_sclk),
        .spi_mosi(spi_mosi),
        .spi_ss(spi_ss)
    );

    // Generate Clock (50MHz -> Period = 20ns)
    always #10 clk = ~clk;

    initial begin
        clk = 0;
        rst_n = 0;
        fifo_data = 8'h00;
        fifo_empty = 1;

        #40;
        rst_n = 1;
        #40;

        // Transmit Data Byte 0xA5
        wait(clk);
        fifo_data  = 8'hA5;
        fifo_empty = 0;
        
        @(posedge fifo_rd_en);
        @(negedge clk);
        fifo_empty = 1;
        
        @(posedge spi_ss);
        #100;
        $finish;
    end
      
endmodule

4. Architectural & Optimization Guidelines

  • Deterministic Handshaking: The structural data capture routing takes place precisely during the STATE_WAIT_FIFO cycle, securing high-speed immunity against downstream glitches or unexpected timing changes on the external FIFO lines.
  • Scalable Clock Dividers: The CLK_DIVIDE design parameter allows engineers to adapt serial output frequencies to meet the timing restrictions of various targeted host chips without breaking the main system fabric layout.

No comments:

Post a Comment

SiteMap