DDR3:添加外围FIFO
Posted xianyufpga
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DDR3:添加外围FIFO相关的知识,希望对你有一定的参考价值。
之前完成了 IP 核的读、写控制,但是无论是读还是写,每次都只能发送一次命令和一次突发的数据,也就是说在读或者写的过程中,当再来一次读或者写的命令时,我们的控制器是没有办法执行的,为了解决掉这个问题,我们可以加存储器将没有办法及时执行的命令和数据缓存起来,当本次操作结束后,可以从缓冲器内取下一次需要执行的命令和数据,这样就可以防止命令和数据丢失。
1、FIFO设计
如图所示,我们添加了 4 个模块:
① rd_data_fifo_ctrl 为读数据的缓冲 fifo
② rd_cmd_fifo_ctrl 为读命令的缓冲 fifo
③ wr_data_fifo_ctrl 为写数据的缓冲 fifo
④ wr_cmd_fifo_ctrl 为写命令的缓冲 fifo。
红线为读使能,绿线为写使能,蓝色为时钟和数据,p1代表读数据和读命令,p2代表写数据和写命令。
当读、写命令 FIFO 中有命令时,对应的 empty 信号将会为低电平,因此我们可以将读、写命令FIFO的 empty 信号取反后,作为仲裁模块的rd_req和wr_req。同时,仲裁模块输出的读、写开始信号可以作为读、写命令 FIFO 的读使能,根据仲裁模块输出的读、写开始信号可以读出读、写命令 FIFO 内的命令、地址、突发长度等,rd_ctrl 和 wr_ctrl 模块根据命令 FIFO 读出的信息可以进行相应的操作。rd_ctrl 模块读出的数据可以存入到 rd_data_fifo_ctrl,因此 rd_data_valid可以作为 rd_data_fifo_ctrl 的写使能,rd_data_128bit 可以作为 rd_data_fifo_ctrl 的写数据。wr_ctrl 模块输入的数据可从rd_data_fifo_ctrl 中读出,因此 data_req 可以作为 wr_data_fifo_ctrl 的读使能,data_128bit 可以作为 wr_data_fifo_ctrl 的读数据。
需要注意的是,虽然名字上是读和写,其他FIFO都是写入一些东西给 rd_ctrl 或 wr_ctrl,而真正从 IP 里读出数据的只有 rd_data_fifo。
二、FIFO 例化
综上所述,可以得出 4 个 FIFO 模块在 top 层中的例化:
1 //rd_fifo --------------------------------------- 2 wire p1_clk ; 3 wire p1_rd_en ; 4 wire [127:0] p1_rd_data ; 5 wire p1_rd_full ; 6 wire p1_rd_empty ; 7 wire [ 6:0] p1_rd_count ; 8 wire [ 2:0] p1_cmd_instr ; 9 wire [ 6:0] p1_cmd_bl ; 10 wire [27:0] p1_cmd_addr ; 11 wire p1_cmd_en ; 12 wire p1_cmd_empty ; 13 //wr_fifo --------------------------------------- 14 wire p2_clk ; 15 wire [ 2:0] p2_cmd_instr ; 16 wire [ 6:0] p2_cmd_bl ; 17 wire [27:0] p2_cmd_addr ; 18 wire p2_cmd_en ; 19 wire p2_cmd_empty ; 20 wire [15:0] p2_wr_mask ; 21 wire [127:0] p2_wr_data ; 22 wire p2_wr_en ; 23 wire p2_wr_full ; 24 wire p2_wr_empty ; 25 wire [ 5:0] p2_wr_count ; 26 27 //FIFO 读走 rd_ctrl 数据 --------------------------------------------------- 28 rd_data_fifo_ctrl u_rd_data_fifo_ctrl 29 ( 30 .rst (ui_clk_sync_rst | (~init_calib_complete)), // input 31 .wr_clk (ui_clk ), // input from rd_ctrl 32 .rd_clk (p1_clk ), // input 33 .din (rd_data_128bit ), // input [127 : 0] from rd_ctrl 34 .wr_en (rd_data_valid ), // input from rd_ctrl 35 .rd_en (p1_rd_en ), // input 36 .dout (p1_rd_data ), // output [127 : 0] 37 .full (p1_rd_full ), // output 38 .empty (p1_rd_empty ), // output 39 .rd_data_count (p1_rd_count ), // output [6 : 0] 40 .wr_rst_busy ( ), // output 41 .rd_rst_busy ( ) // output 42 ); 43 44 //FIFO 送入 rd_ctrl 命令 ---------------------------------------------------- 45 rd_cmd_fifo_ctrl u_rd_cmd_fifo_ctrl 46 ( 47 .rst (ui_clk_sync_rst | (~init_calib_complete)), // input 48 .wr_clk (p1_clk ), // input 49 .rd_clk (ui_clk ), // input from rd_ctrl 50 .din ({p1_cmd_instr,p1_cmd_bl,p1_cmd_addr} ), // input [37 : 0] 51 .wr_en (p1_cmd_en ), // input 52 .rd_en (rd_cmd_start ), // input from rd_ctrl and arbit 53 .dout ({rd_cmd_instr,rd_cmd_bl,rd_cmd_addr} ), // output [37 : 0] to rd_ctrl 54 .full ( ), // output 55 .empty (p1_cmd_empty ), // output to arbit 56 .wr_rst_busy ( ), // output 57 .rd_rst_busy ( ) // output 58 ); 59 assign rd_req = ~p1_cmd_empty; //有读命令,empty为空,产生读请求 60 61 //FIFO 送入 wr_ctrl 命令 ----------------------------------------------------- 62 wr_cmd_fifo_ctrl u_wr_cmd_fifo_ctrl 63 ( 64 .rst (ui_clk_sync_rst | (~init_calib_complete)), // input 65 .wr_clk (p2_clk ), // input 66 .rd_clk (ui_clk ), // input from wr_ctrl 67 .din ({p2_cmd_instr,p2_cmd_bl,p2_cmd_addr} ), // input [37 : 0] 68 .wr_en (p2_cmd_en ), // input 69 .rd_en (wr_cmd_start ), // input from wr_ctrl and arbit 70 .dout ({wr_cmd_instr,wr_cmd_bl,wr_cmd_addr} ), // output [37 : 0] to wr_ctrl 71 .full ( ), // output 72 .empty (p2_cmd_empty ), // output to arbit 73 .wr_rst_busy ( ), // output 74 .rd_rst_busy ( ) // output 75 ); 76 assign wr_req = ~p2_cmd_empty; //有写命令,empty为空,产生写请求 77 78 79 //FIFO 送入 wr_ctrl 数据 ------------------------------------------------------ 80 wr_data_fifo_ctrl u_wr_data_fifo_ctrl 81 ( 82 .rst (ui_clk_sync_rst | (~init_calib_complete)), // input 83 .wr_clk (p2_clk ), // input 84 .rd_clk (ui_clk ), // input from wr_ctrl 85 .din ({p2_wr_mask,p2_wr_data} ), // input [143 : 0] 86 .wr_en (p2_wr_en ), // input 87 .rd_en (data_req ), // input from wr_ctrl and arbit 88 .dout ({wr_cmd_mask,data_128bit} ), // output [143 : 0] to wr_ctrl 89 .full (p2_wr_full ), // output 90 .empty (p2_wr_empty ), // output 91 .wr_data_count (p2_wr_count ), // output [6 : 0] 92 .wr_rst_busy ( ), // output 93 .rd_rst_busy ( ) // output 94 );
三、仿真验证
本次仿真将之前的语句都删除了,重新设计写数据和写命令,并传导给FIFO,通过 FIFO 再给 DDR3 IP。
1 //----------------------------------------------------------- 生成时钟 2 initial begin 3 p_clk = 0; 4 forever 5 #(`Clock/4) p_clk = ~p_clk; 6 end 7 8 //----------------------------------------------------------- 强制赋值 9 initial begin 10 //引入信号 11 force rst = u_top_ddr3_hdmi.u_wr_ctrl.rst; 12 //引出信号 13 force u_top_ddr3_hdmi.p1_clk = p_clk; 14 force u_top_ddr3_hdmi.p2_clk = p_clk; 15 p2_wr_data = 0 ; force u_top_ddr3_hdmi.p2_wr_data = p2_wr_data; 16 p2_wr_en = 0 ; force u_top_ddr3_hdmi.p2_wr_en = p2_wr_en; 17 p2_wr_mask = 0 ; force u_top_ddr3_hdmi.p2_wr_mask = p2_wr_mask; 18 p2_cmd_instr = 0 ; force u_top_ddr3_hdmi.p2_cmd_instr = p2_cmd_instr; 19 p2_cmd_bl = 64; force u_top_ddr3_hdmi.p2_cmd_bl = p2_cmd_bl; 20 p2_cmd_addr = 0 ; force u_top_ddr3_hdmi.p2_cmd_addr = p2_cmd_addr; 21 p2_cmd_en = 0 ; force u_top_ddr3_hdmi.p2_cmd_en = p2_cmd_en; 22 end 23 24 //----------------------------------------------------------- 设计写数据 25 task wr_data; 26 integer i; 27 begin 28 @(negedge rst); 29 repeat (100) @(posedge p_clk); 30 for(i=0;i<64;i=i+1) begin 31 p2_wr_en = 1; 32 p2_wr_data = {96‘d0,i}; 33 @(posedge p_clk); 34 end 35 p2_wr_en = 0; 36 p2_wr_data = 0; 37 @(posedge p_clk); 38 end 39 endtask 40 41 initial begin 42 #100 43 wr_data(); 44 end 45 46 //----------------------------------------------------------- 设计写命令 47 task wr_cmd; 48 begin 49 @(negedge p2_wr_en); 50 @(posedge p_clk); 51 p2_cmd_en = 1; 52 @(posedge p_clk); 53 p2_cmd_en = 0; 54 end 55 endtask 56 57 initial begin 58 #100 59 wr_cmd(); 60 end
Modelsim仿真结果如下所示:
由仿真可以看到,写的部分都正常工作,说明这次的设计没有问题。
参考资料:威三学院FPGA教程
以上是关于DDR3:添加外围FIFO的主要内容,如果未能解决你的问题,请参考以下文章