欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SDRAM小记

程序员文章站 2022-04-01 22:59:54
...

一、知识点

  1. 存储单元主要由列选通三极管、存储电容、刷新放大器组成。
  2. SRAM(异步静态随机存储器)要正确存储一位数据,需要最少6个晶体管,因此,从芯片面积上来说,单片SRAM芯片的容量不可能做到很高。
  3. 工作电压:SDRAM的工作电压是3.3V,DDR的工作电压是2.5V,DDR2的工作电压是1.8V
  4. HY57V281620 芯片,容量128Mbit,数据位宽16bit,具有4个BANK,每个BANK存储32MBit的数据。每个BANK由4096*512个存储单元组成,每个存储单元存储16bit数据。
  5. 控制总线:由4条控制总线来控制SDRAM:
    片选信号CS_N,行选通信号RAS_N,列选通信号CAS_N,写使能信号WE_N。
    13位地址总线,2位BANK地址线,2位掩码DQM
  6. 为了保证SDRAM器件读写的稳定性,控制器的时钟和器件工作时钟均为100MHz,但是器件工作时钟理论向后偏移180°,考虑到布局布线延迟,实际不偏移。
  7. 为了保证存储的数据不丢失,每一行每64ms刷新一次

二、具体工作时序

  1. 上电初始化SDRAM小记
module sdr_initial(
			input clk,
			input sdr_rst_n,
			input sdr_initial_en,
			
			output [19:0]init_bus,
			output reg initial_done
			);
			
		parameter clk_period = 10;//时钟周期10ns
		parameter t100us = 100000/clk_period;
		parameter tRp = 30/clk_period;//延迟30ns
		parameter tRfc =100/clk_period;//延迟100ns
		parameter tMrd = 20/clk_period;//延迟2个时钟周期
		
		parameter IDEL=7'b000_0001,//空闲态
					 s0  =7'b000_0010,//初始化状态,延迟100ns
					 s1  =7'b000_0100,//NOP过渡状态
					 s2  =7'b000_1000,//预充电
					 s3  =7'b001_0000,//自动刷新
					 s4  =7'b010_0000,//自动刷新
					 s5  =7'b100_0000;//加载模式寄存器
						 
						 
		parameter NOP = 4'b0111,//空操作命令
					 PRE = 4'b0010,//预充电命令
					 AREF= 4'b0001,//自动刷新命令
					 MEST= 4'b0000,//加载模式寄存器命令
					 ACT = 4'b0011;//**命令
					 
		parameter ZEROS = 3'b000,//默认3个0
					 OP_CODE = 1'b0,//写突发模式设置
					 OP_MODE = 2'b00,//标准模式
					 CLs = 2,//列选通潜伏期:2
					 BT = 1'b0,//突发类型:顺序
					 BLs = 2;//突发长度:2
		
		parameter BL=(BLs==1)?3'b000:
						 (BLs==2)?3'b001:
						 (BLs==4)?3'b010:
						 (BLs==8)?3'b011:3'b111;//突发长度为1 2 4 8 或者FULL PAGE(111)
		parameter CL=(CLs==2)?3'b010:3'b011;//列选通潜伏期为2或者3
					 
					 
		 
		reg [6:0]state;
		reg [15:0]cnt;
		reg [1:0]init_ba;
		reg [12:0]init_addr;
		reg [3:0]init_cmd;
		reg init_cke;
		
		assign init_bus = {init_ba,init_addr,init_cmd,init_cke};	
		
		aaa@qq.com(posedge clk or negedge sdr_rst_n)
		if(!sdr_rst_n)begin
				cnt 		    <= 16'd0;
				init_cke     <= 1'd0;
				init_addr    <= 13'd0;
				init_cmd 	 <= NOP;
				initial_done <= 1'd0;
				state			 <= IDEL;
				init_ba      <= 2'd0;
							end
		else begin
			case(state)
				IDEL:
					begin
						if(sdr_initial_en)
						begin
							state <= s0;
							end
						else begin
							state        <= IDEL;
							cnt          <= 16'd0;
							init_cke     <= 1'd0;
							init_addr    <= 13'd0;
							init_cmd     <= NOP;
							initial_done <= 1'd0;
							init_ba      <= 2'd0;
								end
					end
				s0:
					begin
						if(cnt >= 9999)
							begin
							init_cke <= 1'd1;
							cnt 		<= 16'd0;
							state 	<= s1;

							end
					else 
							cnt <= cnt+1'b1;
							end
				s1:
				 begin
				 		init_addr[10] <= 1'b1;
						init_cmd 	  <= PRE;
						state 		  <= s2;
					end
					
				s2:
				begin
					if(cnt >=2)
						begin
						cnt 		<= 16'd0;
						init_cmd <= AREF;
						state 	<= s3;
						end
					else begin
						cnt 		<= cnt+1'b1;
						init_cmd <= NOP;
							end
				end
				s3:
				begin
					if(cnt >=9)
						begin
							cnt 		<= 16'd0;
							init_cmd <= AREF;
							state 	<= s4;
						end
					else begin
							cnt 		<= cnt+1'b1;
							init_cmd <= NOP;
							end
				end
				s4:
				begin
					if(cnt >=9)
						begin
							cnt       <= 16'd0;
							init_cmd  <= MEST; 
							init_addr <= {ZEROS,OP_CODE,OP_MODE,CL,BT,BL};//模式寄存器设置
							state     <= s5;
						end
					else begin
							cnt 		<= cnt+1'b1;
							init_cmd <= NOP;
							end
				end
				s5:
				begin
					if(cnt >= 1)
						begin
						initial_done <= 1'b1;
						cnt 			 <= 16'd0;
						state        <=IDEL;
						end
					else begin
						init_cmd     <= NOP;
						cnt          <= cnt+1'b1;
							end
				end
				default:begin
							cnt          <= 16'd0;
							init_cke     <= 1'd1;
							init_addr    <= 13'd0;
							init_cmd     <= NOP;
							initial_done <= 1'd0;
							state        <= IDEL;
							end
				endcase
				end
					 
