SRAM控制原理与读写实例

news2024/11/25 20:43:56

本文对SRAM进行介绍,并对其内部的存储器矩阵、地址译码器、列I/O及I/O数据电路、控制电路、SRAM的读写流程进行简要介绍,并给出SRAM IS62LV256-45U读写实例。

文章目录

  • 存储容量的计算
  • SRAM控制原理
    • SRAM信号线
    • 存储器矩阵
    • 地址译码器、列I/O及I/O数据电路
    • 控制电路
    • SRAM的读写时序
  • SRAM读写实例
    • 顶层模块
    • SRAM控制器模块
    • 其余模块
    • 仿真模块
    • 实验现象演示

存储容量的计算

根据下面"SRAM控制原理"小节的介绍,我们可以知道存储器存储容量的计算由地址线和数据线数可以得到:

例如:一个存储器有16根地址线,8根数据线,求此存储器存储容量?

按位求取 219 x 16位 =29 x 210 x 16位 =512K x 16位

按字节求取 219 x 16位 /8 = 1024K x B = 1024KB

SRAM控制原理

SRAM信号线

任何一颗 SRAM 芯片的 datasheet,会发现它们的时序操作大同小异。SRAM 内部的结构如图 所示,要访问实际的 Momory 区域,FPGA 必须送地址和控制信号,SRAM 内部有与此对应的地址译码(decoder)和控制处理电路(control circuit)。这样,数据总线上的数据就可以相应的读或写了。

以下是一种SRAM芯片的内部结构框图,其左侧引出的是SRAM芯片的控制引脚。
在这里插入图片描述

其中的引脚说明如下表:

信号线类型说明
A0-A18I地址输入
I/O0-I/O7I/O数据输入输出信号,低字节
I/O8-I/O15I/O数据输入输出信号,高字节
C S 1 ‾ \overline{CS1} CS1 C S 2 ‾ \overline{CS2} CS2I片选信号, C S 2 ‾ \overline{CS2} CS2高电平有效, C S 1 ‾ \overline{CS1} CS1低电平有效,部分芯片只有其中一个引脚
O E ‾ \overline{OE} OEI输出使能信号,低电平有效
W E ‾ \overline{WE} WEI写入使能,低电平有效
U B ‾ \overline{UB} UBI数据掩码信号Upper Byte,高位字节允许访问,低电平有效
L B ‾ \overline{LB} LBI数据掩码信号Lower Byte,低位字节允许访问,低电平有效

SRAM的控制比较简单,只要控制信号线使能了访问,从地址线输入要访问的地址,即可从I/O数据线写入或读出数据。

存储器矩阵

以下框图表示的是存储器矩阵,SRAM内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格, 这是SRAM芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储矩阵。
在这里插入图片描述

地址译码器、列I/O及I/O数据电路

地址译码器把N根地址线转换成2N根信号线,每根信号线对应一行或一列存储单元,通过地址线找到具体的存储单元,实现寻址。 如果存储阵列比较大,地址线会分成行和列地址,或者行、列分时复用同一地址总线,访问数据寻址时先用地址线传输行地址再传输列地址。 对于较小的SRAM,没有列地址线,以SRAM芯片的内部结构框图中的SRAM为例,它的数据宽度为16位,即一个行地址对应2字节空间,框图中左侧的A0-A18是行址信号, 18根地址线一共可以表示218=28x1024=512K行存储单元, 所以它一共能访问512Kx16bits大小的空间。访问时, 使用 U B ‾ \overline{UB} UB L B ‾ \overline{LB} LB线控制数据宽度,例如,当要访问宽度为16位的数据时,使用行地址线指出地址,然后把 U B ‾ \overline{UB} UB L B ‾ \overline{LB} LB线都设置为低电平, 那么I/O0-I/O15线都有效,它们一起输出该地址的16位数据(或者接收16位数据到该地址);当要访问宽度为8位的数据时,使用行地址线指出地址, 然后把 U B ‾ \overline{UB} UB L B ‾ \overline{LB} LB其中一个设置为低电平,I/O会对应输出该地址的高8位和低8位数据,因此它们被称为数据掩码信号。

控制电路

控制电路主要包含了片选、读写使能以及上面提到的宽度控制信号 U B ‾ \overline{UB} UB L B ‾ \overline{LB} LB。利用 C S 1 ‾ \overline{CS1} CS1 C S 2 ‾ \overline{CS2} CS2片选信号,可以把多个SRAM芯片组成一个大容量的内存条。 O E ‾ \overline{OE} OE W E ‾ \overline{WE} WE可以控制读写使能,防止误操作。

SRAM的读写时序

对SRAM进行读写数据时,它各个信号线的时序流程见图SRAM的读时序及图 SRAM的写时序。

SRAM读时序:
在这里插入图片描述

SRAM写时序:
在这里插入图片描述

读写时序的流程很类似,下面我们统一解说:

(1) 主机使用地址信号线发出要访问的存储器目标地址;
(2) 控制片选信号 C S 1 ‾ \overline{CS1} CS1 C S 2 ‾ \overline{CS2} CS2使能存储器芯片;
(3) 若是要进行读操作,则控制读使能信号 O E ‾ \overline{OE} OE表示要读数据,若进行写操作则控制写使能信号 W E ‾ \overline{WE} WE表示要写数据;
(4) 使用掩码信号 U B ‾ \overline{UB} UB L B ‾ \overline{LB} LB指示要访问目标地址的高、低字节部分;
(5) 若是读取过程,存储器会通过数据线向主机输出目标数据,若是写入过程,主要使用数据线向存储器传输目标数据。

