fpga控制sdram存储器的读写5:sdram读数据

一.理论知识

1.读命令(Read),控制命令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0101,用来实现对已激活的特定 L-Bank 的特定行的数据突发读取操作, BA[1:0]指定需要读取数据的特定 LBank,地址总线 A0-A9 指定需要读取存储单元的起始列地址, A10 的电平变化控制突发读操作完成后是否立即执行自动预充电操作,若 A10 为高电平,突发读操作完成后,立即执行自动预充电操作,关闭当前行;若 A10 为低电平,突发读操作完成后,当前行依然处于激活状态,以便对当前行执行新的读/写操作,想要关闭当前激活行,需写入预充电指令。读数据有多种模式,这里我们使用不带自动预充电的页突发读模式,激活一次以后,可以连续读取一整页的数据,不过也可以读取小于一整页的数据,当不需要读数据的时候,只需要发送突发中止指令即可。

2.首先是激活特定 L-Bank 的特定行,该操作下13位地址线被用来发送行地址信号,Ba[1:0]表示要激活的Bank。

3.经过tRCD的延迟之后,需要向sdram芯片发送读数据命令。此时13位地址线用来发送列地址信号,但是13位只用到了低9位,高4位全部赋值为0。

4.读数据命令后,要等待2~3个周期的潜伏期,才能接受到sdram输出的数据。这里的潜伏期到底是2个周期还是3个周期,取决于前面初始化阶段的设置。

5.如果要读取N个字节的数据,发送完读数据命令之后的N个周期后发送突发中止指令。突发中止指令不像读数据那样,不需要等待2~3个周期的潜伏期。

6.读数据结束之后,要给芯片发送预充电命令,关闭激活的L-Bank和行。

二.芯片时序分析

在这里插入图片描述
在这里插入图片描述

三.设计verilog时序图

请添加图片描述

四.verilog代码
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,
	input	wire	[15:0] rd_data,
	input	wire	[9:0] rd_burst_len,
	
	output	reg		rd_ack,
	output	reg		rd_end,
	output	reg		[3:0] rd_sdram_cmd,
	output	reg		[1:0] rd_sdram_ba,
	output	reg		[12:0] rd_sdram_addr,
	output	wire	[15:0] rd_sdram_data
);

// 状态
parameter	ST_IDLE		= 0,
			ST_ACTIVE 	= 1,
			ST_ACT_WAIT	= 2,
			ST_READ 	= 3,
			ST_CAS_WAIT = 4,
			ST_RD_DATA 	= 5,
			ST_PRE	 	= 6,
			ST_PRE_WAIT	= 7,
			ST_RD_END	= 8;

// 延迟计数器的值
parameter	CNT_ACT_WAIT	= 2,
			CNT_CAS_WAIT	= 3,
			CNT_PRE_WAIT	= 2;

// 命令
parameter	CMD_NOP		= 4'b0111,
			CMD_ACTIVE	= 4'b0011,
			CMD_PRE		= 4'b0010,
			CMD_READ	= 4'b0101,
			CMD_BURST	= 4'b0110;

reg		[3:0] state;
reg		[9:0] cnt_wait;
reg		cnt_wait_rst;
reg		read_flag;
reg		rd_data_flag;
reg		pre_flag;
reg		end_flag;
reg		[15:0] rd_data_reg;

// state信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		state <= ST_IDLE;
	else 
		case(state)
			ST_IDLE		:
				if(init_end == 1 && rd_en == 1)
					state <= ST_ACTIVE;
				else 
					state <= ST_IDLE;
			ST_ACTIVE 	:
				state <= ST_ACT_WAIT;
			ST_ACT_WAIT	:
				if(read_flag == 1)
					state <= ST_READ;
				else
					state <= ST_ACT_WAIT;
			ST_READ		:
				state <= ST_CAS_WAIT;
			ST_CAS_WAIT	:
				if(rd_data_flag == 1)
					state <= ST_RD_DATA;
				else
					state <= ST_CAS_WAIT;
			ST_RD_DATA	:
				if(pre_flag == 1)
					state <= ST_PRE;
				else
					state <= ST_RD_DATA;
			ST_PRE		:
				state <= ST_PRE_WAIT;
			ST_PRE_WAIT	:
				if(end_flag == 1)
					state <= ST_RD_END;
				else 
					state <= ST_PRE_WAIT;
			ST_RD_END	:
				state <= ST_IDLE;
			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 == 1)
		cnt_wait <= 0;
	else if(cnt_wait_rst == 0)
		cnt_wait <= cnt_wait + 1;
