write in front
Continuing from the previous article (Procedure to configure MIG IP):
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:
- Write command and write data occur on the same clock cycle
- 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
- 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!