基于Xilinx的ROM IP核的使用

1.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如下图所示:

双端口ROM:

2.IP核配置

关于IP核的配置如下图所示:

首先打开IP选型

然后搜索block

然后双击,我们选择单端口ROM

这里选择8位宽,深度256,总是使能。

定义了一个0-255的.coe文件,然后并添加

在Summary界面可以看到自己的配置

然后点击OK,我们的但端口ROM IP核就创建完成了。

3.IP核的使用

本次实验任务是参考野火征途Pro系列开发指南,设计一个ROM 的初始化数据是 0~255,也就是存入数据0~255。然后每隔 0.2s 我们从 0 地址开始往下读取数据显示在数码管上,我们再利用两个按键信号来读取指定地址的数据,每按一个按键就读取一个地址的数据显示在数码管上。再次按下按键后,以当前地址继续以 0.2s 的时间间隔往下读取数据并显示出来。

3.1实验分析

根据实验任务我们可以知道,本次实验需要使用到的外设有按键以及数码管。按键的话肯定需要消抖模块,数码管的话是需要数码管驱动模块。在按键跟数码管中间的数据传输时通过ROM IP进行传输,所以还需要设计一个rom控制模块。模块框图大致如下:

消抖模块、rom控制模块以及rom IP的系统时钟以及系统复位端口没有画出来,正常都应该有的,这里需要注意一下。

按键消抖模块代码如下:

`timescale 1ns / 1ps

module key_filter(
    input           sys_clk     ,
    input           sys_rst_n   ,
    input           key         ,

    output  reg     key_flag    
    );
    
parameter CNT_MAX = 20'd999_999;    
    
reg [19:0]  cnt_20ms;


//20ms cnt
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else if(key == 1'b1)
        cnt_20ms <= 20'd0;
    else if(cnt_20ms == CNT_MAX && key == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 20'd1;
end

//key_flag
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        key_flag <= 1'd0;
    else if(cnt_20ms == (CNT_MAX - 20'd1))
        key_flag <= 1'd1;
    else
        key_flag <= 1'd0;
end

消抖设置了一个20ms的计数器,当按键按下时间小于20ms就认为按键在抖动时间,根据消抖模块可以看一下仿真代码及波形图如下:

`timescale 1ns / 1ns

module tb_key_filter(

    );
    
    
reg            sys_clk  ;
reg            sys_rst_n;
reg            key1     ;
reg            key2     ;
                        
wire           key1_flag;

initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    key1 <= 1'b1;
    key2 <= 1'b1;
    #201
    sys_rst_n <= 1'b1;
    #200
//key1    
    key1 <= 1'b0;
    #20
    key1 <= 1'b1;
    #80
    key1 <= 1'b0;
    #120
    key1 <= 1'b1;
    #20
    key2 <= 1'b0;
    #20
    key2 <= 1'b1;
    #20
    key2 <= 1'b0;
    #100
    key1 <= 1'b0;
    #500
    key1 <= 1'b1;
    #500
    key1 <= 1'b0;
    #400
    key1 <= 1'b1;
end

always #10 sys_clk <= ~sys_clk;

defparam    key_filter_inst1.CNT_MAX = 19;
defparam    key_filter_inst2.CNT_MAX = 19;
    
key_filter key_filter_inst1(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key         (key1     ),
                           
    .key_flag    (key1_flag )
    );

key_filter key_filter_inst2(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key         (key2     ),
                           
    .key_flag    (key2_flag )
    );    

endmodule

接下来我们开始设计rom控制模块,代码如下:

`timescale 1ns / 1ps

module rom_ctrl(
    input               sys_clk     ,
    input               sys_rst_n   ,
    input               key1        ,
    input               key2        ,
    
    output  reg    [7:0]   addr      
    );
    

   
parameter   CNT_MAX = 9_999_999;    //0.2s计数器
parameter   ADDR_MAX = 8'd255;      //最大地址
reg         key1_en;
reg         key2_en;
reg [23:0]  cnt_200ms;

//key1_en
always@(posedge sys_clk or  negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        key1_en <= 1'b0;
    else if(key2 == 1'b1)
        key1_en <= 1'b0;
    else if(key1 == 1'b1)
        key1_en <= ~key1_en;
end

//key2_en
always@(posedge sys_clk or  negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0)
        key2_en <= 1'b0;
    else if(key1 == 1'b1)
        key2_en <= 1'b0;
    else if(key2 == 1'b1)
        key2_en <= ~key2_en;
end        

//0.2s cnt    
always@(posedge sys_clk or  negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0) 
        cnt_200ms <= 24'd0;
    else if(key1_en == 1'b1 || key2_en == 1'b1 || cnt_200ms == CNT_MAX)
        cnt_200ms <= 24'd0;
    else
        cnt_200ms <= cnt_200ms + 24'd1;
end   
     
//地址
always@(posedge sys_clk or  negedge sys_rst_n) begin
    if(sys_rst_n == 1'b0) 
        addr <= 8'd0;
    else if(addr == ADDR_MAX && cnt_200ms == CNT_MAX)
        addr <= 8'd0;
    else if(key1_en == 1'b1)
        addr <= 8'd99;
    else if(key2_en == 1'b1)
        addr <= 8'd199;
    else if(cnt_200ms == CNT_MAX)
        addr <= addr + 8'd1;
end
        
endmodule

仿真代码:

`timescale 1ns / 1ns

module tb_rom_ctrl();

reg            sys_clk  ;
reg            sys_rst_n;
reg            key1     ;
reg            key2     ;
                        
wire  [7:0]    addr     ;

initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    key1 <= 1'b0;
    key2 <= 1'b0;
    #201
    sys_rst_n <= 1'b1;
    #2000
//key1    
    key1 <= 1'b1;
    #20
    key1 <= 1'b0;
    #2000
    key1 <= 1'b1;
    #20
    key1 <= 1'b0;
    #700
//key2
    key2 <= 1'b1;
    #20
    key2 <= 1'b0;
    #2500
    key2 <= 1'b1;
    #20
    key2 <= 1'b0;
    #500
//key1&key2
    key1 <= 1'b1;
    #20
    key1 <= 1'b0;
    #2000
    key2 <= 1'b1;
    #20
    key2 <= 1'b0;
    #2000
    key2 <= 1'b1;
    #20
    key2 <= 1'b0;
    
end

always #10 sys_clk <= ~sys_clk;

defparam    rom_ctrl_inst.CNT_MAX = 19;

rom_ctrl rom_ctrl_inst(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .key1        (key1     ),
    .key2        (key2     ),
                           
    .addr        (addr     )
    );
endmodule

数码管例程就不展示了,大家可以随便使用一个数码管驱动模块,也可以参考野火的数码管例程

最后将这三个模块例化到一起,再进行一个仿真验证:

仿真结果跟我们要实现的任务要求是一致的,说明代码没有问题,这边就不展示上板验证的效果了,下一章介绍RAM的使用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伊藤诚诚诚诚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值