|
看看V1版,实在太凌乱,还是先把风格整理一下,再升级功能吧。相对于V1版,本次V2.3版采用两段式状态机,同时做到同步输出,占用资源大大减小,可读性也大幅提升。另有V2.2版,区别于V2.3就是采用独热编码,其他一样。把自己写的代码贴到这里实在是献丑了,主要是希望得到大家的点评和建议
/**
******************************************************************************
* @file sdram_v2_3.v
* @author 西殿源
* @version V2.3
* @date 09/08/2012 最后修改
* @brief SDRAM控制器
* @info 1. 本版本实现SDRAM最基本功能
2. 将bank1和bank0 接到地址总线最底两位,地址线错两位接到地址总线的高位
3. 顺序寻址,避开页错失的情况,tRCD + CL
4. 不考虑突发读写
5. 不考虑页直接命中的情况
6. 状态机两段式+同步输出
7. 砍掉POWER_ON,
******************************************************************************
*/
module sdram_v2_3(
//-------------------时钟复位-------------------------------------//
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_ro //SDRAM Mask
);
parameter YES = 1'b1;
parameter NO = 1'b0;
parameter HI = 1'b1;
parameter LO = 1'b0;
parameter MODEL_SET = 5'd0; //模式寄存器设置
parameter AUTO_FREH = 5'd1; //自动刷新
parameter SELF_ENT = 5'd2; //进入自刷新,就是刷新操作不在依靠clk
parameter SELF_EXT = 5'd3; //推出自刷新,就是刷新操作不在依靠clk
parameter ROW_ACTV = 5'd4; //行激活,行地址
parameter COLUM_RD = 5'd5; //列激活,列地址
parameter COLUM_WR = 5'd6; //列激活,写数据
parameter BST_STOP = 5'd7; //突发停
parameter PRE_CHAG_A = 5'd8; //所有bank预充电
parameter PRE_CHAG_S = 5'd9; //选中bank预充电
parameter SSPD_ENT = 5'd10; //进入挂起
parameter SSPD_EXT = 5'd11; //退出挂起
parameter POWR_ENT = 5'd12; //进入
parameter POWR_EXT = 5'd13; //退出
parameter DQM = 5'd14; //数据掩码
parameter NOP_OPTN = 5'd15; //空操作
parameter WR_OK = 5'd16; //写入成功
parameter RD_OK = 5'd17; //读出成功
parameter POWER_ON = 5'd18; //上电初始化
parameter CHK_BEGN = 5'd19; //上电初始化
reg wr_ok_temp; //=1写数据成功,用于组合逻辑,
reg rd_ok_temp; //=1读数据成功,用于组合逻辑,
reg cs_temp; //SDRAM片选,用于组合逻辑,
reg cke_temp; //SDRAM时钟使能,用于组合逻辑,
reg[12:0] addr_temp; //SDRAM地址,用于组合逻辑,
reg[1:0] bank_temp; //SDRAM块选,用于组合逻辑,
reg ras_temp; //SDRAM行激活,用于组合逻辑,
reg cas_temp; //SDRAM列激活,用于组合逻辑,
reg we_temp; //SDRAMD读写信号,用于组合逻辑,
reg[1:0] dqm_temp; //SDRAM Mask
reg chk_en; //=1时,处于检测模式,=0时处于状态跳转模式;用于时序逻辑
reg chk_en_temp; //作为chk_en的赋值,用于组合逻辑
reg[15:0] dly_cnt; //延时次数装载寄存器,例如某操作延时2个clk再操作,可以赋以2
reg[15:0] dly_cnt_temp; //作为dly_cnt的赋值,用于组合逻辑
reg[15:0] dly_cnt1; //延时计数器,时序逻辑,用于延时always块
reg dly_en; //=1时使能延时定时器,与dly_cnt配合使用,有效宽度为一个clk周期
reg dly_en_temp; //作为dly_en的赋值,用于组合逻辑
reg dly_en1; //延时计数使能,与dly_cnt1配合使用,
reg dly_ok; //延时到时,=1,脉宽为一个clk周期
// reg[4:0] stat_temp;
reg stat_cnt_en; //状态重复执行使能,用于时序逻辑
reg stat_cnt_en_temp; //状态重复执行使能,用于组合逻辑,作为stat_cnt_en的赋值
reg[3:0] stat_cnt; //状态重复次数计数器
// reg[4:0] nxt_stat_clk; //??ò???×′ì?£???±eóú??ò???2ù×÷ ,ó?óúê±Dò???-
reg[4:0] nxt_stat; //下一个状态,组合逻辑状态
reg[4:0] nxt_opt; //下一个操作,例如无论读写,在行选通之后要紧随列的读选通或写选通,将此值付给nxt_stat
reg[4:0] cur_stat; //当前状态,时序逻辑
// reg[15:0] nop_cnt; //??2ù×÷??êy
reg[23:0] addr_r; //地址锁存,
reg init_en;
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
addr_r <= 0;
wr_ok <= NO;
rd_ok <= NO;
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b00000;
addr_ro <= 0;
bank_ro <= 0;
dqm_ro <= 0;
init_en <= NO;
dly_cnt <= 10; //上电后稳定200us 定位10000;
dly_en <= YES;
stat_cnt_en <= NO;
chk_en <= NO;
cur_stat <= PRE_CHAG_A;
nxt_opt <= PRE_CHAG_A;
end
else if(chk_en) begin //chk_en=1时,每个clk上升沿检测读写请求位
case({wr,rd})
2'b01 : begin
addr_r <= addr_in; //锁存地址
cur_stat <= ROW_ACTV; //跳转到行激活状态
nxt_opt <= COLUM_WR; //行激活状态完毕后跳转到列写激活状态
chk_en <= NO; //关闭读写检测,切入状态跳转模式。下一个clk就切入
init_en <= YES; //触发状态跳转,就在下一个clk
end
2'b10 : begin
addr_r <= addr_in; //锁存地址
cur_stat <= ROW_ACTV; //跳转到行激活状态
nxt_opt <= COLUM_RD; //行激活状态完毕后跳转到列读激活状态
chk_en <= NO; //关闭读写检测,切入状态跳转模式。下一个clk就切入
init_en <= YES;
end
default : ;
endcase
end
else begin //chk_en=0,状态跳转模式
if((init_en == YES)||(dly_ok == YES)) begin //,或延时时间到执行状态跳转
init_en <= NO; //进来后关掉init_en,保证下一次状态跳转是在延时到
cur_stat <= nxt_stat; //状态切换
wr_ok <= wr_ok_temp; //同步输出
rd_ok <= rd_ok_temp; //同步输出
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= {cs_temp,cke_temp,ras_temp,cas_temp,we_temp};
addr_ro <= addr_temp; //同步输出
bank_ro <= bank_temp; //同步输出
dqm_ro <= dqm_temp; //同步输出
stat_cnt_en <= stat_cnt_en_temp; //同步输出
dly_en <= dly_en_temp; //同步输出
dly_cnt <= dly_cnt_temp; //同步输出
chk_en <= chk_en_temp; //同步输出
end
else begin //不满足(init_en == YES)||(dly_ok == YES),执行空操作
{cs_ro,cke_ro,ras_ro,cas_ro,we_ro} <= 5'b01111; //空操作
wr_ok <= NO; //清除写成功标志,保证wr_ro脉宽为一个clk周期
rd_ok <= NO;
dly_en <= NO; //清除写成功标志,保证dly_en脉宽为一个clk周期
end
end
end
always @(cur_stat,stat_cnt) begin
case(cur_stat)
// POWER_ON : begin
// {cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111; //??2ù×÷
// addr_temp = 0; //3?ê??μ
// bank_temp = 0; //3?ê??μ
// dqm_temp = 0; //3?ê??μ
//
// stat_cnt_en_temp = NO; //±?2ù×÷ 2? Dèòa???′?′DD
//// stat_temp = POWER_ON;
//
// wr_ok_temp = NO; //3?ê??μ
// rd_ok_temp = NO; //3?ê??μ
//
// chk_en_temp = NO; //?′DD±?2ù×÷oó2??êDí?ì2a?áD′???ó
//
// dly_cnt_temp = 10; //é?μ??è?¨200us
// dly_en_temp = YES; //′ò?a?¨ê±?÷
//
// nxt_stat = PRE_CHAG_A; //??2ù×÷íêá???oóê??¤3?μ?2ù×÷£?1ê3???ò???2ù×÷?£2ù×÷ó?2ù×÷????£?ò???2ù×÷?a??×′ì?×÷?a1y?é
// end
//
//
PRE_CHAG_A : begin //预充电
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01010; //预充电
addr_temp = 13'b0010000000000; //A[10]=1
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = PRE_CHAG_A; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO; //
rd_ok_temp = NO; //
chk_en_temp = NO; //
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = AUTO_FREH; //??2ù×÷íêá???oóê?×??ˉ?¢D?2ù×÷£?1ê3???ò???2ù×÷?£2ù×÷ó?2ù×÷????£?ò???2ù×÷?a??×′ì?×÷?a1y?é
end
AUTO_FREH : begin //8???¢D??ü?ú
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01001; //8???¢D??ü?ú
addr_temp = 0; //
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = YES; //本状态 需要 重复执行
// stat_temp = AUTO_FREH; //±?2ù×÷Dèòa???′?′DD
wr_ok_temp = NO;
rd_ok_temp = NO;
chk_en_temp = NO;
dly_cnt_temp = 9; //延时9个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
if(stat_cnt == 7) nxt_stat = MODEL_SET;
else nxt_stat = AUTO_FREH;
end
MODEL_SET : begin //模式寄存器设置
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01000; //模式寄存器设置
addr_temp = {
3'b000,
1'b1,
2'b00,
3'b010,
1'b0,
3'b000
};
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = MODEL_SET; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO; //
rd_ok_temp = NO; //
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = CHK_BEGN;
end
ROW_ACTV : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01011; //行激活
addr_temp = addr_r[23:11];
bank_temp = addr_r[1:0]; //??bank1oíbank0 ?óμ?μ??·×ü??×?μ×á???£?μ??·??′íá????óμ?μ??·×ü??μ?????
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = ROW_ACTV; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO;
rd_ok_temp = NO;
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = nxt_opt;
end
COLUM_WR : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01100; //列写激活
addr_temp = {4'b0010,addr_r[10:2]}; //A[10]=1 列地址
bank_temp = addr_r[1:0]; //模块内部的地址总线最地两位接到bank上
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = COLUM_WR; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO; //
rd_ok_temp = NO; //
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = WR_OK;
end
WR_OK : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111; //发出置位写成功标志
addr_temp = 0; //
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = WR_OK; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = YES; //时钟到来时就更新到wr_ok,置位写成功,随后的clk再清除wr_ok。
rd_ok_temp = NO;
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = CHK_BEGN;
end
COLUM_RD : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01101; //áD?¤??£??áêy?Y
addr_temp = {4'b0010,addr_r[10:2]}; //??bank1oíbank0 ?óμ?μ??·×ü??×?μ×á???£?μ??·??′íá????óμ?μ??·×ü??μ?????
bank_temp = addr_r[1:0];
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = COLUM_RD; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO;
rd_ok_temp = NO;
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = RD_OK;
end
RD_OK : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111; //áD?¤??£??áêy?Y
addr_temp = 0; //
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = RD_OK; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO;
rd_ok_temp = YES; //时钟到来时就更新到rd_ok,置位写成功,随后的clk再清除rd_ok。
chk_en_temp = NO;
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = YES; //时钟到来时更新到dly_en,dly_en=1 启动定时计数器,随后的一个clk使dly_en=0
nxt_stat = CHK_BEGN;
end
CHK_BEGN : begin
{cs_temp,cke_temp,ras_temp,cas_temp,we_temp} = 5'b01111; //áD?¤??£??áêy?Y
addr_temp = 0; //
bank_temp = 0; //
dqm_temp = 0; //
stat_cnt_en_temp = NO; //本状态 不 需要重复执行
// stat_temp = CHK_BEGN; //ó?±?2ù×÷?T1???±??3?μ£?ò??aunsafe behavior?ˉ??
wr_ok_temp = NO;
rd_ok_temp = NO;
chk_en_temp = YES; //时钟到来时更新到chk_en,准备转到读写请求检测模式
dly_cnt_temp = 2; //延时2个clk周期再跳转到下一个操作状态
dly_en_temp = NO; //这个打开或关闭都可以
nxt_stat = CHK_BEGN;
end
endcase
end
//??Dèòa2ù×÷???′μ?×′ì???DD??êy
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
stat_cnt <= 0;
end
else if(stat_cnt_en == YES) begin
if(dly_cnt1 == 1) begin
stat_cnt <= stat_cnt + 1;
end
else ;
end
else stat_cnt <= 0;
end
//?óê±??3ì?é
always @(posedge clk,negedge rst_n) begin
if(!rst_n) begin
dly_en1 <= NO;
dly_ok <= NO;
dly_cnt1 <= 0;
end
else if(dly_en == YES) begin
if((dly_cnt - 2)==0) begin
dly_ok <= YES;
dly_en1 <= NO;
end
else begin
dly_cnt1 <= dly_cnt - 3; //?óê±?ü?úêyDT?y
dly_en1 <= YES;
end
end
else if(dly_en1 == YES) begin
if(dly_cnt1 == 0) begin
dly_en1 <= NO;
dly_ok <= YES;
end
else begin
dly_cnt1 <= dly_cnt1 - 1;
end
end
else dly_ok <= NO; //??2ù×÷
end//always
endmodule