串口读写SDRAM存储器
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存储器相关推荐
- 纯API函数实现串口读写。
以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口),发现网上相关的资料大约来源于一个版本,那就是所谓的msdn提供的样例代码(msdn的具体出处,我没有考证), ...
- read接收不全linux,linux下串口读写有关问题 read 一次读不全(5)
当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全(5) www.myexceptions.net ...
- STM32 HAL库 UART 串口读写功能笔记
https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...
- Linux环境下2410开发板串口读写关键代码
今天偶然整理原来的项目开发文档,找到了曾经在2410开发板上做的串口读写程序的代码. 现在贴出来供大家参考. #include <qtopia/qpeapplication.h> /*** ...
- 基于Arm板linux嵌入式系统RS485串口读写通讯
最近在做基于Arm板linux嵌入式系统的RS485串口读写通讯首先参考 http://bbs.chinaunix.net/thread-3650543-1-1.html上的文章,该文章写道,读的时候 ...
- javaRXTX串口读写,实现浏览器页面设置访问,数据库存储
serialPortCommunication 码云源码:serialPortCommunication: JAVA实现的串口读写程序,用于不同电台温度模块记录,分为root用户,操作员用户和普通用户 ...
- linux串口read几次才能接收完,linux下串口读写有关问题 read 一次读不全
当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全 www.myexceptions.net 网 ...
- C# API方式串口读写(转自叶帆工作室)
在调试ICU通信设备的时候,由于串口通信老出现故障,所以就怀疑CF实现的SerialPort类是否有问题,所以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口), ...
- 串口读写flash_老司机带路:LPC82x 存储器及读写保护 手到擒来!
点击上方"单片大师"可以订阅哦 存储器,顾名思义就是用来存放东西的地方,那么对于一款 MCU 而言,在性能描述的时候,我们都会说 SRAM,Flash 的容量大小有多少.对于初学者 ...
最新文章
- Linux基础(9)文本处理三剑客之grep
- 网页视频播放器插件源码
- 2020-12-09 深度学习 卷积神经网络中感受野的详细介绍
- Date类型JSONArray.fromObject转换出错
- 第十三章:位图(五)
- SGS 客户端协议解析
- Struts2源码阅读(一)_Struts2框架流程概述
- 网页广告拦截神器 -- Adblock Plus
- 在eclipse环境下配置OpenCV环境
- EasyCamera开源摄像机接入海康威视摄像机PS流转ES流
- 赵雯北京大学计算机动画系,北京大学艺术类、设计类考研辅导课程
- hp ilo 服务器磁盘定位
- 【教程】Unity账号注册和Unity Hub激活
- 新茶饮式资本扩张,该停脚歇歇了
- Redis 乱码解决与思考
- P4使用Ubuntu中安装教程
- 怎么做 HDFS 的原地平滑缩容?
- 计算机科学导论在线作业,南开21春学期《计算机科学导论》在线作业
- 年前找Java实习面试经历
- AS:另一个程序正在使用此文件,进程无法访问。