本帖最后由 正点原子运营 于 2023-11-17 17:58 编辑
第十九章 IP核之双端口RAM实验
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
3)购买链接:https://detail.tmall.com/item_o.htm?id=692712955836
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)FPGA技术交流QQ群:435699340
在“IP核之单端口RAM实验”中,我们成功实现了对单端口RAM IP核的读写操作,本章我们将通过Quartus II软件生成一个双端口的RAM IP核,并对其进行读写操作。
本章包括以下几个部分:
19.1 简介
19.2 实验任务
19.3 硬件设计
19.4 程序设计
19.5 下载验证
 
19.1 简介
我们知道,RAM IP核分为单端口RAM和双端口RAM,即表示RAM IP核有几个读写端口。对于单端口RAM来说,由于读写共用一对地址线,所以没有办法同时读写不同地址的数据;而对于双端口RAM来说,由于读写地址线是分开的,所以可以同时读写不同地址的数据。
双端口RAM又分为简单双端口RAM和真双端口RAM,顾名思义,简单双端口RAM虽然有两个端口,但是一个端口只能用来写,另一个端口只能用来读,所以简单双端口RAM也称为伪双端口RAM。而真双端口RAM是指两个端口都可以用来写或者读,可以理解成具有两个独立的单端口的RAM,一般用于需要多路写入和读出的情况,而不用例化两个单端口的 RAM,在使用上更为方便。
在实际开发应用中,对于单端口RAM、伪双端口RAM和真双端口RAM的选择,需要根据项目需求来选择合适的RAM。对于不需要同时读写RAM的情况,可以选择单端口RAM;对于需要同时读写RAM,但是只需要一路数据写入,一路数据读出的情况,可以选择简单双端口RAM;而对于需要同时读写RAM,有又两个写入或者读出的情况,可以选择真双端口RAM。双端口RAM一般对于异步数据的缓存使用较多,因此本章使用的是简单双端口RAM IP。
下图为简单双单端口RAM的端口框图。
图 19.1.1 简单双单端口RAM的端口框图
简单双单端口RAM的端口描述如下:
wr_data:写数据信号,位宽范围1~1152;
wr_addr:写地址信号,位宽范围5~20;
wr_en:写使能信号,为高时进行写操作,为低时进行读操作;
wr_clk:写时钟信号;
wr_clk_en:写时钟使能信号,为高时对应地址有效,为低时对应地址无效;
wr_rst:写端口复位信号,高有效;
wr_byte_en:Byte Write使能信号,当配置“Enable Byte Write”选项勾选时有效,位宽范围1~128。为高时对应Byte值有效,为低时对应Byte值无效;
wr_addr_strobe:写地址锁存信号,为高时对应地址无效,上一个地址被保持,为低时对应地址有效;
rd_data:读数据信号,位宽范围1~1152;
rd_addr:读地址信号,位宽范围5~20;
rd_clk:读时钟信号;
rd_clk_en:读时钟使能信号,为高是对应地址有效,为低时对应地址无效;
rd_rst:读端口复位信号,高有效;
rd_oce:读数据输出寄存使能信号,为高时对应地址有效,读数据寄存输出,为低时对应地址无效,读数据保持;
rd_addr_strobe:读地址锁存信号,为高时对应地址无效,上一个地址被保持,为低时对应地址有效;
19.2 实验任务
本节实验任务是使用PDS的IP Compiler配置一个简单双端口RAM IP并对该简单双端口RAM进行读写操作,通过PDS与Modelsim联合仿真观察波形是否正确,最后将设计下载到ATK-DFPGL22G开发板中,并使用Inserter对其进行在线调试观察。
19.3 硬件设计
本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设,各端口信号的管脚分配如下表所示:
表 19.3.1IP核之简单双端口RAM实验管脚分配
对应的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}
复制代码
19.4 程序设计
根据实验任务要求和模块化设计的思想,我们需要如下5个模块:简单双端口RAM模块、写RAM模块、读RAM模块、PLL IP核模块以及顶层模块,顶层模块例化其余模块实现前四个模块的数据交互。由于双端口RAM多用于跨时钟域信号的处理,所以本实验我们使用两个不同的时钟分别作为RAM的写时钟和读时钟,这两个时钟由PLL IP核生成,输出的时钟分别是50Mhz和25Mhz。系统的功能框图如下所示:
图 19.4.1 核之双端口RAM IP核系统框图
由上图可知,PLL IP核输出两个时钟,分别作为写RAM模块和读RAM模块的时钟;写RAM模块负责向RAM中写入数据,而读RAM模块负责从RAM中读出数据。和“IP核之单端口RAM实验”不同的是,本次试验只在上电后写入一次,然后不断地从RAM中读出数据。
首先在PDS软件中创建一个名为ip_2port_ram的工程,工程创建完成后,在PDS软件的上方的菜单栏中“Tools”栏中单击“IP Compiler”按钮后弹出的“IP Compiler”窗口如图 19.4.3所示。
图 19.4.2 点击“IP Compiler”
图 19.4.3 “IP Compiler”窗口
本实验使用的IP核路径是Module→Memory→DRM→DRM Base Simple Dual Port RAM IP核,如下图所示。
图 19.4.4 DRM Base Simple Dual Port RAM
点击上图中红框中的IP后如下图所示:
图 19.4.5 DRM Base Simple Dual Port RAM详情页面
Pathname:新建IP核在工程所在路径,这里保持默认即可。
Instance Name:在这里给新建的RAM IP命名,我们这里命名为“ram_2port”。
接下来的IP框里面的内容是该IP的基础信息,例如名称、版本号与所属公司(Pango)。Part框中的信息为工程所使用的芯片的详细信息“PGL22G-6CMBG324”。
点击上图中的“Customize”按钮进入“Customize IP”窗口对RAM IP的参数进行配置,“Customize IP”窗口界面如下图所示:图中的“Add IP”弹窗点击“Yes”后进入“Customize IP”界面进行单口RAM的参数配置,“Customize IP”界面如图 19.4.7所示。
图 19.4.6 “Add IP”弹窗
图 19.4.7 "Customize IP"界面
本实验我们主要进行如下配置:
图 19.4.8 DRM Base Simple Dual Port RAM参数配置界面
Customize IP界面主要包括如下选项:
DRM Resource Usage:选项主要说明DRM资源使用情况。该选项下的“DRM Resource Type”是DRM资源类型,可以设置“AUTO”、“DRM9K”、“DRM18K”三种模式。三种模式可以根据你需要地址宽度与数据宽度来来设置,一般我们设置为“AUTO”模式。
表 19.4.1 9Kb DRM模式下Simple Dual Port RAM模式允许的位宽组合
表 19.4.2 18Kb DRM模式下Simple Dual Port RAM模式允许的位宽组合
Write/Read Port Use Same Data Widt:配置读写端口是否使用混合位宽模式(注:混合位宽时,关闭Address Strobe功能),本实验勾选该选项,使用混合位宽模式,只需要配置写入端口的地址位宽与数据位宽,读端口的地址位宽与数据位宽会与写入端口保持一致。
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功能即不需要勾选该配置。
图 19.4.9 Byte写使能与输入数据的对应关系
Write Port:对写入端口进行配置。Address Width配置地址位宽,Data Width配置数据位宽。地址位宽合法配置范围为5~20,数据位宽合法配置范围为1~1152,但是所占用的DRM9K或者DRM18K个数必须小于总资源个数。本实验我们配置的地址位宽为5,数据位宽为8位。
Enable wr_clk_en Signal:配置是否使能写端口的wr_clk_en信号(注:Clock Enabel与Address Strobe互斥),本实验未使用保持默认不用勾选。
Enable wr_addr_strobe Signal:配置是否使能写端口的wr_addr_strobe信号(注:1.Byte Write 使能时,关闭Address Strobe功能2.混合位宽时,关闭Address Strobe功能3.Clock Enabel与Address Strobe互斥),本实验配置使用了混合位宽模式所以关闭Address Strobe功能,Enable wr_addr_strobe Signal选项保持默认不用勾选。
Read Port:对读出端口进行配置。Address Width配置地址位宽,Data Width配置数据位宽。地址位宽合法配置范围为5~20,数据位宽合法配置范围为1~1152,但是所占用的DRM9K或者DRM18K个数必须小于总资源个数。本实验我们配置使用了混合位宽模式,所以读端口的地址位宽与数据位宽会直接与写端口的配置保持一致。
Enable rd_clk_en Signal:配置是否使能读端口的rd_clk_en信号(注:Clock Enabel与Address Strobe互斥),本实验未使用保持默认不用勾选。
Enable rd_addr_strobe Signal:配置是否使能读端口的rd_addr_strobe信号(注:1.Byte Write 使能时,关闭Address Strobe功能2.混合位宽时,关闭Address Strobe功能3.Clock Enabel与Address Strobe互斥),本实验配置使用了混合位宽模式所以关闭Address Strobe功能,Enable rd_addr_strobe Signal选项保持默认不用勾选。
Enable rd_oce Signal:配置是否使能rd_oce(输出寄存器选项)信号,使能rd_oce信号时,默认且必须使能读端口输出寄存“Enable Output Register”,本实验不需要使能读信号,所以不勾选该选项。
Enable Output Register:输出寄存器选项。如果勾选了“Enable rd_oce Signal”信号,输出寄存器默认是选中状态,作用是打开DRM内部位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得BRM输出的数据延迟一拍,这不利于我们在调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把BRM的数据输出总线连接到了调试的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径,因此这里取消勾选。
Enable Clock Polarity Invert for Output Register:配置是否使能读端口输出时钟极性反向,使能读端口输出时钟极性反向时,默认且必须使能读端口输出寄存(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”即可,如下图所示。
图 19.4.10 点击“Generate”按钮
点击“OK”后打印如下左图信息,至此简单双单口RAM IP核配置成功,并生成ram_2port_tmpl.v文件。例化简单双单口RAM IP时可以使用下图中ram_2port_tmpl.v文件红框中的代码。
图 19.4.11简单双端口RAM IP创建成功
然后如下图所示,关闭“Customize IP”页面与“IP Compiler”页面。
图 19.4.12 关闭创建IP的窗口
之后我们就可以在“Sources”窗口的“Designs”一栏中出现了该IP核“pll_clk”如下图所示。
图 19.4.13 "ram_2port"IP
除此之外,我们再创建一个PLL IP核(命名为pll_clk),共输出两路时钟,时钟频率分别是50Mhz和25Mhz,如下图所示,创建过程此处不再赘述。
图 19.4.14 PLL IP核
接下来我们设计一个verilog文件对ram进行写入数据,文件名为ram_wr.v,编写的verilog代码如下。
- 1 module ram_wr(
- 2 input clk, //时钟信号
- 3 input rst_n, //复位信号,低电平有效
- 4
- 5 //RAM写端口操作
- 6 output ram_wr_en, //ram写使能
- 7 output reg [4:0] ram_wr_addr, //ram写地址
- 8 output reg [7:0] ram_wr_data //ram写数据
- 9 );
- 10
- 11 //reg define
- 12 reg [5:0] wr_cnt; //写计数器
- 13
- 14 //*****************************************************
- 15 //** main code
- 16 //*****************************************************
- 17
- 18 //wr_cnt计数范围在0~31,ram_wr_en为高电平
- 19 assign ram_wr_en = ((wr_cnt>=6'd0) && (wr_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;
- 20
- 21 //写计数器,计数器范围0~63
- 22 always @(posedge clk or negedge rst_n) begin
- 23 if(!rst_n)
- 24 wr_cnt <= 6'd0;
- 25 else if(wr_cnt == 6'd63)
- 26 wr_cnt <= wr_cnt;
- 27 else
- 28 wr_cnt <= wr_cnt + 1'b1;
- 29 end
- 30
- 31 //写计数器范围:0~31,产生ram写数据信号
- 32 always @(posedge clk or negedge rst_n) begin
- 33 if(!rst_n)
- 34 ram_wr_data <= 8'd0;
- 35 else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
- 36 ram_wr_data <= ram_wr_data + 1'b1;
- 37 else
- 38 ram_wr_data <= 8'd0;
- 39 end
- 40
- 41 //写地址信号 范围:0~31
- 42 always @(posedge clk or negedge rst_n) begin
- 43 if(!rst_n)
- 44 ram_wr_addr <= 5'd0;
- 45 else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
- 46 ram_wr_addr <= ram_wr_addr + 1'b1;
- 47 else
- 48 ram_wr_addr <= 5'd0;
- 49 end
- 50
- 51 endmodule
复制代码 模块中定义了一个写计数器(wr_cnt),从0累加至63,此后一直保持在数值63。当计数范围在0~31之间时,向ram中写入数据,而其它计数值时停止写入。实现的功能是向RAM的地址0写入数据0,地址1写入数据1,一直到地址31写入数据31,只在上电后写入一次。
接下来我们设计一个verilog文件来从RAM中读出数据,文件名为ram_rd.v,编写的verilog代码如下。
- 1 module ram_rd(
- 2 input clk, //时钟信号
- 3 input rst_n, //复位信号,低电平有效
- 4
- 5 //RAM读端口操作
- 6 output ram_rd_en, //ram读使能
- 7 output reg [4:0] ram_rd_addr, //ram读地址
- 8 input [7:0] ram_rd_data //ram读数据
- 9 );
- 10
- 11 reg [5:0] rd_cnt; //读控制计数器
- 12
- 13 //*****************************************************
- 14 //** main code
- 15 //*****************************************************
- 16
- 17 //rd_cnt计数范围在0~31,ram_rd_en为高电平
- 18 assign ram_rd_en = ((rd_cnt>=6'd0) && (rd_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;
- 19
- 20 //读控制计数器,计数器范围0~63
- 21 always @(posedge clk or negedge rst_n) begin
- 22 if(!rst_n)
- 23 rd_cnt <= 6'd0;
- 24 else if(rd_cnt == 6'd63)
- 25 rd_cnt <= 6'd0;
- 26 else
- 27 rd_cnt <= rd_cnt + 1'b1;
- 28 end
- 29
- 30 //写地址信号 范围:0~31
- 31 always @(posedge clk or negedge rst_n) begin
- 32 if(!rst_n)
- 33 ram_rd_addr <= 5'd0;
- 34 else if(rd_cnt >= 6'd0 && rd_cnt <= 6'd31)
- 35 ram_rd_addr <= ram_rd_addr + 1'b1;
- 36 else
- 37 ram_rd_addr <= 5'd0;
- 38 end
- 39
- 40 endmodule
复制代码 模块中定义了一个读计数器(rd_cnt),循环的从0累加至63。当计数范围在0~31之间时,从ram中读出数据,而其它计数值时停止读数据。
接下来我们设计一个verilog文件来例化创建的PLL IP核、RAM IP 核、RAM写模块和RAM读模块,文件名为ip_2port_ram.v,编写的verilog代码如下。
- 1 module ip_2port_ram(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n , //系统复位,低电平有效
- 4
- 5 output clk_50m ,
- 6 output clk_25m ,
- 7 output locked ,
- 8 output ram_wr_en ,
- 9 output [4:0] ram_wr_addr ,
- 10 output [7:0] ram_wr_data ,
- 11 output ram_rd_en ,
- 12 output [4:0] ram_rd_addr ,
- 13 output [7:0] ram_rd_data
- 14 );
- 15
- 16 //*****************************************************
- 17 //** main code
- 18 //*****************************************************
- 19 //锁相环模块
- 20 pll_clk u_pll_clk(
- 21 .pll_rst (~sys_rst_n), // input
- 22 .clkin1 (sys_clk), // input
- 23 .pll_lock (locked), // output
- 24 .clkout0 (clk_50m), // output
- 25 .clkout1 (clk_25m) // output
- 26 );
- 27
- 28 //RAM写模块
- 29 ram_wr u_ram_wr(
- 30 .clk (clk_50m),
- 31 .rst_n (sys_rst_n),
- 32
- 33 .ram_wr_en (ram_wr_en ), //ram写使能
- 34 .ram_wr_addr (ram_wr_addr),
- 35 .ram_wr_data (ram_wr_data)
- 36 );
- 37
- 38 ram_2port ram_2port_inst (
- 39 .wr_data (ram_wr_data), // input [7:0] ram写数据
- 40 .wr_addr (ram_wr_addr), // input [4:0] ram写地址
- 41 .wr_en (ram_wr_en ), // input
- 42 .wr_clk (clk_50m ), // input
- 43 .wr_rst (~sys_rst_n ), // input
- 44 .rd_addr (ram_rd_addr), // input [4:0] ram读地址
- 45 .rd_data (ram_rd_data), // output [7:0] ram读数据
- 46 .rd_clk (clk_25m ), // input
- 47 .rd_rst (~sys_rst_n ) // input
- 48 );
- 49
- 50 //RAM读模块
- 51 ram_rd u_ram_rd(
- 52 .clk (clk_25m),
- 53 .rst_n (sys_rst_n),
- 54 .ram_rd_en (ram_rd_en ), //ram读使能
- 55 .ram_rd_addr (ram_rd_addr),
- 56 .ram_rd_data (ram_rd_data)
- 57 );
- 58
- 59 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_2port_ram_tb;
- 3
- 4 reg grs_n;
- 5 //GTP_GRS I_GTP_GRS(
- 6 GTP_GRS GRS_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 reg sys_clk;
- 17 reg sys_rst_n;
- 18
- 19 // Outputs
- 20 wire clk_50m ;
- 21 wire clk_25m ;
- 22 wire locked ;
- 23
- 24 wire wr_en_tb ; //ram写使能
- 25 wire [4:0] wr_addr_tb; //ram写地址
- 26 wire [7:0] wr_data_tb; //ram写数据
- 27 wire rd_en_tb ; //ram读使能
- 28 wire [4:0] rd_addr_tb; //ram读地址
- 29 wire [7:0] rd_data_tb; //ram读数据
- 30
- 31 // Instantiate the Unit Under Test (UUT)
- 32 ip_2port_ram uut (
- 33 .sys_clk (sys_clk ),
- 34 .sys_rst_n (sys_rst_n),
- 35 .clk_50m (clk_50m ),
- 36 .clk_25m (clk_25m ),
- 37 .locked (locked ),
- 38 .wr_en (wr_en_tb ),
- 39 .wr_addr (wr_addr_tb ),
- 40 .wr_data (wr_data_tb ),
- 41 .rd_en (rd_en_tb ),
- 42 .rd_addr (rd_addr_tb ),
- 43 .rd_data (rd_data_tb )
- 44 );
- 45
- 46 initial begin
- 47 // Initialize Inputs
- 48 sys_clk = 0;
- 49 sys_rst_n = 0;
- 50
- 51 // Wait 100 ns for global reset to finish
- 52 #100;
- 53 sys_rst_n=1;
- 54 // Add stimulus here
- 55 end
- 56
- 57 always #10 sys_clk=~sys_clk;
- 58
- 59 endmodule
复制代码 接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。
图 19.4.15 简单双端口RAM写操作波形图
图 19.4.15为简单双端口RAM的写操作仿真波形图,由上图可知,地址和数据初始化赋值为0,当wr_en_tb信号拉高,说明此时是对ram进行写操作。wr_en_tb信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1,我们总共向ram中写入32个数据。
简单双端口RAM读操作仿真波形图如下图所示:
图 19.4.16 单口RAM读操作波形图
由上图可以看到,ram_rd_en信号拉高,说明此时是对ram进行读操作。ram_rd_en(读使能)信号拉高之后,ram_addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据 ram_rd_data在延时1个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的,也就是说,我们创建的RAM IP核从仿真结果上来看是正确的。
接下来再看一张整体的仿真运行图。
图 19.4.17 整体仿真运行图
由上图可知,我们只在开始时写入一次,随后不再写入,而是每隔一段时间读取一次。
19.5 下载验证
新建DebugCore,将ram_wr_en、ram_wr_addr、ram_wr_data、ram_rd_en、ram_rd_addr和ram_rd_data这六个信号添加至观察列表中,新建DebugCore核的方法这里不再赘述。
编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,连接电源线,并打开开发板的电源开关。
点击PDS工具栏的下载按钮,在弹出的Fabric Configuration界面中双击“Boundary Scan”,我们将生成好的sbit流文件下载到开发板中去。
在在线调试配置界面将ram_wr_en信号设置为高电平触发:
图 19.5.1设置触发信号
然后在如下界面点击触发按钮后进入如图 19.5.3等待触发界面:
图 19.5.2 在线调试“Run”
图 19.5.3 等待触发
在等待触发时按下开发板的复位按键,可以看到如下所示一张整体的在线调试运行图,由下图可知,我们只在开始时写入一次,随后不再写入,而是每隔一段时间读取一次。
图 19.5.4 整体的在线调试运行图
下面是放大后简单双端口RAM写操作在Fabric Debugger中观察的波形如下图所示:
图 19.5.5 RAM写操作波形图
wr_en_tb信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1。我们可以发现,上图中的数据变化和在PDS仿真的波形是一致的。
下面是放大后简单双端口RAM读操作在Fabric Debugger中观察的波形如下图所示:
图 19.5.6 RAM读操作波形图
rd_en(读使能)信号拉高之后,rd_addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据ram_rd_data在延时两个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的。
我们可以发现,上图中的数据变化同样和PDS仿真的波形是一致的。本次实验的IP核之简单双端口RAM读写实验验证成功。
|