一、MIG IP核读写时序
如下图是7系列的MIG IP核结构框图。左侧是用户接口,即用户(FPGA)同MIG交互的接口,用户就必须掌握这些接口才可以使用该IP核。
将用户侧的信号分类如下图。
其中的输入输出是相对于MIG IP核的,即对用户侧来说是相反的。
写命令操作时序如下,其中,写操作app_cmd的值等于0,读操作app_cmd的值等于1。首先检查app_rdy,为高则表明此时IP核可以接受用户命令,在当前时钟拉高app_en,同时发送命令app_cmd和地址app_addr,此时命令和地址写入。
写数据的时序图如下。
个人认为一般native接口最好设置为4:1模式,这样app_wdf_wren与app_wdf_end是同步的。解释见后文。
由图可知,写数据有3种情况,即写数据与写命令发生在同一周期,写数据先于写命令,写数据落后与写命令发生(但不能超过两个时钟周期)。
所以写数据时序为:先检查app_wdf_rdy,为高则表明此时IP核可以接收数据,在当前时钟拉高写使能app_wdf_wren,给出写数据app_wdf_data。
读数据的时序图如下。
发出读命令后,用户等待数据有效信号app_rd_data_valid拉高,表明此时读数据有效。
二、MIG IP核配置
这里说明两点。
1.clock period:ddr芯片的时钟频率,双沿采样,2:1,4:1决定了用户时钟ui_clk。若选择2:1,就需要2个周期的写使能有效才能完成一次突发,app_wdf_end将落后app_wdf_end一个时钟周期。若选择4:1,app_wdf_wren与app_wdf_end是同步的。
2.data_width:ddr芯片的数据位宽,看可以选什么,决定了用户接口的输入数据位宽和输出数据位宽。需要满足带宽一致,即ddr芯片时钟* 2 * data_width==用户接口的输入数据位宽 * ui_clk。
input clock period:给MIG IP的系统时钟,与用户无关,一般设置为200mhz。MIG IP内部有个锁相环,利用这个系统时钟产生各种所需的时钟。
system clock: 这里的系统200M时钟由FPGA内部提供,不由管脚输入,根据管脚时钟用IP核生成。选择 No Buffer,如果实际硬件管脚有提供 200MHz 时钟,也可以选择 Differential(差分输入)或 Signal-Ended(单端输入)。
reference clock:该时钟需要频率为200MHz时钟,若在前面配置中将系统时钟设置为200MHz,所以可以选择Use System Clock,这样就可以将两个输入时钟合并一个共用的 200MH 输入。这里我们还是选择No Buffer。
这里选择第二个。其他未出现的信号默认即可。然后绑DDR引脚,生成IP核。
三、模块框图
该ddr3读写控制框图如下。具体流程为:用户将需要存储的数据存入写fifo,fifo_ctrl模块根据写fifo的状态产生写突发信号控制ddr3_wr模块,完成写操作;用户给出读请求,fifo_ctrl模块根据读fifo的状态产生读突发信号控制ddr3_rd模块,完成读操作。
1.ddr3_wr模块
module ddr3_wr
#(
parameter integer DATA_WIDTH = 128 , //数据位宽,根据MIG例化而来
parameter integer ADDR_WIDTH = 28 //地址位宽
)(
//时钟与复位-------------------------------------------------
input ui_clk , //用户时钟
input ui_clk_sync_rst , //复位,高有效
//用户端信号-------------------------------------------------
input wr_burst_start , //一次突发写开始 由外部写请求产生
input [ADDR_WIDTH - 1:0] wr_burst_len , //突发写入的长度
input [ADDR_WIDTH - 1:0] wr_burst_addr , //突发写入的首地址
input [DATA_WIDTH - 1:0] wr_burst_data , //需要突发写入的数据。来源写FIFO
output wr_burst_ack , //突发写响应,高电平表示正在进行突发写操作
output reg wr_burst_done , //一次突发写完成
output reg wr_burst_busy , //突发写忙状态,高电平有效
//MIG端控制信号----------------------------------------------
output reg app_en , //MIG IP发送命令使能
input app_rdy , //MIG IP命令接收准备好标致 空闲
output [2:0] app_cmd , //MIG IP操作命令,读或者写
output reg [ADDR_WIDTH - 1:0] app_addr , //MIG IP操作地址
input app_wdf_rdy , //MIG IP数据接收准备好 写数据空闲
output app_wdf_wren , //MIG IP写数据使能
output app_wdf_end , //MIG IP突发写当前时钟最后一个数据
output [(DATA_WIDTH/8) - 1:0] app_wdf_mask , //MIG IP数据掩码
output [DATA_WIDTH - 1:0] app_wdf_data //MIG IP写数据
);
//parameter define
localparam WRITE = 3'b000; //MIG写命令
//reg define
reg [ADDR_WIDTH - 1:0] wr_burst_cnt ; //写入数据个数计数器计数
reg [ADDR_WIDTH - 1:0] wr_burst_len_r ; //锁存突发长度
reg [ADDR_WIDTH - 1:0] wr_burst_addr_r ; //锁存突发地址
reg wr_burst_start_r ; //突发开始打一拍,用于锁存突发地址、突发长度
//wire define
wire wr_burst_last ; //拉高表示写入最后一个数据
//*********************************************************************************************
//** main code
//**********************************************************************************************
//不使用掩码
assign app_wdf_mask = 0 ;
//固定为写状态
assign app_cmd = WRITE;
//写指令,命令接收和数据接收都准备好,此时拉高写使能
assign app_wdf_wren = wr_burst_ack;
//从用户端输入的数据直接赋值给MIG IP核
assign app_wdf_data = wr_burst_data;
//由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
assign app_wdf_end = app_wdf_wren;
//处于写使能且是最后一个数据
assign wr_burst_last = app_wdf_wren && (wr_burst_cnt == wr_burst_len_r - 1) ;
//写响应信号,用于从前级获取数据
assign wr_burst_ack = app_en && app_wdf_rdy && app_rdy;
//wr_burst_start打一拍,锁存突发地址、突发长度
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_start_r <= 0;
else
wr_burst_start_r <= wr_burst_start;
end
//锁存突发长度
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_len_r <= 1'b0;
else if(wr_burst_start)
wr_burst_len_r <= wr_burst_len;
end
//锁存突发地址
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_addr_r <= 1'b0;
else if(wr_burst_start)
wr_burst_addr_r <= wr_burst_addr;
end
//app_en控制:突发开始时一直拉高,直到突发结束
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
app_en <= 0;
else if(wr_burst_start_r)
app_en <= 1;
else if(wr_burst_last)
app_en <= 0;
else
app_en <= app_en;
end
//突发写忙状态,拉高表示其处于突发写状态
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_busy <= 0;
else if(wr_burst_start)
wr_burst_busy <= 1; //进入写忙状态
else if(wr_burst_done)
wr_burst_busy <= 0; //突发写完成,拉低写忙状态
else
wr_burst_busy <= wr_burst_busy;
end
//写入地址
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
app_addr <= 0;
else if(wr_burst_start_r)
app_addr <= wr_burst_addr_r; //将突发写的初始地址赋值给MIG
else if(app_wdf_wren) //
app_addr <= app_addr + 8; //
else
app_addr <= app_addr;
end
//突发写完成信号
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_done <= 0;
else if(wr_burst_last)
wr_burst_done <= 1;
else
wr_burst_done <= 0;
end
//写入数据个数(突发长度计数器)
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
wr_burst_cnt <= 0;
else if(app_wdf_wren)begin
if(wr_burst_cnt == wr_burst_len_r - 1)
wr_burst_cnt <= 0; //进入写忙状态
else
wr_burst_cnt <= wr_burst_cnt + 1;
end
else
wr_burst_cnt <= wr_burst_cnt;
end
endmodule
对于MIG端的控制信号,根据时序图编写代码即可。用户端信号,实际是与fifo_ctrl模块进行交互所需的信号,这里将突发开始信号,突发长度,突发首地址以及突发数据均传入,也需要将该模块的目前状态反馈给前一级模块。
2.ddr3_rd模块
module ddr3_rd
#(
parameter integer DATA_WIDTH = 128 , //数据位宽,根据MIG例化而来
parameter integer ADDR_WIDTH = 28 //地址位宽
)
(
//时钟与复位-------------------------------------------------
input ui_clk , //用户时钟
input ui_clk_sync_rst , //复位,高有效
//用户端信号-------------------------------------------------
input rd_burst_start , //一次突发读开始
input [ADDR_WIDTH - 1:0] rd_burst_len , //突发读取的长度
input [ADDR_WIDTH - 1:0] rd_burst_addr , //突发读取的首地址
output [DATA_WIDTH - 1:0] rd_burst_data , //突发读取的数据。存入读FIFO
output rd_burst_ack , //突发读响应,高电平表示正在进行突发读操作
output reg rd_burst_done , //一次突发读完成
output reg rd_burst_busy , //突发读忙状态,高电平有效
//MIG端控制信号----------------------------------------------
output reg app_en , //MIG发送命令使能
input app_rdy , //MIG命令接收准备好标致
output [2:0] app_cmd , //MIG操作命令,读或者写
output reg [ADDR_WIDTH - 1:0] app_addr , //MIG读取DDR3地址
input [DATA_WIDTH - 1:0] app_rd_data , //MIG读出的数据
input app_rd_data_end , //MIG读出的最后一个数据
input app_rd_data_valid //MIG读出的数据有效
);
//parameter define
localparam READ = 3'b001; //MIG读命令
//reg define
reg [ADDR_WIDTH - 1:0] rd_addr_cnt ; //读取地址个数计数
reg [ADDR_WIDTH - 1:0] rd_data_cnt ; //读取数据个数计数
reg [ADDR_WIDTH - 1:0] rd_burst_len_r ; //锁存突发长度
reg [ADDR_WIDTH - 1:0] rd_burst_addr_r ; //锁存突发地址
reg rd_burst_start_r ; //突发开始打一拍,用于锁存突发地址、突发长度
//wire define
wire rd_addr_last ; //拉高
wire rd_data_last ; //拉高读出最后一个数据
//*********************************************************************************************
//** main code
//**********************************************************************************************
//固定为读状态
assign app_cmd = READ;
//将从MIG中读出的数据直接赋值给上级模块
assign rd_burst_data = app_rd_data;
//读出的最后一个地址
assign rd_addr_last = app_en && app_rdy && (rd_addr_cnt == rd_burst_len_r - 1) ;
//读出的最后一个数据
assign rd_data_last = app_rd_data_valid && (rd_data_cnt == rd_burst_len_r - 1) ;
//读响应信号,用于将MIG中的数据输出给上级模块(读出)
assign rd_burst_ack = app_rd_data_valid;
//rd_burst_start打一拍,锁存突发地址、突发长度
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_burst_start_r <= 0;
else
rd_burst_start_r <= rd_burst_start;
end
//锁存突发长度
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_burst_len_r <= 1'b0;
else if(rd_burst_start)
rd_burst_len_r <= rd_burst_len;
end
//锁存突发地址
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_burst_addr_r <= 1'b0;
else if(rd_burst_start)
rd_burst_addr_r <= rd_burst_addr;
end
//app_en控制:突发开始时一直拉高,直到突发结束
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
app_en <= 0;
else if(rd_burst_start_r)
app_en <= 1;
else if(rd_addr_last)
app_en <= 0;
else
app_en <= app_en;
end
//突发读忙状态,拉高表示其处于突发读状态
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_burst_busy <= 0;
else if(rd_burst_start)
rd_burst_busy <= 1; //进入写忙状态
else if(rd_burst_done)
rd_burst_busy <= 0; //突发传输完成,拉低写忙状态
else
rd_burst_busy <= rd_burst_busy;
end
//读取地址
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
app_addr <= 0;
else if(rd_burst_start_r)
app_addr <= rd_burst_addr_r; //将突发写的初始地址赋值给MIG
else if(app_en && app_rdy)
app_addr <= app_addr + 8; //
else
app_addr <= app_addr;
end
//突发读完成信号
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_burst_done <= 0;
else if(rd_data_last)
rd_burst_done <= 1;
else
rd_burst_done <= 0;
end
//读出地址个数计数(读取突发长度计数器)
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_addr_cnt <= 0;
else if(rd_addr_last)
rd_addr_cnt <= 0;
else if(app_en && app_rdy)begin
rd_addr_cnt <= rd_addr_cnt + 1;
end
else
rd_addr_cnt <= rd_addr_cnt;
end
//读出数据个数计数
always @(posedge ui_clk) begin
if(ui_clk_sync_rst)
rd_data_cnt <= 0;
else if(rd_data_last)
rd_data_cnt <= 0;
else if(app_rd_data_valid)begin
rd_data_cnt <= rd_data_cnt + 1;
end
else
rd_data_cnt <= rd_data_cnt;
end
endmodule
3.fifo_ctrl模块
module fifo_ctrl
#(
parameter integer FIFO_DATA_WIDTH = 16 , //根据ddr数据位宽而来
parameter integer FIFO_ADDR_WIDTH = 28 ,
parameter integer MIG_DATA_WIDTH = 128 , //数据位宽,根据MIG例化而来
parameter integer MIG_ADDR_WIDTH = 28 //地址位宽
)(
//时钟和复位 -----------------------------------------------------
input ui_clk , //用户时钟
input ui_clk_sync_rst , //复位,高有效
input init_calib_complete , //MIG初始化完成标志
//用户操作信号 --------------------------------------------
//写fifo信号-------------------------------------------------
input wr_fifo_wr_clk , //写FIFO写时钟
input wr_fifo_wr_req , //写FIFO写请求
input [FIFO_DATA_WIDTH - 1:0] wr_fifo_wr_data , //写FIFO写数据
input wr_fifo_rst , //写FIFO复位信号
input [FIFO_ADDR_WIDTH - 1:0] wr_b_addr , //写DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] wr_e_addr , //写DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] wr_len , //写DDR3突发长度
output [8:0] wr_fifo_num , //写fifo中的数据量,实际是可以读出的数据量
//读fifo信号-------------------------------------------------
input rd_fifo_rd_clk , //读FIFO读时钟
input rd_fifo_rd_req , //读FIFO读请求
output [FIFO_DATA_WIDTH - 1:0] rd_fifo_rd_data , //读FIFO读数据
input [FIFO_ADDR_WIDTH - 1:0] rd_b_addr , //读DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] rd_e_addr , //读DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] rd_len , //读DDR3数据突发长度
input rd_fifo_rst , //读FIFO复位信号
output [8:0] rd_fifo_num , //读fifo中的数据量,实际是已经写进去的数据量
//控制信号----------------------------------------------------
input read_valid , //DDR3读使能
//突发读、写模块信号 --------------------------------------------
//突发写模块 ------------------------------------------------
input wr_burst_busy , //高电平表示正在进行突发写操作
input wr_burst_done , //高电平表示完成了一次突发写操作
input wr_burst_ack , //突发写模块要写入的数据有效
output [MIG_DATA_WIDTH - 1:0] wr_burst_data , //突发写模块要写入的数据
output [FIFO_ADDR_WIDTH - 1:0] wr_burst_len , //突发写长度
output reg wr_burst_req , //请求进行突发写,驱动突发写模块
output reg [FIFO_ADDR_WIDTH - 1:0] wr_burst_addr , //突发写入的首地址
//突发读模块 ------------------------------------------------
input rd_burst_busy , //高电平表示正在进行突发读操作
input rd_burst_done , //高电平表示完成了一次突发读操作
input rd_burst_ack , //突发读模块读出的数据有效
output [FIFO_ADDR_WIDTH - 1:0] rd_burst_len , //突发读长度
output [MIG_DATA_WIDTH - 1:0] rd_burst_data , //突发读模块读出的数据
output reg rd_burst_req , //请求进行突发读
output reg [FIFO_ADDR_WIDTH - 1:0] rd_burst_addr //突发读的首地址
);
//*********************************************************************************************
//** main code
//**********************************************************************************************
assign wr_burst_len = wr_len;
assign rd_burst_len = rd_len;
//wr_burst_req,rd_burst_req:读写请求信号
always@(posedge ui_clk)begin
if(ui_clk_sync_rst)begin
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b0;
end
//初始化完成后响应读写请求,优先执行写操作,防止写入ddr3中的数据丢失
else if(init_calib_complete) begin
if(~wr_burst_busy && ~rd_burst_busy)begin
//写FIFO中的数据量达到写突发长度
if(wr_fifo_num >= wr_len && ~wr_burst_req)begin
wr_burst_req <= 1'b1; //写请求有效
rd_burst_req <= 1'b0;
end
//读FIFO中的数据量小于读突发长度,且读使能信号有效
else if((rd_fifo_num < rd_len) && read_valid && ~rd_burst_req)begin
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b1; //读请求有效
end
else begin
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b0;
end
end
else begin //非空闲状态
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b0;
end
end
else begin //MIG初始化未完成
wr_burst_req <= 1'b0;
rd_burst_req <= 1'b0;
end
end
//wr_burst_addr:ddr3写地址
always@(posedge ui_clk)begin
if(ui_clk_sync_rst)
wr_burst_addr <= wr_b_addr;
else if(wr_fifo_rst)
wr_burst_addr <= wr_b_addr; //复位fifo则地址为初始地址
else if(wr_burst_done) //一次突发写结束,更改写地址
begin
if(wr_burst_addr < (wr_e_addr - wr_len * 8))
wr_burst_addr <= wr_burst_addr + wr_len * 8; //未达到末地址,写地址累加
else
wr_burst_addr <= wr_b_addr; //到达末地址,回到写起始地址
end
end
//rd_burst_addr:ddr3读地址
always@(posedge ui_clk)begin
if(ui_clk_sync_rst)
rd_burst_addr <= rd_b_addr;
else if(rd_fifo_rst)
rd_burst_addr <= rd_b_addr;
else if(rd_burst_done) //一次突发读结束,更改读地址
begin
if(rd_burst_addr < (rd_e_addr - rd_len * 8))
rd_burst_addr <= rd_burst_addr + rd_len * 8; //读地址未达到末地址,读地址累加
else
rd_burst_addr <= rd_b_addr; //到达末地址,回到首地址
end
end
//例化写FIFO-----------------------------------------
wr_fifo_16_128 wr_fifo_16_128_inst (
//写数据接口 ----------------------------------------------
.wr_clk (wr_fifo_wr_clk ),
.wr_rst (wr_fifo_rst ),
.wr_en (wr_fifo_wr_req ),
.din (wr_fifo_wr_data ),
//读数据接口 ----------------------------------------------
.rd_clk (ui_clk ),
.rd_rst (ui_clk_sync_rst ),
.rd_en (wr_burst_ack ),
.dout (wr_burst_data ),
//指示接口 ----------------------------------------------
.full ( ),
.empty ( ),
.rd_data_count (wr_fifo_num ) // output wire [8 : 0] rd_data_count
);
//例化读FIFO-----------------------------------------
rd_fifo_128_16 rd_fifo_128_16_inst (
//写数据接口 ----------------------------------------------
.wr_clk (ui_clk ),
.wr_rst (ui_clk_sync_rst ),
.din (rd_burst_data ),
.wr_en (rd_burst_ack ),
//读数据接口 ----------------------------------------------
.rd_clk (rd_fifo_rd_clk ),
.rd_rst (rd_fifo_rst ),
.rd_en (rd_fifo_rd_req ),
.dout (rd_fifo_rd_data ),
//指示接口 ----------------------------------------------
.full ( ),
.empty ( ),
.wr_data_count (rd_fifo_num ) // output wire [8 : 0] wr_data_count
);
endmodule
该模块中例化了两个fifo。写fifo,写位宽16位(根据我们设置的ddr数据位宽),读位宽128位(根据带宽一致,理解不了直接看例化的位宽是多少),将rd_data_count引出,该信号代表写fifo中具有的128位宽数据的个数,根据这个个数,产生写突发请求。读fifo,读写位宽与写fifo相反,将wr_data_count引出,代表写进读fifo中的128位宽数据的个数。在产生读突发请求的过程中,首先需要读ddr3使能read_valid有效,其次,若读fifo中写进去的数据个数不足读突发长度,就产生一次读突发。
对于突发地址,在fifo_ctrl模块,每当接收当读或写模块的burst_done信号,就代表一次突发完成,此时,fifo_ctrl模块需要更新突发地址,即burst_addr+8,当然前提是地址不会超过读写结束地址。这里的一次突发很容易混淆。在读写控制模块中,一次固定突发8个地址。在fifo_ctrl模块中,我们设置的突发长度为len,就代表fifo_ctrl模块中的一次突发,在读写模块中需要完成len次读写突发。
4.native_ddr3_ctrl模块
module native_ddr3_ctrl
#(
parameter integer FIFO_DATA_WIDTH = 16 , //写fifo写位宽
parameter integer FIFO_ADDR_WIDTH = 28 ,
parameter integer MIG_DATA_WIDTH = 128 , //数据位宽,根据MIG例化而来
parameter integer MIG_ADDR_WIDTH = 28 //地址位宽
)(
//时钟和复位 ------------------------------------------------------
input ui_clk , //用户时钟
input ui_clk_sync_rst , //复位,高有效
input init_calib_complete , //MIG初始化完成标志
//用户操作信号 -------------------------------------------------
//写fifo信号---------------------------------------------------
input wr_fifo_wr_clk , //写FIFO写时钟
input wr_fifo_wr_req , //写FIFO写请求
input [FIFO_DATA_WIDTH - 1:0] wr_fifo_wr_data , //写FIFO写数据
input wr_fifo_rst , //写FIFO复位信号
input [FIFO_ADDR_WIDTH - 1:0] wr_b_addr , //写DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] wr_e_addr , //写DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] wr_len , //写DDR3突发长度
output [8:0] wr_fifo_num , //写fifo中的数据量
//读fifo信号----------------------------------------------
input rd_fifo_rd_clk , //读FIFO读时钟
input rd_fifo_rd_req , //读FIFO读请求
output [FIFO_DATA_WIDTH - 1:0] rd_fifo_rd_data , //读FIFO读数据
input [FIFO_ADDR_WIDTH - 1:0] rd_b_addr , //读DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] rd_e_addr , //读DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] rd_len , //读DDR3数据突发长度
input rd_fifo_rst , //读FIFO复位信号
output [8:0] rd_fifo_num , //读fifo中的数据量
//其他----------------------------------------------
input read_valid , //DDR3读使能
//MIG接口 --------------------------------------------------------------------------------------
//命令接口 ----------------------------------------------
input app_rdy , //MIG 命令接收准备好标
output app_en , //MIG IP发送命令使能
output [2:0] app_cmd , //MIG IP核操作命令,读或者写
output [MIG_ADDR_WIDTH - 1:0] app_addr , //DDR3地址
//读接口 ----------------------------------------------
input [MIG_DATA_WIDTH - 1:0] app_rd_data , //从MIG中读出的数据
input app_rd_data_end , //从MIG中读出的最后一个数据
input app_rd_data_valid , //从MIG中读出的数据有效
//写接口 ----------------------------------------------
input app_wdf_rdy , //MIG数据接收准备好
output app_wdf_wren , //用户写数据使能
output app_wdf_end , //突发写当前时钟最后一个数据
output [(MIG_DATA_WIDTH/8) - 1:0] app_wdf_mask ,
output [MIG_DATA_WIDTH - 1:0] app_wdf_data //用户写数据
);
wire wr_burst_req ; //请求对DDR3写入数据
wire [FIFO_ADDR_WIDTH - 1:0] wr_burst_addr ; //当前写入DDR3的地址
wire wr_burst_busy ; //高电平表示正在进行突发写操作
wire wr_burst_done ; //高电平表示完成了一次突发写操作
wire [MIG_DATA_WIDTH - 1:0] wr_burst_data ; //突发写模块要写入的数据
wire wr_burst_ack ; //突发写模块要写入的数据有效
wire [FIFO_ADDR_WIDTH - 1:0] wr_burst_len ; //突发写长度
wire rd_burst_req ; //请求从DDR3读出数据
wire [FIFO_ADDR_WIDTH - 1:0] rd_burst_addr ; //当前读出DDR3的地址
wire rd_burst_busy ; //高电平表示正在进行突发读操作
wire rd_burst_done ; //高电平表示完成了一次突发读操作
wire [MIG_DATA_WIDTH - 1:0] rd_burst_data ; //突发读模块读出的数据
wire rd_burst_ack ; //突发读模块读出的数据有效
wire [FIFO_ADDR_WIDTH - 1:0] rd_burst_len ; //突发读长度
//MIG接口 -----------------------------------------------------
wire app_en_wr ;
wire [2:0] app_cmd_wr ;
wire [MIG_ADDR_WIDTH - 1:0] app_addr_wr ;
wire app_en_rd ;
wire [2:0] app_cmd_rd ;
wire [MIG_ADDR_WIDTH - 1:0] app_addr_rd ;
//*********************************************************************************************
//** main code
//**********************************************************************************************
//突发读写模块共用MIG的控制信号,所以需要分时复用
assign app_en = wr_burst_busy ? app_en_wr : app_en_rd ;
assign app_cmd = wr_burst_busy ? app_cmd_wr : app_cmd_rd ;
assign app_addr = wr_burst_busy ? app_addr_wr : app_addr_rd ;
fifo_ctrl
#(
.FIFO_DATA_WIDTH (FIFO_DATA_WIDTH ),
.FIFO_ADDR_WIDTH (FIFO_ADDR_WIDTH ),
.MIG_DATA_WIDTH (MIG_DATA_WIDTH ), //数据位宽,根据MIG例化而来
.MIG_ADDR_WIDTH (MIG_ADDR_WIDTH ) //
)
fifo_ctrl_inst(
//时钟和复位 -----------------------------------------------------
.ui_clk (ui_clk ), //用户时钟
.ui_clk_sync_rst (ui_clk_sync_rst ), //复位,高有效
.init_calib_complete (init_calib_complete), //MIG初始化完成标志
//用户操作信号 ----------------------------------------------------
//写fifo信号-------------------------------------------------
.wr_fifo_wr_clk (wr_fifo_wr_clk ), //写FIFO写时钟
.wr_fifo_wr_req (wr_fifo_wr_req ), //写FIFO写请求
.wr_fifo_wr_data (wr_fifo_wr_data ), //写FIFO写数据
.wr_fifo_rst (wr_fifo_rst ), //写FIFO复位信号
.wr_b_addr (wr_b_addr ), //写DDR3首地址
.wr_e_addr (wr_e_addr ), //写DDR3末地址
.wr_len (wr_len ), //写DDR3突发长度
.wr_fifo_num (wr_fifo_num ), //写fifo中的数据量
//读fifo信号-------------------------------------------------
.rd_fifo_rd_clk (rd_fifo_rd_clk ), //读FIFO读时钟
.rd_fifo_rd_req (rd_fifo_rd_req ), //读FIFO读请求
.rd_fifo_rd_data (rd_fifo_rd_data ), //读FIFO读数据
.rd_b_addr (rd_b_addr ), //读DDR3首地址
.rd_e_addr (rd_e_addr ), //读DDR3末地址
.rd_len (rd_len ), //读DDR3数据突发长度
.rd_fifo_rst (rd_fifo_rst ), //读FIFO复位信号
.rd_fifo_num (rd_fifo_num ), //读fifo中的数据量
//控制信号----------------------------------------------------
.read_valid (read_valid ), //DDR3读使能
//突发读、写模块信号 --------------------------------------------
//突发写模块 ------------------------------------------------
.wr_burst_busy (wr_burst_busy ), //高电平表示正在进行突发写操作
.wr_burst_done (wr_burst_done ), //高电平表示完成了一次突发写操作
.wr_burst_ack (wr_burst_ack ), //突发写模块要写入的数据有效
.wr_burst_data (wr_burst_data ), //突发写模块要写入的数据
.wr_burst_req (wr_burst_req ), //请求进行突发写
.wr_burst_addr (wr_burst_addr ), //突发写入的首地址
.wr_burst_len (wr_burst_len ), //突发写长度
//突发读模块 ------------------------------------------------
.rd_burst_busy (rd_burst_busy ), //高电平表示正在进行突发读操作
.rd_burst_done (rd_burst_done ), //高电平表示完成了一次突发读操作
.rd_burst_ack (rd_burst_ack ), //突发读模块读出的数据有效
.rd_burst_data (rd_burst_data ), //突发读模块读出的数据
.rd_burst_req (rd_burst_req ), //请求进行突发读
.rd_burst_addr (rd_burst_addr ), //突发读的首地址
.rd_burst_len (rd_burst_len ) //突发读长度
);
ddr3_wr
#(
.DATA_WIDTH (MIG_DATA_WIDTH ), //数据位宽,根据MIG例化而来
.ADDR_WIDTH (MIG_ADDR_WIDTH ) //
)
ddr3_wr_inst(
.ui_clk (ui_clk ), //用户时钟
.ui_clk_sync_rst (ui_clk_sync_rst ), //复位,高有效
//突发写相关信号
.wr_burst_start (wr_burst_req ),
.wr_burst_len (wr_burst_len ),
.wr_burst_addr (wr_burst_addr ),
.wr_burst_data (wr_burst_data ),
.wr_burst_ack (wr_burst_ack ),
.wr_burst_done (wr_burst_done ),
.wr_burst_busy (wr_burst_busy ), //突发写忙状态
//MIG 用户端控制接口
.app_en (app_en_wr ),
.app_cmd (app_cmd_wr ),
.app_addr (app_addr_wr ),
.app_rdy (app_rdy ), //MIG 命令接收准备好标致
.app_wdf_rdy (app_wdf_rdy ), //MIG数据接收准备好
.app_wdf_wren (app_wdf_wren ), //用户写数据使能
.app_wdf_end (app_wdf_end ), //突发写当前时钟最后一个数据
.app_wdf_mask (app_wdf_mask ),
.app_wdf_data (app_wdf_data ) //用户写数据
);
ddr3_rd #(
.DATA_WIDTH (MIG_DATA_WIDTH ), //数据位宽,根据MIG例化而来
.ADDR_WIDTH (MIG_ADDR_WIDTH ) //
)
ddr3_rd_inst(
.ui_clk (ui_clk ), //用户时钟
.ui_clk_sync_rst (ui_clk_sync_rst ), //复位,高有效
//突发读相关信号
.rd_burst_start (rd_burst_req ),
.rd_burst_len (rd_burst_len ),
.rd_burst_addr (rd_burst_addr ),
.rd_burst_data (rd_burst_data ),
.rd_burst_ack (rd_burst_ack ),
.rd_burst_done (rd_burst_done ),
.rd_burst_busy (rd_burst_busy ), //突发写忙状态
//MIG 用户端控制接口
.app_en (app_en_rd ),
.app_cmd (app_cmd_rd ),
.app_addr (app_addr_rd ),
.app_rdy (app_rdy ), //MIG 命令接收准备好标致
.app_rd_data (app_rd_data ), //从MIG中读出的数据
.app_rd_data_end (app_rd_data_end ), //从MIG中读出的最后一个数据
.app_rd_data_valid (app_rd_data_valid ) //从MIG中读出的数据有效
);
endmodule
5.native_ddr3_top模块
module native_ddr3_top
#(
parameter integer FIFO_DATA_WIDTH = 16 ,
parameter integer FIFO_ADDR_WIDTH = 28 ,
parameter integer MIG_DATA_WIDTH = 128 , //数据位宽,突发长度为8,16bit,共128bit
parameter integer MIG_ADDR_WIDTH = 28 //根据MIG例化而来
)(
//时钟和复位 ------------------------------------------------------
input clk_200 , //
input sys_rst_n , //复位,低有效
//用户操作信号 -------------------------------------------------
//写fifo信号---------------------------------------------------
input wr_fifo_wr_clk , //写FIFO写时钟
input wr_fifo_wr_req , //写FIFO写请求
input [FIFO_DATA_WIDTH - 1:0] wr_fifo_wr_data , //写FIFO写数据
input wr_fifo_rst , //写FIFO复位信号
input [FIFO_ADDR_WIDTH - 1:0] wr_b_addr , //写DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] wr_e_addr , //写DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] wr_len , //写DDR3突发长度
output [8:0] wr_fifo_num , //写fifo中的数据量
//读fifo信号----------------------------------------------
input rd_fifo_rd_clk , //读FIFO读时钟
input rd_fifo_rd_req , //读FIFO读请求
output [FIFO_DATA_WIDTH - 1:0] rd_fifo_rd_data , //读FIFO读数据
input [FIFO_ADDR_WIDTH - 1:0] rd_b_addr , //读DDR3首地址
input [FIFO_ADDR_WIDTH - 1:0] rd_e_addr , //读DDR3末地址
input [FIFO_ADDR_WIDTH - 1:0] rd_len , //读DDR3数据突发长度
input rd_fifo_rst , //读FIFO复位信号
output [8:0] rd_fifo_num , //读fifo中的数据量
//其他----------------------------------------------------------
input read_valid , //DDR3读使能
//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
);
//mig_ctrl相关 -----------------------------------------------------
wire ui_clk ; //用户时钟
wire ui_clk_sync_rst ; //用户复位信号
wire init_calib_complete ; //校准完成信号
wire app_rdy ; //MIG 命令接收准备好标
wire app_en ; //MIG IP发送命令使能
wire [2:0] app_cmd ; //MIG IP核操作命令,读或者写
wire [MIG_ADDR_WIDTH - 1:0] app_addr ; //DDR3地址
wire [MIG_DATA_WIDTH - 1:0] app_rd_data ; //从MIG中读出的数据
wire app_rd_data_end ; //从MIG中读出的最后一个数据
wire app_rd_data_valid ; //从MIG中读出的数据有效
wire app_wdf_rdy ; //MIG数据接收准备好
wire app_wdf_wren ; //用户写数据使能
wire app_wdf_end ; //突发写当前时钟最后一个数据
wire [(MIG_DATA_WIDTH/8) - 1:0] app_wdf_mask ;
wire [MIG_DATA_WIDTH - 1:0] app_wdf_data ; //用户写数据
//*********************************************************************************************
//** main code
//**********************************************************************************************
//============================< 例化native_ddr3_ctrl模块 >===============================================
native_ddr3_ctrl
#(
.FIFO_DATA_WIDTH (FIFO_DATA_WIDTH ),
.FIFO_ADDR_WIDTH (FIFO_ADDR_WIDTH ),
.MIG_DATA_WIDTH (MIG_DATA_WIDTH ), //数据位宽,根据MIG例化而来
.MIG_ADDR_WIDTH (MIG_ADDR_WIDTH ) //
)
native_ddr3_ctrl_inst(
.ui_clk (ui_clk ), //用户时钟
.ui_clk_sync_rst (ui_clk_sync_rst ), //复位,高有效
.init_calib_complete (init_calib_complete), //MIG初始化完成标志
.wr_fifo_wr_clk (wr_fifo_wr_clk ), //写FIFO写时钟
.wr_fifo_wr_req (wr_fifo_wr_req ), //写FIFO写请求
.wr_fifo_wr_data (wr_fifo_wr_data ), //写FIFO写数据
.wr_fifo_rst (~sys_rst_n ), //写FIFO复位信号
.wr_b_addr (wr_b_addr ), //写DDR3首地址
.wr_e_addr (wr_e_addr ), //写DDR3末地址
.wr_len (wr_len ), //写DDR3突发长度
.wr_fifo_num (wr_fifo_num ), //写fifo中的数据量
.rd_fifo_rd_clk (rd_fifo_rd_clk ), //读FIFO读时钟
.rd_fifo_rd_req (rd_fifo_rd_req ), //读FIFO读请求
.rd_fifo_rd_data (rd_fifo_rd_data ), //读FIFO读数据
.rd_b_addr (rd_b_addr ), //读DDR3首地址
.rd_e_addr (rd_e_addr ), //读DDR3末地址
.rd_len (rd_len ), //读DDR3数据突发长度
.rd_fifo_rst (~sys_rst_n ), //读FIFO复位信号
.rd_fifo_num (rd_fifo_num ), //读fifo中的数据量
.read_valid (read_valid ), //DDR3读使能
.app_en (app_en ), //MIG IP发送命令使能
.app_cmd (app_cmd ), //MIG IP核操作命令,读或者写
.app_addr (app_addr ), //DDR3地址
.app_rdy (app_rdy ), //MIG 命令接收准备好标
.app_rd_data (app_rd_data ), //从MIG中读出的数据
.app_rd_data_end (app_rd_data_end ), //从MIG中读出的最后一个数据
.app_rd_data_valid (app_rd_data_valid ), //从MIG中读出的数据有效
.app_wdf_rdy (app_wdf_rdy ), //MIG数据接收准备好
.app_wdf_wren (app_wdf_wren ), //用户写数据使能
.app_wdf_end (app_wdf_end ), //突发写当前时钟最后一个数据
.app_wdf_mask (app_wdf_mask ),
.app_wdf_data (app_wdf_data ) //用户写数据
);
//============================< 例化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_rdy (app_rdy ),
.app_wdf_mask (app_wdf_mask ),
.app_wdf_rdy (app_wdf_rdy ),
.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_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 )
);
endmodule
本文主要参考文章:> https://blog.csdn.net/wuzhikaidetb/article/details/121841813
本人水平不高,如有问题,欢迎各位批评指正。