FPGA中ROM IP与RAM IP核配置与调用

目录

一、ROM IP核

1.简介

2.创建立初始化文件

3.配置

4.调用

4.1 整体设计

 4.2 编写rtl代码:

4.3 仿真验证

 二、RAM IP核 

1.简介

2.配置

3.调用

3.1 整体设计

3.2 rtl代码

3.3  仿真验证

前言:

本人使用的是野火家Xilinx Spartan6系列开发板及配套教程,写博客记录自己的学习。

开发软件:ise14.7     仿真:modelsim 10.5 

一、ROM IP核

1.简介

      ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出预先锁存数据的固态半导体存储器。其特性是一旦储存资料就无法再改变或删除,且资料不会因为电源关闭而消失。       

       ROM IP核是在 FPGA 中通过 IP 核生成的,调用的都是 FPGA 内部的 RAM 资源,掉电内容都会丢失(因为FPGA 芯片内部没有掉电非易失存储器单元)。所以要让 ROM 模块像个掉电非易失存储器要提前添加了数据文件(.coe 格式),让其上电就可以初始化。需注意不同的FPGA生产厂商后缀名是不一样的,Xilinx系列开发板是 .coe而Altera是 .mif。

      Xilinx 推出的 ROM IP 核分为两种类型:单端口 ROM(Single-Port Rom)和双端口ROM(Dual-Port ROM),常用的是单端口。单端口 ROM 提供一个读地址端口和一个读数据端口,只能进行读操作;双端口 ROM 与单端口 ROM 类似,区别是其提供两个读地址端口和两个读数据端口,可看做两个单口 RAM 拼接而成。在使用ADDDRA与CLKA并使能ENA的情况下,ROM模块可以工作。

2.创建立初始化文件

        第一行MEMORY_INITIALIZATION_RADIX=10; 是定义数据的格式,其中 10表示数据格式为 10进制,也可将数据格式定义为二、八、十六进制。 后面 MEMORY_INITIALIZATION_VECTOR= 是ROM 的初始化数据。

       将文本格式(.txt)改为.coe就完成了初始化文件的创建。

3.配置

由于我已经配置完了,方便大家看我把教程提供的配置过程贴出来。

1.新建工程后,添加ROM IP核

 2.选择我们需要生成的 IP 核

 3.对前面过程 IP 核存储空间等信息的确认

 4.    其中1 显示的是配置的 IP 核的输入输出接口框图; 2 框中是配置接口类型选择“本地”即可;3 框的“Datasheet”是下载 Xilinx 的官方 ROM & RAM 核数据手册,这个感兴趣可以看看。

 5.  其中1选择 IP 核类型,选择“Single Port Rom”单端口 ROM。

      在2中选择用于实现内存的算法,其中 Minimum Area 为最小面积算法; Low Power 为低功耗算法; Fixed Primitives 为固定单元算法;这里按默认选择Minimum Area 即可。

 6.    1框中可设置存储数据的位宽和深度, “Read Width”是设置数据位宽,设置为 8 位; Read Depth 是设置数据深度,设置为 256;这样我们设置的 ROM ip核最大能存储的数据即为 256 x 8bit。
       2 框是选择启用类型,可以选择“Use ENA Pin” 添加 ENA 脚,利用该信号去使能端口的读写和复位, 这里我们省去麻烦不添加该信号, 默认选择“Always Enabled”始终启用即可。

7.   其中1是加载数据文件,就是前面创建的 ROM 初始化文件,勾选上“ Load Init File”点击 Browse 进行添加.coe 初始化文件。注意如果文件地址显示为红色这说明文件格式或者地址等有误,可以点击show查看。

 8.   框中选项是选择是否创建复位信号,这里按默认不勾选即可。

  9.    最后一步点击 Generate 完成单端口 ROM IP 核的生成。

配置完成会生成如下 .xco 文件

4.调用

   实验目标:读出存储在ROM IP核中的数据。

