[转]用ModelSim仿真FIFO

文章转自:http://blog.csdn.net/ruby97/article/details/7348939

由于仿真FIFO需要时钟资源,故使用了前一篇文章中使用的PLL模块。

在仿真FIFO模块时,我使用了一个数据发生模块,参考的下面这篇文章中的源码

datagene.v (可以去链接网址下载源码)

数据发生模块的输入输出框图如下所示:

我在Modelsim中利用了该模块进行FIFO的仿真。若对FIFO的IP核不熟悉,可以参考官方文档:http://www.altera.com/literature/ug/ug_fifo.pdf

下面是具体仿真过程:

首先,看一下FIFO配置:

数据单元位宽:16

FIFO最大数据量:512个

读和写使用不同的时钟

OK,下面给出FIFO模块的框图:

找到Quartus中生成的FIFO模块的 verilog文件,把它复制到Modelsim工程中;此外,FIFO仿真还需要的文件是:

  • Altera库文件:altera_mf.v ,220_model.v (前一篇有提到这两个文件的位置)
  • 数据发生器文件:datagene.v (参考特权同学的博客,里面可以下载到)
  • PLL模块:sys_ctrl.v clk_ctrl.v前一篇博文有提到
  • FIFO模块:wrfifo.v 
  • 模块封装以及测试文件
所有文件加入工程后如下所示:
 
其中,write_fifo_module.v代码如下:
module write_fifo_module   
(  
    wrf_din,          
    clk_100m,     
    clk_20m,  
    sdram_wr_ack,  
    wrf_wrreq,    
    sys_data_in,  
    wrf_use  
);  
  
    input   [15:0]   wrf_din;      //data write to FIFO  
    input            clk_100m;     //write clock  
    input          clk_20m;      //read  clock  
    input            wrf_wrreq;    //write fifo request  
    input            sdram_wr_ack; //read fifo request  
    output  [15:0]  sys_data_in;  //read-data  
    output  [8:0]   wrf_use;      //the count of data in fifo  
      
wrfifo          uut_wrfifo(  
                    .data(wrf_din),  
                    .rdclk(clk_20m),  
                    .rdreq(sdram_wr_ack),  
                      
                    .wrclk(clk_100m),  
                    .wrreq(wrf_wrreq),  
                    .q(sys_data_in),  
                    .wrusedw(wrf_use)  
                    );    
endmodule  

下面是fifo_test_module.v 源码

`timescale 1 ps /1 ps  
  
module fifo_test_module(  
    clk,  
    reset,  
    clk_20m,  
    clk_100m,  
    clk_sdram,  
    system_reset,  
    sdram_rd_ack,  
    sdram_wr_ack,  
    syswr_done,  
    write_fifo_req,  
    write_fifo_data_in,  
    moni_addr,  
    sys_data_in,  
    wrf_use  
);  
  
input clk;  
input reset;  
  
output clk_20m;  
output clk_100m;  
output clk_sdram;  
output system_reset;  
  
input sdram_rd_ack;  
input sdram_wr_ack;  
  
output syswr_done;  
output write_fifo_req;  
output [15:0]write_fifo_data_in;  
output [21:0]moni_addr;  
output [15:0]sys_data_in;  
output [8:0]wrf_use;  
  
sys_ctrl        uut_sysctrl(  
                    .clk(clk),  
                    .rst_n(reset),  
                    .sys_rst_n(system_reset),  
                    .clk_20m(clk_20m),  
                    .clk_100m(clk_100m),  
                    .sdram_clk(clk_sdram)  
                    );  
  
datagene            uut_datagene(  
                        .clk_20m(clk_20m),  
                        .clk_100m(clk_100m),  
                        .rst_n(system_reset),  
                        .wrf_din(write_fifo_data_in),  
                        .wrf_wrreq(write_fifo_req),  
                        .moni_addr(moni_addr),  
                        .syswr_done(syswr_done),  
                        .sdram_rd_ack(sdram_rd_ack)  
                    );  
  
write_fifo_module    uut_write_fifo_module(  
             .wrf_din(write_fifo_data_in),        
               .clk_100m(clk_100m),   
               .clk_20m(clk_20m),  
               .sdram_wr_ack(sdram_wr_ack),  
               .wrf_wrreq(write_fifo_req),    
               .sys_data_in(sys_data_in),  
               .wrf_use(wrf_use)                                      
                    );  
endmodule  

最后,是测试激励fifo_test_module_tb的代码:

`timescale 1 ps /1 ps  
  
module fifo_test_module_tb;  
  
reg clk;  
reg reset;  
  
wire clk_20m;  
wire clk_100m;  
wire clk_sdram;  
wire system_reset;  
  
reg sdram_rd_ack;  
reg sdram_wr_ack;  
  
wire syswr_done;  
wire write_fifo_req;  
wire [15:0]write_fifo_data_in;  
wire [21:0]moni_addr;  
wire [15:0]sys_data_in;  
wire [8:0]wrf_use;  
  
