FPGA--SDRAM

SDRAM工作原理

A. 核心架构:三维矩阵的“图书馆”

可以将一块SDRAM芯片想象成一座多层的图书馆,你需要按特定流程才能取出想要的书(数据)。

Bank (存储体): 图书馆的楼层。一块SDRAM芯片通常包含2、4或8个Bank。Bank之间可以独立操作(例如,在一个Bank进行预充电时,可以在另一个Bank读取数据),这是实现高性能流水线操作的关键。

Row (行): 每个楼层上的书架。行地址决定了选择哪个书架。

Column (列): 每个书架上的书。列地址决定了从选定的书架上取出哪本书。

B. 操作流程:命令驱动的“四步取书法”

你不能直接冲到书架上拿书,必须遵循以下命令序列:

1. `ACTIVATE` (激活/行选通): 你告诉图书管理员(SDRAM控制器),“我要去三楼 (Bank地址)的第18排书架 (Row地址)”。管理员随即打开这个书架的所有照明,并将一整排书架(一整行数据,通常为几KB)的内容全部复制到该Bank的行缓冲/感应放大器中。这是一个相对耗时的操作,其延迟由`tRCD`(行到列延迟)规定。

2. `READ` / `WRITE` (读/写): 现在书架的内容已经“缓存”好了,你可以快速地发出读或写命令。你告诉管理员,“请从这个打开的书架上,给我从第5本书 (Column地址)开始的连续8本书”。

3. Burst (突发传输): 这是SDRAM效率的精髓。你不需要为每一本书单独发命令。一旦`READ`命令发出,SDRAM会在接下来的几个时钟周期内,自动地、连续地将从起始列地址开始的一连串数据(一个Burst,通常是2, 4, 8个字)吐出来。`CAS Latency (CL)`规定了从`READ`命令发出到第一个数据出现所需的时钟周期数。

4. `PRECHARGE` (预充电): 当你在这个书架上的操作完成后,你必须告诉管理员,“这个书架我用完了,请关闭照明,准备让我访问别的书架”。这个命令会将行缓冲中的数据写回存储矩阵,并关闭当前行,为下一次`ACTIVATE`命令做准备。其延迟由`tRP`(行预充电延迟)规定。

C. 生存法则:不可或缺的`REFRESH` (刷新)

SDRAM的“D”代表动态(Dynamic)。其存储单元是基于微小的电容。电容会随时间漏电,如果不充电,数据就会丢失。因此,你必须周期性地(通常是每64ms)对SDRAM的所有行进行一次`REFRESH`操作。这个操作由SDRAM控制器自动、强制地执行,在刷新期间,SDRAM暂时无法进行正常的读写。

第三部分:SDRAM项目设计 - 视频帧缓冲 (Video Frame Buffer)

场景: 设计一个系统,能够实时地将一个动态生成的视频流存入SDRAM,并同时从SDRAM中读出,最终在显示器上显示。

1. 核心问题: 视频数据流是连续不断的,而FPGA内部的BRAM资源有限,无法存下一整帧(或多帧)高清图像。例如,一帧640x480x16bit的图像就需要 `640 * 480 * 2 = 614.4 KB`,远超一般FPGA的BRAM容量。

2. 为何需要SDRAM: 我们需要一个大容量、高带宽的外部存储器。让我们计算带宽需求:对于640x480 @ 60Hz的VGA信号,像素时钟约为25MHz。假设每个像素16位(2字节)。

写带宽需求 = `25,000,000 像素/秒 * 2 字节/像素 = 50 MB/s`  

读带宽需求 = `50 MB/s`

总带宽需求约为`100 MB/s`。这个带宽对于现代SDRAM(尤其是DDR系列,其带宽可达数GB/s)来说是轻而易举的,但对于SPI Flash或EEPROM则是天方夜谭。

3. 系统如何工作数据流被分为写路径和读路径。

