XILINX 公司率先在FPGA芯片中集成了MCB硬核,它可以支持到DDR3,对于用户控制接口以通用FIFO的读写方式,代替复杂的ddr2读写逻辑。以sram的地址映射方式代替复杂的行列地址选择。比如Spartan6 FPGA芯片中集成了MCB硬核,它可以支持到DDR3。在Xilinx的开发工具Xilinx ISE中提供了MIG IP核,设计者可以用它来直接生成 DDR3 控制器设计模块,并通过 MIG 的 GUI 图形界面完成相关配置。
1.设计结构
本文主要实现UART+DDR3的读写操作,即首先从串口输入要写入DDR3的数据,由于UART的数据传输波特率这里采用9600,它的时钟频率与DDR3的工作频率并不一致,因此这里再用一个异步FIFO来协调工作,保证系统稳定性。接下来主要讲讲DDR3的读写操作,UART+FIFO部分之前有写过,这里就不展开了。
2.MIG IP核生成
IP核的生成具体的教程很多,这里放上本文用的FPGA型号对应的DDR3,如图:
设置 DDR3 的时钟频率为 312.5Mhz, 这个频率最高可设置为 333.33Mhz,我们这里稍微留一点余量。 DDR3 的型号设置为跟开发板上一致的型号如AX545 的开发板,需选择的 DDR3 型号为 MT41J128M16XX-187E。
根据需要可以选择不同的用户端接口方式,可以选择多个 Port, 也可以组合成一个Port, 这里选择 One 128-bit bidirectional Port。
选择 RZQ 和 ZIO 的引脚分配,AX545 的开发板上 C2 脚作为 RZQ, L6 脚作为 ZIO。另外选择 Debug Signals for Memory Controller 项设置为 Enable,因为可用 chipscope 来测试 DDR3。
3.参数修改
这里主要是UFC引脚约束以及时钟频率参数的修改。在UFC中修改的地方如下:
(1)VCCAUX 的电压修改为 3.3V 。
CONFIG VCCAUX=3.3; # Valid values are 2.5 and 3.3
(2)系统时钟输入的周期,需要跟PCB板上的晶振频率一样。
NET "uut_fifo_test/u_ddr3_test/memc3_infrastructure_inst/sys_clk_ibufg" TNM_NET = "SYS_CLK3";
TIMESPEC "TS_SYS_CLK3" = PERIOD "SYS_CLK3" 20 ns HIGH 50 %;
(3)设置路径,即补上TIG的路径:“uut_fifo_test/u_ddr3_test/”。
NET "uut_fifo_test/u_ddr3_test/memc?_wrapper_inst/mcb_ui_top_inst/mcb_raw_wrapper_inst/selfrefresh_mcb_mode" TIG;
NET "uut_fifo_test/u_ddr3_test/c?_pll_lock" TIG;
INST "uut_fifo_test/u_ddr3_test/memc?_wrapper_inst/mcb_ui_top_inst/mcb_raw_wrapper_inst/gen_term_calib.mcb_soft_calibration_top_inst/
mcb_soft_calibration_inst/DONE_SOFTANDHARD_CAL*" TIG;
(注意:编译过程中可能会出现如下问题:
ERROR lace:1333 - Following IOB's that have input/output programming are locked
to the bank 1 that does not support such values
IO Standard: Name = LVDS_25, VREF = NR, VCCO = 2.50, TERM = NONE, DIR =
BIDIR, DRIVE_STR = NR
解决方法:先REMOVE UCF文件,之后在ADD UCF文件,编译通过)
之后是时钟频率的参数修改,本文设置如下(时钟输入为50Mhz,由于在MIG中设置了 DDR3 的频率为312.5MHz,且DDR3是上下沿采样,所以FPGA内部 DDR3控制器的时钟需要625MHz,所以这里先把时钟频率倍频25, 再分频2,得625Mh的CLKOUT0和CLKOUT1, 再分频 8分别得到 user interface 的时钟和 calibration 的时钟 78.125Mhz。):
localparam C3_CLKOUT0_DIVIDE = 1; //ddr_clock 50*25/2 = 625MHz
localparam C3_CLKOUT1_DIVIDE = 1;
localparam C3_CLKOUT2_DIVIDE = 8; //user interface clock 625/8 = 78.125MHz,对应程序中c3_clk0
localparam C3_CLKOUT3_DIVIDE = 8; //calibration clock 625/8 = 78.125MHz
localparam C3_CLKFBOUT_MULT = 25;
localparam C3_DIVCLK_DIVIDE = 2;
4.DDR3读写接口设计
DDR3的读写操作一般用FSM状态机来实现,主要配合FIFO的读写操作进行设计。程序如下:
首先是写操作:
case(ddr_write_state)
write_idle:begin
c3_p0_wr_en<=1'b0;
c3_p0_wr_mask<=16'd0;
if(ddr_wr_req) begin //如果写DDR请求信号为高
ddr_write_busy<=1'b1; //ddr写数据忙标志
ddr_write_state<=write_fifo;
c3_p0_wr_data<=ddr_wdata_reg; //准备写入DDR的数据
end
end
write_fifo:begin
if(!c3_p0_wr_full) begin //如p0写fifo数据不满
c3_p0_wr_en<=1'b1;
ddr_write_state<=write_data_done;
end
end
write_data_done:begin
c3_p0_wr_en<=1'b0;
ddr_write_state<=write_cmd_start;
end
write_cmd_start:begin
c3_p0_cmd_en_w<=1'b0;
c3_p0_cmd_instr_w<=3'b010; //010为写命令
c3_p0_cmd_bl_w<=6'd0; //burst length为1个128bit数据
c3_p0_cmd_byte_addr_w<=c3_p0_cmd_byte_addr_w+16; //地址加16
ddr_write_state<=write_cmd;
end
write_cmd:begin
if (!c3_p0_cmd_full) begin //如果命令FIFO不满
c3_p0_cmd_en_w<=1'b1; //写命令使能
ddr_write_state<=write_done;
end
end
write_done:begin
c3_p0_cmd_en_w<=1'b0;
ddr_write_state<=write_idle;
ddr_write_busy<=1'b0;
end
default:begin
c3_p0_wr_en<=1'b0;
c3_p0_cmd_en_w<=1'b0;
c3_p0_cmd_instr_w<=3'd0;
c3_p0_cmd_bl_w<=6'd0;
ddr_write_state<=write_idle;
end
endcase;
再是读操作:
case(ddr_read_state)
read_idle:begin
if(ddr_rd_cmd_req==1'b1) begin //如果有ddr读命令请求
ddr_read_state<=read_cmd_start;
ddr_rd_busy <=1'b1;
end
end
read_cmd_start:begin
c3_p0_cmd_en_r<=1'b0;
c3_p0_cmd_instr_r<=3'b001; //命令字为读
c3_p0_cmd_byte_addr_r<= 32; //ddr的验证读地址32
c3_p0_cmd_bl_r<=6'd0; //burst length为1个128bit数据
ddr_read_state<=read_cmd;
end
read_cmd:begin
c3_p0_cmd_en_r<=1'b1; //ddr读命令使能
ddr_read_state<=read_out;
end
read_out:begin
if (!c3_p0_rd_full) //ddr命令读未满
begin
c3_p0_cmd_en <= 0;
c3_p0_cmd_instr <= 0;
c3_p0_cmd_bl <= 0;
//c3_p0_cmd_byte_addr <= 0;
c3_p0_rd_en <= 1;
fifo_data <= c3_p0_rd_data; //读出的数据传到UART输出即可
if (c3_p0_rd_empty)
ddr_read_state<=read_done;
end
end
read_done:begin
c3_p0_cmd_en_r<=1'b0;
ddr_rd_busy <=1'b0;
first_read<=1'b0;
ddr_read_state<=read_idle;
end
default:begin
c3_p0_cmd_en_r<=1'b0;
ddr_read_state<=read_idle;
end
endcase;
5.DDR3串口读写测试
DDR3的测试结果如图所示,比如读入5个128位的数据,DDR3写入一个数据则地址每次要加16(1*128/8),测试时检验地址位为32的数据,结果如下:
***********************READ FIFO***********************
.c3_p0_rd_clk(c3_clk0), //c3_p0_rd_clk
.c3_p0_rd_en(c3_p0_rd_en), //读信号使能
.c3_p0_rd_data(c3_p0_rd_data), //写数据端口
.c3_p0_rd_full(c3_p0_rd_full), //读满数据flag
.c3_p0_rd_empty(c3_p0_rd_empty), //读空数据flag
.c3_p0_rd_count(c3_p0_rd_count), //读数据技术指针
.c3_p0_rd_overflow(c3_p0_rd_overflow), //fifo不够读标志
.c3_p0_rd_error(c3_p0_rd_error), //读error信号
参考博客:
1.http://www.eefocus.com/mayMAN/blog/10-03/186414_eb41d.html
2.http://www.cnblogs.com/yangjun1219/p/5647947.html