Quickly get started with Xilinx DDR3 IP core (2) ---- Official routines of MIG IP core and read and write test module (Native interface)

write in front

        Continuing from the previous article (Procedure to configure MIG IP):

                Quick start with Xilinx DDR3 IP core (1)----Introduction and configuration of MIG IP core (Native interface)

        DDR3 series of articles:

                Quick Start with Xilinx DDR3 IP Cores----Summary (Direct Link)


1. Official routine (example design)

        In my mind, Xilinx is a perfect company (it is too slow to automatically ignore vivado compilation), and the technical ecological support is really good. Xilinx also knows that we will not use DDR3, so we provide an example design for you to learn, how about it? Surprised or not? Are you surprised? (In fact, the vast majority of IP Xilinx provides example design~~)

        Then let's study the reference design provided by the official together.

1.1. Example generation steps

        Right-click the generated IP core (by default you have generated the MIG IP core), select open IP example design, and after selecting the path, a new project mig_7series_0_ex will be generated.

        Open the project mig_7series_0_ex and look at the structure of the entire project----2 main parts: 1. MIG IP core; 2. Data generation module for reading and writing tests

        We don't talk about the reading and writing test module (there are too many, it will not be finished in a while), we learn directly through the waveform.

        Click RUN SIMULATION to execute the simulation directly (you may wait a few minutes here (computer configuration) - it is strongly recommended to use Modelsim simulation, the speed is not a star and a half

1.2. Simulation results

        After the simulation results come out, only some useful signals are kept and grouped, as follows:

         

Find some partial timing diagrams after init_calib_complete is pulled high (DDR3 initialization is completed), as shown below:

        At the blue line and the yellow line, app_cmd is 0, app_en and app_rdy are high, indicating that the write command operation is being executed; at the same time, app_wdf_data is the same as the address, app_wdf_en and app_wdf_rdy are both high, indicating that the write data operation is being performed

As shown below:

        The app_rdy is pulled low for many cycles, which means that the MIG is not ready to write data command, but at this time app_wdf_data is the same as the address, app_wdf_en and app_wdf_rdy are both high, which means that the data writing operation is in progress. This is exactly what the previous chapter mentioned in advance of writing data and writing commands, because the MIG species has a FIFO, which stores the data to be written in the FIFO, and then reads the FIFO when the write command is valid. The advantage of this is that writing commands and writing data can be largely separated.

As shown below:

         At the blue line, app_cmd is 1, app_en and app_rdy are high, and the address app_addr is 0x0000200, which means that the read command is being executed. After several cycles, the read data valid flag signal app_rd_data_valid at the yellow line is pulled high, which means that a data has been successfully read. The data is 0x0000200000020000002000000200, which is consistent with the previous write.

2. Read and write test module

        Through the study of the official routines in the previous section, I have a preliminary understanding of the interface timing of DDR3. Next, I will write a simple read and write test module to operate DDR3. (Not a cloud player)

2.1, Verilog code

        The functions expected to be implemented by the read-write test module:

  • Write a certain amount of data (can be set, the default is 512) to DDR3, the write address starts from 0
  • Read the data written to DDR3 from address 0, and judge whether the read and write data are consistent
  • Loop the last two steps, i.e. write, read, write, read...

         Let’s first review the timing of the Native interface provided by DDR3.

        The read or write of DDR3 includes write command operations, where the value of the write operation command (app_cmd) is equal to 0, and the value of the read operation app_cmd is equal to 1. First look at the write command timing, as shown in the following figure. First check app_rdy, if it is high, it means that the IP core command is ready to receive at this time, it can receive user commands, pull up app_en at the current clock, and send the command (app_cmd) and address (app_addr) at the same time, at this time the command and address are written. enter.

        The timing of writing data is shown in the following figure: 

        There are 3 cases of writing data:

  1. Write command and write data occur on the same clock cycle
  2. The write data occurs before the write command (not necessarily a clock cycle in the figure, because the data is written to the FIFO first) and occurs in the same clock cycle
  3. Write data occurs after the write command, but not more than two clock cycles

        Combined with the above figure, the writing timing is summarized as follows: First, you need to check app_wdf_rdy. This signal is high, indicating that the IP core data reception is in a ready state at this time, and the data sent by the user can be received, and the write enable (app_wdf_wren) is pulled high at the current clock. Write data (app_wdf_data) is given. In this way, the initiated write command operation can successfully write data to the IP core.

        Then look at the read data, as shown in the following figure:

        The read timing is relatively simple. After issuing the read command, the user only needs to wait for the data valid signal (app_rd_data_valid) to be pulled high, which indicates that the data on the data bus is valid return data at this time. It should be noted that after a read command is issued, valid read data does not appear on the data bus several cycles later (the delay time varies).

2.1.1, PLL module

        Originally, the simulation does not need the PLL module (just write the clock excitation directly), but considering that the board test is required later, and the main frequency of the development board I have is 50M, the working clock of the MIG is 200M, so it needs to be generated by the PLL module. working clock. The specific configuration is omitted.

2.1.2, DDR3 simulation model

        The simulation model of DDR3 is provided in the generated example design, which consists of two files:

1. The header file of the DDR3 simulation model

2. DDR3 simulation model

Find these two files and add them to our own test project.

2.1.3. Read and write test module

        The read and write test module generates the control sequence for the MIG IP core, and uses a state machine to realize the process of cyclic write and read. The state machine is as follows:

  • IDLE: initial state, jump to the write data state WRITE after the initialization of the MIG IP core is completed
  • WRITE: write data state, write a certain amount of data to the MIG IP core in this state (512 in the test). When the last data is written, synchronously jump to the wait state WAIT
  • WAIT: Transition state, only maintained for one cycle
  • READ: read data state, in this state read a certain amount of data from the MIG IP core (512 in the test). When the last data is read, a synchronous jump to the initial state IDLE is made. Start a new round of writing and reading

        As mentioned earlier, DDR3 has 3 modes when writing data. When we test, we always use the mode of " write command and write data occur in the same clock cycle " . In this way, the code will be much simpler to write, but the corresponding efficiency will be sacrificed a little (doesn't cause much impact).

        The complete read-write module test code is as follows:

//**************************************************************************
// *** 名称 : ddr3_rw
// *** 作者 : 孤独的单刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 对DDR3进行循环读写
//**************************************************************************

//============================< 端口 >======================================
module ddr3_rw #
( 
	parameter	integer					WR_LEN		= 1024		,	//读、写长度
	parameter	integer					DATA_WIDTH	= 128		,	//数据位宽,突发长度为8,16bit,共128bit
	parameter	integer					ADDR_WIDTH	= 28			//根据MIG例化而来
)(   
//DDR3相关 ------------------------------------------------------      
    input                   			ui_clk					,	//用户时钟
    input                   			ui_clk_sync_rst			,	//复位,高有效
    input                   			init_calib_complete		,	//DDR3初始化完成
//DDR3相关 ------------------------------------------------------	
    input                   			app_rdy					,	//MIG 命令接收准备好标致
    input                   			app_wdf_rdy				,	//MIG数据接收准备好
    input                   			app_rd_data_valid		,	//读数据有效
    input		[DATA_WIDTH - 1:0]   	app_rd_data				,	//用户读数据
    output	reg	[ADDR_WIDTH - 1:0]		app_addr				,	//DDR3地址                      
    output	                			app_en					,	//MIG IP发送命令使能
    output	                			app_wdf_wren			,	//用户写数据使能
    output	                			app_wdf_end				,	//突发写当前时钟最后一个数据 
    output		[2:0]     				app_cmd					,	//MIG IP核操作命令,读或者写
    output	reg	[DATA_WIDTH - 1:0]		app_wdf_data			,	//用户写数据
//指示 ----------------------------------------------------------		
    output	reg             			error_flag     	    	   	//读写错误标志
    );
	
//============================< 信号定义 >======================================
//测试状态机-----------------------------------------				
localparam					IDLE	= 4'b0001		;            	//空闲状态
localparam					WRITE 	= 4'b0010		;            	//写状态
localparam					WAIT  	= 4'b0100		;            	//读到写过度等待
localparam					READ  	= 4'b1000		;            	//读状态
//reg define ----------------------------------------
reg	[3:0]					cur_state				;				//三段式状态机现态
reg	[3:0]					next_state				;				//三段式状态机次态
reg	[ADDR_WIDTH - 1:0]		rd_addr_cnt				;				//用户读地址计数
reg	[ADDR_WIDTH - 1:0]		wr_addr_cnt				;				//用户写地址计数
reg	[ADDR_WIDTH - 1:0]		rd_cnt					;				//实际读地址标记
//wire define ---------------------------------------										
wire						error					;     			//读写错误标记
wire						rst_n					;     			//复位,低有效
wire						wr_proc					;				//拉高表示写过程进行
wire						wr_last					;				//拉高表示写入最后一个数据
wire						rd_addr_last			;				//拉高表示是最后一个读地址

 //*********************************************************************************************
//**                    main code
//**********************************************************************************************
//==========================================================================
//==    信号赋值
//==========================================================================  
assign rst_n = ~ui_clk_sync_rst;
//当MIG准备好后,用户同步准备好
assign app_en = app_rdy && ((cur_state == WRITE && app_wdf_rdy) || cur_state == READ);              
//写指令,命令接收和数据接收都准备好,此时拉高写使能
assign app_wdf_wren = (cur_state == WRITE) && wr_proc;
//由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
assign app_wdf_end = app_wdf_wren; 
assign app_cmd = (cur_state == READ) ? 3'd1 :3'd0;					//处于读的时候命令值为1,其他时候命令值为0	
assign wr_proc = ~app_cmd && app_rdy && app_wdf_rdy;				//拉高表示写过程进行
//处于写使能且是最后一个数据
assign wr_last = app_wdf_wren && (wr_addr_cnt == WR_LEN - 1) ;
//处于读指令、读有效且是最后一个数据
assign rd_addr_last = (rd_addr_cnt == WR_LEN - 1) && app_rdy && app_cmd;
  
//==========================================================================
//==    状态机
//==========================================================================    

always @(posedge ui_clk or negedge rst_n) begin
	if(~rst_n)
		cur_state <= IDLE;
	else
		cur_state <= next_state;
end

always @(*) begin
	if(~rst_n)
		next_state = IDLE;
	else
		case(cur_state)
			IDLE:
				if(init_calib_complete)					//MIG IP核初始化完成 
					next_state = WRITE;				
				else				
					next_state = IDLE;				
			WRITE:				
				if(wr_last) 							//写入最后一个数据
					next_state = WAIT;				
				else				
					next_state = WRITE;							
			WAIT:				
					next_state = READ;				
			READ:				
				if(rd_addr_last) 						//写入最后一个读地址,数据读出需要时间
					next_state = IDLE;
				else
					next_state = READ;					
			default:;
		endcase
end

always @(posedge ui_clk or negedge rst_n) begin
    if(~rst_n) begin				 
        app_wdf_data <= 0;     
        wr_addr_cnt  <= 0;      
        rd_addr_cnt  <= 0;       
        app_addr     <= 0;          
    end
	else
        case(cur_state)
            IDLE:begin
                app_wdf_data <= 0;   
                wr_addr_cnt  <= 0;     
                rd_addr_cnt  <= 0;       
                app_addr     <= 0;
             end
            WRITE:begin
                if(wr_proc)begin   						//写条件满足
                    app_wdf_data <= app_wdf_data + 1;  	//写数据自加
                    wr_addr_cnt  <= wr_addr_cnt + 1;   	//写地址自加
                    app_addr     <= app_addr + 8;      	//DDR3 地址加8
                end
                else begin                             	//写条件不满足,保持当前值
                    app_wdf_data <= app_wdf_data;      
                    wr_addr_cnt  <= wr_addr_cnt;
                    app_addr     <= app_addr; 
                end
              end
            WAIT:begin                                                  
                rd_addr_cnt <= 0;                		//读地址复位
                app_addr    <= 0;                		//DDR3读从地址0开始
              end
            READ:begin                               	//读到设定的地址长度     
                if(app_rdy)begin                  		//若MIG已经准备好,则开始读
                    rd_addr_cnt <= rd_addr_cnt + 1'd1; 	//用户地址每次加一
                    app_addr    <= app_addr + 8;       	//DDR3地址加8
                end
                else begin                             	//若MIG没准备好,则保持原值
                    rd_addr_cnt <= rd_addr_cnt;
                    app_addr    <= app_addr; 
                end
              end
            default:begin
                app_wdf_data <= 0;
                wr_addr_cnt  <= 0;
                rd_addr_cnt  <= 0;
                app_addr     <= 0;
            end
        endcase
end   
//==========================================================================
//==    其他
//==========================================================================

//读信号有效,且读出的数不是写入的数时,将错误标志位拉高
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));  
                      
