SDRAM 控制器(四)——数据写模块

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_clk1时钟信号,100Mwr_sdram_cmd44位SDRAM命令,组成{CS#,RAS#,CAS#,WE#}
sys_rst_n1复位信号,低电平有效wr_sdram_bank22位BANK地址,共4个BANK
init_end1初始化完成信号,只有完成初始化后才能进行写操作wr_sdram_addr1313位SDRAM地址
wr_addr24写操作的一些地址,分别有bank地址(2位)、行地址(13位)、列地址(9位)组成wr_sdram_data16输出到仲裁模块的要写入SDRAM的数据
wr_data16要写入SDRAM的数据wr_end1写操作结束信号,只在写操作结束后维持一个时钟周期
wr_burst_len10一次突发写的长度wr_sdram_en1写模块数据输出使能
wr_en1写使能信号,该信号拉高后才可以进行写操作wr_ack1写操作响应信号,表示该模块对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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发光中请勿扰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值