SDRAM控制器设计

项目名称

SDRAM控制器设计

具体要求

给sdram存入100个数据并读出

设计说明

      模块设计

                               SDRAM控制器设计_第1张图片

端口设计及端口说明并包含参数文件 

module sdram_ctrl (
	clk,  
	rst_n,
	wr,
	rd,
	caddr,
	raddr,
	baddr,
	wr_data,
	rd_data,
	rd_data_vaild,
	wr_data_vaild,
	wdata_done,
	rdata_done,
	sa,
	ba,
	cs_n,
	cke,
	ras_n,
	cas_n,
	we_n,
	dq,
	dqm
);

`include  "params.h"

	input				clk;
	input				rst_n;
	input				wr;//写使能
	input				rd;//读使能
	input[`ASIZE-1:0]caddr;//写列地址
	input[`ASIZE-1:0]raddr;//写行地址
	input[`BSIZE-1:0]baddr;//写sdram 时的bank地址
	input[`DSIZE-1:0]wr_data;//待写入的数据
	output[`DSIZE-1:0]rd_data;//读出的数据
	output reg			rd_data_vaild;//读数据有效区
	output reg			wr_data_vaild;//写数据有效区
	output				wdata_done;//一次突发写完成标志
	output				rdata_done;//一次突发读完成标志
	
	output reg[`ASIZE-1:0]sa;//地址总线
	output reg[`BSIZE-1:0]ba;//bank总线
	output					 cs_n;//片选信号
	output					 cke;//时钟使能
	output					 ras_n;//行地址选通
	output					 cas_n;//列地址选通 
	output					 we_n;//写使能
	output[`DSIZE-1:0]	 dq;//数据总线
	output[`DSIZE/8-1:0]  dqm;//数据掩码

对初始化模块进行例化

//sdram初始化模块例化	
	wire init_cmd;//初始化命令输出
	wire init_addr;//初始化地址输出
	wire init_done;//初始化完成标志
	sdram_init sdram_init(
	.clk(clk),
	.rst_n(rst_n),
	.command(init_cmd),//命令信号
	.saddr(init_addr),//地址信号
	.init_done(init_done)
);

 使能时钟信号

	assign cke=rst_n;//时钟使能信号

命令信号

reg [3:0] command;//操作命令
assign {cs_n,ras_n,cas_n,we_n}=command;//命令信号

 数据总线,写数据有效区数据总线为写入的数据,否则处于高阻态

	assign dq=wr_data_vaild ? wr_data:16'bz;//sdram数据线。采用三态输出
                                             //最终数据线上的数据给读数据信号

数据掩码,,之前没有说这个信号,实际上就是数据屏蔽,有两位高位和低位,当高(低)为为高电平的时候,下一时钟上升沿数据总线的高(低)字节为高阻态,接收不到数据,一般不会操作这个,因为这个容易使sdram不稳定.

assign dqm=2'b00;//不用数据掩码

 主状态机的设计

整个过程的状态机设计如下,初始化完成就进入刷新状态。

在写状态时,刷新请求来了,记住此次刷新请求,写操作完成时产生一个刷新请求操作。

在读状态时,刷新请求来了,记住此次的刷新请求,读完后产生一个刷新请求信号。

 

由状态机可以看出优先级是刷新>写>读

SDRAM控制器设计_第2张图片

 FF控制着读写刷新命令的执行,只有FF=0的时候才能执行指定的操作,执行完后将FF置1,退出任务。

注意这个状态机要严格根据优先级刷新>写>读来写,就是在条件语句中优先判断刷新请求,然后是写请求,再者是读请求,其他请求在这三者之后。

//主状态机
	reg [3:0] main_state;
	reg FF;//标志寄存器,为0的时候才可以进行刷新、写和读,刷新,写和读完成之后置1
	reg ref_req;//刷新操作请求
	reg wr_req;//写请求
	reg rd_req;//读请求
	reg wr_opt_done;//一次突发写操作完成标志
	reg rd_opt_done;//一次读突发完成标志
	localparam IDLE	=4'b0001;//空闲状态
	localparam AREF	=4'b0010;//刷新状态
	localparam WRITE	=4'b0100;//写状态
	localparam READ	=4'b1000;//读状态
