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的设计中,由于二进制的计数值跨时钟域时有可能出现亚稳态,所以可以使用格雷码来解决这个问题。
  • 格雷码特点:
  1. 相邻格雷码之间只有一位变化,其他位相同,所以可以降低亚稳态出现的概率。
  2. 格雷码是循环码,0 和 2^n-1 之间只有一位不同。当 FIFO 深度是 2 次幂时,可以满足用格
    雷码消除亚稳态。

写满、读空判断

在读时钟域进行空状态判断,在写时钟域进行满状态判断。

  • 写满:将读时钟域的读指针同步到写时钟域,被同步的读指针与写时钟域的写指针高两位不一致,其余完全相同。
  • 读空:将写时钟域的二进制写指针转化为格雷码经过两级触发器同步到读时钟域,被同步的写指针与读时钟域的读指针每一位完全相同。

异步FIFO读指针属于读时钟域,写指针属于写时钟域,读写时钟域不同,如下所示:

在这里插入图片描述
判断机制:
最高位与次高位相同,其余位相同则为读空。
最高位与次高位不同,其余位相同则为写满。

电路结构

在这里插入图片描述

  • 在电路中,使用了一个双端口SRAM来作为FIFO的memory,用来存储上游节点的写数据wdata,然后在下游节点读出rdata。此外,SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出的顺序进行,写和读到最高地址后,重新返回零地址。
  • 满信号生成电路:在上游节点和SRAM之间有一个满信号生成电路。这个电路通过判断写时钟域下写指针和读指针的关系,实时生成满信号full以通知上游节点停止写操作。
  • 空信号生成电路:在下游节点和SRAM之间有一个空信号生成电路。这个电路通过判断读时钟域下写指针和读指针的关系,实时生成空信号empty以通知下游节点停止读操作。
  • 将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号。
  • 正确地产生空满标志是任何FIFO设计的关键,空满状态产生的原则是:写满而不溢出,读空而不多读。

代码

module async_fifo
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3) 
(
//write interface
input 	 					wr_clk,
input  						wr_rst_n_i,
input 	    				wr_en_i,
input  	   [DATA_WIDTH-1:0] wr_data_i,
//read interface
input   					rd_clk,
input   					rd_rst_n_i,
input   					rd_en_i,
output reg [DATA_WIDTH-1:0] rd_data_o,
//flags
output 						full_o,
output 						empty_o
);
 
//读写指针
reg [ADDR_WIDTH:0] wr_ptr;     //写指针
reg [ADDR_WIDTH:0] rd_ptr;     //读指针

wire FIFO_DEPTH = 1 << ADDR_WIDTH;//2^3=8
 
// RAM  (fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8)
reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];

 
 
 //写指针   二进制->格雷码
 //格雷码写地址
reg [ADDR_WIDTH:0]  gray_wr_ptr; //格雷码写地址
reg [ADDR_WIDTH:0]  gray_wr_ptr_next;//格雷码写地址同步1拍
 
// 格雷码写地址同步到读时钟
reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍
 
 
 //读地址  二进制->格雷码
 //格雷码读地址
reg [ADDR_WIDTH:0]  gray_rd_ptr; //格雷码读地址
reg [ADDR_WIDTH:0]  gray_rd_ptr_next; //格雷码读地址同步1拍
 
 //格雷码读地址同步到写时钟
reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍
 
//组合逻辑空满标志
wire full_comb;
wire empty_comb;
 
interger i;

//读写地址指针产生
//写指针
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
		wr_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(wr_en_i && !full_o)
		wr_ptr <= wr_ptr +1'b1;
   else 
   		 wr_ptr <= wr_ptr;
//读指针
always@(posedge rd_clk or negedge rd_rst_n_i )
   if(!rd_rst_n_i)
		rd_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(rd_en_i && !empty_o)
		rd_ptr <= rd_ptr +1'b1;
   else 
   		 rd_ptr <= rd_ptr;
 
//二进制码->格雷码
//格雷码写地址
assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
always@(*)
	begin
		gray_wr_ptr_next= gray_wr_ptr;
	end
 
//格雷码写地址同步两级  传递到读时钟域
always@(posedge rd_clk or negedge rd_rst_n)
	begin
		if(!rd_rst_n)
			begin
				gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
				gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
			end
		else 
			begin
				gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
				gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
			end
	end
//格雷码读地址
assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
always@(*)
	begin
		gray_rd_ptr_next= gray_rd_ptr;
   	end
//格雷码读地址同步两级  传递到写时钟域
always@(posedge wr_clk or negedge wr_rst_n)
	begin
  		if(!wr_rst_n)
  			begin
  				gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  				gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  			end
 		 else 
 			begin
  				gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
				gray_rd2wr_ptr_2 <=  gray_rd2wr_ptr_1;
 			end
	end
  
//空满标志产生
//满标志
assign full_comb =( {~(gray_rd2wr_ptr_2[ADDR_WIDTH:ADDR_WIDTH-2]),(gray_rd2wr_ptr_2[ADDR_WIDTH-2:0])}== gray_wr_ptr);
always@(posedge wr_clk or negedge wr_rst_n)
	begin
	  	if(!wr_rst_n)
	  		full_o <= 1'b0;
	  	else
	  	    full_o <= full_comb;
	end
  
//空标志
assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr);
always@(posedge rd_clk or negedge rd_rst_n)
	begin
	  	if(!rd_rst_n)
	  		 empty_o <=1'b0;
	  	else
	  	  	 empty_o <= empty_comb;
	end
 

//write data
always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
  	  begin
  		  for(i=0;i<FIFO_DEPTH;i=i+1)begin
  		  	RAM[i] <= 0;
  		  end	
  		end		
  	else if(wr_en_i && !full_comb)
			RAM[wr_ptr] <= wr_data_i;
//read data
always@(posedge rd_clk or negedge rd_rst_n_i)
  	if(!rd_rst_n_i)
  	  begin
  		  	rd_data_o <= 0;
  		end		
  	else if(rd_en_i && !empty_comb)
			rd_data_o <= RAM[rd_ptr];
 
endmodule

FIFO深度计算

在设计FIFO深度时需要分析轻载和重载时数据的传输任务,一般来说,应该考虑FIFO在重载时的性能,如果其能在重载时满足需求,轻载的时候肯定也没问题。
举例:
FIFO的写时钟为100MHz,读时钟为80MHz。在FIFO输入侧,每100个写时钟,写入80个数据;读数据侧,假定每个时钟读走一个数据。问FIFO深度设置多少可以保证FIFO不会上溢出和下溢出?

其数据传输情况如下所示:
在这里插入图片描述
我们假设写入时为最坏情况(背靠背),即在160 X(1/100)微秒内写入160个数据。以下为写入160个burst数据的时间的计算方法:
在这里插入图片描述
在这段时间内只能读出160 X (1/100) X 80个数据。
在这里插入图片描述
FIFO的深度即为burst长度减去读出数据的长度。
在这里插入图片描述
即FIFO深度至少应为32。

具体细节可参考:硬件加速设计方法

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:39:38  更:2022-07-17 16:39:50 
 
开发: 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 0:40:30-

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