跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
汇编

FPGA 嵌入式块存储器 RAM:原理与实现指南

FPGA 嵌入式块存储器 RAM 是构建高效系统的关键组件,支持随机读写与非破坏性读取。阐述 SRAM 与 DRAM 特性,详解 Vivado 中 Block Memory Generator IP 核配置,涵盖单端口、双端口及真双端口模式。通过图像显示系统实战案例,分析 BRAM 资源计算与时序控制,提供数据缓冲与帧缓存的 Verilog 实现方案及仿真验证方法,帮助开发者优化存储资源使用。

内存管理发布于 2026/4/12更新于 2026/7/2449 浏览
FPGA 嵌入式块存储器 RAM:原理与实现指南

引言:为什么需要 RAM?

在 FPGA 系统设计中,很多时候我们需要的是可读可写的存储器,这就是 RAM(Random Access Memory,随机存取存储器)。无论是用于数据缓存、帧缓冲还是实时数据存储,RAM 都是构建高效 FPGA 系统不可或缺的组成部分。

本文从 RAM 的基本原理出发,详细讲解嵌入式块存储器 RAM 的分类、特性、配置方法以及在实际项目中的应用,特别关注如何通过 Vivado 工具链高效地使用 RAM IP 核。

RAM 的核心特性与应用场景

RAM 的三大核心特性

与 ROM 相比,RAM 具有以下显著特性:

  • 随机存取:支持对任意地址的读写操作,访问顺序不受限制
  • 非破坏性读取:读取操作不会清除存储内容,数据可多次重复读取
  • 覆盖写入:新数据写入会直接覆盖旧数据,支持动态更新

典型应用场景分析

场景一:数据速率匹配缓冲

这是 RAM 最常见的应用场景之一。考虑以下实际问题:某 ADC 以 1μs 的间隔产生 12 位数据(速率:1000 个/ms),而串口以 115200 波特率发送数据(每 6 位数据需要 69.4μs)。数据产生速率远快于发送速率。

解决方案:

// 使用 RAM 作为数据缓冲器
module data_buffer(
    input clk,
    input rst_n,
    input [11:0] adc_data,
    input adc_data_valid,
    output reg [5:0] uart_data,
    output reg uart_data_valid
);
    // 双端口 RAM:端口 A 用于写入 ADC 数据,端口 B 用于读取串口数据
    // 深度:1000,宽度:12 位(写入)和 6 位(读取)
    
    // 写入逻辑
    always @(posedge clk) begin
        if(adc_data_valid) begin
            ram_wea <= 1'b1;
            ram_addra <= write_addr;
            ram_dina <= adc_data;
            write_addr <= write_addr + 1;
        end
    end
    
    // 读取逻辑
    always @(posedge clk) begin
        if(uart_ready) begin
            ram_addrb <= read_addr;
            uart_data <= ram_doutb[5:0]; // 只取低 6 位
            read_addr <= read_addr + 1;
        end
    end
endmodule

场景二:图像帧缓冲

在视频处理系统中,RAM 常被用作帧缓冲器:

// TFT 显示屏图像缓冲
module tft_frame_buffer(
    input clk_pixel,      // 像素时钟
    input clk_system,     // 系统时钟
    input [15:0] pixel_in,// RGB565 像素数据
    input pixel_valid,
    output [15:0] pixel_out
);
    // 双端口 RAM 配置
    // 端口 A:串口写入(系统时钟域)
    // 端口 B:TFT 读取(像素时钟域)
    // 存储需求计算:
    // 800×480 分辨率,16 位/像素 → 800×480×16 = 6,144,000 bits
    // 需要约 170 个 36Kb 的 BRAM 块
endmodule

RAM 的类型:SRAM 与 DRAM 详解

DDR3 控制器接口示例:

// DDR3 控制器接口示例
module ddr3_controller(
    input clk,
    input rst_n,          // 用户接口
    input [31:0] app_addr,
    input [255:0] app_wdf_data,
    input app_wdf_wren,
    output [255:0] app_rd_data,
    output app_rd_data_valid,
    // DDR3 物理接口
    output [13:0] ddr3_addr,
    output [2:0] ddr3_ba,
    output ddr3_cas_n,
    output ddr3_ras_n,
    output ddr3_we_n
);
    // 使用 Xilinx MIG IP 核实现
endmodule

Vivado 中 RAM IP 核的详细配置指南

1. RAM IP 核类型选择

在 Vivado 的 IP Catalog 中搜索"Block Memory Generator",可以看到多种 RAM 类型:

(1)单端口 RAM