fifo_test_module fifo_test_module_uut(  
    .clk(clk),  
    .reset(reset),  
    .clk_20m(clk_20m),  
    .clk_100m(clk_100m),  
    .clk_sdram(clk_sdram),  
    .system_reset(system_reset),  
    .sdram_rd_ack(sdram_rd_ack),  
    .sdram_wr_ack(sdram_wr_ack),  
    .syswr_done(syswr_done),  
    .write_fifo_req(write_fifo_req),  
    .write_fifo_data_in(write_fifo_data_in),  
    .moni_addr(moni_addr),  
    .sys_data_in(sys_data_in),  
    .wrf_use(wrf_use)  
);  
  
  
  
 initial  
 begin  
     clk = 0;  
     reset=1;  
     sdram_rd_ack=0;  
     sdram_wr_ack=0;  
     #500000000 sdram_rd_ack=1;  
     #1000000 sdram_rd_ack=0;  
 end  
   
 always #25000 clk = ~clk;  
   
 always #4000000 sdram_wr_ack=~sdram_wr_ack;  
  
  
endmodule  

OK,下面编写ModelSim命令文件: fifo.do

#Creat a work lib  
vlib work   
#Map the work lib to current lib  
vmap work work  
   
#Compile the source files  
vlog E:/Project/ModelSim/sdram_test/src/lib/altera_mf.v  
vlog E:/Project/ModelSim/sdram_test/src/lib/220model.v  
vlog E:/Project/ModelSim/sdram_test/src/pll/clk_ctrl.v  
vlog E:/Project/ModelSim/sdram_test/src/pll/sys_ctrl.v  
vlog E:/Project/ModelSim/sdram_test/src/data_gen/datagene.v  
vlog E:/Project/ModelSim/sdram_test/src/fifo/wrfifo.v  
vlog E:/Project/ModelSim/sdram_test/src/fifo/write_fifo_module.v  
vlog E:/Project/ModelSim/sdram_test/src/fifo/fifo_test_module.v  
vlog E:/Project/ModelSim/sdram_test/src/fifo_test_module_tb.v  
   
#Start simulation  
vsim  -novopt work.fifo_test_module_tb  
   
#add wave  
add wave -hex /*  
  
  
   
run 200us

下面就可以观察波形了。。:)))

首先,可以看到,在385000ps时,PLL模块初始化完成,可以看到clk_20m,clk_100m,clk_sdram的波形了。

系统运行到约100us时,产生了第一次写FIFO请求信号

下面为了观察FIFO的时序,我们把波形放大:

图不是很清晰,只能看清大概,下面具体分析一下:(两个图是连续的,游标位置没有变)

clk_20m:读FIFO时钟

clk_100m:写FIFO时钟(可能会觉得奇怪,为什么读FIFO写到SDRAM的时钟比 写FIFO时钟还慢,确实,原本都是100M,我为了实验,把读时钟改为了20M)

sdram_wr_ack : FIFO读请求信号(这里命名很奇怪。。是因为后面要接SDRAM,写SDRAM即:读FIFO的数据写到SDRAM),高电平有效

write_fifo_req :表示FIFO写请求信号,高电平有效

write_fifo_data_in :写入FIFO的数据(由数据发生器产生)

sys_data_in :FIFO读出的数据

wrf_use:当前FIFO队列里存在的数据个数

 先看写FIFO的过程,每一个时钟(clk_100m)上升沿,判断写请求信号是否为高电平,如果为高电平,那么就将数据线上的数据写入FIFO,然后在下一个时钟上升沿,wrf_use增加1,表示FIFO队列里的数据增加了一个。

细心的朋友可能会发现,其实在这一过程中,读请求信号一直为高电平,仔细分析这两张图片,大概可以得出如下判断:

在每个读时钟的上升沿,首先判断读请求信号是否为高电平,若为高电平,再判断FIFO是否为空,如果不为空,那么在下一个read_clock的上升沿将数据读出,具体可看下图:

不难发现,第一个read_clock上升沿,FIFO为空;第二个上升沿,FIFO不为空,准备开始读出数据;第三个上升沿,读出数据,同时wrf_use要减1,

而wrf_use是由write_clock维护的,故在下一个写时钟的上升沿,更新wrrf_use (8变成7)。

---------------------------------------------------------------------------------------------------------------------

再看一副图,下一个8*16bit 数据,道理还是和上面的一样。

下面看一下读请求为低电平的情况

不难看出,wrf_use随着数据的写入而增加

在累积了一段时间数据后,又迎来了读请求信号,看下图:(还是:上升沿判断,下一个上升沿读取。。。),不再赘述

 

 

 

 

 

 

 

 

posted @ 2012-12-25 11:25  天马行空W  阅读(2048)  评论(0编辑  收藏  举报