资讯详情

【正点原子FPGA连载】第十四章 IP核之RAM实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

1)实验平台:正点原子领导者ZYNQ开发板 2)平台采购地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-301505-1-1.html 4)正点原子FPGA感兴趣的同学可以加群讨论:99424016 5)关注正点原子微信官方账号,获取最新信息更新 在这里插入图片描述

第十四章 IP核之RAM实验

RAM英文全称是Random Access Memory,也就是说,随机访问存储器可以随时将数据写入任何指定地址的存储单元,也可以随时从任何指定地址读取数据。读写速度由时钟频率决定。RAM主要用于存储程序和程序执行过程中产生的中间数据、计算结果等。本章我们将正确Vivado软件生成的RAM IP核读写测试,并向大家介绍Xilinx RAM IP使用核的方法。 本章包括以下几个部分: 1.1 RAM IP核简介 1.2 实验任务 1.3 硬件设计 1.4 程序设计 1.5 下载验证

1.1 RAM IP核简介

Xilinx 7系列设备具有嵌入式存储结构,满足设计对片上存储的需要。嵌入式存储结构由一列列组成BRAM(块RAM)通过这些BRAM配置存储模块可以实现各种存储功能,如:RAM、移位寄存器,ROM以及FIFO缓冲器。 Vivado软件自带了BMG IP核(Block Memory Generator,块RAM可配置成生成器)RAM或者ROM。两者的区别在于RAM它是一种随机访问存储器,不仅可以存储数据,还可以修改存储的数据;ROM它是一种只读存储器,即在正常工作中只读取数据而不写入数据。需要注意的是,配置成RAM或者ROM使用的资源都是FPGA内部的BRAM,只是配置成ROM只使用嵌入式BRAM读数据端口。本章我们主要介绍BRAM IP核配置成RAM使用方法。 Xilinx 7系列设备内部BRAM都是真双端口RAM(True Dual-Port ram,TDP),两个端口都可以独立对齐BRAM读写。但也可以配置为伪双端口RAM(Simple Dual-Port ram,SDP)(有两个端口,但其中一个只能读,另一个只能写)或单个端口RAM(只有一个端口,读写只能通过这个端口进行)。单端口RAM只有一组数据总线、地址总线、时钟信号以及其他控制信号,而双端口RAM有两组数据总线、地址总线、时钟信号等控制信号。有关BRAM请参考更详细的介绍Xilinx官方手册文档UG473,7 Series FPGAs Memory Resources User Guide”。 单端口RAM类型和双端口RAM操作类型相同,只要我们学会了单端口RAM然后学习双端口的使用,然后学习双端口RAM读写操作也很容易。在本章中,我们将其单端口RAM举例说明。 BMG IP核配置成单端口RAM如下图所示。

图 7.5.13.1 单端口RAM框图 各端口的功能描述如下: DINA:RAM端口A写数据信号。 ADDRA:RAM端口A读写地址信号,单端口RAM读地址和写地址共享该地址线。 WEA:RAM端口A写信号,高电平表示向RAM低电平表示从中写入数据RAM中读数据。 ENA:高电平表示端口A的使能信号A,低电平表示禁止端口A,禁止后端口A上的读写操作将无效。ENA当使能信号被取消时,信号是可选的,RAM将始终处于有效状态。 RSTA:RAM端口A复位信号可配置为高电平或低电平复位,是可选信号。 REGCEA:RAM当端口A输出寄存器使能信号时REGCEA高电平时,DOUTA保持最后一次输出的数据,REGCEA同样是一个可选信号。 CLKA:RAM端口A的时钟信号。 DOUTA:RAM端口A读取的数据。 1.2 实验任务 使用本节的实验任务Xilinx BMG IP核配置成单端口RAM,然后对RAM读写操作,通过Vivado观察自带模拟器中的波形是否正确,最后将设计下载给领导者Zynq并在开发板中使用ILA在线调试观察。 1.3 硬件设计 本章实验仅使用输入时钟信号和按钮复位信号,不使用其他硬件外设。各端口信号的管脚分布如下表所示: 表 14.3.1 IP核之RAM实验管脚分配 信号名 方向 管脚 端口说明 电平标准 sys_clk input U18 系统时钟,50Mhz LVCMOS33 sys_rst_n input N16 系统复位,低电平有效,位于底板上 LVCMOS33 对应的XDC约束语句如下:

