设计任务:
设计 DDR3 SDRAM 的 IP 核的读时序,做出对应的波形图,用 Verilog HDL 实现具体的功能。
DDR3 Sdram IP 读时序:
1、读命令和读数据总线介绍
上节课已经对命令总线做了介绍,由于读命令总线和写命令总线复用一组总线,因此对命令总线不做过多的介绍,但是我们需要知道 app_cmd==3’b001 代表读命令。 读命令的时序同写命令时序相同,均需要 app_en 和 app_rdy 有效时,对应的 app_cmd 和 app_addr 才会被 IP 核接收,如图 1 所示,指令③和地址 A3 会被 IP核接收。

下面给出 app 端读数据总线中的每一根信号作用,此处的 input 和 output 均现对于 IP 核而言。

根据上表所描述,我们可以对 app 端读时序所用到的每一根信号有一点的了解,下面给出读时序的波形。图 2 中所示的①、②、④处的读数据均无效,只有③处的读数据才有效。

app_rd_data_end 同 app_wdf_end 信号相同,在 DDR3 的物理层端与用户端存在两种速率比值不同的情况下,也会存在不同的状态,具体可以参考 DDR3 IP 核写控制章节。
2、读命令和读数据间关系讲解
根据 Xilinx UG586手册我们可知,读数据是在给出读命令之后一段时间后开始出现的,具体的波形可以参考图 3 所示。

3、读控制模块框图
在了解了读命令和读数据的时序及相应关系之后,我们给出读控制模块的框图,示例如图 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 可以告知外界模块,本次突发读结束。
4、读控制模块波形图
下面给出示例波形。其中底 色为绿色的是输入信号,黄色为输出信号,白色为内部信号。

5、读控制模块代码实现
module rd_ctrl(
input wire clk,
input wire rst_n,
input wire rd_cmd_start,
input wire [2:0] rd_cmd_intr,
input wire [27:0] rd_cmd_addr,
input wire [10:0] rd_cmd_bl,
input wire app_rdy,
input wire [127:0] app_rd_data,
input wire app_rd_data_end,
input wire app_rd_data_valid,
output reg app_cmd,
output reg app_en,
output reg [27:0] app_addr,
output reg rd_end,
output reg [127:0] rd_data,
output reg rd_data_valid
);
reg [10:0] cmd_cnt;
reg [10:0] data_cnt;
reg [10:0] cmd_bl;
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
app_cmd<='d0;
end
else if (rd_cmd_start==1'b1) begin
app_cmd<=rd_cmd_intr;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
cmd_bl<='d0;
end
else if (rd_cmd_start==1'b1) begin
cmd_bl<=rd_cmd_bl;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
cmd_cnt<='d0;
end
else if(app_en==1'b1 && app_rdy==1'b1 && cmd_cnt==cmd_bl-1) begin
cmd_cnt<='d0;
end
else if (app_en==1'b1 && app_rdy==1'b1) begin
cmd_cnt<=cmd_cnt+1'b1;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
app_en<=1'b0;
end
else if(app_en==1'b1 && app_rdy==1'b1 && cmd_cnt==cmd_bl-1) begin
app_en<=1'b0;
end
else if (rd_cmd_start==1'b1) begin
app_en<=1'b1;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
data_cnt<='d0;
end
else if (app_rd_data_valid==1'b1 && data_cnt==cmd_bl-1) begin
data_cnt<='d0;
end
else if (app_rd_data_valid==1'b1) begin
data_cnt<=data_cnt+1'b1;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
rd_data_valid<=1'b0;
end
else if (app_rd_data_valid==1'b1) begin
rd_data_valid<=1'b1;
end
else begin
rd_data_valid<=1'b0;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
rd_data<='d0;
end
else if (rd_cmd_start==1'b1) begin
rd_data<=app_rd_data;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
rd_end<=1'b0;
end
else if (app_rd_data_valid==1'b1 && data_cnt==cmd_bl-1) begin
rd_end<=1'b1;
end
else begin
rd_end<=1'b0;
end
end
always @(posedge clk or posedge rst_n) begin
if (!rst_n) begin
app_addr<='d0;
end
else if (rd_cmd_start==1'b1) begin
app_addr<=rd_cmd_addr;
end
else if(rd_end==1'b1) begin
app_addr<='d0;
end
else if(app_en==1'b1 && app_rdy==1'b1) begin
app_addr<=app_addr+'d8;
end
end
endmodule