SDRAM读写控制

article/2025/10/26 8:42:18

SDRAM读写控制器,这里分为三个部分,分别是SDRAM的基本操作实现,SDRAM控制器,封装成FIFO,以方便使用。
一、SDRAM的基本操作:初始化模块、自动刷新模块、写操作模块、读操作模块、SDRAM仲裁模块,顶层模块。
1、初始化模块
在这里插入图片描述
上图是初始化模块的时序图。有图可知,SDRAM上电并且时钟稳定后,SDRAM首先的延迟等待100us,等待期间只能赋予禁止指令或者空操作指令。等待延时完成后,需要对SDRAM所有bank进行一次预充电操作(A10)置为高电平。然后进入预充电完成等待时间tRP,等待完成后至少执行两次自动刷新指令。之后对SDRAM进行加载模式寄存器。
在这里插入图片描述
时序分析图
在这里插入图片描述
寄存器模式配置
在这里插入图片描述

/*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;elseinit_state <= init_state;INIT_PRE :	//预充电状态,直接跳转到预充电等待状态init_state <= INIT_TRP;INIT_TRP :	//预充电等待状态,等待结束,跳转到自动刷新状态if(trp_end)init_state <= INIT_AR;elseinit_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;elseinit_state <= init_state;INIT_MRS :	//模式寄存器设置状态,直接跳转到模式寄存器设置等待状态init_state <= INIT_TMRD;	INIT_TMRD:	//模式寄存器设置等待状态,等待结束,跳到初始化完成状态if(tmrd_end)init_state <= INIT_END;elseinit_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;elsecnt_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;elsecnt_clk <= cnt_clk + 1'b1;//cnt_clk_rst:时钟周期计数复位标志		
always@(*)begincase(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;endcaseend//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)begincmd_init <= NOP;ba_init	 <= 2'b11;addr_init<= 13'h1fff;endelse	case(init_state)INIT_IDLE,INIT_TRF,INIT_TRP,INIT_TMRD,INIT_END:begincmd_init <= NOP;ba_init	 <= 2'b11;addr_init<= 13'h1fff;				endINIT_PRE:begincmd_init <= PCHARGE;ba_init	 <= 2'b11;addr_init<= 13'h1fff;//预充电时,A10为高电平对所有bank进行预充电				endINIT_AR:begincmd_init <= AUTO_RFE;ba_init	 <= 2'b11;addr_init<= 13'h1fff;				endINIT_MRS:begincmd_init <= MREG_ST;ba_init	 <= 2'b00; //配置寄存器模式时bank地址给00addr_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:整页,其他:保留			};				enddefault:begincmd_init <= NOP;ba_init	 <= 2'b11;addr_init<= 13'h1fff;endendcaseassign	init_end = (init_state == INIT_END)?1'b1:1'b0;			endmodule

在这里插入图片描述

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

/*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		,//系统时钟,频率100MHzinput	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~20nsTRFC	= 3'd7;//66nsparameter	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;elsearef_state <=aref_state;AREF_PCH	:	//预充电状态,直接跳转到预充电等待状态aref_state <= AREF_TRP;AREF_TRP	:if(trp_end)	//预充电等待结束,跳转到自动刷新状态aref_state <= AUTO_REF;elsearef_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;elsecnt_clk <= cnt_clk + 1'b1;always@(*)begincase(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;endcaseend
//预充电等待时间结束信号
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)beginaref_cmd    <=  NOP;aref_ba     <=  2'b11;aref_addr   <=  13'h1fff;endelsecase(aref_state)AREF_IDLE,AREF_TRP,AREF_TRF:    //执行空操作指令beginaref_cmd    <=  NOP;aref_ba     <=  2'b11;aref_addr   <=  13'h1fff;endAREF_PCH:  //预充电指令beginaref_cmd    <=  PCHARGE;aref_ba     <=  2'b11;aref_addr   <=  13'h1fff;end AUTO_REF:   //自动刷新指令beginaref_cmd    <=  AUTOREF;aref_ba     <=  2'b11;aref_addr   <=  13'h1fff;endAREF_END:   //一次自动刷新完成beginaref_cmd    <=  NOP;aref_ba     <=  2'b11;aref_addr   <=  13'h1fff;end    default:beginaref_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),这时也有可能会出现数据丢失的情况。
在这里插入图片描述
自动刷新模块的仿真情况,当刷新计数器计数到最大值时自动刷新请求拉高

3、SDRAM写操作模块
写操作时序图,页突发
在这里插入图片描述
当初始化完成且写使能信号拉高时,开始写激活,激活指定bank的某一行,经过写激活等待tRCD,开始写入数据,这时需要给列首地址A0~A8和已激活的bank地址,页突发可以写入页突发终止指令。
写模块状态转移图
在这里插入图片描述

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_ackoutput	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;elsewrite_state <= write_state;WR_ACTIVE	:	//激活状态直接跳转到激活等待write_state <= WR_TRCD;WR_TRCD		:	//激活等待结束,跳转到写命令状态if(trcd_end)write_state <= WR_WRITE;elsewrite_state <= write_state;WR_WRITE	:	//写指令状态直接跳转到写数据状态write_state <= WR_DATA;WR_DATA		:	//突发写结束,跳转到预充电状态if(twrite_end)write_state <= WR_PRE;elsewrite_state <= write_state;WR_PRE		:	//预充电状态直接跳转到预充电等待状态write_state <= WR_TRP;WR_TRP		:	//预充电等待结束,跳转到结束状态if(trp_end)write_state <= WR_END;elsewrite_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;elsecnt_clk <= cnt_clk + 1'b1;always@(*)begincase(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;endcaseendassign	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)beginwrite_cmd <= NOP;write_ba  <= 2'b11;write_addr<= 13'h1fff;endelse	case(write_state)WR_IDEL,WR_TRCD,WR_TRP,WR_END	:beginwrite_cmd <= NOP;write_ba <= 2'b11;write_addr<= 13'h1fff;endWR_ACTIVE	:beginwrite_cmd <= ACTIVE;write_ba   <= wr_addr[23:22];write_addr <= {4'b0000,wr_addr[8:0]};end		WR_WRITE	:beginwrite_cmd <= WRITE;write_ba  <= 2'b00;write_addr<= 13'h0000;endWR_DATA		:if(twrite_end)write_cmd <= STOP;else	beginwrite_cmd <= NOP;write_ba  <= 2'b11;write_addr<= 13'h1fff;endWR_PRE		:beginwrite_cmd <= P_CHARGE;write_ba  <= wr_addr[23:22];write_addr<= 13'h0400;enddefault		:	beginwrite_cmd <= NOP;write_ba  <= 2'b11;write_addr<= 13'h1fff;endendcase//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;elsewr_sdram_en <= wr_ack;assign	wr_sdram_data = (wr_sdram_en)? wr_data:16'd0;	
endmodule

在这里插入图片描述
在这里插入图片描述
仿真结果写入十个数据后执行了突发终止指令
4、SDRAM读操作模块
页突发读模式时序图
在这里插入图片描述
发送读激活命令给SDRAM,选中要激活的bank,读指令后等待tRCD后通过地址总线传入行地址,等待列选通潜伏期,由配置寄存器模式的选定,然后开始读取数据,读完所需数据之后写入读突发终止指令,与写模块不同,当读突发终止指令执行后,还会读取潜伏期个数的数据。入潜伏期为3,则突发终止后还会读出三个数据。
读模块状态转移图
在这里插入图片描述
在这里插入图片描述
读突发长度为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;elserd_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;elseread_state <= read_state;RD_ACTIVE	:	//激活状态直接跳转到激活等待read_state <= RD_TRCD;RD_TRCD		:	//激活等待结束,跳转到读命令状态if(trcd_end)read_state <= RD_READ;elseread_state <= read_state;RD_READ	:	//读指令状态直接跳转到列选通潜伏期状态read_state <= RD_CL;RD_CL	:	//列选通潜伏期结束,跳转到读数据状态if(tcl_end)read_state <= RD_DATA;elseread_state <= read_state;RD_DATA		:	//突发读结束,跳转到预充电状态if(tread_end)read_state <= RD_PRE;elseread_state <= read_state;RD_PRE		:	//预充电状态直接跳转到预充电等待状态read_state <= RD_TRP;RD_TRP		:	//预充电等待结束,跳转到结束状态if(trp_end)read_state <= RD_END;elseread_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;elsecnt_clk <= cnt_clk + 1'b1;always@(*)begincase(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;endcaseendassign	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)beginread_cmd <= NOP;read_ba  <= 2'b11;read_addr<= 13'h1fff;endelse	case(read_state)RD_IDLE,RD_TRCD,RD_TRP,RD_END,RD_CL	:beginread_cmd <= NOP;read_ba <= 2'b11;read_addr<= 13'h1fff;endRD_ACTIVE	:beginread_cmd <= ACTIVE;read_ba   <= rd_addr[23:22];read_addr <= {4'b0000,rd_addr[8:0]};end		RD_READ	:beginread_cmd <= READ;read_ba  <= 2'b00;read_addr<= 13'h0000;endRD_DATA		:if(stop_end)read_cmd <= STOP;else	beginread_cmd <= NOP;read_ba  <= 2'b11;read_addr<= 13'h1fff;endRD_PRE		:beginread_cmd <= P_CHARGE;read_ba  <=rd_addr[23:22];read_addr<= 13'h0400;enddefault		:	beginread_cmd <= NOP;read_ba  <= 2'b11;read_addr<= 13'h1fff;endendcase//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

在这里插入图片描述
在这里插入图片描述
5、SDRAM仲裁模块
在这里插入图片描述
前面四个模块都与用户接口和SDRAM建立起了双向数据通信,如果多个指令一起发送请求,如果没有优先级,就有可能造成冲突,从而导致SDRAM工作出错,因为就需要引入仲裁模块,对前面四个模块进行优先级的划分。
在这里插入图片描述
当初始化完成时,跳转到仲裁状态,当读写与刷新,其中有一个为有效,其他两个无效,则执行操作,当执行操作之后,回到仲裁状态,若两路或多路请求信号同时有效,优先执行优先级较高的操作,默认优先级为自动刷新操作>数据写操作>数据读操作。

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;elsestate <= IDLE;ARBIT	:if(aref_req)state <= A_REF;else	if(wr_req)state <= WRITE;else	if(rd_req)state <= READ;elsestate <= ARBIT;WRITE	:if(wr_end)state <= ARBIT;elsestate <= WRITE;READ	:if(rd_end)state <= ARBIT;elsestate <= READ;A_REF	:if(aref_end)state <= ARBIT;elsestate <= A_REF;default:state <= IDLE;endcasealways@(*)case(state)IDLE	:beginsdram_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	:beginsdram_cmd	<=	wr_cmd;sdram_ba	<=	wr_ba;sdram_addr	<=	wr_addr;				end			READ	:beginsdram_cmd	<=	rd_cmd;sdram_ba	<=	rd_ba;sdram_addr	<=	rd_addr;				end			A_REF	:beginsdram_cmd	<=	aref_cmd;sdram_ba	<=	aref_ba;sdram_addr	<=	aref_addr;				enddefault:beginsdram_cmd	<=	NOP;sdram_ba	<=	2'b11;sdram_addr	<=	13'h1fff;endendcasealways@(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 writeinput	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 readinput	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

在这里插入图片描述
在这里插入图片描述
二、添加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时钟,50Minput   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;elsewr_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;elserd_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)beginif(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;            endalways@(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)beginif(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)beginsdram_wr_req <= 1'b0;sdram_rd_req <= 1'b0;endelse   if(init_end)beginif(wr_fifo_num >= wr_burst_len)beginsdram_wr_req <= 1'b1;sdram_rd_req <= 1'b0;endelse   if(rd_fifo_num < rd_burst_len && read_valid)beginsdram_wr_req <= 1'b0;sdram_rd_req <= 1'b1;endelsebeginsdram_wr_req <= 1'b0;sdram_rd_req <= 1'b0;end              endelsebeginsdram_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/1nsmodule  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);//时钟、复位信号
initialbeginsys_clk     =   1'b1  ;sys_rst_n   <=  1'b0  ;#200sys_rst_n   <=  1'b1  ;endalways  #10 sys_clk = ~sys_clk;initialbeginrx  <=  1'b1;#200rx_byte();endtask  rx_byte();integer j;for(j=0;j<30;j=j+1)rx_bit(data_mem[j]);
endtasktask  rx_bit(input[7:0] data);  //data是data_mem[j]的值。integer i;for(i=0;i<10;i=i+1)begincase(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

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


http://chatgpt.dhexx.cn/article/jQIs88vR.shtml

相关文章

SDRAM 控制器(二)——初始化模块

1、初始化模块 SDRAM 的初始化是芯片上电后必须进行的一项操作&#xff0c;只有进行了初始化操作的 SDRAM 芯片才可被正常使用。SDRAM 的初始化是一套预先定义好的流程&#xff0c;除此之外的其 他操作会导致 SDRAM 出现不可预知的后果。 初始化时序图&#xff1a; CK&#xf…

SDRAM详细介绍

概念介绍&#xff1a; SDRAM&#xff1a;Synchronous Dynamic Random Access Memory&#xff0c;同步动态随机存储器。同步是指其时钟频率和CPU前端总线的系统时钟相同&#xff0c;并且内部命令的发送与数据的传输都以它为基准&#xff1b;动态是指存储阵列需要不断的刷新来保证…

sdram简易控制器设计

耗时一周&#xff0c;终于完成sdram简易控制器的所有代码设计&#xff0c;其中感谢开源骚客 – 邓堪文老师在b站发布的相关视频学习教材&#xff1b;其中仿真模块及所使用到的sdram仿真文件来源于开源骚客&#xff1b; 因为时间较为紧迫&#xff0c;其中就不做代码的一些注释&…

基于FPGA的SDRAM控制器设计(一)

基于FPGA的SDRAM控制器设计&#xff08;一&#xff09; 1. SDRAM控制器整体框架2.UART_RX模块3.UART_TX模块4. RX与TX模块的整合5.需要注意的问题6.代码7.参考资料 1. SDRAM控制器整体框架 图1.1整体框架 PC端通过串口模块UART_RX发送读写命令以及数据到Cmd_encode模块&#xf…

基于FPGA的SDRAM控制器设计(1)

基于FPGA的SDRAM初始化配置 SDRAM简述SDRAM的引脚及作用SDRAM初始化时序控制SDRAM上电时序代码SDRAM测试模块的代码仿真测试结果参考文献总结 SDRAM简述 SDRAM&#xff08; Synchronous Dynamic Random Access Memory&#xff09;&#xff0c;同步动态随机存储器。同步是指 Me…

FPGA进阶(3):SDRAM读写控制器的设计与验证

文章目录 第50讲&#xff1a;SDRAM读写控制器的设计与验证理论部分设计与实现1. sdram_ctrlsdram_initsdram_a_refsdram_writesdram_readsdram_arbitsdram_ctrl 2. sdram_topfifo_ctrlsdram_top 3. uart_sdramuart_rxuart_txfifo_readuart_sdram 第50讲&#xff1a;SDRAM读写控…

SDRAM

简介、优缺点、历史 1、译为“同步动态随机存取内存”&#xff0c;区别于异步DRAM。SRAM是异步静态存储器。 2、同步(Synchronous)&#xff1a;与通常的异步 DRAM 不同&#xff0c; SDRAM 存在一个同步接口&#xff0c;其工作时钟的时钟频率与对应控制器(CPU/FPGA上的读写控制…

关于SDRAM存储器

一、对SDRAM的初步认识 1.1 什么是SDRAM SDRAM&#xff08;Synchronous Dynamic Random Access Memory&#xff09;&#xff0c;同步动态随机存取存储器。 同步&#xff1a;工作频率与对应控制器的系统时钟频率相同&#xff0c;且内存内部的命令以及数据的传输都以此为基准 …

内存控制器与SDRAM

内存接口概念&#xff1a; 通常ARM芯片内置的内存很少&#xff0c;要运行Linux&#xff0c;需要扩展内存。ARM9扩展内存使用SDRAM内存&#xff0c;ARM11使用 DDR SDRAM。S3C2440通常外接32位64MBytes的SDRAM&#xff0c;采用两片16位32M的SDRAM芯片&#xff0c;SDRAM芯片通过地…

SDRAM驱动篇之简易SDRAM控制器的verilog代码实现

在Kevin写的上一篇博文《SDRAM理论篇之基础知识及操作时序》中&#xff0c;已经把SDRAM工作的基本原理和SDRAM初始化、读、写及自动刷新操作的时序讲清楚了&#xff0c;在这一片博文中&#xff0c;Kevin来根据在上一篇博文中分析的思路来把写一个简单的SDRAM控制器。 我们在上一…

FPGA之SDRAM控制器设计(一)

MT48LC128M4A2 – 32 Meg x 4 x 4 banks是512M SRAM&#xff0c;总体概述如下图 分别从上电初始化&#xff0c;刷新&#xff0c;写&#xff0c;读四个部分进行设计&#xff0c;此外还包含主控状态机&#xff0c;一个顶层。 1&#xff1a;上电初始化 整体架构&#xff1a;从控…

内存控制器与SDRAM【赞】

原文链接&#xff1a;https://blog.csdn.net/qq_31216691/article/details/87115697 内存接口概念&#xff1a; 通常ARM芯片内置的内存很少&#xff0c;要运行Linux&#xff0c;需要扩展内存。ARM9扩展内存使用SDRAM内存&#xff0c;ARM11使用 DDR SDRAM。S3C2440通常外接32位6…

SDRAM 介绍

目录 1、名词解释 2、SDRAM 内部结构 3、SDRAM 外部信号描述 4、SDRAM 命令 4.1、COMMAND INHIBIT 4.2、NO OERATION 4.3、ACTIVE 4.4、LOAD MODE REGISTER (LMR) 4.5、READ 4.6、WRITE 4.7、PRECHARGE 4.8、BURST TERMINATE 4.9、REFRESH 4.9.1、AUTO REFRESH …

SDRAM控制器操作时序

此为学习http://dengkanwen.com/137.html整理的笔记&#xff0c;侵删&#xff01; SDRAM工作原理 内部的状态跳转图 我们所需关注的几个地方&#xff1a; 1&#xff09;粗黑线表示在该状态下会自动跳转到另一个状态&#xff0c;细黑线表示需要给命令才会跳转。 2&#xff09…

SDR SDRAM控制器设计

目录 前言 1、关于刷新 2、关于数据中心对齐 3、SDRAM芯片手册介绍 3.1SDRAM芯片的管脚 3.2 SDRAM指令集 3.3 模式寄存器 3.4 关于SDRAM上电初始化和装载模式寄存器 3.5 SDRAM刷新时序 3.6 关于写访问 3.7 关于突发访问 4、FPGA工程设计 4.1状态机设计 5、仿真测试…

【GD32】从零开始学GD32单片机高级篇——外部存储器控制器EXMC详解+SDRAM读写例程

目录 简介外部设备地址映射NOR和PSRAM的地址映射NAND/PC Card地址映射SDRAM地址映射 NOR/PSRAM控制器接口描述控制时序模式1模式2 NAND Flash或PC Card控制器接口描述控制时序 SDRAM控制器接口描述控制时序突发读操作突发写操作读写FIFO跨边界读写操作低功耗模式自刷新模式掉电…

初识内存控制器和SDRAM【一文了解】

原文链接&#xff1a;https://blog.csdn.net/qq_36243942/article/details/85596249 目录 1.引入内存控制器 2.不同位宽内存设备之间的连接 3.如何确定芯片的访问地址 4.分析读写NOR FLASH的读写时序 5.SDRAM初识 6.编程读/写 SDRAM 附录&#xff1a;源代码 1.引入内存控制器 我…

存储控制器(SDRAM操作)

什么是存储控制器 2440是32位单片机&#xff0c;进行数据访问时通过32位地址访问。 CPU发出32位地址信号给存储控制器&#xff0c;存储控制器根据地址信号设置片选信号及地址总线&#xff0c;将相应数据通过数据总线传回存储控制器&#xff0c;存储控制器将收到的数据以字节为…

数字IC实践项目(2)——高速SDRAM控制器的设计与综合(入门级工程项目)

数字IC实践项目&#xff08;2&#xff09;—高速SDRAM控制器的设计与综合&#xff08;入门级工程项目&#xff09; 写在前面的话项目简介和学习目的SDRAM简介SDRAM控制器简介完整项目框图SDRAM控制器项目框图SDRAM初始化模块SDRAM自动刷新模块SDRAM写模块SDRAM读模块SDRAM仲裁机…

SDRAM控制器——仲裁模块的实现

前面一文中&#xff0c;我们已经对SDRAM的上电初始化、自动刷新以及突发读写进行了学习。 本文跟着大佬学习SDRAM中的仲裁模块。 仲裁机制 仲裁&#xff08;arbit&#xff09;&#xff1a;在FPGA中&#xff0c;当多个source源同时发出请求&#xff0c;容易导致操作冲突&#x…