上一節已經實現了DDR3的寫數據的驅動、命令端口、寫數據端口的介紹以及DDR3的用戶數據長度、突發字節等相關寄存器的配置,最終成功地實現了向DDR3中寫入一個0-15的連續遞增的數據。這一節,就在上一節的基礎上繼續實現DDR3的讀時序及其仿真。
DDR3讀數據的時序:
用戶界面的讀取路徑使用簡單的64深度FIFO結構來保存從Read事務返回的數據。Read DataFIFO中的空標志(pX_rd_empty)可用作數據有效指示符。每當pX_rd_empty置為無效時,pX_rd_data總線上就會出現無效數據。要將數據從讀數據FIFO傳輸到FPGA邏輯,必須在pX_rd_clk的上升沿置位pX_rd_en信號.pX_rd_data總線在pX_rd_clk的上升沿跳變。pX_rd_en信號可以如果需要,pX_rd_empty信號可以始終保持斷言,並且可以用作數據有效指示符。
架構和框圖:
注意:在Rd_en 有效之前,先保證READ DATA FIFO中有一定的數據,可以先執行讀指令,P1端口專門用來,從DDR3芯片中讀取數據
時序設計:
注意:我們在寫入數據,后讀數據是這兩個時間段之間,要留有一定的時間間隔。防止發生沖突。在此實驗中我留出的時間間隔為10個時鍾。相關介紹和思路說明:
p0_cmd_en ;是上一節我們講的寫入時產生的命令。
rd_flag : 是表示當前讀數據的工作標志,高電平代表正在進行讀數據。
p1_cmd_en:讀數據端口的相關命令使能,在p0_cmd_en拉高后,也被拉高,當此信號被拉高是可以向DDR 寫入相關寄存器的配置命令。即把p1端口配置成讀數據的端口和狀態。
rd_cnt :該信號表示延遲以及寫入的數據個數。
p1_rd_en:在該信號為高是,開始讀取DDR中的數據。
相關代碼以及邏輯實現:
(1)端口設置:
1 //ddr interface (read part) 2 output wire p1_rd_en , 3 output reg p1_cmd_en , 4 output wire [5:0] p1_cmd_bl , 5 output wire [2:0] p1_cmd_instr , 6 output reg [29:0] p1_cmd_addr , 7 output wire [63:0] p1_rd_data ,
(2)中間變量:
1 localparam RD_END = 'd25 ;//計數結束終端 2 reg rd_flag ;//讀工作狀態標志 3 reg [4:0] rd_cnt ;
(3)相關時序配置:
1 /******************************************************* 2 ***************read part ******************************* 3 *********************************************************/ 4 //rd_flag 5 always @(posedge sclk or negedge s_rst_n)begin 6 if(!s_rst_n) 7 rd_flag <= 1'b0 ; 8 else if(p0_cmd_en == 1'b1 ) 9 rd_flag <= 1'b1 ; 10 else if(rd_cnt == 5'd25) 11 rd_flag <= 1'b0 ; 12 13 end 14 //p1_rd_en 15 always @(posedge sclk or negedge s_rst_n)begin 16 if(!s_rst_n) 17 p1_cmd_en <= 1'b0 ; 18 else if(p0_cmd_en==1'b1) 19 p1_cmd_en <= 1'b1 ; 20 else if(rd_cnt == 1'b1 ) 21 p1_cmd_en <= 1'b0 ; 22 23 end 24 //rd_cnt 25 always @(posedge sclk or negedge s_rst_n)begin 26 if(!s_rst_n) 27 rd_cnt <= 5'd0; 28 else if(rd_flag == 1'b1 ) 29 rd_cnt <= rd_cnt + 1'b1 ; 30 else if(rd_flag == 1'b0 ) 31 rd_cnt <= 5'd0 ; 32 end 33 34 //p1_cmd_addr 35 always @(posedge sclk or negedge s_rst_n)begin 36 if(!s_rst_n) 37 p1_cmd_addr <= 'd0 ; 38 else if(p1_cmd_en==1'b1 ) 39 p1_cmd_addr <= p1_cmd_addr+ 'd64; 40 end 41
(4)相關寄存器以及命令
1 //讀數據端口 2 assign p1_cmd_bl = 'd7 ; 3 assign p1_cmd_instr = 3'b001 ; 4 assign p1_rd_en = (rd_cnt>=10 && rd_cnt <= RD_END) ? 1'b1:1'b0;
TB測試文件:因為上一節我們已經成功地寫入了數據,所以這次在上次的基礎上,進行仿真和測試。
寫入數據和讀出數據的總代碼:
1 module ddr_drive( 2 3 //systerm signals 4 input wire sclk , 5 input wire s_rst_n , 6 //ddr interface (write part) 7 output reg p0_wr_en , 8 output wire p0_cmd_en , 9 output wire [5:0] p0_cmd_bl , 10 output wire [2:0] p0_cmd_instr , 11 output wire [29:0]p0_cmd_addr , 12 output reg [63:0]p0_wr_data , 13 output wire [7:0] p0_wr_mask , 14 //ddr interface (read part) 15 output wire p1_rd_en , 16 output reg p1_cmd_en , 17 output wire [5:0] p1_cmd_bl , 18 output wire [2:0] p1_cmd_instr , 19 output reg [29:0] p1_cmd_addr , 20 output wire [63:0] p1_rd_data , 21 //debug signals 22 input wire wr_trig 23 24 ); 25 /********************************************************************** 26 ****************define parameter and signals************************** 27 ***********************************************************************/ 28 localparam RD_END = 'd25 ;//計數結束終端 29 30 reg wr_en_neg ;//negedge flag 31 32 reg rd_flag ;//讀工作狀態標志 33 reg [4:0] rd_cnt ; 34 /********************************************************************** 35 *******************main code ***************************************** 36 ***********************************************************************/ 37 //寫數據端口 38 assign p0_cmd_bl = 'd15 ; 39 assign p0_cmd_instr = 3'b000 ; 40 41 assign p0_cmd_en = ~p0_wr_en & wr_en_neg ; 42 assign p0_cmd_addr = 'd0 ; 43 assign p0_wr_mask = 8'h0 ; 44 45 //讀數據端口 46 assign p1_cmd_bl = 'd7 ; 47 assign p1_cmd_instr = 3'b001 ; 48 assign p1_rd_en = (rd_cnt>=10 && rd_cnt <= RD_END) ? 1'b1:1'b0; 49 //assign p1_cmd_addr = 'd0 ; 50 //p0_wr_en 51 always @(posedge sclk or negedge s_rst_n)begin 52 if(!s_rst_n) 53 p0_wr_en <= 1'b0 ; 54 else if(p0_wr_data>=15) 55 p0_wr_en <= 1'b0 ; 56 else if (wr_trig == 1'b1) 57 p0_wr_en <= 1'b1 ; 58 end 59 60 //p0_wr_data 61 always @(posedge sclk or negedge s_rst_n)begin 62 if(!s_rst_n) 63 p0_wr_data <= 'd0 ; 64 else if(p0_wr_en == 1'b1 ) 65 p0_wr_data <= p0_wr_data + 1'b1 ; 66 67 end 68 69 70 //wr_en_neg.邊沿檢測 71 always @(posedge sclk )begin 72 wr_en_neg <= p0_wr_en ; 73 74 end 75 76 /******************************************************* 77 ***************read part ******************************* 78 *********************************************************/ 79 //rd_flag 80 always @(posedge sclk or negedge s_rst_n)begin 81 if(!s_rst_n) 82 rd_flag <= 1'b0 ; 83 else if(p0_cmd_en == 1'b1 ) 84 rd_flag <= 1'b1 ; 85 else if(rd_cnt == 5'd25) 86 rd_flag <= 1'b0 ; 87 88 end 89 //p1_rd_en 90 always @(posedge sclk or negedge s_rst_n)begin 91 if(!s_rst_n) 92 p1_cmd_en <= 1'b0 ; 93 else if(p0_cmd_en==1'b1) 94 p1_cmd_en <= 1'b1 ; 95 else if(rd_cnt == 1'b1 ) 96 p1_cmd_en <= 1'b0 ; 97 98 end 99 //rd_cnt 100 always @(posedge sclk or negedge s_rst_n)begin 101 if(!s_rst_n) 102 rd_cnt <= 5'd0; 103 else if(rd_flag == 1'b1 ) 104 rd_cnt <= rd_cnt + 1'b1 ; 105 else if(rd_flag == 1'b0 ) 106 rd_cnt <= 5'd0 ; 107 end 108 109 //p1_cmd_addr 110 always @(posedge sclk or negedge s_rst_n)begin 111 if(!s_rst_n) 112 p1_cmd_addr <= 'd0 ; 113 else if(p1_cmd_en==1'b1 ) 114 p1_cmd_addr <= p1_cmd_addr+ 'd64; 115 end 116 117 118 endmodule
頂層的代碼變化以及例化的變化:

1 module ddr_top( 2 3 4 5 //sysyterm interface 6 input c3_sys_clk , 7 input c3_sys_rst_i , 8 //ddr3 interface 9 inout [15:0] mcb3_dram_dq , 10 output wire [12:0] mcb3_dram_a , 11 output wire [2:0] mcb3_dram_ba , 12 output wire mcb3_dram_ras_n , 13 output wire mcb3_dram_cas_n , 14 output wire mcb3_dram_we_n , 15 output wire mcb3_dram_odt , 16 output wire mcb3_dram_reset_n , 17 output wire mcb3_dram_cke , 18 output wire mcb3_dram_dm , 19 inout mcb3_dram_udqs , 20 inout mcb3_dram_udqs_n , 21 inout mcb3_rzq , 22 inout mcb3_zio , 23 output wire mcb3_dram_udm , 24 inout mcb3_dram_dqs , 25 inout mcb3_dram_dqs_n , 26 output wire mcb3_dram_ck , 27 output wire mcb3_dram_ck_n , 28 //debug 29 input wire wr_trig , 30 input wire c3_calib_done 31 32 ); 33 34 /********************************************************************* 35 *************************signals define******************************** 36 **********************************************************************/ 37 38 39 40 //ddr interface (write modle ) 41 wire p0_wr_en ; 42 wire p0_cmd_en ; 43 wire [5:0] p0_cmd_bl ; 44 wire [2:0] p0_cmd_instr ; 45 wire [29:0]p0_cmd_addr ; 46 wire [63:0]p0_wr_data ; 47 wire [7:0] p0_wr_mask ; 48 49 //ddr interface (read part) 50 wire p1_rd_en ; 51 wire p1_cmd_en ; 52 wire [5:0] p1_cmd_bl ; 53 wire [2:0] p1_cmd_instr ; 54 wire [29:0] p1_cmd_addr ; 55 wire [63:0] p1_rd_data ; 56 57 58 59 60 61 /********************************************************************* 62 ****************************main code ******************************** 63 **********************************************************************/ 64 ddr_drive ddr_drive_inst( 65 66 //systerm signals 67 .sclk (c3_clk0 ), 68 .s_rst_n (~c3_rst0 ), 69 //ddr interface 70 .p0_wr_en (p0_wr_en ), 71 .p0_cmd_en (p0_cmd_en ), 72 .p0_cmd_bl (p0_cmd_bl ), 73 .p0_cmd_instr (p0_cmd_instr), 74 .p0_cmd_addr (p0_cmd_addr ), 75 .p0_wr_data (p0_wr_data ), 76 .p0_wr_mask (p0_wr_mask ), 77 //ddr interface (read part) 78 .p1_rd_en (p1_rd_en ), 79 .p1_cmd_en (p1_cmd_en ), 80 .p1_cmd_bl (p1_cmd_bl ), 81 .p1_cmd_instr (p1_cmd_instr ), 82 .p1_cmd_addr (p1_cmd_addr ), 83 .p1_rd_data (p1_rd_data ), 84 //debug signals 85 .wr_trig (wr_trig ) 86 87 ); 88 89 90 91 92 93 mig_39_2 # ( 94 .C3_P0_MASK_SIZE(8), 95 .C3_P0_DATA_PORT_SIZE(64), 96 .C3_P1_MASK_SIZE(8), 97 .C3_P1_DATA_PORT_SIZE(64), 98 .DEBUG_EN(0), 99 .C3_MEMCLK_PERIOD(3200),//當前的時鍾周期 100 .C3_CALIB_SOFT_IP("TRUE"), 101 .C3_SIMULATION("TRUE"),//仿真 102 .C3_RST_ACT_LOW(1),//復位信號的配置 103 .C3_INPUT_CLK_TYPE("SINGLE_ENDED"),//時鍾模式 104 .C3_MEM_ADDR_ORDER("BANK_ROW_COLUMN"),//內存讀取的順序模式 105 .C3_NUM_DQ_PINS(16), 106 .C3_MEM_ADDR_WIDTH(13), 107 .C3_MEM_BANKADDR_WIDTH(3) 108 ) 109 u_mig_39_2 ( 110 //DDR3 的接口 111 .c3_sys_clk (c3_sys_clk), //input DDR3的參考時鍾 112 .c3_sys_rst_i (c3_sys_rst_i), //input DDR3的復位信號 113 114 .mcb3_dram_dq (mcb3_dram_dq), 115 .mcb3_dram_a (mcb3_dram_a), 116 .mcb3_dram_ba (mcb3_dram_ba), 117 .mcb3_dram_ras_n (mcb3_dram_ras_n), 118 .mcb3_dram_cas_n (mcb3_dram_cas_n), 119 .mcb3_dram_we_n (mcb3_dram_we_n), 120 .mcb3_dram_odt (mcb3_dram_odt), 121 .mcb3_dram_cke (mcb3_dram_cke), 122 .mcb3_dram_ck (mcb3_dram_ck), 123 .mcb3_dram_ck_n (mcb3_dram_ck_n), 124 .mcb3_dram_dqs (mcb3_dram_dqs), 125 .mcb3_dram_dqs_n (mcb3_dram_dqs_n), 126 .mcb3_dram_udqs (mcb3_dram_udqs), // for X16 parts 127 .mcb3_dram_udqs_n (mcb3_dram_udqs_n), // for X16 parts 128 .mcb3_dram_udm (mcb3_dram_udm), // for X16 parts 129 .mcb3_dram_dm (mcb3_dram_dm), 130 .mcb3_dram_reset_n (mcb3_dram_reset_n), 131 132 133 134 135 //sppourt for user 136 .c3_clk0 (c3_clk0),//output 輸出給用戶提供的 137 .c3_rst0 (c3_rst0),//output 輸出給用戶提供的 138 139 140 141 .c3_calib_done (c3_calib_done), 142 .mcb3_rzq ( mcb3_rzq ), 143 .mcb3_zio (mcb3_zio ), 144 145 //P0,p1表示兩個用戶會接口 146 /*********************command path****************************/ 147 .c3_p0_cmd_clk (c3_clk0 ), //命令FIFO的用戶時鍾。 FIFO信號是 在這個時鍾的上升沿捕獲。 148 .c3_p0_cmd_en (p0_cmd_en), //該高電平有效信號是用於寫入的寫入使能信號命令FIFO。 149 .c3_p0_cmd_instr (p0_cmd_instr), //當前指令的命令代碼。 位0表示READ / WRITE選擇,Bit 1為Auto預充電啟用,位2代表刷新總是優先考慮 150 .c3_p0_cmd_bl (p0_cmd_bl), //當前用戶字數的突發長度交易。 突發長度編碼為0到63,代表1到64個用戶詞(例如,6'b00011 是一個突發長度4的交易)。 用戶字寬等於端口寬度(例如,突發長度為3 64位端口傳輸3 x 64位用戶字= 192位 總)。 151 /*當前事務的字節起始地址。地址 152 必須與端口大小對齊: 153 32位端口:低兩位必須為0。 154 64位端口:低三位必須為0。 155 128位端口:低4位必須為0*/ 156 .c3_p0_cmd_byte_addr (p0_cmd_addr), 157 .c3_p0_cmd_empty ( ), //這個命令FIFO的高電平有效空標志 158 .c3_p0_cmd_full ( ), //此高電平有效輸出是命令的man標志 159 160 /*********************write cmd****************************/ 161 .c3_p0_wr_clk (c3_clk0 ),//該信號是寫數據FIFO的用戶時鍾 162 /*該高電平有效信號是寫使能 163 用於寫數據FIFO。它表明了 164 pX_wr_data上的值有效 165 加載到FIFO。數據已加載 166 pX_wr_clk的上升沿時 167 pX_wr_en = 1且pX_wr_full = 0。*/ 168 .c3_p0_wr_en (p0_wr_en), 169 .c3_p0_wr_mask (p0_wr_mask),//寫數據的掩碼, 170 /*寫入要寫入的數據值 171 數據FIFO並發送到內存。 PX_SIZE 172 可以是32位,64位或128位,具體取決於 173 端口配置*/ 174 .c3_p0_wr_data (p0_wr_data), 175 .c3_p0_wr_full ( ), //高有效的滿信號 176 .c3_p0_wr_empty ( ),//高有效的空信號 177 /*寫入數據FIFO的計數值。這個 178 輸出表示有多少用戶單詞 179 在FIFO中(從1到64)。計數值為 180 0表示FIFO為空。這個信號 181 延遲的延遲比 182 pX_wr_empty標志。因此,FIFO 183 可能是空的或經歷不足 184 即使計數不為0。*/ 185 .c3_p0_wr_count ( ), 186 .c3_p0_wr_underrun ( ),//高電平有效,欠載標志。 187 .c3_p0_wr_error ( ), 188 /*********************read cmd****************************/ 189 .c3_p0_rd_clk (0 ),//該信號是du數據FIFO的用戶時鍾 190 .c3_p0_rd_en (0 ), 191 .c3_p0_rd_data ( ), 192 .c3_p0_rd_full ( ), 193 .c3_p0_rd_empty ( ), 194 .c3_p0_rd_count ( ), 195 .c3_p0_rd_overflow ( ), 196 .c3_p0_rd_error ( ), 197 /********************P1 user port************************/ 198 .c3_p1_cmd_clk (c3_clk0 ), 199 .c3_p1_cmd_en (p1_cmd_en ), 200 .c3_p1_cmd_instr (p1_cmd_instr ), 201 .c3_p1_cmd_bl (p1_cmd_bl ), 202 .c3_p1_cmd_byte_addr (p1_cmd_addr ), 203 .c3_p1_cmd_empty ( ), 204 .c3_p1_cmd_full ( ), 205 206 .c3_p1_wr_clk ( 0 ), 207 .c3_p1_wr_en ( 0 ), 208 .c3_p1_wr_mask ( 0 ), 209 .c3_p1_wr_data ( 64'h0 ), 210 .c3_p1_wr_full ( ), 211 .c3_p1_wr_empty ( ), 212 .c3_p1_wr_count ( ), 213 .c3_p1_wr_underrun ( ), 214 .c3_p1_wr_error ( ), 215 //讀數據用到的端口 216 .c3_p1_rd_clk ( c3_clk0 ), 217 .c3_p1_rd_en ( p1_rd_en ), 218 .c3_p1_rd_data ( p1_rd_data ), 219 .c3_p1_rd_full ( ), 220 .c3_p1_rd_empty ( ), 221 .c3_p1_rd_count ( ), 222 .c3_p1_rd_overflow ( ), 223 .c3_p1_rd_error ( ) 224 ); 225 226 227 endmodule
仿真結果:
特備注意:關於這里的存儲地址字節以及用戶數據突發長度之間的關系要自行理解清楚