Showing posts with label VLSI. Show all posts
Showing posts with label VLSI. Show all posts

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.

Verification Interview Questions Bank

SystemVerilog & Verification Interview Preparation Hub

Interview Questions on System Verilog/Verification - Freshers

Core concepts covered: 2-state vs 4-state simulation data types (logic vs bit), arrays, queues, basic OOP in SV, and introductory constrained randomization logic.
Browse Questions

Interview Questions on System Verilog/Verification - Mid-exp

Core concepts covered: Deep vs shallow copying, virtual interfaces, dynamic testbench infrastructure, pre/post randomization hooks, and introductory UVM architecture (~5 years experience).
Browse Questions

Interview Questions on System Verilog/Verification - Senior

Core concepts covered: Resolving complex constraint conflicts, advanced functional cross-coverage closing strategies, complete UVM factory design patterns, and testbench SoC architecture (10+ years experience).
Browse Questions

SiteMap