写在前面
本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。
目录
实验任务
在之前设计的 DDR3 控制器的基础上,添加用户写、读模块,便于在用户端更容易的对 DDR3 进行写读控制。
实验环境
开发环境:Vivado 2018.2
FPGA 芯片型号:xc7a100tffg484-2
DDR3 型号:MT41J256M16HA-125
实验介绍
经过前几部分的控制器设计,已经包含有DDR3的读写模块、读写仲裁模块,读写指令 FIFO以及读写数据 FIFO 模块,现在便可以对此项设计进行写读验证,但是直接对 FIFO 端的信号进行控制的话,信号过多设计起来较为麻烦,因此考虑在外围添加用户端的写、读控制模块,减少信号交互数,便于用户端进行写读控制。
接口详解
用户写控制模块
用户端的写控制模块是与写指令 FIFO 和写数据 FIFO 的接口进行交互的,因此可以依据 FIFO 的相关信号进行用户写模块的设计,大致设计思路为:当用户写使能信号拉高时,依次将写数据累加,并且定义一个计数器用于计算写入数据的个数,这个可以作为突发长度的设计,当数据完全写入写数据 FIFO 中时,输出相应的指令信号:指令类型、突发长度、初始地址。其中指令类型可以固定为全0表示进行写操作,突发长度可以根据写入数据计数值得到,初始地址根据每次指令使能信号拉高累加。最后当写数据 FIFO 的空信号拉高时,拉个一个时钟周期的单次写完成信号。
接口申明
信号 |
方向 |
描述 |
clk |
input |
用户端时钟信号 |
rst_n |
input |
用户端复位,低有效 |
wr_en |
input |
写数据使能信号 |
wr_data |
input |
写入的数据 |
fifo_wr_data_empty |
input |
写fifo为空信号 |
fifo_wr_data_en |
output |
写数据fifo写使能信号 |
fifo_wr_data |
output |
写入fifo的数据 |
fifo_wr_cmd_en |
output |
写指令fifo写使能信号 |
fifo_wr_cmd_brust_len |
output |
写数据突发长度 |
fifo_wr_cmd_addr |
output |
此次写数据初始地址 |
fifo_wr_cmd_instr |
output |
操作指令信号 |
user_wr_end |
output |
单次写操作完成标志信号 |
用户读控制模块
用户端的读控制模块是与读指令 FIFO 和读数据 FIFO 的接口进行交互的,因此可以依据 FIFO 的相关信号进行用户读模块的设计,大致设计思路为:当开始读信号拉高时,依次累加读地址信号,并定义一个计数器,用于计算读出数据的个数,当读出的数据数量达到单次突发长度,并拉低读使能信号,并且拉高读完成信号,表示单次的读操作完成。
程序设计
写控制模块
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
// Engineer : Linest-5
-
// File : user_wr_ctrl.v
-
// Create : 2022-09-29 15:07:20
-
// Revise : 2022-09-29 16:26:45
-
// Module Name : user_wr_ctrl
-
// Description : 用户端写控制模块
-
// Editor : sublime text3, tab size (4)
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-
module user_wr_ctrl(
-
input clk,
//用户端时钟
-
input rst_n,
//用户端复位,低有效
-
input wr_en,
//写数据使能信号
-
input [
127:
0] wr_data,
//写入的数据
-
input fifo_wr_data_empty,
//写fifo为空信号
-
output reg fifo_wr_data_en,
//写数据fifo写使能信号
-
output reg [
127:
0] fifo_wr_data,
//写入fifo的数据
-
output reg fifo_wr_cmd_en,
//写指令fifo写使能信号
-
output reg [
7:
0] fifo_wr_cmd_brust_len,
//写数据突发长度
-
output reg [
28:
0] fifo_wr_cmd_addr,
//此次写数据初始地址
-
output [
2:
0] fifo_wr_cmd_instr,
//操作指令信号
-
output reg user_wr_end
//单次写操作完成标志信号
-
);
-
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/* 信号定义 */
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
reg [
7:
0] data_cnt;
//写入数据个数计数信号
-
reg fifo_wr_data_empty_reg;
//写数据fifo空标志打拍信号
-
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/* Main Code */
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
//写指令:3'b000,读指令:3'b001
-
assign fifo_wr_cmd_instr =
3'b000;
-
-
//写入的数据个数计数信号
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
data_cnt <= 'd0;
-
end
-
else
if (wr_en) begin
-
data_cnt <= data_cnt + 'd1;
-
end
-
else begin
-
data_cnt <= 'd0;
-
end
-
end
-
-
//对数据使能信号打拍输出
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_data_en <= 'd0;
-
end
-
else begin
-
fifo_wr_data_en <= wr_en;
-
end
-
end
-
-
//对写入的数据打拍输出
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_data <= 'd0;
-
end
-
else begin
-
fifo_wr_data <= wr_data;
-
end
-
end
-
-
//当数据完全写入fifo中后,启动命令使能信号
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_cmd_en <= 'd0;
-
end
-
else
if ((~wr_en) && fifo_wr_data_en) begin
-
fifo_wr_cmd_en <= 'd1;
-
end
-
else begin
-
fifo_wr_cmd_en <= 'd0;
-
end
-
end
-
-
//根据写入的数据计数得到突发长度输出
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_cmd_brust_len <= 'd0;
-
end
-
else
if ((~wr_en) && fifo_wr_data_en) begin
-
fifo_wr_cmd_brust_len <= data_cnt;
-
end
-
else begin
-
fifo_wr_cmd_brust_len <= 'd0;
-
end
-
end
-
-
//根据每次的写数据的个数确定下一次写的初始地址
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_cmd_addr <= 'd0;
-
end
-
else
if (fifo_wr_cmd_en) begin
-
fifo_wr_cmd_addr <= fifo_wr_cmd_addr + (fifo_wr_cmd_brust_len << 'd3);
-
end
-
else begin
-
fifo_wr_cmd_addr <= fifo_wr_cmd_addr;
-
end
-
end
-
-
//对写数据fifo的空标志拉高信号打拍
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_wr_data_empty_reg <= 'd0;
-
end
-
else begin
-
fifo_wr_data_empty_reg <= fifo_wr_data_empty;
-
end
-
end
-
-
//一次写完成信号生成
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
user_wr_end <= 'd0;
-
end
-
else
if (fifo_wr_data_empty && (~fifo_wr_data_empty_reg)) begin
-
user_wr_end <= 'd1;
-
end
-
else begin
-
user_wr_end <= 'd0;
-
end
-
end
-
-
endmodule
读控制模块
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
// Engineer : Linest-5
-
// File : user_rd_ctrl.v
-
// Create : 2022-09-29 16:27:57
-
// Revise : 2022-09-29 16:27:57
-
// Module Name : user_rd_ctrl
-
// Description : 用户端读控制模块
-
// Editor : sublime text3, tab size (4)
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-
module user_rd_ctrl(
-
input clk,
//用户端时钟
-
input rst_n,
//用户端复位,低有效
-
input rd_start,
-
input [
6:
0] fifo_rd_data_count,
-
output reg fifo_rd_data_en,
-
output reg fifo_rd_cmd_en,
-
output [
7:
0] fifo_rd_cmd_brust_len,
-
output reg [
28:
0] fifo_rd_cmd_addr,
-
output [
2:
0] fifo_rd_cmd_instr,
-
output reg user_rd_end
-
);
-
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/* 信号定义 */
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
reg [
7:
0] rd_data_cnt;
-
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/* Main Code */
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
//对开始读信号打拍输出
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_rd_cmd_en <= 'd0;
-
end
-
else begin
-
fifo_rd_cmd_en <= rd_start;
-
end
-
end
-
-
assign fifo_rd_cmd_brust_len = 'd64;
-
assign fifo_rd_cmd_instr =
3'b001;
-
-
//每次读操作的初始地址
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_rd_cmd_addr <= 'd0;
-
end
-
else
if (fifo_rd_cmd_en) begin
-
fifo_rd_cmd_addr <= fifo_rd_cmd_addr + (fifo_rd_cmd_brust_len << 'd3);
-
end
-
else begin
-
fifo_rd_cmd_addr <= fifo_rd_cmd_addr;
-
end
-
end
-
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
fifo_rd_data_en <= 'd0;
-
end
-
else
if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
-
fifo_rd_data_en <= 'd0;
-
end
-
else
if (fifo_rd_data_count == (fifo_rd_cmd_brust_len - 'd6)) begin
-
fifo_rd_data_en <= 'd1;
-
end
-
else begin
-
fifo_rd_data_en <= fifo_rd_data_en;
-
end
-
end
-
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
rd_data_cnt <= 'd0;
-
end
-
else
if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
-
rd_data_cnt <= 'd0;
-
end
-
else
if (fifo_rd_data_en) begin
-
rd_data_cnt <= rd_data_cnt + 'd1;
-
end
-
else begin
-
rd_data_cnt <= 'd0;
-
end
-
end
-
-
always @(posedge clk or negedge rst_n) begin
-
if (!rst_n) begin
-
user_rd_end <= 'd0;
-
end
-
else
if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
-
user_rd_end <= 'd1;
-
end
-
else begin
-
user_rd_end <= 'd0;
-
end
-
end
-
-
endmodule
顶层模块
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/* Engineer : Linest-5
-
/* File : top_ddr3_init.v
-
/* Create : 2022-09-15 09:58:59
-
/* Revise : 2022-09-24 21:11:50
-
/* Module Name :
-
/* Description :
-
/* Editor : sublime text3, tab size (4)
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-
module top_ddr3_init(
-
// Inouts
-
inout [15:0] ddr3_dq,
-
inout [1:0] ddr3_dqs_n,
-
inout [1: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 [1:0] ddr3_dm,
-
output [0:0] ddr3_odt,
-
// Inputs
-
// Differential system clocks
-
input sys_clk,
-
input rst_n
-
);
-
-
wire init_calib_complete;
-
wire ui_clk;
-
wire ui_clk_sync_rst;
-
wire [
127:
0] wr_data;
-
wire [
7:
0] wr_brust_len;
-
wire wr_start;
-
wire [
28:
0] wr_addr;
-
wire [
2:
0] wr_cmd;
-
wire [
15:
0] wr_mask;
-
wire data_req;
-
wire wr_end;
-
wire app_rdy;
-
wire app_wdf_rdy;
-
wire [
2:
0] app_cmd;
-
wire app_en;
-
wire [
28:
0] app_addr;
-
wire [
127:
0] app_wdf_data;
-
wire app_wdf_wren;
-
wire [
15:
0] app_wdf_mask;
-
wire app_wdf_end;
-
-
wire [
7:
0] rd_brust_len;
-
wire rd_start;
-
wire [
28:
0] rd_addr;
-
wire [
2:
0] rd_cmd;
-
wire [
127:
0] rd_data;
-
wire rd_data_valid;
-
wire rd_end;
-
wire [
127:
0] app_rd_data;
-
wire app_rd_data_end;
-
wire app_rd_data_valid;
-
-
wire [
2:
0] app_wr_cmd;
-
wire app_wr_en;
-
wire [
28:
0] app_wr_addr;
-
-
wire [
2:
0] app_rd_cmd;
-
wire app_rd_en;
-
wire [
28:
0] app_rd_addr;
-
-
/**************FIFO控制部分*******************/
-
wire user_clk;
-
//写指令部分
-
wire fifo_wr_cmd_en;
-
wire [
7:
0] fifo_wr_cmd_brust_len;
-
wire [
28:
0] fifo_wr_cmd_addr;
-
wire [
2:
0] fifo_wr_cmd_instr;
-
wire fifo_wr_cmd_empty;
-
wire fifo_wr_cmd_full;
-
-
//写数据部分
-
wire fifo_wr_data_en;
-
wire [
127:
0] fifo_wr_data;
-
wire fifo_wr_data_full ;
-
wire fifo_wr_data_empty;
-
wire [
6:
0] fifo_wr_data_count;
-
-
//读指令部分
-
wire fifo_rd_cmd_en;
-
wire [
7:
0] fifo_rd_cmd_brust_len;
-
wire [
28:
0] fifo_rd_cmd_addr;
-
wire [
2:
0] fifo_rd_cmd_instr;
-
wire fifo_rd_cmd_empty;
-
wire fifo_rd_cmd_full;
-
-
//读数据部分
-
wire fifo_rd_data_en;
-
wire [
127:
0] fifo_rd_data;
-
wire fifo_rd_data_full;
-
wire fifo_rd_data_empty;
-
wire [
6:
0] fifo_rd_data_count;
-
-
//用户写控制模块
-
wire user_wr_en;
-
wire [
127:
0] user_wr_data;
-
wire user_wr_end;
-
-
//用户读控制模块
-
wire user_rd_start;
-
wire user_rd_end;
-
-
assign app_en = app_wr_en | app_rd_en;
-
assign app_addr = app_wr_addr | app_rd_addr;
-
assign app_cmd = (app_wr_en ==
'd1) ? 3'b000 :
3'b001;
-
-
//DDR工作时钟PLL例化
-
ddr3_clock ddr3_clock_inst(
-
.clk_out1(sys_clk_in),
// output clk_out1
-
.
clk_in1(
sys_clk)
// input clk_in1
-
);
-
-
//DDR初始化模块例化
-
ddr3_init u_ddr3_init (
-
-
// Memory interface ports
-
.ddr3_addr (ddr3_addr),
// output [14:0] ddr3_addr
-
.
ddr3_ba (
ddr3_ba),
// output [2:0] ddr3_ba
-
.
ddr3_cas_n (
ddr3_cas_n),
// output ddr3_cas_n
-
.
ddr3_ck_n (
ddr3_ck_n),
// output [0:0] ddr3_ck_n
-
.
ddr3_ck_p (
ddr3_ck_p),
// output [0:0] ddr3_ck_p
-
.
ddr3_cke (
ddr3_cke),
// output [0:0] ddr3_cke
-
.
ddr3_ras_n (
ddr3_ras_n),
// output ddr3_ras_n
-
.
ddr3_reset_n (
ddr3_reset_n),
// output ddr3_reset_n
-
.
ddr3_we_n (
ddr3_we_n),
// output ddr3_we_n
-
.
ddr3_dq (
ddr3_dq),
// inout [15:0] ddr3_dq
-
.
ddr3_dqs_n (
ddr3_dqs_n),
// inout [1:0] ddr3_dqs_n
-
.
ddr3_dqs_p (
ddr3_dqs_p),
// inout [1:0] ddr3_dqs_p
-
.
init_calib_complete (
init_calib_complete),
// output init_calib_complete
-
-
.
ddr3_cs_n (
ddr3_cs_n),
// output [0:0] ddr3_cs_n
-
.
ddr3_dm (
ddr3_dm),
// output [1:0] ddr3_dm
-
.
ddr3_odt (
ddr3_odt),
// output [0:0] ddr3_odt
-
// Application interface ports
-
.
app_addr (
app_addr),
// input [28:0] app_addr
-
.
app_cmd (
app_cmd),
// input [2:0] app_cmd
-
.
app_en (
app_en),
// input app_en
-
.
app_wdf_data (
app_wdf_data),
// input [127:0] app_wdf_data
-
.
app_wdf_end (
app_wdf_wren),
// input app_wdf_end
-
.
app_wdf_wren (
app_wdf_wren),
// input app_wdf_wren
-
.
app_rd_data (
app_rd_data),
// output [127:0] app_rd_data
-
.
app_rd_data_end (
app_rd_data_end),
// output app_rd_data_end
-
.
app_rd_data_valid (
app_rd_data_valid),
// output app_rd_data_valid
-
.
app_rdy (
app_rdy),
// output app_rdy
-
.
app_wdf_rdy (
app_wdf_rdy),
// output app_wdf_rdy
-
.
app_sr_req (
1'b0),
// input app_sr_req
-
.
app_ref_req (
1'b0),
// input app_ref_req
-
.
app_zq_req (
1'b0),
// input app_zq_req
-
.
app_sr_active (
app_sr_active),
// output app_sr_active
-
.
app_ref_ack (
app_ref_ack),
// output app_ref_ack
-
.
app_zq_ack (
app_zq_ack),
// output app_zq_ack
-
.
ui_clk (
ui_clk),
// output ui_clk
-
.
ui_clk_sync_rst (
ui_clk_sync_rst),
// output ui_clk_sync_rst
-
.
app_wdf_mask (
app_wdf_mask),
// input [15:0] app_wdf_mask
-
// System Clock Ports
-
.
sys_clk_i (
sys_clk_in),
// input sys_clk_i
-
.
sys_rst (
rst_n)
// input sys_rst
-
);
-
-
//用户端写控制模块例化
-
user_wr_ctrl inst_user_wr_ctrl (
-
.clk (user_clk),
-
.rst_n ((~ui_clk_sync_rst) || (init_calib_complete)),
-
.wr_en (user_wr_en),
-
.wr_data (user_wr_data),
-
.fifo_wr_data_empty (fifo_wr_data_empty),
-
.fifo_wr_data_en (fifo_wr_data_en),
-
.fifo_wr_data (fifo_wr_data),
-
.fifo_wr_cmd_en (fifo_wr_cmd_en),
-
.fifo_wr_cmd_brust_len (fifo_wr_cmd_brust_len),
-
.fifo_wr_cmd_addr (fifo_wr_cmd_addr),
-
.fifo_wr_cmd_instr (fifo_wr_cmd_instr),
-
.user_wr_end (user_wr_end)
-
);
-
-
//用户端读控制模块
-
user_rd_ctrl inst_user_rd_ctrl (
-
.clk (user_clk),
-
.rst_n ((~ui_clk_sync_rst) || (init_calib_complete)),
-
.rd_start (user_rd_start),
-
.fifo_rd_data_count (fifo_rd_data_count),
-
.fifo_rd_data_en (fifo_rd_data_en),
-
.fifo_rd_cmd_en (fifo_rd_cmd_en),
-
.fifo_rd_cmd_brust_len (fifo_rd_cmd_brust_len),
-
.fifo_rd_cmd_addr (fifo_rd_cmd_addr),
-
.fifo_rd_cmd_instr (fifo_rd_cmd_instr),
-
.user_rd_end (user_rd_end)
-
);
-
-
-
//写指令FIFO控制模块例化
-
wr_cmd_fifo_ctrl inst_wr_cmd_fifo_ctrl (
-
.wr_cmd_clk (user_clk),
//input
-
.ui_clk (ui_clk),//input
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
-
.fifo_wr_cmd_en (fifo_wr_cmd_en),//input
-
.fifo_wr_cmd_brust_len (fifo_wr_cmd_brust_len),//input
-
.fifo_wr_cmd_addr (fifo_wr_cmd_addr),//input
-
.fifo_wr_cmd_instr (fifo_wr_cmd_instr),//input
-
.fifo_wr_cmd_start (wr_start),//input
-
.fifo_wr_cmd_empty (fifo_wr_cmd_empty),
-
.fifo_wr_cmd_full (fifo_wr_cmd_full),
-
.wr_brust_len (wr_brust_len),
-
.wr_addr (wr_addr),
-
.wr_cmd (wr_cmd)
-
);
-
-
//写数据FIFO控制模块例化
-
wr_data_fifo_ctrl inst_wr_data_fifo_ctrl (
-
.wr_data_clk (user_clk),
//input
-
.ui_clk (ui_clk),//input
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
-
.fifo_wr_data_en (fifo_wr_data_en),//input
-
.fifo_wr_data (fifo_wr_data),//input
-
.data_req (data_req),//input
-
.wr_data (wr_data),
-
.fifo_wr_data_full (fifo_wr_data_full),
-
.fifo_wr_data_empty (fifo_wr_data_empty),
-
.fifo_wr_data_count (fifo_wr_data_count)
-
);
-
-
//读指令FIFO控制模块例化
-
rd_cmd_fifo_ctrl inst_rd_cmd_fifo_ctrl (
-
.rd_cmd_clk (user_clk),
//input
-
.ui_clk (ui_clk),//input
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
-
.fifo_rd_cmd_en (fifo_rd_cmd_en),//input
-
.fifo_rd_cmd_brust_len (fifo_rd_cmd_brust_len),//input
-
.fifo_rd_cmd_addr (fifo_rd_cmd_addr),//input
-
.fifo_rd_cmd_instr (fifo_rd_cmd_instr),//input
-
.fifo_rd_cmd_start (rd_start),//input
-
.fifo_rd_cmd_empty (fifo_rd_cmd_empty),
-
.fifo_rd_cmd_full (fifo_rd_cmd_full),
-
.rd_brust_len (rd_brust_len),
-
.rd_addr (rd_addr),
-
.rd_cmd (rd_cmd)
-
);
-
-
//读数据FIFO控制模块例化
-
rd_data_fifo_ctrl inst_rd_data_fifo_ctrl (
-
.rd_data_clk (user_clk),
//input
-
.ui_clk (ui_clk),//input
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
-
.fifo_rd_data_en (fifo_rd_data_en),//input
-
.rd_data_valid (rd_data_valid),//input
-
.rd_data (rd_data),//input
-
.fifo_rd_data (fifo_rd_data),
-
.fifo_rd_data_full (fifo_rd_data_full),
-
.fifo_rd_data_empty (fifo_rd_data_empty),
-
.fifo_rd_data_count (fifo_rd_data_count)
-
);
-
-
-
//DDR仲裁模块例化
-
ddr3_arbit inst_ddr3_arbit (
-
.ui_clk (ui_clk),
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),
-
.wr_req (~fifo_wr_cmd_empty),
-
.rd_req (~fifo_rd_cmd_empty),
-
.wr_end (wr_end),
-
.rd_end (rd_end),
-
.wr_start (wr_start),
-
.rd_start (rd_start)
-
);
-
-
//DDR写操作模块例化
-
ddr3_wr_ctrl inst_ddr3_wr_ctrl (
-
.ui_clk (ui_clk),
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),
-
.wr_data (wr_data),
-
.wr_brust_len (wr_brust_len),
-
.wr_start (wr_start),
-
.wr_addr (wr_addr),
-
.wr_cmd (wr_cmd),
-
.wr_mask (wr_mask),
-
.data_req (data_req),
-
.wr_end (wr_end),
-
.app_rdy (app_rdy),
-
.app_wdf_rdy (app_wdf_rdy),
-
.app_cmd (app_wr_cmd),
-
.app_en (app_wr_en),
-
.app_addr (app_wr_addr),
-
.app_wdf_data (app_wdf_data),
-
.app_wdf_wren (app_wdf_wren),
-
.app_wdf_mask (app_wdf_mask),
-
.app_wdf_end (app_wdf_end)
-
);
-
-
//DDR读操作模块例化
-
ddr3_rd inst_ddr3_rd (
-
.ui_clk (ui_clk),
-
.rst (ui_clk_sync_rst || (~init_calib_complete)),
-
.init_calib_complete (init_calib_complete),
-
.rd_brust_len (rd_brust_len),
-
.rd_start (rd_start),
-
.rd_addr (rd_addr),
-
.rd_cmd (rd_cmd),
-
.rd_data (rd_data),
-
.rd_data_valid (rd_data_valid),
-
.rd_end (rd_end),
-
.app_rdy (app_rdy),
-
.app_rd_data (app_rd_data),
-
.app_rd_data_end (app_rd_data_end),
-
.app_rd_data_valid (app_rd_data_valid),
-
.app_cmd (app_rd_cmd),
-
.app_en (app_rd_en),
-
.app_addr (app_rd_addr)
-
);
-
-
endmodule
最后编写测试文件即可观察仿真波形,提供激励的信号不多,按照写读时序依次给出使能等信号即可。
仿真测试
模块信号分类
开启仿真将顶层信号调出,由于模块和信号众多,所以将各个模块信号分类,依次分析观察。分别为以下几类:
- 用户写模块
- 写指令 FIFO 模块
- 写数据 FIFO 模块
- DDR 写控制模块
- DDR 读控制模块
- 读指令 FIFO 模块
- 读数据 FIFO 模块
- 用户读模块
- 仲裁模块
用户写模块
当用户端的写使能信号拉高,依次将写数据加1,数据 0-63 慢一拍写入 FIFO 中,同时写入的数据量计数,并指明指令有效信号和指令内容。
写指令 FIFO 模块
有用户端口传来的命令使能信号,当指令 FIFO 的空信号拉低时,表示有指令内容存入,给到仲裁模块输出写开始信号,并且指令会传递下去。可以看到首次写的地址从0开始,而下一次写的初始地址则为512,因为突发长度为64,每个地址16位,每次写入的数据为128,因此每次数据占据8个地址,突发长度64所以一共需要占据8*64=512位,也就是0-511,因此下一次写操作的初始地址为512。
写数据 FIFO 模块
首先将数据写入 FIFO 中,然后当数据请求来临时,将数据从 FIFO 中读出,当全部数据读出后,FIFO 空信号拉高,由于数据太多不好展开,和前面的读写 FIFO 仿真一样。
DDR 写控制模块
开始写信号拉高后,依次将数据写入 DDR3 中,并含有命令握手信号和写操作握手信号,当完成写操作后将写完成信号拉高。
DDR 写控制模块
初始化完成信号拉高,可以进行读写操作;读开始信号拉高,若干个时钟周期后数据读出以及同步的有效信号,由于速率比为4:1,采用的8突发模式,因此读最后一个数据标志信号和读出数据有效信号一致;剩余为命令握手信号以及读指令相关信号,当命令握手有效表示指令有效。
读指令 FIFO 模块
读使能信号拉高以及同步的指令内容,当指令 FIFO 被读空后,发起下一次的读指令开始信号。可以看到首次读的地址从0开始,而下一次读的初始地址则为512,因为突发长度为64,每个地址16位,每次读出的数据为128,因此每次数据占据8个地址,突发长度64所以一共需要占据8*64=512位,也就是0-511,因此下一次读操作的初始地址为512。
读数据 FIFO 模块
这个时序比较简单,读出的数据写入 FIFO,当写入的数据达到设定值时,开始将数据读出,注意利用 FIFO 自带的计数器不是绝对准确的,最初设计就是因为计数器不准的原因导致仿真出现错误,因此在设定读出阈值时,尽量比预期的小一些,我这里设定的是比突发长度小6,当然小十几也是可以,只是别卡在边界值,这样容易出错!
用户读模块
开始读信号拉高,用户端从 FIFO 中读出数据,这里没有设计读出数据信号,但是从读数据使能和读数据计数信号来看也是可以证明设计无误,当将数据全部读出后,拉高读完成信号。
仲裁模块
仲裁模块逻辑设计不复杂,优先写操作,写完成开始读,读完成后开始写,循环往复,井然有序,在仲裁模块那篇有详细描述仲裁的功能设计。
至此,完成了 DDR MIG 控制器外围用户接口的设计,通过此设计可以使得用户更加方便的对 DDR 进行读写操作!
汇总篇
本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。
转载:https://blog.csdn.net/m0_61298445/article/details/127954656