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个时钟周期后发送数据所需要的时间。写操作不需要列选通潜伏期。(激活列地址后读出数据到IO所需要的时间)。
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, //预充电等待周期 tRP
REFRESH_CLK = 3'd7, //自刷新等待周期 tRC
MODE_CLK = 3'd3; //配置模式寄存器等待周期 tMRD
localparam 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
begin
case(main_state)
IDLE:
begin
if(wait_end)
main_state <= CHARGE;
else
main_state <= IDLE;
end
CHARGE:
main_state <= CHARGE_WAIT;
CHARGE_WAIT:
begin
if(charge_end)
main_state <= REFRESH;
else
main_state <= CHARGE_WAIT;
end
REFRESH:
main_state <= REFRESH_WAIT;
REFRESH_WAIT:
begin
if(refresh_end)
begin
if(refresh_cnt == 4'd8)
main_state <= MODE;
else
main_state <= REFRESH;
end
else
main_state <= REFRESH_WAIT;
end
MODE:
main_state <= MODE_WAIT;
MODE_WAIT:
begin
if(mode_end)
main_state <= END;
else
main_state <= MODE_WAIT;
end
END: 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 @(*)
begin
case(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)
begin
Init_cmd <= NONE_CMD;
Init_ba <= 2'b11;
Init_adrr <= 13'h1fff;
end
else
begin
case(main_state)
IDLE,CHARGE_WAIT,REFRESH_WAIT,MODE_WAIT,END:
begin
Init_cmd <= NONE_CMD;
Init_ba <= 2'b11;
Init_adrr <= 13'h1fff;
end
CHARGE:
begin
Init_cmd <= CHARGE_CMD;
Init_ba <= 2'b11;
Init_adrr <= 13'h1fff;
end
REFRESH:
begin
Init_cmd <= REFRESH_CMD;
Init_ba <= 2'b11;
Init_adrr <= 13'h1fff;
end
MODE:
begin
Init_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:整页,其他:保留
};
end
default:
begin
Init_cmd <= NONE_CMD;
Init_ba <= 2'b11;
Init_adrr <= 13'h1fff;
end
endcase
end
//产生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容量
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #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
begin
case(main_state)
RE_IDLE:
begin
if(Init_end && Re_en)
main_state <= RE_CHARGE;
else
main_state <= RE_IDLE;
end
RE_CHARGE:
main_state <= RE_CHARGE_WAIT;
RE_CHARGE_WAIT:
begin
if(charge_end)
main_state <= RE_REFRESH;
else
main_state <= RE_CHARGE_WAIT;
end
RE_REFRESH:
main_state <= RE_REFRESH_WAIT;
RE_REFRESH_WAIT:
begin
if(refresh_end)
begin
if(refresh_cnt == 2'd2)
main_state <= RE_END;
else
main_state <= RE_REFRESH;
end
else
main_state <= RE_REFRESH_WAIT;
end
RE_END : main_state <= RE_IDLE;
default : main_state <= RE_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;
always @(*)
begin
case(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
end
assign 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
begin
case(main_state)
RE_IDLE,RE_CHARGE_WAIT,RE_REFRESH_WAIT,RE_END:
begin
Re_cmd <= NONE_CMD;
Re_ba <= 2'b11;
Re_addr <= 13'h1fff;
end
RE_CHARGE:
begin
Re_cmd <= CHARGE_CMD;
Re_ba <= 2'b11;
Re_addr <= 13'h1fff;
end
RE_REFRESH:
begin
Re_cmd <= REFRESH_CMD;
Re_ba <= 2'b11;
Re_addr <= 13'h1fff;
end
default:
begin
Re_cmd <= NONE_CMD;
Re_ba <= 2'b11;
Re_addr <= 13'h1fff;
end
endcase
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容量
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #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
begin
case(state)
WR_IDLE :
begin
if(Init_end && Wr_en)
state <= WR_ACTIVE;
else
state <= WR_IDLE;
end
WR_ACTIVE :
state <= ACTIVE_WAIT;
ACTIVE_WAIT :
begin
if(active_end)
state <= WRITE;
else
state <= ACTIVE_WAIT;
end
WRITE : //写命令
state <= WR_DATA;
WR_DATA :
begin
if(wr_data_end)
state <= WR_CHAR;
else
state <= WR_DATA;
end
WR_CHAR :
state <= WR_CHAR_WAIT;
WR_CHAR_WAIT:
begin
if(char_end)
state <= WR_END;
else
state <= WR_CHAR_WAIT;
end
WR_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 @(*)
begin
case(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
begin
case(state)
WR_IDLE,ACTIVE_WAIT,WR_CHAR_WAIT,WR_END:
begin
Wr_ba <= 2'b11;
Wr_cmd <= NONE_CMD;
Wr_sdram_addr <= 13'h1fff;
end
WR_ACTIVE: //写激活指令
begin
Wr_ba <= Wr_addr[23:22];
Wr_cmd <= ACTIVE_CMD;
Wr_sdram_addr <= Wr_addr[21:9]; //行地址
end
WRITE:
begin
Wr_ba <= Wr_addr[23:22];
Wr_cmd <= WR_CMD;
Wr_sdram_addr <= {4'b0000,Wr_addr[8:0]}; //行地址高4位清零 写列地址
end
WR_DATA:
begin
if(wr_data_end)
Wr_cmd <= STOP;
else
begin
Wr_ba <= 2'b11;
Wr_cmd <= NONE_CMD;
Wr_sdram_addr <= 13'h1fff;
end
end
WR_CHAR:
begin
Wr_ba <= Wr_addr[23:22];
Wr_cmd <= CHARGE_CMD;
Wr_sdram_addr <= 13'h0400;
end
default :
begin
Wr_ba <= 2'b11;
Wr_cmd <= NONE_CMD;
Wr_sdram_addr <= 13'h1fff;
end
endcase
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/1ns
module 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容量
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #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;
else
wr_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;
else
wr_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
begin
case(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 @(*)
begin
case(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)
begin
Rd_cmd <= NONE_CMD;
Rd_ba <= 2'b11;
Rd_sdram_addr <= 13'h1fff;
end
else
begin
case(state)
RD_IDLE,ACTIVE_WAIT,RD_CL,CHAR_WAIT,RD_END:
begin
Rd_cmd <= NONE_CMD;
Rd_ba <= 2'b11;
Rd_sdram_addr <= 13'h1fff;
end
RD_ACTIVE: //读激活
begin
Rd_cmd <= ACTIVE_CMD;
Rd_ba <= Rd_addr[23:22];
Rd_sdram_addr <= Rd_addr[21:9]; //激活行地址
end
READ : //读命令
begin
Rd_cmd <= READ_CMD;
Rd_ba <= Rd_addr[23:22];
Rd_sdram_addr <= {4'b0000,Rd_addr[8:0]};
end
RD_DATA: //读数据
begin
if(rd_b_end)
Rd_cmd <= STOP_CMD;
else
Rd_cmd <= NONE_CMD;
Rd_ba <= 2'b11;
Rd_sdram_addr <= 13'h1fff;
end
RD_CHAR: //预充电指令
begin
Rd_cmd <= CHARGE_CMD;
Rd_ba <= Rd_addr[23:22];
Rd_sdram_addr <= 13'h0400;
end
default :
begin
Rd_cmd <= NONE_CMD;
Rd_ba <= 2'b11;
Rd_sdram_addr <= 13'h1fff;
end
endcase
end
//输出读出的SDRAM数据
assign Rd_sdram_data = (Rd_ack == 1'b1)?rd_data_reg : 16'b0;
endmodule
SDRAM读模块仿真代码
`timescale 1ns/1ns
module 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容量
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #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;
else
wr_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;
else
wr_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;
else
rd_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;
else
begin
case(ju_state)
IDLE :
begin
if(Init_end)
ju_state <= JUDGE;
else
ju_state <= IDLE;
end
JUDGE :
begin
if(Re_req)
ju_state <= REFRESH;
else
if(Rd_req)
ju_state <= READ;
else
if(Wr_req)
ju_state <= WRITE;
else
ju_state <= JUDGE;
end
READ :
begin
if(Rd_end)
ju_state <= JUDGE;
else
ju_state <= READ;
end
WRITE :
begin
if(Wr_end)
ju_state <= JUDGE;
else
ju_state <= WRITE;
end
REFRESH :
begin
if(Re_end)
ju_state <= JUDGE;
else
ju_state <= REFRESH;
end
default: ju_state <= JUDGE;
endcase
end
//自动刷新使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Re_en <= 1'b0;
else
if((ju_state == JUDGE) && (Re_req == 1'b1))
Re_en <= 1'b1;
else
if(Re_end)
Re_en <= 1'b0;
//写使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Wr_en <= 1'b0;
else
if((ju_state == JUDGE) && (Re_req == 1'b0) && (Wr_req == 1'b1))
Wr_en <= 1'b1;
else
if(Wr_end)
Wr_en <= 1'b0;
//读使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rd_en <= 1'b0;
else
if((ju_state == JUDGE) && (Re_req == 1'b0) && (Rd_req == 1'b1))
Rd_en <= 1'b1;
else
if(Rd_end)
Rd_en <= 1'b0;
//Sdram_ba Sdram_addr sdram_cmd输出
always @(*)
begin
case(ju_state)
JUDGE:
begin
Sdram_addr<= 13'h1fff;
Sdram_ba <= 2'b11;
sdram_cmd <= NONE_CMD;
end
IDLE:
begin
Sdram_addr<= Init_addr;
Sdram_ba <= Init_ba;
sdram_cmd <= Init_cmd;
end
WRITE:
begin
Sdram_addr<= Wr_addr;
Sdram_ba <= Wr_ba;
sdram_cmd <= Wr_cmd;
end
READ:
begin
Sdram_addr<= Rd_addr;
Sdram_ba <= Rd_ba;
sdram_cmd <= Rd_cmd;
end
REFRESH:
begin
Sdram_addr<= Re_addr;
Sdram_ba <= Re_ba;
sdram_cmd <= Re_cmd;
end
default:
begin
Sdram_addr<= 13'h1fff;
Sdram_ba <= 2'b11;
sdram_cmd <= NONE_CMD;
end
endcase
end
//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控制模块代码
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)
begin
rd_ack_dly <= 1'b0;
wr_ack_dly <= 1'b0;
end
else
begin
rd_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;
else
if(Wr_rst)
Sdram_wr_addr <= Sdram_wr_b_addr;
else
if(wr_ack_dly_fall) //一次突发写结束,更改写地址
begin
if(Sdram_wr_addr < (Sdram_wr_e_addr - Wr_burst_len))
Sdram_wr_addr <= Sdram_wr_addr + Wr_burst_len;
else
Sdram_wr_addr <= Sdram_wr_b_addr; //到达末地址,回到写起始地址
end
else
Sdram_wr_addr <= Sdram_wr_addr;
//读地址更新
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Sdram_rd_addr <= 1'b0;
else
if(Wr_rst)
Sdram_rd_addr <= Sdram_rd_b_addr;
else
if(rd_ack_dly_fall) //一次突发写结束,更改写地址
begin
if(Sdram_rd_addr < (Sdram_rd_e_addr - Rd_burst_len))
Sdram_rd_addr <= Sdram_rd_addr + Rd_burst_len;
else
Sdram_rd_addr <= Sdram_rd_b_addr; //到达末地址,回到写起始地址
end
else
Sdram_rd_addr <= Sdram_rd_addr;
//读写请求信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
begin
Sdram_wr_req <= 1'b0;
Sdram_rd_req <= 1'b0;
end
else //优先执行写操作,防止写入SDRAM中的数据丢失
if(Init_end) //初始化完成后响应读写请求
begin
if(wr_fifo_num >= Wr_burst_len) //写FIFO中的数据量达到写突发长度
begin
Sdram_wr_req <= 1'b1;
Sdram_rd_req <= 1'b0;
end
else
if((Rd_fifo_num < Rd_burst_len) && Rd_value) //读FIFO中的数据量小于读突发长度,且读使能信号有效
begin
Sdram_wr_req <= 1'b0;
Sdram_rd_req <= 1'b1;
end
else
begin
Sdram_wr_req <= 1'b0;
Sdram_rd_req <= 1'b0;
end
end
else
begin
Sdram_wr_req <= 1'b0;
Sdram_rd_req <= 1'b0;
end
FIFO_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/1ns
module 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;
//时钟、复位信号
initial
begin
sys_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #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;
else
wr_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;
else
cnt_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;
else
wr_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;
else
wr_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;
else
wr_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;
else
rd_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;
else
cnt_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;
else
if(Rd_fifo_num == Burst_num)
Rd_en <= 1'b1;
else
if(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;
else
rd_en_dly <= Rd_en;
//读有效标志信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
rd_value <= 1'b0;
else
if(data_num == Burst_num)
rd_value <= 1'b1;
else
if(read_cnt == Burst_num)
rd_value <= 1'b0;
else
rd_value <= rd_value;
//计数比特时钟周期个数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
baud_cnt <= 1'b0;
else
if(rd_value)
begin
if(baud_cnt == BAUD_CNT_END)
baud_cnt <= 1'b0;
else
baud_cnt <= baud_cnt + 1'b1;
end
else
baud_cnt <= 1'b0;
//计数比特周期数标志信号产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bit_flag <= 1'b0;
else
if(baud_cnt == (BAUD_CNT_END >> 1))
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//计数比特标志信号个数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bit_cnt <= 1'b0;
else
if(bit_flag)
begin
if(bit_cnt == 4'd10)
bit_cnt <= 1'b0;
else
bit_cnt <= bit_cnt + 1'b1;
end
else
bit_cnt <= bit_cnt;
//READ_FIFO读使能信号产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
read_fifo_en <= 1'b0;
else
if(bit_flag && (bit_cnt == 4'd10))
read_fifo_en <= 1'b1;
else
read_fifo_en <= 1'b0;
//READ_FIFO读数据个数计数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
read_cnt <= 1'b0;
else
if(read_cnt == Burst_num)
read_cnt <= 10'd0;
else
if(read_fifo_en)
read_cnt <= read_cnt + 1'b1;
//Tx输出使能信号产生
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Tx_flag <= 1'b0;
else
Tx_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;
else
data_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
begin
sys_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信号)仿真成功。