基于FPGA的SDRAM控制器设计(3)
基于FPGA的SDRAM读写模块设计
SDRAM工作状态转移图
SDRAM芯片是按照一定的状态时序操作的,所以我们为了更好的写代码需要清楚SDRAM的状态转移图,如下:
其中粗线是执行完该状态后,不需要我们给什么命令,自动转移到下一个状态,但是细线需要我们给出相应的命令。
这里引用Kevin老师对其中一个过程的描述,从“IDLE”状态跳到“WRITE”状态的路线以及从“WRITE”状态跳到“IDLE”状态的路线:
IDLE 状态到 WRITE 状态:
- 在 IDLE 状态需要先给 ACT 命令**某一行, 此时处于 Row Active 状态;
- 在 Row Active 状态之后, 给 Write 命令则会进入 WRITE 状态;
- 在 WRITE 状态后, 再给一次 Write 命令, 就可以继续写入数据。
WRITE 状态到 IDLE 状态: - 在 WRITE 状态给 PRE 命令, 则 SDRAM 将跳出 WRITE 状态进入 Precharge
状态; - 在 Precharge 状态后, 就会自动进入 IDLE 状态了
要从 WRITE 状态跳到 IDLE 状态的一个原因是, 我们需要进行刷新操作, 进入刷新操作, 必须从 IDLE 状态进入。我们注意到WRITE 状态下边还有一个 WRITEA 状态,执行完一次WRITE状态它会自动的进入到
Precharge 状态,因此比在 WRITE 状态的工作效率要低很多,也比较少使用。
SDRAM写时序图
从SDRAM的技术手册中我们很容易找到对应的写时序图,如下:
上面对应的时间也可以从手册中找到,在上两篇关于SDRAM的文章中我们已经给出了每个时间的最小值。
SDRAM的读时序图
同理,我们给出SDRAM的读时序图,如下:
这里是给出读延迟2个时钟的时序图,注意我们模式寄存器中设置的是三个时钟延迟。同时,我们注意,***SDRAM的状态转移图,读写操作是对称的,读写的时序也是对称的,所以我们在写完写操作之后可以利用notepad++的全部替换功能,替换一下写模块的名称、写操作的命令、删除写模块的数据端口并增加都模块的使能端口,便可以把写模块转换成读模块。***所以接下来我们将以写模块为例进行设计SDRAM的读写模块。
同学们也可以对比上面两个时序图与SDRAM的状态转移图会发现两者完美契合。
SDRAM写模块状态机
假设我们现在需要往 SDRAM 中写入两行数据, 那什么时候可以退出仲裁状
态机的写状态:
- 数据已经写完;
- SDRAM 需要进行刷新操作;
- 数据未写完, 需要**下一行继续写。
为什么按照上面的条件进行设计SDRAM的写模块,因为上面的条件比较齐全,可以将我们所需要考虑的情况考虑的比较完善。
这里也是使用了Kevin的状态机,但是没有按照该老师的时序图设计,因为自我感觉有了状态机再看时序图反倒有些多余。
这里虽然没使用Kevin老师的时序图,这里也给出来,方便大家调试,对这里有问题的同学可以参考该老师的教学视频,但是还是支持大家自我完成该模块的设计。
至此我们便完成了SDRAM的读写理论设计,接下来我们便完成该模块的代码设计。
SDRAM的读写操作代码
SDRAM顶层模块的设计,这里包括仲裁模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_top.v
// Create Time : 2020-02-09 17:22:24
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_top(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output wire sdram_clk ,
output wire sdram_cke ,
output reg sdram_cs_n ,
output reg sdram_cas_n ,
output reg sdram_ras_n ,
output reg sdram_we_n ,
output reg [ 1:0] sdram_bank ,
output reg [11:0] sdram_addr ,
output wire [ 1:0] sdram_dqm ,
inout [15:0] sdram_dq ,
//Others
input wr_trig ,
input rd_trig
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam NOP = 4'b0111 ;
localparam IDLE = 5'b0_0001 ;
localparam ARBIT = 5'b0_0010 ;
localparam AREF = 5'b0_0100 ;
localparam WRITE = 5'b0_1000 ;
localparam READ = 5'b1_0000 ;
//sdram_init
wire [ 3:0] init_cmd ;
wire [11:0] init_addr ;
wire init_done ;
//AREF
wire aref_req ;
reg aref_en ;
wire aref_end ;
wire [ 3:0] aref_cmd ;
wire [11:0] aref_addr ;
//WRITE
wire [ 3:0] wr_cmd ;
wire [11:0] wr_addr ;
wire [ 1:0] wr_bank_addr ;
wire [15:0] wr_data ;
reg wr_en ;
wire wr_end ;
wire wr_req ;
//READ
wire [ 3:0] rd_cmd ;
wire [11:0] rd_addr ;
wire [ 1:0] rd_bank_addr ;
reg rd_en ;
wire rd_end ;
wire rd_req ;
wire rd_data_en ;
//ARBIT
reg [ 4:0] state ;
//Others
reg [15:0] sdram_dq1 ;
reg sdram_dq_en ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign sdram_dqm = 2'b00;
assign sdram_clk = ~sclk;
assign sdram_cke = 1'b1;
assign sdram_dq = sdram_dq_en == 1'b1 ? sdram_dq1 : 16'hzzzz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE : if(init_done == 1'b1)
state <= ARBIT;
else
state <= state;
ARBIT : if(aref_req == 1'b1)
state <= AREF;
else if(wr_req == 1'b1)
state <= WRITE;
else if(rd_req == 1'b1)
state <= READ;
else
state <= state;
AREF : if(aref_end == 1'b1)
state <= ARBIT;
else
state <= state;
WRITE : if(wr_end == 1'b1)
state <= ARBIT;
else
state <= state;
READ : if(rd_end == 1'b1)
state <= ARBIT;
else
state <= state;
default : state <= IDLE;
endcase
always @(*)
case(state)
IDLE : begin
sdram_addr = init_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = init_cmd;
sdram_dq_en = 1'b0;
end
AREF : begin
sdram_addr = aref_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = aref_cmd;
end
WRITE : begin
sdram_addr = wr_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = wr_cmd;
sdram_dq1 = wr_data;
sdram_bank = wr_bank_addr;
sdram_dq_en = 1'b1;
end
READ : begin
sdram_addr = rd_addr;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = rd_cmd;
sdram_bank = rd_bank_addr;
sdram_dq_en = 1'b0;
end
default : begin
sdram_addr = 12'd0;
{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = NOP;
sdram_dq1 = 16'd0;
sdram_bank = 2'b00;
sdram_dq_en = 1'b0;
end
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
aref_en <= 1'b1;
else
aref_en <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
wr_en <= 1'b0;
else if(state == ARBIT && wr_req == 1'b1)
wr_en <= 1'b1;
else
wr_en <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_en <= 1'b0;
else if(state == ARBIT && aref_req == 1'b1)
rd_en <= 1'b0;
else if(state == ARBIT && wr_req == 1'b1)
rd_en <= 1'b0;
else if(state == ARBIT && rd_req == 1'b1)
rd_en <= 1'b1;
else
rd_en <= 1'b0;
sdram_init sdram_init_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.sdram_cmd (init_cmd ),
.sdram_addr (init_addr ),
//Others
.init_done (init_done )
);
sdram_aref sdram_aref_inst(
//Sysytem Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.aref_cmd (aref_cmd ),
.aref_addr (aref_addr ),
//Others
.aref_req (aref_req ),
.aref_end (aref_end ),
.aref_en (aref_en ),
.init_done (init_done )
);
sdram_write sdram_write_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.wr_cmd (wr_cmd ),
.wr_addr (wr_addr ),
.bank_addr (wr_bank_addr ),
.wr_data (wr_data ),
//Communication Interfaces
.wr_trig (wr_trig ),
.wr_en (wr_en ),
.wr_end (wr_end ),
.wr_req (wr_req ),
.aref_req (aref_req )
);
sdram_read sdram_read_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.rd_cmd (rd_cmd ),
.rd_addr (rd_addr ),
.bank_addr (rd_bank_addr ),
//Communication Interfaces
.rd_trig (rd_trig ),
.rd_en (rd_en ),
.rd_end (rd_end ),
.rd_req (rd_req ),
.aref_req (aref_req ),
.rd_data_en (rd_data_en )
);
endmodule
SDRAM上电初始化模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_init.v
// Create Time : 2020-02-09 16:20:31
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_init(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] sdram_cmd ,
output reg [11:0] sdram_addr ,
//Others
output reg init_done
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam DELAY_200US = 20000 ;
//SDRAM Command
localparam NOP = 4'b0111 ;
localparam PRE = 4'b0010 ;
localparam AREF = 4'b0001 ;
localparam MSET = 4'b0000 ;
reg [14:0] cnt_200us ;
reg flag_200us ;
reg [ 4:0] cnt_cmd ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_200us <= 11'd0;
else if(flag_200us == 1'b0)
cnt_200us <= cnt_200us + 1'b1;
else
cnt_200us <= cnt_200us;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_200us <= 1'b0;
else if(cnt_200us >= DELAY_200US)
flag_200us <= 1'b1;
else
flag_200us <= flag_200us;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_cmd <= 5'd0;
else if(flag_200us == 1'b1 && cnt_cmd <= 5'd19)
cnt_cmd <= cnt_cmd + 1'b1;
else
cnt_cmd <= cnt_cmd;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sdram_cmd <= NOP;
else case(cnt_cmd)
1 : sdram_cmd <= PRE;
3 : sdram_cmd <= AREF;
11 : sdram_cmd <= AREF;
19 : sdram_cmd <= MSET;
default : sdram_cmd <= NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sdram_addr <= 12'b0100_0000_0000;
else if(cnt_cmd == 5'd19)
sdram_addr <= 12'b0000_0011_0010;
else
sdram_addr <= 12'b0100_0000_0000;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
init_done <= 1'b0;
else if(cnt_cmd > 5'd19)
init_done <= 1'b1;
else
init_done <= init_done;
endmodule
SDRAM自刷新模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_aref.v
// Create Time : 2020-02-10 13:34:03
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_aref(
//Sysytem Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] aref_cmd ,
output wire [11:0] aref_addr ,
//Others
input init_done ,
output reg aref_req ,
output reg aref_end ,
input aref_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
localparam NOP = 4'b0111 ;
localparam PRE = 4'b0010 ;
localparam AREF = 4'b0001 ;
localparam DELAY_15US = 11'd1500 ;
reg aref_flag ;
reg [ 2:0] cnt_cmd ;
reg [10:0] cnt_15ms ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign aref_addr = 12'b0100_0000_0000;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_flag <= 1'b0;
else if(cnt_cmd >= 3'd7)
aref_flag <= 1'b0;
else if(aref_en == 1'b1)
aref_flag <= 1'b1;
else
aref_flag <= aref_flag;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_cmd <= 3'd0;
else if(cnt_cmd >= 3'd7)
cnt_cmd <= 3'd0;
else if(aref_flag == 1'b1)
cnt_cmd <= cnt_cmd + 1'b1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_cmd <= NOP;
else case(cnt_cmd)
1 : aref_cmd <= PRE;
4 : aref_cmd <= AREF;
default : aref_cmd <= NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_end <= 1'b0;
else if(cnt_cmd >= 3'd7)
aref_end <= 1'b1;
else
aref_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_15ms <= 20'd0;
else if(cnt_15ms == DELAY_15US)
cnt_15ms <= 20'd0;
else if(init_done == 1'b1)
cnt_15ms <= cnt_15ms + 1'b1;
else
cnt_15ms <= 20'd0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
aref_req <= 1'b0;
else if(cnt_15ms == DELAY_15US)
aref_req <= 1'b1;
else if(aref_en == 1'b1)
aref_req <= 1'b0;
else
aref_req <= aref_req;
endmodule
SDRAM写模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_write.v
// Create Time : 2020-02-10 20:05:26
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_write(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] wr_cmd ,
output reg [11:0] wr_addr ,
output wire [ 1:0] bank_addr ,
output reg [15:0] wr_data ,
//Communication Interfaces
input wr_trig ,
input wr_en ,
output reg wr_end ,
output reg wr_req ,
input aref_req
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
// Define State
localparam S_IDLE = 5'b0_0001 ;
localparam S_REQ = 5'b0_0010 ;
localparam S_ACT = 5'b0_0100 ;
localparam S_WR = 5'b0_1000 ;
localparam S_PRE = 5'b1_0000 ;
// SDRAM Command
localparam CMD_NOP = 4'b0111 ;
localparam CMD_PRE = 4'b0010 ;
localparam CMD_AREF = 4'b0001 ;
localparam CMD_ACT = 4'b0011 ;
localparam CMD_WR = 4'b0100 ;
reg [ 4:0] state ;
reg flag_act_end ;
reg row_end ;
reg [ 1:0] burst_cnt ;
reg data_end ;
reg flag_row_end ;
reg flag_data_end ;
reg flag_aref_req ;
reg [ 8:0] col_addr ;
reg [ 1:0] burst_cnt_r ;
reg [11:0] row_addr ;
reg data_end_r ;
reg data_end_r2 ;
reg row_end_r ;
reg row_end_r2 ;
reg flag_s_wr ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign bank_addr = 2'b00;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= S_IDLE;
else case(state)
S_IDLE : if(wr_trig == 1'b1)
state <= S_REQ;
else
state <= state;
S_REQ : if(wr_en == 1'b1)
state <= S_ACT;
else
state <= state;
S_ACT : if(flag_act_end == 1'b1)
state <= S_WR;
else
state <= state;
S_WR : if(data_end_r == 1'b1 || row_end_r == 1'b1)
state <= S_PRE;
else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
state <= S_PRE;
else
state <= state;
S_PRE : if(flag_data_end == 1'b1)
state <= S_IDLE;
else if(flag_aref_req == 1'b1)
state <= S_REQ;
else if(flag_row_end == 1'b1)
state <= S_ACT;
else
state <= state;
default : state <= S_IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_aref_req <= 1'b0;
else if(state == S_PRE && aref_req == 1'b1)
flag_aref_req <= 1'b1;
else
flag_aref_req <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_act_end <= 1'b0;
else if(state == S_ACT && wr_cmd == CMD_ACT)
flag_act_end <= 1'b1;
else
flag_act_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_end <= 1'b0;
else if(col_addr == 9'd508 && burst_cnt == 2'd1)
row_end <= 1'b1;
else
row_end <= 1'b0;
always @(posedge sclk)begin
row_end_r <= row_end;
row_end_r2 <= row_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
burst_cnt <= 2'd0;
else if(state == S_WR)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= 2'd0;
always @(posedge sclk)
burst_cnt_r <= burst_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_end <= 1'b0;
else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1)
data_end <= 1'b1;
else
data_end <= 1'b0;
always @(posedge sclk)begin
data_end_r <= data_end;
data_end_r2 <= data_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_row_end <= 1'b0;
else if(state == S_PRE && row_end_r2 == 1'b1)
flag_row_end <= 1'b1;
else
flag_row_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_data_end <= 1'b0;
else if(state == S_PRE && data_end_r2 == 1'b1)
flag_data_end <= 1'b1;
else
flag_data_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
col_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
col_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR)
col_addr <= col_addr + 3'd4;
else
col_addr <= col_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_addr <= 12'd0;
else if(data_end == 1'b1)
row_addr <= 12'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
row_addr <= row_addr + 1'b1;
else
row_addr <= row_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_cmd <= CMD_NOP;
else case(state)
S_ACT : if(wr_cmd != CMD_ACT && flag_act_end == 1'b0)
wr_cmd <= CMD_ACT;
else
wr_cmd <= CMD_NOP;
S_WR : if(data_end == 1'b1 || row_end == 1'b1)
wr_cmd <= CMD_NOP;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
wr_cmd <= CMD_NOP;
else if(burst_cnt == 2'd0)
wr_cmd <= CMD_WR;
else
wr_cmd <= CMD_NOP;
S_PRE : if(wr_cmd != CMD_PRE)
wr_cmd <= CMD_PRE;
else
wr_cmd <= CMD_NOP;
default : wr_cmd <= CMD_NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_addr <= 12'd0;
else case(state)
S_ACT : if(wr_cmd != CMD_ACT)
wr_addr <= row_addr;
else
wr_addr <= 12'd0;
S_WR : if(data_end == 1'b1 || row_end == 1'b1)
wr_addr <= 12'd0;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
wr_addr <= 12'd0;
else if(burst_cnt == 2'd0)
wr_addr <= {3'b000,col_addr};
else
wr_addr <= 12'd0;
S_PRE : wr_addr <= 12'b0100_0000_0000;
default : wr_addr <= 12'd0;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_s_wr <= 1'b0;
else if(state == S_WR)
flag_s_wr <= 1'b1;
else
flag_s_wr <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_data <= 16'd0;
else if(flag_s_wr == 1'b1)
wr_data <= wr_data + 1'b1;
else
wr_data <= wr_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_end <= 1'b0;
else if(flag_data_end == 1'b1)
wr_end <= 1'b1;
else if(state != S_IDLE && flag_aref_req == 1'b1 && wr_end == 1'b0)
wr_end <= 1'b1;
else
wr_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_req <= 1'b0;
else if(wr_en == 1'b1)
wr_req <= 1'b0;
else if(state == S_REQ)
wr_req <= 1'b1;
else
wr_req <= wr_req;
endmodule
SDRAM读模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_read.v
// Create Time : 2020-02-11 20:48:41
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_read(
//System Interfaces
input sclk ,
input rst_n ,
//SDRAM Interfaces
output reg [ 3:0] rd_cmd ,
output reg [11:0] rd_addr ,
output wire [ 1:0] bank_addr ,
//Communication Interfaces
input rd_trig ,
input rd_en ,
output reg rd_end ,
output reg rd_req ,
input aref_req ,
output reg rd_data_en
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
// Define State
localparam S_IDLE = 5'b0_0001 ;
localparam S_REQ = 5'b0_0010 ;
localparam S_ACT = 5'b0_0100 ;
localparam S_RD = 5'b0_1000 ;
localparam S_PRE = 5'b1_0000 ;
// SDRAM Command
localparam CMD_NOP = 4'b0111 ;
localparam CMD_PRE = 4'b0010 ;
localparam CMD_AREF = 4'b0001 ;
localparam CMD_ACT = 4'b0011 ;
localparam CMD_RD = 4'b0101 ;
reg [ 4:0] state ;
reg flag_act_end ;
reg row_end ;
reg [ 1:0] burst_cnt ;
reg data_end ;
reg flag_row_end ;
reg flag_data_end ;
reg flag_aref_req ;
reg [ 8:0] col_addr ;
reg [ 1:0] burst_cnt_r ;
reg [11:0] row_addr ;
reg data_end_r ;
reg data_end_r2 ;
reg row_end_r ;
reg row_end_r2 ;
reg [ 3:0] cnt_data ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign bank_addr = 2'b00;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= S_IDLE;
else case(state)
S_IDLE : if(rd_trig == 1'b1)
state <= S_REQ;
else
state <= state;
S_REQ : if(rd_en == 1'b1)
state <= S_ACT;
else
state <= state;
S_ACT : if(flag_act_end == 1'b1)
state <= S_RD;
else
state <= state;
S_RD : if(data_end_r == 1'b1 || row_end_r == 1'b1)
state <= S_PRE;
else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
state <= S_PRE;
else
state <= state;
S_PRE : if(flag_data_end == 1'b1)
state <= S_IDLE;
else if(flag_aref_req == 1'b1)
state <= S_REQ;
else if(flag_row_end == 1'b1)
state <= S_ACT;
else
state <= state;
default : state <= S_IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_aref_req <= 1'b0;
else if(state == S_PRE && aref_req == 1'b1)
flag_aref_req <= 1'b1;
else
flag_aref_req <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_act_end <= 1'b0;
else if(state == S_ACT && rd_cmd == CMD_ACT)
flag_act_end <= 1'b1;
else
flag_act_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_end <= 1'b0;
else if(col_addr == 9'd508 && burst_cnt == 2'd1)
row_end <= 1'b1;
else
row_end <= 1'b0;
always @(posedge sclk)begin
row_end_r <= row_end;
row_end_r2 <= row_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
burst_cnt <= 2'd0;
else if(state == S_RD)
burst_cnt <= burst_cnt + 1'b1;
else
burst_cnt <= 2'd0;
always @(posedge sclk)
burst_cnt_r <= burst_cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_end <= 1'b0;
else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1)
data_end <= 1'b1;
else
data_end <= 1'b0;
always @(posedge sclk)begin
data_end_r <= data_end;
data_end_r2 <= data_end_r;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_row_end <= 1'b0;
else if(state == S_PRE && row_end_r2 == 1'b1)
flag_row_end <= 1'b1;
else
flag_row_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
flag_data_end <= 1'b0;
else if(state == S_PRE && data_end_r2 == 1'b1)
flag_data_end <= 1'b1;
else
flag_data_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
col_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
col_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD)
col_addr <= col_addr + 3'd4;
else
col_addr <= col_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
row_addr <= 12'd0;
else if(data_end == 1'b1)
row_addr <= 12'd0;
else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
row_addr <= row_addr + 1'b1;
else
row_addr <= row_addr;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_cmd <= CMD_NOP;
else case(state)
S_ACT : if(rd_cmd != CMD_ACT && flag_act_end == 1'b0)
rd_cmd <= CMD_ACT;
else
rd_cmd <= CMD_NOP;
S_RD : if(data_end == 1'b1 || row_end == 1'b1)
rd_cmd <= CMD_NOP;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
rd_cmd <= CMD_NOP;
else if(burst_cnt == 2'd0)
rd_cmd <= CMD_RD;
else
rd_cmd <= CMD_NOP;
S_PRE : if(rd_cmd != CMD_PRE)
rd_cmd <= CMD_PRE;
else
rd_cmd <= CMD_NOP;
default : rd_cmd <= CMD_NOP;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_addr <= 12'd0;
else case(state)
S_ACT : if(rd_cmd != CMD_ACT)
rd_addr <= row_addr;
else
rd_addr <= 12'd0;
S_RD : if(data_end == 1'b1 || row_end == 1'b1)
rd_addr <= 12'd0;
else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
rd_addr <= 12'd0;
else if(burst_cnt == 2'd0)
rd_addr <= {3'b000,col_addr};
else
rd_addr <= 12'd0;
S_PRE : rd_addr <= 12'b0100_0000_0000;
default : rd_addr <= 12'd0;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_end <= 1'b0;
else if(flag_data_end == 1'b1)
rd_end <= 1'b1;
else if(state != S_IDLE && flag_aref_req == 1'b1 && rd_end == 1'b0)
rd_end <= 1'b1;
else
rd_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_req <= 1'b0;
else if(rd_en == 1'b1)
rd_req <= 1'b0;
else if(state == S_REQ)
rd_req <= 1'b1;
else
rd_req <= rd_req;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_data <= 4'd0;
else if(cnt_data == 4'd7)
cnt_data <= 4'd0;
else if(cnt_data >= 4'd1)
cnt_data <= cnt_data + 1'b1;
else if(rd_cmd == CMD_RD)
cnt_data <= cnt_data + 1'b1;
else
cnt_data <= 4'd0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_data_en <= 1'b0;
else if(cnt_data >= 4'd3 && cnt_data <= 4'd5)
rd_data_en <= 1'b1;
else
rd_data_en <= 1'b0;
endmodule
观察上述模块很容易发现读写模块中最重要的就是上面的那个状态机。
SDRAM读写测试模块
这里给出顶层测试模块的编写:
`timescale 1ns / 1ps
`define CLOCK 10
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_init_tb.v
// Create Time : 2020-02-09 17:10:08
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sdram_init_tb;
reg sclk ;
reg rst_n ;
wire sdram_clk ;
wire sdram_cke ;
wire sdram_cs_n ;
wire sdram_cas_n ;
wire sdram_ras_n ;
wire sdram_we_n ;
wire [ 1:0] sdram_bank ;
wire [11:0] sdram_addr ;
wire [ 1:0] sdram_dqm ;
wire [15:0] sdram_dq ;
reg wr_trig ;
reg rd_trig ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
wr_trig <= 1'b0;
rd_trig <= 1'b0;
#(100*`CLOCK);
rst_n <= 1'b1;
#(250_000)
wr_trig <= 1'b1;
#(`CLOCK)
wr_trig <= 1'b0;
#(20_000)
rd_trig <= 1'b1;
#(`CLOCK)
rd_trig <= 1'b0;
#(20_000);
wr_trig <= 1'b1;
#(`CLOCK)
wr_trig <= 1'b0;
#(20_000);
rd_trig <= 1'b1;
#(`CLOCK)
rd_trig <= 1'b0;
#(20_000);
$stop;
end
always #(`CLOCK/2) sclk = ~sclk;
sdram_top sdram_top_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//SDRAM Interfaces
.sdram_clk (sdram_clk ),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_cas_n (sdram_cas_n ),
.sdram_ras_n (sdram_ras_n ),
.sdram_we_n (sdram_we_n ),
.sdram_bank (sdram_bank ),
.sdram_addr (sdram_addr ),
.sdram_dqm (sdram_dqm ),
.sdram_dq (sdram_dq ),
//Others
.wr_trig (wr_trig ),
.rd_trig (rd_trig )
);
defparam sdram_model_plus_inst.addr_bits = 12;
defparam sdram_model_plus_inst.data_bits = 16;
defparam sdram_model_plus_inst.col_bits = 9;
defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; // 2M
sdram_model_plus sdram_model_plus_inst(
.Dq (sdram_dq ),
.Addr (sdram_addr ),
.Ba (sdram_bank ),
.Clk (sdram_clk ),
.Cke (sdram_cke ),
.Cs_n (sdram_cs_n ),
.Ras_n (sdram_ras_n ),
.Cas_n (sdram_cas_n ),
.We_n (sdram_we_n ),
.Dqm (sdram_dqm ),
.Debug (1'b1 )
);
endmodule
这里为了博客的简洁性不再给出相应的仿真模型文件,同学们可以关注前几篇SDRAM的博客,也可以进群下载相应的工程。
SDRAM仿真测试
这里也不给出具体的仿真图形,因为数据太长,截图不方便,感兴趣的同学可以自己仿真试试,但是这里要给出一个之前所说奇怪的实验现象,就是4突发的最后一个数据的有限长度比一个时钟周期短的多:
其实这里只需要下板验证一下,但是因为在家开发板没带回来,也就无法验证,这里很有可能是仿真模型的问题。即使SDRAM就是有这个坑人的特性,我们也可以用SDRAM来捕捉读数据,实在不行,我们还可以对sclk进行PLL移相操作。
参考文献
[1]、开源骚客
总结
创作不易,认为文章有帮助的同学们可以关注点赞支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: