1.背景與介紹
1)在導師安排的新的任務中,用到了一塊2G大小的DDR3(MT41K128M16JT-107)。本打算像之前用SDRAM一樣自己寫初始化,讀寫模塊,但是師兄跟我說可以用Xilinx自帶的MCB來進行控制,會方便很多,於是自己在網上找了一些資料,摸索了一番,然后在實際運用后,寫下了這篇隨筆。
2)我們先看MCB內部結構圖,重點關注兩個部分User Logic 與PHY。
PHY是外部IO接口,也就是和DDR3直接連接的接口;
User Logic 對應的部分則是需要我用戶編寫的部分,從圖中可以看出:用戶只需控制CMD與數據即可,而這里的CMD也不復雜,Xilinx將其做的十分簡單,用戶只需發送對應的讀、寫、帶刷新寫、帶刷新讀即可,相對來說使用還是很簡單的。
2.生成IP核
1)如何產生IP核的過程就不說了,記住叫Memory Interface Generator就行,直接進入設置部分。
2)選擇兼容平台,根據用戶自己需求選擇,我這里無需其他拓展,因此都沒選擇。
3)選擇bank區域,根據用戶數據手冊上的介紹,優先選擇左側/左下側。(用戶數據在點擊圖中左下區域后可以得到)
4)選擇DDR3的芯片型號,如果沒有,可以自己定義一塊,不過需要注意的是要選擇接口相似的芯片作為模板。
5)定義自己的芯片,我的芯片是MT41K128M16JT-107 ,其中128M16代表128Meg*16,-107表示速度在芯片數據手冊種得知可以向后兼容(-125、-15E、-187E)因此選擇MT41J128M16XX-125,這樣需要改動的東西就非常少了。
先附上我使用的芯片的部分參數
再附上我定義的芯片,可見幾乎沒有改動。
6)此處無需改動,保留默認值即可。
7)此處有幾種選擇,我選擇是128bit,既一次寫入/讀取128bit數據,具體如何設置,可以查看用戶手冊ug388.pdf。
8)此處設置終端補償電阻PIN腳(保持默認或者看自己板上怎么連接的),單時鍾輸入。
9)最后一路NEXT到底,點擊Generate,一個MCB的IP核就生成了。
10)IP核生成后,我們會在其目錄下看見三個文件夾,其中內容如圖所示。
11)我們打開user_design文件夾,其中par與rtl最為重要,par中包含UCF文件,這會讓我們定義端口容易很多,而rtl則包含設計的.v文件
3.使用
0)首先自然是要生成相應的時鍾 333MHz!!!
1)將生成的MCB移出(右鍵--remove),再將uesr_design中rtl下文件手動添加至工程中(右鍵--add source)。(為的是使用PLL生成的時鍾)
2)修改memc3_infrastructure_inst 中的時鍾
先注釋掉這一部分(這是MCB生成時鍾的部分)
將這里的時鍾換成sys_clk(原來是sys_clk_ibufg)
3)接下來我們來了解一下MCB DDR3的接口 ,接口的說明也在下面這段代碼中;

