本文重点在于MIG IP核app接口时序的理解和多burst模式的重新封装
首先解决时钟方案:
对于xc7vx690t,最大物理层时钟为800MHz,我们选择800MHz,对应DDR(双边沿采样)1600MHz;
控制端时钟(即app端信号时钟域),通过物理层时钟800MHz分频而来,一般而言为1/2或1/4,选择最大800MHz物理层时钟时,只能1/4,因此app端信号都在200MHz时钟频率下,这个时钟是ip返回的ui_clk;
DDR3的输入时钟,即板子供给DDR3的时钟,内部物理层800MHz时钟是由此时钟在内部PLL倍频而来。
app端重要信号时序:
主要是对这幅时序理解:
- 首先控制过程分为读写命令通道和数据通道,而且图示特别指出,数据通道到来时刻不应晚超过3个cycle(ui_clk)(可以发现命令和数据应该是双FIFO架构);
- 笔者在处理此处时序卡了很久,bug总显示DDR3读入和写出的数据不一致,最终索性让写入命令和写入数据对齐,可能丢失性能,不过最终结果是正确的;
- 对于命令通道:app_en和app_rdy拉高时将app_cmd和app_addr写入FIFO,0写1读;
- 对于数据通道:app_wdf_rdy和app_wdf_wren同时拉高时,将app_wdf_data写入FIFO中;(wdf=write data fifo)
- app_wdf_end表示一个burst的结束,因为控制端时钟为1/4物理层时钟,又物理层时钟双边沿采样,因此一个burst长度为8(控制端一个时钟周期送给ddr3 8个ddr3位宽的数据),如果分两个周期传输8个数据,那么app_wdf_end应该先为低后为高,即在burst传输的最后一个周期拉高一个周期。
贴出以前写的代码:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2021/12/02 15:01:14
// Design Name:
// Module Name: ddr3_burst_new
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ddr3_burst_new
#(
parameter MEM_DATA_BITS = 512,
parameter ADDR_BITS = 29
)
(
input rst_n,
input clk,
input rd_burst_req,
input wr_burst_req,
input[15:0] rd_burst_len,
input[15:0] wr_burst_len,
input[ADDR_BITS - 4:0] rd_burst_addr,
input[ADDR_BITS - 4:0] wr_burst_addr,
output rd_burst_data_valid,
output wr_burst_data_req,
output[MEM_DATA_BITS - 1:0] rd_burst_data,
input[MEM_DATA_BITS - 1:0] wr_burst_data,
output rd_burst_finish,
output wr_burst_finish,
output burst_finish,
///////////////////
output reg [ADDR_BITS-1:0] app_addr = 0,
output[2:0] app_cmd,
output app_en,
output [MEM_DATA_BITS-1:0] app_wdf_data,
output app_wdf_end,
output [MEM_DATA_BITS/8-1:0] app_wdf_mask,
output app_wdf_wren,
input [MEM_DATA_BITS-1:0] app_rd_data,
input app_rd_data_end,
input app_rd_data_valid,
input app_rdy,
input app_wdf_rdy,
input init_calib_complete
);
//=====================================================parameter==============================================
localparam S0_IDLE = 6'b000001; //初始化状态,DDR初始化成功就跳转S1
localparam S1_WAIT = 6'b000010; //等待状态,等FIFO缓存好数据就跳转S2
localparam S2_WRITE = 6'b000100; //写DDR状态,FIFO数据写完就跳转到S3
localparam S3_WR_DONE = 6'b001000; //写完成状态,给出读地址初始值就跳到S4
localparam S4_READ = 6'b010000; //读DDR状态,读到相应长度的数量就跳到S5
localparam S5_RD_DONE = 6'b100000; //读完成状态,跳回IDLE
//=====================================================defination==============================================
//FSM
wire S1_WAIT_2_S2_WRITE;
wire S1_WAIT_2_S4_READ;
wire S2_WRITE_2_S3_WR_DONE;
wire S4_READ_2_S5_RD_DONE;
wire S5_RD_DONE_2_S0_IDLE;
reg [7 : 0] current_state = S0_IDLE;
reg [7 : 0] next_state = S0_IDLE;
//cnt
reg [15 : 0] cnt_write = 0;
wire add_cnt_write;
wire end_cnt_write;
reg [15 : 0] cnt_read = 0;
wire add_cnt_read;
wire end_cnt_read;
reg [15 : 0] cnt_read_data = 0;
wire add_cnt_read_data;
wire end_cnt_read_data;
//=====================================================output==============================================
//=========================FSM
assign S1_WAIT_2_S2_WRITE = wr_burst_req;
assign S1_WAIT_2_S4_READ = rd_burst_req;
assign S2_WRITE_2_S3_WR_DONE = end_cnt_write;
assign S4_READ_2_S5_RD_DONE = end_cnt_read;
assign S5_RD_DONE_2_S0_IDLE = end_cnt_read_data;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
current_state <= S0_IDLE;
else
current_state <= next_state;
end
always @(*)begin
case(current_state)
S0_IDLE :
if(init_calib_complete == 1'b1)
next_state <= S1_WAIT;
else
next_state <= S0_IDLE;
S1_WAIT :
if(S1_WAIT_2_S2_WRITE)
next_state <= S2_WRITE;
else if(S1_WAIT_2_S4_READ)
next_state <= S4_READ;
else
next_state <= S1_WAIT;
S2_WRITE :
if(S2_WRITE_2_S3_WR_DONE)
next_state <= S3_WR_DONE;
else
next_state <= S2_WRITE;
S3_WR_DONE :
next_state <= S0_IDLE;
S4_READ :
if(S4_READ_2_S5_RD_DONE)
next_state <= S5_RD_DONE;
else
next_state <= S4_READ;
S5_RD_DONE :
if(S5_RD_DONE_2_S0_IDLE)
next_state <= S0_IDLE;
else
next_state <= S5_RD_DONE;
default :
next_state <= S0_IDLE;
endcase
end
//app_addr
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
app_addr <= 0;
end
else begin
if(S1_WAIT_2_S2_WRITE && (current_state == S1_WAIT)) app_addr <= {wr_burst_addr, 3'd0};
else if(S1_WAIT_2_S4_READ && (current_state == S1_WAIT)) app_addr <= {rd_burst_addr, 3'd0};
else if((current_state == S2_WRITE) && app_rdy && app_wdf_rdy && app_en) app_addr <= app_addr + 'd8;
else if((current_state == S4_READ) && app_rdy && app_en) app_addr <= app_addr + 'd8;
end
end
//cnt_write
assign add_cnt_write = app_wdf_wren;
assign end_cnt_write = add_cnt_write && (cnt_write == wr_burst_len - 1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_write <= 0;
end
else begin
if(end_cnt_write) cnt_write <= 0;
else if(add_cnt_write) cnt_write <= cnt_write + 1;
end
end
//cnt_read
assign add_cnt_read = (current_state == S4_READ) && app_rdy && app_en;
assign end_cnt_read = add_cnt_read && (cnt_read == rd_burst_len - 1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_read <= 0;
end
else begin
if(end_cnt_read) cnt_read <= 0;
else if(add_cnt_read) cnt_read <= cnt_read + 1;
end
end
//cnt_read_data
assign add_cnt_read_data = app_rd_data_valid;
assign end_cnt_read_data = add_cnt_read_data && (cnt_read_data == rd_burst_len - 1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_read_data <= 0;
end
else begin
if(end_cnt_read_data) cnt_read_data <= 0;
else if(add_cnt_read_data) cnt_read_data <= cnt_read_data + 1;
end
end
assign app_wdf_mask = 16'b0; //掩码置0,表示传输的全部为有效数据
assign app_en = (current_state == S2_WRITE) || (current_state == S4_READ);// app_wdf_wren || (current_state == S4_READ);
assign app_cmd = (current_state == S4_READ) ? 3'b001 : 3'b000//
assign app_wdf_wren = (current_state == S2_WRITE) && app_rdy && app_wdf_rdy;
assign app_wdf_end = app_wdf_wren;
assign app_wdf_data = wr_burst_data;
assign wr_burst_data_req = app_wdf_wren;
assign rd_burst_finish = end_cnt_read_data;
assign wr_burst_finish = end_cnt_write;
assign rd_burst_data_valid = app_rd_data_valid;
assign rd_burst_data = app_rd_data;
endmodule