fpga控制sdram存储器的读写2:sdram初始化

本文详细阐述了SDRAM芯片上电后的初始化步骤,包括关闭未激活行、预充电、刷新、设置模式寄存器,以及关键操作时序、延迟时间和Verilog代码实现。通过解读数据手册,一步步指导如何在FPGA中精确控制初始化流程以确保高效稳定的工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.理论知识

sdram芯片上电后,执行读写操作之前,首先要做的第一件事就是初始化,这里解释下初始化的目的。

1.在使用sdram资源的时候,要先给出芯片的地址,这个地址就是行/列地址。初始化第一步,首先就是要关闭所有bank中激活的行。虽然系统上电之后,sdram还没开始使用,没有被激活的行,但是依然要执行预充电这一步操作,确保在使用之前所有bank中没有激活的行。

2.sdram是动态存储器,物理层面是靠电容来维持电平信号,需要不断地给它刷新来给电容充电。在使用sdram资源之前,要给sdram的所有bank刷新。为了确保刷新成功,在初始化期间,要刷新多次。

3.刷新完之后,要给sdram发送模式寄存器配置指令,模式寄存器主要控制芯片的读写方式突发方式突发长度等配置信息。为后面的读写操作提前设置参数信息。

二.解读数据手册

1.初始化操作时序
初始化操作时序
2.延迟时间
在这里插入图片描述
在这里插入图片描述
3.配置寄存器的值
在这里插入图片描述
上面的时序图是从镁光公司提供的sdram芯片数据手册中截选出来的。时序图介绍:

1.sdram上电后,加载稳定的时钟信号,然后设置CKE为高电平。芯片手册要求sdram的时钟频率最高不超过166M,这里我们选用100M时钟频率。sdram要求在时钟上升沿到来的时候,所有的信号数据要准备好,sdram会在上升沿采集这些信号。我们在fpga内部定义一个100M时钟信号,所有信号都在100M时钟的上升沿赋值。但是fpga的信号传输到sdram芯片中间会有延迟,所以我们另外再定义一个100M时钟信号,比前面的100M时钟信号偏移一定的角度,新的100M时钟上升沿比前面的100M时钟早到一会儿,然后把这个新的100M时钟信号发送给sdram。这样就能确保sdram能在时钟上升沿的时候采集到稳定的信号。

2.等待至少T=100us的时间,我们这里取200us,此过程中操作命令保持为空操作命令。

3.200us等待结束后,写入预充电命令,A10设置为高电平,对所有Bank进行预充电。

4.预充电指令写入后,等待tRP时间,从上图中找到tRP最少为20ns,sdram的时钟是100M,所以我们这里设置为2个时钟周期,此过程中操作命令保持为空操作命令。

5.等待tRP时间之后,写入自动刷新命令。

6.自动刷新命令写入后,等待 tRFC时间,从上图中找到tRFC最少为66纳秒,我们设置为7个时钟周期,此过程中操作命令保持为空操作命令。

7.tRFC等待时间结束后,再次写入自动刷新命令。

8.自动刷新命令写入后,等待 tRFC时间,此过程中操作命令保持为空操作命令。

9.数据手册中要求自动刷新的次数不少于2,我们这里设定自动刷新的次数等于8次。

10.自动刷新结束后,写入模式寄存器配置指令,具体模式寄存器配置的数据值是通过在地址总线 A0-A12赋予不同的信号来给出的。

11.模式寄存器配置指令写入后,等待 tMRD 时间,从上图中找到tMRD最少是2个时钟周期,我们这里设置为3个时钟周期,此过程中操作命令保持为空操作命令。

12.tMRD等待时间结束后,sdram芯片初始化完成,才能进行后续的读写和刷新操作。

三.设计verilog时序图

请添加图片描述

四.verilog代码
module sdram_init
(
	input	wire	sys_clk,
	input	wire	sys_rst_n,
	
	output	reg		[3:0] init_cmd,		// {CS_N,RAS_N,CAS_N,WE_N}
	output	reg		[1:0] init_ba,		// BA[1:0]
	output	reg		[12:0] init_addr,	// A[12:0]
	output	reg		init_end			// 初始化结束信号
);