1 MCB_TEST # ( 2 .C3_P0_MASK_SIZE(16), 3 .C3_P0_DATA_PORT_SIZE(128), 4 .DEBUG_EN(0), 5 .C3_MEMCLK_PERIOD(3000), //change 6 .C3_CALIB_SOFT_IP("TRUE"), 7 .C3_SIMULATION("FALSE"), 8 .C3_RST_ACT_LOW(0), 9 .C3_INPUT_CLK_TYPE("SINGLE_ENDED"), 10 .C3_MEM_ADDR_ORDER("ROW_BANK_COLUMN"), 11 .C3_NUM_DQ_PINS(16), 12 .C3_MEM_ADDR_WIDTH(15), 13 .C3_MEM_BANKADDR_WIDTH(3) 14 ) 15 u_MCB_MIG ( 16 .c3_sys_clk (clk_ddr3_sys), //DDR3時鍾333MHz對應3000ps 17 .c3_sys_rst_i (rst_ddr3_sys), //復位信號 高有效 18 //物理接口,不用管 19 .mcb3_dram_dq (mcb3_dram_dq), //讀寫數據 20 .mcb3_dram_a (mcb3_dram_a), //讀寫地址 21 .mcb3_dram_ba (mcb3_dram_ba), //bank選擇 22 .mcb3_dram_ras_n (mcb3_dram_ras_n), //行使能 低有效 23 .mcb3_dram_cas_n (mcb3_dram_cas_n), //列使能 低有效 24 .mcb3_dram_we_n (mcb3_dram_we_n), //使能 低有效 25 .mcb3_dram_odt (mcb3_dram_odt), //PHYIO 26 .mcb3_dram_cke (mcb3_dram_cke), //PHYIO 27 .mcb3_dram_ck (mcb3_dram_ck), //PHYIO 28 .mcb3_dram_ck_n (mcb3_dram_ck_n), //PHYIO 29 .mcb3_dram_dqs (mcb3_dram_dqs), //PHYIO 30 .mcb3_dram_dqs_n (mcb3_dram_dqs_n), //PHYIO 31 .mcb3_dram_udqs (mcb3_dram_udqs), // for X16 parts 32 .mcb3_dram_udqs_n (mcb3_dram_udqs_n), // for X16 parts 33 .mcb3_dram_udm (mcb3_dram_udm), // for X16 parts 34 .mcb3_dram_dm (mcb3_dram_dm), //PHYIO 35 .mcb3_dram_reset_n (mcb3_dram_reset_n), //PHYIO 36 .c3_clk0 (c3_clk0), //PHYIO 37 .c3_rst0 (c3_rst0), //PHYIO 38 .c3_calib_done (c3_calib_done), //goes High to indicate that calibration has completed 39 .mcb3_rzq (mcb3_rzq), 40 .mcb3_zio (mcb3_zio), 41 42 //command path signals 43 .c3_p0_cmd_clk (clk_ddr3), //用戶讀寫MCB內部FIFO時鍾 44 .c3_p0_cmd_en (mcb3_cmd_en), //high signal is the write-enable signal for command fifo 45 .c3_p0_cmd_instr (mcb3_cmd_instr), //CMD 46 .c3_p0_cmd_bl (mcb3_cmd_bl), //突發長度,大小為0-63,代表1-64的突發長度(即連續讀/寫多少次) (for example, 6'b00011 is a burst length 4 transaction). The user word width equals the port width (for example, a burst length of 3 on a 64-bit port transfers 3 x 64-bit user words = 192 bits total). 47 .c3_p0_cmd_byte_addr (mcb3_cmd_byte_addr),//MCB的讀寫地址,寫的時候等於寫地址,讀的時候等於讀地址 48 .c3_p0_cmd_empty (mcb3_cmd_empty), //MCB空 49 .c3_p0_cmd_full (mcb3_cmd_full), //MCB滿 50 //write datapath 51 .c3_p0_wr_clk (clk_ddr3), //等於上面的時鍾; 52 .c3_p0_wr_en (mcb3_wr_en), //Data is loaded on the rising edge of pX_wr_clk when pX_wr_en = 1 and pX_wr_full = 0. 53 .c3_p0_wr_mask (mcb3_wr_mask), //when a mask bit is high, the corresponding bte of data is masked. 54 .c3_p0_wr_data (mcb3_wr_data), //write data, size can be 32,64,128 bits, depending on port configuration. 55 .c3_p0_wr_full (mcb3_wr_full), 56 .c3_p0_wr_empty (mcb3_wr_empty), 57 .c3_p0_wr_count (mcb3_wr_count), //output indicates how many user words are in the FIFO(0-64)0 means fifo empty 58 .c3_p0_wr_underrun (mcb3_wr_underrun), //indicates there was not enough data in write data fifo to complete the transacion. 59 .c3_p0_wr_error (mcb3_wr_error), 60 //read datapath 61 .c3_p0_rd_clk (clk_ddr3), 62 .c3_p0_rd_en (mcb3_rd_en), 63 .c3_p0_rd_data (mcb3_rd_data), 64 .c3_p0_rd_full (mcb3_rd_full), 65 .c3_p0_rd_empty (mcb3_rd_empty), 66 .c3_p0_rd_count (mcb3_rd_count), 67 .c3_p0_rd_overflow (mcb3_rd_overflow), 68 .c3_p0_rd_error (mcb3_rd_error) 69 );
從上面的注釋可以看出,和用戶編寫讀寫程序緊密相關的是下面這些接口:
1*命令相關
//command path signals
.c3_p0_cmd_clk (clk_ddr3),
.c3_p0_cmd_en (mcb3_cmd_en),
.c3_p0_cmd_instr (mcb3_cmd_instr),
.c3_p0_cmd_bl (mcb3_cmd_bl),
.c3_p0_cmd_byte_addr (mcb3_cmd_byte_addr),
.c3_p0_cmd_empty (mcb3_cmd_empty),
.c3_p0_cmd_full (mcb3_cmd_full),
以en、instr、byte_addr為主,當進行讀寫操作時,這三個要同時變化;
2*讀寫相關
//write datapath
.c3_p0_wr_clk (clk_ddr3),
.c3_p0_wr_en (mcb3_wr_en),
.c3_p0_wr_mask (mcb3_wr_mask),
.c3_p0_wr_data (mcb3_wr_data),
.c3_p0_wr_full (mcb3_wr_full),
.c3_p0_wr_empty (mcb3_wr_empty),
.c3_p0_wr_count (mcb3_wr_count),
.c3_p0_wr_underrun (mcb3_wr_underrun),
.c3_p0_wr_error (mcb3_wr_error),
//read datapath
.c3_p0_rd_clk (clk_ddr3),
.c3_p0_rd_en (mcb3_rd_en),
.c3_p0_rd_data (mcb3_rd_data),
.c3_p0_rd_full (mcb3_rd_full),
.c3_p0_rd_empty (mcb3_rd_empty),
.c3_p0_rd_count (mcb3_rd_count),
.c3_p0_rd_overflow (mcb3_rd_overflow),
.c3_p0_rd_error (mcb3_rd_error)
以en、data、full為主;
4)簡單舉例:
1*先說指令
localparam MCB_CMD_WR = 3'b000; //mcb write cmd
localparam MCB_CMD_RD = 3'b001; //mcb read cmd
localparam MCB_CMD_WP = 3'b010; //mcb write with auto precharge cmd
localparam MCB_CMD_RP = 3'b011; //mcb read with auto precharge cmd
localparam MCB_CMD_RF = 3'b100; //mcb refresh cmd
2*簡單舉例(以寫為例,代碼不全,領會思路)