写路径: `视频数据源 -> FIFO -> SDRAM控制器 -> SDRAM`。视频数据源(可以是摄像头接口,或者我们项目中的一个测试图案生成器)以像素时钟的速率产生数据。数据先写入一个小的FIFO,用于缓冲。上层逻辑当FIFO中的数据达到一定量(例如一个Burst的长度)时,向SDRAM控制器发出一个写请求。

读路径: `VGA时序发生器 -> 地址计算 -> SDRAM控制器 -> FIFO -> VGA输出`。VGA时序发生器根据当前的行场扫描位置,计算出需要显示的像素在SDRAM中的地址。然后向上层逻辑发出读请求。SDRAM控制器从SDRAM中读出一个Burst的数据,并存入另一个FIFO。VGA输出逻辑则从这个FIFO中以像素时钟的速率平滑地取出数据并显示。   

FIFO的作用: FIFO是连接平滑数据流(视频)和突发数据流(SDRAM)之间的关键桥梁,用于数据速率匹配和时钟域交叉。

1. sdram_controller_ip.v

module sdram_controller_ip ();

2. async_fifo_ip.v

       module async_fifo_ip #();

3. vga_timing_generator.v

`timescale 1ns / 1ps

module vga_timing_generator (
    input  wire i_pixel_clk,
    input  wire i_rst_n,
    output reg  o_h_sync,
    output reg  o_v_sync,
    output wire o_display_on,
    output wire [9:0] o_pixel_x,
    output wire [9:0] o_pixel_y
);

    // VGA 640x480 @ 60Hz Timing Parameters
    localparam H_DISPLAY      = 640;
    localparam H_FRONT_PORCH  = 16;
    localparam H_SYNC_PULSE   = 96;
    localparam H_BACK_PORCH   = 48;
    localparam H_TOTAL        = H_DISPLAY + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH; // 800

    localparam V_DISPLAY      = 480;
    localparam V_FRONT_PORCH  = 10;
    localparam V_SYNC_PULSE   = 2;
    localparam V_BACK_PORCH   = 33;
    localparam V_TOTAL        = V_DISPLAY + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH; // 525

    reg [9:0] r_h_count = 0;
    reg [9:0] r_v_count = 0;

    assign o_pixel_x = r_h_count;
    assign o_pixel_y = r_v_count;

    assign o_display_on = (r_h_count < H_DISPLAY) && (r_v_count < V_DISPLAY);

    always @(posedge i_pixel_clk or negedge i_rst_n) begin
        if (!i_rst_n) begin
            r_h_count <= 0;
            r_v_count <= 0;
        end else begin
            if (r_h_count == H_TOTAL - 1) begin
                r_h_count <= 0;
                if (r_v_count == V_TOTAL - 1) begin
                    r_v_count <= 0;
                end else begin
                    r_v_count <= r_v_count + 1;
                end
            end else begin
                r_h_count <= r_h_count + 1;
            end
        end
    end

    always @(posedge i_pixel_clk or negedge i_rst_n) begin
        if (!i_rst_n) begin
            o_h_sync <= 1;
            o_v_sync <= 1;
        end else begin
            // Horizontal Sync (active low)
            if (r_h_count >= H_DISPLAY + H_FRONT_PORCH && r_h_count < H_DISPLAY + H_FRONT_PORCH + H_SYNC_PULSE)
                o_h_sync <= 0;
            else
                o_h_sync <= 1;
           
            // Vertical Sync (active low)
            if (r_v_count >= V_DISPLAY + V_FRONT_PORCH && r_v_count < V_DISPLAY + V_FRONT_PORCH + V_SYNC_PULSE)
                o_v_sync <= 0;
            else
                o_v_sync <= 1;
        end
    end

endmodule


4. frame_buffer_control.v

`timescale 1ns / 1ps