always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		main_state<=IDLE;
		FF<=1;
	end
	else begin
		case(main_state)
			 IDLE	:	begin
							command<=init_cmd;
							sa<=init_addr;
							if(init_done)
								main_state<=AREF;
							else
								main_state<=IDLE;
						end
		    AREF	:	begin
							if(FF==0)
								auto_ref;//自动刷新任务
							else if(ref_req)begin
								main_state<=AREF;
								FF<=0;//开始执行任务
							end
							else if(wr_req)begin
								main_state<=WRITE;
								FF<=0;
							end
							else if(rd_req)begin
								main_state<=READ;
								FF<=0;
							end
							else
								main_state<=AREF;	
						end
		    WRITE:	begin
							if(FF==0)
								write_data;//写数据任务
							else begin
								if(ref_req)begin
									main_state<=AREF;	
									FF<=0;
								end
								else if(wr_opt_done & wr_req)begin
									main_state<=WRITE;	
									FF<=0;
								end
								else if(wr_opt_done & rd_req)begin
									main_state<=READ;	
									FF<=0;
								end
								else if(wr_opt_done & !wr_req & !rd_req)begin
									main_state<=AREF;	
								end
								else
									main_state<=WRITE;
							end	
						end
		    READ	:	begin
							if(FF==0)
								read_data;
							else begin
								if(ref_req)begin
									main_state<=AREF;
									FF<=0;
								end
								else if(rd_opt_done & wr_req)begin
									main_state<=WRITE;
									FF<=0;
								end
								else if(rd_opt_done & rd_req)begin
									main_state<=READ;
									FF<=0;
								end
								else if(rd_opt_done & !wr_req & !rd_req)begin
									main_state<=AREF;
								end
								else
									main_state<=READ;
							end
						end
		endcase	
	end

 设计刷新定时计数器,自动刷新周期为64ms/4096=15625, 取计数值为1560


//刷新定时计数器
reg [31:0] ref_time_cnt;//刷新定时计数器

always@(posedge clk or negedge rst_n)
	if(!rst_n)
		ref_time_cnt<=0;
	else if(ref_time_cnt==AUTO_REF)
		ref_time_cnt<=1;
	else if(init_done || ref_time_cnt>0)//在初始化完成开始刷新计数或者刷新计数器为1的时候开始刷新计数
		ref_time_cnt<=ref_time_cnt+1;
	else
		ref_time_cnt<=ref_time_cnt;
		
wire ref_time_flag;//刷新定时标志		
assign ref_time_flag=(ref_time_cnt==AUTO_REF);

定义刷新定时标志,刷新间隔计时完成时,需要进行刷新

wire ref_time_flag;//刷新定时标志		
assign ref_time_flag=(ref_time_cnt==AUTO_REF);

 行列bank地址寄存器,当有读写请求时对这些地址进行寄存。