1 //先將數據存入MCB的FIFO中; 2 if(cnt_fifo_rd>=7'd64) begin //write 64 at one time 1KB 3 cnt_fifo_rd <= cnt_fifo_rd; 4 ad_fifo_rd_en_r <= 1'b0; //去讀本地fifo中的數據 5 end 6 else begin 7 cnt_fifo_rd <= cnt_fifo_rd + 1'b1; 8 ad_fifo_rd_en_r <= 1'b1; 9 end 10 assign mcb3_wr_en = ad_fifo_rd_en_r; //實際上要延遲一個時鍾,這里做演示就不延遲了。 11 assign mcb3_wr_data = ad_fifo_rd_data; //同時傳數據 12 //產生寫命令的請求 13 if(!rst_n) 14 u_wr_cmd_en <= 1'b0; 15 else if(u_wr_cmd_done) //檢測到指令發送完成,清除標志信號? 16 u_wr_cmd_en <= 1'b0; //clear 17 else if(cnt_fifo_rd==7'd63) //寫入63次后產生寫命令請求(提前一個產生,等存夠64次后剛好產生寫請求 18 u_wr_cmd_en <= 1'b1; //enable 19 else 20 u_wr_cmd_en <= u_wr_cmd_en; 21 end 22 //檢測到寫命令請求,發出寫指令 23 if(u_wr_cmd_en) begin //write 24 mcb3_cmd_instr_r <= MCB_CMD_WP; 25 mcb3_cmd_byte_addr_r <= u_wr_addr; 26 mcb3_cmd_bl_r <= mcb3_wr_bl; 27 mcb3_cmd_wr_p <= 1'b1; 28 mcb3_cmd_rd_p <= 1'b0; 29 end 30 assign u_wr_cmd_done0 = mcb3_cmd_en&(mcb3_cmd_instr== MCB_CMD_WP); // 用戶寫指令發送完成,產生標志信號