//			reg [6:0]current_state;
//			reg [6:0]next_state;
//			reg [15:0]cnt;
//			
//			aaa@qq.com(posedge clk or negedge sdr_rst_n)
//			 if(!sdr_rst_n)
//				current_state <= IDEL;
//			 else
//				current_state <= next_state;
//				
//			aaa@qq.com(*)
//			begin
//				case(current_state)
//					IDEL:
//						begin
//							if(sdr_initial_en)
//								next_state = s0;
//							else
//								next_state = IDEL;
//						end
//					s0:
//						begin
//							if(cnt>=t100us-1)
//								next_state=s1;
//							else
//								next_state = current_state;
//						end
//					s1:
//						begin
//								next_state = s2;
//						end
//					s2:
//						begin
//							if(cnt>=tRp-1)
//								next_state = s3;
//							else 
//								next_state = current_state;
//						end
//					s3:
//						begin
//							if(cnt>=tRfc-1)
//								next_state = s4;
//							else	
//								next_state = current_state;
//						end
//					s4:
//						begin
//							if(cnt>=tRfc-1)
//								next_state = s5;
//							else
//								next_state = current_state;
//						end
//					s5:
//						begin
//							if(cnt>=tMrd-1)
//								next_state = IDEL;
//							else
//								next_state = current_state;
//						end
//					default:next_state=IDEL;	
//					endcase
//			end
//		
//		aaa@qq.com(posedge clk or negedge sdr_rst_n)
//		if(!sdr_rst_n)begin
//				cnt <= 16'd0;
//				init_cke <= 1'd0;
//				init_addr <= 13'd0;
//				init_cmd <= NOP;
//				initial_done <= 1'd0;
//			end
//		else begin
//			case(next_state)
//				IDEL:
//					begin
//						cnt <= 16'd0;
//						init_cke <= 1'd0;
//						init_addr <= 13'd0;
//						init_cmd <= NOP;
//					end
//				s0:
//					begin
//						if(cnt >= t100us-1)
//							begin
//							init_cke <= 1'd1;
//							cnt <= 16'd0;
//							end
//					else 
//							cnt<=cnt+1'b1;
//							end
//				s1:
//				 begin
//						init_addr[10] <=1'b1;
//						init_cmd <= PRE;
//					end
//					
//				s2:
//				begin
//					if(cnt >=tRp-1)
//						begin
//						cnt <= 16'd0;
//						init_cmd <= AREF;
//						end
//					else begin
//						cnt <= cnt+1'b1;
//						init_cmd <= NOP;
//							end
//				end
//				s3:
//				begin
//					if(cnt >=tRfc-1)
//						begin
//							cnt <= 16'd0;
//							init_cmd <= AREF;
//						end
//					else begin
//							cnt <= cnt+1'b1;
//							init_cmd <= NOP;
//							end
//				end
//				s4:
//				begin
//					if(cnt >=tRfc-1)
//						begin
//							cnt <= 16'd0;
//							init_cmd <= MEST; 
//							init_addr <= 13'b0_0000_0010_0001;//模式寄存器设置
//						end
//					else begin
//							cnt <= cnt+1'b1;
//							init_cmd <= NOP;
//							end
//				end
//				s5:
//				begin
//					if(cnt >= tMrd-1)
//						begin
//						initial_done <= 1'b1;
//						cnt <= 16'd0;
//						end
//					else begin
//						init_cmd <= NOP;
//						cnt <= cnt+1'b1;
//							end
//				end
//				default:begin
//							cnt <= 16'd0;
//							init_cke <= 1'd1;
//							init_addr <= 13'd0;
//							init_cmd <= NOP;
//							initial_done <= 1'd0;
//							end
//				endcase
//				end
endmodule 
  1. 自动刷新
    SDRAM小记