在读写时序中,有几个比较重要的时间参数,以IS62WV51216BLL-55ns型号SRAM的时间参数为例:

时间参数IS62WV51216BLL-55ns时间要求说明
tRC不小于55ns读操作的总时间(地址信号要大于此时间)
tAA最迟不大于55ns从接收到地址信号到给出有效数据的时间(有效数据出现的时间应大于此时间)
tDOE最迟不大于25ns从接收到读使能信号到给出有效数据的时间(有效数据出现的时间应大于此时间)
tWC不小于55ns写操作的总时间(地址信号要大于此时间)
tSA大于0ns从发送地址信号到给出写使能信号的时间
tPWE不小于40ns从接收到写使能信号到数据采样的时间
tSD大于25ns写结束前的数据建立时间
tHD大于0ns写结束后的数据保持时间

SRAM读写实例

实验平台:Xilinx Spartan 6、SRAM芯片型号为IS62LV256-45U。

实现效果:两个按键分别为写数据按键、读数据按键;按下写数据按键后,开始向SRAM中地址依次写数据2、4、6、8······直至写满,写满后led灯亮;按下读按键后依次向SRAM地址读数据并用数码管显示读出的数据。

顶层模块

整体框图如下,实验由以下模块构成:
在这里插入图片描述

PLL模块:产生系统时钟50MHz以及系统复位;
按键消抖模块:对读写按键进行消抖,产生读写标志信号;
按键读写控制:实现写按键产生写请求将SRAM写满,读按键每隔1s产生一次读请求读数据;
SRAM控制器模块:控制SRAM进行读写,为控制SRAM的核心部分;
BCD模块:将读取的数据转换为二进制编码的十进制,如将1234转换为0001_0010_0011_0100;
数码管控制模块:将二十进制进行数码管显示。

