浅谈预充电命令
预充电命令关闭在指定 BANK 或者全部 BANK(具体是单一或者全部 BANK 由 A10 状态选择)中已经打开的行。
在预充电命令执行并经过 tRP(PRECHARGE command period)时间后,对应的BANK将可以重新被操作,即从执行预充电命令到下一次能够对该 BANK 执行新的命令,需要等待 tRP 的时间。
A10=1::对所有BANK 行进行预充电,若 A10 =0:只对 BA0 和 BA1 指定的 BANK 中的行进行预充电。当某个 BANK行被执行了预充电命令后,该 BANK 将处于空闲状态,在下次读写操作之前必须重新激活。
BA0-BA1 :BANK;A0–A12:行(ROW)。
数据写模块:
输入:
init_end :初始化操作,sdram完成其他操作前必须初始化结束
wr_addr wr_data wr_brust_len :地址 数据 突发长度
wr_en :写使能信号 ,通过fifo传入仲裁模块,由仲裁模块提供
输出:
wr_cmd wr_ba wr_addr wr_data: 命令 bank地址 地址 数据
wr_sdram_en: 写sdram使能信号,传入仲裁模块
时序
使用sdram不进行自动预充电的页突发:
tRCD:激活等待时间 查阅对应手册
激活指令激活sdram ; bank地址同时行地址;等待tRCD,同时NOP;A0-A8地址总线要写入列地址,列首地址;写入数据;更新数据,在最后一个数据后的下一个时钟,写入突发终止信号。
波形图绘制
数据写入完成后 进行预充电 保持一个周期WR_PCH。
cnt_clk:计数器,计数周期,对状态机的状态周期进行控制。
cnt_clk_rst 对cnt_clk进行控制
trcd_end twr_end trp_end 激活等待结束信号 数据写完成等待 预充电等待
wr_ack 数据开始写入时拉高,写入数据完成后拉低。 写响应信号
wr_end 写入数据完成后,该信号拉高
wr_cmd 写入命令 : NOP空操作命令 激活指令 NOP 写指令 NOP(数据写入) 突发结束指令_STOP 预充电 NOP
wr_ba bank地址开始为全1 2'b11, 对2'b00 bank进行操作, nop操作是2'b11,数据写入2'b00, 预充电 2'b00
wr_addr 地址总线 一开始全为1, 给首地址13'h0000,对第0行操作
wr_sdram_en: 写使能,数据写入时拉高
wr_sdram_data:
程序编写
(
input wire sys_clk,
input wire sys_rst_n,
input wire init_end,
input wire [23:0]wr_addr, //两位宽bank地址 13位宽行地址 9位宽列地址
input wire [15:0]wr_data, //sdram 芯片位宽为16位
input wire [9:0]wr_burst_len, //我们使用页突发,我们使用512,表示512需要10位
input wire wr_en,
output reg [3:0]wr_cmd,
output reg [1:0]wr_ba,
output reg [12:0]wr_sdram_addr,
output reg wr_sdram_en,
output wire wr_end,
output wire [15:0]wr_sdram_data,
output wire wr_ack
);
//格雷码表示状态 定义八个状态
parameter WR_IDLE = 3'b000,
WR_ACTIVE = 3'b001,
WR_TRCD = 3'b011,
WRITE= 3'b010,
WR_DATA = 3'b110,
WR_PCH = 3'b111,
WR_TRP = 3'b101,
WR_END = 3'b100;
//指令
parameter NOP = 4'b0111,
ACTIVE = 4'b0011,
WR_CMD = 4'b0100,
B_STOP = 4'b0110,
P_CHARGE =4'b0010;
parameter TRCD = 3'd2,
TRP = 3'd7;
reg [2:0]wr_state;
reg [9:0]cnt_clk;
reg cnt_clk_rst;
wire trcd_end;
wire twr_end;
wire trp_end;
//状态变量的赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
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_state <= wr_state;
WR_ACTIVE:
wr_state <= WR_TRCD;
WR_TRCD:
if(trcd_end == 1'b1 )
wr_state <= WRITE;
else
wr_state <= wr_state;
WRITE:
wr_state <= WR_DATA;
WR_DATA:
if(twr_end == 1'b1 )
wr_state <= WR_PCH;
else
wr_state <= wr_state;
WR_PCH:
wr_state <= WR_TRP;
WR_TRP:
if(trp_end == 1'b1 )
wr_state <= WR_END;
else
wr_state <= wr_state;
WR_END:
wr_state <= WR_IDLE;
default:
wr_state <= WR_IDLE;
endcase
//cnt_clk 计数器的赋值 计数器如果突发长度为512需要保持在10位宽
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
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;
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)))? 1'b1:1'b0;
assign trp_end =((wr_state <= WR_TRP)&& (cnt_clk == TRP))? 1'b1:1'b0;
assign wr_ack = ((wr_state==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 == 1'b0)
begin
wr_cmd <= NOP;
wr_ba <= 2'b11;
wr_sdram_addr <= 13'h1fff;
end
//结合状态机进行赋值
else
case(wr_state)
WR_IDLE,WR_TRCD,WR_TRP:
begin
wr_cmd <= NOP;
wr_ba <= 2'b11;
wr_sdram_addr <= 13'h1fff;
end
WR_ACTIVE:
begin
wr_cmd <= ACTIVE;
wr_ba <= wr_addr[23:22];
wr_sdram_addr <= wr_addr[21:9];
end
WRITE:
begin
wr_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_cmd <= B_STOP;
else
begin
wr_cmd <= NOP;
wr_ba <= 2'b11;
wr_sdram_addr <= 13'h1fff;
end
WR_PCH:
begin
wr_cmd <= P_CHARGE;
wr_ba <= wr_addr[23:24];
//对所有bank进行预充电 A10置为1
wr_sdram_addr <= 13'h0400;
end
WR_END:
begin
wr_cmd <= NOP;
wr_ba <= 2'b11;
wr_sdram_addr <= 13'h1fff;
end
default:
begin
wr_cmd <= NOP;
wr_ba <= 2'b11;
wr_sdram_addr <= 13'h1fff;
end
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
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