SDRAM小项目——写模块

news2025/1/26 15:25:58

写模块跟着视频看了一个多星期,一开始始终有点弄不清楚,现在记录一下理解的过程。

阅读文档信息:

首先阅读文档信息,了解SDRAM写过程的状态转换和时序图

SDRAM整体状态流程如图所示:

在SDRAM整体系统中,若要进入写模块,则需要从idle状态首先激活一行(row_acttive),再进入写状态(write),发送precharge命令跳出写状态。

WRITEA 状态不使用,因为当处于 WRITEA 状态时,它会自动的进入到 Precharge 状态。想要继续进行写操作就要先再次激活行,也就是说 WRITEA 比在 WRITE 状态的工作效率要低很多。

SDRAM写模块时序图如图所示:

观察时序图可知,在发送active命令时同时发送A0-A12,BANK信息,其中A0-A12指定了行信息,也就是说在ACT命令(激活状态)下,确定了要写入数据的行(SDRAM的数据是一行一行的写),BANK命令确定了要写入数据的SDRAM的块。也就是说在ACT命令下已经确定了bank和row;经过tRCD(至少20ns)时间后,再能发送write命令,发送write命令的同时也发送col命令(col命令也通过A0-A9发送,此时A10为低电平),也同时发送bank命令和数据。设置突发长度为4,等待4个周期的时间写入数据,才可以进行precharge命令退出写模式。

SDRAM写模块状态如图所示(写模块内部状态转移,与SDRAM整体的状态无关):

dle,w_req状态承接上下部分,接收到来自顶层模块的写触发信号(w_trig)后,由idle状态到w_req,此时w_req状态向顶层模块发送req信号,接收到写使能(w_en )信号后进入到act状态,此时也接收到row地址和bank地址信息。由时序图可知经过20ns后进入write状态。在write状态下,当数据写完或者刷新时间到的时候或者指定的行数据写完的时候,需要跳出write状态,此时进入pre状态。在pre状态下,数据写完进入idle状态;当刷新请求来的时候要进行刷新,在顶层模块的设计下刷新结束后会进入仲裁模块,此时需要重新发送写请求,接收到写使能;在指定的一行写完后需要重新激活,此时不需要外部的写使能信号再传输进来。

写模块代码如下:

其中burst_cnt用来计数突发长度,write->pre状态,break_cnt用来计数从pre->act状态(至少20ns)

module sdram_wirte(
		input					sclk		,
		input					srst		,
		//communicate with top
		input					w_en		,
		output	wire			w_req		,
		output	reg				flag_w_end	,
		//
		input					ref_req		,
		input					w_trig		,
		//write interface
		output	reg	[3:0]		w_cmd		,
		output	reg	[11:0]		w_addr		,
		output	wire	[1:0]		bank_addr,
		output  reg		[15:0]	w_data
);

//==========================================================
//=======	define parameter and internal signal	========
//==========================================================
//define state
localparam		s_idle  =  5'b00001;
localparam		s_req	=	5'b00010;
localparam		s_act	=	5'b00100;
localparam		s_wr	= 	5'b01000;
localparam		s_pre	= 	5'b10000;

//
localparam		cmd_nop = 5'b0111;
localparam		cmd_pre = 5'b0010;
localparam		cmd_ref = 5'b0001;
localparam		cmd_act = 5'b0011;
localparam		cmd_wr = 5'b0100;


reg				flag_wr;
reg[4:0]		state;

reg 						flag_act_end;
reg 						flag_pre_end;
reg							sd_row_end;
reg[1:0]					burst_cnt;
reg[1:0]					burst_cnt_t;
reg							wr_data_end;

reg[3:0]					act_cnt;
reg[3:0]					break_cnt;
reg[6:0]					col_cnt;

reg[11:0]					row_addr;
wire[8:0]					col_addr;


