基于FPGA的SDRAM控制器设计(4)
基于FPGA完整SDRAM控制器
SDRAM控制器接口简述
完整的SDRAM控制器的模块框图如下:
前面的三篇文章,我们已经简述了基本的SDRAM的基本操作。这里总结一下SDRAM的几个模块,SDRAM的上电初始化,自刷新、读写模块、顶层仲裁控制。了解了上面的操作,我们已经可以完成SDRAM控制器的代码完成,接下来我们便完善SDRAM控制器的接口,简化该SDRAM控制器设计,使得该SDRAM控制器可以很容易的使用。下面的接口定义如下:
顶层模块的接口主要时上面的接口,我们把SDRAM做成了FIFO类型的接口。
其中1时系统接口:
sclk:是100MHz的时钟,
rst_n:是系统复位信号,
2是SDRAM硬件的接口信号,连接到SDRAM硬件上。
3是SDRAM写FIFO的信号
4是SDRAM读FIFO的信号
5是SDRAM最大的读地址信号,
RROW_ADDR_END是SDRAM的FIFO接口最大的行地址,
RCOL_MADDR_END是SDRAM最大的列地址,超过上面的信号便会清零,注意RCOL_MADDR_END信号必须是4的倍数,因为我们SDRAM中是4突发的。
6是SDRAM最大FIFO接口最大的写地址信号,与5的描述相同。
自动读写模块的框图
这里为了方便同学们理解,我们给出自动读写模块的框图,也是仿照开源骚客的文章设计:
SDRAM控制器完整代码
经过前面三篇文章的学习,我们这里不在给出原理,直接给出使用的SDRAM的代码:
sdram_top模块:
`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 ,
//User Interfaces
input wfifo_wclk ,
input wfifo_wr_en ,
input [15:0] wfifo_wr_data ,
input rfifo_rclk ,
input rfifo_rd_en ,
output wire [15:0] rfifo_rd_data ,
output wire rfifo_rd_ready
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter RROW_ADDR_END = 937 ;
parameter RCOL_MADDR_END = 256 ;
parameter WROW_ADDR_END = 937 ;
parameter WCOL_MADDR_END = 256 ;
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 ;
wire wr_trig ;
//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_trig ;
//sdram_auto_write_read
wire wfifo_rd_en ;
wire [15:0] wfifo_rd_data ;
wire rfifo_wr_en ;
wire [15:0] rfifo_wr_data ;
//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;
assign rfifo_wr_data = sdram_dq;
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_auto_write_read sdram_auto_write_read_inst(
// system signals
.rst_n (rst_n ),
// wfifo
.wfifo_wclk (wfifo_wclk ),
.wfifo_wr_en (wfifo_wr_en ),
.wfifo_wr_data (wfifo_wr_data ),
.wfifo_rclk (sclk ),
.wfifo_rd_en (wfifo_rd_en ),
.wfifo_rd_data (wfifo_rd_data ),
.wr_trig (wr_trig ),
// rfifo
.rfifo_wclk (~sclk ), // 100MHz
.rfifo_wr_en (rfifo_wr_en ),
.rfifo_wr_data (rfifo_wr_data ),
.rfifo_rclk (rfifo_rclk ),
.rfifo_rd_en (rfifo_rd_en ),
.rfifo_rd_data (rfifo_rd_data ),
.rd_trig (rd_trig ),
// user interfaces
.rfifo_rd_ready (rfifo_rd_ready )
);
sdram_write #(
.WROW_ADDR_END (WROW_ADDR_END ),
.WCOL_MADDR_END (WCOL_MADDR_END )
) 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 ),
.wfifo_rd_en (wfifo_rd_en ),
.wfifo_rd_data (wfifo_rd_data )
);
sdram_read #(
.RROW_ADDR_END (RROW_ADDR_END ),
.RCOL_MADDR_END (RCOL_MADDR_END )
) 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 (rfifo_wr_en )
);
endmodule
sdram_init模块:
`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_aref模块:
`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_auto_write_read模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : aaa@qq.com
// Website :
// Module Name : sdram_auto_write_read.v
// Create Time : 2020-02-15 11:26:21
// 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_auto_write_read(
// system signals
input rst_n ,
// wfifo
input wfifo_wclk ,
input wfifo_wr_en ,
input [15:0] wfifo_wr_data ,
input wfifo_rclk ,
input wfifo_rd_en ,
output wire [15:0] wfifo_rd_data ,
output reg wr_trig ,
// rfifo
input rfifo_wclk , // 100MHz
input rfifo_wr_en ,
input [15:0] rfifo_wr_data ,
input rfifo_rclk ,
input rfifo_rd_en ,
output wire [15:0] rfifo_rd_data ,
output reg rd_trig ,
// user interfaces
output reg rfifo_rd_ready
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter WFIFO_RD_CNT = 256 ;
parameter RFIFO_WR_CNT = 250 ;
wire [10:0] wfifo_rd_count ;
wire [10:0] rfifo_wr_count ;
reg flag_rd ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge rfifo_wclk or negedge rst_n)
if(rst_n == 1'b0)
rfifo_rd_ready <= 1'b0;
else if(rfifo_wr_count >= RFIFO_WR_CNT)
rfifo_rd_ready <= 1'b1;
else
rfifo_rd_ready <= rfifo_rd_ready;
always @(posedge wfifo_rclk or negedge rst_n)
if(rst_n == 1'b0)
flag_rd <= 1'b0;
else if(wfifo_rd_count >= WFIFO_RD_CNT)
flag_rd <= 1'b1;
else
flag_rd <= flag_rd;
always @(posedge rfifo_wclk or negedge rst_n)
if(rst_n == 1'b0)
rd_trig <= 1'b0;
else if(rfifo_wr_count < RFIFO_WR_CNT && flag_rd == 1'b1)
rd_trig <= 1'b1;
else
rd_trig <= 1'b0;
always @(posedge wfifo_rclk or negedge rst_n)
if(rst_n == 1'b0)
wr_trig <= 1'b0;
else if(wfifo_rd_count >= WFIFO_RD_CNT)
wr_trig <= 1'b1;
else
wr_trig <= 1'b0;
//wfifo
fifo_generator_0 wfifo_inst(
.rst (~rst_n ), // input wire rst
.wr_clk (wfifo_wclk ), // input wire wr_clk
.rd_clk (wfifo_rclk ), // input wire rd_clk
.din (wfifo_wr_data ), // input wire [15 : 0] din
.wr_en (wfifo_wr_en ), // input wire wr_en
.rd_en (wfifo_rd_en ), // input wire rd_en
.dout (wfifo_rd_data ), // output wire [15 : 0] dout
.full ( ), // output wire full
.empty ( ), // output wire empty
.rd_data_count (wfifo_rd_count ), // output wire [10 : 0] rd_data_count
.wr_data_count ( ) // output wire [10 : 0] wr_data_count
);
//rfifo
fifo_generator_0 rfifo_inst(
.rst (~rst_n ), // input wire rst
.wr_clk (rfifo_wclk ), // input wire wr_clk
.rd_clk (rfifo_rclk ), // input wire rd_clk
.din (rfifo_wr_data ), // input wire [15 : 0] din
.wr_en (rfifo_wr_en ), // input wire wr_en
.rd_en (rfifo_rd_en ), // input wire rd_en
.dout (rfifo_rd_data ), // output wire [15 : 0] dout
.full ( ), // output wire full
.empty ( ), // output wire empty
.rd_data_count ( ), // output wire [10 : 0] rd_data_count
.wr_data_count (rfifo_wr_count ) // output wire [10 : 0] wr_data_count
);
endmodule
sdram_write模块:
`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 wire [15:0] wr_data ,
//Communication Interfaces
input wr_trig ,
input wr_en ,
output reg wr_end ,
output reg wr_req ,
input aref_req ,
output reg wfifo_rd_en ,
input [15:0] wfifo_rd_data
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter WROW_ADDR_END = 937 ;
parameter WCOL_MADDR_END = 256 ;
parameter WCOL_FADDR_END = 512 ;
// 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 ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign bank_addr = 2'b00;
assign wr_data = wfifo_rd_data;
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(state == S_WR && wr_cmd == CMD_WR && col_addr == WCOL_MADDR_END - 4 && row_addr == WROW_ADDR_END)
data_end <= 1'b1;
else if((col_addr == 'd252 || col_addr == 508) && 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 <= 9'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == WCOL_MADDR_END - 4 && row_addr == WROW_ADDR_END)
col_addr <= 9'd0;
else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
col_addr <= 9'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(state == S_WR && wr_cmd == CMD_WR && col_addr == WCOL_MADDR_END - 4 && row_addr == WROW_ADDR_END)
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'd2 && 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'd2 && 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)
wfifo_rd_en <= 1'b0;
else if(state == S_WR)
wfifo_rd_en <= 1'b1;
else
wfifo_rd_en <= 1'b0;
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_read模块:
`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**********************************
//========================================================================================/
parameter RROW_ADDR_END = 937 ;
parameter RCOL_MADDR_END = 256 ;
// 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 rfifo_wd_en_r1 ;
reg rfifo_wd_en_r2 ;
reg rfifo_wd_en_r3 ;
//========================================================================================\
//************** 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(state == S_RD && rd_cmd == CMD_RD && col_addr == RCOL_MADDR_END - 4 && row_addr == RROW_ADDR_END)
data_end <= 1'b1;
else if((col_addr == 'd252 || col_addr == 508) && 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 == RCOL_MADDR_END - 4 && row_addr == RROW_ADDR_END)
col_addr <= 9'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(state == S_RD && rd_cmd == CMD_RD && col_addr == RCOL_MADDR_END - 4 && row_addr == RROW_ADDR_END)
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'd2 && 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'd2 && 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)
rfifo_wd_en_r1 <= 1'b0;
else if(state == S_RD)
rfifo_wd_en_r1 <= 1'b1;
else
rfifo_wd_en_r1 <= 1'b0;
always @(posedge sclk)begin
rfifo_wd_en_r2 <= rfifo_wd_en_r1;
rfifo_wd_en_r3 <= rfifo_wd_en_r2;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_data_en <= 1'b0;
else
rd_data_en <= rfifo_wd_en_r3;
endmodule
SDRAM控制器完整的模块代码如上,一共五个模块,其中SDRAM上电初始化、自刷新与前面的模块一摸一样,SDRAM读写模块只是更改了一小部分内容,又在前面基础上添加了sdram_auto_write_read自动读写模块。
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 clk_50m ;
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 wfifo_wr_en ;
reg [15:0] wfifo_wr_data ;
wire [15:0] rfifo_rd_data ;
wire rfifo_rd_ready ;
reg [15:0] rfifo_test_data ;
reg [20:0] err_cnt ;
reg rfifo_rd_en ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
#(200*`CLOCK)
rst_n <= 1'b1;
end
always #(`CLOCK/2) sclk = ~sclk;
initial begin
clk_50m = 1'b0;
wfifo_wr_en <= 1'b0;
rfifo_rd_en <= 1'b0;
#(250_000)
wfifo_wr_en <= 1'b1;
#(5_000_000)
rfifo_rd_en <= 1'b1;
#(459_852_690-250_000);
wfifo_wr_en <= 1'b0;
rfifo_rd_en <= 1'b0;
// #(9_863_860-250_000);
// wfifo_wr_en <= 1'b0;
// rfifo_rd_en <= 1'b1;
end
always #(`CLOCK*5) clk_50m = ~clk_50m;
always @(posedge clk_50m or negedge rst_n)
if(rst_n == 1'b0)
wfifo_wr_data <= 16'd0;
else if(wfifo_wr_en == 1'b1)
wfifo_wr_data <= wfifo_wr_data + 1'b1;
else
wfifo_wr_data <= wfifo_wr_data;
always @(posedge clk_50m or negedge rst_n)
if(rst_n == 1'b0)
rfifo_test_data <= 16'd0;
else if(rfifo_rd_en == 1'b1)
rfifo_test_data <= rfifo_test_data + 1'b1;
else
rfifo_test_data <= rfifo_test_data;
always @(posedge clk_50m or negedge rst_n)
if(rst_n == 1'b0)
err_cnt <= 21'd0;
else if(rfifo_rd_en == 1'b1 && rfifo_test_data != rfifo_rd_data)
err_cnt <= err_cnt + 1'b1;
else
err_cnt <= err_cnt;
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 ),
//User Interfaces
.wfifo_wclk (clk_50m ),
.wfifo_wr_en (wfifo_wr_en ),
.wfifo_wr_data (wfifo_wr_data ),
.rfifo_rclk (clk_50m ),
.rfifo_rd_en (rfifo_rd_en ),
.rfifo_rd_data (rfifo_rd_data ),
.rfifo_rd_ready (rfifo_rd_ready )
);
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
测试模型的代码,我们为了简单起见不再给出,有需要的同学可以去前面的文章中自取。
仿真结果
细看我们的测试模块,我们编写的是循环测试的代码,运行了一段时间后,err_cnt依旧是0,证明了我们模块设计的正确性。
总结
创作不易,认为文章有帮助的同学们可以关注点赞支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: