吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现
Chisel实战之以FIFO为例(一)——FIFO Buffer
这一部分,我们将以FIFO以及FIFO的各种变体为例,进行Chisel数字设计的实战。这一部分的实战会以小规模的数字设计为例,比如一个FIFO Buffer,它是大规模数字设计中的常用构件块。再比如串行接口(串口),它本身也可以用到FIFO Buffer。这一篇文章就从最基本的FIFO Buffer开始。
FIFO Buffer概念
写端(发送端)和读端(接收端)之间可以通过在它们之间构建个缓冲区(Buffer)来解耦合。最常见的一种Buffer就是先进先出缓冲区(First-In First-Out Buffer,FIFO Buffer)。下图就展示了发送端(Writer)、FIFO Buffer和接收端(Reader)之间的信号连接:
数据由发送端在write
有效时通过din
放到FIFO里面,而接收端在read
有效时通过dout
来从FIFO中读取数据。
FIFO刚开始是空的,由empty
信号给出空状态。从空的FIFO中读取数据是未定义的行为。当数据一直在往FIFO里面写但是不被读的话,FIFO就会满,由full
信号给出满状态。向一个满的FIFO中写数据通常会被忽略,数据也会被丢弃。换句话来说,empty
信号和full
信号充当了握手信号的角色。
可能有这么两种FIFO的实现:
- 用片上内存和读写指针实现;
- 用寄存器链和小型状态机实现;
对于小的缓冲区(最多10个元素)而言,由独立的寄存器链接到一起形成的缓冲区的链条组织成的FIFO实现起来更简单,需要的资源也很少。
单FIFO Buffer的Chisel实现
我们首先从定义FIFO分别在发送端和接收端侧的IO接口开始,假定数据的尺寸是通过size
可配置的。
首先是发送端侧的接口,规定写数据为din
并由write
信号使能,信号full
指示FIFO的状态,执行发送端的流控制。发送端侧接口代码如下:
class WriterIO(size: Int) extends Bundle {val write = Input(Bool())val full = Output(Bool())val din = Input(UInt(size.W))
}
然后是接收端侧的接口,规定数据从dout
输入并由read
信号使能,信号empty
指示FIFO的状态,并执行接收端的控制流。接收端侧接口代码如下:
class ReaderIO(size: Int) extends Bundle {val read = Input(Bool())val empty = Output(Bool())val dout = Output(UInt(size.W))
}
接下来就可以实现一个简单的单FIFO Buffer了,也就是只能存放一个数据的FIFO缓冲区。这个Buffer有一个WriterIO
类型的入队端口enq
和一个ReaderIO
类型的出队端口deq
。Buffer的状态机是一个保存数据的寄存器dataReg
和一个简单的FSM状态寄存器stateReg
。这个FSM只有两种状态,要么empty
要么full
。如果Buffer是empty
的,给定write
信号会向寄存器中写入输入数据并将状态切换为full
,如果Buffer是full
的,给定read
信号会读出寄存器中的数据并将状态切换为empty
。实现如下:
class FIFORegister(size: Int) extends Module {val io = IO(new Bundle {val enq = new WriterIO(size)val deq = new ReaderIO(size)})val empty :: full :: Nil = Enum(2)val stateReg = RegInit(empty)val dataReg = RegInit(0.U(size.W))when(stateReg === empty) {when(io.enq.write) {stateReg := fulldataReg := io.enq.din}} .elsewhen(stateReg === full) {when(io.deq.read) {stateReg := emptydataReg := 0.U // 单纯方便在波形图中看是不是空了}} .otherwise {// 也应该没有“否则”的状态了}io.enq.full := stateReg === fullio.deq.empty := stateReg === emptyio.deq.dout := dataReg
}
用单FIFO构建完整的FIFO(Bubble FIFO)
上面的FIFORegister
的缓冲区只能存放一个数据,这一小节我们实现可定义深度的FIFO,即完整的FIFO,将会实现为BubbleFifo
类,它的接口和FIFORegister
的接口是一样的,但是它有两个参数,一个是用于给定数据宽度的size
,另一个是用于给定Buffer深度的depth
。
我们可以用depth
个FIFORegister
来构建深度为depth
的气泡FIFO Buffer。我们将这些单个的Buffer放到一个Scala的Array
里面来创建多Buffer。不过Scala的数组没有硬件意义,只是提供了一个用来引用被创建的Buffer的容器。然后我们可以用一个for
循环将这些Buffer链接起来。第一个Buffer的入队端链接到完整的FIFO的入队IO上,同样,最后一个Buffer的出队端也链接到完整的FIFO的出队IO上。实现代码如下:
class BubbleFifo(size: Int, depth: Int) extends Module {val io = IO(new Bundle {val enq = new WriterIO(size)val deq = new ReaderIO(size)})val buffers = Array.fill(depth) {Module(new FIFORegister(size))}for (i <- 0 until depth - 1) {buffers(i + 1).io.enq.din := buffers(i).io.deq.doutbuffers(i + 1).io.enq.write := buffers(i).io.deq.readbuffers(i).io.deq.read := ~buffers(i + 1).io.enq.full}io.enq <> buffers(0).io.enqio.deq <> buffers(depth - 1).io.deq
}
这个通过把多个单Buffer串联起来实现FIFO的方法叫做气泡FIFO,即Bubble FIFO,因为数据跟气泡一样穿过队列。
结语
这一篇文章介绍了FIFO Buffer的概念,然后用Chisel实现了单Buffer的FIFO,接着又用单Buffer实现了完整的FIFO Buffer,即气泡Buffer。这种方法很简单,在数据率低于时钟频率的时候很好用,比如在作为串口的解耦合缓冲区的时候,下一篇文章就会介绍在串口中使用Bubble FIFO。然而在数据率达到时钟频率的时候,Bubble FIFO就存在两个限制了:一是因为每个Buffer的状态都在empty
和full
之间来回切换,这就意味着FIFO存在每个字两时钟周期的吞吐上限;二是数据需要像气泡一样穿过完整的FIFO,从因此从输如到输出的时延至少为Buffer的深度。对于这两个问题,在下下篇文章中也会得到解决。
吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现相关推荐
- 吃透Chisel语言.38.Chisel实战之以FIFO为例(三)——几种FIFO的变体的Chisel实现
Chisel实战之以FIFO为例(三)--几种FIFO的变体的Chisel实现 上一篇文章对实现串口通信的最小系统做了简单阐述,然后从发送端开始,实现了基于单字节FIFO缓冲区的发送端,接着类似地实现 ...
- 吃透Chisel语言.33.Chisel进阶之硬件生成器(二)——Chisel组合逻辑电路生成:以BCD编码表为例
Chisel进阶之硬件生成器(二)--Chisel组合逻辑电路生成:以BCD编码表为例 上一篇文章我们学习了两种类型的变量在Chisel之中的使用,然后分别介绍了Chisel中四种参数化的方法,对于我 ...
- 吃透Chisel语言.01.大家Verilog和VHDL用得好好的,为什么要整个Chisel语言出来?
大家Verilog和VHDL用得好好的,为什么要整个Chisel语言出来??? 说到数字芯片或微处理器的设计实现,你首先想到的语言是什么?可能会是Verilog和VHDL这种早在上世纪八十年代就开发出 ...
- 吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块
Chisel模块详解(五)--Chisel中使用Verilog模块 上一篇文章讲述了用函数实现轻量级模块的方法,可以大幅度提升编码效率.Chisel中也提供了一些好用的函数,方便我们编写代码,也方便C ...
- vhdl语言入门_初学Chisel语言,看这篇就够了:最方便简洁的入门资料整理
声明:本文是我一个很优秀的学生总结的,放出来供广大chisel语言爱好者参考. Chisel(Constructing Hardware In a Scala Embedded Language)是U ...
- 看书标记【R语言 商务数据分析实战6】
看书标记--关于R语言 chapter 6 6.2 任务实 [R语言 商务数据分析实战6] chapter 6 P2P信用贷款风险控制(用户逾期还款概率模型) 关于数据库的应用+数据清洗+实时数据识别 ...
- R语言单变量分析实战:汇总统计(Summary Statistics)、频率表(Frequency Table)、图表(charts: boxplot、histogram、density)
R语言单变量分析实战:汇总统计(Summary Statistics).频率表(Frequency Table).图表(charts: boxplot.histogram.density) 目录
- R语言Box-Cox变换实战(Box-Cox Transformation):将非正态分布数据转换为正态分布数据、计算最佳λ、变换后构建模型
R语言Box-Cox变换实战(Box-Cox Transformation):将非正态分布数据转换为正态分布数据.计算最佳λ.变换后构建模型 目录
- R语言Goldfeld-Quandt检验实战:检验回归模型中是否存在异方差性(heteroscedasticity)、发生了异常差(heteroscedasticity)问题如何解决
R语言Goldfeld-Quandt检验实战:检验回归模型中是否存在异方差性(heteroscedasticity).发生了异常差(heteroscedasticity)问题如何解决 目录
最新文章
- 嵌入式程序员面试时应该知道的16个问题
- mysql master thread_mysql innodb master_thread伪代码整理
- Java程序员从笨鸟到菜鸟之(八十六)跟我学jquery(二)大话jquery选择器
- 一个服务器上配置多个tomcat
- JavaWeb调用python脚本(可传参)
- python根据ip获取地理位置_使用python根据ip获取目标地理位置信息
- Linux 实用命令
- Java jdk下载及安装
- Failed to load class org.slf4j.impl.StaticLoggerBinder
- Java8 - Stream API快速入门
- rlocfind matlab,绘制根轨迹的MATLAB函数介绍
- 全国地名联动选择的程序|地名三级联动(原创JS无数据库19K优化版)
- 086-Python生成Wifi二维码 一扫联网
- 浓缩就是精华——21行python实现输入法自动提示(带过程举例,附录也精彩)
- 伪装文件病毒分析-流氓软件
- 计算机音乐谱子 追光者,岑宁儿《追光者》简谱
- 科普 | 抖音服务器带宽有多大,为什么能够供那么多人同时刷?
- 《视觉锤》 读书笔记
- 大型OA协同办公系统 - 利用力软工作流引擎实现复杂业务流程
- 凶残的挖矿脚本,奴役我数千机器!
热门文章
- 集成融云直播聊天室(官方集成以及自我总结)
- springcloud——zuul
- Linux记录用户操作日志
- c语言302是什么错误,错误: 程序中有游离的‘\302’ ‘\240’等
- ipython的介绍
- Python 判断密码等级的强弱,不使用正则表达式
- wordpress伪静态如何支持中文(目前不支持分类目录中文)
- 现在所在时代处于计算机网络的第几个阶段,计算机进入普及阶段是从()时代起。...
- Redis List
- 郭金东旗下江苏钟山化工参加2019聚氨酯行业大会并受到表彰