module frame_buffer_control #(
    parameter H_ACTIVE      = 640,
    parameter V_ACTIVE      = 480,
    parameter PIXEL_WIDTH   = 16,
    parameter SDRAM_DATA_WIDTH = 128, // Corresponds to app_wdf_data width
    parameter SDRAM_ADDR_WIDTH = 28
)(
    // Pixel Clock Domain Interface
    input  wire i_pixel_clk,
    input  wire i_pixel_rst_n,
    input  wire [PIXEL_WIDTH-1:0] i_pixel_data,
    input  wire i_pixel_data_valid,
    output wire [PIXEL_WIDTH-1:0] o_pixel_data,
    input  wire i_vga_data_req,
    input  wire [9:0] i_read_pixel_x,
    input  wire [9:0] i_read_pixel_y,

    // SDRAM Clock Domain Interface
    input  wire i_sdram_clk,
    input  wire i_sdram_rst_n,

    // SDRAM Controller User Interface
    output reg        o_app_en,
    output reg [2:0]  o_app_cmd,
    output reg [SDRAM_ADDR_WIDTH-1:0] o_app_addr,
    input  wire       i_app_rdy,
    output reg [SDRAM_DATA_WIDTH-1:0] o_app_wdf_data,
    output reg        o_app_wdf_wren,
    output reg        o_app_wdf_end,
    input  wire       i_app_wdf_rdy,
    input  wire [SDRAM_DATA_WIDTH-1:0] i_app_rd_data,
    input  wire       i_app_rd_data_valid
);
   
    localparam PIXELS_PER_BEAT = SDRAM_DATA_WIDTH / PIXEL_WIDTH; // e.g., 128/16 = 8
    localparam BURST_LEN_IN_BEATS = 8; // Must match SDRAM controller config
    localparam BURST_LEN_IN_PIXELS = BURST_LEN_IN_BEATS * PIXELS_PER_BEAT; // e.g., 8*8 = 64 pixels

    // --- FIFO Instantiations ---
    wire wr_fifo_full;
    wire [10:0] wr_fifo_count;
    wire rd_fifo_empty;
    wire [10:0] rd_fifo_count;
    wire [SDRAM_DATA_WIDTH-1:0] wr_fifo_rd_data;

    // Write FIFO (Pixel Clock -> SDRAM Clock)
    // We need to pack pixels into the SDRAM bus width
    reg [SDRAM_DATA_WIDTH-1:0] r_pixel_pack_reg;
    reg [$clog2(PIXELS_PER_BEAT)-1:0] r_pack_cnt = 0;
    reg wr_fifo_wr_en = 0;

    always @(posedge i_pixel_clk or negedge i_pixel_rst_n) begin
        if(!i_pixel_rst_n) begin
            r_pack_cnt <= 0;
            wr_fifo_wr_en <= 0;
        end else begin
            wr_fifo_wr_en <= 0; // Default
            if (i_pixel_data_valid) begin
                r_pixel_pack_reg <= {i_pixel_data, r_pixel_pack_reg[SDRAM_DATA_WIDTH-1:PIXEL_WIDTH]};
                if (r_pack_cnt == PIXELS_PER_BEAT - 1) begin
                    r_pack_cnt <= 0;
                    wr_fifo_wr_en <= 1; // Push a full word into FIFO
                end else begin
                    r_pack_cnt <= r_pack_cnt + 1;
                end
            end
        end
    end

    async_fifo_ip #(
        .DATA_WIDTH(SDRAM_DATA_WIDTH), .ADDR_WIDTH(8) // Depth 256
    ) wr_fifo (
        .wr_clk(i_pixel_clk), .wr_rst_n(i_pixel_rst_n),
        .wr_en(wr_fifo_wr_en), .wr_data(r_pixel_pack_reg),
        .full(wr_fifo_full), .wr_data_count(),
        .rd_clk(i_sdram_clk), .rd_rst_n(i_sdram_rst_n),
        .rd_en(sdram_wr_fifo_rd_en), .rd_data(wr_fifo_rd_data),
        .empty(), .rd_data_count(wr_fifo_count)
    );

    // Read FIFO (SDRAM Clock -> Pixel Clock)
    reg [PIXEL_WIDTH-1:0] rd_fifo_rd_data_unpacked;
    assign o_pixel_data = rd_fifo_rd_data_unpacked;

    // ... Unpacking logic similar to packing logic ...

    // --- SDRAM Control Logic (in sdram_clk domain) ---
    reg [SDRAM_ADDR_WIDTH-1:0] r_wr_addr = 0;
    reg [SDRAM_ADDR_WIDTH-1:0] r_rd_addr = 0;
   
    // Write State Machine
    // ...
    // When wr_fifo_count >= BURST_LEN_IN_BEATS and read FSM is idle
    // and i_app_rdy is high, start a write transaction.
    // Pop data from wr_fifo and drive o_app_wdf_data
   
    // Read State Machine
    // ...
    // When rd_fifo_count < (FIFO_DEPTH - BURST_LEN_IN_BEATS)
    // and i_app_rdy is high, start a read transaction.
    // Calculate address based on i_read_pixel_x/y
    // When i_app_rd_data_valid is high, push data to rd_fifo.
   
    // Arbiter (Read has priority to prevent screen tearing)
    // ...

