三、DDR3 Sdram IP 读时序:
1、读命令和读数据总线介绍
上节课已经对命令总线做了介绍,由于读命令总线和写命令总线复用一组总
线,因此对命令总线不做过多的介绍,但是我们需要知道 app_cmd==3’b001 代表读命令。
读命令的时序同写命令时序相同,均需要 app_en 和 app_rdy 有效时,对应的app_cmd 和 app_addr 才会被 IP 核接收,如图 1 所示,指令③和地址 A3 会被 IP核接收。
下面给出 app 端读数据总线中的每一根信号作用,此处的 input 和 output 均
现对于 IP 核而言。
显然,从ddr读出的数据不是一给读命令就出来的,而是得过一阵子,反正只有在app_rd_data_valid为高时数据有效。(所以我们要在valid为高时进行数据的突发计数和有效数据采集)。
app_rd_data_end 同 app_wdf_end 信号相同,在 DDR3 的物理层端与用户端存在两种速率比值不同的情况下,也会存在不同的状态,具体可以参考 DDR3 IP核写控制章节。
2、读控制模块框图及波形
在了解了读命令和读数据的时序及相应关系之后,我们给出读控制模块的框
图,示例如图 4 所示,其中 A7_rd_ctrl 模块即为我们需要完成的模块。
从图 4 中可以得知 A7_rd_ctrl 模块与 DDR3 IP 核是通过 app 接口进行通信,另外 A7_rd_ctrl 模块预留了一些接口,下面给出这些接口的具体描述。
当 rd_cmd_start 有效时,启动本次的读突发,根据由外部输入的 rd_cmd_bl
可以确定本次突发需要读出多少数据;rd_cmd_start 有效时,rd_cmd_addr 代表本次突发读的起始地址,由于 128bit = 8x16bit,因此每个 128bit 数据需要读出 8 个DDR3 SDRAM 的地址内数据,因此每发送一次 rd_cmd_start,rd_cmd_addr 需要加 8;由于该模块只实现读控制,因此 rd_cmd_instr 可以一直保持为读状态;可以由 rd_end 可以告知外界模块,本次突发读结束。下面给出示例波形。其中底色为绿色的是输入信号,黄色为输出信号,白色为内部信号。***(个人建议地址突发等等的最好与start同步发出,至于为什么在以后就知道了)***
3.代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/07/17 21:51:41
// Design Name:
// Module Name: rd_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module rd_ctrl(
// for top
input wire [10:0] rd_cmd_bl,
input wire rd_cmd_start,
input wire [27:0] rd_cmd_addr,
output reg rd_data_valid,
output reg rd_end,
output reg [127:0] rd_data_128bit,
//for ip
input wire ui_clk,
input wire rst_n,
input wire app_rdy,
input wire [127:0] app_rd_data,
input wire app_rd_data_end,
input wire app_rd_data_valid,
output wire [2:0] app_cmd,
output reg app_en,
output reg [27:0] app_addr
);
reg [10:0] data_cnt;
reg [10:0] reg_cmd_bl;
reg [10:0] cmd_cnt;
assign app_cmd = 3'b001;
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
reg_cmd_bl <= 11'd0;
else if(rd_cmd_start == 1'b1)
reg_cmd_bl <= rd_cmd_bl;
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_rdy == 1'b1&&app_en == 1'b1)
app_en <= 1'b0;
else if(rd_cmd_start == 1'b1)
app_en <= 1'b1;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
cmd_cnt <= 11'd0;
else if(cmd_cnt == reg_cmd_bl - 1'b1&&app_rdy == 1'b1&&app_en == 1'b1)
cmd_cnt <= 11'd0;
else if(app_rdy == 1'b1&&app_en == 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(cmd_cnt == reg_cmd_bl - 1'b1&&app_rdy == 1'b1&&app_en == 1'b1)
app_addr <= 28'd0;
else if(rd_cmd_start == 1'b1)
app_addr <= rd_cmd_addr;
else if(app_rdy == 1'b1&&app_en == 1'b1)
app_addr <= app_addr + 4'd8;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
rd_data_valid <= 1'b0;
else
rd_data_valid <= app_rd_data_valid;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
rd_data_128bit <= 128'd0;
else
rd_data_128bit <= app_rd_data;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
data_cnt <= 11'd0;
else if(data_cnt == reg_cmd_bl - 1'b1&&app_rd_data_valid == 1'b1)
data_cnt <= 11'd0;
else if(app_rd_data_valid == 1'b1)
data_cnt <= data_cnt + 1'd1;
end
always@(posedge ui_clk)begin
if(rst_n == 1'b0)
rd_end <= 1'b0;
else if(data_cnt == reg_cmd_bl - 1'b1&&app_rd_data_valid == 1'b1)
rd_end <= 1'b1;
else
rd_end <= 1'b0;
end
endmodule