一、 项目介绍:
本章节将会讲解 A7 芯片内自带的 DDR3 SDRAM 的 IP 核的写时序,以及对应的波形图和 Verilog HDL 实现。
我们调取的 DDR3 SDRAM 控制器给用户端预留了接口,我们可以通过这些预留的接口总线实现对该 IP 核的控制,本章节将会讲解如何根据 Xilinx 官方提供的技术参数来实现对 IP 核的写控制。
写命令和写数据总线介绍
DDR3 SDRAM控制器IP核主要预留了两组总线,一组可以直接绑定到DDR3 SDRAM 芯片端口,一组是留给用户端使用的,框图如图 1 所示。
说实话,别看端口很多,但是控制起来并不难。
对于写模块,我们采用数据先于命令的传输模式。当app_wdf_rdy与app_wdf_wren同时有效时,往mig中写入8倍于DDR3接口的数据。(记住是当当app_wdf_rdy与app_wdf_wren同时有效时写入一次数据)
当数据写入后(此时只写入了一个数据,当然对于DDR3存储芯片而言是写入了八个数据),可以写入写命令给mig使其发送给DDR3存储芯片,当然也是app_en于app_rdy同时有效才可以。
重点:7系列的mig和ise的mig有很大的不同,给了更大的自主化,ise中mig对于数据和命令各有单独的fifo,数据fifo的深度是64命令fifo的深度是4;并且ise中的写命令和vivado中的写命令不同,ise的写命令是一个命令就把规定的突发全写进ddr芯片而vivado是一个命令写一个数据。所以我打算把vivado的mig往ise上靠,这样也方便控制。
写控制模块框图及波形
在了解了写命令和写数据的时序及相应关系之后, 我们给出写控制模块的框
图, 示例如图 6 所示,其中 A7_wr_ctrl 模块即为我们需要完成的模块。
从图 6 中可以得知 A7_wr_ctrl 模块与 DDR3 IP 核是通过 app 接口进行通信,另外 A7_wr_ctrl 模块预留了一些接口, 下面给出这些接口的具体描述。
重点:在代码之前,我们要知道命令最好不要提前于数据,但是无需担心,正常发送即可,因为7系列的mig虽然没有ise的说的那么清楚,但7系列的mig也有fifo可以缓存数据和命令且数据fifo深度远远大于命令fifo,所以命令一般是比数据晚到的。
app_rdy:拉低的原因是命令FIFO即将满了以及正在执行命令;
app_wdf_rdy:拉低的原因是数据fifo即将满了。
由此可猜测app_rdy经常拉低。待会可以验证。
还有,因为是8bit突发,所以初始地址最好是8的倍数,这样可以保证地址的准确。不是也可以,但是会有误差,比如初始地址是4‘d10,那么第一个数据不会存在地址10内而是存在地址8内。这是一个该注意的地方。
代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/07/16 21:08:46
// Design Name:
// Module Name: wr_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module wr_ctrl(
//for top
input wire wr_cmd_start,
input wire [10:0] wr_cmd_bl,
input wire [27:0] wr_cmd_addr,
input wire [127:0] data_128bit,
input wire [15:0] wr_cmd_mask,
output wire data_req,
output reg wr_end,
//for app
input wire rst_n,
input wire ui_clk,
output reg [27:0] app_addr,
output wire [2:0] app_cmd,
output reg app_en,
output wire [127:0] app_wdf_data,
output wire app_wdf_end,
output reg app_wdf_wren,
input wire app_rdy,
input wire app_wdf_rdy,
output reg [15:0] app_wdf_mask
);
reg req_flag;
reg [10:0] reg_cmd_bl;
reg [10:0] wr_cnt;
reg [10:0] cmd_cnt;
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
app_wdf_wren <= 1'b0;
else if(wr_cnt == reg_cmd_bl-1'b1&&app_wdf_wren == 1'b1&&app_wdf_rdy == 1'b1 )
app_wdf_wren <= 1'b0;
else if(wr_cmd_start == 1'b1)
app_wdf_wren <= 1'b1;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
req_flag <= 1'b0;
else if(wr_cnt == reg_cmd_bl - 2'd2&&app_wdf_wren == 1'b1&&app_wdf_rdy == 1'b1)
req_flag <= 1'b0;
else if(wr_cmd_start == 1'b1)
req_flag <= 1'b1;
end
assign app_wdf_data = data_128bit;
assign data_req = app_wdf_rdy&&req_flag&&app_wdf_wren;
assign app_wdf_end = app_wdf_wren;
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
wr_cnt = 11'd0;
else if(wr_cnt == reg_cmd_bl - 1'b1&&app_wdf_wren == 1'b1&&app_wdf_rdy == 1'b1)
wr_cnt <= 11'd0;
else if(app_wdf_wren == 1'b1&&app_wdf_rdy == 1'b1)
wr_cnt <= wr_cnt + 1'd1;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
app_wdf_mask <= 16'b0;
else if(wr_cmd_start == 1'b1)
app_wdf_mask <= wr_cmd_mask;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
app_en <= 1'b0;
else if(cmd_cnt == reg_cmd_bl - 1'b1&&app_en == 1'b1&&app_rdy == 1'b1)
app_en <= 1'b0;
else if(app_wdf_wren == 1'b1)
app_en <= 1'b1;
end
assign app_cmd = 3'b000;
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
cmd_cnt <= 11'd0;
else if(cmd_cnt == reg_cmd_bl - 1'b1&&app_en == 1'b1&&app_rdy == 1'b1)
cmd_cnt <= 11'd0;
else if(app_en == 1'b1&&app_rdy == 1'b1)
cmd_cnt <= cmd_cnt + 1'd1;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
app_addr <= 28'd0;
else if(wr_cmd_start == 1'b1)
app_addr <= wr_cmd_addr;
else if(app_en == 1'b1&&app_rdy == 1'b1)
app_addr <= app_addr + 'd8;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
wr_end <= 1'b0;
else if(cmd_cnt == reg_cmd_bl - 1'b1&&app_en == 1'b1&&app_rdy == 1'b1)
wr_end <= 1'b1;
else
wr_end <= 1'b0;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
reg_cmd_bl <= 11'd0;
else if(wr_cmd_start == 1'b1)
reg_cmd_bl <= wr_cmd_bl;
end
endmodule
Testbench
//for wr
reg wr_cmd_start;
reg [10:0] wr_cmd_bl;
reg [27:0] wr_cmd_addr;
reg [127:0] data_128bit;
reg [15:0] wr_cmd_mask;
wire data_req;
wire wr_end;
wire ui_clk;
wire in_calie ;
assign ui_clk = DDR_TOP_inst.ui_clk;
assign in_calie = DDR_TOP_inst.init_calib_complete;
initial begin
sclk = 1’b0;
rst_n = 1’b0;
wr_cmd_start = 1’b0;
wr_cmd_bl = 11’b0;
wr_cmd_addr = 28’d0;
data_128bit = 128’b0;
wr_cmd_mask = 16’d0;
#120;
rst_n = 1’b1;
@(posedge in_calie)
#1002;
@(posedge ui_clk)
wr_cmd_start = 1’b1;
wr_cmd_bl = 11’d1024;
wr_cmd_addr = 28’d32;
data_128bit = 128’b0;
wr_cmd_mask = 16’d0;
@(posedge ui_clk)
wr_cmd_start = 1’b0;
wr_cmd_bl = 11’b0;
wr_cmd_addr = 28’d0;
data_128bit = 128’b0;
wr_cmd_mask = 16’d0;
end
always@(posedge ui_clk)
if(in_calie == 1’b0)
data_128bit <= 128’b0;
else if(wr_end == 1’b1)
data_128bit <= 128’d0;
else if(data_req == 1’b1)
data_128bit <= data_128bit + 'd12;