基于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 : nnzhang1996@foxmail.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