异步FIFO的设计
所谓异步是指写时钟是完全独立并且不一致的,或者不同频率,或者同频但不同相。读地址和空标志是由读时钟产生的,而写地址和满标志则由写时钟产生,当要产生FIFO的空、满标志时,必须进行读写地址的比较时,问题就来临了。如果直接采样地址比较的话,地址线一般有多位,由于每个地址寄存器的物理空间位置的不一致性,造成写地址的每一位在写时钟作用下,跳变得不一致,即产生毛刺,要过一小段时间才能稳定。在未稳定期内,刚好读时钟进行采样写地址,如果正好读写地址一样,这时就出现误判断,逻辑错误。同时采样读写地址相差N个来产生空满信号,时间上会多一些,因为涉及加和减操作。
为避免地址跳变的不一致造成读写地址误判断,采样格雷码。该码的优点是相邻两值只有一位跳变,其他不变。这样地址变化的时间较短,极大提高比较精度。
格雷码是一种做加一运算时,只变化一位的编码码,下表即为一个三位格雷码编码格式。
格雷码映射表
可见,格雷码每次只在相邻的位发生变化,这种编码带来的好处是它可避免前面介绍的因线延迟不一致而引起的毛刺现象。
图1 格雷码FIFO的基本原理图
运用格雷码编写异步FIFO的基本思想如上图所示:
图2 格雷码的时序
格雷码是不能进行加减产生空满标志的,所以采样延时一拍的方法。用读地址Rd_addr产生读地址的格雷码Rd_next_gray_addr,将Rd_next_gray_addr延一拍得到Rd_gray_addr再将Rd_gray_addr延一拍得到Rd_last_gray_addr你会发现在绝对时间上
Rd_next_gray_addr、Rd_gray_addr、Rd_last_gray_addr这个地址有先后关系从大到小排列,并且相差如图所示写地址的格雷码的产生也类似,即Wt_next_gray_addr 、Wt_gray_addr 、Wt_last_gray_addr利用这6个格雷码进行比较 ,同时加上读写使能 ,就能表示空和满标志。
空满标志的产生
先说空标志Empty,当读写格雷码地址相等(Rd_gray_addr== Wt_gray_addr )或者FIFO内还剩下一个深度的字(Rd_next_gray_addr== Wt_gray_addr ),并且正在不空的情况下执行读操作Read_enable==1,这时Empty标志应该置为有效 (高电平有效) ,如下图所示
图3 空标志信号产生示意图
图4 满标志信号产生示意图
满标志full
当写FIFO的格雷码地址等于上次读的格雷码地址时
(Wt_gray_addr==Rd_last_gray_addr),或者下次要写的格雷码地址等于上次读的格雷码地址(Wt_next_gray_addr==Rd_last_gray_addr ),并且正在执行写操作 ,此时需要置Full标志有效。为了避免复杂的逻辑,提高FIFO的整体速度 ,可使用FIFO深度=实际深度-1,如上图3所示。
如果要产生几乎空、几乎满标志时,可以多做几个格雷码的延时地址,利用这些读、写格雷码地址距离远近关系就可以灵活的产生特定读写地址间距的几乎空或几乎满标志。如果需要在大间距内时,产生几乎空满信号(比如读写地址相差10),那必须采用另外一种方法:以几乎满为例。
一 、首先得到FIFO已经有多少个未读数据,只要使用写地址减去读地址就可以了。原因是结果和两个读写地址的位宽一样,借位和进位不理会,读者可以自己验算一下。
二 、异步电路要产生精确的几乎空和几乎满比较难,都有一定的误差,但是具体情况(已知读、写时钟频率)可以做到误差很小。
下面给出了几乎满的控制逻辑,首先将读地址read_addr变成格雷码Rd_truegray(相对于读时钟) ,再用写时钟同步该读地址的格雷码Rd_truegray得到Rag_wt_syn(相对于写时钟)。
然后将同步后的格雷码Rag_wt_syn变成二进制的地址Ra_write_syn,最后用写地址的延时一拍Wt_addr_p1减去同步后的地址Ra_write_syn(为什么写地址延时,是为保证同一时刻的读写地址比较), 就得到比较精确的FIFO有效数据的个数Fifo_status。利用该个数与FIFO的可用深度(自己决定 ,高溢出水线) 进行比较得到几乎满。这就是范围较大的比较。
代码:
always @(posedge read_clock or posedge fifo_gsr)begin
if(fifo_gsr)begin
Rd_truegray <= 8'h0;
end
else begin
Rd_truegray <=#1 {
(read_addr[7]),(read_addr[7]^read_addr[6]),
(read_addr[6]^read_addr[5]),(read_addr[5]^read_addr[4]),
(read_addr[4]^read_addr[3]),(read_addr[3]^read_addr[2]),
(read_addr[2]^read_addr[1]),(read_addr[1]^read_addr[0]) };
end
end
always @(posedge write_clock or posedge fifo_gsr)begin
if(fifo_gsr)begin
Rag_wt_syn <= 8'h0;
end
else begin
Rag_wt_syn <=#1 Rd_truegray;
end
end
wire Ra_7_5 = Rag_wt_syn[7] ^ Rag_wt_syn[6] ^ Rag_wt_syn[5] ;
wire Ra_7_4 = Rag_wt_syn[7] ^ Rag_wt_syn[6] ^ Rag_wt_syn[5] ^ Rag_wt_syn[4];
wire Ra_3_1 = Rag_wt_syn[3] ^ Rag_wt_syn[2] ^ Rag_wt_syn[1] ;
assign Ra_write_syn[7] = Rag_wt_syn[7]; //7
assign Ra_write_syn[6] = Rag_wt_syn[7] ^ Rag_wt_syn[6]; //6
assign Ra_write_syn[5] = Ra_7_5; //5
assign Ra_write_syn[4] = Ra_7_4 ; //4
assign Ra_write_syn[3] = Ra_7_4 ^ Rag_wt_syn[3] ; //3
assign Ra_write_syn[2] = Ra_7_4 ^ Rag_wt_syn[3] ^ Rag_wt_syn[2]; //2
assign Ra_write_syn[1] = Ra_7_4 ^ Ra_3_1 ; //1
assign Ra_write_syn[0] = Ra_7_5 ^ Ra_3_1 ^ Rag_wt_syn[4] ^ Rag_wt_syn[0]; //0
always @(posedge write_clock or posedge fifo_gsr)begin
if(fifo_gsr)begin
Wt_addr_p1 <=#1 0;
end
else begin
Wt_addr_p1 <=#1 write_addr ;
end
end
always @(posedge write_clock or posedge fifo_gsr)begin
if(fifo_gsr)begin
Fifo_status <= 8'h0;
end
else begin //if(!Full)
Fifo_status <= Wt_addr_p1 - Ra_write_syn;
end
end
always @(posedge write_clock or posedge fifo_gsr)begin
if(fifo_gsr)begin
Almostfull <=#1 1'b0;
end
else if (Fifo_status[7:4] == 4'hF)begin
Almostfull <=#1 1'b1;
end
else begin
Almostfull <=#1 1'b0;
end
end
三、几乎空、与几乎满类似,, 由读时钟产生。
四、几乎满相对于写时钟而言,同理几乎空相对读时钟,这样产生的精确度比较高如果几乎满和几乎空用同一个时钟,误差与两个时钟的具体大小相关,比如几乎满和几乎空都是用写时钟得到的,那么几乎满比较精确,如果写时钟比读时钟慢,则几乎空误差较大,如果写时钟比读时钟快,几乎空的误差也比较精确。
读写地址的产生
如果外部写使能(Write_enable )来了,同时FIFO的不满的话,写地址自增1 。如果读使能(Read_enable )有效 ,同时FIFO不空的话,读地址自减1。在进行地址的递增时判断FIFO的空满标志是为了自我保护,避免读写地址交错,产生错误逻辑。
图5 FIFO的自我保护机制
END
- 这或许是我见过的最简单的正则表达式教程(一)
- Linux下如何安装和卸载JDK
- 讨厌算法的程序员 2 - 证明算法的正确性
- Day4上午解题报告
- [编程经验] Python正则表达式
- Day4下午解题报告
- linux下 Error running javac compiler
- 讨厌算法的程序员 1 - 插入排序
- Linux下使用ssh密钥实现无交互备份
- [编程经验] Python中的continue和break语句
- 洛谷 P3386 【模板】二分图匹配 Dinic版
- [编程经验] 拉勾网爬虫数据的后续处理
- Linux下使用rsync实现文件备份
- 【干货】基于TensorFlow卷积神经网络的短期股票预测
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- SpringCloud2020 学习笔记(一)springboot和springcloud技术选型以及版本选择
- SpringCloud2020 学习笔记(二)父工程搭建
- SpringCloud2020 学习笔记(三) cloud-api-commons通用模块搭建
- SpringCloud2020 学习笔记(四) cloud-provider-payment8001支付模块
- SpringCloud2020 学习笔记(五)cloud-consumer-order80 消费者订单模块
- SpringCloud2020 学习笔记(六)如何开启idea中的Run DashBoard or Services
- SpringCloud2020 学习笔记(七)cloud-eureka-server7001 EurekaServer服务端安装
- SpringCloud2020 学习笔记(八)cloud-provider-payment8001支付模块入驻eurekaServer注册中心
- SpringCloud2020 学习笔记(九)cloud-consumer-order80 消费者订单模块入驻eurekaServer注册中心
- SpringCloud2020 学习笔记(十)cloud-eureka-server7001 cloud-eureka-server7002 Eureka集群安装
- Python爬虫之抓取某东苹果手机评价
- python学习笔记
- 必看!!!python列表( 增 删 改 查),超详细讲解!!
- Python识别验证码
- 多线程爪巴虫下载进击的巨人