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

SDRAM控制器设计

程序员文章站 2024-02-25 11:36:58
...

项目名称

SDRAM控制器设计

具体要求

给sdram存入100个数据并读出

设计说明

      模块设计

                               SDRAM控制器设计

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

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控制器设计

 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;//读状态
aaa@qq.com(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;//刷新定时计数器

aaa@qq.com(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地址寄存器
aaa@qq.com(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;
aaa@qq.com(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;//一次刷新操作完成标志
aaa@qq.com(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;
aaa@qq.com(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;
//一次写操作完成标志
aaa@qq.com(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;
//一次写操作完成标志
aaa@qq.com(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;
aaa@qq.com(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;

突发写数据操作

//写数据操作,数据写入时刻有效区间
aaa@qq.com(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;
aaa@qq.com(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;
			
//一次突发读操作完成标志
aaa@qq.com(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;
aaa@qq.com(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;

//读数据操作,数据有效区
aaa@qq.com(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
aaa@qq.com(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));

刷新请求

//刷新请求信号
aaa@qq.com(*)
	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

写操作请求

//写操作请求信号
aaa@qq.com(*)
	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
	

读请求信号

//读操作请求信号
aaa@qq.com(*)
	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

 

相关标签: 项目进阶