FPGA中的嵌入式块存储器RAM:从原理到实现的完整指南

FPGA中的嵌入式块存储器RAM:从原理到实现的完整指南

文章目录

一、引言:为什么需要RAM?

在前一篇文章中,我们深入探讨了FPGA中ROM的原理与应用。然而,在实际的FPGA系统设计中,很多时候我们需要的是可读可写的存储器,这就是RAM(Random Access Memory,随机存取存储器)。无论是用于数据缓存、帧缓冲还是实时数据存储,RAM都是构建高效FPGA系统不可或缺的组成部分。

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

二、RAM的核心特性与应用场景

1.RAM的三大核心特性
与ROM相比,RAM具有以下显著特性:

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

2.典型应用场景分析
场景一:数据速率匹配缓冲
这是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控制器接口示例 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 );

特点:

两个端口都可独立读写

需要处理读写冲突

适用于复杂的数据共享场景

  1. 关键配置参数详解
    (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 
  1. 字节写使能功能
    字节写使能允许按字节粒度控制数据写入,特别适用于处理不同位宽的数据:
// 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: 不写入任何字节

五、实战案例:基于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 
  1. 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-1023for(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 

Read more

【2025最新保姆级教程】手把手教你用github制作个人主页(申学找工作必备)

【2025最新保姆级教程】手把手教你用github制作个人主页(申学找工作必备)

📌 我的仓库地址,里面也有一部分的教程说明: Yixin0313/personal-homepage-template: Github通用个人主页模板:适用于学术和求职场景 | A general-purpose template: suitable for both academic and professional use. 📌 前言:为什么要用 GitHub Pages 制作个人主页? 在当今竞争激烈的技术领域,拥有一个专业的个人主页能极大提升你的竞争力,尤其是对于申学、找工作的同学来说: ✅ 申学的同学(展示研究、项目、发表论文) ✅ 找工作的程序员(作品集、博客、简历) ✅ 想建立个人网站的开发者 📢 最终效果:你将拥有一个像这样完整的个人网站! 第一步:Fork 我的仓库 1. 访问我的 GitHub 仓库:https://github.com/Yixin0313/personal-homepage-template 2. 点击右上角

By Ne0inhk
无人机助力道路智能养护,基于最新端到端范式YOLO26全系列【n/s/m/l】参数模型开发构建无人机航拍道路交通场景下水泥路面缺陷智能检测识别系统

无人机助力道路智能养护,基于最新端到端范式YOLO26全系列【n/s/m/l】参数模型开发构建无人机航拍道路交通场景下水泥路面缺陷智能检测识别系统

道路养护是保障交通命脉安全的隐形防线,其重要性不亚于道路建设本身。我国每年因道路病害引发的交通事故占比高达12%,及时修复1平方米的早期裂缝可避免后续数万元的修复成本。在这场与道路病害赛跑的战役中,传统养护模式正遭遇前所未有的挑战,而人工智能与无人机技术的融合应用,正在重构道路养护的底层逻辑。 一、传统巡检困境:人力与效率的掣肘 传统道路巡检高度依赖"老师傅"的经验判断,养护工需驾驶巡检车以低于60km/h的速度逐段排查,日均有效巡检里程不足80公里。这种模式存在三重先天缺陷:其一,人工目检存在30%以上的漏检率,尤其在下雨、夜间等恶劣环境下效率骤降;其二,从发现病害到修复的平均响应周期长达72小时,期间病害可能持续扩展;其三,养护工需长时间暴露在车流中作业,存在重大安全隐患。 某市政公司测算显示,要维持100公里高速公路的常规养护,需配置3台巡检车、6名专业人员,年直接成本超过400万元。这种重资产运营模式在应对突发灾害时更显捉襟见肘,暴雨过后常出现数十处积水点,传统巡检团队需3-5天才能完成全域排查。 二、技术赋能:无人机巡检的颠覆性突破 搭载多光谱成像仪与边缘计算模

By Ne0inhk

轻小说机翻机器人:5分钟打造你的日语小说翻译神器

轻小说机翻机器人:5分钟打造你的日语小说翻译神器 【免费下载链接】auto-novel轻小说机翻网站,支持网络小说/文库小说/本地小说 项目地址: https://gitcode.com/GitHub_Trending/au/auto-novel 轻小说机翻机器人是一款开源的日语小说翻译工具,支持网络小说、文库小说和本地小说的全自动翻译处理。作为专业的轻小说翻译解决方案,它能自动抓取日本主流平台内容,提供多引擎翻译服务,并构建完整的阅读生态,让日语阅读不再受语言障碍困扰。 🚀 核心价值:为什么选择轻小说机翻机器人? 全自动小说采集系统 内置对Kakuyomu、小説家になろう等6大日本小说平台的支持,只需输入小说名称或URL,系统即可智能抓取内容并完成翻译。通过crawler/src/lib/domain/目录下的平台适配代码(如kakuyomu.ts、syosetu.ts),实现对不同网站结构的精准解析。 多引擎翻译切换 集成百度翻译、有道翻译、OpenAI类API、Sakura等多种翻译器,满足从快速浏览到深度阅读的不同需求。翻译引擎实现代码位于web/src/do

By Ne0inhk
AiOnly大模型深度测评:调用GPT-5 API+RAG知识库,快速构建智能客服机器人

AiOnly大模型深度测评:调用GPT-5 API+RAG知识库,快速构建智能客服机器人

声明:本测试报告系作者基于个人兴趣及使用场景开展的非专业测评,测试过程中所涉及的方法、数据及结论均为个人观点,不代表任何官方立场或行业标准。 引言 AI 技术加速渗透各行各业的今天,你是否也面临这样的困境:想调用 GPT-5、Claude4.5等顶尖模型却被海外注册、跨平台适配搞得焦头烂额?想快速搭建智能客服、内容生成工具,却因模型接口差异、成本不可控而望而却步?或是作为中小团队,既想享受 AI 红利,又受限于技术门槛和预算压力? AiOnly平台的出现,正是为了打破这些壁垒。 本文将从实战角度出发,带你全方位解锁这个「全球顶尖大模型 MaaS 平台」:从 5 分钟完成注册到 API 密钥创建,从单模型调用到融合 RAG 知识库的智能体开发,然后手把手教你在 Windows 环境部署一个日均成本不足 0.5 元的电商客服机器人。无论你是 AI 开发者、企业运营者,还是想低成本尝试 AI

By Ne0inhk