SDRAM同步动态随机存储器

SDRAM概念

SDRAM与SRAM区别

SDRAM:同步动态存储器。
同步:需要时钟信号,数据与时钟信号的上升沿同步变化。
动态:每64ms内动态刷新存储器中的数据。电容自放电特性,为了保持数据不变需要刷新。
随机:可以任意选取SDRAM中存储器单元地址进行读写。
SRAM:异步静态存储器。
不需要时钟信号,功耗高,不需要刷新。

SDRAM存储器框图

1bit存储器内部由行地址线与列地址线组成。


CLK:时钟输入信号;
CKE:时钟有效信号,高电平有效。
CS_N:片选信号(总开关)。低电平有效。
RAS_N:行地址选通有效信号,使能行地址访问,预充电,在时钟上升沿存行地址。低电平有效。
CAS_N:列地址选通,上升沿传到列地址。低电平有效。
WE_N:使能写操作。低电平有效。
BA:bank地址,4个bank。
DQM:高低电平掩码,若DQM[1]=1,则DQ[15:8]为高阻态。
SA:不同的操作命令意义不同。13位宽,共8192行。

SDRAM参数定义

CL:列选通潜伏期;从发送读命令信号2/3个时钟周期后发送数据所需要的时间。写操作不需要列选通潜伏期。(打开列地址后传数据所需要的时间)。
tRCD:开行地址(激活命令)后需要tRCDns打开列地址。
tRP:预充电命令(关闭行命令)发送后等待tRP时间才可重新操作。

SDRAM控制器

SDRAM初始化模块

SDRAM初始化模块时序图

SDRAM初始化模块状态转移图

SDRAM初始化模块代码

可参考SDRAM学习(二)之初始化.

module SDRAM_INIT(Clk,Rst_n,Init_cmd,Init_ba,Init_adrr,Init_done
);
input Clk;
input Rst_n;output  reg[3:0]Init_cmd;  //初始化命令
output  reg[1:0]Init_ba;   //初始化bank地址
output  reg[12:0]Init_adrr;  //选择的地址a0-a12 相当于sa
output  Init_done;     //初始化完成标志信号localparam  WAIT = 15'd20_000;  //上电后等待时钟数(200us)localparam  IDLE = 3'd0, //空闲状态CHARGE = 3'd1, //预充电状态CHARGE_WAIT = 3'd2, //预充电等待状态REFRESH = 3'd3, //自刷新状态REFRESH_WAIT = 3'd4,//自刷新等待状态MODE = 3'd5, //模式寄存器状态MODE_WAIT = 3'd6, //模式寄存器等待状态END = 3'd7; //结束态parameter   CHARGE_CLK = 3'd2, //预充电等待周期   tRPREFRESH_CLK = 3'd7, //自刷新等待周期  tRCMODE_CLK = 3'd3; //配置模式寄存器等待周期   tMRDlocalparam  NONE_CMD = 4'b0111, //空命令指令CHARGE_CMD = 4'b0010, //预充电指令REFRESH_CMD = 4'b0001, //自动刷新指令MODE_CMD = 4'b0000;//配置模式寄存器指令reg [2:0]main_state;
reg [14:0]cnt_200us;
wire charge_end;
wire refresh_end;
wire mode_end;
reg [3:0]refresh_cnt;
reg [2:0]cnt_clk;
reg cnt_clk_en;wire wait_end;always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_200us <= 1'b0;
else
if(cnt_200us == WAIT)
cnt_200us <= WAIT;
else
cnt_200us <= cnt_200us + 1'b1;assign wait_end = (cnt_200us == (WAIT - 1'b1))?1'b1 : 1'b0;//主状态机 格雷码
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
main_state <= 3'd0;
else
begincase(main_state)IDLE:beginif(wait_end)main_state <= CHARGE;else main_state <= IDLE;endCHARGE:main_state <= CHARGE_WAIT;CHARGE_WAIT:  beginif(charge_end)main_state <= REFRESH;else main_state <= CHARGE_WAIT;endREFRESH:main_state <= REFRESH_WAIT;REFRESH_WAIT:beginif(refresh_end)beginif(refresh_cnt == 4'd8)main_state <= MODE;else main_state <= REFRESH;endelse main_state <= REFRESH_WAIT;endMODE: main_state <= MODE_WAIT;MODE_WAIT:beginif(mode_end)main_state <= END;else main_state <= MODE_WAIT;endEND: main_state <= END;default : main_state <= IDLE;endcase
end//计数时钟计数器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_clk <= 1'b0;
else
if(cnt_clk_en)
cnt_clk <= 1'b0;
else
cnt_clk <= cnt_clk + 1'b1;assign charge_end = ((cnt_clk == CHARGE_CLK) && (main_state == CHARGE_WAIT))? 1'b1:1'b0;
assign refresh_end = ((cnt_clk == REFRESH_CLK) && (main_state == REFRESH_WAIT))? 1'b1:1'b0;
assign mode_end = ((cnt_clk == MODE_CLK) && (main_state == MODE_WAIT))? 1'b1:1'b0;//产生时钟计数器使能信号
always @(*)
begincase(main_state)IDLE : cnt_clk_en <= 1'b1;CHARGE_WAIT : cnt_clk_en <= (charge_end == 1'b1)? 1'b1:1'b0;REFRESH_WAIT : cnt_clk_en <= (refresh_end == 1'b1)? 1'b1:1'b0;    MODE_WAIT : cnt_clk_en <= (mode_end == 1'b1)? 1'b1:1'b0;END : cnt_clk_en <= 1'b1; default: cnt_clk_en <= 1'b0;endcase
end//输出信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
beginInit_cmd   <= NONE_CMD;Init_ba <= 2'b11;Init_adrr  <= 13'h1fff;
end
else
begincase(main_state)IDLE,CHARGE_WAIT,REFRESH_WAIT,MODE_WAIT,END:beginInit_cmd  <= NONE_CMD;Init_ba <= 2'b11;Init_adrr  <= 13'h1fff;      endCHARGE:beginInit_cmd <= CHARGE_CMD;Init_ba <= 2'b11;Init_adrr  <= 13'h1fff;endREFRESH:beginInit_cmd    <= REFRESH_CMD;Init_ba <= 2'b11;Init_adrr  <= 13'h1fff;       endMODE:beginInit_cmd   <= MODE_CMD;Init_ba <= 2'b00;Init_adrr  <= { 地址辅助配置模式寄存器,参数不同,配置的模式不同3'b000, //A12-A10;预留1'b0, //A9=0; 读写方式,0:突发读&突发写,1:突发读&单写2'b00, //{A8,A7}=00:标准模式,默认3'b011,//{A6,A5,A4}=011:CAS潜伏期,010:2,011:3,其他:保留1'b0, //A3=0:突发传输方式,0:顺序,1:隔行3'b111 //{A2,A1,A0}=111:突发长度,000:单字节,001:2字节//010:4字节,011:8字节,111:整页,其他:保留};          enddefault:beginInit_cmd    <= NONE_CMD;Init_ba <= 2'b11;Init_adrr  <= 13'h1fff;endendcaseend//产生Done信号
assign Init_done = (main_state == END)? 1'b1:1'b0;//计数刷新次数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
refresh_cnt <= 1'b0;
else
if(main_state == IDLE)
refresh_cnt <= 1'b0;
else
if(main_state == REFRESH)
refresh_cnt <= refresh_cnt + 1'b1;
else
refresh_cnt <= refresh_cnt;endmodule

SDRAM初始化模块仿真文件

