SDRAM-学习5- 数据读模块

135 阅读2分钟

不带自动预充电的 页突发数据读模式:

image.png

读取6个数据 发送页突发停止指令。 第六个周期就写入页突发停止指令。

image.png

读指令后会有潜伏期之后,才会读数据。 A4-A6为潜伏期。 011 为三个潜伏期。

潜伏期之后读数据,突发长度为 潜伏期+读数据长度 13个周期

cnt_clk cnt_clk_rst 信号也需要。

激活信号等待结束信号trcd_end 潜伏期等待结束信号tcl_end 读状态的等待结束信号trd_end 预充电等待结束信号trp_end

同步数据到当前时钟频率下 跨时钟域信号处理

输入数据通过寄存器打一拍,rd_data_reg

bank 地址和地址总线 开始为全1,激活后地址为全0 表示对第0个bank第0行,数据读取时写入数据列地址。 潜伏期等待全为1,写入突发终止指令在读指令开始的下个周期写入,开始计数,第n个周期开始写入。对所有bank进行预充电 地址总线写入13'h400. A10 为高电平,表示对所有bank进行操作。

image.png

程序

(
   input wire sys_clk,
	input wire sys_rst_n,
	input wire	init_end,
	input wire [23:0]rd_addr,    //两位宽bank地址 13位宽行地址 9位宽列地址
	input wire [15:0]rd_data,    //sdram 芯片位宽为16位
	input wire [9:0]rd_burst_len,  //我们使用页突发,我们使用512,表示512需要10位
	input wire rd_en, 
	
	output reg [3:0]rd_cmd,	
	output reg [1:0]rd_ba,
	output reg [12:0]rd_sdram_addr,
	output reg rd_sdram_en,
	output wire rd_end,
	output wire [15:0]rd_sdram_data,
	output wire rd_ack


);
	reg [15:0]rd_data_reg;
	wire rd_b_end;
   reg [3:0]rd_state;
	reg [9:0]cnt_clk;
	reg cnt_clk_rst;
	
	wire trcd_end;
	wire tcl_end;
	wire trd_end;
	wire trp_end;

	//指令
parameter NOP = 4'b0111,

ACTIVE = 4'b0011,
WR_CMD = 4'b0100,
RD_CMD = 4'B0101,
B_STOP = 4'b0110,
P_CHARGE =4'b0010;


	
	
	
	
	
	//格雷码表示状态  读状态九个状态,
parameter RD_IDLE =  4'b0000,
RD_ACTIVE = 			4'b0001,
RD_TRCD = 				4'b0011,
READ= 					4'b0010,
RD_CL = 					4'b0110,
RD_DATA = 				4'b0111,
RD_PCH = 				4'b0101,
RD_TRP = 				4'b0100,
RD_END = 				4'b1100;
	
	//等待的参数
	parameter TRCD = 3'd2,
	TCL = 3'd3,
