软件环境:vivado 2017.4 硬件平台:XC7K325
之前有发过一篇PL侧MIG的配置方式的博客,但是当时没有留测试代码,是再犹豫代码要不要统一搞GIT上,但是我这网不知道是咋了,老是代码同步不过去,火大的很,气的我就不搞了,过一段时间以后就。。。就忘了密码了,然后又找回来,又GIT不上去,又火大。。。循环循环循环,算了,干脆老样子,直接贴下面算了。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/08/15 15:09:24
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top
#(//***************************************************************************
// Traffic Gen related parameters
//***************************************************************************
parameter PORT_MODE = "BI_MODE",
parameter DATA_MODE = 4'b0010,
parameter TST_MEM_INSTR_MODE = "R_W_INSTR_MODE",
parameter EYE_TEST = "FALSE",
// set EYE_TEST = "TRUE" to probe memory
// signals. Traffic Generator will only
// write to one single location and no
// read transactions will be generated.
parameter DATA_PATTERN = "DGEN_ALL",
// For small devices, choose one only.
// For large device, choose "DGEN_ALL"
// "DGEN_HAMMER", "DGEN_WALKING1",
// "DGEN_WALKING0","DGEN_ADDR","
// "DGEN_NEIGHBOR","DGEN_PRBS","DGEN_ALL"
parameter CMD_PATTERN = "CGEN_ALL",
// "CGEN_PRBS","CGEN_FIXED","CGEN_BRAM",
// "CGEN_SEQUENTIAL", "CGEN_ALL"
parameter CMD_WDT = 'h3FF,
parameter WR_WDT = 'h1FFF,
parameter RD_WDT = 'h3FF,
parameter SEL_VICTIM_LINE = 0,
parameter BEGIN_ADDRESS = 32'h00000000,
parameter END_ADDRESS = 32'h00ffffff,
parameter PRBS_EADDR_MASK_POS = 32'hff000000,
//***************************************************************************
// The following parameters refer to width of various ports
//***************************************************************************
parameter CK_WIDTH = 1,
// # of CK/CK# outputs to memory.
parameter nCS_PER_RANK = 1,
// # of unique CS outputs per rank for phy
parameter CKE_WIDTH = 1,
// # of CKE outputs to memory.
parameter DM_WIDTH = 8,
// # of DM (data mask)
parameter ODT_WIDTH = 1,
// # of ODT outputs to memory.
parameter BANK_WIDTH = 3,
// # of memory Bank Address bits.
parameter COL_WIDTH = 10,
// # of memory Column Address bits.
parameter CS_WIDTH = 1,
// # of unique CS outputs to memory.
parameter DQ_WIDTH = 64,
// # of DQ (data)
parameter DQS_WIDTH = 8,
parameter DQS_CNT_WIDTH = 3,
// = ceil(log2(DQS_WIDTH))
parameter DRAM_WIDTH = 8,
// # of DQ per DQS
parameter ECC = "OFF",
parameter ECC_TEST = "OFF",
//parameter nBANK_MACHS = 4,
parameter nBANK_MACHS = 4,
parameter RANKS = 1,
// # of Ranks.
parameter ROW_WIDTH = 15,
// # of memory Row Address bits.
parameter ADDR_WIDTH = 29,
// # = RANK_WIDTH + BANK_WIDTH
// + ROW_WIDTH + COL_WIDTH;
// Chip Select is always tied to low for
// single rank devices
//***************************************************************************
// The following parameters are mode register settings
//***************************************************************************
parameter BURST_MODE = "8",
// DDR3 SDRAM:
// Burst Length (Mode Register 0).
// # = "8", "4", "OTF".
// DDR2 SDRAM:
// Burst Length (Mode Register).
// # = "8", "4".
//***************************************************************************
// The following parameters are multiplier and divisor factors for PLLE2.
// Based on the selected design frequency these parameters vary.
//***************************************************************************
parameter CLKIN_PERIOD = 5000,
// Input Clock Period
parameter CLKFBOUT_MULT = 4,
// write PLL VCO multiplier
parameter DIVCLK_DIVIDE = 1,
// write PLL VCO divisor
parameter CLKOUT0_PHASE = 315.0,
// Phase for PLL output clock (CLKOUT0)
parameter CLKOUT0_DIVIDE = 1,
// VCO output divisor for PLL output clock (CLKOUT0)
parameter CLKOUT1_DIVIDE = 2,
// VCO output divisor for PLL output clock (CLKOUT1)
parameter CLKOUT2_DIVIDE = 32,
// VCO output divisor for PLL output clock (CLKOUT2)
parameter CLKOUT3_DIVIDE = 8,
// VCO output divisor for PLL output clock (CLKOUT3)
parameter MMCM_VCO = 800,
// Max Freq (MHz) of MMCM VCO
parameter MMCM_MULT_F = 8,
// write MMCM VCO multiplier
parameter MMCM_DIVCLK_DIVIDE = 1,
// write MMCM VCO divisor
//***************************************************************************
// Simulation parameters
//***************************************************************************
parameter SIMULATION = "FALSE",
// Should be TRUE during design simulations and
// FALSE during implementations
//***************************************************************************
// IODELAY and PHY related parameters
//***************************************************************************
parameter TCQ = 100,
parameter DRAM_TYPE = "DDR3",
//***************************************************************************
// System clock frequency parameters
//***************************************************************************
parameter nCK_PER_CLK = 4,
// # of memory CKs per fabric CLK
//***************************************************************************
// Debug parameters
//***************************************************************************
parameter DEBUG_PORT = "OFF",
// # = "ON" Enable debug signals/controls.
// = "OFF" Disable debug signals/controls.
parameter RST_ACT_LOW = 1
// =1 for active low reset,
// =0 for active high.)
)
(
// Inouts
inout [63:0] ddr3_dq,
inout [7:0] ddr3_dqs_n,
inout [7:0] ddr3_dqs_p,
// Outputs
output [14: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 [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [0:0] ddr3_cs_n,
output [7:0] ddr3_dm,
output [0:0] ddr3_odt,
// Inputs
//output tg_compare_error,
//output init_calib_complete,
input clk_50
);
wire clk_200;
wire locked;
wire sys_clk_i;
wire tg_compare_error;
wire init_calib_complete;
localparam DATA_WIDTH = 64;
localparam RANK_WIDTH = clogb2(RANKS);
localparam PAYLOAD_WIDTH = (ECC_TEST == "OFF") ? DATA_WIDTH : DQ_WIDTH;
localparam BURST_LENGTH = STR_TO_INT(BURST_MODE);
localparam APP_DATA_WIDTH = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;
localparam APP_MASK_WIDTH = APP_DATA_WIDTH / 8;
//***************************************************************************
// Traffic Gen related parameters (derived)
//***************************************************************************
localparam TG_ADDR_WIDTH = ((CS_WIDTH == 1) ? 0 : RANK_WIDTH)
+ BANK_WIDTH + ROW_WIDTH + COL_WIDTH;
localparam MASK_SIZE = DATA_WIDTH/8;
wire [(2*nCK_PER_CLK)-1:0] app_ecc_multiple_err;
wire [(2*nCK_PER_CLK)-1:0] app_ecc_single_err;
wire [ADDR_WIDTH-1:0] app_addr;
wire [2:0] app_cmd;
wire app_en;
wire app_rdy;
wire [APP_DATA_WIDTH-1:0] app_rd_data;
wire app_rd_data_end;
wire app_rd_data_valid;
wire [APP_DATA_WIDTH-1:0] app_wdf_data;
wire app_wdf_end;
wire [APP_MASK_WIDTH-1:0] app_wdf_mask;
wire app_wdf_rdy;
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;
wire app_wdf_wren;
wire [(64+(2*APP_DATA_WIDTH))-1:0] error_status;
wire [(PAYLOAD_WIDTH/8)-1:0] cumlative_dq_lane_error;
wire mem_pattern_init_done;
wire [47:0] tg_wr_data_counts;
wire [47:0] tg_rd_data_counts;
wire modify_enable_sel;
wire [2:0] data_mode_manual_sel;
wire [2:0] addr_mode_manual_sel;
wire [APP_DATA_WIDTH-1:0] cmp_data;
reg [63:0] cmp_data_r;
wire cmp_data_valid;
reg cmp_data_valid_r;
wire cmp_error;
wire [(PAYLOAD_WIDTH/8)-1:0] dq_error_bytelane_cmp;
wire clk;
wire rst;
wire [11:0] device_temp;
assign sys_clk_i = clk_200;//200M的系统时钟
clk_wiz_0 clk_wiz_0
(
// Clock out ports
.clk_out1(clk_200),//output
// Status and control signals
.reset(1'b0),//input
.locked(locked),//output
// Clock in ports
.clk_in1(clk_50)//input
);
function integer clogb2 (input integer size);
begin
size = size - 1;
for (clogb2=1; size>1; clogb2=clogb2+1)
size = size >> 1;
end
endfunction // clogb2
function integer STR_TO_INT;
input [7:0] in;
begin
if(in == "8")
STR_TO_INT = 8;
else if(in == "4")
STR_TO_INT = 4;
else
STR_TO_INT = 0;
end
endfunction
mig_7series_0 u_mig_7series_0(
// Inouts
.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]
// Outputs
.ddr3_addr(ddr3_addr), //output [14:0]
.ddr3_ba(ddr3_ba), //output [2:0]
.ddr3_ras_n(ddr3_ras_n), //output
.ddr3_cas_n(ddr3_cas_n), //output
.ddr3_we_n(ddr3_we_n), //output
.ddr3_reset_n(ddr3_reset_n), //output
.ddr3_ck_p(ddr3_ck_p), //output [0:0]
.ddr3_ck_n(ddr3_ck_n), //output [0:0]
.ddr3_cke(ddr3_cke), //output [0:0]
.ddr3_cs_n(ddr3_cs_n), //output [0:0]
.ddr3_dm(ddr3_dm), //output [7:0]
.ddr3_odt(ddr3_odt), //output [0:0]
// Inputs
// Single-ended system clock
.sys_clk_i(sys_clk_i),// input
// user interface signals
.app_addr(app_addr), //input [28: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_mask(64'd0), //input [63:0]
.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(clk), //output
.ui_clk_sync_rst(rst), //output
.init_calib_complete(init_calib_complete), //output
.device_temp(device_temp), //output [11:0]
.sys_rst(locked) //input
);
//ddr test application
localparam [1:0]IDLE =2'd0;
localparam [1:0]WRITE =2'd1;
localparam [1:0]WAIT =2'd2;
localparam [1:0]READ =2'd3;
localparam [2:0]CMD_WRITE =3'd0;
localparam [2:0]CMD_READ =3'd1;
localparam TEST_DATA_RANGE =24'd1000;//数据范围
reg [1 :0]state=0;
reg [23:0]Count_64=0;
reg [ADDR_WIDTH-1:0]app_addr_begin=0;
reg [7:0] count_temp=0;//读数据比对
reg [15:0] count_temp2=0;//确保读1000个数据标志
assign app_wdf_end =app_wdf_wren;//两个相等即可
assign app_en =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : ((state==READ)&&app_rdy&&(count_temp2 <= TEST_DATA_RANGE));//读1000个数后取消使能
assign app_wdf_wren =(state==WRITE) ? (app_rdy&&app_wdf_rdy) : 1'b0;
assign app_cmd =(state==WRITE) ? CMD_WRITE : CMD_READ;
assign app_addr =app_addr_begin;
assign app_wdf_data ={
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],
Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0],Count_64[7:0]
};//写入测试数据
(* KEEP = "TRUE" *) reg [31:0]app_rd_data_r;
(* KEEP = "TRUE" *) reg app_rd_data_valid_r;
always @(posedge clk) begin
app_rd_data_r <= app_rd_data[31:0];
app_rd_data_valid_r <= app_rd_data_valid;
end
always@(posedge clk)
if(rst&!init_calib_complete)
begin
state <=IDLE;
app_addr_begin <=29'd0;
Count_64 <=24'd0;
end
else case(state)
IDLE: begin
state <=WRITE;
Count_64 <=24'd0;
count_temp2 <= 16'd0;
app_addr_begin <=29'd0;
end
WRITE: begin
state <=(Count_64==TEST_DATA_RANGE)&&app_rdy&&app_wdf_rdy ? WAIT:state;//写够跳等待
Count_64 <=app_rdy&&app_wdf_rdy?(Count_64+24'd1):Count_64;//写有效自增
app_addr_begin <=app_rdy&&app_wdf_rdy?(app_addr_begin+29'd8):app_addr_begin;//下一跳(512/64 = 8)自增地址
end
WAIT: begin
state <=READ;
Count_64 <=24'd0;
app_addr_begin <=29'd0;
end
READ: begin
state <=(Count_64==TEST_DATA_RANGE)&&app_rdy? IDLE:state;//读完退出
count_temp2 <=app_rdy?(count_temp2+24'd1):count_temp2; //读够1000关app_en
Count_64 <=app_rd_data_valid_r?(Count_64+24'd1):Count_64;//读取个数计数
app_addr_begin <=app_rdy?(app_addr_begin+29'd8):app_addr_begin;//下一跳(512/64 = 8)自增地址
end
default:begin
state <=IDLE;
app_addr_begin <=29'd0;
Count_64 <=24'd0;
end
endcase
//16bit count used for comparation
//读取数据比对组
always @(posedge clk) begin
if(app_rd_data_valid_r)
count_temp<= count_temp + 8'b1;
else if(app_cmd==CMD_WRITE)
count_temp <= 8'd0;
end
wire [31:0]cm_data;//compare data
assign cm_data = {count_temp,count_temp,count_temp,count_temp};
assign tg_compare_error=(app_rd_data_valid_r&&(cm_data!=app_rd_data_r));//读出不相等,出错
ila_0 degbug_0 (
.clk(clk), // input wire clk
.probe0(app_rd_data_r), // input wire [31:0] probe0
.probe1(cm_data), // input wire [31:0] probe1
.probe2(state[1:0]), // input wire [2:0] probe2
.probe3(app_rd_data_valid_r), // input wire [0:0] probe4
.probe4(tg_compare_error),
.probe5(app_rdy),
.probe6(app_wdf_rdy),
.probe7(app_addr_begin),//29
.probe8(Count_64),//24
.probe9(app_en),
.probe10(app_wdf_wren),
.probe11(app_cmd)
);
endmodule
说下测试原理,往DDR中写1000个数,数据量的大小由TEST_DATA_RANGE来控制,因为我这里只是简单测试,简单认为1000个循环读写没问题,程序能用,读写正常就行,更复杂的地址遍历当然也可以,再这个基础上改就行。
首先是写状态,在app_rdy与app_wdf_rdy均有效的情况下,可以往DDR中写,当写够数量以后,跳到WAIT状态,而后进入读,在读状态这里需要注意一下,app_en在读的时候判别式是这样的,(state==READ)&&app_rdy&&(count_temp2 <= TEST_DATA_RANGE),在app_rdy拉高可以进行读操作的时候,count_temp2变量自增,自增到TEST_DATA_RANGE范围后,判别式为假,app_en拉低,读地址无效,此时只是伴随读地址增加,不再继续读而已,但是读过程并没有结束,因为读过程结束不结束是由Count_64来控制的,app_rd_data_valid_r拉高才表示真正读到了数据,读数据有效,等Count_64自增到TEST_DATA_RANGE,读过程才结束,进入下一个写-等-读循环。这里在读状态,为什么要搞两个变量控制,看我下面实测的图,我进一步说明。
下图是写状态时候抓的。
展开以后是。
可以看到,在进入写状态以后,随着app_rdy和app_wdf_rdy为高,app_en为高,地址开始增加,往DDR里开始写数就行,这里写进去的数就是Count_64拼成的带宽512 bit的数。写时序如下所示。
在读状态时,抓图如下。
先看读状态时候时序。当app_rdy为高,拉高app_en写入读地址app_addr,而后!在若干个周期之后!等待app_rd_data_valid为高,此时预示着数据被读出,读出的数据有效。
展开读状态的图进一步细说。
在进入读状态之后,可以看到伴随app_rdy为高,app_en拉高以后,app_rd_data_valid在过了30多个clk之后才为高,此时数据才慢慢被读出,但是这个时候,读地址已经写了30多个了,所以当读地址写够1000个时候,数据还有30来个没读完呢,这才用两个变量,Count_64用来计读出来多少个数据了,count_temp2用来计读地址写了多少个了,地址写够1000不写了,等着数据读完就行。用count_temp来在读的过程中生成对照组,当读出来的数据与count_temp不一致时,拉高tg_compare_error,表示读取错误。下图是再展开时候读取比对的图,从读状态时候的第一张图也能看出,读取过程中没有出错。
整个过程就是这样,回头说说代码中最前端的默认参数哪来的,怎么设置,其实打开生成好的MIG核的example design,最开始的地方就能看到。
打开以后就是下面这个样子啦。
这种测试其实最多测试一下功能正常不正常,测速行不行?行,但是没啥意义,因为这种只有MIG的测试与实际使用差别太大,实际工程项目中,随着工程越来越大,布局布线方式不同,以及工作环境温度的变化,对DDR的稳定性都是有影响的,都会影响到DDR最高稳定工作频率,所以说单独测速没意义,功能正常基本就行。
最后再说这里面的一个坑,在MIG接口列表里,需要注意一下复位, .sys_rst()这个接口,一般习惯上来说,接口上不带n就表示高电平有效,也就是高电平复位,但是!但是!这里,即便这个接口不带n,也是低电平有效!再说一次!MIG核的sys_rst信号!低电平复位!!!