end

// cnt_wait_rst信号
always@(*) begin
	if(!sys_rst_n)
		cnt_wait_rst <= 1;
	else if(state == ST_IDLE)
		cnt_wait_rst <= 1;
	else if(read_flag == 1 || rd_data_flag == 1 || pre_flag == 1 || end_flag == 1 || state == ST_RD_END)
		cnt_wait_rst <= 1;
	else
		cnt_wait_rst <= 0;
end

// read_flag信号
always@(*) begin
	if(!sys_rst_n)
		read_flag <= 0;
	else if(state == ST_ACT_WAIT && cnt_wait == CNT_ACT_WAIT)
		read_flag <= 1;
	else 
		read_flag <= 0;
end

// rd_data_flag信号
always@(*) begin
	if(!sys_rst_n)
		rd_data_flag <= 0;
	else if(state == ST_CAS_WAIT && cnt_wait == CNT_CAS_WAIT - 1)
		rd_data_flag <= 1;
	else
		rd_data_flag <= 0;
end

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

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

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

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

// rd_data_reg信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rd_data_reg <= 16'b0;
	else 
		rd_data_reg <= rd_data;
end

// rd_sdram_data信号
assign	rd_sdram_data = (rd_ack == 1) ? rd_data_reg : 16'b0;

// rd_sdram_cmd信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rd_sdram_cmd <= CMD_NOP;
	else if(state == ST_ACTIVE)
		rd_sdram_cmd <= CMD_ACTIVE;
	else if(state == ST_READ)
		rd_sdram_cmd <= CMD_READ;
	else if(state == ST_RD_DATA && cnt_wait == rd_burst_len - CNT_CAS_WAIT)
		rd_sdram_cmd <= CMD_BURST;
	else if(state == ST_PRE)
		rd_sdram_cmd <= CMD_PRE;
	else
		rd_sdram_cmd <= CMD_NOP;
end

// rd_sdram_ba信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rd_sdram_ba <= 2'b11;
	else if(state == ST_ACTIVE || state == ST_READ || state == ST_PRE)
		rd_sdram_ba <= rd_addr[23:22];
	else 
		rd_sdram_ba <= 2'b11;
end

