一.理论知识
1.写数据之前,芯片已经完成初始化操作。初始化操作对所有L-Bank进行预充电,此时芯片中没有激活的Bank和行。所以在写数据之前,第一步要激活某个Bank和Bank中的特定行,表示即将要对该行进行写数据操作,此时需要给芯片发送激活指令,Bank地址,13位行地址。
2.紧接着要给芯片发送写数据指令和列地址信号,因为addr是分时复用信号线,此时只需要给芯片发送9位列地址信号,A9~A12是空闲状态。A10 的电平变化控制突发写操作完成后是否立即执行自动预充电操作,若 A10 为高电平,突发写操作完成后,立即执行自动预充电操作,关闭当前行;若 A10 为低电平,突发写操作完成后,当前行依然处于激活状态,以便对当前行执行新的读/写操作,想要关闭当前激活行,需写入预充电指令。剩下的A9,A11和A12信号当前不起作用,只需要赋值0即可。
3.发送写数据指令的同时发送第一个数据,然后每个时钟上升沿依次发送下一个数据。因为这里写数据选择的是页突发模式,所以数据的个数不能超过列地址个数,也就是2的9次方等于512。当不需要写数据的时候,只需要发送突发停止指令即可。
4.由于上面采用的是非自动预充电模式,因此在突发停止指令发送完之后,要给芯片发送预充电指令,用来关闭激活的bank和特定行,这样就不能在当前bank和特定行中继续执行读写操作。下次读写数据的时候,需要重新激活bank和特定行。
二.芯片时序分析
三.设计verilog时序图
四.verilog代码
module sdram_write
(
input wire sys_clk,
input wire sys_rst_n,
input wire init_end,
input wire wr_en,
input wire [23:0] wr_addr,
input wire [15:0] wr_data,
input wire [9:0] wr_burst_len,
output reg wr_ack,
output reg [3:0] wr_sdram_cmd,
output reg [1:0] wr_sdram_ba,
output reg [12:0] wr_sdram_addr,
output reg wr_end,
output reg wr_sdram_en,
output reg [15:0] wr_sdram_data
);
// 状态
parameter ST_IDLE = 0,
ST_ACTIVE = 1,
ST_ACT_WAIT = 2,
ST_WRITE = 3,
ST_WR_DATA = 4,
ST_BURST = 5,
ST_PRE = 6,
ST_PRE_WAIT = 7,
ST_WR_END = 8;
// 延迟计数器的值
parameter CNT_ACT_WAIT = 2,
CNT_PRE_WAIT = 2;
// 命令
parameter CMD_NOP = 4'b0111,
CMD_ACTIVE = 4'b0011,
CMD_PRE = 4'b0010,
CMD_WRITE = 4'b0100,
CMD_BURST = 4'b0110;
reg [3:0] state;
reg [9:0] cnt_wait;
reg cnt_wait_rst;
reg write_flag;
reg burst_flag;
reg pre_flag;
reg end_flag;
// state信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
state <= ST_IDLE;
else
case(state)
ST_IDLE :
if(init_end == 1 && wr_en == 1)
state <= ST_ACTIVE;
else
state <= ST_IDLE;
ST_ACTIVE :
state <= ST_ACT_WAIT;
ST_ACT_WAIT :
if(write_flag == 1)
state <= ST_WRITE;
else
state <= ST_ACT_WAIT;
ST_WRITE :
state <= ST_WR_DATA;
ST_WR_DATA :
if(burst_flag == 1)
state <= ST_BURST;
else
state <= ST_WR_DATA;
ST_BURST :
state <= ST_PRE;
ST_PRE :
state <= ST_PRE_WAIT;
ST_PRE_WAIT :
if(end_flag == 1)
state <= ST_WR_END;
else
state <= ST_PRE_WAIT;
ST_WR_END :
state <= ST_IDLE;
default :
state <= ST_IDLE;
endcase
end
// cnt_wait信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_wait <= 0;
else if(cnt_wait_rst == 1)
cnt_wait <= 0;
else if(cnt_wait_rst == 0)
cnt_wait <= cnt_wait + 1;
end
// cnt_wait_rst信号
always@(*) begin
if(!sys_rst_n)
cnt_wait_rst <= 1;
else if(state == ST_IDLE)
cnt_wait_rst <= 1;
else if(write_flag == 1 || burst_flag == 1 || pre_flag == 1 || end_flag == 1 || state == ST_WR_END)
cnt_wait_rst <= 1;
else
cnt_wait_rst <= 0;
end
// write_flag信号
always@(*) begin
if(!sys_rst_n)
write_flag <= 0;
else if(state == ST_ACT_WAIT && cnt_wait == CNT_ACT_WAIT)
write_flag <= 1;
else
write_flag <= 0;
end
// burst_flag信号
always@(*) begin
if(!sys_rst_n)
burst_flag <= 0;
else if(state == ST_WR_DATA && cnt_wait == wr_burst_len - 1)
burst_flag <= 1;
else
burst_flag <= 0;
end
// pre_flag信号
always@(*) begin
if(!sys_rst_n)
pre_flag <= 0;
else if(state == ST_BURST)
pre_flag <= 1;
else
pre_flag <= 0;
end
// end_flag信号
always@(*) begin
if(!sys_rst_n)
end_flag <= 0;
else if(state == ST_PRE_WAIT && cnt_wait == CNT_PRE_WAIT)
end_flag <= 1;
else
end_flag <= 0;
end
// wr_end信号
always@(*) begin
if(!sys_rst_n)
wr_end <= 0;
else if(state == ST_WR_END)
wr_end <= 1;
else
wr_end <= 0;
end
// wr_ack信号
always@(*) begin
if(!sys_rst_n)
wr_ack <= 0;
else if(state == ST_WRITE)
wr_ack <= 1;
else if(state == ST_BURST)
wr_ack <= 0;
else
wr_ack <= wr_ack;
end
// wr_sdram_en信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
wr_sdram_en <= 0;
else
wr_sdram_en <= wr_ack;
end
// wr_sdram_data信号
always@(*) begin
if(!sys_rst_n)
wr_sdram_data <= 0;
else if(wr_sdram_en == 1)
wr_sdram_data <= wr_data;
else if(wr_sdram_en == 0)
wr_sdram_data <= 0;
end
// wr_sdram_cmd信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
wr_sdram_cmd <= CMD_NOP;
else if(state == ST_ACTIVE)
wr_sdram_cmd <= CMD_ACTIVE;
else if(state == ST_WRITE)
wr_sdram_cmd <= CMD_WRITE;
else if(state == ST_BURST)
wr_sdram_cmd <= CMD_BURST;
else if(state == ST_PRE)
wr_sdram_cmd <= CMD_PRE;
else
wr_sdram_cmd <= CMD_NOP;
end
// wr_sdram_ba信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
wr_sdram_ba <= 2'b11;
else if(state == ST_ACTIVE || state == ST_WRITE || state == ST_PRE)
wr_sdram_ba <= wr_addr[23:22];
else
wr_sdram_ba <= 2'b11;
end
// wr_sdram_addr信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
wr_sdram_addr <= 13'h1fff;
else if(state == ST_ACTIVE)
wr_sdram_addr <= wr_addr[21:9];
else if(state == ST_WRITE)
wr_sdram_addr <= {4'b0000,wr_addr[8:0]};
else if(state == ST_PRE)
wr_sdram_addr <= 13'h0400;
else
wr_sdram_addr <= 13'h1fff;
end
endmodule
五.仿真代码
`timescale 1ns/1ns
module tb_sdram_write();
reg sys_clk;
reg sys_rst_n;
reg wr_en;
reg [23:0] wr_addr;
reg [15:0] wr_data;
reg [9:0] wr_burst_len;
wire [3:0] init_cmd;
wire [1:0] init_ba;
wire [12:0] init_addr;
wire init_end;
wire wr_ack;
wire [3:0] wr_sdram_cmd;
wire [1:0] wr_sdram_ba;
wire [12:0] wr_sdram_addr;
wire wr_end;
wire wr_sdram_en;
wire [15:0] wr_sdram_data;
wire clk_100m;
wire clk_100m_shift;
wire clk_locked;
wire rst_n;
wire [3:0] sdram_cmd;
wire [1:0] sdram_ba;
wire [12:0] sdram_addr;
wire [15:0] sdram_dq;
assign rst_n = sys_rst_n & clk_locked;
initial begin
sys_clk = 0;
sys_rst_n <= 0;
#100
sys_rst_n <= 1;
end
always #10 sys_clk = ~sys_clk;
// wr_en信号
always@(posedge clk_100m or negedge rst_n) begin
if(!rst_n)
wr_en <= 0;
else if(wr_end == 1)
wr_en <= 0;
else if(init_end == 1)
wr_en <= 1;
end
// bank和地址信号
//assign wr_addr = 24'h000000;
// wr_data信号
always@(posedge clk_100m or negedge rst_n) begin
if(!rst_n)
wr_data <= 0;
else if(wr_ack == 1)
wr_data <= wr_data + 1;
else if(wr_ack == 0)
wr_data <= 0;
else
wr_data <= wr_data;
end
// wr_burst_len信号
//assign wr_burst_len = 10;
// dq信号
assign sdram_dq = (wr_sdram_en == 1) ? wr_sdram_data : 16'hz;
clk_100m clk_100m_inst
(
.areset (~sys_rst_n ),
.inclk0 (sys_clk ),
.c0 (clk_100m ),
.c1 (clk_100m_shift ),
.locked (clk_locked)
);
sdram_init sdram_init_inst
(
.sys_clk (clk_100m ),
.sys_rst_n (rst_n ),
.init_cmd (init_cmd ),
.init_ba (init_ba ),
.init_addr (init_addr ),
.init_end (init_end )
);
defparam sdram_model_plus_inst.addr_bits = 13; // 行地址13位
defparam sdram_model_plus_inst.data_bits = 16; // 一次读写数据16位
defparam sdram_model_plus_inst.col_bits = 9; // 列地址9位
defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; // L-Bank容量
sdram_write sdram_write_inst
(
.sys_clk (clk_100m ),
.sys_rst_n (rst_n ),
.init_end (init_end ),
.wr_en (wr_en ),
.wr_addr (24'h000000 ),
.wr_data (wr_data ),
.wr_burst_len (10 ),
.wr_ack (wr_ack ),
.wr_sdram_cmd (wr_sdram_cmd ),
.wr_sdram_ba (wr_sdram_ba ),
.wr_sdram_addr (wr_sdram_addr ),
.wr_end (wr_end ),
.wr_sdram_en (wr_sdram_en ),
.wr_sdram_data (wr_sdram_data )
);
assign sdram_addr = (init_end == 1) ? wr_sdram_addr : init_addr;
assign sdram_ba = (init_end == 1) ? wr_sdram_ba : init_ba;
assign sdram_cmd = (init_end == 1) ? wr_sdram_cmd : init_cmd;
sdram_model_plus sdram_model_plus_inst
(
.Dq (sdram_dq ), // 数据输入输出端口
.Addr (sdram_addr ), // 地址端口
.Ba (sdram_ba ), // Bank地址
.Clk (clk_100m_shift ), // 带有偏移的时钟信号
.Cke (1 ), // 时钟使能信号
.Cs_n (sdram_cmd[3]), // 命令第4位
.Ras_n (sdram_cmd[2]), // 命令第3位
.Cas_n (sdram_cmd[1]), // 命令第2位
.We_n (sdram_cmd[0]), // 命令第1位
.Dqm (2'b00 ), // 数据掩码
.Debug (1 ) // 开启Debug模式
);
endmodule