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 = {96d0,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的主要内容,如果未能解决你的问题,请参考以下文章

基于FPGA的车牌识别

用户端实现DDR3 SDRAM写读控制

zc706调试ddr3只能用vivado吗

SOC芯片的FPGA原型验证

为啥我在vivado生成不了 ddr3 ip核

快捷键向选中的代码外围添加需要的代码块