小梅哥15——嵌入式块RAM应用之FIFO

实现功能:调用FIFO lP核,并进行不同形式的配置,通过仿真来验证其接口时序

1. FIFO 

First In First Out,即先进先出。FPGA或者ASIC中使用到的FIFO一般指对数据的存储具有先进先出特性的存储器,常被用于数据的缓存或高速异步数据的交互。

单时钟 FIFO :SCFIFO——single-clock FIFO

双时钟FIFO:普通双时钟DCFIFO——dual-clock FIFO(supports same port widths for input and output data),和混合宽度双时钟DCFIFO_MIXED_WIDTHS (supports different port widths for inputand output data)。

data[7:0]: 数据写入端口。wreq : 写请求信号。rdreq : 读请求信号。sclr :同步清零。aclr :异步清零。q[7:0] 数据读取端口。full : FIFO内数据写满状态信号。almost_full : 数据即将写满信号。empty : FIFO数据空信号。almost_empty : 即将数据全部读取完成信号。usedw[7…0] : 可用数据个数。

2. 应用

单时钟FIFO:常用于片内数据交互。eg:在 FPGA 控制下从外部传感器读取一连串传感器数据,先写入FIFO中,再以UART串口将数据依次发送出去。由于传感器的单次读取数据可能很快,但并不是时刻都需要采集数据,例如某传感器使用SPI接口的协议,FPGA 以2M的SPI数据速率从该传感器中读取20个数据,然后以9600 的波特率通过串口发送出去。此过程每秒钟执行一次。因为2M的数据速率远高于串口9600 的波特率,因此需要将从传感器中采集到的数据首先用FIFO缓存起来,然后再以串口的数据速率缓慢发送出去。由于【传感器数据的读取和串口数据的发送都是可以同步于同一个时钟,都可以由系统时钟分频得到】,因此可以使用单时钟结构的FIFO来实现此功能。

双时钟FIFO:常用于异步数据的收发。eg:在一个高速数据采集系统中,实现将高速ADC采集的数据通过千兆以太网发送到pc机。ADC采样时钟(CLK1)由外部专用锁相环产生,FPGA内部工作时钟(CLK2)由独立的时钟芯片加片上锁相环产生的,则CLK1 和 CLK2 是两个不同域的时钟,其频率和相位没有必然的联系。假如 CLK1 为 65M,CLK2 为 125M,那么就不能使用 125M 的数据来直接采集 65M 速率的数据,因为两者数据速率不匹配,在采集过程中会出现异步信号亚稳态。低速可以使用2级D触发器同步,高速使用一个具备双时钟结构的FIFO来进行异步数据的收发,进而避免出现亚稳态情况。

​ ADC的数据位宽为8位,基于UDP协议的以太网发送模块所需的数据也是8位,因此使用的是非混合宽度的双时钟FIFО结构。假如 CLK1的频率为20M,ADC的数据位宽为16位,则可以使用混合宽度的双时钟 FIFO,在实现异步时钟域数据收发的同时,实现数据位宽的转换。通过设置双时钟FIFO的写入位宽为16位,读取位宽为8位,则可以实现将16位的ADC数据转换为以太网支持的8位发送数据,然后通过以太网发送到pc机。

3. 配置FIFO IP核及仿真验证

 配置见文档

单时钟FIFO:数据位宽为 16bits,数据深度为 256words,almost full为可用数据>=254,almost empty为可用数据<=2。

仿真测试文件

`timescale 1ns/1ps
`define clk_period 20

module fifo_tb;

    reg Clk;
	reg [15:0]data;
	reg rdreq;
	reg sclr;
	reg wrreq;
	
	wire almost_empty;
	wire almost_full;
	wire empty;
	wire full;
	wire [15:0]q;
	wire [7:0]usedw;//取值0-255,取值为0表示FIFO中没有可用数据或者表示有256个可用数据
	
	integer i;

   fifo fifo(
	    .clock(Clk),
	    .data(data),
	    .rdreq(rdreq),
	    .sclr(sclr),
	    .wrreq(wrreq),
	    .almost_empty(almost_empty),
	    .almost_full(almost_full),
	    .empty(empty),
	    .full(full),
	    .q(q),
	    .usedw(usedw)
		);

   initial Clk = 1'b1;
   always#(`clk_period/2) Clk = ~Clk;
	
	initial begin
	   wrreq = 1'b0;//开始写入请求为无效
	   data = 16'd0;
		rdreq = 1'b0;
		#(`clk_period*20 + 1'b1);
		for(i=0;i<=255;i=i+1'b1)begin
		    wrreq = 1'b1;
			 data = i;
			 #(`clk_period);
		end
		wrreq = 1'b0;
		#(`clk_period*20);
		for(i=0;i<=255;i=i+1'b1)begin
		    rdreq = 1'b1;
			 #(`clk_period);
		end
		$stop;
	end	

endmodule

 


双时钟FIFO:

写位宽16,读位宽8,没写入一个需要读两次

`timescale 1ns/1ps
`define wrclk_period 20//写16位,50M
`define rdclk_period 10//读8位,100M

module mydcfifo_tb;

  	reg	[15:0]  data;
	reg	  rdclk;
	reg	  rdreq;
	reg	  wrclk;
	reg	  wrreq;
	
	wire	[7:0]  q;
	wire	  rdempty;
	wire	[8:0]  rdusedw;
	wire	  wrfull;
	wire	[7:0]  wrusedw;
	
	integer i;
	
  mydcfifo mydcfifo(
	        .data(data),
	        .rdclk(rdclk),
	        .rdreq(rdreq),
	        .wrclk(wrclk),
	        .wrreq(wrreq),
	        .q(q),
	        .rdempty(rdempty),
	        .rdusedw(rdusedw),
	        .wrfull(wrfull),
	        .wrusedw(wrusedw)
	   );

   initial wrclk = 1'b1;
   always#(`wrclk_period/2) wrclk = ~wrclk;
	
	initial rdclk = 1'b1;
   always#(`rdclk_period/2) rdclk = ~rdclk;
	
	initial begin
	   data = 16'd0;
		rdreq = 1'b0;
		wrreq = 1'b0;
		#(`wrclk_period*20 + 1'b1)
		for(i=0;i<=255;i=i+1'b1)begin
		    wrreq = 1'b1;
			 data = i;
			 #(`wrclk_period);
		end
		wrreq = 1'b0;
		
		#(`rdclk_period*20);
		for(i=0;i<=512;i=i+1'b1)begin
		    rdreq = 1'b1;
			 #(`rdclk_period);
		end
		rdreq = 1'b0;
		$stop;
	end

endmodule

 在wrreq被检测到后,下一个时钟wruseddw才变化

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值