//==========================================================
//====================	main	code	====================
//==========================================================
//flag_wr
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			flag_wr <= 1'b0;
		else if(w_trig == 1'b1  &&flag_wr == 1'b0)
			flag_wr <= 1'b1;
		else if(wr_data_end == 1'b1)
			flag_wr <= 1'b0;
end

// w_cmd
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			w_cmd <= cmd_nop;
		else case(state)
			s_act:
				if(act_cnt == 'd0)
					w_cmd <= cmd_act;
				else
					w_cmd <= cmd_nop;
			s_wr:
				if(burst_cnt == 'd0)
					w_cmd <= cmd_wr;
				else
					w_cmd <= cmd_nop;
			s_pre:
				if(break_cnt == 'd0)
					w_cmd <= cmd_pre;
				else
					w_cmd <= cmd_nop;
			default:w_cmd <= cmd_nop;
		endcase
end

//burst_cnt
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			burst_cnt  <=  'd0;
		else if(state == s_wr)
			burst_cnt <= burst_cnt + 1'b1;
		else
			burst_cnt <= 'd0;
end

//burst_cnt
always@(posedge sclk )begin
		burst_cnt_t <= burst_cnt;
end


//state
always@(posedge sclk  or negedge srst)begin
		if(srst == 1'b0)
			state  <= s_idle;
		else case(state)
			s_idle:
				if(w_trig == 1'b1)
					state  <=  s_req;
				else
					state  <=  s_idle;
			s_req:
				if(w_en  ==  1'b1)
					state  <=  s_act ;
				else
					state  <=  s_req;
			s_act:
				if(flag_act_end  ==  1'b1)
					state  <=  s_wr;
				else
					state  <=  s_act;
			s_wr:
				if(wr_data_end  ==  1'b1)
					state  <=  s_pre;
				else if(ref_req == 1'b1  && burst_cnt_t == 'd3 &&flag_wr  == 1'b1)
					state  <=  s_pre;
				else if(sd_row_end == 1'b1  &&  flag_wr == 1'b1)
					state  <=  s_pre;
			s_pre:
				if(ref_req == 1'b1  && flag_wr  ==  1'b1)
					state  <=  s_req;
				else if(flag_pre_end  == 1'b1  &&flag_wr == 1'b1)
					state  <=  s_act;
				else if(wr_data_end == 1'b1)
					state  <= s_idle;
		default:state  <=  s_idle;
		endcase
end

//flag_act_end
always@(posedge sclk or negedge srst)begin
		if(srst  ==  1'b0)
			flag_act_end  <=  1'b0;
		else if (act_cnt  ==  'd3)				//  3?
			flag_act_end  <=  1'b1;
		else
			flag_act_end  <=  1'b0;
end

//flag_pre_end
always@(posedge sclk or negedge srst)begin
		if(srst  ==  1'b0)
			flag_pre_end  <=  1'b0;
		else if (break_cnt  ==  'd3)
			flag_pre_end  <=  1'b1;
		else
			flag_pre_end  <=  1'b0;
end

//break_cnt
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			break_cnt  <=  'd0;
		else if(state  ==  s_pre)
			break_cnt <= break_cnt +1'b1;
		else
			break_cnt <= 'd0;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			wr_data_end <=  'd0;
		else if(row_addr == 'd1 && col_addr == 'd511)\
			wr_data_end  <= 1'b1;
		else
			wr_data_end <= 1'b0;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
				col_cnt <= 'd0;
		else if(col_addr  ==  'd511)
				col_cnt <= 'd0;
		else if(burst_cnt_t  == 'd3)
				col_cnt <= col_cnt + 1'b1;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
				row_addr <= 'd0;
		else if(sd_row_end == 1'b1)
				row_addr <= row_addr +'b1;
end

//w_addr
always@(*)begin
		case(state)
			s_act:
				if(act_cnt == 'd0)
					w_addr <= row_addr;
			s_wr:
				w_addr <= {3'b000,col_addr};
			s_pre:
				if(break_cnt == 'd0)
					w_addr <= {12'b0100_0000_0000};
		endcase
end

always@(posedge sclk or negedge srst )begin
		if(srst == 1'b0)
			sd_row_end <= 1'b0;
		else if(col_addr == 'd510)
			sd_row_end <= 1'b1;
		else	
			sd_row_end <= 1'b0;
end

always@(posedge sclk or negedge srst )begin
		if(srst == 1'b0)
			flag_w_end <= 1'b0;
		else if((state == s_pre && ref_req == 1'b1)
				|| (state == s_pre && wr_data_end == 1'b1))
			flag_w_end <= 1'b1;
		else	
			flag_w_end <= 1'b0;
end

assign bank_addr = 2'b00;
assign col_addr = {col_cnt,burst_cnt_t};
assign w_req = state[1];


//产生测试数据
always@(*)begin
	case(burst_cnt_t):
		0: w_data <= 'd3;
		1: w_data <= 'd5;
		2: w_data <= 'd7;
		3: w_data <= 'd9;
	endcase
end


endmodule

顶层模块代码:

在测试中添加了一个w_trig信号

疑问:实际上w_trig信号如何产生?fpga input?按下按键?

module	sdram_top(
		input			sclk,
		input			srst,
		//sdram		interface
		output	wire	sdram_clk,
		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_bank,
		output	reg		[11:0]	sdram_addr,
		output	wire	[1:0]	sdram_dqm,
		inout			[15:0]	sdram_dq,
		//
		input 			w_trig
);

//==========================================================
//=======	define parameter and internal signal	========
//==========================================================

//define state machine
reg		[4:0]	state;
localparam		idle		=		5'b00001;
localparam		arbit		= 		5'b00010;
localparam		aref		= 		5'b00100;
localparam		write		=		5'b01000;
localparam		read		=       5'b10000;

//init module
reg[3:0]		sd_cmd;
wire 			flag_init_end;
wire	[3:0]	init_cmd;
wire	[11:0]	init_addr;

//refresh module
reg				ref_en;
wire			ref_req;
wire			flag_ref_end;
wire 	[3:0]	cmd_reg;
wire	[11:0]	ref_addr;

//write module
 reg				w_en	;	
 wire               w_req	;	
 wire               flag_w_end;
 wire[3:0]           w_cmd		;
 wire[11:0]           w_addr		;
 wire[1:0]           w_bank_addr;
 wire[15:0]			w_data;

//==========================================================
//====================	main	code	====================
//==========================================================
always@(posedge sclk or negedge srst) begin
	if(srst 	==		1'b0)
		state	<=		idle;
	else
	case(state)
		idle:
				if(flag_init_end	==	1'b1)
					state	<=	arbit;
				else
					state	<=	idle;
		arbit:
				if(ref_en	==		1'b1)
					state	<=	aref;
				else if(w_en == 1'b1)
					state <= write;
				else
					state	<=	arbit;
		aref:
				if(flag_ref_end	==	1'b1)
					state	<=	arbit;
				else
					state	<=	aref;
		write:
				if(flag_w_end == 1'b1)
					state <= arbit;
				else
					state <= write;
		default:	
					state	<=	idle;
	endcase
end

always@(*)
begin
	case(state)
		idle:
		begin
			sd_cmd <= init_cmd;
			sdram_addr <= init_addr;
		end
		aref:
		begin
			sd_cmd <= cmd_reg;
			sdram_addr <= ref_addr;
		end
		write:
		begin
			sd_cmd <= w_cmd;
			sdram_addr <= w_addr;
		end
		default:begin
			sd_cmd <= 4'b0111;//nop
			sdram_addr <= 'd0;
		end
	endcase	
end


//ref_en
always@(posedge sclk or  negedge srst)begin
		if(srst == 1'b0)
			ref_en <= 1'b0;
		else if(state == arbit	&& ref_req == 1'b1)
			ref_en <= 1'b1;
		else	
			ref_en <= 1'b0;
end

//w_en
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			w_en <= 1'b0;
		else if(state == arbit && ref_en == 1'b0 && w_req == 1'b1)
			w_en <= 1'b1;
		else
			w_en <= 1'b0;
end




assign	sdram_cke	=	1'b1;
//assign	sdram_addr	=	(state == idle) ?init_addr:ref_addr;
//assign	{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = (state == idle) ?init_cmd:cmd_reg;
assign	{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sd_cmd;
assign 	sdram_dqm	=	2'b00;
assign	sdram_clk	=	~sclk;	//内部时钟上升沿采集命令,命令又是由系统时钟上升沿产生的??(为了保证采样时刻处在数据中间时刻)
assign	sdram_dq = (state == write)?w_data:{16{1'bz}};
assign  sdram_bank = (state == write)?w_bank_addr:2'b00;


sdram_init	sdram_init_inst(
	.sclk				(sclk)	,
	.srst				(srst)	,
	.cmd_reg			(init_cmd)	,	
	.sdram_addr			(init_addr)	,	
	.flag_init_end	    		(flag_init_end)
);	


sdram_aref		sdram_aref(
		.sclk						(sclk),
		.srst						(srst),
		//commmunicate with arbit
		.ref_en							(ref_en),
		.ref_req						(ref_req),
		.flag_ref_end					(flag_ref_end),
		//others                       
		.flag_init_end					(flag_init_end),
		.cmd_reg						(cmd_reg),
		.ref_addr						(ref_addr)
);

sdram_wirte		sdram_wirte_inst(
		.sclk							(sclk),
		.srst							(srst),
		//communicate with top          
		.w_en							(w_en		),
		.w_req							(w_req		),
		.flag_w_end						(flag_w_end),
		//                              
		.ref_req						(ref_req),
		.w_trig							(w_trig	),
		//write interface               
		.w_cmd							(w_cmd		),
		.w_addr							(w_addr		),
		.bank_addr                      (w_bank_addr  ),
		.w_data							(w_data)
);


endmodule

测试代码:

`timescale	1ns/1ns

module	tb_sdram_top;

	reg			sclk;
	reg			srst;
//----------------------------------------
	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_bank;
	wire	[11:0]	sdram_addr;
	wire	[1:0]	sdram_dqm;
    wire	[15:0]	sdram_dq;
	
	reg				w_trig;
//----------------------------------------

initial begin	
	w_trig <= 0;
	#205000
	w_trig <= 'b1;
	#20
	w_trig <= 'b0;
end

initial begin
			sclk <= 1;
			srst <= 0;
			#100
			srst <=1;
end

always	#10		sclk <= ~sclk;

defparam sdram_model_plus_inst.addr_bits =    12;
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;//1 M 


sdram_top		sdram_top_inst(
			.sclk							(sclk		),
			.srst							(srst		),
			.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_bank						(sdram_bank	),
			.sdram_addr						(sdram_addr	),
			.sdram_dqm						(sdram_dqm	),
			.sdram_dq						(sdram_dq	),
			.w_trig							(w_trig)
);


sdram_model_plus sdram_model_plus_inst(
 .Dq				(sdram_dq)	,
 .Addr				(sdram_addr), 
 .Ba				(sdram_bank), 
 .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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1383485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谈⼀谈你对TCPIP四层模型,OSI七层模型的理解

TCP/IP四层模型 对比 OSI七层模型 OSI七层模型 为了增强通⽤性和兼容性&#xff0c;计算机⽹络都被设计成层次机构&#xff0c;每⼀层都遵守⼀定的规则。因此有了OSI这样⼀个抽象的⽹络通信参考模型&#xff0c;按照这个标准使计算机⽹络系统可以互相连接 物理层 通过⽹线、光…

南京观海微电子----时序分析基本概念(一)——建立时间

1. 概念的理解 以上升沿锁存为例&#xff0c;建立时间&#xff08;Tsu&#xff09;是指在时钟翻转之前输入的数据D必须保持稳定的时间。如下图所示&#xff0c;一个数据要在上升沿被锁存&#xff0c;那么这个数据就要在时钟上升沿的建立时间内保持稳定。 建立时间是对触发器而…

Akira勒索软件团伙及其策略的全面解析

Sophos MDR威胁情报团队曾于2023年5月发表过一篇博文&#xff0c;称Akira勒索软件“将1988年的时光带回”。起因是Akira会将受害者网站篡改为具有复古美学的页面&#xff0c;让人想起20世纪80年代的绿色屏幕控制台。而且&#xff0c;Akira的名字也可能取自1988年流行的同名动画…

HCIA的访问控制列表ACL

ACL&#xff1a;访问控制列表 -----控制列表&#xff08;策略列表&#xff09; 功能&#xff1a; 1&#xff1a;定义感兴趣的路由&#xff08;控制层面&#xff09; 可以定义感兴趣的路由&#xff0c;可以把感兴趣的路由抓取出来&#xff0c;给它做过滤&#xff0c;也可以改变…

polar CTF CB链

一、题目 二、解答 1、通过jar包&#xff0c;可以看到/user路由下有反序列化操作 看到存在commons-beanutils依赖且版本为1.9.2&#xff0c;可利用CB链Getshell。 使用ysoserial项目中的CommonsBeanutils1链写一个POC&#xff0c;注意确保ysoserial项目中的pom.xml中的comm…

寡年是否适合结婚?寡妇年结婚有什么禁忌吗?让程序来告诉你有多少人是寡妇年结婚的。

什么是寡年&#xff1f; 百度百科 原文&#xff1a;寡年-百度百科 指整年没有“立春”的日子就是“盲年”&#xff0c;俗称寡年。又名滑头年 社会上流传的“寡妇年”&#xff0c;是指整个农历年都没有立春的年份。以农历2005年的鸡年为例&#xff0c;立春在公历2月4日&…

Rust-内存安全

堆和栈 一个进程在执行的时候&#xff0c;它所占用的内存的虚拟地址空间一般被分割成好几个区域&#xff0c;我们称为“段”(Segment)。常见的几个段如下。 代码段。编译后的机器码存在的区域。一般这个段是只读的。bss段。存放未初始化的全局变量和静态变量的区域。数据段。…

java: 5-6 break

文章目录 1. break1.1 介绍1.2 语法和流程图1.3 入门练习1.4 细节说明1.5 练习 【老韩视频p137-】 1. break 看个需求&#xff1a;随机生成 1-100 的一个数&#xff0c;直到生成了 97 这个数&#xff0c;看看你一共用了几次? 【思路分析:循环&#xff0c;但是循环的次数不知道…

NLP论文阅读记录 - 2022 W0S | 基于Longformer和Transformer的提取摘要层次表示模型

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 A Hierarchical Representation Model Based on Longformer and …

【模型评估 04】A/B测试的陷阱

互联网公司中&#xff0c;A/B测试是验证新模块、新功能、新产品是否有效&#xff1b;新算法、新模型的效果是否有提升&#xff1b;新设计是否受到用户欢迎&#xff1b;新更改是否影响用户体验的主要测试方法。在机器学习领域中&#xff0c;A/B测试是验证模型最终效果的主要手段…

知乎x-zse-96算法分析

声明 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 如有侵权,请联系我进行删除。 这里只是我分析过程,以及一些重要点的记录,没有…

yolov5的完整部署(适合新人和懒人,一键安装)

第一步&#xff1a;安装Anaconda 下载并安装后&#xff0c;配置一下镜像 在这里面&#xff0c;看情况输入镜像源&#xff0c;这里我建议大家搞阿里云镜像源。 # 添加清华源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda conf…

零知识证明的最新发展和应用

PrimiHub一款由密码学专家团队打造的开源隐私计算平台&#xff0c;专注于分享数据安全、密码学、联邦学习、同态加密等隐私计算领域的技术和内容。 当企业收集大量客户数据去审查、改进产品和服务以及将数据资产货币化时&#xff0c;他们容易受到网络攻击威胁&#xff0c;造成数…

从Demo理解Thrift Thrift和Dubbo的区别

文章目录 安装demo尝试Thrift协议栈Thrift 与 Dubbo 的区别 字节里的RPC框架都是用的Thrift&#xff0c;我猜这主要原因有2: Thrift是Facebook开源的项目&#xff0c;平台中立Thrift支持跨语言调用&#xff0c;这非常适合字节Java、Go语言都存在的环境&#xff0c;语言中立 但…

手把手Docker部署Gitblit服务器

1拉取镜像 docker pull jacekkow/gitblit:v1.9.1 2.启动 docker run -d --name gitblit --restart always -p 10006:8080 -p 18443:8443 -p 19418:9418 -p 29418:29418 -v /data/gitblit/data:/opt/gitblit-data jacekkow/gitblit:v1.9.1 3.查看 默认账户/密码:admin/adm…

运算放大器相关知识总结(1)

1、 前言 最近做了一个小项目&#xff0c;这个项目是研发一款阻抗测量仪。这个阻抗测量仪可以测量人体在不同频率下的生物电阻抗&#xff0c;该设备的核心是模拟电路&#xff0c;技术难点是减小模拟电路噪声。该项目前前忙了2个多月&#xff0c;借着研发这个项目的机会把自己掌…

Jenkins-用户管理

用户管理 1 安装插件 2 选择安全策略为刚刚安装的插件 3 这个是安装插件以后会有的选项 4 增加一个角色 5 根据需要赋值角色的权限&#xff0c;并分配给用户

Protecting Intellectual Property of Deep NeuralNetworks with Watermarking

保护深度神经网络的知识产权与数字水印技术 ABSTRACT 深度学习是当今人工智能服务的关键组成部分&#xff0c;在视觉分析、语音识别、自然语言处理等多个任务方面表现出色&#xff0c;为人类提供了接近人类水平的能力。构建一个生产级别的深度学习模型是一项非常复杂的任务&a…

每日算法打卡:摘花生 day 14

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码 原题链接 1015. 摘花生 题目难度&#xff1a;简单 题目来源&#xff1a;《信息学奥赛一本通》 题目描述 Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来…

机器学习_捕捉函数的变化趋势(凸函数)

文章目录 连续性是求导的前提条件通过求导发现 y 如何随 x 而变凸函数有一个全局最低点 机器学习所关心的问题之一捕捉函数的变化趋势&#xff0c;也就是标签&#xff08;y&#xff09;是如何随着特征字段&#xff08;x&#xff09;而变化的&#xff0c;这个变化趋势是通过求导…