//自动刷新
module ref_fsm(
					clk,
					rst_n,
					ref_en,
					
					ref_bus,
					ref_done
);

	input clk;
	input rst_n;
	input ref_en;//使能自动刷新(Auto refresh)
	
	output [19:0]ref_bus;
	output reg ref_done;

	
	reg [3:0]state;
	reg [3:0]ref_cnt;//自动刷新时间间隔计数器
	reg [1:0]ref_ba;
	reg [12:0]ref_addr;
	reg [3:0]ref_cmd;
	reg ref_cke;
	
	parameter IDEL = 4'b0001,
				 s0 = 4'b0010,
				 s1 = 4'b0100,
				 s2 = 4'b1000;
				 
	parameter NOP = 4'b0111,//空操作命令
				 PRE = 4'b0010,//预充电命令
				 AREF= 4'b0001,//自动刷新命令
				 MEST= 4'b0000,//加载模式寄存器命令
				 ACT = 4'b0011;//**命令
				 
//	parameter auto_refresh_pediod = 1560;
	
	assign ref_bus = {ref_ba,ref_addr,ref_cmd,ref_cke};
	aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)begin
		state <= IDEL;
		ref_cmd <= NOP;
		ref_ba <= 2'd0;
		ref_addr <= 13'd0;
		ref_done <= 1'd0;
		ref_cke <= 1'b1;
		ref_cnt <= 4'b0;
					end
	else begin
		case(state)
			IDEL:
			begin
				if(ref_en) begin
					state <= s0;
					ref_cmd <= PRE;
					ref_addr[10] <= 1'd1;
					ref_done <= 1'd0;
					ref_cke <= 1'b1;
												end
				else begin
					state <= IDEL;
					ref_cmd <= NOP;
					ref_ba <= 2'd0;
					ref_addr <= 13'd0;
					ref_done <= 1'd0;
					ref_cnt <= 4'b0;
					ref_cke <= 1'b1;
						end
			end
			
			s0:
			begin
				state <= s1;
				ref_cmd <= NOP;
			end
				
			s1:
			begin
				state <= s2; 
				ref_cmd <= AREF;
			end
			
			s2://tRFC
			begin
				if(ref_cnt>=9)begin
					ref_cnt <= 4'd0;
					state <= IDEL;
					ref_done <= 1'b1;
									end
				else begin
					ref_cnt <= ref_cnt+1'b1;
					ref_cmd <= NOP;
						end
			end
			
			default:begin
						state <= IDEL;
						ref_cmd <= NOP;
						ref_ba <= 2'd0;
						ref_addr <= 13'd0;
						ref_done <= 1'd0;
						end
			endcase
	
			end
	
//	aaa@qq.com(posedge clk or negedge rst_n)
//	if(!rst_n)begin
//		rt_flag <= 1'd0;
//		cnt <= 11'd0;
//		ref_almost <= 1'b0;
//				 end
//	else if(en_cnt) begin
//		if(cnt >= (auto_refresh_pediod-1))begin
//			rt_flag <= 1'b1;
//			cnt <= 10'd0;
//			ref_almost <= 1'b0;
//														end
//		else if(cnt >= (auto_refresh_pediod-21))begin
//			ref_almost <= 1'b1;
//			rt_flag <= 1'd0;
//			cnt <= cnt+1'b1;
//															end
//		else begin
//			rt_flag <= 1'd0;
//			cnt <= cnt+1'b1;
//			ref_almost <= 1'b0;
//				end
//								end
//	else begin
//		   rt_flag <= 1'd0;
//		   cnt <= 11'd0;
//			ref_almost <= 1'b0;
//			end
//		
				


endmodule 
  1. 写时序(写数据的时候没有潜伏期,在发出写明令的同时需要立即给出要写的数据)
    SDRAM小记
    //写状态机
    module wr_fsm(rst_n,
    clk,
    wr_en,
    row,
    col,
    wr_data,
    ba,

    		  wr_bus,
    		  wr_done,
    		  wr_out_en,
    		  dq_out
    		  );
    

    input rst_n;
    input clk;
    input wr_en;
    input [12:0]row;
    input [9:0]col;
    input [31:0]wr_data;
    input [1:0]ba;

    output [19:0]wr_bus;
    output reg wr_done;
    output reg wr_out_en;
    output reg[15:0]dq_out;

    parameter NOP = 4’b0111,//空操作命令
    PRE = 4’b0010,//预充电命令
    AREF= 4’b0001,//自动刷新命令
    MEST= 4’b0000,//加载模式寄存器命令
    ACT = 4’b0011,//**命令
    WR = 4’b0100,//写命令
    RD = 4’b0101;//读命令

parameter IDEL = 4’b0001,
s0 = 4’b0010,
s1 = 4’b0100,
s2 = 4’b1000;

reg [3:0]state;
reg [2:0]cnt;

