本实验的目的是熟悉SRAM的工作原理,并能够通过编程来访问SRAM。本实验利用VHDL或Verilog编程访问开发系统核心板上的SRAM IS61LV24516,要求:
1. 依次向SRAM中的每个地址写入一个数,然后将此数从该地址中读出,判断写入的数和读出的数是否正确;向每个地址中写入的数从0开始依次加1。
2. 如果对某个地址写入的数和读出的数相同,则LED0缓慢闪烁,每2秒闪烁一次,表示读写正确;如果对某个地址写入的数和读出的数不同,则LED7快速闪烁,每0.4秒闪烁一次,表示读写错误。



SRAM是英文Static RAM的缩写,即静态存储器。它是一种具有静态存取功能的存储器,不需要刷新电路即能保存它内部存储的数据。SRAM访问速度快,不必配合内存刷新电路,可提高整体的工作速度。但SRAM集成度低,功耗较大,相同的容量下体积较大,而且价格较高,少量用于关键性系统以提高效率。
SRAM按是否需要时钟又分为同步SRAM和异步SRAM。同步SRAM采用一个输入时钟来启动存储器的所有事务处理(读、写等);而异步SRAM并不具备时钟输入,通过监视输入以获取来自控制器的命令,一旦识别出某条命令,SRAM将立即执行命令。
开发系统核心板上的SRAM IS61LV24516为异步SRAM,无时钟信号,管脚定义如表13-1所示。

表13-1 SRAM管脚定义

A0~A17访问SRAM的地址,18位,地址空间为256K。
I/O0~I/O1L12,L11数据线,16位宽,SRAM的总容量为512K字节。
CE片选信号,低电平有效。
OE读使能信号,低电平有效。
WE写使能信号,低电平有效。
LB低8位访问控制信号,低电平有效。如果LB为0则表示访问16位数据的低8位。
UB高8为访问控制信号,低电平有效。如果UB为低则表示访问16为数据的高8位。

图13-1给出了各管脚的详细功能说明。 图13-1  管脚详细功能定义

图13-1 管脚详细功能定义

SRAM的写入时序如图13 2所示。 图13-2 SRAM写入时序

图13-2 SRAM写入时序

在进行写入操作时,CE、WE为低,OE为高,在Address上输出要写入的地址,对于UB、LB可以根据访问数据的需要来设置。在WE信号拉低tHZWE时间后SRAM在数据线(Dout)上输出高阻,在此之前信号是不确定的;在数据线输出高阻之后,FPGA可以在数据线(Din)上输出要写入的数据。为了保证不会发生数据冲突,要求FPGA在tHZWE时间后才可以输出要写入的数据。写入时序时间要求如表13 2所示。

表13-2 写入时序时间要求

表13-2 写入时序时间要求

SRAM的读取时序如图13-3所示。 图13-3 SRAM写入时序

图13-3 SRAM读取时序

在读取操作时要求,OE和CE为低,LB和UB根据需要设置电平,在Address上输出要读取数据的地址,在max{tAA, tACE, tBA}时间后,数据线上输出要读取的数据。读取时序时间要求如表13 3所示。

表13-3 读取时序时间要求

表13-3 读取时序时间要求

3.1 总体架构(SRAM.v)

图13-4 程序总体架构

图13-4 程序总体架构

程序由四个模块组成(图 13-4):clkgen、SRAMTester、SRAMIF和LEDindicator:
1) 由于核心板上的SRAM是异步SRAM, SRAM
IF将SRAM的异步接口转换成同步接口,SRAMIF可以作为一个通用模块供其它模块调用。
2) SRAM
Tester是对SRAM进行测试,对每一个地址写入一个数据,同时读取该地址的数据,检测和写入的数据是否一致。
3) SRAMTester检测结果通知LEDindicator,用LED来指示读写是否正确。
4) Clkgen是一个PLL,将50MHz的输入时钟信号变成100MHz的时钟信号,以验证SRAM的高速访问性能。

3.2 SRAM_IF模块(SRAM_IF.v)

