IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 异步FIFO设计 -> 正文阅读

[嵌入式]异步FIFO设计

异步FIFO常用于实现多bit数据的跨时钟域传输。

4.4 Verilog FIFO 设计


1. 功能

可实现快到慢、慢到快的跨时钟域多bit数据传输,具体功能介绍如下:、

● 可对FIFO深度、宽度进行参数化自定义

● 能够实现数据的异步读写功能,且读出的数据是先入先出的顺序

● 能够指示FIFO空、满状态。同时FIFO内部数据量达到配置的参数数目时,拉高一个可配置的满状态信号

● 写数据宽度和读数据宽度可以不一致,但要实现位数拼接和分割

例如写是8bit读是32bit,那么每1次读的数据应当是每4次写的数据。

同理写是32bit读是8bit,那么每1次读的数据应当是每1次写的数据中8bit的那部分。

2. 架构

首先是异步FIFO的架构,基于自顶向下的设计思路,将功能划分为多个模块,再相互连接

2.1. 顶层模块 async_fifo

由以下几个模块构成:wr_logc、rd_logic和simple dual-port RAM,其中

● Simle dual-port RAM完成数据的存储和读写功能

● wr_logic完成写逻辑的实现,同时产生waddr与双口RAM通信。

rd_logic完成读逻辑的实现,同时产生raddr与双口RAM通信。

注意写逻辑还需要输出full信号,读逻辑需要输出empty信号,这两个信号均与读写指针有关,所以需要wptr和rptr的跨时钟域通信。

在这里插入图片描述

参数描述

先是端口信号描述

Signal Direction Width(bits) Description
rstninput1FIFO异步复位
wclkinput1写时钟
wr_eninput1写使能
wdatainput`WDATA_WIDTH写数据
fulloutput1写满标志
pfulloutput1可编程满标志。当FIFO内数据数目达到`PROG_DEPTH时,该信号拉高
rclk input1读时钟
rd_eninput1读使能
rdataoutput`RDATA_WIDTH读数据
emptyoutput1读空标志

之后是对FIFO配置参数描述

Parameter Units Description
`WDATA_WIDTHbitFIFO写数据宽度,即伪双口RAM的写数据宽度
`RDATA_WIDTHbitFIFO读数据宽度,即伪双口RAM的读数据宽度
`ASYNC_FIFO_DEPTHbitFIFO深度
`PROG_DEPTHbit可编程数据深度。当FIFO内数据数目达到该值时,pfull拉高

2.2. 伪双口RAM dual_port_ram

异步FIFO由读写两个逻辑组成,需要对写数据输入输出,所以使用伪双口RAM就行了。

但是注意我们要实现读写数据宽度不同的FIFO,所以此处的RAM读写宽度也不同,相应的读写地址宽度也不同。

注意这几个RAM的区别
单口RAM(Single-port RAM):只有一端一个时钟,可读、写
伪双口RAM(Simple Dual-port RAM):一端只读,另一端只写,且读写时钟不同
真双口RAM(True Dual-port RAM):两个端口都可读、写,且两端口的时钟不同
单端口和双端口RAM的区别

参数描述

先是端口信号描述

Signal Direction Width(bits) Description
rstninput1RAM异步复位
wclkinput1写时钟
wr_eninput1写使能
wdatainput`WDATA_WIDTH写数据
waddrinput`WADDR_WIDTH写地址
rclkinput1读时钟
rd_eninput1读使能
rdataoutput`RDATA_WIDTH读数据
raddrinput`RADDR_WIDTH读地址

之后是对伪双口RAM配置参数描述

Parameter Units Description
`WDATA_WIDTHbit伪双口RAM写数据宽度
`RDATA_WIDTHbit伪双口RAM读数据宽度
`WADDR_WIDTHbit伪双口RAM写地址宽度,需要根据FIFO深度和写数据宽度计算得出
`RADDR_WIDTHbit伪双口RAM读地址宽度,需要根据FIFO深度和读数据宽度计算得出

例如设置FIFO写数宽16bit,FIFO读数宽8bit,FIFO深度为64bit。
那么RAM写地址应该有64/16=4个,读地址有64/8=8个,所以任意一个写地址可分为两个读地址,如下图
在这里插入图片描述

2.3. 写逻辑 wr_logic

写逻辑这边主要实现对伪双口RAM的写控制,同时基于读逻辑模块的rptr信号产生FIFO的full和pfull信号

参数描述

端口信号描述

Signal Direction Width(bits) Description
rstninput1异步复位
wclkinput1写时钟
wr_eninput1写使能
wdatainput`WDATA_WIDTH写数据
rptr_ginput`RADDR_WIDTH+1带扩展位的Grey码读指针,注意是rclk域下的信号
Signal Direction Width(bits) Description
wr_en_ramoutput1双口RAM的写使能
waddr_ramoutput`WADDR_WIDTH双口RAM的写地址
wdata_ramoutput`WDATA_WIDTH双口RAM的写数据
fulloutput1FIFO写满标志
pfulloutput1FIFO可编程写满标志
wptr_goutput`WADDR_WIDTH+1带扩展位的Grey码写指针

