Spartan6 LX45上的(串口)UART+DDR3读写操作

    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,如图:

Spartan6 LX45上的(串口)UART+DDR3读写操作_第1张图片

设置 DDR3  的时钟频率为 312.5Mhz,  这个频率最高可设置为 333.33Mhz,我们这里稍微留一点余量。 DDR3 的型号设置为跟开发板上一致的型号如AX545 的开发板,需选择的 DDR3 型号为 MT41J128M16XX-187E。

根据需要可以选择不同的用户端接口方式,可以选择多个 Port,  也可以组合成一个Port,  这里选择 One 128-bit bidirectional Port。

Spartan6 LX45上的(串口)UART+DDR3读写操作_第2张图片

选择 RZQ 和 ZIO 的引脚分配,AX545 的开发板上 C2 脚作为 RZQ, L6 脚作为 ZIO。另外选择 Debug Signals for Memory Controller 项设置为 Enable,因为可用 chipscope 来测试 DDR3。

Spartan6 LX45上的(串口)UART+DDR3读写操作_第3张图片

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的数据,结果如下:

Spartan6 LX45上的(串口)UART+DDR3读写操作_第4张图片
从图中可以看出绿色部分(读出的128位数据)与读入的地址位为32处的数据一致,即DDR3能正常连续读写,功能验证完毕。 
补充说明:
************************CMD 指令部分*********************
.c3_p0_arb_en(1'b1),                    //注意仲裁使能引入为高电平
.c3_p0_cmd_clk(c3_clk0),               //c3_p0_cmd_clk
.c3_p0_cmd_en(c3_p0_cmd_en),          //CMD时能en、高有效
.c3_p0_cmd_instr(c3_p0_cmd_instr),      //为3bit命令接口如3’b000写命令当然这里也支持数据刷新,我们为自动刷新,控制字参考UG388的用户接口说明
.c3_p0_cmd_bl(c3_p0_cmd_bl),           //64深度的fifo向ddr2搬运的数据数量里最多为64个和fifo深度想通
.c3_p0_cmd_byte_addr(c3_p0_cmd_byte_addr),    //32map地址如软件测试
.c3_p0_cmd_empty(c3_p0_cmd_empty),          //高有效空flag
.c3_p0_cmd_full(c3_p0_cmd_full)               //高有效满flag

***********************WRITE FIFO部分***********************
.c3_p0_wr_clk(c3_clk0),                  //c3_p0_wr_clk
.c3_p0_wr_en(c3_p0_wr_en),              //写数据的使能
.c3_p0_wr_mask(c3_p0_wr_mask),         //屏蔽数据的宽度
.c3_p0_wr_data(c3_p0_wr_data),           //写数据接口
.c3_p0_wr_full(c3_p0_wr_full),            //写满数据flag
.c3_p0_wr_empty(c3_p0_wr_empty),        //写空数据flag
.c3_p0_wr_count(c3_p0_wr_count),         //写计数
.c3_p0_wr_underrun(c3_p0_wr_underrun),   //fifo不够写标志
.c3_p0_wr_error(c3_p0_wr_error)

***********************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

你可能感兴趣的:(Spartan6 LX45上的(串口)UART+DDR3读写操作)