SRAM_IF的功能是将SRAM的异步接口转换成同步接口,提供一个同步总线接口的SRAM访问模块。由于FPGA的设计主要是同步设计,转换成同步接口易于被其它模块所使用,提高设计的易用性和可靠性。

SRAM的接口定义如下:

 
input clk, reset;	  //时钟和复位输入信号。
 
//以下信号为总线侧接口信号。
input bus_en;	//SRAM访问使能信号,高电平有效。
input bus_RW; //SRAM读写控制信号,高电平为读取访问,低电平为写入访问。
input [17:0] bus_addr; 		//SRAM访问地址。
input [15:0] bus_datain; 	//写入SRAM的数据。
output [15:0] bus_dataout; 	//从SRAM中读取的数据。
output bus_dataready; 		//从SRAM中读取数据完成,高电平有效。
 
 //以下信号为SRAM的接口信号,各信号含义如表13-1所示。
output 		ce_i, oe_i, we_i, lb_i, ub_i;
inout wire  [15:0]	data_i;
output 	 [17:0] 	addr_i;

图13-5 SRAM_IF写入时序

图13-5 SRAM_IF写入时序

总线侧接口控制信号busen、busrw的宽度为一个时钟周期,期间地址线和数据线有效。在接收到写控制信号时,SRAMIF将SRAM控制信号cei、wei、oei、lbi、ubi拉低,同时输出地址信号addr_i,在下一个时钟周期输出数据。因为在写控制信号输出一段时间后SRAM才会置数据线为高阻,这时才可以在数据线上输出要写入的数据信号。

SRAM_IF的读取时序如图13-6所示。

图13-6 SRAM_IF读取时序

图13-6 SRAM_IF读取时序

读控制信号busen和busrw为一个时钟周期的宽度,期间读地址busaddr有效。在检测到读控制信号后,SRAMIF将cei、oei、lbi和ubi置为有效,同时输出读地址。在两个时钟周期后读取datai上的数据,并通过busdataout将数据输出给总线,同时置bus_dataready有效,宽度为一个时钟周期。在两个时钟周期后读取数据是因为SRAM读操作至少需要10ns,而时钟周期为10ns,一个周期不能保证读取操作完成,为保险起见,在两个时钟周期后读取数据。

各接口信号由内部控制信号控制:

 
assign busy = reading | writing;  //信号reading和writing为读、写操作标志信号,为高表示正在进行读操作或者写操作,任一操作都表示模块忙。
		assign ce_i = !busy;  //如果有读操作或写操作,则ce_i拉地,对SRAM使能。
		assign oe_i = !reading; //如果有读操作,则读控制信号oe_i有效。
assign we_i = !writing; //如果有写操作,则写控制信号we_i有效。
assign lb_i = !busy;   
assign ub_i = !busy;   //模块采用16位读写模式,所以lb_i和ub_i拉低表示16位读写。
 
写操作由状态write_state控制,由3个状态组成:
always @(posedge clk or negedge reset )	begin : SRAM_writing
		if(!reset) begin
			writing <= 0;
			write_state <= 0;
		end
		else begin
			case( write_state )
			2'd0: begin                       //等待状态
				if( bus_en && !bus_RW ) begin  //表示写操作。
					writing <= 1'b1;          // 置写操作标识。
					write_state <= 2'b1;       //状态转移。
				end
			end
			2'd1: begin                       //数据输出
				write_state <= 2'd2;           //状态转移。
				data_t <= bus_datain;          // 输出数据, data_i = data_t
			end
			3'd2: begin                      //写操作完成。
				write_state <= 2'd0;			  //状态转移至等待状态。
 
				writing <= 1'b0;              //写操作标识清0。
				data_t <= 16'bzzzz_zzzz_zzzz_zzzz; //将输出数据置为高阻。
			end
		endcase
		end
	end

下面模块控制SRAM接口模块的地址线:

 
always @(posedge clk or negedge reset )	begin 
		if( !reset ) 
			addr_i <= 0;
		else 
			if( bus_en )              //当读写控制使能时,输出地址线。
				addr_i <= bus_addr;
	end