之后是需要配置的参数描述

Parameter Units Description
`WDATA_WIDTHbit伪双口RAM写数据宽度
`WADDR_WIDTHbit伪双口RAM写地址宽度,需要根据FIFO深度和写数据宽度计算得出
`RADDR_WIDTHbit伪双口RAM读地址宽度,需要根据FIFO深度和读数据宽度计算得出

2.4. 读逻辑 rd_logic

读逻辑这边主要实现对伪双口RAM的读控制,同时基于写逻辑模块的wptr信号产生FIFO的empty信号

参数描述

端口信号描述

Signal Direction Width(bits) Description
rstninput1异步复位
rclkinput1读时钟
rd_eninput1读使能
rdata_raminput`RDATA_WIDTH读数据,来自于RAM
wptr_ginput`WADDR_WIDTH+1带扩展位的Grey码写指针,注意是wclk域下的信号
Signal Direction Width(bits) Description
rd_en_ramoutput1送入RAM的读使能
raddr_ramoutput`RADDR_WIDTH双口RAM的读地址
rdataoutput`RDATA_WIDTHFIFO读数据
emptyoutput1FIFO读空标志
rptr_goutput`RADDR_WIDTH带扩展位的Grey码读指针

之后是需要配置的参数描述

Parameter Units Description
`RDATA_WIDTHbit伪双口RAM读数据宽度
`RADDR_WIDTHbit伪双口RAM读地址宽度,需要根据FIFO深度和读数据宽度计算得出
`WADDR_WIDTHbit伪双口RAM写地址宽度,需要根据FIFO深度和写数据宽度计算得出

3. 时序与代码

3.1. dual_port_ram

伪双口RAM的读写时序比较好说,采样到写就写入,采样到读就读出。

但是要考虑读写地址位宽的不同,此处采用大小地址的方案,即每个少位宽的大地址由多个多位宽的小地址组成,

如下图所示例子

在这里插入图片描述

注意此处RAM地址并不是Grey码形式的,而是二进制形式。异步FIFO中的Grey码是为了写指针和读指针跨时钟域传输时,避免采样到中间值而设定的。

代码

module dual_port_ram(
	input 					 rstn,
	
	input 					 wclk,
	input 					 wr_en,
	input [`WDATA_WIDTH-1:0] wdata,
	input [`WADDR_WIDTH-1:0] waddr,							
	
	input 					 rclk,
	input 					 rd_en,
	input [`RADDR_WIDTH-1:0] raddr,							
	output [`RDATA_WIDTH-1:0] rdata,
	);
	
generate if(`WADDR_WIDTH > `RADDR_WIDTH)								//写为小地址,读为大地址

localparam SHRINK = 1<<(`WADDR_WIDTH -`RADDR_WIDTH);
localparam HIGH_ADDR_WIDTH = `WADDR_WIDTH;
localparam HIGH_DATA_WIDTH = `WDATA_WIDTH;
localparam LOW_ADDR_WIDTH = `RADDR_WIDTH;
localparam LOW_DATA_WIDTH = `RDATA_WIDTH;

reg [HIGH_DATA_WIDTH-1:0] mem [(1<<HIGH_ADDR_WIDTH)-1:0];
reg [HIGH_ADDR_WIDTH-1:0] low_addrs [SHRINK-1:0];
integer i,j,k;

always@(posedge wclk) begin
	if(wr_en)					
		mem[waddr] <= wdata;											//直接写入即可
end

always@(posedge rclk) begin
	if(rd_en) begin														//对该大地址的每个小地址分别读出
		for(j = 0;j <= SHRINK - 1;j = j + 1) begin
			rdata[(`RDATA_WIDTH/SHRINK)*(j+1)-1 -:(`RDATA_WIDTH/SHRINK)] <= mem[low_addrs[j]];	
			mem[low_addrs[j]] <= 0;	
	end
end

always@(*) begin
	for(k = 0;k <= SHRINK - 1;k = k + 1)
		low_addrs[k] = raddr * SHRINK + k;
end

else																			//写为大地址,读为小地址

localparam SHRINK = 1<<(`RADDR_WIDTH -`WADDR_WIDTH);
localparam HIGH_ADDR_WIDTH = `RADDR_WIDTH;
localparam HIGH_DATA_WIDTH = `RDATA_WIDTH;
localparam LOW_ADDR_WIDTH = `WADDR_WIDTH;
localparam LOW_DATA_WIDTH = `WDATA_WIDTH;

reg [HIGH_DATA_WIDTH-1:0] mem [(1<<HIGH_ADDR_WIDTH)-1:0];
reg [HIGH_ADDR_WIDTH-1:0] low_addrs [SHRINK-1:0];
integer i,j,k;

always@(posedge wclk) begin
	if(wr_en) begin																//对该大地址的每个小地址分别写入							
		for(j = 0;j <= SHRINK - 1;j = j + 1)
			mem[low_addrs[j]] <= wdata[(`WDATA_WIDTH/SHRINK)*(j+1)-1 -:(`WDATA_WIDTH/SHRINK)];			
	end
end

always@(posedge rclk) begin
	if(rd_en) begin																//直接读出即可
		rdata <= mem[raddr];
		mem[raddr] <= 0;
	end
end

always@(*) begin
	for(k = 0;k <= SHRINK - 1;k = k + 1)
		low_addrs[k] = waddr * SHRINK + k;
end

endgenerate

always@(negedge rstn) begin
	if(!rstn) begin
		for(i = 0;i < (1<<HIGH_ADDR_WIDTH);i = i + 1)
			mem[i] <= 0;
	end
end
	
endmodule

3.2. wr_logic 与 rd_logic

三个功能,生成对RAM的读写地址和使能,同时根据写指针和读指针判断FIFO是为full状态还是empty状态

● RAM 读使能与写使能

这个好说,联合FIFO读写使能和空满标志即可

assign wr_en_ram = wr_en & (!full);
assign rd_en_ram = rd_en & (!empty);

● full与empty判定:读写指针的扩展位

首先我们需要两个指针,分别表示下次写入的RAM地址,即写指针下次读出的RAM地址,即读指针

无论读指针还是写指针,当一个指针指向的小地址恰好为另一个指针指向的大地址的第一个小地址时,此时RAM要么写满要么读空。

那么如何分辨呢?方法是,将读写指针的位数扩展一位,每当走过一个FIFO深度时,该bit翻转

扩展位相同,则读空empty有效,扩展位不同,则写满full有效。

注意full和empty都是组合逻辑。如果FIFO满了还写就会覆盖,FIFO空了还读就是空数

● RAM 读指针 与 写指针

读写指针要与RAM的地址端连接。

复位时,俩指针指向各自的第一个地址。

之后每当wr_en有效时,检查full信号是否有效,若无效就将wdata的数据写入RAM,同时写指针移动一个地址。否则不能向RAM写入,可通过控制RAM的wr_en端口阻止写入

之后每当rd_en有效时,检查empty信号是否有效,若无效就从RAM读出rdata的数据,同时读指针移动一个地址。否则不能从RAM读出,可通过控制RAM的rd_en端口阻止读出

之后根据上述full和empty的逻辑判断空满即可。但是注意读写指针均会涉及跨时钟域传输,该如何处理?

读写指针 跨时钟域传输:带扩展位的Grey码

电平同步与Grey码

电平同步是一定的,但由于读写指针是多bit,所以采样异步信号时,很可能采样到跳变过程的中间态

例如读指针正从01跳变10时被wclk采样了,就可能采样到11,这会导致full判断出错以及其它问题。

所以读写指针按照Grey码变化、传输(wptr_g和rptr_g),并在连接RAM地址端或判断full或empty时再转化为二进制码(wptr_b和rptr_b),如下图

请添加图片描述

还有一个思路就是读写指针按照二进制码变化、连接RAM和判断,只不过是在传输的过程时转化为Grey码
但是这种思路行不通,因为码值转换是组合逻辑实时变化,依然会出现中间态。
例如二进制从01跳变到10,会出中间态11,等价于Grey码从01跳变到11,会出中间态10。故没有本质改变,必须是变化也要按照Grey码变化。
但是反过来就不会。例如Grey码从01跳变到11,无中间态,等价于二进制从01跳变到10,也不会出现中间态。

其实最根本的解决办法是完全抛弃二进制码,全局使用Grey码。但是在已知大地址(Grey码)的情况下,我算不出来出其对应的那个几个小地址(Grey码)

带扩展位的Grey码

纯用Grey码有个问题就是,指针跑完一圈后,扩展位翻转,但其他位也会翻转呀。

例如0000(第一位是扩展位)走到最后一个地址是0100,然后回到第一个地址,按照之前我们的逻辑,此时指针是1000。1000相比0100翻转了两次!

这样我们还是会异步采样到第三态!还得改进改进

改进的思路是,指针走动方向不变,扩展位为0时其余位递增、扩展位为1时其余位递减,如下

RAM内存 第一圈 第二圈 第三圈 第四圈 ...
指针顺序为地址一到八 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码 带扩展位Grey码 带扩展位二进制码
地址一 0 000 0 000 1 100 1 000 0 000 0 000 1 100 1 000 ... ...
地址二 0 001 0 001 1 101 1 001 0 001 0 001 1 101 1 001 ... ...
地址三 0 011 0 010 1 111 1 010 0 011 0 010 1 111 1 010 ... ...
地址四 0 010 0 011 1 110 1 011 0 010 0 011 1 110 1 011 ... ...
地址五 0 110 0 100 1 010 1 100 0 110 0 100 1 010 1 100 ... ...
地址六 0 111 0 101 1 011 1 101 0 111 0 101 1 011 1 101 ... ...
地址七 0 101 0 110 1 001 1 110 0 101 0 110 1 001 1 110 ... ...
地址八 0 100 0 111 1 000 1 111 0 100 0 111 1 000 1 111 ... ...

读写指针 跨时钟域传输:电平同步 带来的延迟

OK方案就此敲定,现在来分析一下最后一个问题,电平同步产生的延迟会不会导致full和empty不是实时响应,导致FIFO满写or空读?

empty有效,但FIFO有数

empty无效,但FIFO为空

full有效,但FIFO非满

full无效,但FIFO已满

4. 综合

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:16:35  更:2022-04-15 00:17:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:28:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码