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.使用
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); // 用户写指令发送完成,产生标志信号