endmodule


5. video_frame_buffer_top.v

`timescale 1ns / 1ps

module video_frame_buffer_top (
    // System Inputs
    input  wire i_sys_clk,   // Main high-speed clock (e.g., 100MHz for SDRAM)
    input  wire i_sys_rst_n,

    // VGA Outputs
    output wire o_vga_h_sync,
    output wire o_vga_v_sync,
    output wire [4:0] o_vga_r,
    output wire [5:0] o_vga_g,
    output wire [4:0] o_vga_b

    // SDRAM Physical Interface
    // ... connect all ddr3_* ports from sdram_controller_ip here
);

    // --- Clock Generation ---
    // In a real design, a PLL IP would be used to generate
    // sdram_clk (e.g., 100MHz) and pixel_clk (25.175MHz) from i_sys_clk.
    wire pixel_clk;
    wire sdram_clk = i_sys_clk; // For simplicity, assume sys_clk is the SDRAM clock
    // pll_ip u_pll (...);

    // --- VGA and Video Pattern ---
    wire [9:0] w_pixel_x, w_pixel_y;
    wire       w_display_on;
    wire [15:0] w_pattern_data;
    wire       w_pattern_valid;

    vga_timing_generator u_vga_timing (
        .i_pixel_clk(pixel_clk),
        .i_rst_n(i_sys_rst_n), // Use synchronized reset
        .o_h_sync(o_vga_h_sync),
        .o_v_sync(o_vga_v_sync),
        .o_display_on(w_display_on),
        .o_pixel_x(w_pixel_x),
        .o_pixel_y(w_pixel_y)
    );

    video_pattern_generator u_pattern_gen (
        .i_display_on(w_display_on),
        .i_pixel_x(w_pixel_x),
        .i_pixel_y(w_pixel_y),
        .o_pixel_data(w_pattern_data),
        .o_data_valid(w_pattern_valid)
    );
   
    // --- Frame Buffer Core Logic ---
    wire [15:0] w_vga_out_data;
    // ... wires for sdram controller interface ...

    frame_buffer_control u_fb_ctrl (
        .i_pixel_clk(pixel_clk),
        .i_pixel_rst_n(i_sys_rst_n),
        .i_pixel_data(w_pattern_data),
        .i_pixel_data_valid(w_pattern_valid),
        .o_pixel_data(w_vga_out_data),
        .i_vga_data_req(w_display_on), // Request data when in display area
        .i_read_pixel_x(w_pixel_x),
        .i_read_pixel_y(w_pixel_y),

        .i_sdram_clk(sdram_clk),
        .i_sdram_rst_n(i_sys_rst_n),

        // Connections to SDRAM Controller IP
        .o_app_en(app_en),
        //...
    );

    // --- VGA Output Driver ---
    assign {o_vga_r, o_vga_g, o_vga_b} = w_display_on ? w_vga_out_data : 16'h0000;

    // --- SDRAM Controller IP Instantiation ---
    sdram_controller_ip u_sdram_ctrl (
        .sys_clk(sdram_clk),
        .sys_rst_n(i_sys_rst_n),
        //... connect all app_* ports to u_fb_ctrl
        //... connect all ddr3_* ports to top-level I/O
    );

endmodule



原文链接:,转发请注明来源!