//========================================================================
// 	module_name.v	:top.v
// 	Author			:YprgDay
// 	Description		:顶层
//========================================================================
module top(
	input   wire            ext_clk_25m ,   
	input   wire            ext_key_wr	,
	input   wire            ext_key_rd	,
	output  wire  [7:0] 	led  		,
	
	output 	wire			sram_cs_n	,	// SRAM片选信号,低电平有效。
	output 	wire			sram_we_n	,	// SRAM写选通信号,低电平有效。
	output 	wire			sram_oe_n	,	// SRAM输出选通信号,低电平有效。
	output	wire  [14:0] 	sram_addr	,	// SRAM地址总线。			
	inout		  [7:0] 	sram_data	,	// SRAM数据总线。
	
	output 	wire  [3:0] 	dtube_cs_n	,	//7段数码管位选信号
	output 	wire  [7:0] 	dtube_data	//7段数码管段选信号(包括小数点为8段)

);
	wire     		sys_rst_n   ;   //复位信号,低电平有效
	wire	 		key_flag_wr ;
	wire	 		key_flag_rd ;
	                            
	wire		 	sramwr_req	;	
	wire	[14:0] 	sramwr_addr	;	
	wire	[7:0] 	sramwr_data	;	
	                    
	wire			sramrd_req	;	
	wire	[14:0] 	sramrd_addr	;	
	wire	[7:0] 	sramrd_data	;	
	wire	[7:0] 	sramrd_data_tube;
	
	wire	[3:0]   unit        ;   //个位BCD码
	wire	[3:0]   ten         ;   //十位BCD码
	wire	[3:0]   hun         ;   //百位BCD码
	wire	[3:0]   tho         ;   //千位BCD码
	wire	[3:0]   t_tho       ;   //万位BCD码
	wire	[3:0]   h_hun       ;   //十万位BCD码

	pll_controller u_pll_controller(	
		.CLK_IN1			(ext_clk_25m),      // IN
		// Clock out ports
		.CLK_OUT1			(sys_clk	),     // 50MHz
		.LOCKED				(sys_rst_n	)
	);
	
	key_filter u_key_wr(
		.sys_clk			(sys_clk	), 
		.sys_rst_n 			(sys_rst_n	),
		.key_in 			(ext_key_wr	), 
		.key_flag           (key_flag_wr)
	);   
	key_filter u_key_rd(
		.sys_clk			(sys_clk	), 
		.sys_rst_n 			(sys_rst_n	),
		.key_in 			(ext_key_rd	), 
		.key_flag           (key_flag_rd)
	); 
	
	rd_wr_ctrl u_rd_wr_ctrl(
		.sys_clk 			(sys_clk	),	//时钟信号
		.sys_rst_n			(sys_rst_n	),	//复位信号,低电平有效
		            
		.key_wr				(key_flag_wr),	//写按键
		.led_wr_full_flag	(led[0]		),	//写满后led亮
		.key_rd				(key_flag_rd),	//读按键

		.sramwr_req			(sramwr_req	),	// SRAM写请求信号,高电平有效。
		.sramwr_addr		(sramwr_addr),	// SRAM写入地址寄存器。
		.sramwr_data		(sramwr_data),	// SRAM写入数据寄存器。

		.sramrd_req			(sramrd_req	),	// SRAM读请求信号,高电平有效。
		.sramrd_addr		(sramrd_addr),	// SRAM读出地址寄存器。
		.sramrd_data		(sramrd_data),	// SRAM读出数据寄存器。	
		.sramrd_data_tube	(sramrd_data_tube)//读出数据给数码管
	);
	
	sram_controller u_sram_controller
	(
		.sys_clk			(sys_clk	),	
		.sys_rst_n			(sys_rst_n	),	
	
		.sramwr_req			(sramwr_req	),	
		.sramwr_addr		(sramwr_addr),	
		.sramwr_data		(sramwr_data),	

		.sramrd_req			(sramrd_req	),	
		.sramrd_addr		(sramrd_addr),	
		.sramrd_data		(sramrd_data),	
							
		.sram_cs_n			(sram_cs_n	),	
		.sram_we_n			(sram_we_n	),	
		.sram_oe_n			(sram_oe_n	),	
		.sram_addr			(sram_addr	),	
		.sram_data			(sram_data	)	
	);
	
	bcd_8421 u_bcd_8421(
		.sys_clk     		(sys_clk  	),   //系统时钟,频率50MHz
		.sys_rst_n   		(sys_rst_n	),   //复位信号,低电平有效
		.data        		({12'd0,sramrd_data_tube}),   //输入需要转换的数据
					         			
		.unit        		(unit     	),   //个位BCD码
		.ten         		(ten      	),   //十位BCD码
		.hun         		(hun      	),   //百位BCD码
		.tho         		(tho      	),   //千位BCD码
		.t_tho       		(t_tho    	),   //万位BCD码
		.h_hun       		(h_hun    	)    //十万位BCD码
	);
	
	seg7 u_seg7(
		.clk 				(sys_clk	),	
		.rst_n				(sys_rst_n	),	
		.display_num		({tho,hun,ten,unit}),	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
		.dtube_cs_n			(dtube_cs_n	),	//7段数码管位选信号
		.dtube_data     	(dtube_data	)
    );
    
	assign led[7:1] = 7'b1111111;//关掉其他led灯
	
endmodule

SRAM控制器模块

读写时序要求如下,读写操作总时间大于45ns即可,我们取100ns;读数据有效数据出现的时间最大45ns,我们取80ns处,保证时序需求。
在这里插入图片描述

写数据时输入写请求、写数据、写地址后对SRAM进行写数据;其余时刻sram_data为高阻态。由于数据总线为双向口,使用inout定义。
在这里插入图片描述

读数据时输入读请求、读地址后对SRAM进行读数据;
在这里插入图片描述

//========================================================================
// 	module_name.v	:sram_controller.v
// 	Author			:YprgDay
// 	Description		:SRAM的读写控制器
//========================================================================
module sram_controller
(
	input 				sys_clk,	//时钟信号
	input 				sys_rst_n,	//复位信号,低电平有效
		
	//FPGA内部对SRAM的读写控制信号
	input 				sramwr_req	,	// SRAM写请求信号,高电平有效,用于状态机控制。
	input		[14:0] 	sramwr_addr	,	// SRAM写入地址寄存器。
	input		[7:0] 	sramwr_data	,	// SRAM写入数据寄存器。
		
	input 				sramrd_req	,	// SRAM读请求信号,高电平有效,用于状态机控制。					
	input		[14:0] 	sramrd_addr	,	// SRAM读出地址寄存器。
	output reg	[7:0] 	sramrd_data	,	// SRAM读出数据寄存器。	
	
	//FPGA与SRAM芯片的接口信号
	output reg 			sram_cs_n	,	// SRAM片选信号,低电平有效。
	output reg 			sram_we_n	,	// SRAM写选通信号,低电平有效。
	output reg 			sram_oe_n	,	// SRAM输出选通信号,低电平有效。
	output reg [14:0] 	sram_addr	,	// SRAM地址总线。			
	inout	   [7:0] 	sram_data		// SRAM数据总线。
);

	//状态机控制SRAM的状态。
	parameter	IDLE	= 3'd0	;
	parameter	WRT0	= 3'd1	;
	parameter	WRT1	= 3'd2	;
	parameter	REA0	= 3'd3	;
	parameter	REA1	= 3'd4	;
	//=========================< Variable >==============================
	reg	[2:0] state,next_state;	//状态机跳转
	reg [2:0] cnt;				//用于WRT0、REA0读写状态下的计数
	reg 	  wr_data_falg;		//写数据时一直拉高
	//=========================< Block >=================================
	always @ (posedge sys_clk or negedge sys_rst_n)	begin//时序逻辑控制状态变迁。
	if(sys_rst_n == 1'b0) 
		cnt <= 3'd0;
	else if(state == IDLE)
		cnt <= 3'd0;
	else if((state == WRT0 )||(state == REA0))
		cnt <= cnt + 1'b1;
	else
		cnt <= 3'd0;
	end
	//=========================< State machine >=========================
	always @ (posedge sys_clk or negedge sys_rst_n)	begin//时序逻辑控制状态变迁。
	if(sys_rst_n == 1'b0) 
		state <= IDLE;
	else 
		state <= next_state;
	end
	
	always @ (*) begin	//组合逻辑控制不同状态的转换。
		case (state)
			IDLE:if(sramwr_req) 
					next_state = WRT0;		//进入写状态。
				else if(sramrd_req) 
					next_state = REA0;		
				else 
					next_state = IDLE;
			WRT0:if(cnt == 3'd4) 
					next_state = WRT1;
				else 
					next_state = WRT0;
			WRT1:next_state = IDLE;			//过渡状态保证写入完成
			REA0:if(cnt == 3'd4) 			//进入读状态。
					next_state = REA1;
				else 
					next_state = REA0;
			REA1:next_state = IDLE;			//过渡状态保证读取完成	
			default: next_state = IDLE;
		endcase
	end
	//地址与使能
	always @ (posedge sys_clk or negedge sys_rst_n)begin
		if(sys_rst_n == 1'b0) begin
			sram_addr <= 15'd0;
			sram_cs_n <= 1'b1;
			sram_we_n <= 1'b1;
			sram_oe_n <= 1'b1;
		end
		else if(state == WRT0) begin
			sram_addr <= sramwr_addr;	//写SRAM地址
			sram_cs_n <= 1'b0;
			sram_we_n <= 1'b0;
			sram_oe_n <= 1'b1;
		end
		else if(state == WRT1) begin
			sram_addr <= 15'd0;
			sram_cs_n <= 1'b1;
			sram_we_n <= 1'b1;
			sram_oe_n <= 1'b1;
		end
		else if(state == REA0) begin
			sram_addr <= sramrd_addr;	//读SRAM地址
			sram_cs_n <= 1'b0;
			sram_we_n <= 1'b1;
			sram_oe_n <= 1'b0;
		end
		else if(state == REA1) begin
			sram_addr <= 15'd0;
			sram_cs_n <= 1'b1;
			sram_we_n <= 1'b1;
			sram_oe_n <= 1'b1;
		end
		else;
	end
	//读数据
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
	if(sys_rst_n == 1'b0) 
		sramrd_data <= 8'd0;
	else if((state == REA0) && (cnt == 3'd4))//保证数据稳定再读
		sramrd_data <= sram_data;
	else
		sramrd_data <= sramrd_data;
	end
	//写数据
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
	if(sys_rst_n == 1'b0) 
		wr_data_falg <= 1'd0;
	else if(state == WRT0)
		wr_data_falg <= 1'd1;
	else
		wr_data_falg <= 1'd0;
	end
	assign sram_data = wr_data_falg ? sramwr_data : 8'hzz;	
endmodule

其余模块

按键消抖模块:对读写按键进行消抖,产生读写标志信号。

//========================================================================
// 	module_name.v	:key_filter.v
// 	Author			:YprgDay
// 	Description		:按键消抖20ms
//========================================================================
module key_filter
#(
	parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
	input wire sys_clk , //系统时钟50MHz
	input wire sys_rst_n , //全局复位
	input wire key_in , //按键输入信号
	
	output reg key_flag //key_flag为1时表示消抖后检测到按键被按下
	//key_flag为0时表示没有检测到按键被按下
);

	//reg define
	reg [19:0] cnt_20ms ; //计数器
	
	//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
	always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		cnt_20ms <= 20'b0;
	else if(key_in == 1'b1)
		cnt_20ms <= 20'b0;
	else if(cnt_20ms == CNT_MAX && key_in == 1'b0)//未按下是按键为高,按下时按键为低
		cnt_20ms <= cnt_20ms;
	else
		cnt_20ms <= cnt_20ms + 1'b1;
	
	//key_flag:当计数满20ms后产生按键有效标志位
	//且key_flag在999_999时拉高,维持一个时钟的高电平
	always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		key_flag <= 1'b0;
	else if(cnt_20ms == CNT_MAX - 1'b1)
		key_flag <= 1'b1;
	else
		key_flag <= 1'b0;
	
endmodule

按键读写控制:实现写按键产生写请求将SRAM写满,读按键每隔1s产生一次读请求读数据。

//========================================================================
// 	module_name.v	:rd_wr_ctrl.v
// 	Author			:YprgDay
// 	Description		:外部按键控制SRAM读写,按一下写按键向SRAM中写数据到满为止,按一下读按键每秒读出一个数据供后级数码管显示用
//========================================================================
module rd_wr_ctrl
#(
	parameter CNT_RD_MAX = 26'd49_999_999
)
(
	input 				sys_clk 	,	//时钟信号
	input 				sys_rst_n	,	//复位信号,低电平有效
		
	//FPGA内部对SRAM的读写控制信号
	input 				key_wr		,	//写按键
	output reg		 	led_wr_full_flag,//写满后led亮
	
	input 				key_rd		,	//读按键
	
	output reg		 	sramwr_req	,	// SRAM写请求信号,高电平有效。
	output reg  [14:0] 	sramwr_addr	,	// SRAM写入地址寄存器。
	output reg  [7:0] 	sramwr_data	,	// SRAM写入数据寄存器。
	
	output reg			sramrd_req	,	// SRAM读请求信号,高电平有效。					
	output reg	[14:0] 	sramrd_addr	,	// SRAM读出地址寄存器。
	input		[7:0] 	sramrd_data	,	// SRAM读出数据寄存器。	
	output reg	[7:0] 	sramrd_data_tube//读出数据给数码管
);
	//=========================< Variable >==============================
	reg 		wr_flag;//按下写按键后拉高直至写满
	reg			rd_flag;//按下读按键后拉高直至读完
	reg [3:0] 	cnt_wrreq;//每计数10一个请求写
	reg	[25:0]	cnt_rdreq;//每秒读一次@50MHz
	//=========================< Block >=================================
	//写请求计数
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			wr_flag <= 1'b0;
		else if(((&sramwr_addr)&&(cnt_wrreq == 4'd10))||(key_rd))
			wr_flag <= 1'b0;
		else if(key_wr == 1'b1)
			wr_flag <= 1'b1;
		else
			wr_flag <= wr_flag;
	end
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			cnt_wrreq <= 4'd0;
		else if(cnt_wrreq == 4'd10)
			cnt_wrreq <= 4'd0;
		else if(wr_flag == 1'b1)
			cnt_wrreq <= cnt_wrreq + 1'b1;
		else
			cnt_wrreq <= 4'd0;
	end
	//写请求
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			sramwr_req <=1'd0;
		else if(key_wr == 1'b1)
			sramwr_req <= 1'd1;
		else if(cnt_wrreq == 4'd10)
			sramwr_req <= 1'd1;
		else 
			sramwr_req <= 1'd0;
	end
	//写地址数据以此存放2/4/6/8···
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)begin
			sramwr_addr<=15'd0;
			sramwr_data<=8'd2;
		end
		else if(cnt_wrreq == 4'd10)begin
			sramwr_addr<=sramwr_addr + 15'd1;
			sramwr_data<=sramwr_data + 8'd2;
		end
		else begin
			sramwr_addr<=sramwr_addr;
			sramwr_data<=sramwr_data;
		end
	end
	//写满后led亮,读时led灭
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			led_wr_full_flag <=1'd1;
		else if(key_rd == 1'b1)
			led_wr_full_flag <=1'd1;
		else if(&sramwr_addr)
			led_wr_full_flag <=1'd0;
		else
			led_wr_full_flag <=led_wr_full_flag;
	end
	//读请求计数
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			rd_flag <= 1'b0;
		else if((&sramrd_addr)&&(cnt_rdreq == CNT_RD_MAX)||(key_wr))
			rd_flag <= 1'b0;
		else if(key_rd == 1'b1)
			rd_flag <= 1'b1;
		else
			rd_flag <= rd_flag;
	end
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			cnt_rdreq <= 26'd0;
		else if(cnt_rdreq == CNT_RD_MAX)
			cnt_rdreq <= 26'd0;
		else if(rd_flag == 1'b1)
			cnt_rdreq <= cnt_rdreq + 1'b1;
		else
			cnt_rdreq <= 26'd0;
	end
	//读请求
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)
			sramrd_req <=1'd0;
		else if(key_rd == 1'b1)
			sramrd_req <= 1'd1;
		else if(cnt_rdreq == CNT_RD_MAX)
			sramrd_req <= 1'd1;
		else 
			sramrd_req <= 1'd0;
	end
	//读地址
	always @ (posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)begin
			sramrd_addr<=15'd0;
			sramrd_data_tube<=8'd0;
		end
		else if(cnt_rdreq == CNT_RD_MAX)begin
			sramrd_addr<=sramrd_addr + 15'd1;
			sramrd_data_tube<=sramrd_data;
		end
		else begin
			sramrd_addr<=sramrd_addr;
			sramrd_data_tube<=sramrd_data_tube;
		end
	end
endmodule

BCD模块:将读取的数据转换为二进制编码的十进制,如将1234转换为0001_0010_0011_0100;(此模块参考野火BCD模块,数据位宽20,需求位宽为8,例化时高位补零即可)

module  bcd_8421
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire    [19:0]  data        ,   //输入需要转换的数据

    output  reg     [3:0]   unit        ,   //个位BCD码
    output  reg     [3:0]   ten         ,   //十位BCD码
    output  reg     [3:0]   hun         ,   //百位BCD码
    output  reg     [3:0]   tho         ,   //千位BCD码
    output  reg     [3:0]   t_tho       ,   //万位BCD码
    output  reg     [3:0]   h_hun           //十万位BCD码
);

	//********************************************************************//
	//******************** Parameter And Internal Signal *****************//
	//********************************************************************//
	
	//reg   define
	reg     [4:0]   cnt_shift   ;   //移位判断计数器
	reg     [43:0]  data_shift  ;   //移位判断数据寄存器
	reg             shift_flag  ;   //移位判断标志信号
	
	//********************************************************************//
	//***************************** Main Code ****************************//
	//********************************************************************//
	
	//cnt_shift:从0到21循环计数
	always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_shift   <=  5'd0;
		else    if((cnt_shift == 5'd21) && (shift_flag == 1'b1))
			cnt_shift   <=  5'd0;
		else    if(shift_flag == 1'b1)
			cnt_shift   <=  cnt_shift + 1'b1;
		else
			cnt_shift   <=  cnt_shift;
		
	//data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作
	always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			data_shift  <=  44'b0;
		else    if(cnt_shift == 5'd0)
			data_shift  <=  {24'b0,data};
		else    if((cnt_shift <= 20) && (shift_flag == 1'b0))
			begin
				data_shift[23:20]   <=  (data_shift[23:20] > 4) ? (data_shift[23:20] + 2'd3) : (data_shift[23:20]);
				data_shift[27:24]   <=  (data_shift[27:24] > 4) ? (data_shift[27:24] + 2'd3) : (data_shift[27:24]);
				data_shift[31:28]   <=  (data_shift[31:28] > 4) ? (data_shift[31:28] + 2'd3) : (data_shift[31:28]);
				data_shift[35:32]   <=  (data_shift[35:32] > 4) ? (data_shift[35:32] + 2'd3) : (data_shift[35:32]);
				data_shift[39:36]   <=  (data_shift[39:36] > 4) ? (data_shift[39:36] + 2'd3) : (data_shift[39:36]);
				data_shift[43:40]   <=  (data_shift[43:40] > 4) ? (data_shift[43:40] + 2'd3) : (data_shift[43:40]);
			end
		else    if((cnt_shift <= 20) && (shift_flag == 1'b1))
			data_shift  <=  data_shift << 1;
		else
			data_shift  <=  data_shift;
	
	//shift_flag:移位判断标志信号,用于控制移位判断的先后顺序
	always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			shift_flag  <=  1'b0;
		else
			shift_flag  <=  ~shift_flag;
	
	//当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值
	always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			begin
				unit    <=  4'b0;
				ten     <=  4'b0;
				hun     <=  4'b0;
				tho     <=  4'b0;
				t_tho   <=  4'b0;
				h_hun   <=  4'b0;
			end
		else    if(cnt_shift == 5'd21)
			begin
				unit    <=  data_shift[23:20];
				ten     <=  data_shift[27:24];
				hun     <=  data_shift[31:28];
				tho     <=  data_shift[35:32];
				t_tho   <=  data_shift[39:36];
				h_hun   <=  data_shift[43:40];
			end

endmodule

数码管控制模块:将二十进制进行数码管显示。

module seg7(
			input clk,		//时钟信号,50MHz
			input rst_n,	//复位信号,低电平有效
			input[15:0] display_num,	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
			output reg[3:0] dtube_cs_n,	//7段数码管位选信号
			output reg[7:0] dtube_data	//7段数码管段选信号(包括小数点为8段)
		);
//-------------------------------------------------
//参数定义

//数码管显示 0~F 对应段选输出
parameter 	NUM0 	= 8'h3f,//c0,
			NUM1 	= 8'h06,//f9,
			NUM2 	= 8'h5b,//a4,
			NUM3 	= 8'h4f,//b0,
			NUM4 	= 8'h66,//99,
			NUM5 	= 8'h6d,//92,
			NUM6 	= 8'h7d,//82,
			NUM7 	= 8'h07,//F8,
			NUM8 	= 8'h7f,//80,
			NUM9 	= 8'h6f,//90,
			NUMA 	= 8'h77,//88,
			NUMB 	= 8'h7c,//83,
			NUMC 	= 8'h39,//c6,
			NUMD 	= 8'h5e,//a1,
			NUME 	= 8'h79,//86,
			NUMF 	= 8'h71,//8e;
			NDOT	= 8'h80;	//小数点显示

//数码管位选 0~3 对应输出
parameter	CSN		= 4'b1111,
			CS0		= 4'b1110,
			CS1		= 4'b1101,
			CS2		= 4'b1011,
			CS3		= 4'b0111;

//-------------------------------------------------
//分时显示数据控制单元
reg[3:0] current_display_num;	//当前显示数据
reg[7:0] div_cnt;	//分时计数器

	//分时计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) div_cnt <= 8'd0;
	else div_cnt <= div_cnt+1'b1;

	//显示数据
always @(posedge clk or negedge rst_n)
	if(!rst_n) current_display_num <= 4'h0;
	else begin
		case(div_cnt)
			8'hff: current_display_num <= display_num[3:0];
			8'h3f: current_display_num <= display_num[7:4];
			8'h7f: current_display_num <= display_num[11:8];
			8'hbf: current_display_num <= display_num[15:12];
			default: ;
		endcase
	end
		
	//段选数据译码
always @(posedge clk or negedge rst_n)
	if(!rst_n) dtube_data <= NUM0;
	else begin
		case(current_display_num) 
			4'h0: dtube_data <= NUM0;
			4'h1: dtube_data <= NUM1;
			4'h2: dtube_data <= NUM2;
			4'h3: dtube_data <= NUM3;
			4'h4: dtube_data <= NUM4;
			4'h5: dtube_data <= NUM5;
			4'h6: dtube_data <= NUM6;
			4'h7: dtube_data <= NUM7;
			4'h8: dtube_data <= NUM8;
			4'h9: dtube_data <= NUM9;
			4'ha: dtube_data <= NUMA;
			4'hb: dtube_data <= NUMB;
			4'hc: dtube_data <= NUMC;
			4'hd: dtube_data <= NUMD;
			4'he: dtube_data <= NUME;
			4'hf: dtube_data <= NUMF;
			default: ;
		endcase
	end

	//位选译码
always @(posedge clk or negedge rst_n)begin
	if(!rst_n) dtube_cs_n <= CSN;
	else begin
		case(div_cnt[7:6])
			2'b00: dtube_cs_n <= CS0;
			2'b01: dtube_cs_n <= CS1;
			2'b10: dtube_cs_n <= CS2;
			2'b11: dtube_cs_n <= CS3;
			default:  dtube_cs_n <= CSN;
		endcase
	end
end
	
endmodule

仿真模块

//========================================================================
// 	module_name.v	:tb_top.v
// 	Author			:YprgDay
// 	Description		:顶层仿真
//========================================================================
`timescale 1ns / 1ns
module tb_top();
	reg            	ext_clk_25m ;   
	reg            	ext_key_wr	;
	reg            	ext_key_rd	;
	wire  [7:0] 	led  		;
	                            
	wire			sram_cs_n	;	// SRAM片选信号,低电平有效。
	wire			sram_we_n	;	// SRAM写选通信号,低电平有效。
	wire			sram_oe_n	;	// SRAM输出选通信号,低电平有效。
	wire  [14:0] 	sram_addr	;	// SRAM地址总线。			
	wire  [7:0] 	sram_data	;	// SRAM数据总线。
	                            
	wire  [3:0] 	dtube_cs_n	;	//7段数码管位选信号
	wire  [7:0] 	dtube_data	;//7段数码管段选信号(包括小数点为8段)
	
	//=========================< Parameter >==============================
	parameter 				CLK_PERIOD		=	40		    ;//设置时钟信号周期
	parameter 				HALF_CLK_PERIOD	=	CLK_PERIOD/2;//生成时钟信号半周期
	
	defparam 				u_top.u_key_wr.CNT_MAX = 20'd9	;
	defparam 				u_top.u_key_rd.CNT_MAX = 20'd9	;
	defparam 				u_top.u_rd_wr_ctrl.CNT_RD_MAX = 26'd99;
	//=========================< Variable >==============================
	reg slink; 
	reg	[7:0] 		sram_datar	;
	//==========================< Clock block >============================
	always 	  	#HALF_CLK_PERIOD		ext_clk_25m = ~ext_clk_25m;
	//==========================< Reset block >============================
	 initial begin
		slink = 0;
		sram_datar <= 8'd0	;
		ext_clk_25m = 1'b1	;
		ext_key_wr <= 1'b1	;
		ext_key_rd <= 1'b1	;
		#20
		wait(u_top.sys_rst_n == 1'b1)begin
		ext_key_wr <= 1'b0	;
		end
		#400
		ext_key_wr <= 1'b1	;
		#20
		wait(u_top.u_rd_wr_ctrl.sramwr_addr == 15'd3000)begin
		ext_key_rd <= 1'b0	;
		end
		#400
		ext_key_rd <= 1'b1	;		
	end
	
	top u_top(
	.ext_clk_25m 	(ext_clk_25m),   
	.ext_key_wr		(ext_key_wr	),
	.ext_key_rd		(ext_key_rd	),
	.led  			(led  		),
		             	        
	.sram_cs_n		(sram_cs_n	),	// SRAM片选信号,低电平有效。
	.sram_we_n		(sram_we_n	),	// SRAM写选通信号,低电平有效。
	.sram_oe_n		(sram_oe_n	),	// SRAM输出选通信号,低电平有效。
	.sram_addr		(sram_addr	),	// SRAM地址总线。			
	.sram_data		(sram_data	),	// SRAM数据总线。
		             	        
	.dtube_cs_n		(dtube_cs_n	),	//7段数码管位选信号
	.dtube_data		(dtube_data	)	//7段数码管段选信号(包括小数点为8段)
	);	
	
	always @(negedge sram_oe_n) begin
		slink <= 1;
		sram_datar <= 2*sram_addr[7:0];
	@(posedge sram_cs_n);
		slink <= 0;
	end  
  
	assign sram_data = slink ? sram_datar:8'hzz;  

endmodule

实验现象演示

上板验证实验效果,与设计一致。
在这里插入图片描述

参考资料:

1.野火-SRAM控制原理简介

2.《勇敢的芯 伴你玩转 Xilinx FPGA》吴厚航

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

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

相关文章

开发总结-Dao层(Mapper层)

Mybatis-plus新用法 VehicleBO one vehicleService.getOne(Wrappers.<VehicleBO>lambdaQuery().eq(VehicleBO::getVin, reqVo.getVin()));boolean b bizAccountApplyService.remove(Wrappers.<BizAccountApplyBO>lambdaQuery().eq(BizAccountApplyBO::getId, 14…

工业级PDA手持机,信息时代的便捷工具

在当今信息快速发展的时代&#xff0c;各种先进技术不断涌现&#xff0c;为各个行业带来了巨大的变革和提升。工业级 PDA 手持机作为一种集多种功能于一身的便捷工具&#xff0c;正逐渐成为工业领域中不可或缺的一部分。 工业级 PDA 手持机是一种专门为工业应用设计的便携式数据…

提升编码技能:学习如何使用 C# 和 Fizzler 获取特价机票

引言 五一假期作为中国的传统节日&#xff0c;也是旅游热门的时段之一&#xff0c;特价机票往往成为人们关注的焦点。在这个数字化时代&#xff0c;利用爬虫技术获取特价机票信息已成为一种常见的策略。通过结合C#和Fizzler库&#xff0c;我们可以更加高效地实现这一目标&…

基于STM32单片机的汽车胎压、速度及状态监测系统设计与实现

基于STM32单片机的汽车胎压、速度及状态监测系统设计与实现 摘要&#xff1a; 随着汽车电子技术的快速发展&#xff0c;车辆状态实时监控系统的需求日益增长。本文设计并实现了一种基于STM32单片机的汽车胎压、速度及状态监测系统。该系统能够实时监测汽车的胎压、速度以及其他…

Three.js杂记(十三)—— 包围盒

好久没有学习three.js了&#xff0c;现在再重新巩固并深入学习。荒废学习一年多了&#xff0c;希望现在为时未晚&#x1f4aa; 包围盒 包围盒按照字面理解就是包围一个物体的盒子&#xff0c;那就是一个长方形空间。 一般来说&#xff0c;包围盒可以用于&#xff1a; 物体之…

Redis__数据类型

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a;Redis__数据类型 ⏱️ 创作时间&#xff1a;2024年04月28日 ———————————————— 这里写目录标题 文…

Git零基础

Git工作流程图 操作指令 分支 、 指令总结 远程仓库

[C++] 类和对象 _ 剖析构造、析构与拷贝

一、构造函数 构造函数是特殊的成员函数&#xff0c;它在创建对象时自动调用。其主要作用是初始化对象的成员变量&#xff08;不是开辟空间&#xff09;。构造函数的名字必须与类名相同&#xff0c;且没有返回类型&#xff08;即使是void也不行&#xff09;。 在C中&#xff0…

222.完全二叉树的节点个数

题目链接 题目描述 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最…

vue2—— mixin 超级详细!!!

mixin Mixin是面向对象程序设计语言中的类&#xff0c;提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类 Mixin类通常作为功能模块使用&#xff0c;在需要该功能时“混入”&#xff0c;有利于代码复用又避免了多继承的复杂 vue中的mixin 先来看一下官方定义 mi…

【麒麟(Linux)系统远程连接到windows系统并进行文件传输】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言使用步骤总结 前言 一般来说&#xff0c;windows自带远程桌面&#xff0c;使用的RDP协议&#xff0c;Linux上支持RDP协议的软件很多&#xff0c;常用的是Remmi…

基于决策树的DDoS攻击检测与防护系统研究---实验/论文

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

【前端热门框架【vue框架】】——条件渲染和列表渲染的学习的秒杀方式

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;程序员-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;v…

HTTPS证书申请:相关流程及注意事项

申请HTTPS证书的过程主要包括以下几个步骤&#xff0c;以及一些需要注意的事项&#xff1a; 申请流程&#xff1a; 1. 选择证书类型和期限&#xff1a; - 根据需求选择合适的SSL证书类型&#xff0c;常见的有DV&#xff08;域名验证&#xff09;、OV&#xff08;组织验证&#…

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 目录 Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 一、简单介绍 二、问题现象 三、问题分析 四、使用空后处理&#xff0c;解决闪屏花屏的显示问题 五、空后处理完整代码 一、简单介绍 Unity 在…

鸿蒙开发HarmonyOS4.0入门与实践

鸿蒙开发HarmonyOS4.0 配合视频一起食用&#xff0c;效果更佳 课程地址&#xff1a;https://www.bilibili.com/video/BV1Sa4y1Z7B1/ 源码地址&#xff1a;https://gitee.com/szxio/harmonyOS4 准备工作 官网地址 鸿蒙开发者官网&#xff1a;https://developer.huawei.com/con…

使用FPGA发送一个经过曼彻斯特编码的伪随机序列

介绍 这几天突然就不知道要使用FPGA实现什么样的功能了,然后就跑去学习数电了,学的也是晕晕的。正好之前写了一个使用FPGA发送伪随机序列的代码,然后因为需要使用曼彻斯特编码,所以又加了一个模块吧,使得最后输出的波形经过曼彻斯特编码。 曼彻斯特编码 首先,曼彻斯特编…

【操作系统复习资料】(持续更新中)

目录 第一章&#xff1a;操作系统引论 第二章&#xff1a;进程的描述与控制 未完待续。。。。。接 第三章&#xff1a;处理机调度与死锁 第四章&#xff1a;存储器管理 第五章&#xff1a;虚拟存储器 第六章&#xff1a;第八节 磁盘存储器的性能和调度 第一章&#xff1a…

pgvector扩展在IvorySQL Oracle兼容模式下的应用实践

向量数据库是生成式人工智能(GenAI)的关键组成部分。作为PostgreSQL的重要扩展&#xff0c;pgvector支持高达16000维的向量计算能力&#xff0c;使得PostgreSQL能够直接转化为高效的向量数据库。 IvorySQL基于PostgreSQL开发&#xff0c;因此它同样支持添加pgvector扩展。在Ora…

社交媒体数据恢复:新浪微博

当我们在使用新浪微博时&#xff0c;可能会遇到一些意外情况&#xff0c;如误删微博、账号出现问题等。这时&#xff0c;我们需要进行数据恢复。本文将详细介绍如何在新浪微博中进行数据恢复。 首先&#xff0c;我们需要了解新浪微博的数据恢复功能。根据微博的帮助中心&#…