之前设计完成了SDRAM的各个命令输入和状态的设计,这一部分开始将其进行一个整合,根据命令和需要进行状态的选择和变换。
整个流程的设计是:主状态机—task任务线性序列机–状态执行,即根据命令来切换task。步骤是:上电–执行初始化命令–三态根据命令转换(状态的优先级为刷新》》写操作》》读操作,当读操作时默认!刷新!写操作)
//主状态机,优先级:刷新》写》读
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)begin
main_state <= IDLE;
FF <= 1'b1; //刷新操作、写操作和读操作 3 种不同的任务的执行是通过标志位 FF 来控制的,只有在标志位为 0 时,指定的操作任务才能执行,执行完后将 FF 置 1 退出任务。
end
else begin
case(main_state)
IDLE:begin //空闲
Command <= init_cmd; //SDRAM初始化模块进行命令输出
Sa <= init_addr; //SDRAM初始化地址输出
if(init_done) // init_done SDRAM初始化完成标志位
main_state <= AREF;
else
main_state <= IDLE;
end
AREF:begin //自动刷新
if(FF == 1'b0)
auto_ref; //自动刷新任务task
else begin
if(ref_req)begin //ref_req刷新请求操作时
main_state <= AREF;
FF <= 1'b0;
end
else if(wr_req)begin //读请求,读状态
main_state <= WRITE;
FF <= 1'b0;
end
else if(rd_req)begin //写请求,写状态
main_state <= READ;
FF <= 1'b0;
end
else
main_state <= AREF;
end
end
WRITE:begin
if(FF == 1'b0)
write_data; //write_data task
else begin
if(ref_req == 1'b1)begin //ref_req刷新请求操作时
main_state <= AREF;
FF <= 1'b0;
end
else if(wr_opt_done & wr_req)begin // 写完成 & 写请求
main_state <= WRITE;
FF <= 1'b0;
end
else if(wr_opt_done & rd_req)begin //写完成 &!写请求&读请求(因为优先级没有写请求已经是前提不用写)
main_state <= READ;
FF <= 1'b0;
end
else if(wr_opt_done&!wr_req&!rd_req) //(写完成 & !写请求 & !读请求)完成写后没指令就进入自动刷新等待
main_state <= AREF;
else
main_state <= WRITE;
end
end
READ:begin
if(FF == 1'b0)
read_data; //read_data task
else begin
if(ref_req == 1'b1)begin
main_state <= AREF;
FF <= 1'b0;
end
else if(rd_opt_done & wr_req)begin
main_state <= WRITE;
FF <= 1'b0;
end
else if(rd_opt_done & rd_req)begin
main_state <= READ;
FF <= 1'b0;
end
else if( rd_opt_done&!wr_req&!rd_req)
main_state <= AREF;
else
main_state <= READ;
end
end
endcase
end
end
一、刷新状态自动刷新
长时间没有命令要自动刷新保持上电,因此设计时序的自动刷新计数器。
//刷新定时计数器,每隔一段时间进行自动刷新,保持数据
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
ref_time_cnt <= 0;
else if(ref_time_cnt == AUTO_REF) //到达开发板上使用的 SDRAM 芯片64Ms刷新4096次时间
ref_time_cnt <= 1;
else if(init_done || ref_time_cnt > 0) //SDRAM初始化完成或者已经开始刷新计数定时
ref_time_cnt <= ref_time_cnt + 10'd1;
else
ref_time_cnt <= ref_time_cnt;
end
//刷新定时时间标志位,定时到达时置1
assign ref_time_flag = (ref_time_cnt == AUTO_REF); //循环计数,自动隔段刷新
二、自动刷新请求信号的产生(读写请求同理)
//刷新请求信号
always@(*)
begin
case(main_state)
AREF:begin //刷新状态时
if(ref_time_flag) //刷新指令,刷新
ref_req = 1'b1;
else
ref_req = 1'b0;
end
WRITE: //写状态时
begin
if(ref_break_wr && wr_opt_done) //写状态收到刷新指令且写状态完成
ref_req = 1'b1;
else
ref_req = 1'b0;
end
READ:
begin
if(ref_break_rd && rd_opt_done)
ref_req = 1'b1;
else
ref_req = 1'b0;
end
default:
ref_req = 1'b0;
endcase
end
三、命令的保持
此处主要有两个点:
1.当SDRAM进行一个操作时,到来新的命令,将新命令进行寄存,待当前操作完成后执行
2.读写操作寄存器,在读写时对地址进行寄存防止操作过程中地址变化,处理方法也是寄存后执行
//写操作时接收到刷新信号,记住此信号为ref_break_wr
assign ref_break_wr = (ref_time_flag && wr_opt)?1'b1:((!wr_opt)?1'b0:ref_break_wr);
// 刷新定时时间标志位且写操作中拉高;没有在写状态中拉低;否则维持原状态
读写操作寄存器,在读写时对地址进行寄存防止操作过程中地址变化
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
begin
raddr_r <= 0;
caddr_r <= 0;
baddr_r <= 0;
end
else if(rd_req || wr_req) //读写请求中
begin
raddr_r <= Raddr; //Raddr_r是输入SDRAM的行地址,raddr_r将其寄存
caddr_r <= Caddr; //读写列地址
baddr_r <= Baddr; //读写bank地址
end
else
;
end
四、主状态机仿真
主状态机已经设计完毕,仍然带入镁光SDRAM模型进行仿真
//SDRAM控制器模块例化
initial Clk = 1'b1;
always #(`clk100m_period/2) Clk = ~Clk;
initial
begin
Rst_n = 0;
Wr = 0;
Rd = 0;
Caddr = 0;
Raddr = 0;
Baddr = 0;
Wr_data = 0;
#(`clk100m_period*200+1);
Rst_n = 1;
@(posedge sdram_control.sdram_init.init_done) //关注控制模块中的初始化完成信号
#2000; //等待2000ns,初始化完成
repeat(100) //写入100组数据
begin
Wr = 1;
Baddr = 2;
#`clk100m_period;
Wr = 0;
/*****这一块的读行列的手法学习******/
if(Caddr == 512-SC_BL)begin //因为时序,512列减列突发长度后重新开始,其实整个512列已经读完了
Caddr = 0;
Raddr = Raddr + 1; //1行的所有列写满,行加1
end
else
Caddr = Caddr + SC_BL;
#5000; //延时5us
end
Caddr = 0;
Raddr = 0;
#5000;
repeat(100) //读出100组数据
begin
Rd = 1'b1;
#`clk100m_period;
Rd = 1'b0;
if(Caddr == 512-SC_BL)begin
Caddr = 0;
Raddr = Raddr + 1; //1行读完,行加1
end
else
Caddr = Caddr + SC_BL;
#5000; //延时5us
end
#5000;
$stop;
end
initial //两个initial是并行的,即每写入一个数据,被写入的数据加一
begin
forever begin
@(posedge Wr_data_vaild);
repeat(SC_BL) //改变待写入的数据
begin
#`clk100m_period;
Wr_data = Wr_data + 1;
end
end
end
仿真结果:
镁光模型进行信息打印
可以看到数据可以被写入,然后读出