随路时钟概念
Dq(数据总线)与Dqs(时钟信号)构成随路时钟。
在Dqs上升或下降沿翻转Dq数据总线信号。
注意:dq与dqs为双线信号。两个信号都由发送方发送。dqs为数据同步信号。
ODT引脚:片上终端电阻,数据线接上拉电阻。
DDR与DDR2区别
时钟
DDR外部IO时钟频率与内部存储时钟频率一致。
DDR2外部IO时钟是内部存储时钟频率的两倍。
片外驱动调校(OCD)
DDR2在初始化过程中配置模式寄存器前添加新EMRS选项:可选OCD功能。
目的是让DQS与DQ数据信号之间的偏差降低到最小。
片内终结(ODT)
DDR2:ODT功能让信号被电路的终端吸收,不会在电路上形成反射对后面信号造成影响。ODT将终结电阻移植到芯片内部。
前置CAS、附加潜伏期、写入潜伏期
DDR2中读写指令可以紧随行激活发送,但读写操作没有提前,为了避免造成命令冲突,引入附加潜伏期。
RL(读潜伏期)= AL(附加潜伏期)+CL(列选通潜伏期)
从写入命令发出到第一个数据输入潜伏期 WL(写入潜伏期)=RL-1=AL+CL-1;
SDRAM无AL。
tDQSS:发出写指令到写数据之间的时间。
DDR2与DDR3区别
DDR3可以根据温度变化的自刷新频率。
参数
CL(寻址时序):DDR2 CL:2~5 AL(附加延迟):0 ~4
DDR3 CL:5~11 AL(附加延迟): 0、CL-1、CL-2 CWD(写入延迟):由具体的工作频率而定
引脚
DDR3新增重置(Reset)功能: 初始化、重新配置模式寄存器。
ZQ: 该引脚通过命令集指令用相应的时钟周期对导通电阻和ODT电阻进行重新校准。
参考电压信号VREF
DDR3中,使用两个参考电压。VREFCA(命令与地址信号)、VREFDQ(数据总线)。
预读概念
bit (位)
突发长度
DDR_SDRAM(2bit预读): 2 4 8 内部结构导致,从内部总线32位中一次读出至少2个16位数据
DDR2_SDRAM(4bit预读) 4 8 一次至少读出4个16位,2个完整的时钟周期读出
DDR3_SDRAM(8bit预读) 8
使用突发长度时读写只能顺序地址;不使用读写长度时,读写地址可随机但是要连续写指令。
DDR3主要引脚
CK,CK_n: 差分信号时钟。
CKE: 高电平使能操作指令,低电平进入休眠自刷新状态。
CS_n: 片选使能信号。高电平使能芯片。
RAS_n\CAS_n\WE_n: 指令输入。
DM(8位)/DMU,DML(16位): 遮掩输入数据使能信号。
BA0-BA2: bank地址。
A10/AP: 使能在读写操作后自动预充电,高电平使能。
A0-A14(8位宽)/A0-A13(16位宽): 地址总线。
A21/BC_n: 默认高电平8bit突发长度,低电平使用突发突变模式(4bit读,4bit写)。
ODT: 高/低电平使能校准功能。由芯片手册决定。
RESET_n: 初始化或稳定工作中复位使能,重新配置模式寄存器。
DQ: 输入输出引脚,数据总线。
DQS,DQS_n(8位宽)/DQSL,DQSL_n,DQSU,DQSU_n(16位宽): 输入输出引脚,数据同步信号。
TDQS,TDQS_n(8位宽): DRAMs输出引脚。数据遮掩信号,与DQS/DQS_n信号相同,在A11引脚控制下DM/TDQS_n可以使用数据遮掩,此时TDQS_n无效。
Vdd.Vddq: 电压引脚。
Vssq,Vss: 接地。
Vrefca: CA参考电压。
Vrefdq: DQ参考电压。
ZQ: 该引脚通过命令集指令用相应的时钟周期对导通电阻和ODT电阻进行重新校准。
vivado IP核DDR3 SDRAM设置
PHY to Controller Clock Ratio:
DDR3读写实验
DDR3模块设计
DDR3时序设计
DDR3顶层模块代码
`timescale 1ns / 1ps
module ddr_top(
input sys_clk, //系统时钟
// DDR3
inout [7:0] ddr3_dq , //DDR3 数据
inout [0:0] ddr3_dqs_n , //DDR3 dqs负
inout [0:0] ddr3_dqs_p , //DDR3 dqs正
output [14:0] ddr3_addr , //DDR3 地址
output [2:0] ddr3_ba , //DDR3 banck 选
output ddr3_ras_n , //DDR3 行选择
output ddr3_cas_n , //DDR3 列选择
output ddr3_we_n , //DDR3 读写选择
output ddr3_reset_n , //DDR3 复位
output [0:0] ddr3_ck_p , //DDR3 时钟正
output [0:0] ddr3_ck_n , //DDR3 时钟负
output [0:0] ddr3_cke , //DDR3 时钟使能
output [0:0] ddr3_cs_n , //DDR3 片选
output [0:0] ddr3_dm , //DDR3_dm
output [0:0] ddr3_odt //DDR3_odt
);
wire ui_clk ;
wire clk_200 ;
wire ui_clk_sync_rst ;
wire init_done ;
wire [28:0] app_addr ;
wire [2:0] app_cmd ;
wire app_en ;
wire [63:0] app_wdf_data ;
wire app_wdf_end ;
wire app_wdf_wren ;
wire [63:0] app_rd_data ;
wire app_rd_data_end ;
wire app_rd_data_valid ;
wire app_rdy ;
wire app_wdf_rdy ;
wire [31:0] rd_addr_cnt ;
wire [31:0] wr_addr_cnt ;
wire error_flog ;
wire pll_locked ;
wire data_state ;
reg sys_rst ;
reg [31:0] time_cnt ;
wire wr_en ;
wire rd_en ;
wire rst_en ;
always @(posedge clk_200)
if(!pll_locked)
time_cnt <= 32'b0;
else if(time_cnt == 32'd600_0)
time_cnt <= time_cnt ;
else
time_cnt <= time_cnt + 1'b1;
always @(posedge clk_200)
if(!pll_locked)
sys_rst <= 1'b1;
else if(time_cnt == 32'd600_0)
sys_rst <= 1'b0;
else
sys_rst <= sys_rst;
clk_wiz_0 PLL
(
.clk_out1(clk_200), // output clk_out1
.locked(pll_locked), // output locked
.clk_in1(sys_clk)
); // input clk_in1
vio_0 vio_0_inst (
.clk(ui_clk), // input wire clk
.probe_in0(), // input wire [0 : 0] probe_in0
.probe_out0(wr_en), // output wire [0 : 0] probe_out0
.probe_out1(rd_en), // output wire [0 : 0] probe_out1
.probe_out2(rst_en) // output wire [0 : 0] probe_out2
);
ddr3_rw ddr3_rw_inst(
.ui_clk (ui_clk) , //用户时钟 参考时钟 IP设置Clock Period 800M, 4:1比例ui_clk=200M (FPGA与DDR3传输数据时钟200M)
.sys_rst (ui_clk_sync_rst) ,
.rst_en (rst_en),
.init_done (init_done) , //DDR3初始化完成
.app_rdy (app_rdy) , //IP核命令接收准备完成信号
.app_wr_data_rdy (app_wdf_rdy) , //IP核数据接受准备完成信号
.app_rd_data_valid (app_rd_data_valid) , //IP读数据有效
.app_rd_data (app_rd_data) , //mig读出的数据 [63:0]
.wr_en (wr_en) , //写使能
.rd_en (rd_en) , //读使能
.app_addr (app_addr) , //地址 [28:0]
.app_en (app_en) , //IP发送命令信号使能
.app_wd_wren (app_wdf_wren) , //写数据使能
.app_wd_end (app_wdf_end) , //写突发当前时钟最后一个数据
.app_cmd (app_cmd) , //读写命令
.app_data_wr (app_wdf_data) , //写数据 [63:0]
.rd_addr_cnt (rd_addr_cnt) , //用户读地址计数 [31:0]
.wr_addr_cnt (wr_addr_cnt) , //用户写地址计数 [31:0]
.error_flog (error_flog) , //读写错误标志
.data_state (data_state)
);
mig_7series_0 mig_7series_0_inst (
.ddr3_addr (ddr3_addr), // output [14:0]
.ddr3_ba (ddr3_ba), // output [2:0]
.ddr3_cas_n (ddr3_cas_n), // output
.ddr3_ck_n (ddr3_ck_n), // output [0:0]
.ddr3_ck_p (ddr3_ck_p), // output [0:0]
.ddr3_cke (ddr3_cke), // output [0:0]
.ddr3_ras_n (ddr3_ras_n), // output
.ddr3_reset_n (ddr3_reset_n), // output
.ddr3_we_n (ddr3_we_n), // output
.ddr3_dq (ddr3_dq), // inout [7:0]
.ddr3_dqs_n (ddr3_dqs_n), // inout [0:0]
.ddr3_dqs_p (ddr3_dqs_p), // inout [0:0]
.init_calib_complete (init_done), // output
.ddr3_cs_n (ddr3_cs_n), // output [0:0]
.ddr3_dm (ddr3_dm), // output [0:0]
.ddr3_odt (ddr3_odt), // output [0:0]
// Application interface ports
.app_addr (app_addr), // input [28:0]
.app_cmd (app_cmd), // input [2:0]
.app_en (app_en), // input
.app_wdf_data (app_wdf_data), // input [63:0]
.app_wdf_end (app_wdf_end), // input
.app_wdf_wren (app_wdf_wren), // input
.app_rd_data (app_rd_data), // output [63:0]
.app_rd_data_end (app_rd_data_end), // output //突发读当前时钟最后一个数据
.app_rd_data_valid (app_rd_data_valid), // output
.app_rdy (app_rdy), // output
.app_wdf_rdy (app_wdf_rdy), // output
.app_sr_req (1'b0), // input 该输入被保留并且应该被绑定到0。
.app_ref_req (1'b0), // input 该高电平有效输入请求向DRAM发出刷新命令。
.app_zq_req (1'b0), // input 该高电平有效输入请求向DRAM发出ZQ校准命令。
.app_sr_active (), // output 该输出保留。
.app_ref_ack (), // output 该输出保留。
.app_zq_ack (), // output 该输出保留。
.ui_clk (ui_clk), // output 200M
.ui_clk_sync_rst (ui_clk_sync_rst), // output
.app_wdf_mask (8'b0), // input [7:0]
// System Clock Ports
.sys_clk_i (clk_200), //Input Clock Period设置200M,ip核输入200M时钟。
.sys_rst (!sys_rst) // input
);
endmodule
DDR3读写模块代码
`timescale 1ns / 1ps
module ddr3_rw(
input ui_clk , //用户时钟 参考时钟
input sys_rst ,
input rst_en ,
input init_done , //DDR3初始化完成
input app_rdy , //IP核命令接收准备完成信号
input app_wr_data_rdy , //IP核数据接受准备完成信号
input app_rd_data_valid , //IP读数据有效
input [63:0] app_rd_data , //mig读出的数据 [63:0]
input wr_en , //写使能
input rd_en , //读使能
output reg [28:0] app_addr , //地址 [28:0]
output app_en , //IP发送命令信号使能
output app_wd_wren , //写数据使能
output app_wd_end , //写突发当前时钟最后一个数据
output app_cmd , //读写命令
output reg [63:0] app_data_wr , //写数据 [63:0]
output reg [31:0] rd_addr_cnt , //用户读地址计数 [31:0]
output reg [31:0] wr_addr_cnt , //用户写地址计数 [31:0]
output reg error_flog , //读写错误标志
output reg data_state
);
parameter DATA_LENGTH = 32'd1000;
parameter IDLE = 3'b000;
parameter WRITE = 3'b001;
parameter WAIT = 3'b010;
parameter READ = 3'b100;
reg [2:0] state ; //读写状态
reg [25:0] rd_cnt ; //实际读数据标记
wire rst ;
reg error ;
ila_0 ila_0_inst (
.clk(ui_clk), // input wire clk
.probe0({init_done,app_rdy,app_rd_data_valid,app_rd_data,app_addr,app_en,app_wd_wren,app_wd_end,error_flog,data_state,app_data_wr,rd_cnt,app_wr_data_rdy,app_cmd,state,wr_en,rd_en,wr_addr_cnt,rd_addr_cnt,rst_en,sys_rst,rst,error}) // input wire [399:0] probe0
);
assign rst = rst_en | sys_rst;
assign app_en = (((state == WRITE)&&(app_wr_data_rdy&&app_rdy)) ||((state == READ)&&app_rdy)) ? 1'b1:1'b0;
assign app_wd_wren = ((state == WRITE)&&(app_wr_data_rdy&&app_rdy)) ? 1'b1:1'b0;
assign app_wd_end = app_wd_wren ;
assign app_cmd = (state == READ) ? 1'b1:1'b0;
always @(posedge ui_clk or posedge rst)
if(rst || error_flog)
begin
state <= IDLE;
wr_addr_cnt <= 32'd0;
rd_addr_cnt <= 32'd0;
app_addr <= 29'd0;
app_data_wr <= 64'b0;
data_state <= 1'b0 ;
end
else if(init_done)
begin
case(state)
IDLE :
begin
if(wr_en)
begin
state <= WRITE ;
end
else
begin
state <= IDLE ;
wr_addr_cnt <= 32'd0;
rd_addr_cnt <= 32'd0;
app_addr <= 29'd0;
app_data_wr <= 64'b0;
data_state <= 1'b0 ;
end
end
WRITE :
begin
if(wr_addr_cnt == DATA_LENGTH - 1'b1)
state <= WAIT ;
else
if(app_wr_data_rdy && app_rdy)
begin
wr_addr_cnt <= wr_addr_cnt + 1'b1;
app_addr <= app_addr + 29'd8;
app_data_wr <= app_data_wr + 1'b1;
data_state <= 1'b1 ;
end
else
begin
wr_addr_cnt <= wr_addr_cnt;
app_addr <= app_addr ;
app_data_wr <= app_data_wr;
data_state <= data_state ;
end
end
WAIT :
begin
if(rd_en)
begin
state <= READ;
app_addr <= 29'b0;
end
else
begin
state <= WAIT;
app_addr <= app_addr;
end
end
READ :
begin
if(rd_addr_cnt == DATA_LENGTH - 1'b1)
state <= IDLE ;
else if(app_rdy)
begin
rd_addr_cnt <= rd_addr_cnt + 1'b1;
app_addr <= app_addr + 29'd8;
end
else
begin
rd_addr_cnt <= rd_addr_cnt ;
app_addr <= app_addr ;
end
end
default :
begin
state <= IDLE;
wr_addr_cnt <= 32'd0;
rd_addr_cnt <= 32'd0;
app_addr <= 29'd0;
data_state <= data_state ;
end
endcase
end
always @(posedge ui_clk or posedge rst)
if(rst)
rd_cnt <= 26'd0;
else if(app_rd_data_valid)
begin
if(rd_cnt == DATA_LENGTH - 1'b1)
rd_cnt <= 26'd0;
else
rd_cnt <= rd_cnt + 1'b1;
end
else
rd_cnt <= rd_cnt;
always @(posedge ui_clk or posedge rst)
if(rst)
error_flog <= 1'b0;
else if((state == READ) && app_rd_data_valid)
begin
if(rd_cnt != app_rd_data)
error_flog <= 1'b1;
else
error_flog <= 1'b0;
end
else
error_flog <= 1'b0;
always @(posedge ui_clk or posedge rst)
if(rst)
error <= 1'b0;
else if(error_flog)
error <= 1'b1;
else
error <= error;
endmodule
DDR3读写实际波形
使用逻辑分析仪查看DDR3写时序。
使用逻辑分析仪查看DDR3读时序。
DDR4 IP 时钟
vivado IP核PLL设置
X16 DDR4芯片引脚选址
地址/控制引脚:cs_n,ras_n,we_n等。
字节通道分为T0,T1,T2,T3。每个通道分两小组U和L。
X16和X8型号的dqs:必须在U通道上,必须在N6/N7。
dq:不能在N1,N12,但与dps在同一通道。
dm/dbi:与dqs在同一通道线且只能在N0。
文章来源:https://www.toymoban.com/news/detail-521609.html
一个Bank只能绑定一组DQ。
reset_n:任何引脚但IO standard必须满足LVCMOS12。
dq低位对应P。高位对应N。dq[0]:p dq[1]:N文章来源地址https://www.toymoban.com/news/detail-521609.html
到了这里,关于DDR2/3 SDRAM学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!