一、验证
如何验证ROM IP核是否正常工作,首先满足两个要求。
第一:每个地址都对应一个数据
第二:能够根据clk产生随机数据地址,同时能够将对应地址中的内容提取出来
1、实验目标
(1)以生成的mif文件为例子,从地址0——255将其每个地址中间的内容读取出来,并将读取的数据在数码管上显示出来,将每个数据显示的时间定为200ms。再将数据与我们的mif文件进行对比观察数据的正确性
(2)验证随机变量的正确性,引入两个变量即两个按键k1、k2,当k1按下的时候给rom提供一个随机但是有效的地址A1,然后将A1存储的数据读取出来显示在数码管上。k2也是同理,产生一个随机数地址A2,并将A2地址对应的数据提取出来,将其显示在数码管上面。比如按下按键k1,产生随机地址A1之后,以A1为起始地址,255为最后的地址,将数据依次显示结束之后再从地址0开始进行循环
二、实现
1、框图
动态显示模块
rom控制模块
rom ip核模块
按键消抖模块
根据之前的学习,我们现在已经生成了按键消抖模块,动态显示模块、rom ip核模块。现在只差rom控制模块,以及功能的顶层模块
2、rom控制模块波形
(1)
使得每个地址对应一个数据,将数据在数码管上面显示出来,每个数据显示的时间为200ms,所以在每个200ms的周期内显示你每个数据,此时的两个按键都没有按下。
(2)
这个状态是按键1按下的时候,当按键1按下的时候,地址跳转到地址99,并将地址99中的数据显示在数码管上,当按键1再次按下的时候,地址会不断的自动加1,并将地址中的数据显示在数码管上面
(3)
这个状态是按键1按下的时候,当按键2按下的时候,地址跳转到地址199,并将地址199中的数据显示在数码管上,当按键2再次按下的时候,地址会不断的自动加1,并将地址中的数据显示在数码管上面
(4)
这种状态是显示按键1按下,数码管显示地址99中的数据,当下一时刻按下按键2,则数据会跳转到地址199所对应的数据上面。再次按下按键2的时候,数码管显示的数据会从地址199上面依次加1。同样还有第五种情况,可以根据状态4进行修改
3、程序
在rom中我们用到的模块,在之前都已经存在,只有rom_ctrl没有,在这里只单独写出其程序以及测试程序
(1)rom_ctrl程序
module rom_ctrl
#(
parameter CNT_MAX = 9_999_999 //0.2s计数器最大值
)
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低有效
input wire key1 , //按键1消抖后有效信号
input wire key2 , //按键2消抖后有效信号
output reg [7:0] addr //输出读ROM地址
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg define
reg key1_en ; //特定地址1标志信号
reg key2_en ; //特定地址2标志信号
reg [23:0] cnt_200ms ; //0.2s计数器
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//0.2s循环计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_200ms <= 24'd0;
else if(cnt_200ms == CNT_MAX || key1_en == 1'b1 || key2_en == 1'b1)
cnt_200ms <= 24'd0;
else
cnt_200ms <= cnt_200ms + 1'b1;
//产生特定地址1标志信号
always@(posedge sys_clk or negedge sys_rst_n)
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;
//产生特定地址2标志信号
always@(posedge sys_clk or negedge sys_rst_n)
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;
//让地址从0~255循环,其中两个按键控制两个特定地址的跳转
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
addr <= 8'd0;
else if(addr == 8'd255 && 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 + 1'b1;
endmodule
(2)tb_rom_ctrl测试模块
`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;
#30
sys_rst_n <= 1'b1;
#700_000
//仿真设置的为99,即100个时钟周期,每个时钟周期为20ns,总共100*20=2000ns,
//表示仿真过程中每个数据显示的时间为2000ns,在rom ip核中共有256个数据,2000*256=51200,即延时时间不能小于这个数
//key1
key1 <= 1'b1;
#20
key1 <= 1'b0;
#20000
key1 <= 1'b1;
#20
key1 <= 1'b0;
#60000
//key2
key2 <= 1'b1;
#20
key2 <= 1'b0;
#20000
key2 <= 1'b1;
#20
key2 <= 1'b0;
#60000
//按下key1一次,按下key2两次
key1 <= 1'b1;
#20
key1 <= 1'b0;
#20000
key2 <= 1'b1;
#20
key2 <= 1'b0;
#20000
key2 <= 1'b1;
#20
key2 <= 1'b0;
end
always #10 sys_clk = ~sys_clk;
rom_ctrl
#(
.CNT_MAX (24'd99) //0.2s计数器最大值
)
rom_ctrl_inst
(
.sys_clk (sys_clk), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n), //复位信号,低有效
.key1 (key1) , //按键1消抖后有效信号
.key2 (key2 ) , //按键2消抖后有效信号
.addr (addr) //输出读ROM地址
);
endmodule
(3)总模块
module rom
(
input wire sys_clk,
input wire sys_rst_n,
input wire key1,
input wire key2,
output wire stcp,
output wire shcp,
output wire ds,
output wire oe
);
wire key1_flag;
wire key2_flag;
wire [7:0] addr;
wire [7:0] data;
key_filter
#(
.CNT_MAX (20'd999999)
)
key_filter_inst1
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.key_in (key1) ,
.key_flag (key1_flag)
);
key_filter
#(
.CNT_MAX (20'd999999)
)
key_filter_inst2
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.key_in (key2) ,
.key_flag (key2_flag)
);
rom_ctrl
#(
.CNT_MAX (24'd9999999) //0.2s计数器最大值
)
rom_ctrl_inst
(
.sys_clk (sys_clk) , //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n) , //复位信号,低有效
.key1 (key1_flag) , //按键1消抖后有效信号
.key2 (key2_flag) , //按键2消抖后有效信号
.addr (addr) //输出读ROM地址
);
rom_8_256 rom_8_256_inst
(
.address ( addr ),
.clock ( sys_clk ),
.q ( data )
);
seg_595_dynamic seg_595_dynamic_inst
(
. sys_clk (sys_clk) ,
. sys_rst_n (sys_rst_n) ,
. data ({12'b0,data} ) ,//因为在动态显示模块里面的data是20位宽,而data是8位宽
. point (6'b000_000) ,
. seg_en (1'b1) ,
. sign (1'b0) ,
. ds (ds) ,
. shcp (shcp) ,
. stcp (stcp) ,
. oe (oe)
);
endmodule
(4)总测试模块
`timescale 1ns/1ns
module tb_rom();
//wire define
wire stcp;
wire shcp;
wire ds ;
wire oe ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg key1 ;
reg key2 ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//对sys_clk,sys_rst赋初值,并模拟按键抖动
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
key1 <= 1'b1;
key2 <= 1'b1;
#200 sys_rst_n <= 1'b1 ;
//按下按键key1
#700000 key1 <= 1'b0;//按下按键
#20 key1 <= 1'b1;//模拟前抖动
#20 key1 <= 1'b0;//模拟前抖动
#20 key1 <= 1'b1;//模拟前抖动
#20 key1 <= 1'b0;//模拟按下按键
#200 key1 <= 1'b1;//松开按键
#20 key1 <= 1'b0;//模拟后抖动
#20 key1 <= 1'b1;//模拟后抖动
#20 key1 <= 1'b0;//模拟后抖动
#20 key1 <= 1'b1;//模拟后抖动
//按下按键key2
#20000 key2 <= 1'b0;//按下按键
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#200 key2 <= 1'b1;//松开按键
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
//按下按键key2
#20000 key2 <= 1'b0;//按下按键
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#200 key2 <= 1'b1;//松开按键
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
#20 key2 <= 1'b0;//模拟抖动
#20 key2 <= 1'b1;//模拟抖动
end
//sys_clk:模拟系统时钟,每10ns电平取反一次,周期为20ns,频率为50Mhz
always #10 sys_clk = ~sys_clk;
//重新定义参数值,缩短仿真时间仿真
/* defparam rom_inst.key1_filter_inst.CNT_MAX = 5 ;
defparam rom_inst.key2_filter_inst.CNT_MAX = 5 ;
defparam rom_inst.rom_ctrl_inst.CNT_MAX = 99; */
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//---------------rom_inst--------------
rom rom_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.key1 (key1 ), //输入按键信号
.key2 (key2),
.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe ) //输出使能信号
);
endmodule