SDRAM-学习4-数据写模块

221 阅读4分钟

浅谈预充电命令

预充电命令关闭在指定 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)。

数据写模块:

image.png 输入:

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地址总线要写入列地址,列首地址;写入数据;更新数据,在最后一个数据后的下一个时钟,写入突发终止信号。

image.png

波形图绘制

数据写入完成后 进行预充电 保持一个周期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:

image.png

程序编写

(
	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