TRP = 3'd7;
	
	// 读数据  数据寄存 
	always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rd_data_reg <= 16'd0;
	else
		rd_data_reg <= rd_data;
	
	always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rd_state <= RD_IDLE;
	else
		case(rd_state)
			RD_IDLE:
					if(init_end == 1'b1 && rd_en == 1'b1)
						rd_state <= RD_ACTIVE;
					else	
						rd_state <= rd_state;
				
			
			RD_ACTIVE:
					rd_state <= RD_TRCD;
			
			RD_TRCD:
				   if(trcd_end == 1'b1 )
						rd_state <= READ;
					else	
						rd_state <= rd_state;
			
			READ:
					rd_state <= RD_CL;
			RD_CL:
					rd_state <= (tcl_end == 1'b1 )?RD_DATA:RD_CL;
					
			RD_DATA:
					 rd_state <= (trd_end == 1'b1 )?RD_PCH:RD_DATA;
			
			
			RD_PCH:
					rd_state <= RD_TRP;
			RD_TRP:
					 rd_state <= (trp_end == 1'b1 )?RD_END:RD_TRP;
			RD_END:
				rd_state <= RD_IDLE;
			default:
				rd_state <= RD_IDLE;
	
	
	endcase

   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(rd_state)
             RD_IDLE: cnt_clk_rst <= 1'b1;
             RD_TRCD: cnt_clk_rst <= (trcd_end == 1'b1) ? 1'b1:1'b0;
             READ :cnt_clk_rst <= 1'b1;
				 RD_CL: cnt_clk_rst <= (tcl_end == 1'b1) ? 1'b1:1'b0;
             RD_DATA:cnt_clk_rst <= (trd_end==1'b1)?1'b1:1'b0;
             RD_PCH: cnt_clk_rst <= (trp_end==1'b1)?1'b1:1'b0;
				 RD_END: cnt_clk_rst <=1'b1;
             default: cnt_clk_rst <= 1'b0;           
        endcase
    end
	
	//等待结束信号的赋值
	
	assign trcd_end = ((rd_state <= RD_TRCD)&&(cnt_clk == TRCD))? 1'b1:1'b0;
	assign tcl_end =  ((rd_state <= RD_CL)&&(cnt_clk == TCL))? 1'b1:1'b0;
   assign  trd_end =  ((rd_state <= RD_DATA)&&(cnt_clk == (rd_burst_len-1+TCL)))? 1'b1:1'b0;
   assign trp_end =((rd_state <= RD_TRP)&& (cnt_clk == TRP))? 1'b1:1'b0;
	//用来确定突发终止指令的写入位置
	 assign rd_b_end =(cnt_clk == (rd_burst_len-1'b1-TCL))? 1'b1:1'b0;
	
	//读响应信号赋值
	assign rd_ack = ((rd_state==RD_DATA)&&(cnt_clk <= rd_burst_len)&&(cnt_clk>=1'b1))? 1'b1:1'b0;
//结束信号
assign rd_end = (rd_state == RD_END)?1'b1:1'b0;

	
	//地址的赋值
	
	always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            rd_cmd <=   NOP;
            rd_ba <= 2'b11;
            rd_sdram_addr <= 13'h1fff;
        end
       //结合状态机进行赋值
	 else
       case(rd_state)
			
				RD_IDLE,RD_TRCD,RD_TRP:
					begin 
						rd_cmd <= NOP;
						rd_ba <= 2'b11;
						rd_sdram_addr <= 13'h1fff;
					end
				RD_ACTIVE:
					begin 
						rd_cmd <= ACTIVE;
						rd_ba <= rd_addr[23:24];
						rd_sdram_addr <= rd_addr[21:9];
					end
				READ:
					begin 
						rd_cmd <= RD_CMD;
						rd_ba <= rd_addr[23:22];
						//列地址  分时复用  所以需要拼接
						rd_sdram_addr <= {4'b0000,rd_addr[8:0]};
					end
			
				RD_DATA:
					
					if(rd_b_end == 1'b1)
						rd_cmd <= B_STOP;
					else	
						begin 
						rd_cmd <= NOP;
						rd_ba <= 2'b11;
						rd_sdram_addr <= 13'h1fff;
					end
				RD_PCH:
					begin 
						rd_cmd <= P_CHARGE;
						rd_ba <= rd_addr[23:22];
						
						//对所有bank进行预充电  A10置为1
						rd_sdram_addr <= 13'h0400;
					end
					
					
				RD_END:
					begin 
						rd_cmd <= NOP;
						rd_ba <= 2'b11;
						rd_sdram_addr <= 13'h1fff;
					end
				// 潜伏情况也在默认情况中
				default:
					begin 
						rd_cmd <= NOP;
						rd_ba <= 2'b11;
						rd_sdram_addr <= 13'h1fff;
					end
				

	
	endcase 
	
	// 读出数据赋值
	
	assign rd_sdram_data = (rd_ack == 1'b1)?rd_data_reg:16'd0;
	
	
	

endmodule