reg [1:0]wr_ba;
reg [12:0]wr_addr;
reg [3:0]wr_cmd;
reg wr_cke;
assign wr_bus = {wr_ba,wr_addr,wr_cmd,wr_cke};
aaa@qq.com(posedge clk or negedge rst_n)
if(!rst_n)begin
	state     <= IDEL;
	cnt       <= 3'd0;
	wr_ba     <= 2'b0;
	wr_addr   <= 13'd0;
	wr_cke    <= 1'b1; 
	wr_cmd    <= NOP;
	wr_done   <= 1'b0;
	wr_out_en <= 1'b0;
	dq_out    <= 16'd0;
				end
else
	case(state)
		IDEL:
		begin
			if(wr_en)begin
				state   <= s0;		//**bank、行
				wr_cmd  <= ACT;
				wr_ba   <= ba;
				wr_addr <= row;
						end
			else begin
				state     <= IDEL;
				cnt       <= 3'd0;
				wr_ba     <= 2'b0;
				wr_addr   <= 13'd0;
				wr_cke    <= 1'b1; 
				wr_cmd    <= NOP;
				wr_done   <= 1'b0;
				wr_out_en <= 1'b0;
				dq_out    <= 16'd0;
					end
		end
		
		s0://延迟tRCD,再**列
		begin
			if(cnt >= 3'd2)begin//延迟3个周期
				state		 	 <= s1;
				cnt 			 <= 3'd0;
				wr_addr[10]  <= 1'b1;//打开自动预充电
				wr_addr[9:0] <= col;
				wr_cmd       <= WR;
				dq_out    	 <= wr_data[15:0];
				wr_out_en       <= 1'b1;				
								end
			else	begin
				cnt 	 <= cnt+1'b1;
				wr_cmd <= NOP;
					end
		end
		
		s1:
		begin
			state <= s2;
			wr_cmd <= NOP;
			dq_out <= wr_data[31:16];
		end
		
		s2:begin
			wr_out_en <= 1'b0;
			if(cnt >= 3'd4)begin  //延时tWR(2 clk)以及tRP(3clk)
					state     <= IDEL;
					wr_done   <= 1'b1;
					cnt       <= 3'd0;
									end
			else begin
					state     <=s2;
					cnt       <= cnt +1'b1;
					wr_done   <= 1'b0;
					end
		end
		
		default:begin
					state     <= IDEL;
					cnt       <= 3'd0;
					wr_ba     <= 2'b0;
					wr_addr   <= 13'd0;
					wr_cke    <= 1'b1; 
					wr_cmd    <= NOP;
					wr_done   <= 1'b0;
					wr_out_en <= 1'b0;
					dq_out    <= 16'd0;
					end
		endcase

endmodule

//
// write way 2
//
/
//module sdram_write(
// input sclk,
// input !s_rst_n
//
// input wr_en,
// output reg wr_req,
// output reg flag_wr_end,
//
// input ref_req,
// input wr_trig,
//
// output reg [3:0]wr_cmd,
// output reg [11:0]wr_addr,
// output reg [1:0]bank_addr
// );
//
//
// reg flag_wr;
// reg [4:0]state;
//
// reg flag_act_end;
// reg flag_pre_end;
// reg sd_row_end;
// reg [1:0]burst_cnt;
// reg [3:0]act_cnt;
// reg [1:0]burst_cnt_t;
// reg wr_data_end;//数据写完标志
// reg [3:0]break_cnt;
// reg [6:0]col_cnt;
// reg [11:0]row_addr;
// reg [8:0]col_addr;
//
//
// localparam S_IDLE = 5’b0_0001;
// localparam S_REQ = 5’b0_0010;
// localparam S_ACT = 5’b0_0100;
// localparam S_WR = 5’b0_1000;
// localparam S_PRE = 5’b1_0000;
//
//
// //flag_wr
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_wr<= 1’b0;
// else if(wr_trig && flag_wr==1’d0)
// flag_wr<= 1’b1;
// else if(wr_data_end)
// flag_wr<= 1’b0;
//
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// state <= S_IDLE;
// else case(state)
// S_IDLE:
// if(wr_trig)
// state <= S_REQ;
// else
// state <= S_IDLE;
//
// S_REQ:
// if(wr_en)
// state <= S_ACT;
// else
// state <= S_REQ;
//
//
// S_ACT:
// if(flag_act_end)
// state <= S_WR;
// else
// state <= S_ACT;
//
// S_WR:
// if(wr_data_end)
// state <= S_PRE;
// else if(ref_req && burst_cnt_t == 'd3 &&flag_wr)
// state <= S_PRE;
// else if(sd_row_end && flag_wr)
// state <= S_PRE;
//
// S_PRE;
// if(ref_req && flag_wr)
// state <= S_REQ;
// else if(sd_row_end && flag_wr)
// state <= S_ACT;
// else if(wr_data_end)
// state <= S_IDLE;
// default:
// state <= S_IDLE;
// endcase
//
//
--------------------------------------------------
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_cmd <= NOP;
// else case(state)
// S_ACT:
// if(act_cnt == 'd0);
// wr_cmd <= ACT;
// else
// wr_cmd <= NOP;
// S_WR:
// if(burst_cnt == 'd0)
// wr_cmd <= WR;
// else
// wr_cmd <= NOP;
// S_PRE:
//
// if(break_cnt == 'd0)
// wr_cmd <= PRE;
// else
// wr_cmd <= NOP;
// //
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_addr <= 'd0;
// else case(state)
// S_ACT:if(act_cnt == 'd0)
// wr_addr <= row_addr;
// S_WR:
// wr_addr <= {3’b010,col_addr};
// S_PRE:if(break_cnt =='d0)
// wr_addr[10] <= 'd1;
//
//
// // flag_act_end
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_act_end <= 1’b0;
// else if(act_cnt == 'd3)
// flag_act_end <= 1’b1;
// else
// flag_act_end <= 1’b0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// act_cnt <= 'd0;
// else if(state == S_ACT)
// act_cnt <= act_cnt +1’b1;
// else
// act_cnt <= 'd0;
//
//
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// flag_pre_end <= 1’b0;
// else if(break_cnt == 'd3)
// flag_pre_end <= 1’b1;
// else
// flag_pre_end <=1’b0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// break_cnt <= 'd0;
// else if(state == S_PRE)
// break_cnt <= break_cnt +1’b1;
// else
// break_cnt <= 'd0;
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// burst_cnt <= 'd0;
// else if(state == S_WR)
// burst_cnt <= burst_cnt +1’b1;
// else
// burst_cnt <= 'd0;
//
//
// aaa@qq.com(posedge sclk )
// burst_cnt_t <= burst_cnt;
//
// //
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// wr_data_end <= 'd0;
// else if(row_addr == 'd1 && col_addr == 'd511)
// wr_data_end <= 1’b1;
// else
// wr_data_end <= 'd0;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// col_cnt <= 'd0;
// else if(col_addr == 'd511)
// col_cnt <= 'd0;
// else if(burst_cnt_t == 'd3)//突发长度4
// col_cnt <= col_cnt+1’b1;
// else
// col_cnt <= col_cnt;
//
// aaa@qq.com(posedge sclk or negedge !s_rst_n)
// if(!s_rst_n)
// row_addr <= 'd0;
// else if(sd_row_end)
// row_addr <= row_addr +1’b1;
//
//
// assign col_addr = {col_cnt,burst_cnt_t};
// assign bank_addr = 2’b00;
//
//endmodule
4. 读时序
SDRAM小记
理想读时序波形:
SDRAM小记
实际读时序波形:
SDRAM小记

//读状态机
module rd_fsm(
				  clk,
				  capture_clk,
				  rst_n,
				  rd_en,
				  row,
				  col,
				  sdr_dq,
				  ba,
				  
				  rd_done,
				  rd_data,
				  rd_bus
			);
			
	 input clk;
	 input capture_clk;
	 input rst_n;
	 input rd_en;
	 input [1:0]ba;
	 input [12:0]row;
	 input [9:0]col;
	 input[15:0]sdr_dq;
		
	 output reg rd_done;
	 output reg[31:0]rd_data;
	 output [19:0]rd_bus;
	 
	 parameter NOP = 4'b0111,//空操作命令
				  PRE = 4'b0010,//预充电命令
				  AREF= 4'b0001,//自动刷新命令
				  MEST= 4'b0000,//加载模式寄存器命令
				  ACT = 4'b0011,//**命令
				  WR  = 4'b0100,//写命令
				  RD  = 4'b0101;//读命令

	parameter IDEL = 6'b000001,
				 s0   = 6'b000010,
				 s1   = 6'b000100,
				 s2   = 6'b001000,
				 s3   = 6'b010000,
				 s4   = 6'b100000;
				
	 parameter cl=2,//列选通潜伏期 2
				  sl=2;//同步数据潜伏期 :前仿真设置为1,后仿真改为2
	 reg [1:0]rd_ba;
	 reg [12:0]rd_addr;
	 reg [3:0]rd_cmd;
	 reg rd_cke;
	 
	 reg [5:0]state;
	 reg [2:0]cnt;
	 reg load_h;
	 reg load_l;
	 
	 assign rd_bus = {rd_ba,rd_addr,rd_cmd,rd_cke};
	
    aaa@qq.com(posedge clk or negedge rst_n)	
	 if(!rst_n)	begin
		state   <= IDEL;
		cnt     <= 3'b0;
		rd_ba   <= 2'b0;
		rd_addr <= 13'b0;
		rd_cmd  <= NOP;
		rd_cke  <= 1'b1;
		rd_done <= 1'b0;
		load_l  <= 1'b0;
		load_h  <= 1'b0;
					 end
	 else begin
		case(state)
			IDEL:
			begin
				if(rd_en)begin
					state   <= s0;
					rd_cmd  <= ACT;
					rd_addr <= row;
					rd_ba   <= ba;
							 end
				else begin
					state   <= IDEL;
					cnt     <= 3'b0;
					rd_ba   <= 2'b0;
					rd_addr <= 13'b0;
					rd_cmd  <= NOP;
					rd_cke  <= 1'b1;
					rd_done <= 1'b0;
					load_l  <= 1'b0;
					load_h  <= 1'b0;
						end
			end
			
			s0:
			begin
				state   <= s1;
				rd_cmd  <= NOP;
			end
			
			s1:
			begin
				state   <= s2;
				rd_cmd  <= RD;
				rd_addr <= {3'b001,col};
				rd_ba   <= ba;
			end
			
			s2:
			begin
				if(cnt >= cl+sl-1)begin
					cnt <= 3'd0;
					state <= s3;
					load_l <= 1'b1;
						end
				else begin
					cnt <= cnt+1'b1;
					state <=s2;
					rd_cmd  <= NOP;
						end
			end
			
			s3:
			begin
				load_h <= 1'b1;
				load_l <= 1'b0;
				state  <= s4;
			end
			
			s4:
			begin
				load_h <= 1'b0;
				rd_done <= 1'b1;
				state   <= IDEL;
			end
			
			default:begin
					state   <= IDEL;
					cnt     <= 3'b0;
					rd_ba   <= 2'b0;
					rd_addr <= 13'b0;
					rd_cmd  <= NOP;
					rd_cke  <= 1'b1;
					rd_done <= 1'b0;
					load_l  <= 1'b0;
					load_h  <= 1'b0;
						end
		 endcase
			 end
	reg [15:0] dq_capture;	
   reg [15:0]	dq_sys;
	aaa@qq.com(posedge capture_clk)
		dq_capture <= sdr_dq;
		
	aaa@qq.com(posedge clk)
		dq_sys <= dq_capture;
	
	aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		rd_data <= 32'd0;
	else if(load_l)
		rd_data[15:0] <= dq_sys;
	else if(load_h)
		rd_data[31:16] <= dq_sys;
 
endmodule 

三、主状态机

//主状态机
module main_fsm(clk,
					 rst_n,
					 local_addr,
					 col,
					 row,
					 ba,
					 local_ready,//状态显示:1=不忙,可以进行读写;0=忙
					
					 
					 init_en,
					 initial_done,
					 ref_en,
					 ref_done,
					 rt_en,
					 rt_flag,
//					 ref_almost,
					 
					 local_wr,
					 wr_en,
					 wr_done,
					 w_data,
					 local_wdata,
					 
					 
					 local_read,
					 rd_en,
					 rd_done,
					 r_data,
					 local_rdata,
					 local_rdatavalid,
					 
					 s_mux
					 );
					 
	input clk;
	input rst_n;
	input [24:0]local_addr;
	output reg local_ready;
	output reg[12:0]row;
	output reg[9:0]col;
	output reg[1:0]ba;

	output reg init_en;
	input initial_done;
	input ref_done;
	output reg ref_en;
	output reg rt_en;//自动刷新使能信号
	input rt_flag;
//	input ref_almost;//距离下次刷新的时间不足以完成一次读或写的时刻
	
	input local_wr;
	output reg wr_en;
	input wr_done;
	input [31:0]local_wdata;
	output reg[31:0]w_data;
	
	input local_read;
	output reg rd_en;//读使能
	input rd_done; //读完成
	input [31:0]r_data;
	output reg [31:0]local_rdata;//读出数据
	output reg local_rdatavalid;//读数据有效信号
	
	output reg[2:0]s_mux;
	
	
	parameter s0 = 6'b000001,
				 s1 = 6'b000010,
				 s2 = 6'b000100,
				 s3 = 6'b001000,
				 s4 = 6'b010000,
				 s5 = 6'b100000;
				 
  parameter INIT = 3'd1,
				REF  = 3'd2,
				WR   = 3'd3,
				RD   = 3'd4;
	
	
  reg [5:0]state;
	reg ref_trigger;
	//寄存自动使能信号  ,如果系统处于仲裁状态(s3),则直接进入自动刷新模式;如果系统处于读写阶段,则寄存下
	//该自动刷新命令会寄存在ref_trigger寄存器内,直到读写结束重新回到仲裁状态,产生ref_en信号使能刷新,并将
	//ref_trigger 置低。自动刷新时间为15us,远远高于15.6us的最低要求,富裕的时间足以完成一次读写突发。
	aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
			ref_trigger <= 1'b0;
	else if(rt_flag)
			ref_trigger <= 1'b1;
	else if(ref_en)
			ref_trigger <= 1'b0;
	else
			ref_trigger <= ref_trigger;
	
	aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n) begin
		state            <= s0;
		ref_en 			  <= 1'b0;
		rt_en  			  <= 1'b0;
		init_en 			  <= 1'b0;
		s_mux   			  <= INIT;
		wr_en   			  <= 1'b0;
		rd_en   			  <= 1'b0;
		w_data  			  <= 32'd0;
		col     			  <= 10'd0;
		row     			  <= 13'd0;
		ba      			  <= 2'b0;
		local_ready 	  <= 1'b0;
		local_rdata 	  <= 32'd0;
		local_rdatavalid <= 1'b0;
					end
	else begin
		case(state)
		  s0:
		  begin
	
				init_en <= 1'b1;
				state   <= s1;
		  end


		  s1://
		  begin
				if(initial_done) begin
					state  <= s2;
					ref_en <= 1'b1;
					rt_en  <= 1'b1;
					s_mux  <= REF;
										end
			   else begin
					init_en <= 1'b0;
					state   <= s1;
						end
		  end
		  
		  s2://自动刷新完成后进入等待命令状态
		  begin
				ref_en 		<= 1'b0;
				if(ref_done)begin
					state 		<= s3;
					s_mux 		<= REF;
					local_ready <= 1'b1;
								end
				else
					state <= s2;
					s_mux <= REF;
					local_ready <= 1'b0;
		  end
		  
		  s3://仲裁状态:等待读写、自动刷新命令
		  begin
				if(ref_trigger)begin
					state  		<= s2;
				   ref_en 		<= 1'b1;
					local_ready <= 1'b0;
										end
				else if(local_wr)begin
					state 		<= s4;
					wr_en 		<= 1'b1;
					s_mux 		<= WR;
					local_ready <= 1'b0;
					w_data      <= local_wdata;
					row   	   <= local_addr[22:10];
					col   		<= local_addr[9:0];
					ba    		<= local_addr[24:23];
										end
				else if(local_read) begin
					state 			  <= s5;
					rd_en				  <= 1'b1;
					s_mux 		     <= RD;
					local_ready 	  <= 1'b0;
					row   			  <= local_addr[22:10];
					col   			  <= local_addr[9:0];
					ba    			  <= local_addr[24:23];
					local_rdatavalid <= 1'b0;
											end
				else begin
					state 			  <= s3;
					s_mux 			  <= REF;
					local_rdatavalid <= 1'b0;
					ref_en 			  <= 1'b0;
					wr_en 			  <= 1'b0;
					local_ready 	  <= 1'b1;
					  end
		  end
		  
		  s4:
		  begin
				if(wr_done)begin
					state		   <= s3;
					local_ready <= 1'b1;
					s_mux       <= REF;
								end
				else begin
					state <= s4;
					wr_en <= 1'b0;
						end
		  end
		  
		  s5:
		  begin
				if(rd_done)begin
					state 			  <= s3;
					s_mux 			  <= REF;
					local_ready 	  <= 1'b1;
					local_rdatavalid <= 1'b1;
					local_rdata      <= r_data;
								end
				else begin
					state 			  <= s5;
					rd_en				  <= 1'b0;
						end
		  end
		  
		  default:begin
						state            <= s0;
						ref_en 			  <= 1'b0;
						rt_en  			  <= 1'b0;
						init_en 			  <= 1'b0;
						s_mux   			  <= REF;
						wr_en   			  <= 1'b0;
						rd_en   			  <= 1'b0;
						w_data  			  <= 32'd0;
						col     			  <= 10'd0;
						row     			  <= 13'd0;
						ba      			  <= 2'b0;
						local_ready 	  <= 1'b0;
						local_rdata 	  <= 32'd0;
						local_rdatavalid <= 1'b0;
						end
		  endcase
			end

endmodule 

四、顶层模块

//=======================================\
//  前仿真sdr_clk设置为180度,同步潜伏期设置为1
//  后仿真sdr_clk设置为0度,同步潜伏期设置为2
//  capture_clk均设置为180度
//=======================================/
module sdram_ctrl_top(
							 clk50M,
							 sdr_clk,
							 rst_n,

							 sdr_ba,
							 sdr_addr,
							 sdr_dqm,
							 sdr_dq,
							 sdr_CS_N,
							 sdr_RAS_N,
							 sdr_CAS_N,
							 sdr_WE_N,
							 sdr_CKE,
							 
							 local_wdata,//待写入数据
							 local_rdata,//读出的数据
							 local_ready,//可以进行读写操作信号
							 local_addr,//待写入数据的行列BANK地址
							 local_read,
							 local_wr,
							 local_rdatavalid,
							 clk
							 );
							 
	input clk50M;
	output sdr_clk;
	input rst_n;
							 
	output [1:0]sdr_ba;
	output [12:0]sdr_addr;
	output [1:0]sdr_dqm;
	inout [15:0]sdr_dq;
	output sdr_CS_N;
	output sdr_RAS_N;
	output sdr_CAS_N;
	output sdr_WE_N;
	output sdr_CKE;
							 
	input [31:0]local_wdata;//待写入数据
	output [31:0]local_rdata;//读出的数据
	output local_ready;//可以进行读写操作信号
	input [24:0]local_addr;//待写入数据的行列BANK地址
	input local_read;
	input local_wr;
	output local_rdatavalid;
	output clk;
	


	wire sys_rst_n;
	wire locked;
//------------------------------------------------------------------
	wire capture_clk;
	wire [9:0]col;
	wire [12:0]row;
	wire [1:0]ba;
	wire init_en;
	wire initial_done;
	wire ref_en;
	wire ref_done;
	wire rt_en;
	wire rt_flag;//刷新计数器记满标志
//	wire ref_almost;
	wire wr_en;
	wire wr_done;
	wire [31:0]w_data;
	wire rd_en;
	wire rd_done;
	wire [31:0]r_data;
	wire [2:0]s_mux;
	wire [19:0]init_bus;
	wire [19:0]wr_bus;
	wire wr_out_en;
	wire [15:0]dq_out;
	wire [19:0]ref_bus;
	wire [19:0]rd_bus;
	wire [19:0]s_bus;
	
	assign sys_rst_n = rst_n && locked;//待PLL输出稳定后停止复位
	assign sdr_dq = wr_out_en?dq_out:{16{1'bz}};//设置三态门
	assign sdr_dqm = 2'b0;
	assign {sdr_ba,sdr_addr,sdr_CS_N,sdr_RAS_N,sdr_CAS_N,sdr_WE_N,sdr_CKE} = s_bus;
//	assign {sdr_ba,sdr_addr,sdr_CS_N,sdr_RAS_N,sdr_CAS_N,sdr_WE_N,sdr_CKE} = (s_mux == INIT)?init_bus:
//																									  (s_mux == REF)?ref_bus :
//																									  (s_mux == WR)?wr_bus:rd_bus;
																									  
	

	pll_sdram PLL_SDRAM(
							  .areset(1'b0),
							  .inclk0(clk50M),
							  .c0(clk),
							  .c1(sdr_clk),
							  .c2(capture_clk),
							  .locked(locked)
					);
	
	main_fsm MAIN_FSM(
							.clk(clk),
							.rst_n(sys_rst_n),
							.local_addr(local_addr),
							.col(col),
							.row(row),
							.ba(ba),
							.local_ready(local_ready),
						
							.init_en(init_en),
							.initial_done(initial_done),
							.ref_en(ref_en),
							.ref_done(ref_done),
							.rt_en(rt_en),
							.rt_flag(rt_flag),
//							.ref_almost(ref_almost),
							
							.local_wr(local_wr),
							.wr_en(wr_en),
							.wr_done(wr_done),
							.w_data(w_data),
							.local_wdata(local_wdata),
							
							
							.local_read(local_read),
							.rd_en(rd_en),
							.rd_done(rd_done),
							.r_data(r_data),
							.local_rdata(local_rdata),
							.local_rdatavalid(local_rdatavalid),
								
							.s_mux(s_mux)
					    );
					 
			
		sdr_initial SDR_INITIAL(
										.clk(clk),
										.sdr_rst_n(sys_rst_n),
										.sdr_initial_en(init_en),
			
										.init_bus(init_bus),
										.initial_done(initial_done)
			);
			
		ref_fsm REF_FSM(
							 .clk(clk),
							 .rst_n(sys_rst_n),
							 .ref_en(ref_en),
							 
							 .ref_bus(ref_bus),
							 .ref_done(ref_done)
			);
			
		ref_rt_cnt REF_RT_CNT(
									 .rst_n(sys_rst_n),
						          .clk(clk),
						          .en_cnt(rt_en),
						          .rt_flag(rt_flag)
//						          .ref_almost(ref_almost)
						);
		wr_fsm WR_FSM(
						  .rst_n(sys_rst_n),
						  .clk(clk),
						  .wr_en(wr_en),
						  .row(row),
						  .col(col),
						  .wr_data(w_data),
						  .ba(ba),
						  
						  .wr_bus(wr_bus),
						  .wr_done(wr_done),
						  .wr_out_en(wr_out_en),
						  .dq_out(dq_out)
				  );
	
	rd_fsm RD_FSM(
					  .clk(clk),
					  .capture_clk(capture_clk),
					  .rst_n(sys_rst_n),
					  .rd_en(rd_en),
					  .row(row),
					  .col(col),
					  .sdr_dq(sdr_dq),
					  .ba(ba),
					  
					  .rd_done(rd_done),
					  .rd_data(r_data),
					  .rd_bus(rd_bus)
			);
	
	mux_bus MUX_BUS(
						 .s_mux(s_mux),
						 .wr_bus(wr_bus),
						 .rd_bus(rd_bus),
						 .ref_bus(ref_bus),
						 .init_bus(init_bus),
						 .s_bus(s_bus)
			  );
					
					
endmodule