SDRAM读写控制器,这里分为三个部分,分别是SDRAM的基本操作实现,SDRAM控制器,封装成FIFO,以方便使用。

一、SDRAM的基本操作:初始化模块、自动刷新模块、写操作模块、读操作模块、SDRAM仲裁模块,顶层模块。

1、初始化模块

读写请求数监控 读写控制_fpga开发


上图是初始化模块的时序图。有图可知,SDRAM上电并且时钟稳定后,SDRAM首先的延迟等待100us,等待期间只能赋予禁止指令或者空操作指令。等待延时完成后,需要对SDRAM所有bank进行一次预充电操作(A10)置为高电平。然后进入预充电完成等待时间tRP,等待完成后至少执行两次自动刷新指令。之后对SDRAM进行加载模式寄存器。

读写请求数监控 读写控制_fpga开发_02


时序分析图

读写请求数监控 读写控制_读写请求数监控_03


寄存器模式配置

读写请求数监控 读写控制_读写请求数监控_04

/*Sdram 初始化
1、工作时钟定为100Mhz
2、SDRAM型号:W9825G6DH	4M x 4banks x 16bits
3、	行地址位宽:13
4、列地址位宽:9
5、数据位宽:16
6、Sdram 的自动刷新功能:每隔 64ms/2^12=15625 个时钟周期,给出刷新命令。
  */
module		sdram_init
(
	input	wire			sys_clk		,
	input	wire			sys_rst_n	,
	
	output	reg 	[3:0]	cmd_init	,//操作指令(包括片选信号,行选同步信号,列选同步信号,使能信号)
	output	reg 	[1:0]	ba_init		,//板块地址
	output	reg 	[12:0]	addr_init	,//地址总线
	output	wire			init_end	 //初始化完成标志		
);

//格雷码
parameter		INIT_IDLE = 3'b000, //初始状态
				INIT_PRE  =	3'b001, //预充电状态
				INIT_TRP  =	3'b011, //预充电等待时间状态(12-20ns),具体时间看数据手册
				INIT_AR   =	3'b010, //自动刷新状态
				INIT_TRF  =	3'b110, //自动刷新等待时间状态(66ns)
				INIT_MRS  =	3'b111, //寄存器配置状态
				INIT_TMRD =	3'b101, //寄存器配置到active状态等待时间,两个工作时钟周期
				INIT_END  =	3'b100; //初始化完成状态
				
//初始等待时间计数				
parameter		CNT_200 = 15'd20000;


//等待时间周期
parameter		TRP		= 2'd2,//15~20ns预充电等待时间
				TRFC	= 3'd7,//66ns自动刷新等待时间
				TMRD	= 2'd3;//3tclk寄存器配置等待时间
				
parameter		NOP		= 4'b0111,	//空操作指令
				AUTO_RFE= 4'b0001,	//自动刷新指令
				MREG_ST = 4'b0000,	//配置寄存器指令
				PCHARGE = 4'b0010;	//预充电指令

reg		[2:0]	init_state;
reg		[14:0]	cnt_200us;
wire			wait_end;	//初始等待时间结束(200us),高电平有效
reg		[2:0]	cnt_clk;
reg				cnt_clk_rst;
wire			trp_end;
wire			trfc_end;
wire			tmrd_end;
reg		[3:0]	cnt_aref;