//读写行列地址寄存器
reg [`ASIZE-1:0]raddr_r;//读写行地址寄存器
reg [`ASIZE-1:0]caddr_r;//读写列地址寄存器
reg [`BSIZE-1:0]baddr_r;//读写bank地址寄存器
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			begin
				raddr_r<=0;
				caddr_r<=0;
				baddr_r<=0;
			end
		else if(rd_req || wr_req)
			begin
				raddr_r<=raddr;
				caddr_r<=caddr;
				baddr_r<=baddr;
			end
		else
		;

自动刷新操作任务,由于在刷新过程中 涉及到所有bank,所有bank都要停止工作,所以需要先进行预充电关闭所有行。这个就感觉和初始化操作的预充电然后执行两个自动刷新命令,只是这里没有加载模式寄存器

//自动刷新操作任务
localparam ref_pre_time	=1;//预充电时刻
localparam ref_ref1_time=REF_PRE+1;//ref_pre为预充电时间,预充电完成,第一次自动刷新时刻
localparam ref_ref2_time=REF_PRE+REF_REF+1;//REF_REF为自动刷新时间,第2次自动刷新时刻
localparam ref_end		=REF_PRE+REF_REF*2;//自动刷新结束
		
//自动刷新时间计数
reg [15:0] ref_cnt;
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			ref_cnt<=0;
		else if(ref_cnt==ref_end)
			ref_cnt<=0;
		else if(ref_req || ref_cnt>0)
			ref_cnt<=ref_cnt+1;
		else
			ref_cnt<=ref_cnt;
			
//一次刷新操作完成标志
reg ref_opt_done;//一次刷新操作完成标志
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			ref_opt_done<=0;
		else if(ref_cnt==ref_end)
			ref_opt_done<=1;
		else
			ref_opt_done<=0;
			

//自动刷新操作
task auto_ref;
begin
	case(ref_cnt)
		ref_pre_time	:	begin
									command<=C_PRE;
									sa[10]=1;
								end
	   ref_ref1_time	:	command<=C_AREF;
	   ref_ref2_time	:	command<=C_AREF;
	   ref_end			:	begin
									FF<=1;
									command<=C_NOP;
								end
	endcase		
end
endtask

突发写操作

//一次突发写操作
localparam wr_act_time	=1;//行激活时刻
localparam wr_write_time=SC_RCD+1;//SC_RCD为激活到读写时刻的时间,写时刻
localparam wr_pre_time	=SC_RCD+SC_BL+WR_PRE+1;//SC_BL为突发长度,WR_PRE写数据到预充电间隔,写完数据之后的预充电时刻
localparam wr_end_time	=SC_RCD+SC_BL+WR_PRE+REF_PRE;//REF_PRE预充电时间完成,写操作结束时刻

//一次突发写过程计数器
reg [15:0] wr_cnt;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wr_cnt<=0;
	else if(wr_cnt==wr_end_time)
		wr_cnt<=0;
	else if(wr_req || wr_cnt>0)
		wr_cnt<=wr_cnt+1;
	else
		wr_cnt<=0;
//一次写操作完成标志
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wr_opt_done<=0;
	else if(wr_cnt==wr_end_time)
		wr_opt_done<=1;
	else
		wr_opt_done<=0;
//一次写操作完成标志
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wr_opt_done<=0;
	else if(wr_cnt==wr_end_time)
		wr_opt_done<=1;
	else
		wr_opt_done<=0;
//一次突发写操作过程状态标识信号
reg wr_opt;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wr_opt<=0;
	else if(wr_req)
		wr_opt<=1;
	else if(wr_opt_done)
		wr_opt<=0;
	else
		wr_opt<=wr_opt;

突发写数据操作

//写数据操作,数据写入时刻有效区间
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		wr_data_vaild<=0;
	else if((wr_cnt>SC_RCD)&&(wr_cnt<=SC_RCD+SC_BL)) //激活到读写命令延时时刻,写突发完成时                                                    
                                                     //刻
		wr_data_vaild<=1;        //突发写数据时为写数据有效区
	else 
		wr_data_vaild<=0;

		
//一次突发写操作数据写完标志
assign wdata_done=(wr_cnt==SC_RCD+SC_BL+1)?1:0; //突发写数据完成在下个时刻发出写完标志

//一次突发写操作任务
task write_data;
begin
	case(wr_cnt)
		wr_act_time		:	begin                       //行激活时刻
									command<=C_ACT;
									sa<=raddr_r;
									ba<=baddr_r;
								end	
		wr_write_time	:	begin                       //行激活延时,开始写命令时刻
									command<=C_WR;
									sa<={1'B0,caddr_r[8:0]};
									ba<=baddr_r;
								end	
		wr_pre_time		:	begin                         //预充电时刻
									command<=C_PRE;
									sa[10]<=1;
								end	
		wr_end_time		:	begin                         //写操作完成时刻
									command<=C_NOP;
									FF<=1;
								end
	endcase	
end

 突发读操作

//一次读突发操作任务
localparam rd_act_time =1;//激活行
localparam rd_read_time=SC_RCD+1;//读命令时刻
localparam rd_pre_time =SC_RCD+SC_BL+1;//预充电时刻
localparam rd_end_time =SC_RCD+SC_BL+SC_CL;//读操作结束时刻
	
//一次突发读过程计数
reg [15:0] rd_cnt;
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			rd_cnt<=0;
		else if(rd_cnt==rd_end_time)
			rd_cnt<=0;
		else if(rd_req || rd_cnt>0)
			rd_cnt<=rd_cnt+1;
		else
			rd_cnt<=0;
			
//一次突发读操作完成标志
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			rd_opt_done<=0;
		else if(rd_cnt==rd_end_time)
			rd_opt_done<=1;
		else
			rd_opt_done<=0;
			
//一次突发读操作过程状态标志信号
reg rd_opt;
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			rd_opt<=0;
		else if(rd_req)
			rd_opt<=1;
		else if(rd_opt_done)
			rd_opt<=0;
		else
			rd_opt<=rd_opt;
			
			
//一次突发读操作过程数据读完标志位
assign rdata_done=(rd_cnt==rd_end_time)?1:0;

//读数据操作,数据有效区
always@(posedge clk or negedge rst_n)
		if(!rst_n)
			rd_data_vaild<=0;
		else if((rd_cnt>SC_RCD+SC_CL)&&(rd_cnt<=SC_RCD+SC_CL+SC_BL))
			rd_data_vaild<=1;
		else 
			rd_data_vaild<=0;
			
//读数据
assign rd_data=dq;

//一次突发读操作
task read_data;
begin
	case(rd_cnt)
		rd_act_time :	begin
								command<=C_ACT;
								sa<=raddr_r;
								ba<=baddr_r;
							end
		rd_read_time:	begin
								command<=C_RD;
								sa<={1'b0,caddr_r[8:0]};
								ba<=baddr_r;
							end
		rd_pre_time :	begin
								command<=C_PRE;
								sa[10]<=1;
							end
		rd_end_time :	begin
								FF<=1;
								command<=C_NOP;
							end
		default:command<=C_NOP;
	endcase
end
endtask

 正在刷新时来读写信号或者正在读写时来刷新信号


//一次突发写过程状态标志信号
reg ref_opt;//刷新请求来正在刷新为1
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		ref_opt<=0;
	else if(ref_req)           //刷新来时该信号为1
		ref_opt<=1;
	else if(ref_opt_done)       //刷新完成时该信号为0
		ref_opt<=0;
	else
		ref_opt<=ref_opt;
wire ref_break_wr;//写过程刷新到记住刷新信号
wire ref_break_rd; //读过程刷新到记住刷新信号
wire wr_break_ref;//刷新过程外部写使能到记住写使能信号
wire rd_break_ref;//刷新过程外部读使能到记住读使能信号



//写操作过程刷新到记住刷新信号ref_break_wr
assign ref_break_wr=(ref_time_flag && wr_opt)?1:(  (!wr_opt)?0:ref_break_wr);
//读操作过程刷新到记住刷新信号ref_break_rd
	assign ref_break_rd = (ref_time_flag&&rd_opt)?1'b1:
	                      ((!rd_opt)?1'b0:ref_break_rd);
								 
//刷新过程外部写使能到记住写使能信号wr_break_ref
	assign wr_break_ref = ((wr && ref_opt)?1'b1:
	                      ((!ref_opt)?1'b0:wr_break_ref));	
	
//刷新过程外部读使能到记住读使能信号rd_break_ref信号
	assign rd_break_ref = ((rd && ref_opt)?1'b1: 
	                      ((!ref_opt)?1'b0:rd_break_ref));

刷新请求

//刷新请求信号
always@(*)
	begin
		case(main_state)
			AREF:	begin
						if(ref_time_flag)
							ref_req=1;
						else
							ref_req=0;
					end
			WRITE:begin
						if(ref_break_wr && wr_opt_done)
							ref_req=1;
						else
							ref_req=0;
					end
			READ:	begin
						if(ref_break_rd && rd_opt_done)
								ref_req=1;
						else
							ref_req=0;
					end
			default:ref_req=0;
		endcase
	end

写操作请求

//写操作请求信号
always@(*)
	begin
		case(main_state)
			AREF:	begin
						if( (!wr_break_ref)&& wr && !ref_time_flag)
							wr_req=1;
						else if(wr_break_ref && ref_opt_done)
							wr_req=1;
						else
							wr_req<=0;
					end
			WRITE:begin
						if(wr_opt_done && wr && !ref_break_wr)
							wr_req=1;
						else
							wr_req=0;
					end
			READ:	begin
						if(rd_opt_done && wr && !ref_break_rd)
								wr_req=1;
						else
							wr_req=0;
					end
			default:wr_req=0;
		endcase
	end
	

读请求信号

//读操作请求信号
always@(*)
	begin
		case(main_state)
			AREF:	begin
						if( (!rd_break_ref)&& (!wr_break_ref) && (!ref_time_flag) && !wr && rd)
							rd_req=1;
						else if(ref_opt_done && !wr_break_ref && rd_break_ref)
							rd_req=1;
						else
							rd_req<=0;
					end
			WRITE:begin
						if(wr_opt_done && !wr && rd && !ref_break_wr)
							rd_req=1;
						else
							rd_req=0;
					end
			READ:	begin
						if(rd_opt_done && !wr && !ref_break_rd && rd)
								rd_req=1;
						else
							rd_req=0;
					end
			default:rd_req=0;
		endcase
	end

 

你可能感兴趣的:(项目进阶)