上一節已經成功地字節仿照DDR的官方例子,寫出了自己的驅動,並且謝了下關的激勵文件,接下來就主要介紹怎么樣實現DDR的寫數據操作,以及相關端口的介紹,首先根據我們的例子以及我們上一節自己寫的仿真,對相關端口介紹一下。
DDR IP中最核心的代碼就是u_mig_39_2中的端口例化(這個名稱是你在創建DDR IP時自己默認的,當然依然可以修改),其中的接口可以分為倆大部分,可以總結為以下的結構框圖,方便理解和記憶(VISIO畫圖的時候字體選的6PT,有點模糊哈)。
其中以下幾個信號是提供給用戶使用的,用戶可以用這兩個端口來設置DDR IP的參考時鍾和復位信號,都是輸入類型的信號(針對DDR來說)
c3_sys_clk :input DDR3的參考時鍾
c3_sys_rst_i :input DDR3的復位信號
c3_calib_done :當DDR的所有初始化信息完成后此信號拉高,才能對DDR驚醒相關的操作
mcb3_rzq :硬件接電阻的端口
mcb3_zio:硬件接電阻的端口
命令路徑端口及作用:
c3_p0_cmd_clk //命令FIFO的用戶時鍾。 FIFO信號是 在這個時鍾的上升沿捕獲
c3_p0_cmd_en //該高電平有效信號是用於寫入的寫入使能信號命令FIFO。
c3_p0_cmd_instr //當前指令的命令代碼。 位0表示READ / WRITE選擇,Bit 1為Auto預充電啟用,位2代表刷新總是優先考慮
Write: 3'b000 Read: 3'b001 Write with Auto Precharge: 3'b010 Read with Auto Precharge: 3'b011 Refresh: 3'b1xx |
c3_p0_cmd_bl//當前用戶字數的突發長度交易。 突發長度編碼為0到63,代表1到64個用戶詞(例如,6'b00011 是一個突發長度4的交易)。 用戶字寬等於端口寬度(例如,突發長度為3 64位端口傳輸3 x 64位用戶字= 192位 總)。
c3_p0_cmd_byte_addr/*當前事務的字節起始地址。地址必須與端口大小對齊: 32位端口:低兩位必須為0。 64位端口:低三位必須為0。 128位端口:低4位必須為0*/
c3_p0_cmd_empty /這個命令FIFO的高電平有效空標志
c3_p0_cmd_full/此高電平有效輸出是命令的man標志l
路徑命令的時序:
用戶界面的命令路徑使用簡單的4深度FIFO結構來保存待定命令。 請求的指令類型,地址和突發長度事務全部加載到此命令FIFO中。 來自的完整標志(pX_cmd_full)信號命令FIFO必須為低電平才能將新命令接收到FIFO中在pX_cmd_clk的上升沿期間,pX_cmd_en被置位。 否則,命令是忽略。 圖4-4和圖4-5說明了加載命令的協議FIFO。
寫命令的端口及其作用:
c3_p0_wr_clk ://該信號是寫數據FIFO的用戶時鍾
c3_p0_wr_en ://寫數據使能,高有效,拉高的時候才能寫入數據。8
c3_p0_wr_mask://寫數據的掩碼, 8BIT大小,適合一個數據的字節關聯的,決定了當前字節是否可以寫入到MEMERY當中
c3_p0_wr_data :寫入要寫入的數據值 數據FIFO並發送到內存。 PX_SIZE 可以是32位,64位或128位,具體取決於 端口配置
c3_p0_wr_full ://高有效的滿信號
c3_p0_wr_empty:/高有效的空信號
c3_p0_wr_count ://用來表示當前寫FIFO內部含有多少個數據
c3_p0_wr_underrun://
c3_p0_wr_error://可以當做一個狀態輸出
寫命令的時序:
用戶界面的寫入路徑使用簡單的64深度FIFO結構來保存數據准備將事務寫入內存。與命令FIFO類似,滿標志寫入數據FIFO中的(pX_wr_full)必須為低電平才能接受新數據在pX_wr_clk的上升沿期間pX_wr_en被置位時的FIFO。否則,數據被忽略了。如果滿標志為低,則pX_wr_data總線在上升時被捕獲到FIFO中,對於每一個PX_WR_EN有效的時候,PX_WR_DATA數據總線上必須有有效的數據。
命令路徑和寫路徑之間的協調:
命令路徑可以看成是一個命令 FIFO;寫數據路徑可以看成是一個寫數據 FIFO。
在執行寫命令之前要確保Write data FIFO中是有數據的。方法:先向Write data FIFO中寫入想要寫入到DDR內部的數據,之后再向CMD FIFO中寫入相應的命令。以確保在執行寫命令之前,我的write data FIFO中已有數據。
可以得出以下時序圖:
其中Wr_trig是調試信號,
接下來就是寫代碼了:
(1)端口:包括寫命令路徑和寫數據路徑,還包括時鍾、復位等信號。
1 module ddr_drive( 2 3 //systerm signals 4 input wire sclk , 5 input wire s_rst_n , 6 //ddr interface 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 //debug signals 15 input wire wr_trig 16 17 );
(2)產生寫使能信號:
1 //p0_wr_en 2 always @(posedge sclk or negedge s_rst_n)begin 3 if(!s_rst_n) 4 p0_wr_en <= 1'b0 ; 5 else if(p0_wr_data>=15) 6 p0_wr_en <= 1'b0 ; 7 else if (wr_trig == 1'b1) 8 p0_wr_en <= 1'b1 ; 9 end
(3)產生要寫入的數據
1 //p0_wr_data 2 always @(posedge sclk or negedge s_rst_n)begin 3 if(!s_rst_n) 4 p0_wr_data <= 'd0 ; 5 else if(p0_wr_en == 1'b1 ) 6 p0_wr_data <= p0_wr_data + 1'b1 ; 7 8 end
(4)配置寫入的命令,就是配置相關DDR的突發長度等等
1 assign p0_cmd_bl = 'd16 ; 2 assign p0_cmd_instr = 3'b000 ; 3 4 assign p0_cmd_en = ~p0_wr_en & wr_en_neg ; 5 assign p0_cmd_addr = 'd0 ; 6 assign p0_wr_mask = 8'h0 ;
(5)中間變量
1 reg wr_en_neg ;//negedge flag 2 //wr_en_neg.邊沿檢測 3 always @(posedge sclk )begin 4 wr_en_neg <= p0_wr_en ; 5 6 end 7
驅動和寫入一個0-15的數據的小驅動就完成了
(6)TB激勵文件
1 `timescale 1ps/1ps 2 3 4 module tb_ddr_top ; 5 6 reg ddr3_ref_clk ; 7 reg ddr3_rst_n ; 8 //bebug signals 9 reg wr_trig ; 10 wire c3_calib_done ; 11 12 //ddr3 interface 13 wire [15:0] mcb3_dram_dq ; 14 wire [12:0] mcb3_dram_a ; 15 wire [2:0] mcb3_dram_ba ; 16 wire mcb3_dram_ras_n ; 17 wire mcb3_dram_cas_n ; 18 wire mcb3_dram_we_n ; 19 wire mcb3_dram_odt ; 20 wire mcb3_dram_reset_n ; 21 wire mcb3_dram_cke ; 22 wire mcb3_dram_dm ; 23 wire mcb3_dram_udqs ; 24 wire mcb3_dram_udqs_n ; 25 wire mcb3_rzq ; 26 wire mcb3_zio ; 27 wire mcb3_dram_udm ; 28 wire mcb3_dram_dqs ; 29 wire mcb3_dram_dqs_n ; 30 wire mcb3_dram_ck ; 31 wire mcb3_dram_ck_n ; 32 33 34 35 36 parameter C3_MEMCLK_PERIOD = 3200; 37 38 39 initial begin 40 41 ddr3_ref_clk = 1; 42 ddr3_rst_n = 0; 43 #20000; 44 ddr3_rst_n = 1; 45 46 47 end 48 49 50 51 52 //produce debug signals 53 initial begin 54 wr_trig <= 0; 55 @(posedge c3_calib_done) 56 #100000 57 wr_trig <= 1; 58 #25600 59 wr_trig <= 0; 60 end 61 62 63 64 65 always #(C3_MEMCLK_PERIOD/2) ddr3_ref_clk = ~ddr3_ref_clk ; 66 67 68 ddr_top ddr_top_inst( 69 70 71 72 //sysyterm interface 73 .c3_sys_clk (ddr3_ref_clk ), 74 .c3_sys_rst_i (ddr3_rst_n ), 75 //ddr3 interface 76 .mcb3_dram_dq (mcb3_dram_dq ), 77 .mcb3_dram_a (mcb3_dram_a ), 78 .mcb3_dram_ba (mcb3_dram_ba ), 79 .mcb3_dram_ras_n (mcb3_dram_ras_n ), 80 .mcb3_dram_cas_n (mcb3_dram_cas_n ), 81 .mcb3_dram_we_n (mcb3_dram_we_n ), 82 .mcb3_dram_odt (mcb3_dram_odt ), 83 .mcb3_dram_reset_n (mcb3_dram_reset_n ), 84 .mcb3_dram_cke (mcb3_dram_cke ), 85 .mcb3_dram_dm (mcb3_dram_dm ), 86 .mcb3_dram_udqs (mcb3_dram_udqs ), 87 .mcb3_dram_udqs_n (mcb3_dram_udqs_n ), 88 .mcb3_rzq (mcb3_rzq ), 89 .mcb3_zio (mcb3_zio ), 90 .mcb3_dram_udm (mcb3_dram_udm ), 91 .mcb3_dram_dqs (mcb3_dram_dqs ), 92 .mcb3_dram_dqs_n (mcb3_dram_dqs_n ), 93 .mcb3_dram_ck (mcb3_dram_ck ), 94 .mcb3_dram_ck_n (mcb3_dram_ck_n ), 95 //debug signals 96 .wr_trig (wr_trig) , 97 .c3_calib_done (c3_calib_done) 98 99 ); 100 101 ddr3_model_c3 u_mem_c3( 102 .ck (mcb3_dram_ck), 103 .ck_n (mcb3_dram_ck_n), 104 .cke (mcb3_dram_cke), 105 .cs_n (1'b0), 106 .ras_n (mcb3_dram_ras_n), 107 .cas_n (mcb3_dram_cas_n), 108 .we_n (mcb3_dram_we_n), 109 .dm_tdqs ({mcb3_dram_udm,mcb3_dram_dm}), 110 .ba (mcb3_dram_ba), 111 .addr (mcb3_dram_a), 112 .dq (mcb3_dram_dq), 113 .dqs ({mcb3_dram_udqs,mcb3_dram_dqs}), 114 .dqs_n ({mcb3_dram_udqs_n,mcb3_dram_dqs_n}), 115 .tdqs_n (), 116 .odt (mcb3_dram_odt), 117 .rst_n (mcb3_dram_reset_n) 118 ); 119 120 // The PULLDOWN component is connected to the ZIO signal primarily to avoid the 121 // unknown state in simulation. In real hardware, ZIO should be a no connect(NC) pin. 122 PULLDOWN zio_pulldown3 (.O(zio3)); PULLDOWN rzq_pulldown3 (.O(rzq3)); 123 124 125 126 endmodule
(7)例化到頂層
module ddr_top(
//sysyterm interface
input c3_sys_clk ,
input c3_sys_rst_i ,
//ddr3 interface
inout [15:0] mcb3_dram_dq ,
output wire [12:0] mcb3_dram_a ,
output wire [2:0] mcb3_dram_ba ,
output wire mcb3_dram_ras_n ,
output wire mcb3_dram_cas_n ,
output wire mcb3_dram_we_n ,
output wire mcb3_dram_odt ,
output wire mcb3_dram_reset_n ,
output wire mcb3_dram_cke ,
output wire mcb3_dram_dm ,
inout mcb3_dram_udqs ,
inout mcb3_dram_udqs_n ,
inout mcb3_rzq ,
inout mcb3_zio ,
output wire mcb3_dram_udm ,
inout mcb3_dram_dqs ,
inout mcb3_dram_dqs_n ,
output wire mcb3_dram_ck ,
output wire mcb3_dram_ck_n ,
//debug
input wire wr_trig ,
input wire c3_calib_done
);
/*********************************************************************
*************************signals define********************************
**********************************************************************/
//ddr interface (write modle )
wire p0_wr_en ;
wire p0_cmd_en ;
wire [5:0] p0_cmd_bl ;
wire [2:0] p0_cmd_instr ;
wire [29:0]p0_cmd_addr ;
wire [63:0]p0_wr_data ;
wire [7:0] p0_wr_mask ;
/*********************************************************************
****************************main code ********************************
**********************************************************************/
ddr_drive ddr_drive_inst(
//systerm signals
.sclk (c3_clk0 ),
.s_rst_n (~c3_rst0 ),
//ddr interface
.p0_wr_en (p0_wr_en ),
.p0_cmd_en (p0_cmd_en ),
.p0_cmd_bl (p0_cmd_bl ),
.p0_cmd_instr (p0_cmd_instr),
.p0_cmd_addr (p0_cmd_addr ),
.p0_wr_data (p0_wr_data ),
.p0_wr_mask (p0_wr_mask ),
//debug signals
.wr_trig (wr_trig )
);
mig_39_2 # (
.C3_P0_MASK_SIZE(8),
.C3_P0_DATA_PORT_SIZE(64),
.C3_P1_MASK_SIZE(8),
.C3_P1_DATA_PORT_SIZE(64),
.DEBUG_EN(0),
.C3_MEMCLK_PERIOD(3200),//當前的時鍾周期
.C3_CALIB_SOFT_IP("TRUE"),
.C3_SIMULATION("TRUE"),//仿真
.C3_RST_ACT_LOW(1),//復位信號的配置
.C3_INPUT_CLK_TYPE("SINGLE_ENDED"),//時鍾模式
.C3_MEM_ADDR_ORDER("BANK_ROW_COLUMN"),//內存讀取的順序模式
.C3_NUM_DQ_PINS(16),
.C3_MEM_ADDR_WIDTH(13),
.C3_MEM_BANKADDR_WIDTH(3)
)
u_mig_39_2 (
//DDR3 的接口
.c3_sys_clk (c3_sys_clk), //input DDR3的參考時鍾
.c3_sys_rst_i (c3_sys_rst_i), //input DDR3的復位信號
.mcb3_dram_dq (mcb3_dram_dq),
.mcb3_dram_a (mcb3_dram_a),
.mcb3_dram_ba (mcb3_dram_ba),
.mcb3_dram_ras_n (mcb3_dram_ras_n),
.mcb3_dram_cas_n (mcb3_dram_cas_n),
.mcb3_dram_we_n (mcb3_dram_we_n),
.mcb3_dram_odt (mcb3_dram_odt),
.mcb3_dram_cke (mcb3_dram_cke),
.mcb3_dram_ck (mcb3_dram_ck),
.mcb3_dram_ck_n (mcb3_dram_ck_n),
.mcb3_dram_dqs (mcb3_dram_dqs),
.mcb3_dram_dqs_n (mcb3_dram_dqs_n),
.mcb3_dram_udqs (mcb3_dram_udqs), // for X16 parts
.mcb3_dram_udqs_n (mcb3_dram_udqs_n), // for X16 parts
.mcb3_dram_udm (mcb3_dram_udm), // for X16 parts
.mcb3_dram_dm (mcb3_dram_dm),
.mcb3_dram_reset_n (mcb3_dram_reset_n),
//sppourt for user
.c3_clk0 (c3_clk0),//output 輸出給用戶提供的
.c3_rst0 (c3_rst0),//output 輸出給用戶提供的
.c3_calib_done (c3_calib_done),
.mcb3_rzq ( mcb3_rzq ),
.mcb3_zio (mcb3_zio ),
//P0,p1表示兩個用戶會接口
/*********************command path****************************/
.c3_p0_cmd_clk (c3_clk0 ), //命令FIFO的用戶時鍾。 FIFO信號是 在這個時鍾的上升沿捕獲。
.c3_p0_cmd_en (p0_cmd_en), //該高電平有效信號是用於寫入的寫入使能信號命令FIFO。
.c3_p0_cmd_instr (p0_cmd_instr), //當前指令的命令代碼。 位0表示READ / WRITE選擇,Bit 1為Auto預充電啟用,位2代表刷新總是優先考慮
.c3_p0_cmd_bl (p0_cmd_bl), //當前用戶字數的突發長度交易。 突發長度編碼為0到63,代表1到64個用戶詞(例如,6'b00011 是一個突發長度4的交易)。 用戶字寬等於端口寬度(例如,突發長度為3 64位端口傳輸3 x 64位用戶字= 192位 總)。
/*當前事務的字節起始地址。地址
必須與端口大小對齊:
32位端口:低兩位必須為0。
64位端口:低三位必須為0。
128位端口:低4位必須為0*/
.c3_p0_cmd_byte_addr (p0_cmd_addr),
.c3_p0_cmd_empty ( ), //這個命令FIFO的高電平有效空標志
.c3_p0_cmd_full ( ), //此高電平有效輸出是命令的man標志
/*********************write cmd****************************/
.c3_p0_wr_clk (c3_clk0 ),//該信號是寫數據FIFO的用戶時鍾
/*該高電平有效信號是寫使能
用於寫數據FIFO。它表明了
pX_wr_data上的值有效
加載到FIFO。數據已加載
pX_wr_clk的上升沿時
pX_wr_en = 1且pX_wr_full = 0。*/
.c3_p0_wr_en (p0_wr_en),
.c3_p0_wr_mask (p0_wr_mask),//寫數據的掩碼,
/*寫入要寫入的數據值
數據FIFO並發送到內存。 PX_SIZE
可以是32位,64位或128位,具體取決於
端口配置*/
.c3_p0_wr_data (p0_wr_data),
.c3_p0_wr_full ( ), //高有效的滿信號
.c3_p0_wr_empty ( ),//高有效的空信號
/*寫入數據FIFO的計數值。這個
輸出表示有多少用戶單詞
在FIFO中(從1到64)。計數值為
0表示FIFO為空。這個信號
延遲的延遲比
pX_wr_empty標志。因此,FIFO
可能是空的或經歷不足
即使計數不為0。*/
.c3_p0_wr_count ( ),
.c3_p0_wr_underrun ( ),//高電平有效,欠載標志。
.c3_p0_wr_error ( ),
/*********************read cmd****************************/
.c3_p0_rd_clk (c3_p0_rd_clk ),//該信號是du數據FIFO的用戶時鍾
.c3_p0_rd_en (c3_p0_rd_en ),
.c3_p0_rd_data (c3_p0_rd_data ),
.c3_p0_rd_full (c3_p0_rd_full ),
.c3_p0_rd_empty (c3_p0_rd_empty),
.c3_p0_rd_count (c3_p0_rd_count),
.c3_p0_rd_overflow (c3_p0_rd_overflow),
.c3_p0_rd_error (c3_p0_rd_error ),
/********************P1 user port************************/
.c3_p1_cmd_clk (c3_p1_cmd_clk ),
.c3_p1_cmd_en (c3_p1_cmd_en ),
.c3_p1_cmd_instr (c3_p1_cmd_instr ),
.c3_p1_cmd_bl (c3_p1_cmd_bl ),
.c3_p1_cmd_byte_addr (c3_p1_cmd_byte_addr),
.c3_p1_cmd_empty (c3_p1_cmd_empty ),
.c3_p1_cmd_full (c3_p1_cmd_full ),
.c3_p1_wr_clk (c3_p1_wr_clk),
.c3_p1_wr_en (c3_p1_wr_en),
.c3_p1_wr_mask (c3_p1_wr_mask),
.c3_p1_wr_data (c3_p1_wr_data),
.c3_p1_wr_full (c3_p1_wr_full),
.c3_p1_wr_empty (c3_p1_wr_empty),
.c3_p1_wr_count (c3_p1_wr_count),
.c3_p1_wr_underrun (c3_p1_wr_underrun),
.c3_p1_wr_error (c3_p1_wr_error),
.c3_p1_rd_clk (c3_p1_rd_clk),
.c3_p1_rd_en (c3_p1_rd_en),
.c3_p1_rd_data (c3_p1_rd_data),
.c3_p1_rd_full (c3_p1_rd_full),
.c3_p1_rd_empty (c3_p1_rd_empty),
.c3_p1_rd_count (c3_p1_rd_count),
.c3_p1_rd_overflow (c3_p1_rd_overflow),
.c3_p1_rd_error (c3_p1_rd_error)
);
endmodule