OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1140|回复: 0

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第十九章 IP核之双端口RAM实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4666
金钱
4666
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-11-17 17:59:33 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 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

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png
在“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的端口框图。
image001.png

图 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 硬件设计
本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设,各端口信号的管脚分配如下表所示:
QQ截图20231117174946.png

表 19.3.1IP核之简单双端口RAM实验管脚分配

对应的FDC约束语句如下所示:
  1. define_attribute {p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
  2. define_attribute {p:sys_clk} {PAP_IO_LOC} {B5}
  3. define_attribute {p:sys_clk} {PAP_IO_VCCIO} {3.3}
  4. define_attribute {p:sys_clk} {PAP_IO_STANDARD} {LVCMOS12}
  5. define_attribute {p:sys_clk} {PAP_IO_NONE} {TRUE}
  6. define_attribute {p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
  7. define_attribute {p:sys_rst_n} {PAP_IO_LOC} {G5}
  8. define_attribute {p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
  9. define_attribute {p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS12}
  10. define_attribute {p:sys_rst_n} {PAP_IO_NONE} {TRUE}
复制代码

19.4 程序设计
根据实验任务要求和模块化设计的思想,我们需要如下5个模块:简单双端口RAM模块、写RAM模块、读RAM模块、PLL IP核模块以及顶层模块,顶层模块例化其余模块实现前四个模块的数据交互。由于双端口RAM多用于跨时钟域信号的处理,所以本实验我们使用两个不同的时钟分别作为RAM的写时钟和读时钟,这两个时钟由PLL IP核生成,输出的时钟分别是50Mhz和25Mhz。系统的功能框图如下所示:
image003.png

图 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所示。
image005.png

图 19.4.2 点击“IP Compiler”



image007.png

图 19.4.3 “IP Compiler”窗口



本实验使用的IP核路径是Module→Memory→DRM→DRM Base Simple Dual Port RAM IP核,如下图所示。
image009.png

图 19.4.4 DRM Base Simple Dual Port RAM

点击上图中红框中的IP后如下图所示:
image011.png

图 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所示。
image013.png

图 19.4.6 “Add IP”弹窗



image015.png

图 19.4.7 "Customize IP"界面

本实验我们主要进行如下配置:
image017.png

图 19.4.8 DRM Base Simple Dual Port RAM参数配置界面

Customize IP界面主要包括如下选项:
DRM Resource Usage:选项主要说明DRM资源使用情况。该选项下的“DRM Resource Type”是DRM资源类型,可以设置“AUTO”、“DRM9K”、“DRM18K”三种模式。三种模式可以根据你需要地址宽度与数据宽度来来设置,一般我们设置为“AUTO”模式。
image019.png

表 19.4.1 9Kb DRM模式下Simple Dual Port RAM模式允许的位宽组合

image021.png

表 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功能即不需要勾选该配置。
QQ截图20231117175416.png

图 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”即可,如下图所示。
image023.png

图 19.4.10 点击“Generate”按钮

点击“OK”后打印如下左图信息,至此简单双单口RAM IP核配置成功,并生成ram_2port_tmpl.v文件。例化简单双单口RAM IP时可以使用下图中ram_2port_tmpl.v文件红框中的代码。
image025.png

图 19.4.11简单双端口RAM IP创建成功

然后如下图所示,关闭“Customize IP”页面与“IP Compiler”页面。
image027.png

图 19.4.12 关闭创建IP的窗口

之后我们就可以在“Sources”窗口的“Designs”一栏中出现了该IP核“pll_clk”如下图所示。
image029.png

图 19.4.13 "ram_2port"IP

除此之外,我们再创建一个PLL IP核(命名为pll_clk),共输出两路时钟,时钟频率分别是50Mhz和25Mhz,如下图所示,创建过程此处不再赘述。
image031.png

图 19.4.14 PLL IP核

接下来我们设计一个verilog文件对ram进行写入数据,文件名为ram_wr.v,编写的verilog代码如下。
  1. 1  module ram_wr(
  2. 2      input            clk,           //时钟信号
  3. 3      input            rst_n,         //复位信号,低电平有效
  4. 4                                      
  5. 5      //RAM写端口操作                 
  6. 6      output           ram_wr_en,     //ram写使能
  7. 7      output reg [4:0] ram_wr_addr,   //ram写地址        
  8. 8      output reg [7:0] ram_wr_data    //ram写数据
  9. 9  );
  10. 10
  11. 11 //reg define
  12. 12 reg  [5:0]  wr_cnt;                 //写计数器
  13. 13
  14. 14 //*****************************************************
  15. 15 //**                    main code
  16. 16 //*****************************************************
  17. 17
  18. 18 //wr_cnt计数范围在0~31,ram_wr_en为高电平
  19. 19 assign ram_wr_en = ((wr_cnt>=6'd0) && (wr_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;
  20. 20
  21. 21 //写计数器,计数器范围0~63
  22. 22 always @(posedge clk or negedge rst_n) begin
  23. 23     if(!rst_n)
  24. 24         wr_cnt <= 6'd0;
  25. 25     else if(wr_cnt == 6'd63)
  26. 26         wr_cnt <= wr_cnt;
  27. 27     else   
  28. 28         wr_cnt <= wr_cnt + 1'b1;
  29. 29 end
  30. 30
  31. 31 //写计数器范围:0~31,产生ram写数据信号
  32. 32 always @(posedge clk or negedge rst_n) begin
  33. 33     if(!rst_n)
  34. 34         ram_wr_data <= 8'd0;
  35. 35     else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)  
  36. 36         ram_wr_data <= ram_wr_data + 1'b1;
  37. 37     else
  38. 38         ram_wr_data <= 8'd0;
  39. 39 end        
  40. 40
  41. 41 //写地址信号 范围:0~31        
  42. 42 always @(posedge clk or negedge rst_n) begin
  43. 43     if(!rst_n)        
  44. 44         ram_wr_addr <= 5'd0;
  45. 45     else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
  46. 46         ram_wr_addr <= ram_wr_addr + 1'b1;
  47. 47     else
  48. 48         ram_wr_addr <= 5'd0;
  49. 49 end        
  50. 50
  51. 51 endmodule
复制代码
模块中定义了一个写计数器(wr_cnt),从0累加至63,此后一直保持在数值63。当计数范围在0~31之间时,向ram中写入数据,而其它计数值时停止写入。实现的功能是向RAM的地址0写入数据0,地址1写入数据1,一直到地址31写入数据31,只在上电后写入一次。
接下来我们设计一个verilog文件来从RAM中读出数据,文件名为ram_rd.v,编写的verilog代码如下。
  1. 1  module ram_rd(
  2. 2      input            clk,           //时钟信号
  3. 3      input            rst_n,         //复位信号,低电平有效
  4. 4                                      
  5. 5      //RAM读端口操作               
  6. 6      output           ram_rd_en,     //ram读使能
  7. 7      output reg [4:0] ram_rd_addr,   //ram读地址        
  8. 8      input      [7:0] ram_rd_data    //ram读数据
  9. 9  );
  10. 10
  11. 11 reg  [5:0]  rd_cnt;                 //读控制计数器
  12. 12
  13. 13 //*****************************************************
  14. 14 //**                    main code
  15. 15 //*****************************************************
  16. 16
  17. 17 //rd_cnt计数范围在0~31,ram_rd_en为高电平
  18. 18 assign ram_rd_en = ((rd_cnt>=6'd0) && (rd_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;
  19. 19
  20. 20 //读控制计数器,计数器范围0~63
  21. 21 always @(posedge clk or negedge rst_n) begin
  22. 22     if(!rst_n)
  23. 23         rd_cnt <= 6'd0;
  24. 24     else if(rd_cnt == 6'd63)
  25. 25         rd_cnt <= 6'd0;
  26. 26     else   
  27. 27         rd_cnt <= rd_cnt + 1'b1;
  28. 28 end      
  29. 29
  30. 30 //写地址信号 范围:0~31        
  31. 31 always @(posedge clk or negedge rst_n) begin
  32. 32     if(!rst_n)        
  33. 33         ram_rd_addr <= 5'd0;
  34. 34     else if(rd_cnt >= 6'd0 && rd_cnt <= 6'd31)
  35. 35         ram_rd_addr <= ram_rd_addr + 1'b1;
  36. 36     else
  37. 37         ram_rd_addr <= 5'd0;
  38. 38 end        
  39. 39
  40. 40 endmodule
复制代码
模块中定义了一个读计数器(rd_cnt),循环的从0累加至63。当计数范围在0~31之间时,从ram中读出数据,而其它计数值时停止读数据。
接下来我们设计一个verilog文件来例化创建的PLL IP核、RAM IP 核、RAM写模块和RAM读模块,文件名为ip_2port_ram.v,编写的verilog代码如下。
  1. 1  module ip_2port_ram(
  2. 2      input               sys_clk        ,  //系统时钟
  3. 3      input               sys_rst_n      ,  //系统复位,低电平有效
  4. 4      
  5. 5      output              clk_50m        ,
  6. 6      output              clk_25m        ,
  7. 7      output              locked         ,
  8. 8      output              ram_wr_en      ,
  9. 9      output    [4:0]     ram_wr_addr    ,
  10. 10     output    [7:0]     ram_wr_data    ,
  11. 11     output              ram_rd_en      ,
  12. 12     output    [4:0]     ram_rd_addr    ,
  13. 13     output    [7:0]     ram_rd_data
  14. 14     );
  15. 15   
  16. 16 //*****************************************************
  17. 17 //**                    main code
  18. 18 //*****************************************************
  19. 19 //锁相环模块
  20. 20 pll_clk u_pll_clk(
  21. 21     .pll_rst     (~sys_rst_n),   // input
  22. 22     .clkin1      (sys_clk),      // input
  23. 23     .pll_lock    (locked),       // output
  24. 24     .clkout0     (clk_50m),      // output
  25. 25     .clkout1     (clk_25m)       // output
  26. 26     );
  27. 27
  28. 28 //RAM写模块
  29. 29 ram_wr u_ram_wr(
  30. 30     .clk           (clk_50m),
  31. 31     .rst_n         (sys_rst_n),
  32. 32      
  33. 33     .ram_wr_en     (ram_wr_en  ), //ram写使能
  34. 34     .ram_wr_addr   (ram_wr_addr),
  35. 35     .ram_wr_data   (ram_wr_data)
  36. 36     );
  37. 37
  38. 38 ram_2port ram_2port_inst (
  39. 39   .wr_data    (ram_wr_data),    // input [7:0]  ram写数据
  40. 40   .wr_addr    (ram_wr_addr),    // input [4:0]  ram写地址
  41. 41   .wr_en      (ram_wr_en  ),    // input        
  42. 42   .wr_clk     (clk_50m    ),    // input
  43. 43   .wr_rst     (~sys_rst_n ),    // input
  44. 44   .rd_addr    (ram_rd_addr),    // input [4:0]  ram读地址
  45. 45   .rd_data    (ram_rd_data),    // output [7:0] ram读数据
  46. 46   .rd_clk     (clk_25m    ),    // input
  47. 47   .rd_rst     (~sys_rst_n )     // input
  48. 48 );
  49. 49
  50. 50 //RAM读模块   
  51. 51 ram_rd u_ram_rd(
  52. 52     .clk           (clk_25m),
  53. 53     .rst_n         (sys_rst_n),
  54. 54     .ram_rd_en     (ram_rd_en  ), //ram读使能
  55. 55     .ram_rd_addr   (ram_rd_addr),
  56. 56     .ram_rd_data   (ram_rd_data)
  57. 57     );
  58. 58
  59. 59 endmodule
复制代码
程序中例化了ram_rw模块和单口ram IP核,其中ram_rw模块负责产生对ram IP核读/写所需的所有数据、地址以和读写使能信号,同时从ram IP读出的数据也连接至ram_rw模块。
接下来对单口RAM IP核进行仿真,来验证对单口RAM的读写操作是否正确。ip_1port_ram_tb仿真文件源代码如下:
  1. 1  `timescale 1ns / 1ps
  2. 2  module ip_2port_ram_tb;
  3. 3  
  4. 4  reg  grs_n;
  5. 5  //GTP_GRS I_GTP_GRS(
  6. 6      GTP_GRS GRS_INST(
  7. 7      .GRS_N (grs_n)
  8. 8      );
  9. 9      
  10. 10     initial begin
  11. 11     grs_n = 1'b0;
  12. 12     #5000 grs_n = 1'b1;
  13. 13     end
  14. 14
  15. 15     // Inputs
  16. 16     reg sys_clk;
  17. 17     reg sys_rst_n;
  18. 18
  19. 19     // Outputs
  20. 20     wire          clk_50m ;
  21. 21     wire          clk_25m ;
  22. 22     wire          locked  ;
  23. 23     
  24. 24     wire          wr_en_tb  ; //ram写使能
  25. 25     wire [4:0]    wr_addr_tb; //ram写地址
  26. 26     wire [7:0]    wr_data_tb; //ram写数据
  27. 27     wire          rd_en_tb  ; //ram读使能
  28. 28     wire [4:0]    rd_addr_tb; //ram读地址
  29. 29     wire [7:0]    rd_data_tb; //ram读数据
  30. 30
  31. 31     // Instantiate the Unit Under Test (UUT)
  32. 32     ip_2port_ram uut (
  33. 33     .sys_clk      (sys_clk  ),
  34. 34     .sys_rst_n    (sys_rst_n),
  35. 35     .clk_50m      (clk_50m  ),
  36. 36     .clk_25m      (clk_25m  ),
  37. 37     .locked       (locked   ),
  38. 38     .wr_en        (wr_en_tb  ),
  39. 39     .wr_addr      (wr_addr_tb  ),
  40. 40     .wr_data      (wr_data_tb  ),
  41. 41     .rd_en        (rd_en_tb  ),
  42. 42     .rd_addr      (rd_addr_tb  ),
  43. 43     .rd_data      (rd_data_tb  )
  44. 44     );
  45. 45     
  46. 46     initial begin
  47. 47     // Initialize Inputs
  48. 48     sys_clk = 0;
  49. 49     sys_rst_n = 0;
  50. 50     
  51. 51     // Wait 100 ns for global reset to finish
  52. 52     #100;
  53. 53     sys_rst_n=1;
  54. 54     // Add stimulus here
  55. 55     end
  56. 56     
  57. 57     always #10 sys_clk=~sys_clk;
  58. 58     
  59. 59 endmodule
复制代码
接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。
image033.png

图 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读操作仿真波形图如下图所示:
image035.png

图 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核从仿真结果上来看是正确的。
接下来再看一张整体的仿真运行图。
image037.png

图 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信号设置为高电平触发:
image039.png

图 19.5.1设置触发信号

然后在如下界面点击触发按钮后进入如图 19.5.3等待触发界面:
image041.png

图 19.5.2 在线调试“Run”



image043.png

图 19.5.3 等待触发

在等待触发时按下开发板的复位按键,可以看到如下所示一张整体的在线调试运行图,由下图可知,我们只在开始时写入一次,随后不再写入,而是每隔一段时间读取一次。
image045.png

图 19.5.4 整体的在线调试运行图

下面是放大后简单双端口RAM写操作在Fabric Debugger中观察的波形如下图所示:
image047.png

图 19.5.5 RAM写操作波形图

wr_en_tb信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1。我们可以发现,上图中的数据变化和在PDS仿真的波形是一致的。
下面是放大后简单双端口RAM读操作在Fabric Debugger中观察的波形如下图所示:
image049.png

图 19.5.6 RAM读操作波形图

rd_en(读使能)信号拉高之后,rd_addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据ram_rd_data在延时两个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的。
我们可以发现,上图中的数据变化同样和PDS仿真的波形是一致的。本次实验的IP核之简单双端口RAM读写实验验证成功。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:48

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表