本帖最后由 正点原子运营 于 2023-11-16 17:38 编辑
第十八章 IP核之单端口RAM实验
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
6)FPGA技术交流QQ群:435699340
RAM的英文全称是Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。本章我们将对PDS软件生成的单口RAM IP核进行读写测试,并向大家介绍Pango 单口RAM IP核的使用方法。 本章包括以下几个部分: 1.1 RAM IP核简介 1.2 实验任务 1.3 硬件设计 1.4 程序设计 1.5 下载验证
1.1 RAM IP核简介Memory IP分为“Distributed RAM”与“DRM”。
DRM DRM(Dedicated RAM Module)既专用RAM模块,DRM Based RAM/FIFO IP是基于DRM设计的IP,通过对DRM的级联调用实现RAM/ROM/FIFO等IP设计。这里主要说明如何用深圳市紫光同创电子有限公司的
Pango Design Suite套件(后文简称PDS)中IP Compiler工具(后文简称IPC)生成并配置此类IP。DRM Based RAM/FIFO IP的分类为: DRMBased Dual Port RAM:基于DRM的双端口RAM  DRMBased Simple Dual Port RAM:基于DRM的简单双端口RAM  DRMBased Single Port RAM:基于DRM的单端口RAM  DRMBased ROM:基于DRM的ROM  DRMBased FIFO:基于DRM的FIFO Logos系列FPGA的DRM有高达18K bits的存储单元并且容量可被独立配置为2个9K bits或者1个18K bits。每个DRM都能支持DP(True Dual Port,双口)RAM模式,同时也以被配置为SP(Single Port,单口)RAM模式,SDP(Simple Dual Port,简单双口)RAM模式,ROM模式,以及可选丢包重发的同步\异步FIFO模式。DRM资源还支持输入寄存器(IR)、输出寄存器(OR)以及Core Latch,这使得DRM级联使用时拥有更加出色的性能表现。DRM的总数取决于Logos系列器件类型。 Logos系列FPGA DRM的原语是各种模式的基础,其原语有两种:GTP_DRM9K和GTP_DRM18K,其支持类型如表 18.1.1所示: 表18.1.1 Logos系列FPGA DRM原语 注:1.所有原语模块在软件安装路径..\arch\vendor\pango\verilog\simulation可找出。 Distributed RAM DistributedRAM IP是紫光同创基于FPGA片内资源设计的IP,适用于全系列FPGA产品,用户可以通过公司PDS(Pango Design Suite)套件中的IPC(IPCompiler)工具完成IP模块的配置和生成。Distributed RAM IP包含5个子IP: Distributed ROM:分布式ROM Distributed Single Port RAM:分布式单口RAM Distributed Simple Dual Port RAM:分布式简单双端口RAM Distributed Shift Register:分布式移位寄存器 Distributed FIFO:分布式FIFO
Pango PGL22G系列的Memory存储单元可以实现各种存储器的功能,PDS软件自带的IP Compiler(IP编译器)已经生成了各种存储器,例如RAM、移位寄存器、ROM以及FIFO缓冲器。 RAM与ROM这两者的区别是RAM是一种随机存取存储器,不仅仅可以存储数据,同时支持对存储的数据进行修改;而ROM是一种只读存储器,也就是说,在正常工作时只能读出数据,而不能写入数据。需要注意的是,配置成RAM或者ROM使用的资源都是FPGA内部的Distributed RAM,只不过配置成ROM时只用到了嵌入式Distributed RAM的读数据端口。本章我们主要介绍使用IPC(IP Compiler)工具生成的单口RAM实现读写的功能。 单端口RAM(DRM Based Single Port RAM)只有一个端口,读/写只能通过这一个端口来进行。单端口RAM只有一组数据总线、地址总线、时钟信号以及其他控制信号。有关DRM的更详细的介绍,请读者参阅Pango官方的手册文档“Logos系列FPGA专用RAM模块(DRM)用户指南(UG020002,Version1.1)”。 DRM BaseSingle Port RAM的单端口RAM的框图如下图所示。 各个端口的功能描述如下: addr:写地址信号。 addr_strobe:写地址选锁存号,高电平对应地址无效,上一个地址被保持,低电平对应地址有效。 wr_data:写数据信号。 rd_data:读数据信号。 wr_en:写使能信号,高电平表示向RAM中写入数据,低电平表示从RAM中读出数据。 clk:RAM的时钟信号。 clk_en:时钟使能信号,高电平对应地址有效,低电平对应地址无效。 rst:复位信号,高有效 wr_byte_en:Byte Write使能信号,当配置“Enable Byte Write”选项勾选时有效,位宽范围1~128。高电平对应Byte值有效,低电平对应Byte值无效。 rd_oce:输出寄存使能信号,高电平对应地址有效,读数据寄存输出,低电平对应地址无效,读数据保持。
1.2 实验任务本节实验任务是使用PDS的IP Compiler配置一个单口RAM IP并对该单口RAM进行读写操作,通过PDS与Modelsim联合仿真观察波形是否正确,最后将设计下载到ATK-DFPGL22G开发板中,并使用Inserter对其进行在线调试观察。
1.3 硬件设计本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设,各端口信号的管脚分配如下表所示:
对应的FDC约束语句如下所示: - define_attribute{p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
- define_attribute{p:sys_clk} {PAP_IO_LOC} {B5}
- define_attribute{p:sys_clk} {PAP_IO_VCCIO} {3.3}
- define_attribute{p:sys_clk} {PAP_IO_STANDARD} {LVCMOS12}
- define_attribute{p:sys_clk} {PAP_IO_NONE} {TRUE}
- define_attribute{p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
- define_attribute{p:sys_rst_n} {PAP_IO_LOC} {G5}
- define_attribute{p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
- define_attribute{p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS12}
- define_attribute{p:sys_rst_n} {PAP_IO_NONE} {TRUE}
复制代码
1.4 程序设计首先在PDS软件中创建一个名为ip_1port_ram的工程,工程创建完成后,在PDS软件的上方的菜单栏中“Tools”栏中单击“IP Compiler”按钮后弹出的“IP Compiler”窗口如图 18.4.2所示。 本实验使用的IP核路径是Module→Memory→DRM→DRM Base Single Port RAM IP核,如下图所示。 图 18.4.3DRM Based Single Port RAM 点击上图中红框中的IP后如下图所示: 图 18.4.4 DRM Based Single Port RAM IP详情页面 Pathname:新建IP核在工程所在路径,这里保持默认即可。 InstanceName:在这里给新建的RAM IP命名,我们这里命名为“ram_1port”。 接下来的IP框里面的内容是该IP的基础信息,例如名称、版本号与所属公司(Pango)。Part框中的信息为工程所使用的芯片的详细信息“PGL22G-6CMBG324”。 点击上图中的“Customize”按钮进入“Customize IP”窗口对RAM IP的参数进行配置,“Customize IP”窗口界面如下图所示:图中的“Add IP”弹窗点击“Yes”后进入“CustomizeIP”界面进行单口RAM的参数配置,“Customize IP”界面如图 18.4.6所示。 本实验我们主要进行如下配置: 图 18.4.7 “DRM BasedSingle Port RAM”参数配置界面 Customize IP界面主要包括如下选项: DRM Resource Usage:选项主要说明DRM资源使用情况。该选项下的“DRM Resource Type”是DRM资源类型,可以设置“AUTO”、“DRM9K”、“DRM18K”三种模式。三种模式可以根据你需要地址宽度与数据宽度来来设置,一般我们设置为“AUTO”模式。 表 18.4.1 9Kb DRM模式Single Port RAM模式和ROM模式列表 表 18.4.2 18Kb DRM模式Single Port RAM模式和ROM模式列表 Enable Byte Write:配置是否使能Byte Write功能,其含义是Byte写使能,也就是以字节为单位写入数据;Byte Write使能时,同时需要配置字节(Byte)的位宽(Byte Size)与字节个数(Byte Numbers),Byte Size可选择8或者9;Byte Numbers即配置所使用的Byte个数(注:Byte Write使能时,关闭Address Strobe功能。)举例说明:输入数据为32-bit,字节的位宽(Byte Size)设置为8bit,那么就需要4-bit Byte写使能信号,这个使能信号与输入数据各位的对应关系如下图所示。从图中不难看出,当we[3]有效时,只会将输入数据的高8-bit写入到目标地址;当we[0]有效时,只会将输入数据的低8-bit写入到目标地址。其实,也就是根据写使能来更新指定地址上原始数据的某些位,本实验不需要使能Byte Write功能即不需要勾选该配置。 图 18.4.8 Byte写使能与输入数据的对应关系 Address and Data Width Config:选项即地址与位宽配置,Address Width配置地址位宽,Data Width配置数据位宽。地址位宽合法配置范围为1~20,数据位宽合法配置范围为1~1152,但是所占用的DRM9K或者DRM18K个数必须小于总资源个数。本实验我们配置的地址位宽为5,数据位宽为8位。 Enable clk_en Signal:配置是否使能clk_en信号(注:Clock Enable与Address Strobe功能互斥),本实验未使用保持默认不用勾选。 Enable Address Strobe Signal:配置是否使能addr_strobe信号(注:1.Byte Write使能时,关闭Address Strobe功能2.Clock Enable与Address Strobe功能互斥), 本实验未使用保持默认不用勾选。 Write Mode:配置写模式。共分为三种模式,分别是NORMAL_WRITE(正常模式)、TRANSPARENT_WRITE(写优先模式)和READ_BEFORE_WRITE(读优先模式)。正常模式指读写分开操作,不能同时进行读写,本实验选择NORMAL_WRITE模式;写优先模式指数据先写入RAM中,然后在下一个时钟输出该数据;读优先模式指数据先写入RAM中,同时输出RAM中同地址的上一次数据。 Enablerd_oce Signal:配置是否使能rd_oce(输出寄存器选项)信号,使能rd_oce信号时,默认且必须使能读端口输出寄存“Enable Output Register”,本实验不需要使能读信号,所以不勾选该选项。 Enable Output Register:输出寄存器选项。如果勾选了“Enable rd_oce Signal”信号,输出寄存器默认是选中状态,作用是打开DRM内部位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得DRM输出的数据延迟一拍,这不利于我们在调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把DRM的数据输出总线连接到了调试的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径,因此这里取消勾选。 Enable Clock Polarity Invert for OutputRegister:配置是否使能读端口输出时钟极性反向,使能读端口输出时钟极性反向时,默认且必须使能读端口输出寄存(Enable Output Register),这里不需要使能读端口输出时钟极即不需要勾选该配置。 Enable Low Power Mode:配置是否使能低功耗模式,本实验不需要配置成低功耗模式,即不勾选使能低功耗模式。 Reset Type:配置复位方式:"ASYNC"异步复位,"SYNC"同步复位,"Sync_Internally "异步复位同步释放,本实验选择异步复位。 Enable Init:配置是否使能对当前RAM进行初始化,这里不需要该配置即不用勾选使能初始化选项。 Init File:配置使能对当前RAM进行初始化,指定初始化文件路径,若不指定,则生成初始值为全“0”的初始化文件.v文件。 File Type:配置初始化文件数据格式:"BIN"二进制,"HEX"十六进制。 至此本实验所需要的单口RAM IP已配置完成,接下来点击“Customize IP”窗口左上角的“Generate”在弹出的“Question”对话框选择“OK”即可,如下图所示。 点击“OK”后打印如下左图信息,至此单口RAM IP核配置成功,并生成ram_1port_tmpl.v文件。例化单口RAM IP时可以使用下图中ram_1port_tmpl.v文件红框中的代码。 然后如下图所示,关闭“Customize IP”页面与“IP Compiler”页面。 之后我们就可以在“Sources”窗口的“Designs”一栏中出现了该IP核“ram_1port”如下图所示。 接下来我们创建一个新的设计文件,命名为ram_rw.v,代码如下: - 1 module ram_rw(
- 2 input clk , //时钟信号
- 3 input rst_n , //复位信号,低电平有效
- 4
- 5 output ram_en , //ram使能信号
- 6 output ram_wea , //ram读写选择
- 7 output reg [4:0 ram_addr , //ram读写地址
- 8 output reg [7:0 ram_wr_data //ram写数据
- 9 );
- 10
- 11 //reg define
- 12 reg [5:0 rw_cnt; //读写控制计数器
- 13
- 14 //*****************************************************
- 15 //** main code
- 16 //*****************************************************
- 17
- 18 //控制RAM使能信号
- 19 assign ram_en = rst_n;
- 20 //rw_cnt计数范围在0~31,写入数据;32~63时,读出数据
- 21 assign ram_wea =(rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;
- 22
- 23 //读写控制计数器,计数器范围0~63
- 24 always @(posedge clk or negedge rst_n) begin
- 25 if(rst_n == 1'b0)
- 26 rw_cnt <= 1'b0;
- 27 else if(rw_cnt == 6'd63)
- 28 rw_cnt <= 1'b0;
- 29 else
- 30 rw_cnt <= rw_cnt + 1'b1;
- 31 end
- 32
- 33 //产生RAM写数据
- 34 always @(posedge clk or negedge rst_n) begin
- 35 if(rst_n == 1'b0)
- 36 ram_wr_data <= 1'b0;
- 37 else if(rw_cnt <= 6'd31) //在计数器的0-31范围内,RAM写地址累加
- 38 ram_wr_data <= ram_wr_data + 1'b1;
- 39 else
- 40 ram_wr_data <= 1'b0 ;
- 41 end
- 42
- 43 //读写地址信号 范围:0~31
- 44 always @(posedge clk or negedge rst_n) begin
- 45 if(rst_n == 1'b0)
- 46 ram_addr <= 1'b0;
- 47 else if(ram_addr ==5'd31)
- 48 ram_addr <= 1'b0;
- 49 else
- 50 ram_addr <= ram_addr + 1'b1;
- 51 end
- 52
- 53 endmodule
复制代码模块中定义了一个读写控制计数器(rw_cnt),当计数范围在0~31之间时,向ram中写入数据;当计数范围在32~63之间时,从ram中读出数据。
接下来我们设计一个verilog文件来实例化创建的单口RAM IP核以及ram_rw模块,文件名为ip_1port_ram.v,编写的verilog代码如下。 - 1 module ip_1port_ram(
- 2 input sys_clk ,
- 3 input sys_rst_n ,
- 4
- 5 //冗余逻辑,仅仅是为了将端口拉出去,方便观察信号
- 6 output ena ,
- 7 output wea ,
- 8 output [4 : 0 addra,
- 9 output [7 : 0 dina ,
- 10 output [7 : 0 douta
- 11 );
- 12
- 13 //*****************************************************
- 14 //** main code
- 15 //*****************************************************
- 16
- 17 ram_1port u_ram_1port (
- 18 .wr_data (dina ), // input [7:0]
- 19 .addr (addra ), // input [4:0]
- 20 .wr_en (wea ), // input
- 21 .clk (sys_clk ), // input
- 22 .rst (~sys_rst_n), //input
- 23 .rd_data (douta ) // output [7:0]
- 24 );
- 25
- 26 ram_rw u_ram_rw (
- 27 .clk (sys_clk ),
- 28 .rst_n (sys_rst_n),
- 29 .ram_en (ena ),
- 30 .ram_wea (wea ),
- 31 .ram_addr (addra ),
- 32 .ram_wr_data (dina )
- 33 );
- 34
- 35 endmodule
复制代码程序中例化了ram_rw模块和单口ram IP核,其中ram_rw模块负责产生对ram IP核读/写所需的所有数据、地址以和读写使能信号,同时从ram IP读出的数据也连接至ram_rw模块。
接下来对单口RAM IP核进行仿真,来验证对单口RAM的读写操作是否正确。ip_1port_ram_tb仿真文件源代码如下: - 1 `timescale 1ns/ 1ps
- 2 module ip_1port_ram_tb;
- 3
- 4 reg grs_n;
- 5 //GTP_GRS I_GTP_GRS(
- 6 GTP_GRSGRS_INST(
- 7 .GRS_N(grs_n)
- 8 );
- 9
- 10 initial begin
- 11 grs_n =1'b0;
- 12 #5000 grs_n = 1'b1;
- 13 end
- 14
- 15 // Inputs
- 16 regsys_clk;
- 17 regsys_rst_n;
- 18
- 19 // Outputs
- 20 wire ena_tb;
- 21 wire wea_tb;
- 22 wire [4:0 addra_tb;
- 23 wire [7:0 dina_tb;
- 24 wire [7:0 douta_tb;
- 25 // Instantiate the Unit Under Test (UUT)
- 26 ip_1port_ram uut(
- 27 .sys_clk (sys_clk ),
- 28 .sys_rst_n (sys_rst_n),
- 29 .ena (ena_tb ),
- 30 .wea (wea_tb ),
- 31 .addra (addra_tb ),
- 32 .dina (dina_tb ),
- 33 .douta (douta_tb)
- 34 );
- 35
- 36 initial begin
- 37 // Initialize Inputs
- 38 sys_clk =0;
- 39 sys_rst_n =0;
- 40
- 41 // Wait 100 ns for global reset to finish
- 42 #100;
- 43 sys_rst_n=1;
- 44 // Add stimulus here
- 45 end
- 46
- 47 always #10sys_clk=~sys_clk;
- 48
- 49 endmodule
复制代码代码第4行到第13行例化了一个GTP_GRS模块 ,GTP_GRS是一个全局复位,在FIFO或者RAMIP中使用了这个全局复位,所以使用了FIFO或者RAMIP的文件的仿真代码里面就需要对GTP_GRS模块进行例化,不然联合仿真会报错。 接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。 图 18.4.14为单口RAM的写操作仿真波形图,由上图可知,地址和数据初始化赋值为0,当wea_tb信号拉高,说明此时是对ram进行写操作。wea_tb信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1,我们总共向ram中写入32个数据。 单口RAM读操作仿真波形图如下图所示: 由上图可知,ram_wea信号拉低,说明此时是对ram进行读操作。ram_wea信号拉低之后,ram_addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据ram_rd_data在延时1个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的,也就是说,我们创建的单口RAM IP核从仿真结果上来看是正确的。 接下来新建DebugCore,将ena、wea、addra、dina和dout信号添加至观察列表中,新建DebugCore核的方法这里不再赘述。
1.5 下载验证编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,连接电源线,并打开开发板的电源开关。 点击PDS工具栏的下载按钮,在弹出的Fabric Configuration界面中双击“Boundary Scan”,我们将生成好的sbit流文件下载到开发板中去。 单口RAM写操作在Fabric Debugger中观察的波形如下图所示: 图 18.5.1 RAM写操作波形图 wea信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1。我们可以发现,上图中的数据变化和在PDS仿真的波形是一致的。 单口RAM读操作在Fabric Debugger中观察的波形如下图所示: 图 18.5.2 RAM读操作波形图 wea(写使能)信号拉低之后进行读使能,addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据douta在延时1个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的。我们可以发现,上图中的数据变化同样和PDS仿真的波形是一致的。本次实验的IP核之单口RAM读写实验验证成功。 |