F5—创建DDR3内存条DIMM读写测试程序2023-05-16

本文区别于DDR颗粒的配置,记录几个与颗粒配置不同的地方,具体DDR的原理请查看DDR3的应用总结(一)DDR3的应用总结(二)

1.确认板卡FPGA型号为xc7k325tffg900 -2,据此创建FPGA工程。

2.添加MIG IP核。具体配置如下

①第一页描述当前工程信息

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第1张图片

 ②第二页,可设置IP核的名称,保持默认。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第2张图片

 

③第三页 设置兼容情况,保持默认。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第3张图片

④第四页 保持默认选择DDR3 SDRAM

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第4张图片 

 ⑤第五页如图所示配置。其中①指DDR3颗粒的物理时钟,例如一颗16bit位宽的DDR设置为400MHz,则它的传输速度为每一个800MHz(DDR双沿传输的原因)周期传输16bit。这里时钟的范围受到FPGA芯片速度等级和型号的制约,以及与内存条的支持速度也有关。如我用的内存条MT16KTF1G64HZ-1G6,速度范围在1500ps-3000ps之间。②指的是用户时钟,4:1的4指的是①设置的物理时钟。第③部分指的是类型选择,内存条选择SODIMMs。④处是DDR3内存条的型号,如果不在列表需要根据速度参数,位宽大小找一个兼容的型号。⑤处勾选上mask后,如果相应的管脚不连接,会造成DDR3初始化失败。其余配置保持默认即可。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第5张图片

⑥第六页 此处①代表参考时钟,选择200MHz为固定大小。②处如果是内存条就选择RZQ/4,如果是颗粒就选择RZQ/6。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第6张图片 

⑦第七页,参考时钟和系统时钟均由时钟IP生成给到,因此选择no buffer,其他的按照默认配置即可。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第7张图片 

⑧DDR3颗粒要勾选DCI Cascade,内存条不用勾选。后面就是选择引脚,其他的都保持默认,即可。

F5—创建DDR3内存条DIMM读写测试程序2023-05-16_第8张图片

 

3.测试程序

如果需要修改这段程序,需要注意MIG的接口各信号的位宽应该保持一致,另外程序中设计了两个LED灯,读写测试正确的时候,指示灯led1常亮,反之则闪烁。LED2只是容量,当测试到所设置的容量的时候常量。代码中TEST_LENGTH指示的含义是突发次数,也可以说是容量。每一次突发是512bit数据,使用内存的容量除以512bit,即为最大的突发次数。当然使用的芯片的物理位宽不同,例如只有一片16bit位宽的DDR颗粒,那一次突发的数据为8*16bit=128bit,那最大的突发次数就要用容量除以128bit了。