// 状态
parameter	ST_IDLE 		= 0,
			ST_PRE 			= 1,
			ST_PRE_WAIT		= 2,
			ST_AUTO			= 3,
			ST_AUTO_WAIT	= 4,
			ST_MODE			= 5,
			ST_MODE_WAIT	= 6,
			ST_END			= 7;

// 延迟计数器的值
parameter	CNT_IDLE		= 20_000,
			CNT_PRE_WAIT	= 2,
			CNT_AUTO_WAIT	= 7,
			CNT_MODE_WAIT	= 3;

// 自动刷新的次数
parameter	CNT_AUTO		= 8;

// 命令
parameter	CMD_NOP			= 4'b0111,	// 无操作命令也叫空命令,控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0111
			CMD_PRE			= 4'b0010,	// 预充电命令,分为全部预充电和指定L-Bank预充电两种,控制指令均为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0010
			CMD_AUTO		= 4'b0001,	// 自动刷新命令,分为自动刷新命令和自刷新,控制指令均为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0001
			CMD_MODE		= 4'b0000;	// 此命令只有所有L-Bank均处于空闲状态时才可被写入,否则配置出错,控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0000

reg		rst_n_reg;			// 对sys_rst_n寄存一拍
reg		[3:0] state;		// 状态机
reg		[14:0] cnt_wait;	// 延迟计数器
reg		cnt_wait_rst;		// 延迟计数器复位信号
reg		pre_flag;			// 预充电状态标志信号
reg		auto_flag;			// 自动刷新状态标志信号
reg		mode_flag;			// 模式寄存器状态标志信号
reg		end_flag;			// 结束状态标志信号
reg		[3:0] cnt_auto;		// 自动刷新次数计数器

// rst_n_reg信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rst_n_reg <= 0;
	else
		rst_n_reg <= sys_rst_n;
end

// state信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		state <= ST_IDLE;
	else case(state)
		ST_IDLE 	:
			if(pre_flag == 1)
				state <= ST_PRE;
			else
				state <= ST_IDLE;
		ST_PRE 		:
			state <= ST_PRE_WAIT;
		ST_PRE_WAIT	:
			if(auto_flag == 1)
				state <= ST_AUTO;
			else
				state <= ST_PRE_WAIT;
		ST_AUTO		:
			state <= ST_AUTO_WAIT;
		ST_AUTO_WAIT:
			if(auto_flag == 1)
				state <= ST_AUTO;
			else if(mode_flag == 1)
				state <= ST_MODE;
			else 
				state <= ST_AUTO_WAIT;
		ST_MODE		:
			state <= ST_MODE_WAIT;
		ST_MODE_WAIT:
			if(end_flag == 1)
				state <= ST_END;
			else
				state <= ST_MODE_WAIT;
		ST_END		:
			state <= ST_END;
		default		:
			state <= ST_IDLE;
	endcase	
end

// cnt_wait信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_wait <= 0;
	else if(cnt_wait_rst == 0)
		cnt_wait <= cnt_wait + 1;
	else if(cnt_wait_rst == 1)
		cnt_wait <= 0;
end

// cnt_wait_rst信号
always@(*) begin
	if(!sys_rst_n)
		cnt_wait_rst <= 1;
	else if(pre_flag == 1 || auto_flag == 1 || mode_flag == 1 || end_flag == 1)
		cnt_wait_rst <= 1;
	else if(state == ST_END)
		cnt_wait_rst <= 1;
	else if(rst_n_reg == 1)
		cnt_wait_rst <= 0;
end

// pre_flag信号
always@(*) begin
	if(!sys_rst_n)
		pre_flag <= 0;
	else if(state == ST_IDLE && cnt_wait == CNT_IDLE)
		pre_flag <= 1;
	else
		pre_flag <= 0;
end

// auto_flag信号
always@(*) begin
	if(!sys_rst_n)
		auto_flag <= 0;
	else if(state == ST_PRE_WAIT && cnt_wait == CNT_PRE_WAIT)
		auto_flag <= 1;
	else if(state == ST_AUTO_WAIT && cnt_wait == CNT_AUTO_WAIT && cnt_auto < 8)
		auto_flag <= 1;
	else
		auto_flag <= 0;