读操作由状态机read_state控制,有四个状态:
always @(posedge clk or negedge reset )	begin : SRAM_reading
		if(!reset) begin
			reading <= 0;
			bus_dataready <= 0;
			bus_dataout <= 16'd0;
			read_state <= 2'd0;
		end
		else begin
			case( read_state )
			2'd0: begin                              //等待状态。
				if( bus_en && bus_RW ) begin          //检测到读操作。
					reading <= 1'b1;                 //读标识置位。
					bus_dataout <= 16'd0;            //将总线输出清0。
					read_state <= 2'd1;               //状态转移。
				end
			end
			2'd1: begin                              //读等待。
				read_state <= 2'd2;                    //状态转移。
			end
 
			2'd2: begin                             //读取数据。
				reading <= 1'b0;                     // 复位读标识。
				bus_dataout <= data_i;                //读取数据并送给总线接口。
				bus_dataready <= 1'b1;             //数据读取完成标识置位。
				read_state <= 2'd3;                //状态转移。
			end
			2'd3: begin
				bus_dataready <= 1'b0;            //数据读取完成标识清0。
				read_state <= 2'd0;               //状态转移。
			end
			endcase
		end
	end

3.3 SRAM_tester模块(SRAM_tester.v)

SRAM_ Tester模块完成对SRAM的扫描测试,对每一个地址写入一个数据并读取,检测写入和读取的是否一致。操作方式为:给地址A写入数据B(B为A的低16位);读取地址A-1中的数据B2,检测B2是否为A-1的低16位。读取地址A-1而不是A的原因是防止对A进行写操作时候在信号线的残留信号影响读取结果。

SRAM_Tester的接口信号定义如下:

 
input reset, clk;	//时钟、复位信号。
	input data_ready;  //数据有效信号;在读取数据时,data_ready为高表示读取完成,data_in数据有效。
	input [15:0]data_in; //读取操作时读取的数据,data_ready为高时有效。
	output reg [15:0] data_out; //写入操作时要写入的数据。
	output reg [17:0] addr;    //读取或写入的地址。
	output reg enable;       //使能信号,表示rw、addr、data_out有效。
	output reg rw;          //rw读写操作,为高时表示读操作,为低时表示写操作。
	output reg data_right;    //读写正确指示,为高时表示读取和写入的数据一致。
以上接口信号的时序如图13-5和图13-6所示。
由于时钟是100MHz,读写操作每2个时钟周期完成一次,因此定义50MHz的使能信号enable50M:
always @(posedge clk or negedge reset )	begin
if( !reset ) begin
			enable50M <= 0;
end
else begin
	enable50M <= ~enable50M;
end
end

扫描检测代码如下:

 
always @(posedge clk or negedge reset )	begin
if( !reset ) begin	//各信号复位。
enable <= 1'b0;
rw <= 1'b1;
state <= 2'd0;        //state为状态控制信号,有3个状态。
addr <= 18'd0;
data_out <= 0;
readaddr <= 0;
end 
else begin
if( enable )
	enable <= 0;      //当enable为高时,置enable为低,确保enable宽度为一个时钟周期。
			if( enable50M ) begin   //当enable50M为高时进行读写操作。
			case( state )
			2'd0: begin             //进行写操作。
					rw <= 0;
					state <= 3'd1;
					enable <= 1;
				  end
 
			2'd1: begin              //进行读操作。
					rw <= 1;
					enable <= 1;
					addr <= addr - 16'd1;  //读取的地址为addr-1。
					readaddr <= addr - 16'd1;
					state <= 3'd2;
				end
 
			2'd2: begin                  //读取完成,准备下一轮写操作。
					state <= 0;
					addr <= addr + 16'd2;  //下一次读取的地址,由于在写入的时候减1了,所以要加2才是下一次写入的地址。
					data_out <= data_out + 16'd1; //下一次写入的数据。
					enable <= 0;
				end
			default:
				state <= 0;				
			endcase
			end
		end
	end