//寄存状态标志位
always @(posedge ui_clk or negedge rst_n) begin
    if(~rst_n) 
        error_flag <= 0;
    else if(error)
        error_flag <= 1;
end
 
//对DDR3实际读数据个数编号计数
always @(posedge ui_clk or negedge rst_n) begin
    if(~rst_n) 
        rd_cnt <= 0;
	//若计数到读写长度,且读有效,地址计数器则置0   		
    else if(app_rd_data_valid && rd_cnt == WR_LEN - 1)
         rd_cnt <= 0;              
    else if (app_rd_data_valid )	//读有效情况下每个时钟+1
        rd_cnt <= rd_cnt + 1;
end
 
endmodule

        The annotations are still relatively detailed, so I won't explain them, just a few points that need attention.

  • The burst length of DDR3 is fixed at 8, and the selected DDR3 chip is 16bit, so the data bit width = 8 * 16bit = 128bit
  • The burst length of DDR3 is fixed at 8, which means that each operation is actually an operation on 8 addresses, so the address of each write data needs to accumulate 8

2.1.4. Top-level module

        The design of the top-level module is very simple: just instantiate the above three modules. as follows:

//**********************************************************************************
// *** 名称 : ddr3_rw_top
// *** 作者 : 孤独的单刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 顶层模块----例化DDR3仿真模型与DDR3读写测试模块,对DDR3进行循环读写测试
//**********************************************************************************

