FPGA之SDRAM控制器设计(三):写
由于已经涉及了上电刷新,写三个大的状态转移,先把状态转移图给出。主控状态转移图是基于手册上描述来的。在代码注释中会给出每个状态的意义解释。
写时序图
写状态转移图:
主控设计(含读模块):
`include "head.v"
module mainref_fsm(
init_done,ref_done,clk,soft_rst_n,wr_en,rd_en,init_en,ref_en,rt_en,
rt_flag,sel_mux,local_wdata,local_rdata,local_write,local_read,local_addr,
local_ready,local_rddatavalid,wr_done,rd_done,row,col,ba,wdata,rdata
);//刷新的主控状态机,TOP
input init_done;
input ref_done;
input clk;
input soft_rst_n;
input rt_flag;
output reg init_en;
output reg ref_en;
output reg rt_en;
output reg [1:0] sel_mux;
output reg wr_en;
output reg rd_en;
input [31:0] local_wdata;
output reg [31:0] local_rdata;
input local_write;
input local_read;
input [24:0] local_addr;
output reg local_ready;
output reg local_rddatavalid;
input wr_done,rd_done;
output reg [12:0] row;
output reg [9:0] col;
output reg [1:0] ba;
output reg [31:0] wdata;
input [31:0] rdata;
reg [2:0] state;
localparam s0 = 3'b000;
localparam s1 = 3'b001;
localparam s2 = 3'b010;
localparam s3 = 3'b011;
localparam s4 = 3'b100;
localparam s51 = 3'b101;
localparam s52 = 3'b110;
always @(posedge clk)begin
if(soft_rst_n == 1'b0)
begin
ref_en <= 1'b0;
rt_en <= 1'b0;
init_en <= 1'b0;
sel_mux <= `MUX_INIT;
rd_en <= 1'b0;
wr_en <= 1'b0;
wdata <= 'd0;
col <= 'd0;
row <= 'd0;
ba <= 2'b00;
local_ready <= 1'b0;
local_rddatavalid <= 1'b0;
local_rdata <= 'd0;
state <= s0;
end
else
case(state)
s0 : begin
init_en <= 1'b1;
state <= s1;
end //初始化,发出初始化使能信号
s1 : if(init_done == 1'b0)
begin
init_en <= 1'b0;
state <= s1;
end //对应s0和s1的这部分对应ini_fsm.v模块
else if(init_done == 1'b1)
begin
//ref_en <= 1'b1;
rt_en <= 1'b1;
local_ready <= 1'b1;
sel_mux <= `MUX_REF;
state <= s2;
end //上电初始化完成,
s2 : begin
ref_en <= 1'b1;
state <= s3;
end
s3 : if(ref_done == 1'b0)
begin
ref_en <= 1'b0;
state <= s3;
end //s2,s3对应ref_fsm刷新状态机模块
else
state <=s4;
s4 : if(rt_flag == 1'b0)
begin //只有在7810ns时间内才执行读写操作
if(local_write == 1'b0 && local_read == 1'b0)
state <= s4;
else if(local_write == 1'b1)
begin
wr_en <= 1'b1;
row <= local_addr[22:10];
col <= local_addr[9:0];
ba <= local_addr[24:23];
local_ready <= 1'b0;
wdata <= local_wdata;
sel_mux <= `MUX_WR;
state <= s51;
end
else if(local_read == 1'b1)
begin
rd_en <= 1'b1;
row <= local_addr[22:10];
col <= local_addr[9:0];
ba <= local_addr[24:23];
local_ready <= 1'b0;
local_rddatavalid <= 1'b0;
sel_mux <= `MUX_RD;
state <= s52;
end
end
else //rt_flag == 1 ————> 这一次7810ns时间到了,要刷新:拉高ref_en,转到刷新状态机,进行几次操作。
begin
ref_en <= 1'b1;
state <= s3;
end
s51 : if(wr_done == 1'b0)
begin
wr_en <= 1'b0;
state <= s51;
end
else
begin
sel_mux <= `MUX_REF;
local_ready <= 1'b1;
state <= s4;
end
s52 : if(rd_done == 1'b0)
begin
rd_en <= 1'b0;
state <= s52;
end
else
begin
sel_mux <= `MUX_REF;
local_ready <= 1'b1;
local_rddatavalid <= 1'b1;
local_rdata <= rdata;
state <= s4;
end
endcase
end
endmodule
写模块:
`include "head.v"
module write(
clk,soft_rst_n,wr_en,wr_done,wdata,
wr_bus,row,col,ba,dq_out,out_en
);//不需要中断的写模式:在输入列线时将a[10]拉高,自动中断
input clk;
input soft_rst_n;
input wr_en;
input [31:0] wdata;
input [12:0] row;
input [9:0] col;
input [1:0] ba;
output [19:0] wr_bus;
output reg wr_done;
output reg [15:0] dq_out;
output reg out_en;
reg [12:0] wr_a;
reg [1:0] wr_ba;
reg [3:0] wr_cmd;
reg [3:0] cnt;
reg wr_cke;
reg [2:0] state;
localparam s0 = 3'b000;
localparam s1 = 3'b001;
localparam s2 = 3'b010;
localparam s3 = 3'b011;
localparam s4 = 3'b100;
assign wr_bus = {wr_cmd,wr_a,wr_ba,wr_cke};
always @(posedge clk)
if(soft_rst_n == 1'b0)
begin
wr_done <= 1'b0;
wr_a <= 'd0;
wr_ba <= 'd0;
cnt <= 'd0;
wr_cke <= 1'b1;
dq_out <= 'd0;
out_en <= 1'b0;
wr_cmd <= `NOP;
state <= s0;
end
else case(state)
s0 : if(wr_en == 1'b0)
state <= s0;
else
begin
wr_cmd <= `ACT;
wr_a <= row;//加载行
wr_ba <= ba;//选择bank:0,1,2,3其中一个
wr_done <= 1'b0;
state <= s1;
end
s1 : if(cnt < `tWCD - 1)
begin
cnt <= cnt + 1'b1;
wr_cmd <= `NOP;
state <= s1;
end
else
begin
wr_cmd <= `WR;
wr_a[9:0] <= col;//输入列地址
wr_a[10] <= 1'b1;//不用外部输入中断信号,写完一个突发长度自己断掉
dq_out <= wdata[15:0];//数据线分两次输入到SDRAM中,第一次低16位
out_en <= 1'b1;
cnt <= 'd0;
state <= s2;
end
s2 : begin
wr_cmd <= `NOP;
dq_out <= wdata[31:16];//输入高16位数据
state <= s3;
end
s3 : begin
wr_done <= 1'b1;//一次写的过程完成
out_en <= 1'b0;
state <= s4;
end
s4 : begin
wr_done <= 1'b0;
state <= s0;
end
endcase
endmodule
仿真报告: