0
点赞
收藏
分享

微信扫一扫

Vivado 下 IP核 之ROM 读写


目录

Vivado 下 IP核 之ROM 读写

1、实验简介

2、ROM IP 核简介

3、ROM IP 核配置

3.1、创建 ROM 初始化文件

3.2、单端口 ROM 的配置

3.3、双端口 ROM 的配置

3.4、ROM IP 核的调用

(1)ROM 顶层模块代码

(2)ROM IP 核仿真

(3)仿真结果

Vivado 下 IP核 之ROM 读写

1、实验简介

本实验基于 Xinlinx 黑金  AX7A035 FPGA 开发板在Vivado平台下,介绍如何使用 FPGA 内部的 ROM 以及程序对该 ROM 的数据读操作。

2、ROM IP 核简介

          本小节为大家介绍一种较为常用的存储类 IP 核——ROM 的使用方法。ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。而事实上在 FPGA 中通过 IP 核生成的 ROM 或 RAM 调用的都是 FPGA 内部的 RAM 资源,掉电内容都会丢失(这也很容易解释,FPGA 芯片内部本来就没有掉电非易失存储器单元)。用 IP 核生成的 ROM 模块只是提前添加了数据文件(.coe 格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改。

       Xilinx 推出的 ROM IP 核分为两种类型:单端口 ROM(Single-Port Rom)双端口ROM(Dual-Port ROM)。对于单端口 ROM 提供一个读地址端口和一个读数据端口,只能进行读操作;双端口 ROM 与单端口 ROM 类似,区别是其提供两个读地址端口和两个读数据端口,基本上可以看做两个单口 RAM 拼接而成。下面是 ROM 不同配置模式存储器的接口信号图,如图 23-22、图 23-23 所示。

Vivado 下 IP核 之ROM 读写_数据

 

Vivado 下 IP核 之ROM 读写_fpga开发_02

上图中的接口信号我们并不是全部需要用到,因为我们配置时,有些信号是可以不创建的。所以很多接口信号我们可以不用管,只需管我们需要用的信号即可,而什么信号是我们需要用到的呢?这在我们调用完 IP 核后,是可以生成其例化模块的,到时候就可以看到我们需要控制的信号了。

3、ROM IP 核配置

3.1、创建 ROM 初始化文件

ROM 作为只读存储器,在进行 IP 核设置时需要指定初始化文件,即写入存储器中的 数据,数据要以规定的格式才能正确写入 ROM,这种格式就是 coe 文件。coe 是Vivado 规定的一种文件格式,文件格式示意图,具体见图 23-24。

Vivado 下 IP核 之ROM 读写_IP_03

如上图 23-24 所示,该文件的格式较为简单,第一行是定义数据的格式,其中 16 表示数据格式为 16 进制,也可将数据格式定义为二进制和八进制,只需将 16 改为 2 或 8 即可。其中第 3 到第 18 行是 16*8bit 大小 ROM 的初始化数据。

3.2、单端口 ROM 的配置

(1)新建一个 ip_rom 工程后,首先点击 “IP Catalog”,点击后会出现 IP Catalog 页面;我们在 IP 核的搜索框中搜索 “rom”,双击 RAM & ROM & BRAM 核下的 “Block Memory Generator”。

Vivado 下 IP核 之ROM 读写_IP_04

(2)双击之后会出现如下图所示的配置界面。在 "Basic" 栏目下,将 “Component Name” 改为 "rom_256x8b"(rom 是我们调用的 IP 核,256 是调用的 IP 核容量,8 是调用的 IP 核数据位宽。这里这样命名是为了方便识别我们创建的 IP 核类型及资源量)。“Interface Type” 保持默认 "Native "。

在“Memory Type” 框中选择存储器类型,可供选择的类型有:Single Port RAM(单端口 RAMSimple Dual Port RAM(简单双口 RAMTrue Dual Port RAM(真双口 RAMSingl Port ROM(单端口 ROMDoul Port ROM(双端口 ROM。这里我们选择“Single Port Rom”单端口 ROM。 在 Algorithm 一栏中可选择用于实现内存的算法,其中 Minimum Area 为最小面积 算法;Low Power 为低功耗算法;Fixed Primitives 为固定单元算法。这里我们按默认选择 Minimum Area 即可。

Vivado 下 IP核 之ROM 读写_fpga开发_05

 (3)切换到 “Port A Options ”栏目下,设置存储数据的位宽,将 ROM 位宽 "Port A Width" 改为 8;设置数据深度,将 ROM 深度 "Port A Depth" 改为 256,这样我们设置的 ROM 和最大能存储的数据即为 256 x 8bit。(注意:设置的容量需大于我们需要写入的数据文件的数据量);使能管脚 "Enable Port Type" 选择是否创建端口使能信号,这里我们不创建,选择“Always Enabled”始终 使能。如下图:

Vivado 下 IP核 之ROM 读写_IP_06

 (4)切换到“Other Options”栏目,加载数据文件,将 .coe 文件保存到生成的 Rom IP 核中,即我们前面讲到的 ROM 初始化文件, 由于 ROM 是只读存储器,所以我们必须添加 ROM 初始化文件才行。勾选上“Load Init File”点击Browse 进行添加.coe 初始化文件,文件格式我们之前已经讲解,大家可根据自己想存入的数据进行生成该文件。该页面其余按默认设置即可。

Vivado 下 IP核 之ROM 读写_fpga开发_07

 (5)切换到“Summary”页面,在 “Summary” 页面,从“IP Symbol”窗口可以看到我们最终创建的 ROM 核的端口信号。点击 “OK” 完成 ROM 的创建设置。

Vivado 下 IP核 之ROM 读写_数据_08

(6)创建之后接下来跳出如下图所示界面,我们点击“Generate” 生成 ip 核。 

Vivado 下 IP核 之ROM 读写_fpga开发_09

(7)接下来出现如下图所示界面,我们点击“OK”完成生成。

Vivado 下 IP核 之ROM 读写_fpga开发_10

(8)生成之后可以在工程中看到我们正常的 IP 核,双击 IP 核可进入配置界面,对 IP 核的相关参数进行更改。

Vivado 下 IP核 之ROM 读写_初始化_11

3.3、双端口 ROM 的配置

单端口 ROM 的配置步骤讲解完之后,下面介绍一下双端口 ROM 的配置步骤。

(1)该步骤前面的步骤与单端口 ROM 的配置是一样的,到该页面我们在 Memory Type 选择 “Dual Port ROM” 双端口 ROM,其余设置不变。如图 23-34 所示

Vivado 下 IP核 之ROM 读写_数据_12

(2)切换到 “Potr A Options” 页面,如图 23-35 所示,该窗口是对端口 A 的设置,该页面的设置我们跟单端口 ROM 的设置一样即可。



Vivado 下 IP核 之ROM 读写_数据_13



(3)切换到 “Potr B Options” 页面,如图 23-36 所示,该窗口是对端口 B 的设置,这也是双端口 ROM 与单端口 ROM 最大的区别,多了一组端口。这里我们只需对 B 端口的数据位宽设置即可,数据深度会根据端 口 A 的设置自动设置。例如我们端口 A 设置的数据位宽为 8bit,深度为 256;而我们 B 端口设置的数据位宽为 16bit,则其深度即为 128,其数据总量是一样的。其余设置与端口 A 设置一样即可。

Vivado 下 IP核 之ROM 读写_数据_14

(4)切换到“Other Options”页面,如图 23-37 所示,该页面与单端口 ROM 配置一样。

Vivado 下 IP核 之ROM 读写_数据_15

(5)切换到“Summary”页面,如图 23-38 所示,在“Summary”页面,从“IP Symbol”窗口可以看到我们最终创建的 ROM 核的端口信号。可以看到其相比单端口 ROM 来说多了一组端口 B 信号。点击“OK”完成 ROM 的创建设置。接下来步骤与单端口 ROM 一样,就不再重复介绍。

Vivado 下 IP核 之ROM 读写_初始化_16

3.4、ROM IP 核的调用

       在配置 ROM 核之前,我们先生成一个 ROM 的初始化文件(生成数据是十进制数 0~255,256 × 8bit),即我们往地址 0~255 存入十进制数据 0~255。初始化文件生成之后我们按单端口 ROM 的配置步骤配置一个单端口 ROM。

       ROM IP 核配置完成后我们对它进行调用,通过仿真来观察输出信号的变化。验证的方法和例子有很多,后面我们还会在实战项目中使用到这个 IP 核。为了让大家能够清晰的看到 ROM IP 核信号的变化,RTL 代码的顶层只有端口列表和 ROM IP 核的实例化,其中:

  • RTL 代码顶层的输入信号有:50MHz 的系统时钟 sys_clk、输入 ROM 的读地址。这些输入信号需要在 Testbench 中产生激励。
  • RTL 代码顶层的输出信号有:ROM 的输出数据。

(1)ROM 顶层模块代码

ROM 顶层模块参考代码,如下所示:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/19 21:35:56
// Design Name: 
// Module Name: ip_rom
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module ip_rom(
    input  wire              sys_clk ,         //系统时钟,频率 50MHz
    input  wire   [7:0]      addra ,     //输入 rom 地址
    
    output  wire  [7:0]      douta      //输出 rom 数据
    );

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//rom_256x8b_inst
rom_256x8b u_rom_256x8b
(
     .clka       (sys_clk    ),      // input clka
     .addra      (addra      ),      // input [7 : 0] addraendmodule
     .douta      (douta      )       // output [7 : 0] douta
);

endmodule

根据上面 RTL 代码综合出的 RTL 原理图如图 23-39 所示。

Vivado 下 IP核 之ROM 读写_IP_17

(2)ROM IP 核仿真

仿真参考代码如下所示:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/19 21:45:01
// Design Name: 
// Module Name: tb_ip_rom
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module tb_ip_rom();

parameter T = 20;   //50Mhz系统时钟,一个周期为20ns
//parameter T = 5;    //200Mhz系统时钟,一个周期为5ns

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//reg define
reg sys_clk ;
reg sys_rst_n ;
reg [7:0] addra ;

//wire define
wire [7:0] douta ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//对 sys_clk, sys_rst 赋初值,并模拟按键抖动
initial begin
    sys_clk = 1'b1 ;
    sys_rst_n <= 1'b0 ;
    #200 
    sys_rst_n <= 1'b1 ;
end

//sys_clk:模拟系统时钟,每 10ns 电平取反一次,周期为 20ns,频率为 50Mhz
//always #10 sys_clk = ~sys_clk;
always #(T/2) sys_clk = ~sys_clk;

//让地址从 0~255 循环
always @(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
         addra <= 8'd0;
    else if(addra == 8'd255)
         addra <= 8'd0;
    else
         addra <= addra + 1'b1;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//rom_inst
ip_rom  u_ip_rom
(
     .sys_clk (sys_clk    ),     //50Mhz系统时钟,一个周期为20ns
     .addra   (addra      ),     //输入 rom 地址
     
     .douta   (douta      )      //输出 rom 数据
);

endmodule

(3)仿真结果

对代码进行仿真,仿真波形如下所示。

Vivado 下 IP核 之ROM 读写_初始化_18

从图中可以很清楚的看到 ROM IP 核地址与数据的关系。在前面我们知道我们往地址 0~255 存入的数据是 0~255,从波形图中可以看到当读时钟的上升沿采到地址时,在下一刻就会输出该地址存储的数据。

举报

相关推荐

0 条评论