`timescale 1ns/1ns
`define CLK_period 20
module SDRAM_INIT_tb;wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram_init
wire    [3:0]   init_cmd        ;   //初始化阶段指令
wire    [1:0]   init_ba         ;   //初始化阶段L-Bank地址
wire    [12:0]  init_addr       ;   //初始化阶段地址总线
wire            init_end        ;   //初始化完成信号reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;CLK_STRAT   CLK_STRAT_inst (  //IP核PLL.areset ( !sys_rst_n ),.inclk0 ( sys_clk ),.c0 (  clk_50m       ),.c1 (  clk_100m      ),.c2 (  clk_100m_shift),.locked ( locked ));SDRAM_INIT SDRAM_INIT(.Clk(clk_100m),.Rst_n(rst_n),.Init_cmd(init_cmd),.Init_ba(init_ba),.Init_adrr(init_addr),.Init_done(init_end)
);  sdram_model_plus sdram_model_plus_inst(.Dq     (               ),.Addr   (init_addr      ),.Ba     (init_ba        ),.Clk    (clk_100m_shift ),.Cke    (1'b1           ),.Cs_n   (init_cmd[3]    ),.Ras_n  (init_cmd[2]    ),.Cas_n  (init_cmd[1]    ),.We_n   (init_cmd[0]    ),.Dqm    (2'b0           ),.Debug  (1'b1           ));
endmodule

SDRAM初始化模块仿真波形图

对所有bank自刷新8次,产生end结束信号。

SDRAM自刷新模块

SDRAM自动刷新模块图


时钟指令为高时输入自刷新指令为自动刷新模式。需要外步时钟写入。
时钟指令为低时输入自刷新指令为自刷新模式。低功耗模式。此模式下其他指令无效。不需要外步时钟写入。时钟指令为高退出自刷新模式。
64MS内刷新8192行,13位宽,在100M时钟下(周期10ns)计数值设为750.
64MS内刷新4096行,在100M时钟下(周期10ns)计数值设为1510.

SDRAM自动刷新模块时序

SDRAM自动刷新模块状态转移图

SDRAM自动刷新模块代码

module SDRAM_REFRESH(Clk,Rst_n,Init_end,Re_en,Re_cmd,Re_addr,Re_ba,Re_end,Re_req  );input    Clk      ;
input   Rst_n    ;
input   Init_end ;
input   Re_en    ;
output  reg[3:0]Re_cmd;
output  reg[12:0]Re_addr;
output  reg[1:0]Re_ba;
output  Re_end;
output reg  Re_req;parameter CNT_MAX = 9'd450;  //刷新时间计数
localparam RE_IDLE         = 3'b000   ,   //空闲RE_CHARGE       = 3'b001   ,   //预充电RE_CHARGE_WAIT  = 3'b010   ,   //预充电等待RE_REFRESH      = 3'b011   ,   //自动刷新RE_REFRESH_WAIT = 3'b100   ,   //自动刷新等待RE_END          = 3'b101   ;   //结束parameter  CHARGE_CLK = 3'd2, //预充电等待周期REFRESH_CLK = 3'd7; //自刷新等待周期       localparam  NONE_CMD = 4'b0111, //空命令指令CHARGE_CMD = 4'b0010, //预充电指令 行地址REFRESH_CMD = 4'b0001; //自刷新指令reg [8:0]re_cnt;
reg [2:0]main_state;
wire charge_end;
wire refresh_end;
reg [1:0]refresh_cnt;
reg [2:0]cnt_clk;
reg cnt_clk_en;  //高电平清零
wire re_ack;//64ms内刷新13位宽8192行 每4500ns刷新一次
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
re_cnt <= 1'b0;
else
if(re_cnt >= CNT_MAX)
re_cnt <= 1'b0;
else
if(Init_end == 1'b1)
re_cnt <= re_cnt + 1'b1;//请求刷新信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Re_req <= 1'b0;
else
if(re_cnt == (CNT_MAX - 1'b1))
Re_req <= 1'b1;
else
if(re_ack == 1'b1)
Re_req <= 1'b0;assign re_ack = (main_state == RE_REFRESH)?1'b1:1'b0 ; //主状态
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
main_state <= 1'b0;
else
begincase(main_state)RE_IDLE:beginif(Init_end && Re_en)main_state <= RE_CHARGE;else main_state <= RE_IDLE;endRE_CHARGE:    main_state <= RE_CHARGE_WAIT;RE_CHARGE_WAIT:beginif(charge_end)main_state <= RE_REFRESH;else main_state <= RE_CHARGE_WAIT;endRE_REFRESH:    main_state <= RE_REFRESH_WAIT;RE_REFRESH_WAIT:beginif(refresh_end)beginif(refresh_cnt == 2'd2)main_state <= RE_END;else main_state <= RE_REFRESH;endelse main_state <= RE_REFRESH_WAIT;endRE_END : main_state <= RE_IDLE;  default : main_state <= RE_IDLE;endcaseendalways @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_clk <= 1'b0;
else
if(cnt_clk_en)
cnt_clk <= 1'b0;
else
cnt_clk <= cnt_clk + 1'b1;always @(*)
begincase(main_state)RE_IDLE : cnt_clk_en <= 1'b1;RE_CHARGE_WAIT : cnt_clk_en <= (charge_end == 1'b1) ? 1'b1:1'b0;RE_REFRESH_WAIT : cnt_clk_en <= (refresh_end == 1'b1) ? 1'b1:1'b0;default: cnt_clk_en <= 1'b0;endcase
endassign charge_end = ((cnt_clk == CHARGE_CLK) && (main_state == RE_CHARGE_WAIT))? 1'b1:1'b0;
assign refresh_end = ((cnt_clk == REFRESH_CLK) && (main_state == RE_REFRESH_WAIT))? 1'b1:1'b0;//输出信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
begin
Re_cmd  <= 1'b0;
Re_ba <= 1'b0;
Re_addr  <= 1'b0;
end
else
begincase(main_state)RE_IDLE,RE_CHARGE_WAIT,RE_REFRESH_WAIT,RE_END:beginRe_cmd  <= NONE_CMD;Re_ba <= 2'b11;Re_addr  <= 13'h1fff;      endRE_CHARGE:beginRe_cmd    <= CHARGE_CMD;Re_ba <= 2'b11;Re_addr  <= 13'h1fff;endRE_REFRESH:beginRe_cmd   <= REFRESH_CMD;Re_ba <= 2'b11;Re_addr  <= 13'h1fff;       enddefault:beginRe_cmd  <= NONE_CMD;Re_ba <= 2'b11;Re_addr  <= 13'h1fff;endendcase
end//产生Done信号
assign Re_end = (main_state == RE_END)? 1'b1:1'b0;//计数刷新次数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
refresh_cnt <= 1'b0;
else
if(main_state == RE_END)
refresh_cnt <= 1'b0;
else
if(main_state == RE_REFRESH)
refresh_cnt <= refresh_cnt + 1'b1;
else
refresh_cnt <= refresh_cnt;endmodule

SDRAM自动刷新模块仿真文件

初始化后自动刷新。

`timescale  1ns/1ns
module  SDRAM_REFRESH_tb();
//sdram
wire    [3:0]   sdram_cmd       ;   //SDRAM操作指令
wire    [1:0]   sdram_ba        ;   //SDRAM L-Bank地址
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram_init
wire    [3:0]   init_cmd        ;   //初始化阶段指令
wire    [1:0]   init_ba         ;   //初始化阶段L-Bank地址
wire    [12:0]  init_addr       ;   //初始化阶段地址总线
wire            init_end        ;   //初始化完成信号
//sdram_a_ref
wire            aref_req        ;   //自动刷新请求
wire            re_end        ;   //自动刷新结束
wire    [3:0]   re_cmd        ;   //自动刷新阶段指令
wire    [1:0]   re_ba         ;   //自动刷新阶段L-Bank地址
wire    [12:0]  re_addr       ;   //自动刷新阶段地址总线//reg define
reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
reg             re_en         ;   //自动刷新使能//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;//re_en:自动刷新使能
always@(posedge clk_100m or negedge rst_n)if(rst_n == 1'b0)re_en <=  1'b0;else    if((init_end == 1'b1) && (aref_req == 1'b1))re_en <=  1'b1;else    if(re_end == 1'b1)re_en <=  1'b0;assign  sdram_cmd = (init_end == 1'b1) ? re_cmd : init_cmd;
assign  sdram_ba = (init_end == 1'b1) ? re_ba : init_ba;
assign  sdram_addr = (init_end == 1'b1) ? re_addr : init_addr;CLK_STRAT CLK_STRAT_inst (  //IP核PLL.areset ( !sys_rst_n ),.inclk0 ( sys_clk ),.c0 (  clk_50m       ),.c1 (  clk_100m      ),.c2 (  clk_100m_shift),.locked ( locked ));SDRAM_REFRESH SDRAM_REFRESH(.Clk(clk_100m),.Rst_n(rst_n),.Init_end(init_end),.Re_en(re_en),.Re_cmd(re_cmd),.Re_addr(re_addr),.Re_ba(re_ba),.Re_end(re_end),.Re_req(aref_req)  );SDRAM_INIT SDRAM_INIT(.Clk(clk_100m),.Rst_n(rst_n),.Init_cmd(init_cmd),.Init_ba(init_ba),.Init_adrr(init_addr),.Init_done(init_end)
);//SDRAM模型
sdram_model_plus    sdram_model_plus_inst(.Dq     (               ),.Addr   (sdram_addr     ),.Ba     (sdram_ba       ),.Clk    (clk_100m_shift ),.Cke    (1'b1           ),.Cs_n   (sdram_cmd[3]   ),.Ras_n  (sdram_cmd[2]   ),.Cas_n  (sdram_cmd[1]   ),.We_n   (sdram_cmd[0]   ),.Dqm    (2'b0           ),.Debug  (1'b1           )
);
endmodule

SDRAM自动刷新模块仿真结果

re_cnt计数到750发出re_req自动刷新请求信号。

SDRAM写操作模块

SDRAM写模块框图

SDRAM写模块时序

SDRAM写模块状态转移图

写模块代码

module SDRAM_WR(Clk,Rst_n,Init_end,Wr_addr,  Wr_data,Wr_burst_len,Wr_en,Wr_ba,Wr_cmd,Wr_sdram_addr,Wr_sdram_data,Wr_end,Wr_sdram_en,Wr_ack
);
input   Clk;
input   Rst_n;
input   Init_end;
input   [23:0]Wr_addr;  //写SDRAM地址  bank地址 行/列地址组成
input   [15:0]Wr_data;
input   [9:0]Wr_burst_len; //写突发长度max512
input   Wr_en;output    reg[1:0]Wr_ba;
output  reg[3:0]Wr_cmd;
output  reg[12:0]Wr_sdram_addr;//地址数据,辅助预充电操作,行、列地址,A12-A0,13位地址
output  [15:0]Wr_sdram_data;//写入SDRAM的数据
output  Wr_end;
output  reg Wr_sdram_en; //写SDRAM使能信号
output  Wr_ack; localparam WR_IDLE     =3'b000,//空闲态 WR_ACTIVE   =3'b001,//写激活ACTIVE_WAIT =3'b010,//激活等待WRITE       =3'b011,//写命令 写指令    行地址传入RAS_n端(SDRAM )端WR_DATA     =3'b100,//写数据   传列地址Cas_n端为低电平WR_CHAR     =3'b101,//预充电WR_CHAR_WAIT=3'b110,//预充电等待WR_END      =3'b111;//写结束parameter  CHARGE_CLK = 3'd2, //预充电等待周期ACTIVE_CLK = 3'd2; //激活等待周期         localparam  NONE_CMD = 4'b0111, //空命令指令CHARGE_CMD = 4'b0010, //预充电指令  行地址传入RAS_n端(SDRAM )端WR_CMD = 4'b0100, //写指令    列地址传入CAS_n端(SDRAM )端ACTIVE_CMD = 4'b0011,//激活命令STOP = 4'b0110; //突发停止指令reg [2:0]state;
reg [9:0]cnt_clk;
reg cnt_clk_en;
wire active_end;
wire wr_data_end;
wire char_end;//主状态机
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
state <= 1'b0;
else
begincase(state)WR_IDLE     :beginif(Init_end && Wr_en)state <= WR_ACTIVE;else state <= WR_IDLE;end WR_ACTIVE   :state <= ACTIVE_WAIT;ACTIVE_WAIT :beginif(active_end)state <= WRITE;else state <= ACTIVE_WAIT;endWRITE       :     //写命令state <= WR_DATA;WR_DATA     :      beginif(wr_data_end)state <= WR_CHAR;else state <= WR_DATA;endWR_CHAR     :state <= WR_CHAR_WAIT;WR_CHAR_WAIT:beginif(char_end)state <= WR_END;else state <= WR_CHAR_WAIT;     endWR_END      :state <= WR_IDLE;default: state <= WR_IDLE;endcase                     end//使能计数器计数时钟
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_clk <= 1'b0;
else
if(cnt_clk_en)
cnt_clk <= 1'b0;
else
cnt_clk <= cnt_clk + 1'b1;//使能CLK计数信号
always @(*)
begincase(state)WR_END,WR_IDLE,WRITE:cnt_clk_en <= 1'b1;ACTIVE_WAIT:cnt_clk_en <= (active_end == 1'b1)?1'b1:1'b0;WR_DATA:cnt_clk_en <= (wr_data_end == 1'b1)?1'b1:1'b0;  WR_CHAR_WAIT:cnt_clk_en <= (char_end == 1'b1)?1'b1:1'b0;   default : cnt_clk_en <= 1'b0;endcase end//产生end信号
assign active_end = (cnt_clk == ACTIVE_CLK)?1'b1:1'b0;
assign wr_data_end = (cnt_clk == (Wr_burst_len - 1'b1))?1'b1:1'b0;
assign char_end = (cnt_clk == CHARGE_CLK)?1'b1:1'b0;//产生应答信号
assign Wr_ack = ((state == WRITE) || ( (state == WR_DATA) && (cnt_clk <= (Wr_burst_len - 2'd2)) ))?1'b1 : 1'b0;//结束信号
assign Wr_end = (state == WR_END)?1'b1:1'b0;//输出信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
begin
Wr_ba <= 2'd0;
Wr_cmd <= 4'd0;
Wr_sdram_addr <= 13'd0;
end
else
begincase(state)WR_IDLE,ACTIVE_WAIT,WR_CHAR_WAIT,WR_END:beginWr_ba <= 2'b11;Wr_cmd <= NONE_CMD;Wr_sdram_addr <= 13'h1fff;endWR_ACTIVE: //写激活指令beginWr_ba <= Wr_addr[23:22];Wr_cmd <= ACTIVE_CMD;Wr_sdram_addr <= Wr_addr[21:9];       //行地址endWRITE:beginWr_ba <= Wr_addr[23:22];Wr_cmd <= WR_CMD;Wr_sdram_addr <= {4'b0000,Wr_addr[8:0]};       //行地址高4位清零 写列地址end  WR_DATA:beginif(wr_data_end)Wr_cmd <= STOP;else     beginWr_ba <= 2'b11;Wr_cmd <= NONE_CMD;Wr_sdram_addr <= 13'h1fff;     endend          WR_CHAR:beginWr_ba <= Wr_addr[23:22];Wr_cmd <= CHARGE_CMD;Wr_sdram_addr <= 13'h0400;   enddefault : beginWr_ba <= 2'b11;Wr_cmd <= NONE_CMD;Wr_sdram_addr <= 13'h1fff;endendcase
end//写SDRAM使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Wr_sdram_en <= 1'b0;
else
Wr_sdram_en <= Wr_ack;assign Wr_sdram_data = (Wr_sdram_en == 1'b1)?Wr_data : 16'd0;endmodule

写模块仿真代码

`timescale  1ns/1nsmodule  sdram_wr_tb();//wire define
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram_init
wire    [3:0]   init_cmd        ;   //初始化阶段指令
wire    [1:0]   init_ba         ;   //初始化阶段L-Bank地址
wire    [12:0]  init_addr       ;   //初始化阶段地址总线
wire            init_end        ;   //初始化完成信号
//sdram_write
wire    [12:0]  write_addr      ;   //数据写阶段地址总线
wire    [1:0]   write_ba        ;   //数据写阶段L-Bank地址
wire    [3:0]   write_cmd       ;   //数据写阶段指令
wire    [15:0]  wr_sdram_data   ;   //数据写阶段写入SDRAM数据
wire            wr_sdram_en     ;   //数据写阶段写数据有效使能信号
wire            wr_end          ;   //数据写阶段一次突发写结束
wire            sdram_wr_ack    ;   //数据写阶段写响应
//sdram_addr
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
wire    [1:0]   sdram_ba        ;   //SDRAML-Bank地址
wire    [3:0]   sdram_cmd       ;   //SDRAM指令
wire    [15:0]  sdram_dq        ;   //SDRAM数据总线
//reg define
reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
reg             wr_en           ;   //写使能
reg     [15:0]  wr_data_in      ;   //写数据//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;//wr_en:写数据使能
always@(posedge clk_100m or negedge rst_n)if(!rst_n)wr_en   <=  1'b0;else    if(wr_end == 1'b1)wr_en   <=  1'b0;else    if(init_end == 1'b1)wr_en   <=  1'b1;elsewr_en   <=  wr_en;//wr_data_in:写数据
always@(posedge clk_100m or negedge rst_n)if(!rst_n)wr_data_in  <=  16'd0;else    if(wr_data_in == 16'd10)wr_data_in  <=  16'd0;else    if(sdram_wr_ack == 1'b1)wr_data_in  <=  wr_data_in + 1'b1;elsewr_data_in  <=  wr_data_in;//sdram_cmd,sdram_ba,sdram_addr
assign  sdram_cmd = (init_end == 1'b1) ? write_cmd : init_cmd;
assign  sdram_ba = (init_end == 1'b1) ? write_ba : init_ba;
assign  sdram_addr = (init_end == 1'b1) ? write_addr : init_addr;//wr_sdram_data
assign  sdram_dq = (wr_sdram_en == 1'b1) ? wr_sdram_data : 16'hz;CLK_STRAT CLK_STRAT_inst (  //IP核PLL.areset ( !sys_rst_n ),.inclk0 ( sys_clk ),.c0 (  clk_50m       ),.c1 (  clk_100m      ),.c2 (  clk_100m_shift),.locked ( locked ));SDRAM_INIT SDRAM_INIT(.Clk(clk_100m),.Rst_n(rst_n),.Init_cmd(init_cmd),.Init_ba(init_ba),.Init_adrr(init_addr),.Init_done(init_end)
);  SDRAM_WR SDRAM_WR(.Clk(clk_100m),.Rst_n(rst_n),.Init_end(init_end),.Wr_addr(24'h000_000), //初始地址.Wr_data(wr_data_in),.Wr_burst_len(10'd10),.Wr_en(wr_en),.Wr_ba(write_ba),.Wr_cmd(write_cmd),.Wr_sdram_addr(write_addr),.Wr_sdram_data(wr_sdram_data),.Wr_end(wr_end),.Wr_sdram_en(wr_sdram_en),.Wr_ack(sdram_wr_ack)
);
sdram_model_plus    sdram_model_plus_inst(.Dq     (sdram_dq       ),.Addr   (sdram_addr     ),.Ba     (sdram_ba       ),.Clk    (clk_100m_shift ),.Cke    (1'b1           ),.Cs_n   (sdram_cmd[3]   ),.Ras_n  (sdram_cmd[2]   ),.Cas_n  (sdram_cmd[1]   ),.We_n   (sdram_cmd[0]   ),.Dqm    (2'b0           ),.Debug  (1'b1           )
);
endmodule

写模块仿真图

PRE:预充电所有bank。
AREF:自动刷新8次。
LMR: 为Load Mode Register,即设置命令,用来配置设备参数。常用命令中的设置命令。
ACT:激活。
WRITE: 写。
BST:连续页面突发与BURST TERMINATE命令一起使用以产生任意突发长度。由于潜伏期在第7列提前。
可参考ALINX技术博客.

SDRAM读操作模块

SDRAM读模块框图


不带预充电的页突发读模式

SDRAM读模块时序

SDRAM读模块状态转移图

SDRAM读模块代码

module SDRAM_RD(Clk,Rst_n,Init_end,Rd_en,Rd_addr,Rd_data,Rd_burst_len,Rd_ack,Rd_end,Rd_cmd,Rd_ba,Rd_sdram_addr,Rd_sdram_data);input  Clk;
input   Rst_n;
input   Init_end;
input   Rd_en;
input   [23:0]Rd_addr;
input   [15:0]Rd_data;
input   [9:0]Rd_burst_len;output    Rd_ack;
output  Rd_end;
output  reg[3:0]Rd_cmd;
output  reg[1:0]Rd_ba;
output  reg[12:0]Rd_sdram_addr;
output  [15:0]Rd_sdram_data;reg [15:0]rd_data_reg;
reg [3:0] state;
reg [9:0]cnt_clk;
reg cnt_clk_en;
wire active_end;
wire cl_end;
wire rd_data_end;
wire char_end;
wire rd_b_end; //读突发结束信号localparam RD_IDLE     =4'b0000,//空闲态             RD_ACTIVE   =4'b0001,//读激活           ACTIVE_WAIT =4'b0010,//激活等待      READ        =4'b0011,//读命令        RD_CL       =4'b0100, //列潜伏等待期  RD_DATA     =4'b0101,//读数据           RD_CHAR     =4'b0110,//预充电           CHAR_WAIT   =4'b0111,//预充电等待     RD_END      =4'b1000;//读结束          parameter  CHARGE_CLK = 3'd2, //预充电等待周期ACTIVE_CLK = 3'd2, //激活等待周期   TCL_CLK    = 3'd3; //潜伏期localparam  NONE_CMD = 4'b0111, //空命令指令CHARGE_CMD = 4'b0010, //预充电指令  行地址传入RAS_n端(SDRAM )端ACTIVE_CMD = 4'b0011,//激活命令  第三位位低位行地址传入RAS_n端(SDRAM )端STOP_CMD = 4'b0110, //突发停止指令READ_CMD = 4'b0101; //读数据指令   列地址//数据打拍,同一时钟,同时钟变化
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)rd_data_reg <= 16'd0;
else rd_data_reg <= Rd_data;//主状态机
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)state <= 1'b0;
else
begincase(state)RD_IDLE    : //空闲态 state <= (Init_end && Rd_en) ? RD_ACTIVE : RD_IDLE; //三元运算符RD_ACTIVE  : //读激活    state <= ACTIVE_WAIT;ACTIVE_WAIT: //激活等待  state <= (active_end) ? READ : ACTIVE_WAIT;       READ       : //读命令       state <= RD_CL;RD_CL      :  //列潜伏等待期 state <= (cl_end) ? RD_DATA : RD_CL;     RD_DATA    : //读数据 state <= (rd_data_end) ? RD_CHAR : RD_DATA;RD_CHAR    : //预充电   state <= CHAR_WAIT;CHAR_WAIT  : //预充电等待state <= (char_end) ? RD_END : CHAR_WAIT;RD_END     : //读结束     state <= RD_IDLE;default : state <= RD_IDLE;endcase
end//使能计数器计数时钟
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_clk <= 1'b0;
else
if(cnt_clk_en)
cnt_clk <= 1'b0;
else
cnt_clk <= cnt_clk + 1'b1;//使能CLK计数信号
always @(*)
begincase(state)RD_IDLE,READ,RD_END:cnt_clk_en <= 1'b1;ACTIVE_WAIT:cnt_clk_en <= (active_end == 1'b1)?1'b1:1'b0;RD_CL:cnt_clk_en <= (cl_end == 1'b1)?1'b1:1'b0;  RD_DATA:cnt_clk_en <= (rd_data_end == 1'b1)?1'b1:1'b0; CHAR_WAIT:cnt_clk_en <= (char_end == 1'b1)?1'b1:1'b0;  default : cnt_clk_en <= 1'b0;endcase
end//end信号产生
assign active_end = ((state == ACTIVE_WAIT) && (cnt_clk  == ACTIVE_CLK)) ? 1'b1:1'b0;
assign cl_end = ((state == RD_CL) && (cnt_clk == (TCL_CLK - 1'b1))) ? 1'b1:1'b0;
assign rd_data_end = ((state == RD_DATA) && (cnt_clk == (Rd_burst_len + TCL_CLK - 1'b1)))? 1'b1:1'b0;
assign char_end = ((state == CHAR_WAIT) && (cnt_clk == CHARGE_CLK ))? 1'b1:1'b0;
assign rd_b_end = ((state == RD_DATA) && (cnt_clk == (Rd_burst_len - TCL_CLK - 1'b1)))? 1'b1:1'b0;//应答信号产生
assign Rd_ack = ((state == RD_DATA) && (cnt_clk >= 10'b0) && (cnt_clk < Rd_burst_len));//产生读结束标志信号 一个时钟周期
assign Rd_end = (state == RD_END)?1'b1:1'b0;//产生输出信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
beginRd_cmd <= NONE_CMD;Rd_ba <= 2'b11;Rd_sdram_addr <= 13'h1fff;
end
else
begincase(state)RD_IDLE,ACTIVE_WAIT,RD_CL,CHAR_WAIT,RD_END:beginRd_cmd <= NONE_CMD;Rd_ba <= 2'b11;Rd_sdram_addr <= 13'h1fff;          endRD_ACTIVE:  //读激活beginRd_cmd <= ACTIVE_CMD;Rd_ba <= Rd_addr[23:22];Rd_sdram_addr <= Rd_addr[21:9];   //激活行地址endREAD :    //读命令beginRd_cmd <= READ_CMD;Rd_ba <= Rd_addr[23:22];Rd_sdram_addr <= {4'b0000,Rd_addr[8:0]};  endRD_DATA:  //读数据beginif(rd_b_end)Rd_cmd <= STOP_CMD;else Rd_cmd <= NONE_CMD;Rd_ba <= 2'b11;Rd_sdram_addr <= 13'h1fff;               endRD_CHAR:   //预充电指令beginRd_cmd <= CHARGE_CMD;Rd_ba <= Rd_addr[23:22];Rd_sdram_addr <= 13'h0400;  enddefault :beginRd_cmd <= NONE_CMD;Rd_ba <= 2'b11;Rd_sdram_addr <= 13'h1fff;endendcase
end//输出读出的SDRAM数据
assign Rd_sdram_data = (Rd_ack  == 1'b1)?rd_data_reg : 16'b0;endmodule 

SDRAM读模块仿真代码

`timescale  1ns/1nsmodule  sdram_read_tb();//wire define
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram_init
wire    [3:0]   init_cmd        ;   //初始化阶段指令
wire    [1:0]   init_ba         ;   //初始化阶段L-Bank地址
wire    [12:0]  init_addr       ;   //初始化阶段地址总线
wire            init_end        ;   //初始化完成信号
//sdram_write
wire    [12:0]  write_addr      ;   //数据写阶段地址总线
wire    [1:0]   write_ba        ;   //数据写阶段L-Bank地址
wire    [3:0]   write_cmd       ;   //数据写阶段指令
wire    [15:0]  wr_sdram_data   ;   //数据写阶段写入SDRAM数据
wire            wr_sdram_en     ;   //数据写阶段写数据有效使能信号
wire            wr_end          ;   //数据写阶段一次突发写结束
//sdram_read
wire    [12:0]  read_addr       ;   //数据读阶段地址总线
wire    [1:0]   read_ba         ;   //数据读阶段L-Bank地址
wire    [3:0]   read_cmd        ;   //数据读阶段指令
wire    [15:0]  sdram_data_out  ;   //数据读阶段写入SDRAM数据
wire            rd_end          ;   //数据读阶段一次突发写结束
wire            sdram_wr_ack    ;   //数据写阶段写响应
//sdram_addr
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
wire    [1:0]   sdram_ba        ;   //SDRAML-Bank地址
wire    [3:0]   sdram_cmd       ;   //SDRAM指令
wire    [15:0]  sdram_dq        ;   //SDRAM数据总线wire    [12:0]  w_r_addr        ;   //数据读阶段地址总线
wire    [1:0]   w_r_ba          ;   //数据读阶段L-Bank地址
wire    [3:0]   w_r_cmd         ;   //数据读阶段指令//reg define
reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
reg             wr_en           ;   //写使能
reg     [15:0]  wr_data_in      ;   //写数据
reg             rd_en           ;   //读使能//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;//wr_en:写数据使能
always@(posedge clk_100m or negedge rst_n)if(rst_n == 1'b0)wr_en   <=  1'b1;else    if(wr_end == 1'b1)wr_en   <=  1'b0;elsewr_en   <=  wr_en;//wr_data_in:写数据
always@(posedge clk_100m or negedge rst_n)if(rst_n == 1'b0)wr_data_in  <=  16'd0;else    if(wr_data_in == 16'd10)wr_data_in  <=  16'd0;else    if(sdram_wr_ack == 1'b1)wr_data_in  <=  wr_data_in + 1'b1;elsewr_data_in  <=  wr_data_in;//rd_en:读数据使能
always@(posedge clk_100m or negedge rst_n)if(rst_n == 1'b0)rd_en   <=  1'b0;else    if(rd_end == 1'b1)rd_en   <=  1'b0;else    if(wr_en == 1'b0)rd_en   <=  1'b1;elserd_en   <=  rd_en;//sdram_cmd,sdram_ba,sdram_addr
assign  sdram_cmd = (init_end == 1'b1) ? w_r_cmd : init_cmd;
assign  sdram_ba = (init_end == 1'b1) ? w_r_ba : init_ba;
assign  sdram_addr = (init_end == 1'b1) ? w_r_addr : init_addr;//w_r_cmd,w_r_ba,w_r_addr
assign  w_r_cmd = (wr_en == 1'b1) ? write_cmd : read_cmd;
assign  w_r_ba = (wr_en == 1'b1) ? write_ba : read_ba;
assign  w_r_addr = (wr_en == 1'b1) ? write_addr : read_addr;//wr_sdram_data
assign  sdram_dq = (wr_sdram_en == 1'b1) ? wr_sdram_data : 16'hz;CLK_STRAT CLK_STRAT_inst (  //IP核PLL.areset ( !sys_rst_n ),.inclk0 ( sys_clk ),.c0 (  clk_50m       ),.c1 (  clk_100m      ),.c2 (  clk_100m_shift),.locked ( locked ));SDRAM_INIT SDRAM_INIT(.Clk(clk_100m),.Rst_n(rst_n),.Init_cmd(init_cmd),.Init_ba(init_ba),.Init_adrr(init_addr),.Init_done(init_end)
);  SDRAM_WR SDRAM_WR(.Clk(clk_100m),.Rst_n(rst_n),.Init_end(init_end),.Wr_addr(24'h000_000), //初始地址.Wr_data(wr_data_in),.Wr_burst_len(10'd10),.Wr_en(wr_en),.Wr_ba(write_ba),.Wr_cmd(write_cmd),.Wr_sdram_addr(write_addr),.Wr_sdram_data(wr_sdram_data),.Wr_end(wr_end),.Wr_sdram_en(wr_sdram_en),.Wr_ack(sdram_wr_ack)
);SDRAM_RD SDRAM_RD(.Clk(clk_100m),.Rst_n(rst_n),.Init_end(init_end),.Rd_en(rd_en),.Rd_addr(24'h000_000 ),.Rd_data(sdram_dq),.Rd_burst_len(10'd10 ),.Rd_ack(),.Rd_end(rd_end),.Rd_cmd(read_cmd),.Rd_ba(read_ba),.Rd_sdram_addr(read_addr),.Rd_sdram_data(sdram_data_out));sdram_model_plus    sdram_model_plus_inst(.Dq     (sdram_dq       ),.Addr   (sdram_addr     ),.Ba     (sdram_ba       ),.Clk    (clk_100m_shift ),.Cke    (1'b1           ),.Cs_n   (sdram_cmd[3]   ),.Ras_n  (sdram_cmd[2]   ),.Cas_n  (sdram_cmd[1]   ),.We_n   (sdram_cmd[0]   ),.Dqm    (2'b0           ),.Debug  (1'b1           ));endmodule

SDRAM读模块仿真波形

SDRAM判断模块

SDRAM判断模块设计图

SDRAM判断模块时序图

SDRAM判断模块状态转移图

SDRAM判断模块代码

module  SDRAM_JUDGE(    //判断读写自动刷新模块优先级模块  input   wire    Clk         ,input   wire    Rst_n       ,input   wire    [3:0]Init_cmd    ,//初始化input   wire    [1:0]Init_ba     ,input   wire    [12:0]Init_addr  ,input   wire    Init_end         ,input   wire    Re_req           ,//自刷新input   wire    [3:0]Re_cmd      ,input   wire    [12:0]Re_addr    ,input   wire    Re_end           ,input   wire    [1:0]Re_ba       ,input   wire    Wr_req           , //写input   wire    [3:0]Wr_cmd      ,input   wire    [12:0]Wr_addr    ,input   wire    [15:0]Wr_data    ,input   wire    Wr_end           ,input   wire    [1:0]Wr_ba       ,input   wire    Wr_sdram_en      ,input   wire    Rd_req           ,     //读input   wire    [12:0]Rd_addr    ,input   wire    Rd_end           ,input   wire    [1:0]Rd_ba       ,input   wire    [3:0]Rd_cmd      ,output  wire    Sdram_cke        ,        //SDRAM接口output  wire    Sdram_cs_n       ,output  wire    Sdram_ras_n      ,output  wire    Sdram_cas_n      ,output  wire    Sdram_we_n       ,output  reg    [1:0]Sdram_ba    ,output  reg    [12:0]Sdram_addr ,inout  wire    [15:0]Sdram_dq   ,output  reg     Re_en            , //使能信号output  reg     Wr_en            ,   output  reg     Rd_en
);
//定义状态
localparam   IDLE    = 5'b000_01,  //独热码JUDGE   = 5'b000_10,READ    = 5'b001_00,WRITE   = 5'b010_00,REFRESH = 5'b100_00;//定义命令
localparam  NONE_CMD = 4'b0111; //空命令指令reg [4:0]ju_state;
reg [3:0]sdram_cmd;//主状态机
always @(posedge Clk or negedge Rst_n)if(!Rst_n)ju_state <= IDLE;elsebegincase(ju_state)IDLE    :beginif(Init_end)ju_state <= JUDGE;else ju_state <= IDLE;endJUDGE   :beginif(Re_req)ju_state <= REFRESH;elseif(Rd_req)ju_state <= READ;else if(Wr_req)ju_state <= WRITE;elseju_state <= JUDGE;endREAD    :beginif(Rd_end)ju_state <= JUDGE;elseju_state <= READ;endWRITE   :beginif(Wr_end)ju_state <= JUDGE;elseju_state <= WRITE;end REFRESH :beginif(Re_end)ju_state <= JUDGE;elseju_state <= REFRESH;enddefault: ju_state <= JUDGE;endcase end//自动刷新使能信号
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Re_en <= 1'b0;elseif((ju_state == JUDGE) && (Re_req == 1'b1))Re_en <= 1'b1;elseif(Re_end)Re_en <= 1'b0;//写使能信号
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Wr_en <= 1'b0;elseif((ju_state == JUDGE) && (Re_req == 1'b0) && (Wr_req == 1'b1))Wr_en <= 1'b1;elseif(Wr_end)Wr_en <= 1'b0;//读使能信号always @(posedge Clk or negedge Rst_n)if(!Rst_n)Rd_en <= 1'b0;elseif((ju_state == JUDGE) && (Re_req == 1'b0) && (Rd_req == 1'b1))Rd_en <= 1'b1;elseif(Rd_end)Rd_en <= 1'b0;//Sdram_ba Sdram_addr  sdram_cmd输出
always @(*)
begincase(ju_state)JUDGE:beginSdram_addr<= 13'h1fff;Sdram_ba  <= 2'b11;sdram_cmd <= NONE_CMD;endIDLE:beginSdram_addr<= Init_addr;Sdram_ba  <= Init_ba;sdram_cmd <= Init_cmd;endWRITE:beginSdram_addr<= Wr_addr;Sdram_ba  <= Wr_ba;sdram_cmd <= Wr_cmd;endREAD:beginSdram_addr<= Rd_addr;Sdram_ba  <= Rd_ba;sdram_cmd <= Rd_cmd;endREFRESH:beginSdram_addr<= Re_addr;Sdram_ba  <= Re_ba;sdram_cmd <= Re_cmd;enddefault:beginSdram_addr<= 13'h1fff;Sdram_ba  <= 2'b11;sdram_cmd <= NONE_CMD;endendcaseend//Sdram_dq
assign Sdram_dq = (Wr_sdram_en) ? Wr_data : 16'bz;
//片选信号,行地址选通信号,列地址选通信号,写使能信号
assign {Sdram_cs_n , Sdram_ras_n , Sdram_cas_n ,  Sdram_we_n} = sdram_cmd;assign Sdram_cke = 1'b1;endmodule  

SDRAM判断模块状态转移图

SDRAM主控制模块

SDRAM控制模块图

SDRAM控制模块时序

SDRAM控制模块代码

module SDRAM_CTRL(input   wire           Clk           ,input   wire           Rst_n         ,input   wire           Rd_req        ,                                            //读input   wire  [23:0]   Rd_addr       ,    //24位宽 行地址列地址bank地址 写入的首地址input   wire  [9:0]    Rd_burst_len  ,output  wire           Rd_ack        ,output  wire   [15:0]  Sdram_data_out,//从SDRAM读出的数据input   wire           Wr_req        ,                                     //写input   wire  [23:0]   Wr_addr       ,input   wire  [15:0]   Wr_sdram_data       ,//写入SDRAM的数据input   wire  [9:0]    Wr_burst_len  ,output  wire           Wr_ack        ,output  wire           Init_end      ,   //SDRAM 初始化完成标志output  wire           Sdram_cke     ,                                 //与SDRAM的硬件接口output  wire           Sdram_cs_n    ,output  wire           Sdram_cas_n   ,output  wire           Sdram_ras_n   ,output  wire           Sdram_we_n    ,output  wire   [1:0]   Sdram_ba      ,output  wire   [12:0]  Sdram_addr    ,inout   wire   [15:0]  Sdram_dq            // SDRAM 数据总线
);wire  [3:0]         Init_cmd      ;//初始化wire  [1:0]         Init_ba       ;wire  [12:0]        Init_addr     ;//初始化行地址 8192行wire                Rd_en         ;  //读wire  [15:0]        Rd_data       ;wire                Rd_end        ;wire  [3:0]         Rd_cmd        ;wire  [1:0]         Rd_ba         ;wire  [12:0]        Rd_sdram_addr ;wire                Wr_en         ;  //写wire  [1:0]         Wr_ba         ;wire  [3:0]         Wr_cmd        ;wire  [12:0]        Wr_sdram_addr ;wire  [15:0]        WR_MID_DATA ;    //wire                Wr_end        ;wire                Wr_sdram_en   ;wire  [3:0]         Re_cmd        ;//自动刷新wire  [12:0]        Re_addr       ;wire  [1:0]         Re_ba         ;wire                Re_end        ;wire                Re_req        ;wire                Re_en         ;SDRAM_INIT      SDRAM_INIT(.Clk            (Clk),.Rst_n          (Rst_n),.Init_cmd       (Init_cmd ),.Init_ba        (Init_ba  ),.Init_addr      (Init_addr),.Init_done      (Init_end)
);SDRAM_RD        SDRAM_RD(.Clk            (Clk  ),.Rst_n          (Rst_n),.Init_end       (Init_end     ),.Rd_en          (Rd_en        ),.Rd_addr        (Rd_addr      ),.Rd_data        (Sdram_dq      ),.Rd_burst_len   (Rd_burst_len ),.Rd_ack         (Rd_ack       ),.Rd_end         (Rd_end       ),.Rd_cmd         (Rd_cmd       ),.Rd_ba          (Rd_ba        ),.Rd_sdram_addr  (Rd_sdram_addr),.Rd_sdram_data  (Sdram_data_out));SDRAM_WR        SDRAM_WR(.Clk            (Clk  ) ,.Rst_n          (Rst_n) ,.Init_end       (Init_end     ) ,.Wr_addr        (Wr_addr      ) ,  .Wr_data        (Wr_sdram_data      ) ,.Wr_burst_len   (Wr_burst_len ) ,.Wr_en          (Wr_en        ) ,.Wr_ba          (Wr_ba        ) ,.Wr_cmd         (Wr_cmd       ) ,.Wr_sdram_addr  (Wr_sdram_addr) ,.Wr_sdram_data  (WR_MID_DATA  ) ,     //.Wr_end         (Wr_end       ) ,.Wr_sdram_en    (Wr_sdram_en  ) ,.Wr_ack         (Wr_ack       )
);SDRAM_REFRESH       SDRAM_REFRESH(.Clk         (Clk  ),.Rst_n       (Rst_n),.Init_end    (Init_end),.Re_en       (Re_en   ),.Re_cmd      (Re_cmd  ),.Re_addr     (Re_addr ),.Re_ba       (Re_ba   ),.Re_end      (Re_end  ),.Re_req      (Re_req  )
);SDRAM_JUDGE     SDRAM_JUDGE(     //判断读写自动刷新模块优先级模块  .Clk         (Clk)         ,.Rst_n       (Rst_n)         ,.Init_cmd    (Init_cmd)         ,//初始化.Init_ba     (Init_ba)         ,.Init_addr   (Init_addr)        ,.Init_end    (Init_end)              ,.Re_req      (Re_req)              ,//自刷新.Re_cmd      (Re_cmd)         ,.Re_addr     (Re_addr)        ,.Re_end      (Re_end)              ,.Re_ba       (Re_ba)         ,.Wr_req      (Wr_req)              , //写.Wr_cmd      (Wr_cmd)         ,.Wr_addr     (Wr_sdram_addr    )      ,.Wr_data     (WR_MID_DATA    )      ,    ///.Wr_end      (Wr_end     )            ,.Wr_ba       (Wr_ba      )       ,.Wr_sdram_en (Wr_sdram_en)     ,.Rd_req      (Rd_req )             ,     //读.Rd_addr     (Rd_sdram_addr)       ,.Rd_end      (Rd_end )             ,.Rd_ba       (Rd_ba  )        ,.Rd_cmd      (Rd_cmd )        ,.Sdram_cke   (Sdram_cke  )     ,        //SDRAM接口.Sdram_cs_n  (Sdram_cs_n )     ,.Sdram_ras_n (Sdram_ras_n)     ,.Sdram_cas_n (Sdram_cas_n)     ,.Sdram_we_n  (Sdram_we_n )     ,.Sdram_ba    (Sdram_ba   )       ,.Sdram_addr  (Sdram_addr )      ,.Sdram_dq    (Sdram_dq   )      ,.Re_en       (Re_en)     , //使能信号.Wr_en       (Wr_en)     ,   .Rd_en       (Rd_en)
);
endmodule

SDRAM控制模块仿真波形

自动刷新后写,写完读。读后自动刷新。

写入的数据wr_data_in为1 2 3 4 5 6 7 8 9 10,SDRAM输出的数据sdram_data_out1 2 3 4 5 6 7 8 9 10。

SDRAM——FIFO控制模块

将准备输入输出SDRAM的数据做缓存。

SDRAM——FIFO控制模块图

SDRAM——FIFO控制模块时序

SDRAM——FIFO控制模块代码

module FIFO_CTRL(input       wire                Clk               ,
input       wire                Rst_n             ,
//写FIFO
input       wire                Wr_fifo_wr_clk    ,
input       wire                Wr_fifo_wr_req    ,
input       wire     [15:0]     Wr_fifo_wr_data   ,      //写入写FIFO中的数据
input       wire     [23:0]     Sdram_wr_b_addr   ,
input       wire     [23:0]     Sdram_wr_e_addr   ,
input       wire     [9:0]      Wr_burst_len      ,
input       wire                Wr_rst            ,      //写复位信号
//读FIFO
input       wire                Rd_fifo_rd_clk    ,
input       wire                Rd_fifo_rd_req    ,
input       wire     [23:0]     Sdram_rd_b_addr   ,
input       wire     [23:0]     Sdram_rd_e_addr   ,
input       wire     [9:0]      Rd_burst_len      ,      //读突发长度
input       wire                Rd_rst            ,      //读复位信号
output      wire     [15:0]     Rd_fifo_rd_data   ,      //读FIFO中读出的数据
output      wire     [9:0]      Rd_fifo_num       ,      //读FIFO中存在的数据个数
//SDRAM写信号
input       wire                Sdram_wr_ack      ,      //SDRAM写响应
output      reg                 Sdram_wr_req      ,      //写请求信号
output      wire      [15:0]    Sdram_data_in     ,      //从写FIFO中读出数据传给SDRAM
output      reg       [23:0]    Sdram_wr_addr     ,      //SDRAM写地址
//SDRAM读信号
input       wire      [15:0]    Sdram_data_out    ,      //SDRAM读出的信号
input       wire                Sdram_rd_ack      ,      //SDRAM读回应信号
output      reg                 Sdram_rd_req      ,      //读请求信号
output      reg       [23:0]    Sdram_rd_addr     ,      //读地址input       wire                Init_end          ,      //初始化结束信号
input       wire                Rd_value                 //写有效信号);wire         [9:0]      wr_fifo_num               ;      //写FIFO中的数据个数
reg                     rd_ack_dly                ;      //读应答信号打拍
reg                     wr_ack_dly                ;      //写应答信号打拍
wire                    rd_ack_dly_fall           ;      //读应答信号下降沿
wire                    wr_ack_dly_fall           ;      //写应答信号下降沿//应答信号打拍
always @(posedge Clk or negedge Rst_n)if(!Rst_n)beginrd_ack_dly <= 1'b0;wr_ack_dly <= 1'b0;endelsebeginrd_ack_dly <= Sdram_rd_ack;wr_ack_dly <= Sdram_wr_ack;end//应答信号下降沿
assign      rd_ack_dly_fall   =   (!Sdram_rd_ack && rd_ack_dly)  ;
assign      wr_ack_dly_fall   =   (!Sdram_wr_ack && wr_ack_dly)  ;//写地址更新  写优先级高于读
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Sdram_wr_addr <= 1'b0;elseif(Wr_rst)Sdram_wr_addr <= Sdram_wr_b_addr;elseif(wr_ack_dly_fall)  //一次突发写结束,更改写地址beginif(Sdram_wr_addr < (Sdram_wr_e_addr - Wr_burst_len))Sdram_wr_addr <= Sdram_wr_addr + Wr_burst_len;elseSdram_wr_addr <= Sdram_wr_b_addr; //到达末地址,回到写起始地址endelseSdram_wr_addr <= Sdram_wr_addr;//读地址更新
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Sdram_rd_addr <= 1'b0;elseif(Wr_rst)Sdram_rd_addr <= Sdram_rd_b_addr;elseif(rd_ack_dly_fall)  //一次突发写结束,更改写地址beginif(Sdram_rd_addr < (Sdram_rd_e_addr - Rd_burst_len))Sdram_rd_addr <= Sdram_rd_addr + Rd_burst_len;elseSdram_rd_addr <= Sdram_rd_b_addr; //到达末地址,回到写起始地址endelseSdram_rd_addr <= Sdram_rd_addr;//读写请求信号
always @(posedge Clk or negedge Rst_n)if(!Rst_n)beginSdram_wr_req <= 1'b0;Sdram_rd_req <= 1'b0;endelse                                          //优先执行写操作,防止写入SDRAM中的数据丢失if(Init_end)                                  //初始化完成后响应读写请求beginif(wr_fifo_num >= Wr_burst_len)       //写FIFO中的数据量达到写突发长度beginSdram_wr_req <= 1'b1;Sdram_rd_req <= 1'b0;endelseif((Rd_fifo_num < Rd_burst_len) && Rd_value)  //读FIFO中的数据量小于读突发长度,且读使能信号有效beginSdram_wr_req <= 1'b0;Sdram_rd_req <= 1'b1;endelsebeginSdram_wr_req <= 1'b0;Sdram_rd_req <= 1'b0;endendelsebeginSdram_wr_req <= 1'b0;Sdram_rd_req <= 1'b0;endFIFO_DATA  FIFO_WR_DATA_inst (    //写FIFO列化      读时钟要 大于等于 写时钟 读位宽要大于写位宽//用户端口.wrclk   (  Wr_fifo_wr_clk       ),.wrreq   (  Wr_fifo_wr_req       ),.data    (  Wr_fifo_wr_data      ),//SDRAM端口.rdclk   (  Clk      ),.rdreq   (  Sdram_wr_ack      ),.q       (  Sdram_data_in      ),.aclr    (  !Rst_n || Wr_rst     ),.rdusedw (  wr_fifo_num      ),  //FIFO中的数据量.wrusedw (        ));FIFO_DATA  FIFO_RD_DATA_inst (    //读FIFO列化      读时钟要 大于等于 写时钟 读位宽要大于写位宽//SDRAM端口.wrclk   (    Clk             ),.wrreq   (  Sdram_rd_ack       ),.data    (  Sdram_data_out      ),//用户端口.rdclk   (  Rd_fifo_rd_clk          ),.rdreq   (  Rd_fifo_rd_req       ),.q       (  Rd_fifo_rd_data       ),.aclr    (  !Rst_n || Rd_rst    ),.rdusedw (        ),.wrusedw (  Rd_fifo_num      )  //FIFO中的数据量);
endmodule

SDRAM_TOP顶层模块

SDRAM顶层模块设计图

SDRAM顶层模块代码

module SDRAM_TOP(input       wire                Clk               ,
input       wire                Rst_n             ,
input       wire                Clk_out           ,     //输入相位偏移时钟
//写FIFO
input       wire                Wr_fifo_wr_clk    ,
input       wire                Wr_fifo_wr_req    ,
input       wire     [15:0]     Wr_fifo_wr_data   ,      //写入写FIFO中的
input       wire     [23:0]     Sdram_wr_b_addr   ,
input       wire     [23:0]     Sdram_wr_e_addr   ,
input       wire     [9:0]      Wr_burst_len      ,
input       wire                Wr_rst            ,      //写复位信号
//读FIFO
input       wire                Rd_fifo_rd_clk    ,
input       wire                Rd_fifo_rd_req    ,
input       wire     [23:0]     Sdram_rd_b_addr   ,
input       wire     [23:0]     Sdram_rd_e_addr   ,
input       wire     [9:0]      Rd_burst_len      ,      //读突发长度
input       wire                Rd_rst            ,      //读复位信号
input       wire                Rd_value          ,
output      wire     [15:0]     Rd_fifo_rd_data   ,      //读FIFO中读出的数据
output      wire     [9:0]      Rd_fifo_num       ,      //读FIFO中存在的数据个数
//与SDRAM的硬件接口
output      wire                Sdram_clk         ,
output      wire                Sdram_cke         ,
output      wire                Sdram_cs_n        ,
output      wire                Sdram_cas_n       ,
output      wire                Sdram_ras_n       ,
output      wire                Sdram_we_n        ,
output      wire     [1:0]      Sdram_ba          ,
output      wire     [12:0]     Sdram_addr        ,
inout       wire     [15:0]     Sdram_dq          ,     // SDRAM 数据总线
output      wire     [1:0]      Sdram_dqm           );wire       [23:0]        sdram_rd_addr        ;
wire                     sdram_rd_req         ;
wire                     init_end             ;
wire                     sdram_wr_req         ;
wire       [23:0]        sdram_wr_addr        ;
wire       [15:0]        sdram_data_in        ;
wire                     sdram_wr_ack         ;
wire                     sdram_rd_ack         ;
wire       [15:0]        sdram_data_out       ;assign  Sdram_dqm   =   2'b00;
//sdram_clk:SDRAM芯片时钟
assign  Sdram_clk   =   Clk_out; SDRAM_CTRL  SDRAM_CTRL_inst(.Clk               (Clk             )  ,.Rst_n             (Rst_n           )  ,
//SDRAM 控制器读端口.Rd_req            (sdram_rd_req    )  ,//读SDRAM请求信号.Rd_addr           (sdram_rd_addr   )  ,    //24位宽 行地址列地址bank地址 写入的首地址.Rd_burst_len      (Rd_burst_len    )  ,.Init_end          (init_end        ),   //SDRAM 初始化完成标志
//SDRAM 控制器写端口.Wr_req            (sdram_wr_req    )  ,       //写SDRAM请求信号.Wr_addr           (sdram_wr_addr   )  ,.Wr_sdram_data     (sdram_data_in  )  ,.Wr_burst_len      (Wr_burst_len    )  ,.Wr_ack            (sdram_wr_ack    )  ,   //写SDRAM响应信号.Rd_ack            (sdram_rd_ack    )  ,   //读SDRAM响应信号
//从SDRAM读出的数据.Sdram_data_out    (sdram_data_out  )  ,
//与SDRAM的硬件接口.Sdram_cke         (Sdram_cke       )  ,// SDRAM 时钟有效信号.Sdram_cs_n        (Sdram_cs_n      )  ,      // SDRAM 片选信号.Sdram_cas_n       (Sdram_cas_n     )  ,     // SDRAM 列地址选通脉冲.Sdram_ras_n       (Sdram_ras_n     )  ,     // SDRAM 行地址选通脉冲.Sdram_we_n        (Sdram_we_n      )  ,      // SDRAM 写允许位.Sdram_ba          (Sdram_ba        )  ,        // SDRAM L-Bank地址线.Sdram_addr        (Sdram_addr      )  ,        // SDRAM 地址总线.Sdram_dq          (Sdram_dq        )           // SDRAM 数据总线
);FIFO_CTRL   FIFO_CTRL_inst(.Clk               (Clk   )      ,.Rst_n             (Rst_n )      ,
//写FIFO                  .Wr_fifo_wr_clk    (Wr_fifo_wr_clk)       ,.Wr_fifo_wr_req    (Wr_fifo_wr_req )      ,.Wr_fifo_wr_data   (Wr_fifo_wr_data)      ,      //写入写FIFO中的数据.Sdram_wr_b_addr   (Sdram_wr_b_addr)      ,.Sdram_wr_e_addr   (Sdram_wr_e_addr)      ,.Wr_burst_len      (Wr_burst_len   )      ,.Wr_rst            (Wr_rst         )      ,      //写复位信号
//读FIFO                   .Rd_fifo_rd_clk    (Rd_fifo_rd_clk )      ,.Rd_fifo_rd_req    (Rd_fifo_rd_req )      ,.Sdram_rd_b_addr   (Sdram_rd_b_addr)      ,.Sdram_rd_e_addr   (Sdram_rd_e_addr)      ,.Rd_burst_len      (Rd_burst_len   )      ,      //读突发长度.Rd_rst            (Rd_rst)               ,      //读复位信号.Rd_fifo_rd_data   (Rd_fifo_rd_data)      ,      //读FIFO中读出的数据.Rd_fifo_num       (Rd_fifo_num    )      ,      //读FIFO中存在的数据个数
//SDRAM写信号                      .Sdram_wr_ack      (sdram_wr_ack)      ,      //SDRAM写响应.Sdram_wr_req      (sdram_wr_req)      ,      //写请求信号.Sdram_data_in     (sdram_data_in)      ,      //从写FIFO中读出数据传给SDRAM.Sdram_wr_addr     (sdram_wr_addr)      ,      //SDRAM写地址
//SDRAM读信号                     .Sdram_data_out    (sdram_data_out)      ,      //SDRAM读出的信号.Sdram_rd_ack      (sdram_rd_ack)      ,      //SDRAM读回应信号.Sdram_rd_req      (sdram_rd_req)      ,      //读请求信号.Sdram_rd_addr     (sdram_rd_addr)      ,      //读地址.Init_end          (init_end)      ,      //初始化结束信号.Rd_value          (Rd_value)             //写有效信号);endmodule

SDRAM顶层模块仿真代码

`timescale  1ns/1nsmodule  SDRAM_TOP_tb();//wire define
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram
wire            sdram_clk       ;   //SDRAM时钟
wire            sdram_cke       ;   //SDRAM时钟使能信号
wire            sdram_cs_n      ;   //SDRAM片选信号
wire            sdram_ras_n     ;   //SDRAM行选通信号
wire            sdram_cas_n     ;   //SDRAM列选题信号
wire            sdram_we_n      ;   //SDRAM写使能信号
wire    [1:0]   sdram_ba        ;   //SDRAM L-Bank地址
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
wire    [15:0]  sdram_dq        ;   //SDRAM数据总线
wire            sdram_dqm       ;   //SDRAM数据总线wire    [9:0]   rd_fifo_num     ;   //fifo_ctrl模块中读fifo中的数据量
wire    [15:0]  rfifo_rd_data   ;   //fifo_ctrl模块中读fifo读数据//reg define
reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
reg             wr_en           ;   //写使能
reg             wr_en_dly       ;   //写使能打拍
reg     [15:0]  wr_data_in      ;   //写数据
reg             rd_en           ;   //读使能
reg     [2:0]   cnt_wr_wait     ;   //数据写入间隔计数
reg     [3:0]   cnt_rd_data     ;   //读出数据计数
reg             wr_data_flag    ;   //fifo_ctrl模块中写fifo写使能
reg             read_valid      ;   //读有效信号//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量//重定义自动刷新模块自动刷新间隔时间计数最大值
defparam SDRAM_TOP_inst.SDRAM_CTRL_inst.SDRAM_REFRESH.CNT_MAX = 39;//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;//wr_en:写数据使能
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)wr_en   <=  1'b1;else    if(wr_data_in == 10'd10)wr_en   <=  1'b0;elsewr_en   <=  wr_en;//cnt_wr_wait:数据写入间隔计数
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)cnt_wr_wait <=  3'd0;else    if(wr_en == 1'b1)cnt_wr_wait <=  cnt_wr_wait + 1'b1;elsecnt_wr_wait <=  3'd0;//wr_data_flag:fifo_ctrl模块中写fifo写使能
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)wr_data_flag    <=  1'b0;else    if(cnt_wr_wait == 3'd7)wr_data_flag    <=  1'b1;elsewr_data_flag    <=  1'b0;//read_valid:数据读使能
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)read_valid  <=  1'b1;else    if(rd_fifo_num == 10'd10)read_valid  <=  1'b0;//wr_en_dly:写数据使能打拍
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)wr_en_dly   <=  1'b0;elsewr_en_dly   <=  wr_en;//wr_data_in:写数据
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)wr_data_in  <=  16'd0;else    if(cnt_wr_wait == 3'd7)wr_data_in  <=  wr_data_in + 1'b1;elsewr_data_in  <=  wr_data_in;//rd_en:读数据使能
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)rd_en   <=  1'b0;else    if(cnt_rd_data == 4'd9)rd_en   <=  1'd0;else    if((wr_en == 1'b0) && (rd_fifo_num == 10'd10))rd_en   <=  1'b1;elserd_en   <=  rd_en;//cnt_rd_data:读出数据计数
always@(posedge clk_50m or negedge rst_n)if(rst_n == 1'b0)cnt_rd_data <=  4'd0;else    if(rd_en == 1'b1)cnt_rd_data <=  cnt_rd_data + 1'b1;elsecnt_rd_data <=  4'd0;CLK_STRAT   CLK_STRAT_inst (  //IP核PLL.areset ( !sys_rst_n ),.inclk0 ( sys_clk ),.c0 (  clk_50m       ),.c1 (  clk_100m      ),.c2 (  clk_100m_shift),.locked ( locked ));SDRAM_TOP       SDRAM_TOP_inst(.Clk               (clk_100m       )        ,.Rst_n             (rst_n          )        ,.Clk_out           (clk_100m_shift )        ,     //输入相位偏移时钟
//用户写端口          .Wr_fifo_wr_clk    (clk_50m        )        ,    .Wr_fifo_wr_req    (wr_data_flag   )        ,.Wr_fifo_wr_data   (wr_data_in     )        ,      //写入写FIFO中的.Sdram_wr_b_addr   (24'd0          )        ,.Sdram_wr_e_addr   (24'd10         )        ,.Wr_burst_len      (10'd10         )        ,.Wr_rst            (~rst_n         )        ,      //写复位信号
//用户读端口            .Rd_fifo_rd_clk    (clk_50m        )        ,.Rd_fifo_rd_req    (rd_en          )        ,.Rd_fifo_rd_data   (rfifo_rd_data  )        ,      //读FIFO中读出的数据.Sdram_rd_b_addr   (24'd0          )        ,.Sdram_rd_e_addr   (24'd10         )        ,.Rd_burst_len      (10'd10         )        ,      //读突发长度.Rd_rst            (~rst_n         )        ,      //读复位信号.Rd_fifo_num       (rd_fifo_num    )        ,      //读FIFO中存在的数据个数
//用户控制端口.Rd_value          (read_valid     )        ,
//SDRAM 芯片接口           .Sdram_clk         (sdram_clk      )        ,.Sdram_cke         (sdram_cke      )        ,                                .Sdram_cs_n        (sdram_cs_n     )        ,.Sdram_ras_n       (sdram_ras_n    )        ,.Sdram_cas_n       (sdram_cas_n    )        ,.Sdram_we_n        (sdram_we_n     )        ,.Sdram_ba          (sdram_ba       )        ,.Sdram_addr        (sdram_addr     )        ,.Sdram_dq          (sdram_dq       )        ,     // SDRAM 数据总线.Sdram_dqm         (sdram_dqm      )
);sdram_model_plus    sdram_model_plus_inst(.Dq     (sdram_dq       ),.Addr   (sdram_addr     ),.Ba     (sdram_ba       ),.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    (2'b0           ),.Debug  (1'b1           ));endmodule

SDRAM顶层模块仿真图

写入的数据wr_data_in与读出的数据rfifo_rd_data都为1 2 3 4 5 6 7 8 9 10。

FIFO_READ模块

SDRAM读出的数据匹配串口发送的速率;将SDRAM读出的数据做缓存,计数比特个数,满足一次串口发送条件时将书数据传给tx模块。

FIFO_READ模块设计图

FIFO_READ模块设计时序

FIFO_READ模块代码

module  FIFO_READ(input           wire         Clk                ,
input           wire         Rst_n              ,
input           wire  [9:0]  Rd_fifo_num        ,
input           wire  [7:0]  Rd_fifo_rd_data    ,
input           wire  [9:0]  Burst_num          ,             output          reg          Rd_en               ,
output          wire  [7:0]  Tx_data             ,
output          reg          Tx_flag
);reg             rd_en_dly        ;
wire    [9:0]   data_num         ;
reg             rd_value         ;
reg     [12:0]  baud_cnt         ;
reg             bit_flag         ;
reg     [3:0]   bit_cnt          ;
reg             read_fifo_en     ;
reg     [9:0]   read_cnt         ;parameter   BAUD_CNT_END     =   13'd433     ;//读SDRAM读FIFO使能信号产生
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Rd_en   <=  1'b0;elseif(Rd_fifo_num == Burst_num)Rd_en   <=  1'b1;elseif(data_num ==  (Burst_num - 2'd2))Rd_en   <=  1'b0;
//读SDRAM读FIFO使能信号延迟一周期 写入READ_FIFO使能信号
always @(posedge Clk or negedge Rst_n)if(!Rst_n)rd_en_dly   <=  1'b0;elserd_en_dly   <=  Rd_en;
//读有效标志信号
always @(posedge Clk or negedge Rst_n)if(!Rst_n)rd_value    <=  1'b0;elseif(data_num ==  Burst_num)rd_value    <=  1'b1;elseif(read_cnt ==  Burst_num)rd_value    <=  1'b0;elserd_value    <=  rd_value;
//计数比特时钟周期个数
always @(posedge Clk or negedge Rst_n)if(!Rst_n)baud_cnt    <= 1'b0;elseif(rd_value)beginif(baud_cnt ==  BAUD_CNT_END)baud_cnt    <=  1'b0;    elsebaud_cnt    <=  baud_cnt    +   1'b1;endelsebaud_cnt    <=  1'b0;//计数比特周期数标志信号产生
always @(posedge Clk or negedge Rst_n)if(!Rst_n)bit_flag    <= 1'b0;elseif(baud_cnt ==  (BAUD_CNT_END >> 1))bit_flag    <= 1'b1;elsebit_flag    <= 1'b0;
//计数比特标志信号个数
always @(posedge Clk or negedge Rst_n)if(!Rst_n)bit_cnt     <= 1'b0;elseif(bit_flag)beginif(bit_cnt  ==  4'd10)bit_cnt <=  1'b0;elsebit_cnt <=  bit_cnt  +  1'b1;  endelsebit_cnt <=  bit_cnt;
//READ_FIFO读使能信号产生
always @(posedge Clk or negedge Rst_n)if(!Rst_n)read_fifo_en    <=  1'b0;elseif(bit_flag &&  (bit_cnt == 4'd10))read_fifo_en    <=  1'b1;elseread_fifo_en    <=  1'b0;
//READ_FIFO读数据个数计数
always @(posedge Clk or negedge Rst_n)if(!Rst_n)read_cnt    <=  1'b0;elseif(read_cnt ==  Burst_num)read_cnt    <=  10'd0;elseif(read_fifo_en)read_cnt    <=  read_cnt + 1'b1;
//Tx输出使能信号产生
always @(posedge Clk or negedge Rst_n)if(!Rst_n)Tx_flag <=  1'b0;elseTx_flag <=  read_fifo_en;READ    READ_inst (.clock (   Clk      ),.data  (   Rd_fifo_rd_data      ),.rdreq (   read_fifo_en      ),.wrreq (   rd_en_dly      ),.q     (   Tx_data       ),.usedw (   data_num      ));endmodule

UART_SDRAM模块

UART_SDRAM模块设计图

UART_SDRAM模块代码

module  UART_SDRAM(input       wire                Clk               ,
input       wire                Rst_n             ,
input       wire                Rx                ,
input       wire   [2:0]        Bps_set           ,
//与SDRAM的硬件接口
output      wire                Sdram_clk         ,
output      wire                Sdram_cke         ,
output      wire                Sdram_cs_n        ,
output      wire                Sdram_cas_n       ,
output      wire                Sdram_ras_n       ,
output      wire                Sdram_we_n        ,
output      wire     [1:0]      Sdram_ba          ,
output      wire     [12:0]     Sdram_addr        ,
inout       wire     [15:0]     Sdram_dq          ,
output      wire     [1:0]      Sdram_dqm         ,output      wire                Tx
);parameter   DATA_NUM    =   10'd5           ;   //写入SDRAM数据个数
parameter   WAIT_MAX    =   16'd750         ;   //等待计数最大值wire            clk_50M          ;
wire            clk_100M         ;
wire            clk_100M_shift   ;
wire            locked           ;
wire            sys_rst_n        ;
wire    [7:0]   rx_data_out      ;
wire            done_rx          ;
wire            tx_flag          ;
wire    [7:0]   tx_data          ;
wire    [9:0]   rd_fifo_num      ;
wire    [7:0]   rd_fifo_rd_data  ;
wire            rd_fifo_rd_en    ;
reg     [23:0]  data_num         ;
reg             read_valid       ;
reg     [15:0]  cnt_wait         ;assign      sys_rst_n   =   Rst_n & locked ;//data_num:写入SDRAM数据个数计数
always@(posedge clk_50M or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_num    <=  1'd0;else    if(read_valid == 1'b1)data_num    <=  1'd0;else    if(done_rx == 1'b1)data_num    <=  data_num + 1'b1;elsedata_num    <=  data_num;//cnt_wait:等待计数器
always@(posedge clk_50M or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_wait    <=  16'd0;else    if(cnt_wait == WAIT_MAX)cnt_wait    <=  16'd0;else    if(data_num == DATA_NUM)cnt_wait    <=  cnt_wait + 1'b1;//read_valid:数据读使能
always@(posedge clk_50M or negedge sys_rst_n)if(sys_rst_n == 1'b0)read_valid  <=  1'b0;else    if(cnt_wait == WAIT_MAX)read_valid  <=  1'b1;else    if(rd_fifo_num == DATA_NUM)read_valid  <=  1'b0;CLK_STRAT    CLK_STRAT_inst (.areset (!Rst_n     )       ,.inclk0 (Clk   )            ,.c0     (clk_50M   )        ,.c1     (clk_100M   )       ,.c2     (clk_100M_shift   ) ,.locked (locked   ));UART_RX     RX_inst(  //串口接收模块.Clk      (clk_50M  )    ,.Rst_n    (sys_rst_n)    ,.Rs232_rx (Rx)           ,.Bps_set  (Bps_set)      ,.Data_out (rx_data_out)  ,.Done_rx  (done_rx)
);UART_TX     TX_inst(.Clk        (clk_50M)    ,.Rst_n      (sys_rst_n)  ,.Bps_set    (Bps_set)    ,.Con_en     (tx_flag)    ,.Data_in    (tx_data)    ,.Rs232_tx   (Tx)         ,.Done_tx    ()           ,.Uart_state ()
);
FIFO_READ   FIFO_READ_inst(.Clk              (clk_50M)            ,                         .Rst_n            (sys_rst_n)          ,      .Rd_fifo_num      (rd_fifo_num)        ,      .Rd_fifo_rd_data  (rd_fifo_rd_data)    ,     .Burst_num        (DATA_NUM)           ,             .Rd_en            (rd_fifo_rd_en)      ,.Tx_data          (tx_data)            ,.Tx_flag          (tx_flag)
);SDRAM_TOP   SDRAM_TOP_inst(.Clk             (clk_100M)               ,.Rst_n           (sys_rst_n)              ,.Clk_out         (clk_100M_shift)         ,     //输入相位偏移时钟.Wr_fifo_wr_clk  (clk_50M)                ,    .Wr_fifo_wr_req  ( done_rx )              ,.Wr_fifo_wr_data ({8'd0,rx_data_out})     ,      //写入写FIFO中的.Sdram_wr_b_addr (24'd0)                  ,.Sdram_wr_e_addr ({14'd0,DATA_NUM} )              ,.Wr_burst_len    (DATA_NUM)               ,.Wr_rst          (!sys_rst_n)             ,      //写复位信号.Rd_fifo_rd_clk  (clk_50M)                ,.Rd_fifo_rd_req  (rd_fifo_rd_en)          ,.Sdram_rd_b_addr (24'd0)                  ,.Sdram_rd_e_addr ({14'd0,DATA_NUM} )              ,.Rd_burst_len    (DATA_NUM)               ,      //读突发长度.Rd_rst          (!sys_rst_n)             ,      //读复位信号.Rd_value        (read_valid)             ,.Rd_fifo_rd_data (rd_fifo_rd_data)        ,      //读FIFO中读出的数据.Rd_fifo_num     (rd_fifo_num)            ,      //读FIFO中存在的数据个数.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_ba        (Sdram_ba   )     ,.Sdram_addr      (Sdram_addr )     ,.Sdram_dq        (Sdram_dq   )     ,     // SDRAM 数据总线.Sdram_dqm       (Sdram_dqm  )
);
endmodule

UART_SDRAM模块仿真代码

使用串口发送模块向UART_SDRAM发送10,15,20,25,30,35,6个数据。突发长度为整页突发,设置满足5个数据长度写入SDRAM中并读取。

`timescale  1ns/1ns
`define CLK_PER 20
module  UART_SDRAM_tb();wire            tx          ;
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_ba    ;
wire    [12:0]  sdram_addr  ;
wire    [1:0]   sdram_dqm   ;
wire    [15:0]  sdram_dq    ;
wire            rx          ;
wire            tx_done     ;//reg define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg             con_en      ;
reg   [7:0]     data_in     ;//时钟
initial     sys_clk     =   1'b1;
always  #(`CLK_PER/2) sys_clk = ~sys_clk;UART_TX     UART_TX_inst(.Clk         (sys_clk)   ,.Rst_n       (sys_rst_n)   ,.Bps_set     (3'd4)   ,.Con_en      (con_en)   ,.Data_in     (data_in)   ,.Rs232_tx    (rx)   ,.Done_tx     (tx_done)   ,.Uart_state  ());//重定义defparam,用于修改参数,缩短仿真时间
//defparam UART_SDRAM_inst.FIFO_READ_inst.BAUD_CNT_END  = 52;
defparam sdram_model_plus_inst.addr_bits = 13;
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;UART_SDRAM      UART_SDRAM_inst(.Clk           (sys_clk     )    ,.Rst_n         (sys_rst_n   )    ,.Rx            (rx          )    ,.Tx            (tx          )    ,.Bps_set       (3'd4        )    ,  .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_ba      (sdram_ba    )    ,            .Sdram_addr    (sdram_addr  )    ,            .Sdram_dq      (sdram_dq    )    ,            .Sdram_dqm     (sdram_dqm   )
);
sdram_model_plus    sdram_model_plus_inst(   .Dq     (sdram_dq       ), .Addr   (sdram_addr     ),.Ba     (sdram_ba       ),.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    ({2'd0,sdram_dqm}   ),.Debug  (1'b1           )
);initial
beginsys_rst_n   =   1'b0;data_in     =   8'd0;con_en      =   1'b0;#(`CLK_PER*10+1);sys_rst_n   =   1'b1;@(posedge   UART_SDRAM_inst.SDRAM_TOP_inst.SDRAM_CTRL_inst.Init_end)#(`CLK_PER*10+1);   data_in     =   8'd10;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0;@(posedge   tx_done)    #(`CLK_PER*2);   data_in     =   8'd15;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0;@(posedge   tx_done)#(`CLK_PER*2);   data_in     =   8'd20;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0;@(posedge   tx_done)    #(`CLK_PER*2);   data_in     =   8'd25;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0; @(posedge   tx_done)    #(`CLK_PER*2);   data_in     =   8'd30;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0;@(posedge   tx_done)    #(`CLK_PER*2);   data_in     =   8'd35;con_en      =   1'b1;#(`CLK_PER);    con_en      =   1'b0;@(posedge   tx_done)   #(`CLK_PER*7000);$stop;
end
endmodule

UART_SDRAM模块仿图

data_in输入6个数据,tx信号输出前5个数据的波形,并且Tx_inst模块中的Done_tx信号产生5次;SDRAM写入5个数据并读出,(sdram_dq信号)仿真成功。


串口读写SDRAM存储器相关推荐

  1. 纯API函数实现串口读写。

    以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口),发现网上相关的资料大约来源于一个版本,那就是所谓的msdn提供的样例代码(msdn的具体出处,我没有考证), ...

  2. read接收不全linux,linux下串口读写有关问题 read 一次读不全(5)

    当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全(5) www.myexceptions.net ...

  3. STM32 HAL库 UART 串口读写功能笔记

    https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...

  4. Linux环境下2410开发板串口读写关键代码

    今天偶然整理原来的项目开发文档,找到了曾经在2410开发板上做的串口读写程序的代码. 现在贴出来供大家参考. #include <qtopia/qpeapplication.h> /*** ...

  5. 基于Arm板linux嵌入式系统RS485串口读写通讯

    最近在做基于Arm板linux嵌入式系统的RS485串口读写通讯首先参考 http://bbs.chinaunix.net/thread-3650543-1-1.html上的文章,该文章写道,读的时候 ...

  6. javaRXTX串口读写,实现浏览器页面设置访问,数据库存储

    serialPortCommunication 码云源码:serialPortCommunication: JAVA实现的串口读写程序,用于不同电台温度模块记录,分为root用户,操作员用户和普通用户 ...

  7. linux串口read几次才能接收完,linux下串口读写有关问题 read 一次读不全

    当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全 www.myexceptions.net  网 ...

  8. C# API方式串口读写(转自叶帆工作室)

    在调试ICU通信设备的时候,由于串口通信老出现故障,所以就怀疑CF实现的SerialPort类是否有问题,所以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口), ...

  9. 串口读写flash_老司机带路:LPC82x 存储器及读写保护 手到擒来!

    点击上方"单片大师"可以订阅哦 存储器,顾名思义就是用来存放东西的地方,那么对于一款 MCU 而言,在性能描述的时候,我们都会说 SRAM,Flash 的容量大小有多少.对于初学者 ...

最新文章

  1. Linux基础(9)文本处理三剑客之grep
  2. 网页视频播放器插件源码
  3. 2020-12-09 深度学习 卷积神经网络中感受野的详细介绍
  4. Date类型JSONArray.fromObject转换出错
  5. 第十三章:位图(五)
  6. SGS 客户端协议解析
  7. Struts2源码阅读(一)_Struts2框架流程概述
  8. 网页广告拦截神器 -- Adblock Plus
  9. 在eclipse环境下配置OpenCV环境
  10. EasyCamera开源摄像机接入海康威视摄像机PS流转ES流
  11. 赵雯北京大学计算机动画系,北京大学艺术类、设计类考研辅导课程
  12. hp ilo 服务器磁盘定位
  13. 【教程】Unity账号注册和Unity Hub激活
  14. 新茶饮式资本扩张,该停脚歇歇了
  15. Redis 乱码解决与思考
  16. P4使用Ubuntu中安装教程
  17. 怎么做 HDFS 的原地平滑缩容?
  18. 计算机科学导论在线作业,南开21春学期《计算机科学导论》在线作业
  19. 年前找Java实习面试经历
  20. AS:另一个程序正在使用此文件,进程无法访问。

热门文章

  1. 分布式系统基础-全文检索
  2. 谷歌浏览器书签导出与导入到其他浏览器
  3. python训练模型、如何得到模型训练总时长_模型训练时间的估算
  4. 一个.net6简单的图片上传(文件也可用)
  5. java thread 头文件_Linux 下 C++ 的多线程基类 - Thread
  6. oracle导入问题ora31626
  7. 《认知科学与脑机接口》作业整合
  8. 虹软人脸识别SDK的使用
  9. Hbase对表进行查询操作详细教程
  10. 使用广播信道的数据链路层