// 接口信号
module single_port_ram(
    input clka,           // 时钟
    input ena,            // 使能
    input wea,            // 写使能(1=写,0=读)
    input [9:0] addra,    // 地址(10 位,深度 1024)
    input [15:0] dina,    // 数据输入
    output [15:0] douta   // 数据输出
);

特点:所有操作共享同一组端口,读写不能同时进行,适用于简单的数据存储场景。

(2)简单双端口 RAM

// 接口信号
module simple_dual_port_ram(
    // 端口 A:只写
    input clka,
    input ena,
    input wea,            // 始终为 1(只写)
    input [9:0] addra,
    input [15:0] dina,
    // 端口 B:只读
    input clkb,
    input enb,
    input [9:0] addrb,
    output [15:0] doutb
);

特点:端口 A 专用于写,端口 B 专用于读,可同时进行读写操作,适用于生产者 - 消费者模型。

(3)真双端口 RAM

// 接口信号
module true_dual_port_ram(
    // 端口 A:可读写
    input clka,
    input ena,
    input wea,
    input [9:0] addra,
    input [15:0] dina,
    output [15:0] douta,
    // 端口 B:可读写
    input clkb,
    input enb,
    input web,
    input [9:0] addrb,
    input [15:0] dinb,
    output [15:0] doutb
);

特点:两个端口都可独立读写,需要处理读写冲突,适用于复杂的数据共享场景。

2. 关键配置参数详解

(1)存储容量计算

总存储容量 = 数据位宽 × 深度 单位:bits 示例:数据位宽:16 bits,深度:1024,总容量:16 × 1024 = 16,384 bits = 16 Kb

(2)BRAM 资源使用

Xilinx 7 系列 FPGA 的 BRAM 配置:每个 BRAM 块:36 Kb。可配置为 1 个 36Kb RAM 或 2 个独立的 18Kb RAM。 常见配置模式:32K × 1, 16K × 2, 8K × 4, 4K × 9, 2K × 18, 1K × 36, 512 × 72。

(3)工作模式选择

在"Port A Options"或"Port B Options"中:

Write First Mode(写优先模式):当读写同一地址时,写入的数据会立即出现在输出。

always @(posedge clk) begin
    if(wea) begin
        mem[addr] <= din;
        dout <= din; // 写优先
    end else begin
        dout <= mem[addr];
    end
end

Read First Mode(读优先模式):当读写同一地址时,先读取旧数据,再写入新数据。

always @(posedge clk) begin
    dout <= mem[addr]; // 先读
    if(wea) begin
        mem[addr] <= din; // 后写
    end
end

No Change Mode(无变化模式):当读写同一地址时,输出保持不变。

always @(posedge clk) begin
    if(wea) begin
        mem[addr] <= din;
    end else begin
        dout <= mem[addr];
    end
end

3. 字节写使能功能

字节写使能允许按字节粒度控制数据写入,特别适用于处理不同位宽的数据:

// 24 位数据,按字节写入控制
module byte_write_ram(
    input clk,
    input ena,
    input [2:0] wea,      // 3 位写使能,控制 3 个字节
    input [9:0] addra,
    input [23:0] dina,    // 24 位输入数据
    output [23:0] douta   // 24 位输出数据
);
    // wea[2:0] 控制:
    // wea = 3'b111: 写入全部 3 个字节
    // wea = 3'b011: 只写入低 2 个字节(dina[15:0])
    // wea = 3'b001: 只写入最低字节(dina[7:0])
    // wea = 3'b000: 不写入任何字节
endmodule

实战案例:基于 RAM 的图像显示系统

1. 详细实现代码

(1)顶层模块设计

系统架构设计:

