网站 备案规定,可以做分销的网站,wordpress仿京东,做南美生意做什么网站好高云FPGA单端口RAM IP核配置与读写验证实战 最近在用高云#xff08;Gowin#xff09;的FPGA做一个小项目#xff0c;需要用到内部的RAM来缓存一些数据。很多刚开始接触高云FPGA的朋友可能会觉得IP核配置有点复杂#xff0c;仿真和上板验证的流程也不太熟悉。今天我就以最常…高云FPGA单端口RAM IP核配置与读写验证实战最近在用高云Gowin的FPGA做一个小项目需要用到内部的RAM来缓存一些数据。很多刚开始接触高云FPGA的朋友可能会觉得IP核配置有点复杂仿真和上板验证的流程也不太熟悉。今天我就以最常用的单端口RAMSP RAM为例手把手带大家走一遍从IP核配置、Verilog代码编写、ModelSim仿真到上板用在线逻辑分析仪ILA抓波形的完整流程。跟着做一遍你就能彻底掌握高云FPGA里RAM的使用方法了。1. RAM是什么为什么FPGA里需要它咱们先花几分钟搞清楚RAM到底是干嘛的这样后面配置的时候心里才有底。RAM的全称是Random Access Memory翻译过来叫随机存取存储器。这个名字听起来有点唬人其实它的核心特点就两个能读能写、可以随便访问任何一个地址。你可以把它想象成一个有很多小格子的储物柜每个格子地址里都能放东西数据也能随时把东西拿出来而且去哪个格子拿东西速度都差不多。在FPGA的设计里RAM主要分两大类用途完全不同静态RAMSRAM 这是我们今天要用的它直接做在FPGA芯片的硅片里是FPGA的“硬”资源。它的特点是速度快但容量不大。高云FPGA里的Block Memory块RAM就属于这一类。我们做数字逻辑设计时经常用它来做数据缓存、FIFO先入先出队列、或者一些查找表非常方便。动态RAMDRAM 比如大家电脑里用的DDR内存条。它的特点是容量巨大但速度相对慢而且需要复杂的控制器来定时刷新数据。FPGA外接的SDRAM、DDR芯片就属于这类一般用来存大量数据比如视频的一帧图像。今天咱们聚焦在FPGA内部的Block Memory资源上学习怎么把它配置成一个单端口RAM来用。单端口顾名思义就是同一时间只能进行一种操作要么读要么写。虽然不能同时读写但在很多只需要缓存或简单存储的场景下它完全够用而且节省资源。2. 实战目标与设计思路咱们这次要完成一个具体的任务用高云云源软件生成一个单端口RAM的IP核然后写一个控制模块按顺序往RAM里写50个数据再按顺序把它们读出来最后通过仿真和上板验证功能是否正确。整个系统的框图很简单就两部分单端口RAM IP核 由高云云源软件生成负责数据的存储。读写控制模块 我们自己用Verilog写负责产生正确的地址、数据、读写使能信号去“指挥”RAM IP核工作。它们之间的配合关系我画了个简单的时序图帮助大家理解。我们这次会用到两种模式写模式 用Normal正常模式。当写使能信号WRE拉高时数据DI在时钟CLK的上升沿被写入到地址AD指定的位置。读模式 用Bypass旁路模式。在这种模式下当你给出一个读地址后读出的数据DO会晚一个时钟周期才出现在输出端口上。这是一个很重要的细节写代码和看波形的时候要特别注意。注意RAM IP核还有其他工作模式如流水线读模式、通写模式等它们的时序略有不同。具体可以参考高云的官方手册UG285咱们这次先把最常用的两种模式搞明白。3. 手把手配置单端口RAM IP核理论说完了咱们打开高云云源软件Gowin开始实操。3.1 找到并启动IP核生成工具在软件顶部的菜单栏点击Tools - IP Core Generator。也可以在工具栏找到那个像芯片一样的快捷图标点它也一样。3.2 理解IP核配置界面在弹出的IP核列表中找到Memory分类下的Block Memory然后选择Single Port RAM (SP)双击打开配置界面。界面里选项不少别慌咱们一个个来。1. 设置存储器的“大小”这是最关键的一步决定了你的RAM能存多少东西。Address Depth地址深度 可以理解为储物柜有多少个格子。我们填50表示有50个存储单元。Data Width数据宽度 每个格子里能放多“宽”的数据单位是比特bit。我们填8表示每个单元能存一个8位的数据范围0-255。这里有个限制要注意对于GW2A-18C这款芯片地址深度 * 数据宽度的结果不能超过847872。咱们的50 * 8 400远远小于这个限制所以没问题。2. 选择读写模式Read Mode读模式 选择Bypass。这就是前面说的旁路模式读数据延迟一个周期输出。Write Mode写模式 选择Normal。这就是正常写模式。3. 其他配置复位模式 可以根据需要选择同步复位Synchronous或异步复位Asynchronous。通常选同步复位更常见。资源估算 配置过程中可以随时点击Calculate按钮软件会帮你估算这个RAM会消耗多少LUT、DFF等逻辑资源。所有配置确认无误后点击OK。软件会生成一个.v文件比如Gowin_SP.v这就是我们需要的RAM IP核的Verilog模块了。记得把它添加到你的工程中。3.3 认识IP核的“接口”生成的IP核就像一个黑盒子我们通过它外面露出的“引脚”端口来控制它。单端口RAM的主要端口如下端口名方向描述DO输出数据输出信号读出的数据从这里出来。DI输入数据输入信号要写入的数据从这里进去。AD输入地址输入信号告诉RAM你要读写哪个“格子”。CLK输入时钟输入所有操作都在它的上升沿发生。CE输入时钟使能高电平时RAM才工作。通常直接接高电平1‘b1。OCE输入输出时钟使能主要用于Pipeline模式Bypass模式下无效我们也接高电平。WRE输入写使能信号这是关键为1时执行写操作为0时执行读操作。RESET输入复位信号高电平有效。BLKSEL输入块选择信号当需要把多个RAM拼成更大容量时才用我们暂时接高电平。4. 编写Verilog控制模块IP核准备好了它自己不会动需要我们来“驱动”它。接下来我们写一个控制模块让它按照我们的要求先写50个数再读50个数去产生信号。4.1 读写控制模块single_ram_rw.v这个模块是整个设计的“大脑”。它的思路是用一个计数器计数到1000-99。前一半时间0-49我们用来写数据后一半时间50-99用来读数据。module single_ram_rw( input ram_clk, // RAM工作时钟 input ram_rst_n, // 复位信号低电平有效 output reg ram_rw_en, // RAM读写使能 (1:写, 0:读) output reg [5:0] ram_addr, // RAM地址 (0-496位宽足够) output reg [7:0] ram_wr_data // 要写入RAM的数据 ); parameter RAM_RW_COUNT_MAX 7d100; // 总计数周期写50 读50 reg [6:0] ram_rw_count; // 读写控制计数器 // 计数器循环计数 0 - 99 always (posedge ram_clk or negedge ram_rst_n) begin if(!ram_rst_n) ram_rw_count 7d0; else if(ram_rw_count (RAM_RW_COUNT_MAX - 7d1)) ram_rw_count ram_rw_count 7d1; else ram_rw_count 7d0; end // 产生读写使能信号前50个周期为写后50个周期为读 always (*) begin if(!ram_rst_n) ram_rw_en 1d0; else if(ram_rw_count ((RAM_RW_COUNT_MAX / 2) - 7d1)) // 0-49 ram_rw_en 1d1; // 写使能 else // 50-99 ram_rw_en 1d0; // 读使能 end // 产生要写入的数据在写周期内数据从0开始累加 always (posedge ram_clk or negedge ram_rst_n) begin if(!ram_rst_n) ram_wr_data 8d0; else if( ram_rw_count (RAM_RW_COUNT_MAX / 2) - 7d1 ) // 0-48 ram_wr_data ram_wr_data 8d1; // 每个时钟加1 else ram_wr_data 8d0; // 进入读周期后写数据归零实际已不关心 end // 产生地址信号无论是写还是读地址都从0累加到49 always (posedge ram_clk or negedge ram_rst_n) begin if(!ram_rst_n) ram_addr 6d0; else if(ram_addr (RAM_RW_COUNT_MAX / 2) - 7d1) // 地址在0-48范围内 ram_addr ram_addr 6d1; // 地址加1 else ram_addr 6d0; // 地址达到49后回到0 end endmodule代码关键点解析计数器 (ram_rw_count) 它像一个大节拍器控制着整个读写流程的切换。读写使能 (ram_rw_en) 完全由计数器的值决定。这是一个组合逻辑always (*)意味着计数器一变它马上跟着变。写数据 (ram_wr_data) 只在写周期内变化0-48从0开始每个时钟周期加1。这样我们就会往地址0写入0地址1写入1...地址49写入49。地址 (ram_addr) 在读写周期内都持续累加。这意味着我们按顺序写入也按相同的顺序读出。4.2 顶层模块single_ram_top.v一个好的设计习惯是模块化。我们把IP核和控制器分开然后用一个顶层模块把它们“连接”起来。这个顶层文件就像项目的总接线图。module single_ram_top( input sys_clk, // 系统时钟 input sys_rst_n, // 系统复位低电平有效 output ram_rw_en, // 连接到IP核的写使能 output [5:0] ram_addr, // 连接到IP核的地址 output [7:0] ram_wr_data, // 连接到IP核的写数据 output [7:0] ram_rd_data // 从IP核读出的数据 ); // 例化单端口RAM IP核 Gowin_SP u_Gowin_SP( .dout(ram_rd_data), // 输出读出的数据 .clk(sys_clk), // 输入时钟 .oce(1d1), // 输入输出使能常开 .ce(1d1), // 输入时钟使能常开 .reset(~sys_rst_n), // 输入复位注意IP核是高有效我们模块是低有效所以取反 .wre(ram_rw_en), // 输入写使能 .ad(ram_addr), // 输入地址 .din(ram_wr_data) // 输入要写入的数据 ); // 例化我们写的读写控制模块 single_ram_rw u_single_ram_rw( .ram_clk(sys_clk), .ram_rst_n(sys_rst_n), .ram_rw_en(ram_rw_en), .ram_addr(ram_addr), .ram_wr_data(ram_wr_data) ); endmodule连接要点我们把sys_clk和sys_rst_n同时给了IP核和控制模块让它们同步工作。控制模块产生的ram_rw_en、ram_addr、ram_wr_data直接连到了IP核对应的输入端口。IP核的输出dout我们重命名为ram_rd_data就是读出的数据会输出到顶层模块的端口方便我们观察。特别注意复位我们自定义模块的sys_rst_n是低电平有效而IP核的reset端口是高电平有效。所以在连接时我们用~sys_rst_n取反来匹配。5. ModelSim仿真验证代码写完了先别急着上板用ModelSim仿真看看逻辑对不对。这是排查问题最省时间的方法。5.1 编写仿真测试文件single_ram_tb.v仿真文件主要做两件事产生时钟和复位信号例化我们的顶层模块。timescale 10ns / 10ns // 时间单位/精度 module single_ram_tb(); // **重要高云IP核仿真必须添加的语句** // 这个GSR是全局复位/置位单元仿真某些IP核时必须例化否则会报错。 GSR GSR(.GSRI(1b1)); // 定义激励信号 reg sys_clk; reg sys_rst_n; // 定义观察信号 wire ram_rw_en; wire [5:0] ram_addr; wire [7:0] ram_wr_data; wire [7:0] ram_rd_data; // 生成50MHz时钟 (周期20ns) always #10 sys_clk ~sys_clk; // 产生复位信号 initial begin sys_clk 1d0; sys_rst_n 1d0; // 初始复位 #200; // 等待200个时间单位 sys_rst_n 1d1; // 撤销复位 #20000; // 仿真运行一段时间 $stop; // 结束仿真 end // 例化被测设计我们的顶层模块 single_ram_top u_single_ram_top( .sys_clk(sys_clk), .sys_rst_n(sys_rst_n), .ram_rw_en(ram_rw_en), .ram_addr(ram_addr), .ram_wr_data(ram_wr_data), .ram_rd_data(ram_rd_data) ); endmodule5.2 分析仿真波形在ModelSim中运行仿真然后把相关的信号sys_clk,sys_rst_n,ram_rw_en,ram_addr,ram_wr_data,ram_rd_data加到波形窗口里观察。看波形的关键点写操作阶段(ram_rw_en 1)复位撤销后ram_rw_en立刻变高进入写模式。观察ram_addr和ram_wr_data它们应该从0开始在每个时钟上升沿同步加1。这验证了我们的控制逻辑地址0写入数据0地址1写入数据1...读操作阶段(ram_rw_en 0)当计数器走到50ram_rw_en拉低进入读模式。此时ram_addr会再次从0开始累加。重点看ram_rd_data你会发现当ram_addr变为0时ram_rd_data并没有立刻变成我们之前写入的0而是等到下一个时钟周期才变成0。这就是我们之前提到的Bypass模式下的一个时钟周期延迟。之后ram_rd_data会依次输出1, 2, 3...与我们写入的数据完全一致。如果波形符合上述描述恭喜你仿真通过了逻辑设计基本没问题。6. 上板与在线逻辑分析仪验证仿真通过后就可以把设计放到真正的FPGA开发板上跑一下了。高云FPGA内置了在线逻辑分析仪ILA可以像示波器一样抓取芯片内部信号的实时波形非常强大。6.1 引脚约束首先需要告诉软件你的时钟和复位按键接在FPGA的哪个物理引脚上。在云源软件的“I/O Constraints”界面进行分配。例如信号方向引脚电平标准sys_clk输入T7LVCMOS33sys_rst_n输入F10LVCMOS33提示ram_rw_en、ram_addr等信号是内部信号不需要绑定到外部引脚我们通过ILA来观察它们。6.2 使用ILA抓取波形创建ILA IP核 在IP Core Generator里找到“Debug Verification”下的“ILAIntegrated Logic Analyzer”。添加探测信号 在ILA配置中添加你想要观察的信号比如ram_rw_en、ram_addr、ram_wr_data、ram_rd_data。设置合适的采样深度和触发条件例如我们可以设置当ram_rw_en下降沿时触发抓波。综合与下载 完成所有约束和ILA添加后进行综合、布局布线生成比特流文件然后下载到FPGA中。运行抓波 打开云源软件中的“Programmer”或相关调试界面连接板卡。启动ILA功能点击运行。我们的控制逻辑会自动开始工作当触发条件满足时ILA就会捕获一段时间的波形。对比结果 将ILA抓到的波形与之前ModelSim的仿真波形进行对比。你应该看到完全一致的现象写阶段地址数据同步增加读阶段读出的数据延迟一个周期出现且数值正确。到这一步就完成了从设计到硬件验证的完整闭环。这个实战流程虽然针对的是单端口RAM但思路是通用的。掌握了它你就能举一反三去配置和使用高云FPGA的其他IP核了。最重要的是养成“设计-仿真-上板验证”的习惯这能让你在真正的项目开发中少走很多弯路。