//============================< 端口 >======================================
module ddr3_rw_top(
//时钟和复位 ---------------------------
   input              	sys_clk			,         	//系统时钟
   input              	sys_rst_n		,       	//复位,低有效
//DDR3相关 -----------------------------
   inout	[15:0]		ddr3_dq			,         	//DDR3 数据
   inout	[1:0]		ddr3_dqs_n		,      		//DDR3 dqs负
   inout	[1:0]		ddr3_dqs_p		,      		//DDR3 dqs正       
   output	[13:0]      ddr3_addr		,       	//DDR3 地址
   output	[2:0]       ddr3_ba			,         	//DDR3 banck 选择
   output	            ddr3_ras_n		,      		//DDR3 行选择
   output	            ddr3_cas_n		,      		//DDR3 列选择
   output	            ddr3_we_n		,       	//DDR3 读写选择
   output	            ddr3_reset_n	,    		//DDR3 复位
   output	[0:0]       ddr3_ck_p		,       	//DDR3 时钟正
   output	[0:0]       ddr3_ck_n		,       	//DDR3 时钟负
   output	[0:0]       ddr3_cke		,        	//DDR3 时钟使能
   output	[0:0]       ddr3_cs_n		,       	//DDR3 片选
   output	[1:0]       ddr3_dm			,         	//DDR3_dm
   output	[0:0]       ddr3_odt		,        	//DDR3_odt
//标志相关------------------------------
   output             	error_flag					//错误指示信号
);
                
