信号 | 类型 | 作用 |
---|---|---|
CK,~CK | Input | 一对差分信号时钟,可在CK的上升沿和下降沿采样 |
CKE | Input | 时钟使能,操作使能,为低时只能自刷新 |
CS | Input | 片选信号 |
RAS,CAS,WE | Input | 指令线 |
For x8 DM,Forx16 DMU DML | Input | 屏蔽数据 |
BA0-BA2 | Input | bank地址 |
A10/AP | Input | 自动刷新的控制 |
For x8, A0-A14/For x16 A0-A13 | Inpout | 地址线 |
A12/BC | Input | 突发模式控制线,默认8突发 |
ODT | Input | 自动的电压校准,使差分时钟保持稳定,低有效 |
RESET_N | Input | 复位信号 |
DQ | Input/Output | 数据线 |
For x8DQS,(DQS_N)/For x8 DQSL,DQSU | Input/output | 数据选择脉冲 |
For x8,TDQS | Output |
1.上电时复位信号保持低电平,其他信号随意。复位低电平至少保持200us,CKE要在复位信号拉高之前至少保持10ms的低电平。
2.复位信号失效后需要等待500us,CKE信号开始拉高,在此期间DRAM开始初始化。
3.CKE为高电平且稳定后,需要加载寄存器。
4.配置寄存器时指令是相同的,通过BA0-BA2来控制是配置MR。如MR2是BA2-BA0->010;
5.配置完成后需要ZQCL校准。
寄存器 | 作用 |
---|---|
BL | 突发长度 |
CL | 突发长度 |
RBT | 顺序读还是常规读 |
CAS | CL延迟配置 |
BL | 突发长度 |
DDR3 SDRAM程序过于复杂,因此官方提供MIG IP核来简化DDR3 SDRAM的驱动程序开发
MIG IP核集成了SDRAM DDR2 DDR3接口,将原本的上电、刷新、读、写四个过程简化为了只有读写两个过程,且不用考虑行选通和列选通的等待时间,大大简化了DDR控制器的操作难度。
DDR3有四突发和八突发模式,当控制时钟和DDR的时钟在1:4时默认八突发。此时数据线是DDR硬件位宽的八倍。128:16或者256:32。
读数据接口
Column 1 | Column 2 |
---|---|
app_addr[ADDR_WIDTH-1:0] | DDR3的地址线 |
app_cmd[2:0] | DDR3指令手册,只有读写两个指令 |
app_en | MIG核的使能信号 |
app_rdy | 表示MIG核可以接受指令 |
app_hi_pri | 优先级指令 |
app_rd_data[APP_DATA_WIDTH-1:0] | 读出的数据 |
app_rd_data_end | 最后一次突发,突发结束标志位 |
app_rd_data_valid | 读数据有效标志 |
app_sz | 需要保留并置零 |
写数据接口
Column 1 | Column 2 |
---|---|
app_wdf_data[APP_DATA_WIDTH-1:0] | 为写命令提供数据 |
app_wdf_end | 突发写操作的最后一个时钟周期 |
app_wdf_mask[APP_DATA_WIDTH-1:0] | 数据掩码 |
app_wdf_rdy | 写数据允许标志 |
app_wdf_wren | 写数据使能 |
app_correct_en_i | 纠错功能 |
app_sr_req | 保留并且置零 |
app_sr_active | 保留 |
其他接口
Column 1 | Column 2 |
---|---|
app_ref_req | 刷新请求 |
app_ref_ack | 刷新请求的应答 |
app_zq_req | 校准请求 |
app_zq_ack | 校准请求应答 |
ui_clk | 用户时钟,由DRAM时钟的四分之一或者二分之一 |
init_calib_complete | 初始化完成标志 |
app_ecc_multiple_err[7:0] | 当开启ECC功能并且数据有效时的纠错指示位 |
ui_clk_sync_rst | UI复位,高电平有效 |
app_ecc_single_err[7:0] |
当app_en和app_rdy同时为高时,地址和写命令才有效
背靠背和非背靠背
地址和命令是否连续。
如上图所示,写数据有三种情形均可以正确写入:
(1)写数据时序和写命令时序发生在同一拍;
(2)写数据时序比写命令时序提前一拍;
(3)写数据时序比写命令时序至多延迟晚两拍;
//****************************************Copyright (c)***********************************//
//原子哥在线教学平台:www.yuanzige.com
//技术支持:www.openedv.com
//淘宝店铺:http://openedv.taobao.com
//关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved
//----------------------------------------------------------------------------------------
// File name: ddr3_rw_top
// Last modified Date: 2019/8/21 9:56:36
// Last Version: V1.0
// Descriptions: 读写测试实现模块
//----------------------------------------------------------------------------------------
// Created by: 正点原子
// Created date: 2019/8/21 10:55:56
// Version: V1.0
// Descriptions: The original version
//
//****************************************************************************************//
module ddr3_rw (
input ui_clk, //用户时钟
input ui_clk_sync_rst, //复位,高有效
input init_calib_complete, //DDR3初始化完成
input app_rdy, //MIG 命令接收准备好标致
input app_wdf_rdy, //MIG数据接收准备好
input app_rd_data_valid, //读数据有效
input [255:0] app_rd_data, //用户读数据
output reg [27:0] app_addr, //DDR3地址
output app_en, //MIG IP发送命令使能
output app_wdf_wren, //用户写数据使能
output app_wdf_end, //突发写当前时钟最后一个数据
output [2:0] app_cmd, //MIG IP核操作命令,读或者写
output reg [255:0] app_wdf_data, //用户写数据
output reg [1 :0] state, //读写状态
output reg [23:0] rd_addr_cnt, //用户读地址计数
output reg [23:0] wr_addr_cnt, //用户写地址计数
output reg [20:0] rd_cnt, //实际读地址标记
output reg error_flag, //读写错误标志
output reg led //读写测试结果指示灯
);
//parameter define
parameter TEST_LENGTH = 1000;
parameter L_TIME = 25'd25_000_000;
parameter IDLE = 2'd0; //空闲状态
parameter WRITE = 2'd1; //写状态
parameter WAIT = 2'd2; //读到写过度等待
parameter READ = 2'd3; //读状态
//reg define
reg [24:0] led_cnt; //led计数
//wire define
wire error; //读写错误标记
wire rst_n; //复位,低有效
//*****************************************************
//** main code
//*****************************************************
assign rst_n = ~ui_clk_sync_rst;
//读信号有效,且读出的数不是写入的数时,将错误标志位拉高
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));
//在写状态MIG IP 命令接收和数据接收都准备好,或者在读状态命令接收准备好,此时拉高使能信号,
assign app_en = ((state == WRITE && (app_rdy && app_wdf_rdy))
||(state == READ && app_rdy)) ? 1'b1:1'b0;
//在写状态,命令接收和数据接收都准备好,此时拉高写使能
assign app_wdf_wren = (state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
//由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
assign app_wdf_end = app_wdf_wren;
//处于读的时候命令值为1,其他时候命令值为0
assign app_cmd = (state == READ) ? 3'd1 :3'd0;
//DDR3读写逻辑实现
always @(posedge ui_clk or negedge rst_n) begin
if((~rst_n)||(error_flag)) begin
state <= IDLE;
app_wdf_data <= 128'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
else if(init_calib_complete)begin //MIG IP核初始化完成
case(state)
IDLE:begin
state <= WRITE;
app_wdf_data <= 256'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
WRITE:begin
if(wr_addr_cnt == TEST_LENGTH - 1 &&(app_rdy && app_wdf_rdy))
state <= WAIT; //写到设定的长度跳到等待状态
else if(app_rdy && app_wdf_rdy)begin //写条件满足
app_wdf_data <= app_wdf_data + 1; //写数据自加
wr_addr_cnt <= wr_addr_cnt + 1; //写地址自加
app_addr <= app_addr + 8; //DDR3 地址加8
end
else begin //写条件不满足,保持当前值
app_wdf_data <= app_wdf_data;
wr_addr_cnt <= wr_addr_cnt;
app_addr <= app_addr;
end
end
WAIT:begin
state <= READ; //下一个时钟,跳到读状态
rd_addr_cnt <= 24'd0; //读地址复位
app_addr <= 28'd0; //DDR3读从地址0开始
end
READ:begin //读到设定的地址长度
if(rd_addr_cnt == TEST_LENGTH - 1 && app_rdy)
state <= IDLE; //则跳到空闲状态
else if(app_rdy)begin //若MIG已经准备好,则开始读
rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
app_addr <= app_addr + 8; //DDR3地址加8
end
else begin //若MIG没准备好,则保持原值
rd_addr_cnt <= rd_addr_cnt;
app_addr <= app_addr;
end
end
default:begin
state <= IDLE;
app_wdf_data <= 256'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
endcase
end
end
//对DDR3实际读数据个数编号计数
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
rd_cnt <= 0; //若计数到读写长度,且读有效,地址计数器则置0
else if(app_rd_data_valid && rd_cnt == TEST_LENGTH - 1)
rd_cnt <= 0; //其他条件只要读有效,每个时钟自增1
else if (app_rd_data_valid )
rd_cnt <= rd_cnt + 1;
end
//寄存状态标志位
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
error_flag <= 0;
else if(error)
error_flag <= 1;
end
//led指示效果控制
always @(posedge ui_clk or negedge rst_n) begin
if((~rst_n) || (~init_calib_complete )) begin
led_cnt <= 25'd0;
led <= 1'b0;
end
else begin
if(~error_flag) //读写测试正确
led <= 1'b1; //led灯常亮
else begin //读写测试错误
led_cnt <= led_cnt + 25'd1;
if(led_cnt == L_TIME - 1'b1) begin
led_cnt <= 25'd0;
led <= ~led; //led灯闪烁
end
end
end
end
endmodule