4.1 整体设计

 4.2 编写rtl代码:

   打开工程目录下的 ROM IP 核保存位置,找到 rom.veo文件。

     打开rom.veo文件,为了将ip核信号与顶层模块中信号连接,将红框内容复制到顶层模块中。

`timescale  1ns/1ns

module  rom_top(
    input   wire            sys_clk     ,  
    input   wire            sys_rst_n    ,  
    
	output  wire      [7:0]  douta
    
);
reg     [7:0]  addra; 

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        addra <= 8'd0;
    else if(addra == 8'd4)
        addra <= 8'd0;
    else 
        addra <= addra + 1'b1;
//调用IP核
rom rom_inst(
  .clka (sys_clk),    
  .addra(addra),  
  .douta(douta)   
);
endmodule

4.3 仿真验证

代码:

`timescale 1ns / 1ns

module tb_rom_top();
    reg           sys_clk;
    reg           sys_rst_n;

    wire   [7:0] douta;    
	
initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
end 

always #10 sys_clk = ~ sys_clk;
		
rom_top rom_top_inst(
    .sys_clk   (sys_clk),
    .sys_rst_n  (sys_rst_n),
    .douta     (douta  ) 
    );
endmodule

仿真结果:

  由仿真结果来看,我们可以知道两点一、读取数据是按照先寻址的方式,二、观察地址8’d0可知,先存入的数据会被后来数据覆盖。

 二、RAM IP核 

1.简介

       RAM 是随机存取存储器( Random Access Memory)的简称,是一个易失性存储器。RAM 工作时可以随时从任何一个指定的地址写入或读出数据,这是其与 ROM 的最大区别。 ROM 是只读存储器,而 RAM 是可写可读存储器。在FPGA中ROM本质只用到了RAM 资源的读数据端口。

     Xilinx 推出的 RAM IP 核分为两种类型:单端口RAM 和双端口RAM。其中双端口RAM 又分为简单双端口 RAM 和真正双端口 RAM。对于单端口RAM,读写操作共用一组地址线,读写操作不能同时进行。单端口RAM各管脚作用:

DINA:写入数据位

DOUTA:读出数据位

ADDRA:读或写数据时地址位,读写不可同时进行

WEA:写使能端,高为写入数据,低为读出数据

ENA:读写信号使能端,高ROM读写功能有效,低禁止读或写

RSTA:复位端

REGCEA:读出寄存器使能端,当REGCEA为高电平时,DOUTA保持最后一次输出的数据。

CLKA: 时钟信号

2.配置

  RAM配置过程中,输入的数据可以随时输入的,故不用事先创建初始化文件。下面详细介绍操作模式的意思。

操作模式:RAM 读写操作模式共分为三种。
Write First(写优先模式) : 若我们在同一个时钟沿下对同一个地址进行读写,则读出的数据为写入的数据。这里使用常规的写优先模式。
Read First(读优先模式) : 若我们在同一个时钟沿下对同一个地址进行读写, 则读出的数据为该地址写入数据前存储的数据。
No Change(不变模式) : 在该模式下不能同时进行读写操作, 输出数据为同时读写操作前输出的数据。

 配置完成产生.xco文件

3.调用

实验目标:先写入数据,再读出数据

3.1 整体设计

 3.2 rtl代码

