2023年7月17日发(作者:)
FPGA-学习⽚外SRAM读写测试包括RAMROMFIFO对⽐总结与实战学习内容RAM、ROM、FIFO等概念对⽐,SRAM读写测试。实现功能⼀个8位数字从0000000开始,每⼀秒⾃加1,并将每⼀秒的数字存⼊SRAM中,并在这⼀秒内读出存⼊的数字⽤⼋位的LED灯来显⽰。开发环境xilinx spartan6开发板、ISE14.7、modelsim10.5、verilog存储器相关知识这篇⽂章学习的是SRAM的读写测试,但是我借这个机会好好再明确⼀下RAM、ROM、FIFO的概念,对⽐⼀下他们的区别。RAM随机存取存储器(random access memory,RAM)⼜称作“随机存储器”,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,⽽且速度很快,通常作为操作系统或其他正在运⾏中的程序的临时数据存储媒介。存储单元的内容可按需随意取出或存⼊,这种存储器在断电时将丢失其存储内容,故主要⽤于存储短时间使⽤的程序。 按照存储单元的⼯作原理,随机存储器⼜分为静态随机存储器SRAM(英⽂:StaTIc RAM,SRAM)和动态随机存储器DRAM(英⽂DynamicRAM,DRAM)。 RAM的特点主要有:1.随机存储:指的是当存储器中的数据被读取或写⼊时,所需要的时间与这段信息所在的位置或所写⼊的位置⽆关。2.易失性:当RAM被断电以后保存在上⾯的数据会⾃动消失,⽽ROM不会⾃动消失可以长时间断电保存,3.对静电敏感:随机存取存储器对环境的静电荷⾮常敏感。静电会⼲扰存储器内电容器的电荷,引致数据流失,甚⾄烧坏电路。故此触碰随机存取存储器前,应先⽤⼿触摸⾦属接地。4.访问速度快:现代的随机存取存储器⼏乎是所有访问设备中写⼊和读取速度最快的。5.需要刷新:现代的随机存取存储器依赖电容器存储数据。电容器充满电后代表1(⼆进制),未充电的代表0。由于电容器或多或少有漏电的情形,若不作特别处理,数据会渐渐随时间流失。刷新是指定期读取电容器的状态,然后按照原来的状态重新为电容器充电,弥补流失了的电荷。需要刷新正好解释了随机存取存储器的易失性。SRAM速度⾮常快。是⽬前读写最快的存储设备,但是⽐较昂贵,所以只在要求⽐较苛刻的地⽅使⽤,⽐如CPU的⼀⼆级缓冲;DRAM保留数据的时间很短,我们计算机的内存就是DRAM。ROM只读存储器(Read-Only Memory,ROM)以⾮破坏性读出⽅式⼯作,只能读出⽆法写⼊信息。信息⼀旦写⼊后就固定下来,即使切断电源,信息也不会丢失,所以⼜称为固定存储器。ROM所存数据通常是装⼊整机前写⼊的,整机⼯作过程中只能读出,不像随机存储器能快速⽅便地改写存储内容。ROM所存数据稳定 ,断电后所存数据也不会改变,并且结构较简单,使⽤⽅便,因⽽常⽤于存储各种固定程序和数据。进⼀步发展出可编程只读存储器(PROM)、可擦可编程序只读存储器(EPROM)和带电可擦可编程只读存储器(EEPROM)等不同的种类。FIFOFIFO( First Input First Output)简单说就是指先进先出。在系统设计中,以增加数据传输率、处理⼤量数据流、匹配具有不同传输率的系统为⽬的⽽⼴泛使⽤FIFO存储器,从⽽提⾼了系统性能。FIFO存储器是⼀个先⼊先出的双⼝缓冲器,即第⼀个进⼊其内的数据第⼀个被移出,其中⼀个是存储器的输⼊⼝,另⼀个⼝是存储器的输出⼝。对于单⽚FIFO来说,主要有两种结构:1. 触发导向传输结构的FIFO是由寄存器阵列构成的。2. 零导向传输结构的FIFO是由具有读和写地址指针的双⼝RAM构成。同时分为同步FIFO和异步FIFO1. 同步FIFO,读和写应⽤同⼀个时钟。它的作⽤⼀般是做交互数据的⼀个缓冲,也就是说它的主要作⽤就是⼀个buffer。2. 异步FIFO,读写应⽤不同的时钟,它有两个主要的作⽤,⼀个是实现数据在不同时钟域进⾏传递,另⼀个作⽤就是实现不同数据宽度的数据接⼝。SRAM读写分析你找来任何⼀颗 SRAM 芯⽚的 datasheet,会发现它们的时序操作⼤同⼩异,在这⾥总结⼀些它们共性的东西,也提⼀些⽤ Verilog 简单的快速操作 SRAM 的技巧。SRAM 内部的结构如图 7.38 所⽰,要访问实际的 Momory 区域,FPGA 必须送地址(A0-A14)和控制信号(CE#OE#WE#),SRAM 内部有与此对应的地址译码(decoder)和控制处理电路(control circuit)。这样,数据总线(I/O0-I/O7)上的数据就可以相应的读或写了。本⽂以特权同学开发板上的 IS62LV256-45U为例进⾏说明,其管脚定义如下表所⽰。序号1234567管脚⽅向inputinputinputinputinoutinputinput功能地址总线芯⽚使能输⼊,低有效输出使能输⼊,低有效写使能输⼊,低有效数据总线电源数字地A0−A14CEOEWEIO0−IO7VCCGNDSRAM芯⽚接⼝如下图所⽰。SRAM的读操作时序图如下图所⽰。SRAM的写操作时序图如下图所⽰。简单介绍⼀下读/写操作是怎么进⾏的:具体操作是这样的,要写数据时,(这⾥是相对于⽤ FPGA 操作 SRAM ⽽⾔的,软件读写可能有时间顺序的问题需要注意),⽐较⾼效率的操作是送数据和地址,把 CE#和 WE#拉低。然后延时tWC 时间再把 CE#和 WE#拉⾼,这时就把数据写⼊了相应地址了,就这么简单。读数据就更简单了,只要把需要读出的地址放到 SRAM 的地址总线上,把 CE#和 OE#拉低,然后延时tAA时间后就可以读出数据了。SRAM读写时序表如下图所⽰verilog代码分为三个部分,顶层⽂件sp6.v ⼦模块test_timing.v
sram_controller.v顶层⽂件sp.v如下module sp6( input ext_clk_25m, //外部输⼊25MHz时钟信号 input ext_rst_n, //外部输⼊复位信号,低电平有效 output[7:0] led, //LED指⽰灯,点亮表⽰读写SRAM同⼀个地址正确,熄灭表⽰读写SRAM同⼀个地址失败。 output sram_cs_n, // SRAM⽚选信号,低电平有效。 output sram_we_n, // SRAM写选通信号,低电平有效。 output sram_oe_n, // SRAM输出选通信号,低电平有效。 output[14:0] sram_addr, // SRAM地址总线。
inout[7:0] sram_data // SRAM数据总线。 );
//-------------------------------------//PLL例化wire clk_12m5; //PLL输出12.5MHz时钟wire clk_25m; //PLL输出25MHz时钟wire clk_50m; //PLL输出50MHz时钟wire clk_65m; //PLL输出65MHz时钟wire clk_108m; //PLL输出108MHz时钟wire clk_130m; //PLL输出130MHz时钟wire sys_rst_n; //PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,⾼电平正常⼯作 pll_controller uut_pll_controller (// Clock in ports (// Clock in ports .CLK_IN1(ext_clk_25m), // IN // Clock out ports .CLK_OUT1(clk_12m5), // OUT .CLK_OUT2(clk_25m), // OUT .CLK_OUT3(clk_50m), // OUT .CLK_OUT4(clk_65m), // OUT .CLK_OUT5(clk_108m), // OUT .CLK_OUT6(clk_130m), // OUT // Status and control signals .RESET(~ext_rst_n),// IN .LOCKED(sys_rst_n)); // OUT
//-------------------------------------//每秒钟定时SRAM读和写时序产⽣模块wire sramwr_req; // SRAM写请求信号,⾼电平有效,⽤于状态机控制。wire sramrd_req; // SRAM读请求信号,⾼电平有效,⽤于状态机控制。
wire[7:0] sramwr_data; // SRAM写⼊数据寄存器。wire[7:0] sramrd_data; // SRAM读出数据寄存器。
wire[14:0] sramwr_addr; // SRAM写⼊地址寄存器。wire[14:0] sramrd_addr; // SRAM读出地址寄存器。
test_timing uut_test_timing( .clk(clk_50m), //时钟信号 .rst_n(sys_rst_n), //复位信号,低电平有效 .led(led[0]), //LED指⽰灯,点亮表⽰读写SRAM同⼀个地址正确,熄灭表⽰读写SRAM同⼀个地址失败。 .sramwr_req(sramwr_req), // SRAM写请求信号,⾼电平有效,⽤于状态机控制。 .sramrd_req(sramrd_req), // SRAM读请求信号,⾼电平有效,⽤于状态机控制。
.sramwr_data(sramwr_data), // SRAM写⼊数据寄存器。 .sramrd_data(sramrd_data), // SRAM读出数据寄存器。
.sramwr_addr(sramwr_addr), // SRAM写⼊地址寄存器。 .sramrd_addr(sramrd_addr) // SRAM读出地址寄存器。 );
//-------------------------------------//SRAM的基本读写时序模块sram_controller uut_sram_controller( .clk(clk_50m), //时钟信号 .rst_n(sys_rst_n), //复位信号,低电平有效 .sramwr_req(sramwr_req), // SRAM写请求信号,⾼电平有效,⽤于状态机控制。 .sramrd_req(sramrd_req), // SRAM读请求信号,⾼电平有效,⽤于状态机控制。
.sramwr_data(sramwr_data), // SRAM写⼊数据寄存器。 .sramrd_data(sramrd_data), // SRAM读出数据寄存器。
.sramwr_addr(sramwr_addr), // SRAM写⼊地址寄存器。 .sramrd_addr(sramrd_addr), // SRAM读出地址寄存器。 .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数据总线。 );assign led[7:1] = 7'b1111111;
endmodule⼦模块test_timing.v如下,这个模块主要是⼀个⽣成数据的测试模块,主要是⽣成⼀个1S的计时器,来通过判断计数器的值的⼤⼩来进⾏写和读的操作。module test_timing( input clk, //时钟信号 input rst_n, //复位信号,低电平有效 output reg led, //LED指⽰灯,点亮表⽰读写SRAM同⼀个地址正确,熄灭表⽰读写SRAM同⼀个地址失败。 output sramwr_req, // SRAM写请求信号,⾼电平有效,⽤于状态机控制。 output sramrd_req, // SRAM读请求信号,⾼电平有效,⽤于状态机控制。
output reg[7:0] sramwr_data, // SRAM写⼊数据寄存器。 input[7:0] sramrd_data, // SRAM读出数据寄存器。
output reg[14:0] sramwr_addr, // SRAM写⼊地址寄存器。 output reg[14:0] sramrd_addr // SRAM读出地址寄存器。 );//-------------------------------------//1s定时逻辑产⽣reg[25:0] delay; //延时计数器,不断计数,周期为1s,⽤于产⽣定时信号。always @ (posedge clk or negedge rst_n) if(!rst_n) delay <= 26'd0; //else if(delay < 26'd19_999) delay <= delay+1; //for test else if(delay < 26'd49_999_999) delay <= delay+1'b1;
else delay <= 26'd0;
assign sramwr_req = (delay == 26'd1000); //产⽣写请求信号,每秒钟产⽣⼀个⾼电平脉冲assign sramrd_req = (delay == 26'd1100); //产⽣读请求信号,每秒钟产⽣⼀个⾼电平脉冲
//-------------------------------------//定时SRAM写⼊数据寄存器
always @ (posedge clk or negedge rst_n) //写⼊数据每1s⾃增1。 if(!rst_n) sramwr_data <= 8'd0; else if(delay == 26'd4000) sramwr_data <= sramwr_data+1'b1;
//-------------------------------------//定时SRAM读和写地址寄存器
always @ (posedge clk or negedge rst_n) //写⼊和读出地址每1s⾃增1。 if(!rst_n) sramwr_addr <= 15'd0; else if(delay == 26'd4000) sramwr_addr <= sramwr_addr+1'b1;
always @ (posedge clk or negedge rst_n) //写⼊和读出地址每1s⾃增1。 if(!rst_n) sramrd_addr <= 15'd0; else if(delay == 26'd4000) sramrd_addr <= sramrd_addr+1'b1;
//-------------------------------------//在同⼀地址读和写操作完成后,⽐对写⼊和读出的数据是否⼀致,通过LED输出⽐对结果
always @ (posedge clk or negedge rst_n) //每1s⽐较⼀次同⼀地址写⼊和读出的数据。 if(!rst_n) led <= 1'b0; else if(delay == 26'd3000) begin if(sramwr_data == sramrd_data) led <= 1'b0; //写⼊和读出数据⼀致,LED点亮 else led <= 1'b1; //写⼊和读出数据不同,LED熄灭 end
endmodule⼦模块sram_controller.v如下,主要是sram读写的时序控制,根据时序图和时序表来写。module sram_controller( input clk, //时钟信号 input rst_n, //复位信号,低电平有效 //FPGA内部对SRAM的读写控制信号 input sramwr_req, // SRAM写请求信号,⾼电平有效,⽤于状态机控制。 input sramrd_req, // SRAM读请求信号,⾼电平有效,⽤于状态机控制。
input[7:0] sramwr_data, // SRAM写⼊数据寄存器。 output reg[7:0] sramrd_data, // SRAM读出数据寄存器。
input[14:0] sramwr_addr, // SRAM写⼊地址寄存器。 input[14:0] sramrd_addr, // 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 = 4'd0, WRT0 = 4'd1, WRT1 = 4'd2, REA0 = 4'd3, REA1 = 4'd4;reg[3:0] cstate,nstate;
`define DELAY_00NS (cnt==3'd0) //⽤于产⽣SRAM读写时序所需要的0ns延时`define DELAY_20NS (cnt==3'd1) //⽤于产⽣SRAM读写时序所需要的20ns延时`define DELAY_40NS (cnt==3'd2) //⽤于产⽣SRAM读写时序所需要的40ns延时`define DELAY_60NS (cnt==3'd3) //⽤于产⽣SRAM读写时序所需要的60ns延时reg[2:0] cnt; //延时计数器always @ (posedge clk or negedge rst_n) if(!rst_n) cnt <= 3'd0; else if(cstate == IDLE) cnt <= 3'd0; else cnt <= cnt+1'b1;
//-------------------------------------//SRAM读写状态机always @ (posedge clk or negedge rst_n) //时序逻辑控制状态变迁。 if(!rst_n) cstate <= IDLE; else cstate <= nstate;always @ (cstate or sramwr_req or sramrd_req or cnt) begin //组合逻辑控制不同状态的转换。 case (cstate) IDLE: if(sramwr_req) nstate <= WRT0; //进⼊写状态。 else if(sramrd_req) nstate <= REA0; //进⼊读状态。 else nstate <= IDLE; WRT0: if(`DELAY_60NS) nstate <= WRT1; else nstate <= WRT0;
WRT1: nstate <= IDLE;
REA0: if(`DELAY_60NS) nstate <= REA1; else nstate <= REA0;
REA1: nstate <= IDLE;
default: nstate <= IDLE; endcaseend//-------------------------------------//地址赋值
always @ (posedge clk or negedge rst_n) if(!rst_n) sram_addr <= 15'd0; else if(cstate == WRT0) sram_addr <= sramwr_addr; //写SRAM地址
else if(cstate == WRT1) sram_addr <= 15'd0; else if(cstate == REA0) sram_addr <= sramrd_addr; //读SRAM地址 else if(cstate == REA1) sram_addr <= 15'd0; //-------------------------------------
//SRAM读写数据的控制reg sdlink; // SRAM数据总线⽅向控制信号,1为输出,0为输⼊。always @ (posedge clk or negedge rst_n) //在状态REA1时执⾏SRAM读数据操作。 if(!rst_n) sramrd_data <= 8'd0; else if((cstate == REA0) && `DELAY_60NS) sramrd_data <= sram_data;always @ (posedge clk or negedge rst_n) //控制不同状态下SRAM数据总线的⽅向。SRAM只有在执⾏写操作时为输出,其他时候均为输⼊。 if(!rst_n) sdlink <= 1'b0; else if(cstate == WRT0) sdlink <= 1'b1; else if(cstate == WRT1) sdlink <= 1'b0;assign sram_data = sdlink ? sramwr_data : 8'hzz;
//-------------------------------------//SRAM⽚选、读选通和写选通信号的控制 //SRAM⽚选信号产⽣always @ (posedge clk or negedge rst_n) if(!rst_n) sram_cs_n <= 1'b1; else if(cstate == WRT0) begin if(`DELAY_00NS) sram_cs_n <= 1'b1;
else sram_cs_n <= 1'b0; end else if(cstate == REA0) sram_cs_n <= 1'b0; else sram_cs_n <= 1'b1; //SRAM读选通信号产⽣always @ (posedge clk or negedge rst_n) if(!rst_n) sram_oe_n <= 1'b1; else if(cstate == REA0) sram_oe_n <= 1'b0;
else sram_oe_n <= 1'b1;
//SRAM写选通信号产⽣always @ (posedge clk or negedge rst_n) if(!rst_n) sram_we_n <= 1'b1; else if(cstate == WRT0) begin if(`DELAY_20NS) sram_we_n <= 1'b0; end else sram_we_n <= 1'b1;
endmodule写操作仿真结果如下图所⽰:仿真测试结果读操作仿真结果如下图所⽰参考特权同学野⽕FPGA
发布者:admin,转转请注明出处:http://www.yc00.com/news/1689548730a265251.html
评论列表(0条)