//============================< 信号定义 >======================================                      
//parameter define
parameter	integer		WR_LEN		= 512	;		//读、写长度	
parameter	integer		DATA_WIDTH	= 128	;	    //数据位宽,突发长度为8,16bit,共128bit
parameter	integer		ADDR_WIDTH	= 28	;	    //根据MIG例化而来
//wire define  
wire                  	ui_clk				;		//用户时钟
wire [ADDR_WIDTH - 1:0]	app_addr			;		//DDR3 地址
wire [2:0]            	app_cmd				;		//用户读写命令
wire                  	app_en				;		//MIG IP核使能
wire                  	app_rdy				;		//MIG IP核空闲
wire [DATA_WIDTH - 1:0]	app_rd_data			;		//用户读数据
wire                  	app_rd_data_end		;     	//突发读当前时钟最后一个数据 
wire                  	app_rd_data_valid	;   	//读数据有效
wire [DATA_WIDTH - 1:0]	app_wdf_data		;		//用户写数据 
wire                  	app_wdf_end			;		//突发写当前时钟最后一个数据 
wire [15:0]           	app_wdf_mask		;		//写数据屏蔽
wire                  	app_wdf_rdy			;		//写空闲
wire                  	app_wdf_wren		;		//DDR3 写使能                  
wire                  	locked				;		//锁相环频率稳定标志
wire                  	clk_ref_i			;		//DDR3参考时钟
wire                  	sys_clk_i			;		//MIG IP核输入时钟
wire                  	clk_200				;		//200M时钟
wire                  	ui_clk_sync_rst		;     	//用户复位信号
wire                  	init_calib_complete	;		//校准完成信号