//sdram初始化状态机
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		init_state <= INIT_IDLE;
	else	case(init_state)
		INIT_IDLE:	//系统上电后,在初始状态等待200us跳转到预充电状态
			if(wait_end)
				init_state <= INIT_PRE;
			else
				init_state <= init_state;
		INIT_PRE :	//预充电状态,直接跳转到预充电等待状态
			init_state <= INIT_TRP;
        INIT_TRP :	//预充电等待状态,等待结束,跳转到自动刷新状态
			if(trp_end)
				init_state <= INIT_AR;
			else
				init_state <= init_state;
        INIT_AR  :	//自动刷新状态,直接跳转到自动刷新等待状态
			init_state <= INIT_TRF;
        INIT_TRF :	//自动刷新8次,自动跳转到模式寄存器设置状态
			if(trfc_end & cnt_aref == 4'd8)
				init_state <= INIT_MRS;
			else	if(trfc_end)
				init_state <= INIT_AR;
			else
				init_state <= init_state;
        INIT_MRS :	//模式寄存器设置状态,直接跳转到模式寄存器设置等待状态
			init_state <= INIT_TMRD;	
        INIT_TMRD:	//模式寄存器设置等待状态,等待结束,跳到初始化完成状态
			if(tmrd_end)
				init_state <= INIT_END;
			else
				init_state <= init_state;
        INIT_END :	//初始化完成状态,保持此状态
			init_state <= INIT_END;
		default:init_state <= INIT_IDLE;
	endcase
	
//计时200us
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_200us <= 15'd0;
	else	if(cnt_200us == CNT_200)
		cnt_200us <= CNT_200;
	else
		cnt_200us <= cnt_200us + 1'b1;
		
//wait_end:上电后200us等待结束标志
assign	wait_end = (cnt_200us == CNT_200 - 1'b1)?1'b1:1'b0;

//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_clk <= 3'd0;
	else	if(cnt_clk_rst)
		cnt_clk <= 3'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;

//cnt_clk_rst:时钟周期计数复位标志		
always@(*)
	begin
		case(init_state)
			INIT_IDLE	: cnt_clk_rst <= 1'b1;
			INIT_TRP	: cnt_clk_rst <= (trp_end)?1'b1:1'b0;
			INIT_TRF	: cnt_clk_rst <= (trfc_end)?1'b1:1'b0;
			INIT_TMRD	: cnt_clk_rst <= (tmrd_end)?1'b1:1'b0;
			INIT_END	: cnt_clk_rst <= 1'b1;
			default		: cnt_clk_rst <= 1'b0;
		endcase
	end

//trp_end,trc_end,tmrd_end:等待结束标志	
assign	trp_end = (cnt_clk == TRP & init_state == INIT_TRP)?1'b1:1'b0;
assign	trfc_end = (cnt_clk == TRFC & init_state == INIT_TRF)?1'b1:1'b0;
assign	tmrd_end = (cnt_clk == TMRD & init_state == INIT_TMRD)?1'b1:1'b0;


//对自动刷新计数
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_aref <= 4'd0;
	else	if(init_state == INIT_AR)
		cnt_aref <= cnt_aref + 1'b1;
	else	if(init_state == INIT_IDLE)
		cnt_aref <= 4'd0;

//输出配置
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
			cmd_init <= NOP;
			ba_init	 <= 2'b11;
			addr_init<= 13'h1fff;
		end
	else	case(init_state)
		INIT_IDLE,INIT_TRF,INIT_TRP,INIT_TMRD,INIT_END:
			begin
				cmd_init <= NOP;
				ba_init	 <= 2'b11;
				addr_init<= 13'h1fff;				
			end
		INIT_PRE:
			begin
				cmd_init <= PCHARGE;
				ba_init	 <= 2'b11;
				addr_init<= 13'h1fff;//预充电时,A10为高电平对所有bank进行预充电				
			end
		INIT_AR:
			begin
				cmd_init <= AUTO_RFE;
				ba_init	 <= 2'b11;
				addr_init<= 13'h1fff;				
			end
		INIT_MRS:
			begin
				cmd_init <= MREG_ST;
				ba_init	 <= 2'b00; //配置寄存器模式时bank地址给00
				addr_init<= 
				{			
							 //地址辅助配置模式寄存器,参数不同,配置的模式不同
					3'b000,	 //A12-A10:预留
					1'b0,    //A9=0:读写方式,0:突发读&突发写,1:突发读&单写
					2'b00,   //{A8,A7}=00:标准模式,默认
					3'b011,  //{A6,A5,A4}=011:CAS潜伏期,010:2,011:3,其他:保留
					1'b0,    //A3=0:突发传输方式,0:顺序,1:隔行
					3'b111	 //{A2,A1,A0}=111:突发长度,000:单字节,001:2字节
							 //010:4字节,011:8字节,111:整页,其他:保留			
				};				
			end
		default:
			begin
				cmd_init <= NOP;
				ba_init	 <= 2'b11;
				addr_init<= 13'h1fff;
			end
		endcase
			
assign	init_end = (init_state == INIT_END)?1'b1:1'b0;			
			
endmodule

读写请求数监控 读写控制_读写请求数监控_05

读写请求数监控 读写控制_读写请求数监控_06


读写请求数监控 读写控制_预充电_07


观察仿真模型结果,初始化操作的状态跳转和写入的操作指令正确。初始化对所有bank进行预充电,经过8次自动刷新后开始配置寄存器模式,对地址线A0~A12配置,参照数据手册可知,列选通潜伏期为3,突发传输方式为顺序突发,突发长度为整页突发。

2、自动刷新模块

自动刷新时序

读写请求数监控 读写控制_预充电_08


状态转移图

读写请求数监控 读写控制_读写请求数监控_09

/*Sdram自动刷新
1、工作时钟定为100Mhz
2、SDRAM型号:W9825G6DH	4M x 4banks x 16bits
3、	行地址位宽:13
4、列地址位宽:9
5、数据位宽:16
6、Sdram 的自动刷新功能:每隔 64ms/2^13=7812.5 ns,给出刷新命令。
  */

module	sdram_aref
(
	input	wire			sys_clk		,//系统时钟,频率100MHz
	input	wire			sys_rst_n	,//复位信号,低电平有效
	input	wire			init_end	,//初始化结束信号
	input	wire			aref_en		,//自动刷新使能,由仲裁模块返回
	
	output	reg		[3:0]	aref_cmd	,//自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
	output	reg	[1:0]	aref_ba		,//自动刷新阶段Bank地址
	output	reg	[12:0]	aref_addr	,//地址数据,辅助预充电操作,A12-A0,13位地址
	output	wire			aref_end	,//自动刷新结束标志
	output	reg				aref_req	 //向仲裁模块发送自动刷新请求信号
);

parameter	CNT_MAX = 10'd750;

parameter	AREF_IDLE = 3'b000,  //初始状态
			AREF_PCH  = 3'b001,  //预充电
			AREF_TRP  = 3'b011,  //预充电等待
			AUTO_REF  = 3'b010,  //自动刷新指令写入
			AREF_TRF  = 3'b110,  //自动刷新等待时间
			AREF_END  = 3'b111;  //结束状态
			
parameter	TRP		= 2'd2,//15~20ns
			TRFC	= 3'd7;//66ns

			
parameter	NOP		= 4'b0111,	//空操作指令
			AUTOREF= 4'b0001,	//自动刷新指令
			PCHARGE = 4'b0010;	//预充电指令

reg	[9:0]		cnt_ref;	//刷新周期,去7500ns,计时750次,给仲裁预留时间
wire			aref_ack;	//自动刷新响应信号
reg	[3:0]		cnt_clk;	
reg	[2:0]		aref_state;
reg				cnt_clk_rst; //等待时间计数复位
wire			trp_end;
wire			trf_end;
reg	[1:0]		cnt_aref;   //自动刷新次数,至少两次


//cnt_ref:刷新计数器
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_ref <= 10'd0;
	else	if(cnt_ref >= CNT_MAX -1'b1)
		cnt_ref <= 10'd0;
	else	if(init_end)
		cnt_ref <= cnt_ref + 1'b1;
		
assign	aref_ack = (aref_state == AREF_PCH)?1'b1:1'b0;

//aref_req:自动刷新请求
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		aref_req <= 1'b0;
	else	if(cnt_ref == CNT_MAX - 1'b1)
		aref_req <= 1'b1;
	else	if(aref_ack == 1'b1)
		aref_req <= 1'b0;
		

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		aref_state <= AREF_IDLE;
	else	case(aref_state)
		AREF_IDLE	:	//初始化结束且自动刷新使能有效,跳转到预充电状态
			if(init_end && aref_en)
				aref_state <= AREF_PCH;
			else
				aref_state <=aref_state;
		AREF_PCH	:	//预充电状态,直接跳转到预充电等待状态
			aref_state <= AREF_TRP;
		AREF_TRP	:
			if(trp_end)	//预充电等待结束,跳转到自动刷新状态
				aref_state <= AUTO_REF;
			else
				aref_state <= aref_state;
		AUTO_REF	:	//直接跳转到自动刷新等待状态
			aref_state <= AREF_TRF;
		AREF_TRF	:	//自动刷新等待完成且自动刷新两次,跳转到结束状态。不满足两次则跳转到自动刷新状态
			if(trf_end && (cnt_aref == 2'd2))
				aref_state <= AREF_END;
			else	if(trf_end)
				aref_state <= AUTO_REF;
				
		AREF_END	:	//直接跳转到初始状态
			aref_state <= AREF_IDLE;
		default:aref_state <= AREF_IDLE;
	endcase

//cnt_clk:时钟周期计数,记录初始化各状态等待时间		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_clk <= 3'd0;
	else	if(cnt_clk_rst)
		cnt_clk <= 3'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;

always@(*)
	begin
		case(aref_state)
			AREF_IDLE	: cnt_clk_rst <= 1'b1;
			AREF_TRP 	: cnt_clk_rst <= (trp_end)?1'b1:1'b0;
			AREF_TRF 	: cnt_clk_rst <= (trf_end)?1'b1:1'b0;
			AREF_END 	: cnt_clk_rst <= 1'b1;
			default		: cnt_clk_rst <= 1'b0;
		endcase
	end
//预充电等待时间结束信号
assign	trp_end = (cnt_clk == TRP & aref_state == AREF_TRP)?1'b1:1'b0;
//自动刷新等待时间结束信号
assign	trf_end = (cnt_clk == TRFC & aref_state == AREF_TRF)?1'b1:1'b0;

//自动刷新计数
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_aref <= 2'd0;
	else	if(aref_state == AUTO_REF)
		cnt_aref <= cnt_aref + 1'b1;
	else	if(aref_state == AREF_IDLE)
		cnt_aref <= 2'd0;


/* always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		aref_cmd <= NOP;
	else	case(aref_state)
		AREF_IDLE,AREF_TRP,AREF_TRF,AREF_END:
			aref_cmd <= NOP;
		AREF_PCH:
			aref_cmd <= PCHARGE;
		AUTO_REF:
			aref_cmd <= AUTOREF;
		default:	aref_cmd <= NOP;
	endcase */

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            aref_cmd    <=  NOP;
            aref_ba     <=  2'b11;
            aref_addr   <=  13'h1fff;
        end
    else
        case(aref_state)
            AREF_IDLE,AREF_TRP,AREF_TRF:    //执行空操作指令
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end
            AREF_PCH:  //预充电指令
                begin
                    aref_cmd    <=  PCHARGE;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end 
            AUTO_REF:   //自动刷新指令
                begin
                    aref_cmd    <=  AUTOREF;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end
            AREF_END:   //一次自动刷新完成
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end    
            default:
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end    
        endcase


	
/* assign	aref_ba = 2'b11;

assign	aref_addr = 13'h1fff; */

assign	aref_end = (aref_state == AREF_END)?1'b1:1'b0;

endmodule

SDRAM规定的刷新时间为64ms,也就是在该时间内要对所有的行进行刷新,这里SDRAM对应的行地址为13位宽,对应8192行,64ms/8192=7812.5ns,那么必须大约7.8us的时间就要发出一次自动刷新命令,这是为了保证SDRAM内的数据能够上电后一直保存。以工作时钟频率为100MHz,一个时钟就是10ns,则需要780个时钟周期刷新一次,但是在设计的过程中,我们只是让他计数到750,因为当计数到该值时发出自动刷新请求会经仲裁模块,这里留一下余量。

而且在此过程中有一问题,就是自动刷新的命令会不会影响到读写操作,假设我就以7812.5ns为周期进行自动刷新,当读写操作还在进行着时,刷新请求来到了,这时难道让我们打断读写操作?这肯定是不行的,要等待读写操作完成后再发出自动刷新请求。再假设每次刷新请求操作都超过7812.5ns,那么总时长就超过64ms了,数据可能会丢失。所里这里需要留有余量,设定在7500ns刷新一次,这时余量就为三百多ns,假设你的读写突发长度为10,读写完总共花的时间为100ns,远小于余量,这时就不会出现上诉情况,但如果读写突发长度为整页突发或者大于余量/时钟周期的突发长度(321/10=32),这时也有可能会出现数据丢失的情况。

读写请求数监控 读写控制_自动刷新_10


自动刷新模块的仿真情况,当刷新计数器计数到最大值时自动刷新请求拉高3、SDRAM写操作模块

写操作时序图,页突发

读写请求数监控 读写控制_fpga开发_11


当初始化完成且写使能信号拉高时,开始写激活,激活指定bank的某一行,经过写激活等待tRCD,开始写入数据,这时需要给列首地址A0~A8和已激活的bank地址,页突发可以写入页突发终止指令。

写模块状态转移图

读写请求数监控 读写控制_读写请求数监控_12

module	sdram_write
(
	input	wire			sys_clk			,
	input	wire			sys_rst_n		,
	input	wire			init_end		,//初始化完成信号,由初始化模块传入
	input	wire			wr_en			,//写使能,由仲裁模块传入
	input	wire	[23:0]	wr_addr			,//包括bank地址,行地址,列地址,fifo传入
	input	wire	[15:0]	wr_data			,//待写入sdram的数据,由fifo传入
	input	wire	[9:0]	wr_burst_len	,//整页突发,这里10突发终止,如果没有突发终止会写满一行
	
    //输出到仲裁模块
	output	wire			wr_ack			,
	output	wire			wr_end			,//一次突发写结束
	output	reg		[3:0]	write_cmd		,
	output	reg		[1:0]	write_ba		,
	output	reg		[12:0]	write_addr		,
	output	reg				wr_sdram_en		,//写sdram数据有效使能,与wr_ack
	output	wire	[15:0]	wr_sdram_data	
	
);


parameter		WR_IDEL 	= 3'b000	,//初始状态
				WR_ACTIVE	= 3'b001	,//激活状态
				WR_TRCD		= 3'b011	,//激活等待状态
				WR_WRITE	= 3'b010	,//写操作
				WR_DATA		= 3'b110	,//写数据
				WR_PRE		= 3'b111	,//预充电
				WR_TRP		= 3'b101	,//预充电等待
				WR_END		= 3'b100	;//一次突发写结束
				
parameter   	TRCD_CLK    =   10'd2   ,   //激活周期
				TRP_CLK     =   10'd2   ;   //预充电周期
			
			
parameter		NOP			= 4'b0111,//空操作
				ACTIVE		= 4'b0011,//激活
				WRITE		= 4'b0100,//写
				STOP		= 4'b0110,//突发停止
				P_CHARGE	= 4'b0010;//预充电

				
reg	[2:0]	write_state	;
reg	[3:0]	cnt_clk		;
reg			cnt_clk_rst	;
wire		trcd_end	;//激活等待周期结束
wire		twrite_end	;//突发写结束
wire		trp_end		;//预充电结束



always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		write_state <= WR_IDEL;
	else	case(write_state)
		WR_IDEL 	:	//初始化完成且仲裁模块返回写使能,转激活状态
			if(init_end && wr_en)
				write_state <= WR_ACTIVE;
			else
				write_state <= write_state;
		
		WR_ACTIVE	:	//激活状态直接跳转到激活等待
			write_state <= WR_TRCD;
		
		WR_TRCD		:	//激活等待结束,跳转到写命令状态
			if(trcd_end)
				write_state <= WR_WRITE;
			else
				write_state <= write_state;
			
		WR_WRITE	:	//写指令状态直接跳转到写数据状态
			write_state <= WR_DATA;
		
		WR_DATA		:	//突发写结束,跳转到预充电状态
			if(twrite_end)
				write_state <= WR_PRE;
			else
				write_state <= write_state;
				
		WR_PRE		:	//预充电状态直接跳转到预充电等待状态
			write_state <= WR_TRP;
		
		WR_TRP		:	//预充电等待结束,跳转到结束状态
			if(trp_end)
				write_state <= WR_END;
			else
				write_state <= write_state;
		
		WR_END		:	//完成一次突发写,跳回到初始状态
			write_state <= WR_IDEL;
		
		default:	write_state <= WR_IDEL;
	endcase

//周期计数,对各个状态的等待时间周期计数	
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_clk <= 4'd0;
	else	if(cnt_clk_rst)
		cnt_clk <= 4'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;
		
always@(*)
	begin
		case(write_state)
			WR_IDEL	:	cnt_clk_rst <= 1'b1;
			WR_TRCD	:	cnt_clk_rst <= (trcd_end)? 1'b1:1'b0;
			WR_WRITE:	cnt_clk_rst <= 1'b1;
			WR_DATA	:	cnt_clk_rst <= (twrite_end)? 1'b1:1'b0;
			WR_TRP	:	cnt_clk_rst <= (trp_end)? 1'b1:1'b0;
			WR_END	:	cnt_clk_rst <= 1'b1;
			default	:	cnt_clk_rst <= 1'b0;
		endcase
			
	end

assign	trcd_end 	= (write_state == WR_TRCD && cnt_clk == TRCD_CLK)? 1'b1:1'b0;
assign	twrite_end 	= (write_state == WR_DATA && cnt_clk == wr_burst_len - 1'b1)? 1'b1:1'b0;
assign	trp_end		= (write_state == WR_TRP && cnt_clk == TRP_CLK)? 1'b1:1'b0;	

//一次突发写结束
assign	wr_end	= (write_state == WR_END)? 1'b1:1'b0;	

//sdram操作指令
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
			write_cmd <= NOP;
			write_ba  <= 2'b11;
			write_addr<= 13'h1fff;
		end
	else	case(write_state)
		WR_IDEL,WR_TRCD,WR_TRP,WR_END	:
			begin
				write_cmd <= NOP;
				write_ba <= 2'b11;
				write_addr<= 13'h1fff;
			end
		WR_ACTIVE	:
			begin
				write_cmd <= ACTIVE;
				write_ba   <= wr_addr[23:22];
				write_addr <= {4'b0000,wr_addr[8:0]};
			end		
		WR_WRITE	:
			begin
				write_cmd <= WRITE;
				write_ba  <= 2'b00;
				write_addr<= 13'h0000;
			end
		WR_DATA		:
			if(twrite_end)
				write_cmd <= STOP;
			else	begin
				write_cmd <= NOP;
				write_ba  <= 2'b11;
				write_addr<= 13'h1fff;
			end
		WR_PRE		:
			begin
				write_cmd <= P_CHARGE;
                write_ba  <= wr_addr[23:22];
				write_addr<= 13'h0400;
			end
		default		:	
			begin
				write_cmd <= NOP;
				write_ba  <= 2'b11;
				write_addr<= 13'h1fff;
			end
	
	endcase

//wr_ack:输出传到fifo控制模块
assign  wr_ack = ( write_state == WR_WRITE)
                || ((write_state == WR_DATA) 
                && (cnt_clk <= (wr_burst_len - 2'd2)));
			
		
//wr_sdram_en:写SDRAM数据有效信号		
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		wr_sdram_en <= 1'b0;
	else
		wr_sdram_en <= wr_ack;


assign	wr_sdram_data = (wr_sdram_en)? wr_data:16'd0;	
endmodule

读写请求数监控 读写控制_预充电_13


读写请求数监控 读写控制_预充电_14


仿真结果写入十个数据后执行了突发终止指令

4、SDRAM读操作模块

页突发读模式时序图

读写请求数监控 读写控制_读写请求数监控_15


发送读激活命令给SDRAM,选中要激活的bank,读指令后等待tRCD后通过地址总线传入行地址,等待列选通潜伏期,由配置寄存器模式的选定,然后开始读取数据,读完所需数据之后写入读突发终止指令,与写模块不同,当读突发终止指令执行后,还会读取潜伏期个数的数据。入潜伏期为3,则突发终止后还会读出三个数据。

读模块状态转移图

读写请求数监控 读写控制_等待时间_16


读写请求数监控 读写控制_fpga开发_17


读突发长度为10,读数据状态要保持读突发长度加上列选通潜伏期的个数,10+3。读突发终止指令则要在读数据状态第八个时钟周期开始写入。

module	sdram_read
(
	input	wire			sys_clk			,
	input	wire			sys_rst_n		,
	input	wire			init_end		,//初始化完成信号,由初始化模块传入
	input	wire			rd_en			,//写使能,由仲裁模块传入
	input	wire	[23:0]	rd_addr			,//包括bank地址,行地址,列地址
	input	wire	[15:0]	rd_data			,//读出sdram的数据,由fifo传入
	input	wire	[9:0]	rd_burst_len	,//整页突发,这里10突发终止,如果没有突发终止会读满一行
	
	output	wire			rd_ack			,//读sdram有效信号
	output	wire			rd_end			,//一次突发读结束
	output	reg		[3:0]	read_cmd		,
	output	reg		[1:0]	read_ba			,
	output	reg		[12:0]	read_addr		,
	output	wire	[15:0]	rd_sdram_data	 //sdram读出数据
	
);


parameter		RD_IDLE	 =	4'b0000, 
				RD_ACTIVE=	4'b0001, //读激活
				RD_TRCD	 =	4'b0011, //读激活等待
				RD_READ	 =	4'b0010, //读指令写入
				RD_CL	 =	4'b0110, //列选通潜伏期
				RD_DATA	 =	4'b0111, //读数据
				RD_PRE	 = 	4'b0101, //预充电
				RD_TRP	 = 	4'b0100, //预充电等待
				RD_END	 =	4'b1100; //结束一次突发读
				
parameter   	TRCD_CLK    =   10'd2   ,   //激活周期
				CL_CLK		=  	10'd3	,	//列选通潜伏期
				TRP_CLK     =   10'd2   ;   //预充电周期
			
			
parameter		NOP			= 4'b0111,//空操作
				ACTIVE		= 4'b0011,//激活
				READ		= 4'b0101,//读指令
				STOP		= 4'b0110,//突发停止
				P_CHARGE	= 4'b0010;//预充电


reg	[15:0]	rd_data_reg	; 				
reg	[3:0]	read_state	;
reg	[3:0]	cnt_clk		;
reg			cnt_clk_rst	;
wire		trcd_end	;//激活等待周期结束
wire		trp_end		;//预充电结束
wire		tread_end	;
wire		tcl_end		;//列选通等待结束
wire		stop_end	;//读突发终止


//对数据打1拍,rd_data同步在sys_clk下,只是相位不同步,所以能直接打拍
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)
		read_state <= RD_IDLE;
	else	case(read_state)
		RD_IDLE 	:	//初始化完成且仲裁模块返回写使能,转激活状态
			if(init_end && rd_en)
				read_state <= RD_ACTIVE;
			else
				read_state <= read_state;
		
		RD_ACTIVE	:	//激活状态直接跳转到激活等待
			read_state <= RD_TRCD;
		
		RD_TRCD		:	//激活等待结束,跳转到读命令状态
			if(trcd_end)
				read_state <= RD_READ;
			else
				read_state <= read_state;
			
		RD_READ	:	//读指令状态直接跳转到列选通潜伏期状态
			read_state <= RD_CL;
			
		RD_CL	:	//列选通潜伏期结束,跳转到读数据状态
			if(tcl_end)
				read_state <= RD_DATA;
			else
				read_state <= read_state;
		
		RD_DATA		:	//突发读结束,跳转到预充电状态
			if(tread_end)
				read_state <= RD_PRE;
			else
				read_state <= read_state;
				
		RD_PRE		:	//预充电状态直接跳转到预充电等待状态
			read_state <= RD_TRP;
		
		RD_TRP		:	//预充电等待结束,跳转到结束状态
			if(trp_end)
				read_state <= RD_END;
			else
				read_state <= read_state;
		
		RD_END		:	//完成一次突发读,跳回到初始状态
			read_state <= RD_IDLE;
		
		default:	read_state <= RD_IDLE;
	endcase

//周期计数,对各个状态的等待时间周期计数	
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_clk <= 4'd0;
	else	if(cnt_clk_rst)
		cnt_clk <= 4'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;
		
always@(*)
	begin
		case(read_state)
			RD_IDLE	:	cnt_clk_rst <= 1'b1;
			RD_TRCD	:	cnt_clk_rst <= (trcd_end)? 1'b1:1'b0;
			RD_READ:	cnt_clk_rst <= 1'b1;
			RD_CL	:	cnt_clk_rst <= (tcl_end)? 1'b1:1'b0;
			RD_DATA	:	cnt_clk_rst <= (tread_end)? 1'b1:1'b0;
			RD_TRP	:	cnt_clk_rst <= (trp_end)? 1'b1:1'b0;
			RD_END	:	cnt_clk_rst <= 1'b1;
			default	:	cnt_clk_rst <= 1'b0;
		endcase
			
	end

assign	trcd_end 	= (read_state == RD_TRCD && cnt_clk == TRCD_CLK)? 1'b1:1'b0;
assign	tread_end 	= (read_state == RD_DATA && cnt_clk == rd_burst_len + 10'd2)? 1'b1:1'b0;
assign	trp_end		= (read_state == RD_TRP && cnt_clk == TRP_CLK)? 1'b1:1'b0;
assign	tcl_end		= (read_state == RD_CL && cnt_clk == CL_CLK - 1'b1)? 1'b1:1'b0;	
assign	stop_end 	= (read_state == RD_DATA && cnt_clk == rd_burst_len - 10'd1 - CL_CLK)? 1'b1:1'b0;

//一次突发写结束
assign	rd_end	= (read_state == RD_END)? 1'b1:1'b0;	

//sdram操作指令
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
			read_cmd <= NOP;
			read_ba  <= 2'b11;
			read_addr<= 13'h1fff;
		end
	else	case(read_state)
		RD_IDLE,RD_TRCD,RD_TRP,RD_END,RD_CL	:
			begin
				read_cmd <= NOP;
				read_ba <= 2'b11;
				read_addr<= 13'h1fff;
			end
		RD_ACTIVE	:
			begin
				read_cmd <= ACTIVE;
				read_ba   <= rd_addr[23:22];
				read_addr <= {4'b0000,rd_addr[8:0]};
			end		
		RD_READ	:
			begin
				read_cmd <= READ;
				read_ba  <= 2'b00;
				read_addr<= 13'h0000;
			end
		RD_DATA		:
			if(stop_end)
				read_cmd <= STOP;
			else	begin
				read_cmd <= NOP;
				read_ba  <= 2'b11;
				read_addr<= 13'h1fff;
			end
		RD_PRE		:
			begin
				read_cmd <= P_CHARGE;
                read_ba  <=rd_addr[23:22];
				read_addr<= 13'h0400;
			end
		default		:	
			begin
				read_cmd <= NOP;
				read_ba  <= 2'b11;
				read_addr<= 13'h1fff;
			end
	
	endcase

//rd_ack:读SDRAM有效信号
//输出会少一位数据,
assign  rd_ack = ( read_state == RD_DATA)
                && ((cnt_clk >= 10'd1) 
                && (cnt_clk < (rd_burst_len + 1'd1)));

//这个不会读漏		
/* assign  rd_ack = ( read_state == RD_DATA)
                && ((cnt_clk >= 10'd0) 
                && (cnt_clk < (rd_burst_len))); */
		
		



assign	rd_sdram_data = (rd_ack)? rd_data_reg:16'd0;	
endmodule

读写请求数监控 读写控制_自动刷新_18


读写请求数监控 读写控制_读写请求数监控_19


5、SDRAM仲裁模块

读写请求数监控 读写控制_fpga开发_20


前面四个模块都与用户接口和SDRAM建立起了双向数据通信,如果多个指令一起发送请求,如果没有优先级,就有可能造成冲突,从而导致SDRAM工作出错,因为就需要引入仲裁模块,对前面四个模块进行优先级的划分。

读写请求数监控 读写控制_等待时间_21


当初始化完成时,跳转到仲裁状态,当读写与刷新,其中有一个为有效,其他两个无效,则执行操作,当执行操作之后,回到仲裁状态,若两路或多路请求信号同时有效,优先执行优先级较高的操作,默认优先级为自动刷新操作>数据写操作>数据读操作。

module	sdram_arbit
(
	input	wire			sys_clk		,
	input	wire			sys_rst_n	,
	input	wire			init_end	,
	input	wire	[3:0]	init_cmd	,
	input	wire	[1:0]	init_ba	,
	input	wire	[12:0]	init_addr	,
	input	wire			aref_req	,
	input	wire			aref_end	,
	input	wire	[3:0]	aref_cmd	,
	input	wire	[1:0]	aref_ba		,
	input	wire	[12:0]	aref_addr	,	
	input	wire			wr_req		,
	input	wire			wr_end		,
	input	wire	[3:0]	wr_cmd		,
	input	wire	[1:0]	wr_ba		,
	input	wire	[12:0]	wr_addr		,
	input	wire			wr_sdram_en	,
	input	wire	[15:0]	wr_data		,	
	input	wire			rd_req		,
	input	wire			rd_end		,
	input	wire	[3:0]	rd_cmd		,
	input	wire	[1:0]	rd_ba		,
	input	wire	[12:0]	rd_addr		,
	
	
	output	reg				aref_en		,
	output	reg				wr_en		,
	output	reg				rd_en		,
	output	wire			sdram_cke	,
	output	wire			sdram_cs_n	,
	output	wire			sdram_ras_n	,
	output	wire			sdram_cas_n	,
	output	wire			sdram_we_n	,
	output	reg		[1:0]	sdram_ba	,
	output	reg		[12:0]	sdram_addr	,
	inout	wire	[15:0]	sdram_dq	
	
);



reg	[4:0]	state;
reg	[3:0]	sdram_cmd;



parameter		IDLE	= 5'b00001,
				ARBIT	= 5'b00010,
				WRITE	= 5'b00100,
				READ	= 5'b01000,
				A_REF	= 5'b10000;
				

parameter		NOP		= 4'b0111;	//空操作指令
				

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		state <= IDLE;
	else	case(state)
		IDLE	:
			if(init_end)
				state <= ARBIT;
			else
				state <= IDLE;
        ARBIT	:
			if(aref_req)
				state <= A_REF;
			else	if(wr_req)
				state <= WRITE;
			else	if(rd_req)
				state <= READ;
			else
				state <= ARBIT;
        WRITE	:
			if(wr_end)
				state <= ARBIT;
			else
				state <= WRITE;
        READ	:
			if(rd_end)
				state <= ARBIT;
			else
				state <= READ;
        A_REF	:
			if(aref_end)
				state <= ARBIT;
			else
				state <= A_REF;
		default:state <= IDLE;
	endcase
	
	
always@(*)
	case(state)
		IDLE	:
			begin
				sdram_cmd 	<= 	init_cmd;
				sdram_ba	<= 	init_ba;
				sdram_addr	<=	init_addr;
			end		
//       ARBIT	:
//			begin
//				sdram_cmd	<=	NOP;
//				sdram_ba	<=	2'b11;
//				sdram_addr	<=	13'h1fff;
//			end			
        WRITE	:
			begin
				sdram_cmd	<=	wr_cmd;
				sdram_ba	<=	wr_ba;
				sdram_addr	<=	wr_addr;				
			end			
        READ	:
			begin
				sdram_cmd	<=	rd_cmd;
				sdram_ba	<=	rd_ba;
				sdram_addr	<=	rd_addr;				
			end			
        A_REF	:
			begin
				sdram_cmd	<=	aref_cmd;
				sdram_ba	<=	aref_ba;
				sdram_addr	<=	aref_addr;				
			end
		default:
			begin
				sdram_cmd	<=	NOP;
				sdram_ba	<=	2'b11;
				sdram_addr	<=	13'h1fff;
			end
	endcase
			
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		aref_en	<= 1'b0;
	else	if((aref_req == 1'b1)&& (state == ARBIT))
		aref_en <= 1'b1;
	else	if(aref_end == 1'b1)
		aref_en <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		wr_en <= 1'b0;
	else	if((aref_req == 1'b0) && (wr_req == 1'b1) && (state == ARBIT))
		wr_en <= 1'b1;
	else	if(wr_end == 1'b1)
		wr_en <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rd_en <= 1'b0;
	else	if((aref_req == 1'b0) && (rd_req == 1'b1) && (state == ARBIT))
		rd_en <= 1'b1;
	else	if(rd_end == 1'b1)
		rd_en <= 1'b0;
		

assign	{sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd;

assign	sdram_cke = 1'b1;

assign	sdram_dq = (wr_sdram_en)? wr_data: 16'bz;

endmodule

把五个合成一个顶层模块

module	sdram_ctrl
(
	//时钟、复位,初始化端口
	input	wire			sys_clk		,
	input	wire			sys_rst_n	,
	output	wire			init_end	,
	//sdram write
	input	wire	[23:0]	wr_sdram_addr,
	input	wire	[15:0]	wr_sd_data,
	input	wire	[9:0]	wr_burst_len,
	input	wire			wr_sdram_req,//写SDRAM请求
	output	wire			wr_sdram_ack,//写SDRAM有效信号
	//sdram read
	input	wire	[23:0]	rd_sdram_addr,
	input	wire	[9:0]	rd_burst_len,
	input	wire			rd_sdram_req,
	output	wire			rd_sdram_ack,
	output	wire	[15:0]	rd_sdram_out,
	//sdram引脚接口
	output	wire			sdram_cke	,
	output	wire			sdram_cs_n	,
	output	wire			sdram_cas_n	,
	output	wire			sdram_ras_n	,
	output	wire			sdram_we_n	,
	output	wire	[1:0]	sdram_ba	,
	output	wire	[12:0]	sdram_addr	,
	inout	wire	[15:0]	sdram_dq	
);


wire [3:0]  init_cmd ;
wire [1:0]  init_ba  ;
wire [12:0] init_addr;

wire        aref_req ;
wire        aref_end ;
wire [3:0]  aref_cmd ;
wire [1:0]  aref_ba  ;
wire [12:0] aref_addr;
wire        wr_end ; 
wire [3:0]  wr_cmd ; 
wire [1:0]  wr_ba  ; 
wire [12:0] wr_addr; 
wire        wr_sdram_en;
wire [15:0] wr_sdram_data; 
wire        rd_end ; 
wire [3:0]  rd_cmd ; 
wire [1:0]  rd_ba  ; 
wire [12:0] rd_addr;
wire        aref_en;
wire        wr_en;
wire        rd_en;      


sdram_init  sdram_init_inst
(
	.sys_clk     (sys_clk),
	.sys_rst_n   (sys_rst_n),

	.cmd_init    (init_cmd),//操作指令(包括片选信号,行选同步信号,列选同步信号,使能信号)
	.ba_init     (init_ba),//板块地址
	.addr_init   (init_addr),//地址总线
	.init_end    (init_end) //初始化完成标志		
);


sdram_aref  sdram_aref_inst
(
	.sys_clk     (sys_clk),//系统时钟,频率100MHz
	.sys_rst_n   (sys_rst_n),//复位信号,低电平有效
	.init_end    (init_end),//初始化结束信号
	.aref_en     (aref_en),//自动刷新使能,由仲裁模块返回

	.aref_cmd    (aref_cmd),//自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
	.aref_ba     (aref_ba),//自动刷新阶段Bank地址
	.aref_addr   (aref_addr),//地址数据,辅助预充电操作,A12-A0,13位地址
	.aref_end    (aref_end),//自动刷新结束标志
	.aref_req    (aref_req) //向仲裁模块发送自动刷新请求信号
);



sdram_write     sdram_write_inst
(
	.sys_clk		(sys_clk),
	.sys_rst_n		(sys_rst_n),
	.init_end		(init_end),//初始化完成信号,由初始化模块传入
	.wr_en			(wr_en),//写使能,由仲裁模块传入
	.wr_addr		(wr_sdram_addr),//包括bank地址,行地址,列地址
	.wr_data		(wr_sd_data),//待写入sdram的数据,由fifo传入
	.wr_burst_len	(wr_burst_len),//整页突发,这里10突发终止,如果没有突发终止会写满一行

	.wr_ack			(wr_sdram_ack),//写sdram有效信号
	.wr_end			(wr_end),//一次突发写结束
	.write_cmd		(wr_cmd),
	.write_ba		(wr_ba),
	.write_addr		(wr_addr),
	.wr_sdram_en	(wr_sdram_en),
	.wr_sdram_data	(wr_sdram_data)
	
);


sdram_read  sdram_read_inst
(
	.sys_clk		(sys_clk),
	.sys_rst_n		(sys_rst_n),
	.init_end		(init_end),//初始化完成信号,由初始化模块传入
	.rd_en			(rd_en),//写使能,由仲裁模块传入
	.rd_addr        (rd_sdram_addr),//包括bank地址,行地址,列地址
	.rd_data        (sdram_dq),//读出sdram的数据,由sdram读出
	.rd_burst_len   (rd_burst_len),//整页突发,这里10突发终止,如果没有突发终止会读满一行

	.rd_ack			(rd_sdram_ack),//读sdram有效信号
	.rd_end			(rd_end),//一次突发读结束
	.read_cmd		(rd_cmd),
	.read_ba		(rd_ba),
	.read_addr		(rd_addr),
	.rd_sdram_data	(rd_sdram_out) //sdram读出数据
	
);



sdram_arbit     sdram_arbit
(
	.sys_clk     (sys_clk),
	.sys_rst_n   (sys_rst_n),
    
	.init_end    (init_end ),
	.init_cmd    (init_cmd ),
	.init_ba     (init_ba  ),
	.init_addr   (init_addr),
    
	.aref_req    (aref_req ),
	.aref_end    (aref_end ),
	.aref_cmd    (aref_cmd ),
	.aref_ba     (aref_ba  ),
	.aref_addr   (aref_addr),
	
	.wr_req      (wr_sdram_req),
	.wr_end      (wr_end     ),
	.wr_cmd      (wr_cmd     ),
	.wr_ba       (wr_ba      ),
	.wr_addr     (wr_addr    ),
	.wr_sdram_en (wr_sdram_en),
	.wr_data     (wr_sdram_data),
	
	.rd_req      (rd_sdram_req),
	.rd_end      (rd_end),
	.rd_cmd      (rd_cmd),
	.rd_ba       (rd_ba),
	.rd_addr     (rd_addr),


	.aref_en     (aref_en),
	.wr_en       (wr_en),
	.rd_en       (rd_en),
    
	.sdram_cke   (sdram_cke),
	.sdram_cs_n  (sdram_cs_n),
	.sdram_ras_n (sdram_ras_n),
	.sdram_cas_n (sdram_cas_n),
	.sdram_we_n  (sdram_we_n),
	.sdram_ba    (sdram_ba),
	.sdram_addr  (sdram_addr),
	.sdram_dq    (sdram_dq)
	
);

endmodule

读写请求数监控 读写控制_自动刷新_22


读写请求数监控 读写控制_等待时间_23


二、添加FIFO控制模块

异步FIFO模块能够解决多bit数据跨时钟域处理的问题,在SDRAM操作冲突时可以将数据寄存在FIFO以防止数据被覆盖或者丢失,为数据读写模块提供SDRAM读写地址,产生读写请求。引入FIFO,相当于把对SDRAM的操作封装成了一个FIFO。

module  sdram_fifo_ctrl
(
    input   wire            sys_clk         ,//
    input   wire            sys_rst_n       ,//
    input   wire            wr_rst           ,
    input   wire    [9:0]   wr_burst_len    ,//
    input   wire            wr_fifo_clk     ,//写fifo的时钟
    input   wire            wr_fifo_req     ,//写fifo的写请求
    input   wire    [15:0]  wr_fifo_data    ,//写入写FIFO的数据,由外部传入,如ad采集
    input   wire    [23:0]  sdram_wr_b_addr ,//
    input   wire    [23:0]  sdram_wr_e_addr ,//
     
    input   wire            rd_rst           ,
    input   wire            rd_fifo_clk     ,//读fifo时钟,50M
    input   wire            rd_fifo_req     ,//读fifo读请求
    input   wire    [23:0]  sdram_rd_b_addr ,//sdram读数据首地址
    input   wire    [9:0]   rd_burst_len    ,//
    input   wire    [23:0]  sdram_rd_e_addr ,//
                                             
    input   wire            read_valid      ,//
    input   wire            init_end        ,//初始化完成信号,由sdram_ctrl传入
    input   wire            sdram_wr_ack    ,//sdram写数据有效信号,提前一个时钟周期,写fifo的读请求
    input   wire            sdram_rd_ack    ,//sdram读数据有效信号,读FIFO的写请求
    input   wire    [15:0]  sdram_data_out  ,//由sdram读出到读fifo中缓存
                                             
                                             
    output  reg             sdram_wr_req    ,//sdram写数据请求
    output  reg     [23:0]  sdram_wr_addr   ,//sdram写地址,首地址开始写,写突发
    output  wire    [15:0]  sdram_data_in   ,//写fifo的输出,待写入sdram的数据
    output  reg             sdram_rd_req    ,//sdram读数据请求
    output  reg     [23:0]  sdram_rd_addr   ,//sdram读地址,首地址开始读
    output  wire    [15:0]  rd_fifo_rd_data ,//由读fifo读出缓存的数据
    output  wire    [9:0]   rd_fifo_num      //对sdram读出到fifo的数据计数
);

reg          wr_ack_reg;
wire         wr_ack_fall;
reg          rd_ack_reg;
wire         rd_ack_fall;
wire [9:0]   wr_fifo_num;


always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		wr_ack_reg <= 1'b0;
    else
        wr_ack_reg <= sdram_wr_ack;
 
 
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		rd_ack_reg <= 1'b0;
    else
        rd_ack_reg <= sdram_rd_ack;
        
        
assign  wr_ack_fall = (wr_ack_reg && ~sdram_wr_ack);
assign  rd_ack_fall = (rd_ack_reg && ~sdram_rd_ack);


always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		sdram_wr_addr <= 24'd0;
    else    if(wr_rst)
        sdram_wr_addr <= sdram_wr_b_addr;
    else    if(wr_ack_fall)
        begin
            if(sdram_wr_addr < (sdram_wr_e_addr - wr_burst_len))
                        //不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址,写地址累加
                sdram_wr_addr   <=  sdram_wr_addr + wr_burst_len;
            else        //不使用乒乓操作,到达末地址,回到写起始地址
                sdram_wr_addr   <=  sdram_wr_b_addr;            
        end
    
   
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		sdram_rd_addr <= 24'd0;
    else    if(rd_rst)
        sdram_rd_addr <= sdram_rd_b_addr;
    else    if(rd_ack_fall)
        begin
            if(sdram_rd_addr < (sdram_rd_e_addr - rd_burst_len))
                        //不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址,写地址累加
                sdram_rd_addr   <=  sdram_rd_addr + rd_burst_len;
            else        //不使用乒乓操作,到达末地址,回到写起始地址
                sdram_rd_addr   <=  sdram_rd_b_addr;            
        end        


always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		begin
            sdram_wr_req <= 1'b0;
            sdram_rd_req <= 1'b0;
        end
     else   if(init_end)
        begin
            if(wr_fifo_num >= wr_burst_len)
                begin
                    sdram_wr_req <= 1'b1;
                    sdram_rd_req <= 1'b0;
                end
             else   if(rd_fifo_num < rd_burst_len && read_valid)
                begin
                    sdram_wr_req <= 1'b0;
                    sdram_rd_req <= 1'b1;
                end
             else
                begin
                    sdram_wr_req <= 1'b0;
                    sdram_rd_req <= 1'b0;
                end              
        end
     else
        begin
            sdram_wr_req <= 1'b0;
            sdram_rd_req <= 1'b0;
        end                      
        

sdram_fifo	sdram_wr_fifo_inst (
	.aclr ( ~sys_rst_n || wr_rst ),
	.data ( wr_fifo_data ),
	.rdclk ( sys_clk ),
	.rdreq ( sdram_wr_ack ),
	.wrclk ( wr_fifo_clk ),
	.wrreq ( wr_fifo_req ),
	.q ( sdram_data_in ),
	.rdusedw ( wr_fifo_num ),
	.wrusedw (  )
	);


sdram_fifo	sdram_rd_fifo_inst (
	.aclr ( ~sys_rst_n || rd_rst ),
	.data ( sdram_data_out ),
	.rdclk ( rd_fifo_clk ),
	.rdreq ( rd_fifo_req ),
	.wrclk ( sys_clk ),
	.wrreq ( sdram_rd_ack ),
	.q ( rd_fifo_rd_data ),
	.rdusedw ( ),
	.wrusedw ( rd_fifo_num )
	);


endmodule

仿真文本

`timescale  1ns/1ns


module  tb_uart_sdram();



//wire define
wire            tx          ;
wire            sdram_clk   ;
wire            sdram_cke   ;
wire            sdram_cs_n  ;
wire            sdram_cas_n ;
wire            sdram_ras_n ;
wire            sdram_we_n  ;
wire    [1:0]   sdram_ba    ;
wire    [12:0]  sdram_addr  ;
wire    [1:0]   sdram_dqm   ;
wire    [15:0]  sdram_dq    ;

//reg define
reg           sys_clk   ;
reg           sys_rst_n ;
reg           rx    ;
reg   [7:0]   data_mem [30:0] ;  //data_mem是一个存储器,相当于一个ram



//读取sim文件夹下面的data.txt文件,并把读出的数据定义为data_mem
initial
  $readmemh("E:/intelFPGA/data/uart_sdram/sim/test_data.txt",data_mem);

//时钟、复位信号
initial
  begin
    sys_clk     =   1'b1  ;
    sys_rst_n   <=  1'b0  ;
    #200
    sys_rst_n   <=  1'b1  ;
  end

always  #10 sys_clk = ~sys_clk;


initial
  begin
    rx  <=  1'b1;
    #200
    rx_byte();
  end

task  rx_byte();
  integer j;
  for(j=0;j<30;j=j+1)
    rx_bit(data_mem[j]);
endtask

task  rx_bit(input[7:0] data);  //data是data_mem[j]的值。
  integer i;
    for(i=0;i<10;i=i+1)
      begin
        case(i)
          0:  rx  <=  1'b0   ;  //起始位
          1:  rx  <=  data[0];
          2:  rx  <=  data[1];
          3:  rx  <=  data[2];
          4:  rx  <=  data[3];
          5:  rx  <=  data[4];
          6:  rx  <=  data[5];
          7:  rx  <=  data[6];
          8:  rx  <=  data[7];  //上面8个发送的是数据位
          9:  rx  <=  1'b1   ;  //停止位
        endcase
        #(52*20);                  //一个波特时间=ssys_clk周期*波特计数器
      end
endtask

//重定义defparam,用于修改参数,缩短仿真时间
//defparam uart_sdram_inst.uart_rx_inst.BAUD_CNT_END      = 52;
//defparam uart_sdram_inst.uart_rx_inst.BAUD_CNT_END_HALF = 26;
//defparam uart_sdram_inst.uart_tx_inst.BAUD_CNT_END      = 52;
defparam uart_sdram_inst.CLK_FREQ = 50_000_0;
defparam uart_sdram_inst.fifo_read_inst.BAUD_CNT = 52;
defparam uart_sdram_inst.fifo_read_inst.BAUD_HALF = 26;
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;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//-------------uart_sdram_inst-------------
uart_sdram  uart_sdram_inst(

    .sys_clk     (sys_clk     ),
    .sys_rst_n   (sys_rst_n   ),
    .rx          (rx          ),

    .tx          (tx          ),

    .sdram_clk   (sdram_clk   ),
    .sdram_cke   (sdram_cke   ),
    .sdram_cs_n  (sdram_cs_n  ),
    .sdram_cas_n (sdram_cas_n ),
    .sdram_ras_n (sdram_ras_n ),
    .sdram_we_n  (sdram_we_n  ),
    .sdram_ba    (sdram_ba    ),
    .sdram_addr  (sdram_addr  ),
    .sdram_dqm   (sdram_dqm   ),
    .sdram_dq    (sdram_dq    )

);

//-------------sdram_model_plus_inst-------------
sdram_model_plus    sdram_model_plus_inst(
    .Dq     (sdram_dq       ),
    .Addr   (sdram_addr     ),
    .Ba     (sdram_ba       ),
    .Clk    (sdram_clk      ),
    .Cke    (sdram_cke      ),
    .Cs_n   (sdram_cs_n     ),
    .Ras_n  (sdram_ras_n    ),
    .Cas_n  (sdram_cas_n    ),
    .We_n   (sdram_we_n     ),
    .Dqm    (sdram_dqm      ),
    .Debug  (1'b1           )
);

endmodule

读写请求数监控 读写控制_预充电_24


读写请求数监控 读写控制_预充电_25


读写请求数监控 读写控制_读写请求数监控_26


把采集数据写入写FIFO中,等待SDRAM控制模块SDRAM写数据有效信号拉高时,开始从写FIFO读出传到SDRAM。当SDRAM读数据有效信号拉高时,开始从SDRAM读出数据写入到读FIFO中,上位机再从读FIFO中读出数据。以上为学习内容,具体可参考野火教程。