module dimm_top(
    input              sys_clk_p,
    input              sys_clk_n,       
	inout  [63:0]      ddr3_dq			, 
	inout  [7:0]       ddr3_dqs_n		, 
	inout  [7:0]       ddr3_dqs_p		,   
	output [15:0]      ddr3_addr		, 
	output [2:0]       ddr3_ba			, 
	output             ddr3_ras_n		, 
	output             ddr3_cas_n		, 
	output             ddr3_we_n		, 
	output             ddr3_reset_n	, 
	output [1:0]       ddr3_ck_p		, 
	output [1:0]       ddr3_ck_n		, 
	output [1:0]       ddr3_cke		    , 
	output [1:0]       ddr3_cs_n		, 
	output [7:0]       ddr3_dm			, 
	output [1:0]       ddr3_odt         , 
    output reg         led1,              
    output reg         led2        
    );         
    wire                clk_rst;                            
    wire                clk_200;
    reg     [29:0]      app_addr_begin=0;
    wire                app_en;              //写命令使能
    wire    [2:0]       app_cmd;             //用户读写命令
    wire                app_wdf_wren;        //DDR3写使能
    wire                app_wdf_end;         //突发写最后一个数标识
    wire    [29:0]      app_addr;            //用户平面地址
    wire                app_rdy;             //设备接收准备就绪   
    wire                app_wdf_rdy;         //写响应
    wire    [511:0]     app_rd_data;         //用户读数据
    wire                app_rd_data_end;     //突发读当前时钟最后一个数据
    wire                app_rd_data_valid;   //读数据有效
    wire    [511:0]     app_wdf_data;        //用户写数据
    wire                app_sr_active;       //保留
    wire                app_ref_ack;         //刷新请求
    wire                app_zq_ack;          //ZQ 校准请求
    wire        		init_calib_complete; //校准完成信号
    wire                ui_clk ;             //用户时钟
    wire                ui_clk_sync_rst;              
    clk_wiz_0 u_clk_wiz_0(.clk_out1(clk_200), .reset(1'b0), .locked(clk_rst), .clk_in1_p(sys_clk_p),.clk_in1_n(sys_clk_n)); 

    mig_7series_0 mig_JC (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr), 		// output [15:0]	
    .ddr3_ba                        (ddr3_ba),  		// output [2:0]
    .ddr3_cas_n                     (ddr3_cas_n), 		// output			
    .ddr3_ck_n                      (ddr3_ck_n),  		// output [1:0]		
    .ddr3_ck_p                      (ddr3_ck_p),  		// output [1:0]		
    .ddr3_cke                       (ddr3_cke),  		// output [1:0]		
    .ddr3_ras_n                     (ddr3_ras_n),  		// output		
    .ddr3_reset_n                   (ddr3_reset_n),  	// output			
    .ddr3_we_n                      (ddr3_we_n),  		// output		
    .ddr3_dq                        (ddr3_dq),  		// inout [63:0]	
    .ddr3_dqs_n                     (ddr3_dqs_n),  		// inout [7:0]		
    .ddr3_dqs_p                     (ddr3_dqs_p),  		// inout [7:0]		
    .init_calib_complete            (init_calib_complete),  // output		
	.ddr3_cs_n                      (ddr3_cs_n),  		// output [1:0]		
    .ddr3_dm                        (ddr3_dm),  		// output [7:0]	
    .ddr3_odt                       (ddr3_odt),  		// output [1:0]		
    // Application interface ports
    .app_addr                       (app_addr), 		 // input [29:0]		
    .app_cmd                        (app_cmd),  		 // input [2:0]		
    .app_en                         (app_en),  			 // input	
    .app_wdf_data                   (app_wdf_data), 	 // input [511:0]	
    .app_wdf_end                    (app_wdf_end), 		 // input			
    .app_wdf_wren                   (app_wdf_wren),  	 // input				
    .app_rd_data                    (app_rd_data),  	 // output [511:0]		
    .app_rd_data_end                (app_rd_data_end),   // output		
    .app_rd_data_valid              (app_rd_data_valid), // output	
    .app_rdy                        (app_rdy),  		 // output		
    .app_wdf_rdy                    (app_wdf_rdy),  	 // output			
    .app_sr_req                     (1'b0),  			 // input			
    .app_ref_req                    (1'b0),  			 // input		
    .app_zq_req                     (1'b0),              // input			
    .app_sr_active                  (app_sr_active),     // output			
    .app_ref_ack                    (app_ref_ack), 		 // output		
    .app_zq_ack                     (app_zq_ack),  	   	 // output		
    .ui_clk                         (ui_clk),  			 // output用户时钟输出,其实是通过IP配置自己配出来的 	 
    .ui_clk_sync_rst                (ui_clk_sync_rst),   // output		
    .app_wdf_mask                   (64'b0),  			 // input [63:0] //写数据屏蔽
    .sys_clk_i                      (clk_200),//输入IP的时钟
    // Reference Clock Ports
    .clk_ref_i                      (clk_200),//参考时钟		
    .sys_rst                        (clk_rst) // input sys_rst
    );
parameter  TEST_LENGTH = 27'd134200000; //每一次突发是512bit 8GB可以支持134217728次突发 99.98%
// parameter  TEST_LENGTH = 32'd60000000;
//**************1.先写后读状态机state machine
parameter  IDLE  = 2'd0;           
parameter  WRITE = 2'd1;          
parameter  WAIT  = 2'd2;           
parameter  READ  = 2'd3;   
reg [511:0]my_512_data;
reg [26:0] wr_addr_cnt;
reg [26:0] rd_addr_cnt;
reg [1:0]  state;
 always @(posedge ui_clk or negedge rst_n) begin
     if((~rst_n)||(error_flag)) begin 
         state    <= IDLE;          
         my_512_data <= 512'd0;     
         wr_addr_cnt  <= 27'd0;      
         rd_addr_cnt  <= 27'd0;       
         app_addr_begin<= 30'd0;         
     end
     else if(init_calib_complete)begin               //MIG IP核初始化完成
         case(state)
            IDLE:begin
                state    <= WRITE;
                my_512_data <= 512'd0;   
                wr_addr_cnt  <= 27'd0;     
                rd_addr_cnt  <= 27'd0;       
                app_addr_begin     <= 30'd0; 
            end
            WRITE:begin
                if((wr_addr_cnt == TEST_LENGTH-1) &&(app_rdy && app_wdf_rdy))
                    state    <= WAIT;                  //写到设定的长度跳到等待状态
                else if(app_rdy && app_wdf_rdy)begin   //写条件满足
                    my_512_data <= my_512_data + 1;  //写数据自增
                    wr_addr_cnt  <= wr_addr_cnt + 1;   //写计数自增
                    app_addr_begin<= app_addr_begin + 8;      //DDR3 地址自增
                end else begin          //写条件不满足,保持当前状态
                     my_512_data <= my_512_data;      
                     wr_addr_cnt  <= wr_addr_cnt;
                     app_addr_begin<= app_addr_begin; 
                end
            end
            WAIT:begin                                                 
                state   <= READ;                     //下一个时钟,跳到读状态
                rd_addr_cnt <= 27'd0;                //读地址复位
                app_addr_begin<= 30'd0;                //DDR3读从地址0
            end
            READ:begin                               //读到设定的地址长度    
                if((rd_addr_cnt == TEST_LENGTH -1 ) && app_rdy)
                    state   <= IDLE;                   //则跳到空闲状态 
                else if(app_rdy)begin                  //若MIG已经准备就绪,则开始读
                    rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
                    app_addr_begin    <= app_addr_begin + 8;       //DDR3地址加8
                end else begin   //若MIG没准备好,则保持原
                    rd_addr_cnt <= rd_addr_cnt;
                    app_addr_begin    <= app_addr_begin; 
                end
            end
            default:begin
                state    <= IDLE;
                my_512_data  <= 512'd0;
                wr_addr_cnt  <= 27'd0;
                rd_addr_cnt  <= 27'd0;
                app_addr_begin <= 30'd0;
            end
         endcase
     end
 end   
//**************2.根据状态机与MIG指示信号为app信号赋值
 assign app_en  =((state == WRITE && (app_rdy && app_wdf_rdy))||(state == READ && app_rdy)) ? 1'b1:1'b0;             
 assign app_cmd =(state == READ) ? 3'd1 :3'd0;  
 assign app_wdf_wren=(state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
 assign app_wdf_end =app_wdf_wren; 
 assign app_addr    =app_addr_begin;
 assign app_wdf_data=my_512_data;   
//*******************3.用户判错逻辑
reg     [26:0]   rd_cnt;
wire             rst_n;     //复位,低有效
reg              error_flag;
parameter  L_TIME = 28'd200_000_000;
reg     [27:0]   led_cnt;    //led计数
wire             error;     //读写错误标记
assign rst_n = ~ui_clk_sync_rst;//&&myrst
always @(posedge ui_clk or negedge rst_n) begin
     if(~rst_n) 
        rd_cnt  <= 0;              //若计数到读写长度,且读有效,地址计数器则�?0                                    
     else if(app_rd_data_valid&&(rd_cnt == TEST_LENGTH - 1))
        rd_cnt <= 0;              //其他条件只要读有效,每个时钟自增1
     else if (app_rd_data_valid)
        rd_cnt <= rd_cnt + 1;
 end
//判断错误,读出数据应为计数递增数据
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));
always @(posedge ui_clk or negedge rst_n) begin
    if(~rst_n)
        led2<=0;
    else if(rd_cnt==32'd134200000-1)
        led2<=1;
end 
always @(posedge ui_clk or negedge rst_n) begin
     if(~rst_n) 
         error_flag <= 0;
     else if(error)
         error_flag <= 1;
  end
//读写测试正确,指示灯led1常亮,反之则闪烁
always @(posedge ui_clk or negedge rst_n) begin
      if((~rst_n) || (~init_calib_complete )) begin
         led_cnt <= 28'd0;
         led1 <= 1'b0;
     end
     else begin
         if(~error_flag)   //常亮代表正常,闪烁代表故障                            
             led1 <= 1'b1;                     
         else begin                            
             led_cnt <= led_cnt + 28'd1;
             if(led_cnt == L_TIME - 1'b1) begin
             led_cnt <= 25'd0;
             led1 <= ~led1;                     
             end                    
        end
    end
end
endmodule

你可能感兴趣的:(FPGA积累——基础篇,fpga开发)