// rd_sdram_addr信号
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rd_sdram_addr <= 13'h1fff;
	else if(state == ST_ACTIVE)
		rd_sdram_addr <= rd_addr[21:9];
	else if(state == ST_READ)
		rd_sdram_addr <= {4'b0000,rd_addr[8:0]};
	else if(state == ST_PRE)
		rd_sdram_addr <= 13'h0400;
	else 
		rd_sdram_addr <= 13'h1fff;
end

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

module tb_sdram_read();

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;

reg		wr_en;
reg		[15:0] wr_data;
wire	wr_ack;
wire	[3:0] wr_sdram_cmd;
wire	[1:0] wr_sdram_ba;
wire	[12:0] wr_sdram_addr;
wire	wr_end;
wire	wr_sdram_en;
wire	[15:0] wr_sdram_data;

reg		rd_en;
wire	[15:0] read_sdram_dq;
wire	rd_ack;
wire	rd_end;
wire	[3:0] rd_sdram_cmd;
wire	[1:0] rd_sdram_ba;
wire	[12:0] rd_sdram_addr;
wire	[15:0] rd_sdram_data;

wire	[15:0] sdram_dq;
wire	[3:0] sdram_cmd;
wire	[1:0] sdram_ba;
wire	[12:0] sdram_addr;
wire	[3:0] sdram_rw_cmd;
wire	[1:0] sdram_rw_ba;
wire	[12:0] sdram_rw_addr;

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;

// wr_en信号
always@(posedge clk_100m or negedge rst_n) begin
    if(rst_n == 1'b0)
        wr_en <= 1'b1;
    else    if(wr_end == 1'b1)
        wr_en <= 1'b0;
    else
        wr_en <= wr_en;
end

// rd_en信号
always@(posedge clk_100m or negedge rst_n) begin
    if(rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if(rd_end == 1'b1)
        rd_en   <=  1'b0;
    else    if(wr_en == 1'b0)
        rd_en   <=  1'b1;
    else
        rd_en   <=  rd_en;
end

// wr_data信号
always@(posedge clk_100m or negedge rst_n) begin
    if(!rst_n)
        wr_data <= 0;
    else if(wr_ack == 1)
        wr_data <= wr_data + 1;
    else
		wr_data <= 0;
end

// sdram_dq信号
assign	sdram_dq = (wr_sdram_en == 1) ? wr_sdram_data : 16'bz;

// read_sdram_dq信号
assign	read_sdram_dq = (rd_en == 1) ? sdram_dq : 16'bz;

// 时钟信号
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	)
);

// 数据写操作
sdram_write sdram_write_inst
(
	.sys_clk		(clk_100m		),
	.sys_rst_n		(rst_n			),
	.init_end		(init_end		),
	.wr_en			(wr_en			),
	.wr_addr		(24'h000000		),
	.wr_data		(wr_data		),
	.wr_burst_len	(10				),

	.wr_ack			(wr_ack			),
	.wr_sdram_cmd	(wr_sdram_cmd	),
	.wr_sdram_ba	(wr_sdram_ba	),
	.wr_sdram_addr	(wr_sdram_addr	),
	.wr_end			(wr_end			),
	.wr_sdram_en	(wr_sdram_en	),
	.wr_sdram_data	(wr_sdram_data	)
);

// 数据读操作
sdram_read sdram_read_inst
(
	.sys_clk		(clk_100m		),
	.sys_rst_n		(rst_n			),
	.init_end		(init_end		),
	.rd_en			(rd_en			),
	.rd_addr		(24'h000000		),
	.rd_data		(read_sdram_dq	),
	.rd_burst_len	(10				),

	.rd_ack			(rd_ack			),
	.rd_end			(rd_end			),
	.rd_sdram_cmd	(rd_sdram_cmd	),
	.rd_sdram_ba	(rd_sdram_ba	),
	.rd_sdram_addr	(rd_sdram_addr	),
	.rd_sdram_data	(rd_sdram_data	)
);

assign sdram_addr = (init_end == 1) ? sdram_rw_addr : init_addr;
assign sdram_ba = (init_end == 1) ? sdram_rw_ba : init_ba;
assign sdram_cmd = (init_end == 1) ? sdram_rw_cmd : init_cmd;

assign sdram_rw_addr = (wr_en == 1) ? wr_sdram_addr : rd_sdram_addr;
assign sdram_rw_ba = (wr_en == 1) ? wr_sdram_ba : rd_sdram_ba;
assign sdram_rw_cmd = (wr_en == 1) ? wr_sdram_cmd : rd_sdram_cmd;

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仿真模型
sdram_model_plus sdram_model_plus_inst
(
	.Dq		(sdram_dq	), 	// 数据输入输出端口
	.Addr	(sdram_addr	), 	// 地址端口
	.Ba		(sdram_ba	), 	// Bank地址
	.Clk	(clk_100m_shift	), 	// 带有偏移的时钟信号
	.Cke	(1			), 	// 时钟使能信号
	.Cs_n	(sdram_cmd[3]),	// 命令第4位
	.Ras_n	(sdram_cmd[2]),	// 命令第3位
	.Cas_n	(sdram_cmd[1]),	// 命令第2位
	.We_n	(sdram_cmd[0]),	// 命令第1位
	.Dqm	(2'b00		),	// 数据掩码
	.Debug	(1			)	// 开启Debug模式
);

endmodule
六.仿真结果

在这里插入图片描述

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值