一.理论知识
1.读命令(Read),控制命令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0101,用来实现对已激活的特定 L-Bank 的特定行的数据突发读取操作, BA[1:0]指定需要读取数据的特定 LBank,地址总线 A0-A9 指定需要读取存储单元的起始列地址, A10 的电平变化控制突发读操作完成后是否立即执行自动预充电操作,若 A10 为高电平,突发读操作完成后,立即执行自动预充电操作,关闭当前行;若 A10 为低电平,突发读操作完成后,当前行依然处于激活状态,以便对当前行执行新的读/写操作,想要关闭当前激活行,需写入预充电指令。读数据有多种模式,这里我们使用不带自动预充电的页突发读模式,激活一次以后,可以连续读取一整页的数据,不过也可以读取小于一整页的数据,当不需要读数据的时候,只需要发送突发中止指令即可。
2.首先是激活特定 L-Bank 的特定行,该操作下13位地址线被用来发送行地址信号,Ba[1:0]表示要激活的Bank。
3.经过tRCD的延迟之后,需要向sdram芯片发送读数据命令。此时13位地址线用来发送列地址信号,但是13位只用到了低9位,高4位全部赋值为0。
4.读数据命令后,要等待2~3个周期的潜伏期,才能接受到sdram输出的数据。这里的潜伏期到底是2个周期还是3个周期,取决于前面初始化阶段的设置。
5.如果要读取N个字节的数据,发送完读数据命令之后的N个周期后发送突发中止指令。突发中止指令不像读数据那样,不需要等待2~3个周期的潜伏期。
6.读数据结束之后,要给芯片发送预充电命令,关闭激活的L-Bank和行。
二.芯片时序分析
三.设计verilog时序图
四.verilog代码
module sdram_read
(
input wire sys_clk,
input wire sys_rst_n,
input wire init_end,
input wire rd_en,
input wire [23:0] rd_addr,
input wire [15:0] rd_data,
input wire [9:0] rd_burst_len,
output reg rd_ack,
output reg rd_end,
output reg [3:0] rd_sdram_cmd,
output reg [1:0] rd_sdram_ba,
output reg [12:0] rd_sdram_addr,
output wire [15:0] rd_sdram_data
);
// 状态
parameter ST_IDLE = 0,
ST_ACTIVE = 1,
ST_ACT_WAIT = 2,
ST_READ = 3,
ST_CAS_WAIT = 4,
ST_RD_DATA = 5,
ST_PRE = 6,
ST_PRE_WAIT = 7,
ST_RD_END = 8;
// 延迟计数器的值
parameter CNT_ACT_WAIT = 2,
CNT_CAS_WAIT = 3,
CNT_PRE_WAIT = 2;
// 命令
parameter CMD_NOP = 4'b0111,
CMD_ACTIVE = 4'b0011,
CMD_PRE = 4'b0010,
CMD_READ = 4'b0101,
CMD_BURST = 4'b0110;
reg [3:0] state;
reg [9:0] cnt_wait;
reg cnt_wait_rst;
reg read_flag;
reg rd_data_flag;
reg pre_flag;
reg end_flag;
reg [15:0] rd_data_reg;
// 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 && rd_en == 1)
state <= ST_ACTIVE;
else
state <= ST_IDLE;
ST_ACTIVE :
state <= ST_ACT_WAIT;
ST_ACT_WAIT :
if(read_flag == 1)
state <= ST_READ;
else
state <= ST_ACT_WAIT;
ST_READ :
state <= ST_CAS_WAIT;
ST_CAS_WAIT :
if(rd_data_flag == 1)
state <= ST_RD_DATA;
else
state <= ST_CAS_WAIT;
ST_RD_DATA :
if(pre_flag == 1)
state <= ST_PRE;
else
state <= ST_RD_DATA;
ST_PRE :
state <= ST_PRE_WAIT;
ST_PRE_WAIT :
if(end_flag == 1)
state <= ST_RD_END;
else
state <= ST_PRE_WAIT;
ST_RD_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(read_flag == 1 || rd_data_flag == 1 || pre_flag == 1 || end_flag == 1 || state == ST_RD_END)
cnt_wait_rst <= 1;
else
cnt_wait_rst <= 0;
end
// read_flag信号
always@(*) begin
if(!sys_rst_n)
read_flag <= 0;
else if(state == ST_ACT_WAIT && cnt_wait == CNT_ACT_WAIT)
read_flag <= 1;
else
read_flag <= 0;
end
// rd_data_flag信号
always@(*) begin
if(!sys_rst_n)
rd_data_flag <= 0;
else if(state == ST_CAS_WAIT && cnt_wait == CNT_CAS_WAIT - 1)
rd_data_flag <= 1;
else
rd_data_flag <= 0;
end
// pre_flag信号
always@(*) begin
if(!sys_rst_n)
pre_flag <= 0;
else if(state == ST_RD_DATA && cnt_wait == rd_burst_len - 1)
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
// rd_ack信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_ack <= 0;
else if(state == ST_RD_DATA)
rd_ack <= 1;
else
rd_ack <= 0;
end
// rd_end信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_end <= 0;
else
rd_end <= end_flag;
end
// rd_data_reg信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_data_reg <= 16'b0;
else
rd_data_reg <= rd_data;
end
// rd_sdram_data信号
assign rd_sdram_data = (rd_ack == 1) ? rd_data_reg : 16'b0;
// rd_sdram_cmd信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_sdram_cmd <= CMD_NOP;
else if(state == ST_ACTIVE)
rd_sdram_cmd <= CMD_ACTIVE;
else if(state == ST_READ)
rd_sdram_cmd <= CMD_READ;
else if(state == ST_RD_DATA && cnt_wait == rd_burst_len - CNT_CAS_WAIT)
rd_sdram_cmd <= CMD_BURST;
else if(state == ST_PRE)
rd_sdram_cmd <= CMD_PRE;
else
rd_sdram_cmd <= CMD_NOP;
end
// rd_sdram_ba信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_sdram_ba <= 2'b11;
else if(state == ST_ACTIVE || state == ST_READ || state == ST_PRE)
rd_sdram_ba <= rd_addr[23:22];
else
rd_sdram_ba <= 2'b11;
end
// rd_sdram_addr信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rd_sdram_addr <= 13'h1fff;
else if(state == ST_ACTIVE)
rd_sdram_addr <= rd_addr[21:9];
else if(state == ST_READ)
rd_sdram_addr <= {4'b0000,rd_addr[8:0]};
else if(state == ST_PRE)
rd_sdram_addr <= 13'h0400;
else
rd_sdram_addr <= 13'h1fff;
end
endmodule
五.仿真代码
`timescale 1ns/1ns
module tb_sdram_read();
reg sys_clk;
reg sys_rst_n;
wire [3:0] init_cmd;
wire [1:0] init_ba;
wire [12:0] init_addr;
wire init_end;
wire clk_100m;
wire clk_100m_shift;
wire clk_locked;
wire rst_n;
reg wr_en;
reg [15:0] wr_data;
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;
reg rd_en;
wire [15:0] read_sdram_dq;
wire rd_ack;
wire rd_end;
wire [3:0] rd_sdram_cmd;
wire [1:0] rd_sdram_ba;
wire [12:0] rd_sdram_addr;
wire [15:0] rd_sdram_data;
wire [15:0] sdram_dq;
wire [3:0] sdram_cmd;
wire [1:0] sdram_ba;
wire [12:0] sdram_addr;
wire [3:0] sdram_rw_cmd;
wire [1:0] sdram_rw_ba;
wire [12:0] sdram_rw_addr;
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 == 1'b0)
wr_en <= 1'b1;
else if(wr_end == 1'b1)
wr_en <= 1'b0;
else
wr_en <= wr_en;
end
// rd_en信号
always@(posedge clk_100m or negedge rst_n) begin
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;
end
// 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
wr_data <= 0;
end
// sdram_dq信号
assign sdram_dq = (wr_sdram_en == 1) ? wr_sdram_data : 16'bz;
// read_sdram_dq信号
assign read_sdram_dq = (rd_en == 1) ? sdram_dq : 16'bz;
// 时钟信号
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 )
);
// 数据写操作
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 )
);
// 数据读操作
sdram_read sdram_read_inst
(
.sys_clk (clk_100m ),
.sys_rst_n (rst_n ),
.init_end (init_end ),
.rd_en (rd_en ),
.rd_addr (24'h000000 ),
.rd_data (read_sdram_dq ),
.rd_burst_len (10 ),
.rd_ack (rd_ack ),
.rd_end (rd_end ),
.rd_sdram_cmd (rd_sdram_cmd ),
.rd_sdram_ba (rd_sdram_ba ),
.rd_sdram_addr (rd_sdram_addr ),
.rd_sdram_data (rd_sdram_data )
);
assign sdram_addr = (init_end == 1) ? sdram_rw_addr : init_addr;
assign sdram_ba = (init_end == 1) ? sdram_rw_ba : init_ba;
assign sdram_cmd = (init_end == 1) ? sdram_rw_cmd : init_cmd;
assign sdram_rw_addr = (wr_en == 1) ? wr_sdram_addr : rd_sdram_addr;
assign sdram_rw_ba = (wr_en == 1) ? wr_sdram_ba : rd_sdram_ba;
assign sdram_rw_cmd = (wr_en == 1) ? wr_sdram_cmd : rd_sdram_cmd;
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仿真模型
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