set_property -dict { 
        PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk] set_property -dict { 
        PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n] 

1.4 程序设计 首先在Vivado在软件中创建一个名称ip_ram项目建成后,Vivado软件左侧Flow Navigator栏中单击IP Catalog如下图所示。

图 7.5.13.1 点击“IP Catalog” 在“IP Catalog在窗口的搜索框中输入Block Memory唯一匹配的Block Memory Generator如下图所示(图中出现的两个)IP核为同一个BMG IP核)。

图 7.5.13.2 在搜索框中输入Block Memory” 双击“Block Memory Generator”后弹出IP接下来,对于核的配置界面BMG IP核配置,Basic选项页配置界面如下图所示。

图 7.5.13.3 “Basic选项页配置 Component Name:设置该IP在这里保持默认名称。 Interface Type:RAM接口总线。在这里保持默认,选择Native接口类型(标准RAM接口总线); Memory Type:存储类型。可配置成Single Port RAM(单端口RAM)、Simple Dual Port RAM(伪双端口RAM)、True Dual Port RAM(真双端口RAM)、Single Port ROM(单端口ROM)和Dual Port ROM(双端口ROM),这里选择Single Port RAM,即配置成单端口RAM。 ECC Options:Error Correction Capability,纠错能力选项,单端口RAM不支持ECC。 Write Enable:字节写使能选项,勾中后可以单独写入数据的字节RAM在这里不使能。 Algorithm Options:算法选项Minimum Area(最小面积),Low Power(低功耗)和Fixed Primitives(固定原语),这里默认选择Minimum Area。 然后切换到下一步Port A该页面配置如下:

图 7.5.13.4 “Port A Options”选项页配置 Write Width:端口A写数据位宽,单位Bit,这里设置成8。 Read Width:端口A读数据位宽,一般和写数据位宽保持一致,设置成8。 Write Depth:写深度,这里设置成32,即RAM所能访问的地址范围为0-31。 Read Depth:读深度,默认和写深度保持一致。 Operating Mode:RAM读写操作模式。共分为三种模式,分别是Write First(写优先模式)、Read First(读优先模式)和No Change(不变模式)。写优先模式指数据先写入RAM中,然后在下一个时钟输出该数据;读优先模式指数据先写入RAM中,同时输出RAM中同地址的上一次数据;不变模式指读写分开操作,不能同时进行读写,这里选择No Change模式。 Enable Port Type:使能端口类型。Use ENA pin(添加使能端口A信号);Always Enabled(取消使能信号,端口A一直处于使能状态),这里选择默认的Use ENA pin。 Port A Optional Output Register:端口A输出寄存器选项。其中“Primitives Output Register”默认是选中状态,作用是打开BRAM内部位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得BRAM输出的数据延迟一拍,这不利于我们在Vivado的ILA调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把BRAM的数据输出总线连接到了ILA的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径,因此这里取消勾选。 Port A Output Reset Options:RAM复位信号选项,这里不添加复位信号,保持默认即可。 另外,需要注意的是,下面的“Primitives Output Register”默认是选中状态的,此选项的作用是打开块RAM内部的位于输出数据总线之后的输出流水线寄存器,虽然在一般设计中为了改善时序性能会保持此选项的默认勾选状态,但是这会使得块RAM输出的数据延迟一拍,这不利于我们在Vivado的ILA调试窗口中直观清晰地观察信号;而且在本实验中我们仅仅是把块RAM的数据输出总线连接到了ILA的探针端口上来进行观察,除此之外数据输出总线没有别的负载,不会带来难以满足的时序路径。 接下来的“Other Options”选项页用于设置RAM的初始值等,本次实验不需要设置,直接保持默认即可。 最后一个是“Summary”选项页,该页面显示了存储器的类型,消耗的BRAM资源等,我们直接点击“OK”按钮完成BMG IP核的配置,如下图所示:

图 7.5.13.5 “Summary”选项页 接下来会弹出询问是否在工程目录下创建存放IP核的文件,我们点击“OK”按钮即可。 紧接着会弹出“Genarate Output Products”窗口,我们直接点击“Generate”,如下图所示。

图 7.5.13.6 “Genarate Output Products”窗口 之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该IP核对应的run“blk_mem_gen_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。

图 7.5.13.7 “blk_mem_gen_0_synth_1”run 在其Out-of-Context综合的过程中,我们就可以进行RTL编码了。首先打开IP核的例化模板,在“Source”窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“blk_mem_gen_0”-“Instantitation Template”,我们可以看到“blk_mem_gen_0.veo”文件,它是由IP核自动生成的只读的verilog例化模板文件,双击就可以打开它,如下图所示。

图 7.5.13.8 “blk_mem_gen_0.veo”文件 接下来我们创建一个新的设计文件,命名为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      input        [7:0]  ram_rd_data   //ram读数据 
10     );
11 
12 //reg define
13 reg    [5:0]  rw_cnt ;                //读写控制计数器
14 
15 //*****************************************************
16 //** main code
17 //*****************************************************
18 
19 //控制RAM使能信号
20 assign ram_en = rst_n;
21 //rw_cnt计数范围在0~31,写入数据;32~63时,读出数据
22 assign ram_wea = (rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;
23 
24 //读写控制计数器,计数器范围0~63
25 always @(posedge clk or negedge rst_n) begin
26     if(rst_n == 1'b0)
27         rw_cnt <= 1'b0;    
28     else if(rw_cnt == 6'd63)
29         rw_cnt <= 1'b0;
30     else
31         rw_cnt <= rw_cnt + 1'b1;    
32 end  
33 
34 //产生RAM写数据
35 always @(posedge clk or negedge rst_n) begin
36     if(rst_n == 1'b0)
37         ram_wr_data <= 1'b0;  
38     else if(rw_cnt <= 6'd31)  //在计数器的0-31范围内,RAM写地址累加
39         ram_wr_data <= ram_wr_data + 1'b1;
40     else
41         ram_wr_data <= 1'b0 ;   
42 end  
43 
44 //读写地址信号 范围:0~31
45 always @(posedge clk or negedge rst_n) begin
46     if(rst_n == 1'b0)
47         ram_addr <= 1'b0;
48     else if(ram_addr == 5'd31)
49         ram_addr <= 1'b0;
50     else    
51         ram_addr <= ram_addr + 1'b1;
52 end
53 
63 endmodule

模块中定义了一个读写控制计数器(rw_cnt),当计数范围在031之间时,向ram中写入数据;当计数范围在3263之间时,从ram中读出数据。 接下来我们设计一个verilog文件来实例化创建的RAM IP核以及ram_rw模块,文件名为ip_ram.v, 编写的verilog代码如下。

1  module ip_ram(
2      input         sys_clk ,  //系统时钟
3      input         sys_rst_n  //系统复位,低电平有效
4      );
5  
6  //wire define
7  wire             ram_en      ;  //RAM使能 
8  wire             ram_wea     ;  //ram读写使能信号,高电平写入,低电平读出 
9  wire    [4:0]    ram_addr    ;  //ram读写地址 
10 wire    [7:0]    ram_wr_data ;  //ram写数据 
11 wire    [7:0]    ram_rd_data ;  //ram读数据 
12 
13 //*****************************************************
14 //** main code
15 //*****************************************************
16 
17 //ram读写模块
18 ram_rw  u_ram_rw(
19     .clk            (sys_clk     ),
20     .rst_n          (sys_rst_n   ),
21  //RAM
22  .ram_en         (ram_en      ),
23     .ram_wea        (ram_wea     ),
24     .ram_addr       (ram_addr    ),
25     .ram_wr_data    (ram_wr_data ),
26     .ram_rd_data    (ram_rd_data )
27     );
28 
29 //ram ip核
30 blk_mem_gen_0  blk_mem_gen_0 (
31  .clka  (sys_clk      ),  // input wire clka
32  .ena   (ram_en       ),  // input wire ena 
33  .wea   (ram_wea      ),  // input wire [0 : 0] wea
34  .addra (ram_addr     ),  // input wire [4 : 0] addra
35  .dina  (ram_wr_data  ),  // input wire [7 : 0] dina
36  .douta (ram_rd_data  )  // output wire [7 : 0] douta
37 );
38 
39 endmodule

程序中例化了ram_rw模块和ram IP核blk_mem_gen_0,其中ram_rw模块负责产生对ram IP核读/写所需的所有数据、地址以和读写使能信号,同时从ram IP读出的数据也连接至ram_rw模块。 接下来对RAM IP核进行仿真,来验证对RAM的读写操作是否正确。tb_ip_ram仿真文件源代码如下:

1  `timescale 1ns / 1ps
2  
3  module tb_ip_ram();
4  
5  reg     sys_clk;
6  reg     sys_rst_n;      
7  
8  always #10 sys_clk = ~sys_clk;
9  
10 initial begin
11     sys_clk = 1'b0;
12     sys_rst_n = 1'b0;
13     #200
14     sys_rst_n = 1'b1;
15 end
16 
17 ip_ram u_ip_ram(
18     .sys_clk          (sys_clk        ),
19     .sys_rst_n        (sys_rst_n      )
20     );
21 
22 endmodule

接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。

图 7.5.13.9 RAM写操作波形图 图 7.5.13.9为RAM的写操作仿真波形图,由上图可知,ram_wea信号拉高,说明此时是对ram进行写操作。ram_wea信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1,我们总共向ram中写入32个数据。 RAM读操作仿真波形图如下图所示:

图 7.5.13.10 RAM读操作波形图 由上图可知,ram_wea信号拉低,说明此时是对ram进行读操作。ram_wea信号拉低之后,ram_addr从0开始增加,也就是说从ram的地址0开始读数据;ram中读出的数据ram_rd_data在延时一个时钟周期之后,开始输出数据,输出的数据为0,1,2……,和我们写入的值是相等的, 也就是说,我们创建的RAM IP核从仿真结果上来看是正确的。 接下来添加ILA IP核,将ram_en、ram_wea、ram_addr、ram_wr_data和ram_rd_data信号添加至观察列表中,添加ILA IP核的方法这里不再赘述。 最后为工程添加IO管脚约束,对应的XDC约束语句如下所示:

set_property -dict { 
        PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict { 
        PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

1.5 下载验证 编译工程并生成比特流.bit文件。将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,连接电源线,并打开开发板的电源开关。 点击Vivado左侧“Flow Navigator”窗口最下面的“Open Hardware Manager”,此时Vivado软件识别到下载器,点击“Hardware”窗口中“Progam Device”下载程序,在弹出的界面中选择“Program”下载程序。 RAM写操作在ILA中观察的波形如下图所示:

图 7.5.13.1 RAM写操作ILA波形图 ram_wea信号拉高之后,地址和数据都是从0开始累加,也就说当ram地址为0时,写入的数据也是0;当ram地址为1时,写入的数据也是1。我们可以发现,上图中的数据变化和在Vivado仿真的波形是一致的。 RAM读操作在ILA中观察的波形如下图所示:

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

标签: 连接器j15

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台