`timescale  1ns/1ns

module  ram
#(
      parameter CNT_MAX = 25'd999_999    //20ms
)
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst_n   ,   
   
    output  wire    [7:0]   douta          
);
reg             wea        ;   //写使能
reg    [24:0]   cnt_20ms   ;   
reg    [3:0]    cnt        ;   
reg    [7:0]    addr       ;   //地址线
reg    [7:0]    dina       ;   //写数据

//计时20ms
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
	    cnt_20ms <= 25'b0;
    else if (cnt_20ms == CNT_MAX)
	    cnt_20ms <= 25'b0;
	else 
	    cnt_20ms <= cnt_20ms + 1'b1;
//cnt:以20ms为单位,计0-8个数
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
	    cnt <= 4'b0;
	else if(cnt == 4'd9 && cnt_20ms == CNT_MAX)
	    cnt <= 4'd0;
    else if(cnt_20ms == CNT_MAX)
	    cnt <= cnt + 1'b1;
//addr:地址		
always@(posedge sys_clk or  negedge sys_rst_n)
     if(sys_rst_n == 1'b0)
		addr <= 8'd0;
	 else if(cnt < 4'd4 && cnt_20ms == CNT_MAX)
	    addr <= addr + 8'd1;
	 else if ((cnt > 4'd4 && cnt < 4'd9) && cnt_20ms == CNT_MAX)
	    addr <= addr - 8'd1;
		
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
       wea <= 1'b0;
	else if (cnt == 4'd4 && cnt_20ms == CNT_MAX)
       wea <= 1'b0;
    else if (cnt < 4'd5)
       wea <= 1'b1;   //存入数
    else 
        wea <= 1'b0;   //读取数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        dina <= 8'd2;
    else if (dina  == 8'd10  &&  cnt_20ms == CNT_MAX )
	    dina <= 8'd2;
	else if (cnt_20ms == CNT_MAX)
  	    dina <= dina + 8'd2;
    
		
//调用ip核  RAM是建立的RAM ip核
ram_ip   ram_ip_inst
(
    .addra      (addr       ), 
    .clka       (sys_clk    ), 
    .dina       (dina       ), 
    .wea        (wea        ), 
    .douta      (douta    )    
);
endmodule

3.3  仿真验证

仿真代码:

`timescale  1ns/1ns

module  ram
#(
      parameter CNT_MAX = 25'd999_999    //20ms
)
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst_n   ,   
   
    output  wire    [7:0]   douta          
);
reg             wea        ;   //写使能
reg    [24:0]   cnt_20ms   ;   
reg    [3:0]    cnt        ;   
reg    [7:0]    addr       ;   //地址线
reg    [7:0]    dina       ;   //写数据

//计时20ms
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
	    cnt_20ms <= 25'b0;
    else if (cnt_20ms == CNT_MAX)
	    cnt_20ms <= 25'b0;
	else 
	    cnt_20ms <= cnt_20ms + 1'b1;
//cnt:以20ms为单位,计0-8个数
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
	    cnt <= 4'b0;
	else if(cnt == 4'd9 && cnt_20ms == CNT_MAX)
	    cnt <= 4'd0;
    else if(cnt_20ms == CNT_MAX)
	    cnt <= cnt + 1'b1;
//addr:地址		
always@(posedge sys_clk or  negedge sys_rst_n)
     if(sys_rst_n == 1'b0)
		addr <= 8'd0;
	 else if(cnt < 4'd4 && cnt_20ms == CNT_MAX)
	    addr <= addr + 8'd1;
	 else if ((cnt > 4'd4 && cnt < 4'd9) && cnt_20ms == CNT_MAX)  //addr数据维持
	    addr <= addr - 8'd1;
		
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
       wea <= 1'b0;
	else if (cnt == 4'd4 && cnt_20ms == CNT_MAX)  //占用一个脉冲,使wea严格对齐cnt。cnt为0-4是wea为高,5—9为低
       wea <= 1'b0;
    else if (cnt < 4'd5)
       wea <= 1'b1;   //存入数
    else 
        wea <= 1'b0;    //读取数据  
	
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        dina <= 8'd2;
    else if (dina  == 8'd10  &&  cnt_20ms == CNT_MAX )
	    dina <= 8'd2;
	else if (cnt_20ms == CNT_MAX)
  	    dina <= dina + 8'd2;
    
		
//调用ip核  RAM是建立的RAM ip核
ram_ip   ram_ip_inst
(
    .addra      (addr       ), 
    .clka       (sys_clk    ), 
    .dina       (dina       ), 
    .wea        (wea        ), 
    .douta      (douta    )    
);
endmodule

仿真结果: 

 由仿真结果说明,当wea为低电平时,写数据功能失效,此时按照地址读数据。

  • 7
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡0糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值