//*****************************************************************************************
//**                    main code
//*****************************************************************************************

//============================< 例化DDR3读写测试模块 >======================================
 ddr3_rw #(
	.WR_LEN							(WR_LEN					),
	.DATA_WIDTH                     (DATA_WIDTH 			),
	.ADDR_WIDTH                     (ADDR_WIDTH 			)
 )
 u_ddr3_rw(
    .ui_clk               			(ui_clk					),                
    .ui_clk_sync_rst      			(ui_clk_sync_rst		),       
    .init_calib_complete  			(init_calib_complete	),
	
    .app_rdy              			(app_rdy				),
    .app_wdf_rdy          			(app_wdf_rdy			),
    .app_rd_data_valid    			(app_rd_data_valid		),
    .app_rd_data          			(app_rd_data			),   
    .app_addr             			(app_addr				),
    .app_en               			(app_en					),
    .app_wdf_wren         			(app_wdf_wren			),
    .app_wdf_end          			(app_wdf_end			),
    .app_cmd              			(app_cmd				),
    .app_wdf_data         			(app_wdf_data			),
   
    .error_flag           			(error_flag				)
    );	
//============================< 例化MIG IP核 >===============================================
mig_7series_0 u_mig_7series_0 (
//DDR3接口-------------------------------------------------
    .ddr3_addr                      (ddr3_addr			),   
    .ddr3_ba                        (ddr3_ba			),     
    .ddr3_cas_n                     (ddr3_cas_n			),  
    .ddr3_ck_n                      (ddr3_ck_n			),   
    .ddr3_ck_p                      (ddr3_ck_p			),   
    .ddr3_cke                       (ddr3_cke			),    
    .ddr3_ras_n                     (ddr3_ras_n			),  
    .ddr3_reset_n                   (ddr3_reset_n		),
    .ddr3_we_n                      (ddr3_we_n			),   
    .ddr3_dq                        (ddr3_dq			),     
    .ddr3_dqs_n                     (ddr3_dqs_n			),  
    .ddr3_dqs_p                     (ddr3_dqs_p			),                                                    
	.ddr3_cs_n                      (ddr3_cs_n			),   
    .ddr3_dm                        (ddr3_dm			),     
    .ddr3_odt                       (ddr3_odt			),
//用户接口-------------------------------------------------    
    .app_addr                       (app_addr			),    
    .app_cmd                        (app_cmd			),     
    .app_en                         (app_en				),      
    .app_wdf_data                   (app_wdf_data		),
    .app_wdf_end                    (app_wdf_end		), 
    .app_wdf_wren                   (app_wdf_wren		),
    .app_rd_data                    (app_rd_data		), 
    .app_rd_data_end                (app_rd_data_end	),                                                     
    .app_rd_data_valid              (app_rd_data_valid	),  
    .app_wdf_mask                   (31'b0				),                                                    
    .app_rdy                        (app_rdy			), 
    .app_wdf_rdy                    (app_wdf_rdy		), 
    .app_sr_req                     (1'b0				), 
    .app_ref_req                    (1'b0				), 
    .app_zq_req                     (1'b0				), 
    .app_sr_active                  (					),
    .app_ref_ack                    (					),  
    .app_zq_ack                     (					),
//全局信号-------------------------------------------------	
    .ui_clk                         (ui_clk				),       
    .ui_clk_sync_rst                (ui_clk_sync_rst	), 
    .init_calib_complete            (init_calib_complete), 														      
    .sys_clk_i                      (clk_200			),
    .clk_ref_i                      (clk_200			),
    .sys_rst                        (sys_rst_n			)     
);
//============================< 例化PLL模块 >=============================================== 
clk_wiz_0 u_clk_wiz_0
   (
    .clk_out1						(clk_200			),     	// output clk_out1
    .reset							(1'b0				),		// input resetn
    .locked							(locked				),		// output locked
    .clk_in1						(sys_clk			)		// input clk_in1
);                     

endmodule

2.2. Testbench and simulation results

        Ok, the Verilog code is written, and then run the simulation to verify the following.

2.2.1、Testbench

        Testbench is also very simple, only need to provide clock and reset stimulus; just instantiate the read-write test module and DDR3 simulation model, as follows:

//**************************************************************************
// *** 名称 : tb_ddr3_rw_top
// *** 作者 : 孤独的单刀
// *** 博客 : https://blog.csdn.net/wuzhikaidetb
// *** 日期 : 2021.12
// *** 描述 : 对DDR3进行循环读写测试
//**************************************************************************
`timescale 1ns/100ps

module	tb_ddr3_rw_top();
//============================< 信号 >======================================
//时钟和复位 --------------------
reg				sys_clk			;				//系统时钟
reg				sys_rst_n		;				//系统复位
//DDR3相关 ----------------------								
wire [15:0]     ddr3_dq			;				//DDR3 数据
wire [1:0]      ddr3_dqs_n		;				//DDR3 dqs负
wire [1:0]      ddr3_dqs_p		;				//DDR3 dqs正       
wire [13:0]     ddr3_addr		;				//DDR3 地址
wire [2:0]      ddr3_ba			;				//DDR3 banck 选择
wire            ddr3_ras_n		;				//DDR3 行选择
wire            ddr3_cas_n		;				//DDR3 列选择
wire            ddr3_we_n		;				//DDR3 读写选择
wire            ddr3_reset_n	;				//DDR3 复位
wire [0:0]      ddr3_ck_p		;				//DDR3 时钟正
wire [0:0]      ddr3_ck_n		;				//DDR3 时钟负
wire [0:0]      ddr3_cke		;				//DDR3 时钟使能
wire [0:0]      ddr3_cs_n		;				//DDR3 片选
wire [1:0]      ddr3_dm			;				//DDR3_dm
wire [0:0]		ddr3_odt		;				//DDR3_odt
//标志相关-----------------------			
wire			error_flag		;				//读、写错误标志

//============================< 测试条件设置 >===============================
//设置初始测试条件--------------------------------------------------------
initial begin
	sys_clk = 1'b0			;					//初始时钟为0
	sys_rst_n <= 1'b0		;					//初始复位
	#50											//5个时钟周期后
	sys_rst_n <= 1'b1		;					//拉高复位,系统进入工作状态
end
//设置时钟----------------------------------------------------------------
always #10 sys_clk = ~sys_clk;					//系统时钟周期20ns


//============================< 被测试模块例化 >===============================
//例化DDR3读写测试-------------------
ddr3_rw_top	ddr3_rw_top_inst(
   .sys_clk			(sys_clk		),			//系统时钟
   .sys_rst_n		(sys_rst_n		),			//复位,低有效
   .ddr3_dq			(ddr3_dq		),			//DDR3 数据
   .ddr3_dqs_n		(ddr3_dqs_n		),      	//DDR3 dqs负
   .ddr3_dqs_p		(ddr3_dqs_p		),      	//DDR3 dqs正       
   .ddr3_addr		(ddr3_addr		),       	//DDR3 地址
   .ddr3_ba			(ddr3_ba		),         	//DDR3 banck 选择
   .ddr3_ras_n		(ddr3_ras_n		),      	//DDR3 行选择
   .ddr3_cas_n		(ddr3_cas_n		),      	//DDR3 列选择
   .ddr3_we_n		(ddr3_we_n		),       	//DDR3 读写选择
   .ddr3_reset_n	(ddr3_reset_n	),    		//DDR3 复位
   .ddr3_ck_p		(ddr3_ck_p		),       	//DDR3 时钟正
   .ddr3_ck_n		(ddr3_ck_n		),       	//DDR3 时钟负
   .ddr3_cke		(ddr3_cke		),        	//DDR3 时钟使能
   .ddr3_cs_n		(ddr3_cs_n		),       	//DDR3 片选
   .ddr3_dm			(ddr3_dm		),         	//DDR3_dm
   .ddr3_odt		(ddr3_odt		),       	//DDR3_odt
   .error_flag		(error_flag		)			//错误标志
);   

//例化DDR3模型-----------------------
ddr3_model	ddr3_model_inst
(	
	.rst_n   		(sys_rst_n		),	
	.ck      		(ddr3_ck_p		),	
	.ck_n    		(ddr3_ck_n		),	
	.cke     		(ddr3_cke		),	
	.cs_n    		(ddr3_cs_n		),	
	.ras_n   		(ddr3_ras_n		),	
	.cas_n   		(ddr3_cas_n		),	
	.we_n    		(ddr3_we_n		),	
	.dm_tdqs 		(ddr3_dm		),	
	.ba      		(ddr3_ba		),	
	.addr    		(ddr3_addr		),	
	.dq      		(ddr3_dq		),	
	.dqs     		(ddr3_dqs_p		),	
	.dqs_n   		(ddr3_dqs_n		),	
	.tdqs_n  		(				),			//NULL
	.odt     		(ddr3_odt		)	
);

endmodule

2.2.2. Simulation results

        For the simulation, we did not use Vivado to run (not to say that it is slow, it is really slow), and switched to Modelsim to run. Modelsim runs simulation fast, and it is convenient to modify and debug back and forth.

        Without further ado, the simulation results are as follows:

        In the above picture: the MIG initialization complete signal init_calib_complete is pulled high, indicating that the MIG controller initialization is complete. Then, enter to start writing data, and then read data. It keeps repeating.  

        The picture above is: the beginning of the process of writing data:

                The write address starts to accumulate from 0 and accumulates 8 each time, because the burst length is 8;

                The written data starts to accumulate from 0, and accumulates 1 each time;

                When the command is valid, the write command is valid, and the write command is low (representing write), the data is written.

        The picture above is: the end of the write data process:

                The write address starts to accumulate from 0, and accumulates 8 each time, because the burst length is 8, and it ends at 4088;

                The written data starts to accumulate from 0, accumulates 1 each time, and ends at 511, a total of 512 data are written;

                When the command is valid, the write command is valid, and the write command is low (representing write), the data is written.

        The picture above is: the beginning of the process of reading data:

                The read address starts to accumulate from 0 and accumulates 8 each time, because the burst length is 8;

                It takes a certain amount of time from issuing a read command to actually reading the data;

                When the command is valid and the command is high (representing read), the data is read;

                The read data matches the written data (0-1-2-...).

        The picture above is: the end of the process of reading data:

                The read address starts to accumulate from 0, and accumulates 8 each time, because the burst length is 8, to 4088;

                It takes a certain amount of time from issuing a read command to actually reading the data;

                When the command is valid and the command is high (representing read), the data is read;

                The read data matches the written data (0-1-2-...511).

3. Board verification

3.1, verification environment

        FPGA:XC7A35T FGG484-2

        DDR3 : NT5CB128M16CP-DI

        Vivado : Vivado 2019.2

        Modelsim:Modelsim SE-64 2020.4

        File: V1.0

        Number: 71

3.2. Verification results

        The results are basically consistent with the simulation results, the write and read verifications are correct, and the read data are consistent. Post some pictures

        Write operation after initialization is complete:

        Read operation:

        So far, the verification of the read-write module is successful. 

3. Other

3.1. Possible problems:

3.1.1. How does Vivado combine modelsim simulation?

        Using modelsim to simulate the IP core of xilinx mainly needs to solve the problem of the simulation library. I have to say that modelsim is really easy to use.

        For reference: Co-simulation of vivado and modelsim

3.1.2. Use modelsim to simulate DDR3 error Module 'SIP_PHY_CONTROL' is not defined

        I didn't find any good solution, so I reinstalled the new version of modelsim (version 2020.4). Problem solved after reinstalling.

3.1.3. VIVADO calls ModelSim without response

        Basically a syntax error, please double check.

        For reference: The solution to the failure to call ModelSim during Xilinx VIVADO simulation

3.1.4. After the app_rdy signal is pulled high for a period of time, it is always low

        The following three reserved signals are all resolved after receiving 0 (Xilinx tells you to do this in the manual, but you don't listen, hey...)

3.2. Postscript

  • As you can see, the Native interface is actually relatively easy to use. But is there a way to make it easier to use? Of course there is! FIFO is inconvenient to use, and data can be written with an enable signal. Next, we will encapsulate the MIG into a FIFO for use, which greatly simplifies the workload.
  • It is not easy to create. If this article is helpful to you, please like , comment and bookmark more . Your support is the biggest motivation for me to keep updating!
  • If you have any thoughts about this article, you can leave a message in the comment area. If you need the entire project, please leave an email in the comments or send a private message to my email (pay attention to protecting privacy).
  • My own ability is insufficient, if there are mistakes, please point out a lot!

Guess you like

Origin blog.csdn.net/wuzhikaidetb/article/details/121646652