module image_display_system(
    input clk_100m,       // 100MHz 系统时钟
    input clk_pixel,      // 像素时钟(25MHz for 800x480@60Hz)
    input rst_n,          // 复位
    // 串口接口
    input uart_rx,
    output uart_tx,
    // TFT 显示接口
    output [15:0] tft_data,
    output tft_hsync,
    output tft_vsync,
    output tft_de
);
    wire clk_sys = clk_100m;
    wire clk_disp = clk_pixel;
    
    // 串口接收模块
    wire [7:0] uart_rx_data;
    wire uart_rx_valid;
    uart_receiver u_uart_rx(.clk(clk_sys), .rst_n(rst_n), .rx(uart_rx), .data(uart_rx_data), .valid(uart_rx_valid));
    
    // 图像数据写入控制
    wire [15:0] ram_wdata;
    wire [16:0] ram_waddr; // 800*480=384000,需要 19 位地址
    wire ram_wen;
    image_writer u_writer(.clk(clk_sys), .rst_n(rst_n), .uart_data(uart_rx_data), .uart_valid(uart_rx_valid), .ram_wdata(ram_wdata), .ram_waddr(ram_waddr), .ram_wen(ram_wen));
    
    // 双端口 RAM 实例
    wire [15:0] ram_rdata;
    wire [16:0] ram_raddr;
    blk_mem_gen_0 u_ram(
        // 端口 A:写端口(串口数据写入)
        .clka(clk_sys), .ena(1'b1), .wea(ram_wen), .addra(ram_waddr[16:0]), .dina(ram_wdata), .douta(),
        // 端口 A 不读取
        // 端口 B:读端口(TFT 显示读取)
        .clkb(clk_disp), .enb(1'b1), .addrb(ram_raddr[16:0]), .doutb(ram_rdata)
    );
    
    // TFT 显示控制器
    tft_controller u_tft(.clk(clk_disp), .rst_n(rst_n), .pixel_data(ram_rdata), .pixel_addr(ram_raddr), .tft_data(tft_data), .tft_hsync(tft_hsync), .tft_vsync(tft_vsync), .tft_de(tft_de));
endmodule

(2)图像数据写入模块

module image_writer(
    input clk,
    input rst_n,
    input [7:0] uart_data,
    input uart_valid,
    output reg [15:0] ram_wdata,
    output reg [16:0] ram_waddr,
    output reg ram_wen
);
    reg [1:0] byte_cnt;   // 字节计数器(0-1,两个字节组成一个 16 位像素)
    reg [7:0] pixel_low;  // 像素低字节
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            byte_cnt <= 2'd0;
            pixel_low <= 8'd0;
            ram_waddr <= 17'd0;
            ram_wen <= 1'b0;
        end elseif(uart_valid) begin
            case(byte_cnt)
                2'd0: begin
                    pixel_low <= uart_data; // 保存低字节
                    byte_cnt <= 2'd1;
                    ram_wen <= 1'b0;
                end
                2'd1: begin
                    ram_wdata <= {uart_data, pixel_low}; // 拼接高字节和低字节
                    ram_waddr <= ram_waddr + 1'b1;
                    ram_wen <= 1'b1;
                    byte_cnt <= 2'd0;
                end
            endcase
        end else begin
            ram_wen <= 1'b0;
        end
    end
endmodule

(3)TFT 显示控制器

module tft_controller(
    input clk,            // 像素时钟
    input rst_n,
    input [15:0] pixel_data, // 从 RAM 读取的像素数据
    output reg [16:0] pixel_addr, // 读取地址
    output reg [15:0] tft_data,
    output reg tft_hsync,
    output reg tft_vsync,
    output reg tft_de
);
    // 显示时序参数(800x480@60Hz)
    parameter H_ACTIVE = 800;   // 水平有效像素
    parameter H_FP = 40;        // 水平前沿
    parameter H_SYNC = 128;     // 水平同步脉冲
    parameter H_BP = 88;        // 水平后沿
    parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;
    parameter V_ACTIVE = 480;   // 垂直有效行
    parameter V_FP = 13;        // 垂直前沿
    parameter V_SYNC = 2;       // 垂直同步脉冲
    parameter V_BP = 33;        // 垂直后沿
    parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;
    
    reg [10:0] h_cnt;   // 水平计数器(0-1047)
    reg [9:0] v_cnt;    // 垂直计数器(0-527)
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            h_cnt <= 11'd0;
            v_cnt <= 10'd0;
            pixel_addr <= 17'd0;
            tft_hsync <= 1'b0;
            tft_vsync <= 1'b0;
            tft_de <= 1'b0;
            tft_data <= 16'd0;
        end else begin
            // 水平计数器递增
            if(h_cnt == H_TOTAL - 1) begin
                h_cnt <= 11'd0;
                // 垂直计数器递增
                if(v_cnt == V_TOTAL - 1) begin
                    v_cnt <= 10'd0;
                    pixel_addr <= 17'd0; // 帧结束,复位地址
                end else begin
                    v_cnt <= v_cnt + 1'b1;
                end
            end else begin
                h_cnt <= h_cnt + 1'b1;
            end
            
            // 生成同步信号
            tft_hsync <= (h_cnt >= H_ACTIVE + H_FP) && (h_cnt < H_ACTIVE + H_FP + H_SYNC);
            tft_vsync <= (v_cnt >= V_ACTIVE + V_FP) && (v_cnt < V_ACTIVE + V_FP + V_SYNC);
            
            // 生成数据使能信号
            tft_de <= (h_cnt < H_ACTIVE) && (v_cnt < V_ACTIVE);
            
            // 在有效显示区域内读取像素数据
            if(tft_de) begin
                tft_data <= pixel_data;
                pixel_addr <= pixel_addr + 1'b1;
            end else begin
                tft_data <= 16'd0;
            end
        end
    end
endmodule

2. RAM 资源需求分析

对于 800×480 分辨率的 TFT 显示,使用 RGB565 格式(16 位/像素):

总像素数:800 × 480 = 384,000 像素 每像素数据:16 bits 总存储需求:384,000 × 16 = 6,144,000 bits Xilinx XC7Z015 芯片资源:

  • 每个 BRAM 块:36 Kb
  • 总 BRAM 数量:95 个
  • 总 BRAM 容量:95 × 36 Kb = 3,420 Kb 结论:无法存储完整一帧图像

仿真验证

测试平台设计

module ram_tb;
    reg clk;
    reg rst_n;
    
    // 时钟生成(100MHz)
    always #5 clk = ~clk;
    
    // 测试序列
    initial begin
        clk = 0;
        rst_n = 0;
        // 复位释放
        #100 rst_n = 1;
        
        // 测试 1:顺序写入然后读取
        test_sequential();
        // 测试 2:随机地址访问
        test_random();
        // 测试 3:读写冲突测试
        test_collision();
        $finish;
    end
    
    task test_sequential;
        integer i;
        begin
            $display("=== 顺序读写测试开始 ===");
            // 写入 0-1023
            for(i = 0; i < 1024; i = i + 1) begin
                @(posedge clk);
                ram_wen = 1;
                ram_addr = i;
                ram_wdata = i * 2;
            end
            @(posedge clk); ram_wen = 0;
            
            // 验证读取
            for(i = 0; i < 1024; i = i + 1) begin
                @(posedge clk);
                ram_addr = i;
                @(posedge clk);
                if(ram_rdata !== i * 2) begin
                    $display("错误:地址 %d,期望值 %d,实际值 %d", i, i*2, ram_rdata);
                end
            end
            $display("=== 顺序读写测试完成 ===");
        end
    endtask
    
    task test_random;
        integer i, addr;
        begin
            $display("=== 随机访问测试开始 ===");
            for(i = 0; i < 100; i = i + 1) begin
                addr = $random % 1024;
                // 写入随机数据
                @(posedge clk);
                ram_wen = 1;
                ram_addr = addr;
                ram_wdata = $random;
                test_data[addr] = ram_wdata;
                // 等待写入完成
                @(posedge clk); ram_wen = 0;
                // 延迟 2 个周期后读取验证
                repeat(2) @(posedge clk);
                ram_addr = addr;
                @(posedge clk);
                if(ram_rdata !== test_data[addr]) begin
                    $display("随机测试错误:地址 %d", addr);
                end
            end
            $display("=== 随机访问测试完成 ===");
        end
    endtask
endmodule

目录

  1. 引言:为什么需要 RAM?
  2. RAM 的核心特性与应用场景
  3. RAM 的三大核心特性
  4. 典型应用场景分析
  5. RAM 的类型:SRAM 与 DRAM 详解
  6. Vivado 中 RAM IP 核的详细配置指南
  7. 1. RAM IP 核类型选择
  8. 2. 关键配置参数详解
  9. 3. 字节写使能功能
  10. 实战案例:基于 RAM 的图像显示系统
  11. 1. 详细实现代码
  12. 2. RAM 资源需求分析
  13. 仿真验证
  14. 测试平台设计
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 拆解LLM内部机制:从下一个词的概率到流畅应答
  • 2026 年 AI Agent 工具选型:OpenClaw 生态 9 款产品上手对比
  • Ubuntu 20.04 LTS 升级至 24.04 LTS 完整步骤
  • VS Code 前端开发必备插件推荐及配置教程
  • Eino ADK 体系篇:ChatModelAgent 核心机制与实战解析
  • 在 Apple Silicon Mac 上折腾 Whisper-WebUI 的记录
  • Yuan2.0 数据预处理
  • 基于 ARX 结构和反馈机制的序列密码算法 Phelix
  • AI 时代的软件工程:使用 OpenSpec 驱动自动化开发
  • 鸿蒙 APP 运维监控、生态运营与专属变现实战
  • Inception 网络:多尺度卷积结构与图像识别应用
  • ComfyUI Manager 插件管理工具安装与使用指南
  • Odoo 模型继承体系详解:BaseModel 到 TransientModel
  • 国产数据库新机遇:电科金仓以融合技术同步全球竞争
  • 基于深度学习的智能害虫识别系统
  • CVPR 2024 论文阅读:Fusion-Mamba 跨模态目标检测
  • JiuwenClaw AI 智能体实战:任务规划与上下文管理
  • WhisperX 快速上手指南:基于 OpenAI Whisper 的语音识别工具
  • Llama-Factory 快速迭代 NLP 模型微调指南
  • DeerFlow 2.0 开源:从研究工具到超级智能体架构

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online