1、知识点
SDRAM提供了多种数据写入方式,诸如单个写、页突发写(突发长度1、2、4、8、全页)等。本文目前只对全页突发的模式做一个讲解与仿真,后续视情况提供其他写方式的讲解与仿真。
需要注意的是绝大部分SDRAM都不支持在全页突发的情况下自动预充电,所以我们的写操作时序中包含手动预充电。时序图如下:
各信号说明如下:
CK:工作时钟,具体时钟频率视不同的芯片而不同
CKE:时钟使能,在整个突发写过程中都需要拉高(实际上除了在休眠模式拉低,其他操作全需要拉高)
COMMAND:SDRAM命令,由4根线拼接而成,分别是CS#(片选信号),RAS#(行选通信号),CAS#(列选通信号),WE#(写使能信号),通过这4根命令线,再结合SDRAM的地址、输入输出数据等,就可以对SDRAM进行各种命令操作
DQM/DQML,DQMU:数据掩码,通过数据掩码可以实现对输入或输出数据的某一位进行“掩埋”,也就是使某一位失效
A[9:0],A[12:11]:数据地址线,同时也可用来设置模式寄存器
A10:数据地址线,同时也可以用来使能一些具体操作,比如控制自动预充电使能、使能预充电bank数量
BA[1:0]:bank地址
DQ数据:数据总线,突发写过程中需要根据 时序操作数据总线写入数据
通过对时序图的分析可以总结出初始化过程如下:
1.发送激活指令,同时在bank总线输出想要写入数据的bank地址,在地址总线输出写入数据的行地址,所以该操作也称之为 “ 行激活 ”
2.发送激活指令后需要等待一定的时间,即tRCD,在此期间同样需要发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)
3.等待结束后发送写指令,同时在在bank总线输出想要写入数据的bank地址,在地址总线输出写入数据的列首地址,并且在数据总线输出要写入SDRAM的第一个数据,所以该操作也称之为 “ 列读写 ”
4.每个周期多输出空指令,但数据总线一直输出想要输出的数据
5.在所有数据输出完成后的下一个周期输出 “ 突发终止 ”指令,结束这次突发写入,由此可实现突发长度的控制(不能超过一行数据的最大数,即2*9=512个数据)
6.发送预充电指令对所有BANK进行预充电操作,A10拉高即是选中所有BANK
7.进行预充电操作后需要等待一定的时间,即tRP,在此期间同样需要发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)
8.tRP时间结束后,一次突发写操作完成
2、设计
信号名称 | 位宽 | 描述 | 信号名称 | 位宽 | 描述 |
sys_clk | 1 | 时钟信号,100M | wr_sdram_cmd | 4 | 4位SDRAM命令,组成{CS#,RAS#,CAS#,WE#} |
sys_rst_n | 1 | 复位信号,低电平有效 | wr_sdram_bank | 2 | 2位BANK地址,共4个BANK |
init_end | 1 | 初始化完成信号,只有完成初始化后才能进行写操作 | wr_sdram_addr | 13 | 13位SDRAM地址 |
wr_addr | 24 | 写操作的一些地址,分别有bank地址(2位)、行地址(13位)、列地址(9位)组成 | wr_sdram_data | 16 | 输出到仲裁模块的要写入SDRAM的数据 |
wr_data | 16 | 要写入SDRAM的数据 | wr_end | 1 | 写操作结束信号,只在写操作结束后维持一个时钟周期 |
wr_burst_len | 10 | 一次突发写的长度 | wr_sdram_en | 1 | 写模块数据输出使能 |
wr_en | 1 | 写使能信号,该信号拉高后才可以进行写操作 | wr_ack | 1 | 写操作响应信号,表示该模块对SDRAM进行了写操作(因为写操作的数据来源是FIFO,所以该信号可以作为FIFO的读使能,所以要提前一个时钟周期) |
时序图:
仿真:
①状态机转移
②写数据
③输出地址,数据
④指令
总结:
1、仲裁模块产生使能,数据写完成后被拉低
2、地址、数据和突发长度由外部产生,传给FIFO控制模块,FIFO控制模块在传给数据写模块,数据写入与WR_DATA一致
3、由状态机实现:
对各个状态、状态跳转条件、输出进行说明:
WR_IDLE:初始状态,当初始化完成信号拉高(初始化完成)且仲裁模块发送突发写使能信号后,跳转至下一状态WR_ACTIVE,在此状态发送NOP指令
WR_ACTIVE:行激活状态,只维持一个时钟周期然后跳转到下一状态WR_TRCD,发送行激活指令(ACTIVE)+bank地址+行地址
WR_TRCD:行激活等待状态在此状态等待时间满足TRCD后就跳转到下一个状态WRITE,在此状态发送NOP指令
WRITE:列读写状态,只维持一个时钟周期然后跳转到下一状态WR_DATA,发送列读写指令(WRITE)+bank地址+列首地址
WR_DATA: 数据写入状态,在此状态先发送NOP指令 ,同时在数据总线上输出数据;当所有要写入的数据都输出后,发送突发终止指令(BURST TERM),终结写操作,跳转到WR_PCH状态
WR_PCH:发送预充电指令状态,只维持一个时钟周期然后跳转到下一状态WR_TRP,发送行预充电指令(PRECHARGE)+A10拉高
WR_TRP:预充电指令等待状态、在此状态等待时间满足TRP后就跳转到下一个状态WR_END,在此状态发送NOP指令
WR_END:该状态拉高突发写标志完成信号wr_end一个周期,告诉仲裁模块写操作完成,以便仲裁模块对刷新、写、读等请求进行仲裁(防止时序冲突)3、计数器
对等待时间进行计数
对写数据个数进行计数
4、响应信号,在写数据时有效
5、数据写结束,在WR_END状态拉高,并传给仲裁模块,此时数据写使能被拉低
6、输出指令
7、wr_sdram_en
写数据时拉高,职后一个周期
8、写数wr_sdram_data,与wr_data一致
代码:
//----------------------------------------------------------------------------------------------------
//--SDRAM写模块
//----------------------------------------------------------------------------------------------------
module SDRAM_WR
(
input sys_clk ,
input sys_rst_n ,
input init_end ,
input [23:0] wr_addr , //2bit的bank地址,13bit的row地址,9bit的colum的地址
input [15:0] wr_data , //每个存储单元16bit
input [9:0] wr_burst_len , //每一行对应的列有2^9=512个存储单元,页突发选最大
input wr_en ,
output reg [3:0] wr_sdram_cmd ,
output reg [1:0] wr_ba ,
output reg [12:0] wr_sdram_addr ,
output reg wr_sdram_en ,
output wr_end ,
output [15:0] wr_sdram_data , //输出数据与输入数据保持一直
output wr_ack
);
//------------<参数定义>----------------------
parameter WR_IDLE = 3'b000,
WR_ACTIVE = 3'b001,
WR_TRCD = 3'b011,
WR_WRITE = 3'b010,
WR_DATA = 3'b110,
WR_PCH = 3'b111,
WR_TRP = 3'b101,
WR_END = 3'b100;
parameter TRCD = 3'd2,
TRP = 3'd2;
parameter NOP = 4'b0111,
ACTIVE = 4'b0011,
WR_CMD = 4'b0100,
B_STOP = 4'b0110,
P_CHARGE = 4'b0010;
wire TRCD_end ; //行激活等待时间结束标志
wire twr_end ; //突发写结束标志
wire TRP_end ; //预充电等待时间结束标志
reg [2:0] wr_state ;
reg [9:0] cnt_clk ;
reg cnt_clk_rst ;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
wr_state <= WR_IDLE;
else
case(wr_state)
WR_IDLE :
if(init_end == 1'b1 && wr_en == 1'b1)
wr_state <= WR_ACTIVE;
else;
WR_ACTIVE :
wr_state <= WR_TRCD;
WR_TRCD :
if(TRCD_end == 1'b1)
wr_state <= WR_WRITE;
else;
WR_WRITE :
wr_state <= WR_DATA ;
WR_DATA :
if(twr_end == 1'b1)
wr_state <= WR_PCH;
else;
WR_PCH :
wr_state <= WR_TRP;
WR_TRP :
if(TRP_end == 1'b1)
wr_state <= WR_END;
else;
WR_END :
wr_state <= WR_IDLE;
default : wr_state <= WR_IDLE;
endcase
//用于计数各个状态以实现状态跳转,计数复位信号cnt_clk_rst有效时复位,其他时间累加
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_clk <= 10'd0;
else if(cnt_clk_rst == 1'b1)
cnt_clk <= 10'd0;
else
cnt_clk <= cnt_clk + 1'b1;
//工作状态计数器的复位信号
always @(*)begin
case(wr_state)
WR_IDLE : cnt_clk_rst <= 1'b1;
WR_TRCD : cnt_clk_rst <= (TRCD_end == 1'b1) ? 1'b1 : 1'b0;
WR_WRITE : cnt_clk_rst <= 1'b1;
WR_DATA : cnt_clk_rst <= (twr_end == 1'b1) ? 1'b1 : 1'b0;
WR_PCH : cnt_clk_rst <= (TRP_end == 1'b1) ? 1'b1 : 1'b0;
WR_END : cnt_clk_rst <= 1'b1;
default : cnt_clk_rst <= 1'b0;
endcase
end
assign TRCD_end = (wr_state == WR_TRCD && cnt_clk == TRCD) ? 1'b1 : 1'b0;
assign twr_end = (wr_state == WR_DATA && (cnt_clk == (wr_burst_len - 1'b1)))? 1'b1 : 1'b0;
assign TRP_end = (wr_state == WR_TRP && cnt_clk == TRP)? 1'b1 : 1'b0;
//写 SDRAM 响应信号
assign wr_ack = (wr_state == WR_WRITE || (wr_state == WR_DATA && (cnt_clk <= (wr_burst_len - 2'd2))))? 1'b1 : 1'b0;
//一次突发写结束
assign wr_end = (wr_state == WR_END) ? 1'b1 : 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
begin
wr_sdram_cmd <= NOP ;
wr_ba <= 2'b11 ;
wr_sdram_addr <= 13'h1fff ;
end
else
case(wr_state)
WR_IDLE,WR_TRCD,WR_TRP:
begin
wr_sdram_cmd <= NOP ;
wr_ba <= 2'b11 ;
wr_sdram_addr <= 13'h1fff ;
end
WR_ACTIVE:
begin
wr_sdram_cmd <= ACTIVE ;
wr_ba <= wr_addr[23:22];
wr_sdram_addr <= wr_addr[21:9] ;
end
WR_WRITE:
begin
wr_sdram_cmd <= WR_CMD ;
wr_ba <= wr_addr[23:22];
wr_sdram_addr <= {4'b0000,wr_addr[8:0]} ;
end
WR_DATA:
if(twr_end == 1'b1)
wr_sdram_cmd <= B_STOP ;
else
begin
wr_sdram_cmd <= NOP ;
wr_ba <= 2'b11 ;
wr_sdram_addr <= 13'h1fff ;
end
WR_PCH:
begin
wr_sdram_cmd <= P_CHARGE ;
wr_ba <= wr_addr[23:22];
wr_sdram_addr <= 13'h0400 ;
end
WR_END:
begin
wr_sdram_cmd <= NOP ;
wr_ba <= 2'b11 ;
wr_sdram_addr <= 13'h1fff ;
end
default :
begin
wr_sdram_cmd <= NOP ;
wr_ba <= 2'b11 ;
wr_sdram_addr <= 13'h1fff ;
end
endcase
//wr_sdram_en:数据总线输出使能
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_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
//----------------------------------------------------------------------------------------------------
//--SDRAM数据写模块仿真测试
//----------------------------------------------------------------------------------------------------
`timescale 1ns/1ns
module SDRAM_WR_tb();
wire clk50m ;
wire clk100m ;
wire clk100m_shift ;
wire locked ;
wire rst_n ;
wire [3:0] init_cmd;
wire [1:0] init_ba ;
wire [12:0] init_addr;
wire init_end;
wire [3:0] wr_sdram_cmd ;
wire [1:0] wr_ba ;
wire [12:0] wr_sdram_addr ;
wire wr_sdram_en ;
wire wr_end ;
wire [15:0] wr_sdram_data ;
wire wr_ack ;
wire [3:0] sdram_cmd ;
wire [1:0] sdram_ba ;
wire [12:0] sdram_addr ;
wire [15:0] sdram_data ;
reg sys_clk ;
reg sys_rst_n ;
reg [15:0] wr_data_in ;
reg wr_en ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk <= ~sys_clk;
assign rst_n = sys_rst_n & locked ;
always @(posedge clk100m or negedge rst_n)
begin
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;
end
assign sdram_cmd = (init_end == 1'b1)? wr_sdram_cmd : init_cmd ;
assign sdram_ba = (init_end == 1'b1)? wr_ba : init_ba ;
assign sdram_addr = (init_end == 1'b1)? wr_sdram_addr : init_addr ;
//重定义仿真模型中的相关参数
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;
always @(posedge clk100m or negedge rst_n)
begin
if(!rst_n)
wr_data_in <= 16'd0;
else if(wr_data_in == 16'd10)
wr_data_in <= 16'd0;
else if(wr_ack == 1'b1)
wr_data_in <= wr_data_in + 1'b1;
else
wr_data_in <= wr_data_in;
end
assign sdram_data = (wr_sdram_en == 1'b1)? wr_sdram_data : 16'hzzzz;
//------------<例化被测试模块>--------------------
clk_gen clk_gen_inst
(
.areset ( ~sys_rst_n),
.inclk0 ( sys_clk ),
.c0 ( clk50m ),
.c1 ( clk100m ),
.c2 ( clk100m_shift ),
.locked ( locked )
);
SDRAM_INIT SDRAM_INIT_inst(
. sys_clk (clk100m) ,
. sys_rst_n (rst_n) ,
. init_cmd (init_cmd) ,
. init_ba (init_ba) ,
. init_addr (init_addr) ,
. init_end (init_end)
);
SDRAM_WR SDRAM_WR_inst
(
.sys_clk (clk100m) ,
.sys_rst_n (rst_n) ,
.init_end (init_end) ,
.wr_addr (24'h000_000) , //2bit的bank地址,13bit的row地址,9bit的colum的地址
.wr_data (wr_data_in) , //每个存储单元16bit
.wr_burst_len (10'd10) , //每一行对应的列有2^9=512个存储单元,页突发选最大
.wr_en (wr_en) ,
.wr_sdram_cmd (wr_sdram_cmd) ,
.wr_ba (wr_ba ) ,
.wr_sdram_addr (wr_sdram_addr) ,
.wr_sdram_en (wr_sdram_en) ,
.wr_end (wr_end ) ,
.wr_sdram_data (wr_sdram_data) , //输出数据与输入数据保持一直
.wr_ack (wr_ack )
);
sdram_model_plus sdram_model_plus_inst
(
.Dq (sdram_data) ,
.Addr (sdram_addr) ,
.Ba (sdram_ba) ,
.Clk (clk100m_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'b00) ,
.Debug (1'b1)
);
endmodule