"); //-->
至简设计法纯逻辑实现SDARM控制器
1、设计目的:本项目展示如何用“至简设计法”设计SDARM,具体功能要求如下。
1)读写仲裁机制是:当如果同时出现读写请求时,如果上次执行了读操作,则此次执行写操作;如果上一次执行了写操作,则此次执行读操作。如果不是同时出现读写请求,则是什么请求就执行什么操作。
2)采用全页模式的读写操作,该模式在读、写完成时,需要给出预充电命令才能结束。
3)刷新请求始终优于读、写请求。
DDR的时序与SDRAM是相似的,学好SDRAM后,理解DDR2和DDR3就非常容易了。
2、至简设计代码实现(附录部分代码)
下面是使用至简设计法实现的SDRAM控制器,该控制器使用了四段式状态机,其他信号根据状态机对齐而设计,结构相当清晰,无论是设计还是调试,都非常容易,相信有一定基础的工程师,能感觉到这样设计的精简、奇妙之处,欢迎借鉴、学习。
网络上亦有相当多的SDRAM实现代码,欢迎您拿来与明德扬对比,我们对自身的代码设计就是有这样的自信。
//四段式状态机
//第一段:同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDL;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑always模块,描述状态转移条件判断
//只需要思考转移到哪里去
always@(*)begin
case(state_c)
NOP:begin
if(nop2pre_start)begin
state_n = PRE;
end
else begin
state_n = state_c;
end
end
PRE:begin
if(pre2aut_start)begin
state_n = AUT;
end
else if(pre2idl_start)begin
state_n = IDL;
end
else begin
state_n = state_c;
end
end
AUT:begin
if(aut2aut_start)begin
state_n = AUT;
end
else if(aut2lmd_start)begin
state_n = LMD;
end
else if(aut2idl_start)begin
state_n = IDL;
end
end
LMD:begin
if(lmd2idl_start)begin
state_n = IDL;
end
else begin
state_n = state_c;
end
end
IDL:begin
if(idl2act_start)begin
state_n = ACT;
end
else if(idl2aut_start)begin
state_n = AUT;
end
else begin
state_n = state_c;
end
end
ACT:begin
if(act2red_start)begin
state_n = RED;
end
else if(act2wrt_start)begin
state_n = WRT;
end
else begin
state_n = state_c;
end
end
RED:begin
if(red2pre_start)begin
state_n = PRE;
end
else begin
state_n = state_c;
end
end
WRT:begin
if(wrt2pre_start)begin
state_n = PRE;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = IDL;
end
endcase
end
//第三段:设计转移条件
//相同现态的要放在一起,条件互斥,方便比较
assign nop2pre_start = state_c==NOP && end_cnt;
assign pre2aut_start = state_c==PRE && init_flag==1 && end_cnt;
assign pre2idl_start = state_c==PRE && init_flag==0 && end_cnt;
assign aut2aut_start = state_c==AUT && init_flag==1 && init_auto_flag==1 && end_cnt;
assign aut2lmd_start = state_c==AUT && init_flag==1 && init_auto_flag==0 && end_cnt;
assign aut2idl_start = state_c==AUT && init_flag==0 && end_cnt;
assign lmd2idl_start = state_c==LMD && end_cnt;
assign idl2act_start = state_c==IDL && ref_req==0&& (wr_req||rd_req);
assign idl2aut_start = state_c==IDL && ref_req ==1 ;
assign act2red_start = state_c==ACT && rd_flag==1 && end_cnt;
assign act2wrt_start = state_c==ACT && rd_flag==0 && end_cnt;
assign red2pre_start = state_c==RED && end_cnt;
assign wrt2pre_start = state_c==WRT && end_cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
init_flag <= 1;
end
else if(lmd2idl_start)begin
init_flag <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
init_auto_flag <= 1;
end
else if(aut2aut_start)begin
init_auto_flag <= 0;
end
end
//刷新请求
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
ref_req <= 0;
end
else if(end_cnt_self)begin
ref_req <= 1;
end
else if(idl2aut_start)begin
ref_req <= 0;
end
end
//写响应
assign wr_ack = idl2act_start==1&& (wr_req==1&&((rd_flag==1)||(rd_flag==0&&rd_req==0)));
//读响应
assign rd_ack = idl2act_start==1&& (rd_req==1&&((rd_flag==0)||(rd_flag==1&&wr_req==0)));
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rd_flag <= 0;
end
else if(rd_flag==0&&rd_ack==1)begin
rd_flag <= 1;
end
else if(rd_flag==1&&wr_ack==1)begin
rd_flag <= 0;
end
end
//刷新时间计算
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_self <= 0;
end
else if(add_cnt_self)begin
if(end_cnt_self)
cnt_self <= 0;
else
cnt_self <= cnt_self + 1;
end
end
assign add_cnt_self = init_flag==0;
assign end_cnt_self = add_cnt_self && cnt_self==1562-1 ;
//命令时间计数,此处使用了变量法,并且复用了一个计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = state_c!=IDL;
assign end_cnt = add_cnt && cnt==x-1 ;
//各个命令所需要的时间,在下面列出来就好了
always @(*)begin
if(state_c==NOP)begin
x = INITIATE;
end
else if(state_c==PRE)begin
x = TRP;
end
else if(state_c==AUT)begin
x = TRC;
end
else if(state_c==LMD)begin
x = TMRD;
end
else if(state_c==ACT)begin
x = TRCD ;
end
else begin
x = 256 ;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cke <= 1;
end
else begin
cke <= 1;
end
end
assign command = {cs,ras,cas,we};
//下面是产生命令的代码,由于状态机结构简单,要产生COMMAND是非常方便的
//仅从名字下就可以看出代码是否正确
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
command <= 0;
end
else if(nop2pre_start||wrt2pre_start||red2pre_start)begin
command <= precharge;
end
else if(pre2aut_start||aut2aut_start||idl2aut_start)begin
command <= autorefresh;
end
else if(aut2lmd_start)begin
command <= loadmode;
end
else if(idl2act_start)begin
command <= active;
end
else if(act2red_start)begin
command <= read;
end
else if(act2wrt_start)begin
command <= write;
end
else begin
command <= nop;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dqm <= 0;
end
else if(init_flag==1)begin
dqm <= 2'b11;
end
else begin
dqm <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dq_out <= 0;
end
else begin
dq_out <= wdata;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dq_out_en <= 0;
end
else if(act2wrt_start)begin
dq_out_en <= 1;
end
else if(wrt2pre_start)begin
dq_out_en <= 0;
end
end
always @(posedge clk or negedge rst_n)begin //256拍
if(rst_n==1'b0)begin
rdata_flag <= 0;
end
else if(act2red_start)begin
rdata_flag <= 1;
end
else if(red2pre_start)begin
rdata_flag <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rdata_flag_ff0 <= 0;
rdata_flag_ff1 <= 0;
rdata_flag_ff2 <= 0;
end
else begin
rdata_flag_ff0 <= rdata_flag;
rdata_flag_ff1 <= rdata_flag_ff0;
rdata_flag_ff2 <= rdata_flag_ff1;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rdata <= 0;
end
else begin
rdata <= dq_in;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rdata_vld <= 0;
end
else begin
rdata_vld <= rd_flag_ff2;
end
end
always @(posedge clk or negedge rst_n)begin //锁存地址waddr;
if(rst_n==1'b0)begin
waddr_ff0 <= 0;
end
else if(idl2act_start)begin
waddr_ff0 <= waddr;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
addr <= 0;
end
else if(nop2pre_start)begin
addr <=12'b0100_0000_0000;
end
else if(aut2lmd_start)begin
addr <= 12'b00_1_00_010_0_111;//latency Mode 为010 的情况
end
else if(idl2act_start)begin
addr[7:0] <= waddr[19:8]; //行地址
end
else if(act2wrt_start||act2red_start)begin
addr[7:0] <= waddr_ff0[7:0]; //列地址
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
bank <=0;
end
else if(nop2pre_start)begin
bank <= 2'b11;
end
else if(idl2act_start)begin
bank <= waddr[21:20];
end
else if(act2red_start||act2wrt_start)begin
bank < waddr_ff0[21:20];
end
else begin
bank <= 0;
end
end
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。