end

// mode_flag信号
always@(*) begin
	if(!sys_rst_n)
		mode_flag <= 0;
	else if(state == ST_AUTO_WAIT && cnt_wait == CNT_AUTO_WAIT && cnt_auto == 8)
		mode_flag <= 1;
	else
		mode_flag <= 0;
end

// end_flag信号
always@(*) begin
	if(!sys_rst_n)
		end_flag <= 0;
	else if(state == ST_MODE_WAIT && cnt_wait == CNT_MODE_WAIT && cnt_auto == CNT_AUTO)
		end_flag <= 1;
	else
		end_flag <= 0;
end

// cnt_auto信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt_auto <= 0;
	else if(state == ST_AUTO)
		cnt_auto <= cnt_auto + 1;
	else 
		cnt_auto <= cnt_auto;
end

// init_cmd[3:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		init_cmd <= CMD_NOP;
	else if(state == ST_PRE)
		init_cmd <= CMD_PRE;
	else if(state == ST_AUTO)
		init_cmd <= CMD_AUTO;
	else if(state == ST_MODE)
		init_cmd <= CMD_MODE;
	else 
		init_cmd <= CMD_NOP;
end

// init_ba[1:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		init_ba <= 2'b11;
	else if(state == ST_MODE)
		init_ba <= 2'b00;
	else
		init_ba <= 2'b11;
end

// init_addr[12:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		init_addr <= 13'h1fff;
	else if(state == ST_MODE)
		init_addr <= {3'b000,1'b0,2'b00,3'b011,1'b0,3'b111};
	else
		init_addr <= 13'h1fff;
end

// init_end信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		init_end <= 0;
	else if(end_flag == 1)
		init_end <= 1;
	else
		init_end <= init_end;
end

endmodule
五.仿真代码
`timescale 1ns/1ns

module tb_sdram_init();

reg		sys_clk;
reg		sys_rst_n;

wire	[3:0] init_cmd;
wire	[1:0] init_ba;
wire	[12:0] init_addr;
wire	init_end;
wire	clk_100m;
wire	clk_100m_shift;
wire	clk_locked;
wire	rst_n;

assign	rst_n = sys_rst_n & clk_locked;

initial begin
	sys_clk		= 0;
	sys_rst_n	<= 0;
	#100
	sys_rst_n	<= 1;
end

always #10 sys_clk = ~sys_clk;

clk_100m clk_100m_inst
(
	.areset (~sys_rst_n	),
	.inclk0 (sys_clk	),
	.c0 	(clk_100m	),
	.c1 	(clk_100m_shift	),
	.locked (clk_locked)
);

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

	.init_cmd	(init_cmd	),
	.init_ba	(init_ba	),
	.init_addr	(init_addr	),
	.init_end	(init_end	)
);

defparam	sdram_model_plus_inst.addr_bits = 13;	// 行地址13位
defparam	sdram_model_plus_inst.data_bits = 16;	// 一次读写数据16位
defparam	sdram_model_plus_inst.col_bits = 9;		// 列地址9位
defparam	sdram_model_plus_inst.mem_sizes = 2*1024*1024;	// L-Bank容量

sdram_model_plus sdram_model_plus_inst
(
	.Dq		(			), 	// 数据输入输出端口
	.Addr	(init_addr	), 	// 地址端口
	.Ba		(init_ba	), 	// Bank地址
	.Clk	(clk_100m_shift	), 	// 带有偏移的时钟信号
	.Cke	(1			), 	// 时钟使能信号
	.Cs_n	(init_cmd[3]),	// 命令第4位
	.Ras_n	(init_cmd[2]),	// 命令第3位
	.Cas_n	(init_cmd[1]),	// 命令第2位
	.We_n	(init_cmd[0]),	// 命令第1位
	.Dqm	(2'b00		),	// 数据掩码
	.Debug	(1			)	// 开启Debug模式
);

endmodule
六.仿真结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值