以上是上电200us后,对SDRAM的初始化时序图:左数第一条黄线是对所有bank预充电,第二条到第三条黄线是8个刷新周期,第四条黄线是模式寄存器设置。
以上是对SDRAM读写操作的时序图:左数第一条黄线是行选通,行地址;第二条列选通,列地址,写数据;第三条发出标志写结束;第四条行选通,行地址;第五条列选通,列地址;第六条发出标志读结束,数据已放到数据总线。
/**
******************************************************************************
* @file sdram_v1.v
* @author 西殿源
* @version V1
* @date 09/03/2012 最后修改,硬件验证通过
* @brief SDRAM控制器
* @info 1. 本版本实现SDRAM最基本功能
2. 将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
3. 顺序寻址,避开页错失的情况,tRCD + CL
4. 不考虑突发读写
5. 不考虑页直接命中的情况
6. 代码风格上尚未做到组合逻辑与时序逻辑的分离
******************************************************************************
*/
module sdram_v1(
//-------------------时钟复位-------------------------------------//
input clk,
input rst_n,
//-------------------片内端口-------------------------------------//
input[23:0] addr_in, //内部输入地址总线
input wr, //内部写内存信号,低电平有效,脉宽一个clk周期即可
input rd, //内部读内存信号,低电平有效,脉宽一个clk周期即可
output reg wr_ok, //写SDRAM,=1标志数据已写入SDRAM,脉宽一个clk周期
output reg rd_ok, //读SDRAM,=1标志数据已送到总线上,脉宽一个clk周期
//-------------------片外端口-------------------------------------//
output reg cs_ro, //SDRAM片选,寄存器输出
output reg cke_ro, //SDRAM时钟使能
output reg[12:0] addr_ro, //SDRAM地址总线
output reg[1:0] bank_ro, //SDRAM块片选
output reg ras_ro, //SDRAM行激活
output reg cas_ro, //SDRAM列激活
output reg we_ro, //SDRAM写使能
output reg[1:0] dqm //SDRAM Mask
);
parameter YES = 1'b1;
parameter NO = 1'b0;
parameter HI = 1'b1;
parameter LO = 1'b0;
parameter INIT_200 = 3'd0;
parameter INIT_CHG = 3'd1;
parameter INIT_FRE = 3'd2;
parameter INIT_MRS = 3'd3;
parameter INIT_END = 3'd4;
parameter INIT = 3'd0;
parameter IDLE = 3'd1;
parameter WR_SG = 3'd2;
parameter WR_BU = 3'd3;
parameter RD_SG = 3'd4;
parameter RD_BU = 3'd5;
reg[1:0] next_init;
reg[2:0] cur_init; //SDRAM状态之初始化状态寄存器
reg[15:0] dly_cnt; //延时周期数设置寄存器,君位
reg[15:0] dly_cnt1; //延时周期计数器,臣位
reg[2:0] next_stat;
reg[2:0] cur_stat; //SDRAM状态寄存器
reg dly_en; //延时器使能,与dly_cnt一起使用,君位
reg dly_en1; //延时器使能,与dly_cnt1一起使用,臣位
wire dly_ok; //延时到
reg exe_enr;
wire exe_enw;
reg[1:0] step;
reg[7:0] i;
reg nop_yes;
assign exe_enw = (exe_enr == YES) || (dly_ok == YES);
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
cur_stat <= INIT; //SDRAM工作状态寄存器初始化
cur_init <= INIT_200; //SDRAM初始化状态寄存器
dly_en <= NO;
dly_cnt <= 0;
exe_enr <= YES;
step <= 0; //SDRAM某工作状态的子状态初始化
nop_yes <= NO;
i <= 0;
wr_ok <= LO;
rd_ok <= LO;
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b11111;
addr_ro <= 0;
bank_ro <= 0;
dqm <= 0;
end
else begin
case(cur_stat)
INIT : begin //初始化未完毕不能转到其他状态
if(exe_enw == YES) begin //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
exe_enr <= NO; // 进入条件块应清除exe_enr,使得只有延时时刻到达时才进入下一子状态
case(cur_init)
INIT_200 : begin //上电后200us稳定期
cs_ro <= LO;
dly_cnt <= 10000; //用于延时块
dly_en <= YES; //用于延时块
nop_yes <= NO;
cur_init <= INIT_CHG;
end
INIT_CHG : begin //所有bank预充电
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01010;
addr_ro[10] <= HI; //所有bank预充电,持续时间要大于20ns
dly_cnt <= 2; //用于延时块
dly_en <= YES; //用于延时块
nop_yes <= YES;
i <= 7;
cur_init <= INIT_FRE;
end
INIT_FRE : begin //8个刷新周期
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01001;
dly_cnt <= 9; //用于延时块
dly_en <= YES; //用于延时块
i <= i - 1;
nop_yes <= YES;
if(i==0) cur_init <= INIT_MRS;
else ;
end
INIT_MRS : begin //模式寄存器设置
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01000;
bank_ro <= 2'd0;
addr_ro <= {
3'b000,
1'b1,
2'b00,
3'b010,
1'b0,
3'b000
};
dly_cnt <= 2; //用于延时块 设定模式寄存器后,等2个clk才能接受新的命令
dly_en <= YES; //用于延时块
nop_yes <= YES;
cur_init <= INIT_END;
end
INIT_END : begin //清除操作
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111; //空操作
cur_stat <= IDLE;
end
default : cur_init <= INIT_200;
endcase
end//if
else begin
dly_en <= NO;
if(nop_yes == YES) {cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
else ;
end
end
IDLE : begin //空闲状态检测wr,rd位,判断是否有数据读写请求
case({wr,rd}) //
2'b01 : begin
cur_stat <= WR_SG; //有写数据请求,跳转到写状态,单蹦个寻址
step <= 0;
end
2'b10 : begin
cur_stat <= RD_SG; //有读数据请求,跳转到读状态,单蹦个寻址
step <= 0;
end
default : ; //空操作
endcase
exe_enr <= YES;
wr_ok <= LO;
rd_ok <= LO;
end
WR_SG : begin //单蹦个的写状态
if(exe_enw == YES) begin //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
exe_enr <= NO; //进入条件块后清除exe_enr,使得只有延时时刻到达时才进入下一子状态
case(step)
0 : begin
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01011; //行激活
bank_ro <= addr_in[1:0]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
addr_ro <= addr_in[23:11];
dly_cnt <= 2; //用于延时块 tRCD=2
dly_en <= YES; //用于延时块
nop_yes <= YES;
step <= 1;
end
1 : begin
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01100; //列激活
addr_ro[8:0] <= addr_in[10:2]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
addr_ro[10] <= HI; //自动预充
dly_cnt <= 2; //用于延时块
dly_en <= YES; //用于延时块
nop_yes <= YES;
step <= 2;
end
2 : begin //清除操作
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
cur_stat <= IDLE; //本状态任务完毕,转到IDLE状态_ok <=
wr_ok <= HI; //=1标志数据已写入SDRAM,脉宽一个clk周期
end
default : ;
endcase
end//if
else begin
dly_en <= NO;
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
end
end//WR_SG
RD_SG : begin //单蹦个的读状态
if(exe_enw == YES) begin //由exe_enr或dly_ok驱动本条件块;刚进入此状态时,由exe_enr驱动;之后由dly_ok驱动
exe_enr <= NO; //进入条件块后清除exe_enr,使得只有延时时刻到达时才进入下一子状态
case(step)
0 : begin
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01011; //行激活
bank_ro <= addr_in[1:0]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
addr_ro <= addr_in[23:11];
dly_cnt <= 2; //用于延时块 tRCD=2
dly_en <= YES; //用于延时块
nop_yes <= YES;
step <= 1;
end
1 : begin
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01101;
addr_ro <= addr_in[10:2]; //将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
addr_ro[10] <= HI; //自动预充 CL=2
dly_cnt <= 2; //用于延时块
dly_en <= YES; //用于延时块
nop_yes <= YES;
step <= 2;
end
2 : begin //清除操作
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
cur_stat <= IDLE; //本状态任务完毕,转到IDLE状态d_
rd_ok <= HI; //=1标志数据已送到总线上,脉宽一个clk周期
end
default : ;
endcase
end
else begin
dly_en <= NO;
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111;
end
end//RD_SG
default : cur_stat <= INIT;
endcase
end//else
end//always
//延时进程块
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
dly_en1 <= NO;
dly_cnt1 <= 1; //初始化要赋以非零值,防止dly_ok拉高
end
else if(dly_en == YES) begin
dly_en1 <= YES;
dly_cnt1 <= dly_cnt - 2; //延时周期数修正
end
else if(dly_en1 == YES) begin
if(dly_cnt1 == 0) begin
dly_en1 <= NO;
dly_cnt1 <= 1; //随便赋以非零值,保证dly_ok脉宽为一个clk周期
end
else begin
dly_cnt1 <= dly_cnt1 - 1;
end
end
else ; //空操作
end//always
assign dly_ok = (dly_cnt1 == 0)?YES : NO;
endmodule
363732023_340721035 2012-10-9 13:50
你太强了
用户1629256 2012-9-5 14:32
wxg1988 2012-9-4 15:26