readcomparison对读取的结果进行检测,看是否和写入的一致,读取值应为地址的低16位。当模块刚开始运行时,读取的地址为3FFFF,即最高地址位,但第一次运行时该地址中还未写入数据,无法进行比较。因此,定义信号firstread,复位时firstread为高,当firstread为高时不进行比较,并将first_read置0。之后便可以对结果进行比较:

 
always @(posedge clk or negedge reset )	begin : result_comparison
		if( !reset ) begin
		   first_read <= 1;
			data_right <= 1;
		end
		else begin
			if( data_ready ) begin
			  if( first_read ) begin  //当first_read为高时,不比较。
			     first_read <= 0;
			  end
			  else if( data_in != readaddr[15:0] )  //对结果进行比较。
				 data_right <= 0;             //如果结果错误,将data_right置0。
			end
		end	
	end

3.4 LED_indicator模块(LED_indicator.v)

Led_indicator模块是根据检测结果来控制led灯,这个模块较为简单,不再详述。



SRAMSIM(SRAMSIM.v)是SRAM的仿真模块,用以模拟SRAM的行为,二维数组SRAMub和SRAMlb 分别用来模拟的SRAM的高8位和低8位,数组的长度可以自由设置,为了仿真方便设为10,也就是地址空间为0-9。由于核心板上的SRAM是异步SRAM,为了更真实地模拟SRAM的行为,采用组合逻辑加延时的方式来实现:

 
//接口信号定义:
input ce,oe,we,lb,ub;  
input [17:0] addr;
inout wire [15:0] data;
 
//用数组模拟存储器:
reg [7:0] SRAMub[10:0];
reg [7:0] SRAMlb[10:0];
 
reg [15:0] data_t;
assign data = data_t;
 
always@* begin
  	case({ce,oe,we,lb,ub})           //对ce, oe, we, lb, ub几个信号进行判断。
	5'b01000:                     //对16位数据进行写操作。
		begin 
		#5 SRAMub[addr] <= data[15:8];  //这里加了5ns的延时,延时长短可以调整。
		SRAMlb[addr] <= data[7:0];
		end
	5'b01010:                     //对高8位数据进行写操作。
		begin 
		#5 SRAMub[addr] <= data[15:8];
		end
	5'b01001:                    //对低8位数据进行写操作。
		begin
		#5 SRAMlb[addr] <= data[7:0];
		end
	5'b00100:                     //对16位数据进行读操作。
		begin
		#5 data_t[15:8] <= SRAMub[addr];
		data_t[7:0] <= SRAMlb[addr];
		end
	5'b00110:                       //对高8位数据进行读操作。
		begin
		#5 data_t[15:8] <= SRAMub[addr];
		end
	5'b00101:                       //对低8位数据进行读操作。
		begin
		#5 data_t[7:0] <= SRAMlb[addr];
		end
 
	default: data_t <= 16'bzzzz_zzzz_zzzz_zzzz;  //其它情况下,数据线输出高阻。
        endcase
end

testtop是测试顶层模块,产生时钟和复位信号。

仿真结果如图13-7所示,图中给出了一个完整的读写操作,写入地址是1,读取地址是0。 图13-7 SRAM仿真结果

图13-7 SRAM仿真结果

图13-8为将程序下载到开发系统后从SignalTap中观察到的实际运行结果。 图13-8 SRAM读写运行结果

图13-8 SRAM读写运行结果

从上图可以看出,从SRAM中读出的数和写入的一致。

最后在实验板上的结果是只有right_light指示灯LED0缓慢地闪烁,验证了实验板上SRAM工作是正常的。



文件名功能
SRAM.v顶层文件。
SRAMIF.v|SRAMIF将SRAM的异步接口转换成同步接口。
SRAMTester.v|SRAMTester是对SRAM进行测试,对每一个地址写入一个数据,同时读取该地址的数据,检测和写入的数据是否一致。
SRAMSIM.v|SRAM仿真模块,模拟SRAM的行为,用于对程序的仿真。| |Testtop.v|仿真顶层模块。| |SRAMsim.mpfModelSim仿真项目。



把程序下载到开发系统上后, sw1为复位端reset,可以看到LED0缓慢闪烁或LED7快速闪烁。LED0为rightlight,表示读写正确;LED7为errorlight,如从sram中读出的数据有误,则此灯快速闪烁。