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的实现:

  1. 用片上内存和读写指针实现;
  2. 用寄存器链和小型状态机实现;

对于小的缓冲区(最多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

我们可以用depthFIFORegister来构建深度为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的状态都在emptyfull之间来回切换,这就意味着FIFO存在每个字两时钟周期的吞吐上限;二是数据需要像气泡一样穿过完整的FIFO,从因此从输如到输出的时延至少为Buffer的深度。对于这两个问题,在下下篇文章中也会得到解决。

吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现相关推荐

  1. 吃透Chisel语言.38.Chisel实战之以FIFO为例(三)——几种FIFO的变体的Chisel实现

    Chisel实战之以FIFO为例(三)--几种FIFO的变体的Chisel实现 上一篇文章对实现串口通信的最小系统做了简单阐述,然后从发送端开始,实现了基于单字节FIFO缓冲区的发送端,接着类似地实现 ...

  2. 吃透Chisel语言.33.Chisel进阶之硬件生成器(二)——Chisel组合逻辑电路生成:以BCD编码表为例

    Chisel进阶之硬件生成器(二)--Chisel组合逻辑电路生成:以BCD编码表为例 上一篇文章我们学习了两种类型的变量在Chisel之中的使用,然后分别介绍了Chisel中四种参数化的方法,对于我 ...

  3. 吃透Chisel语言.01.大家Verilog和VHDL用得好好的,为什么要整个Chisel语言出来?

    大家Verilog和VHDL用得好好的,为什么要整个Chisel语言出来??? 说到数字芯片或微处理器的设计实现,你首先想到的语言是什么?可能会是Verilog和VHDL这种早在上世纪八十年代就开发出 ...

  4. 吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块

    Chisel模块详解(五)--Chisel中使用Verilog模块 上一篇文章讲述了用函数实现轻量级模块的方法,可以大幅度提升编码效率.Chisel中也提供了一些好用的函数,方便我们编写代码,也方便C ...

  5. vhdl语言入门_初学Chisel语言,看这篇就够了:最方便简洁的入门资料整理

    声明:本文是我一个很优秀的学生总结的,放出来供广大chisel语言爱好者参考. Chisel(Constructing Hardware In a Scala Embedded Language)是U ...

  6. 看书标记【R语言 商务数据分析实战6】

    看书标记--关于R语言 chapter 6 6.2 任务实 [R语言 商务数据分析实战6] chapter 6 P2P信用贷款风险控制(用户逾期还款概率模型) 关于数据库的应用+数据清洗+实时数据识别 ...

  7. R语言单变量分析实战:汇总统计(Summary Statistics)、频率表(Frequency Table)、图表(charts: boxplot、histogram、density)

    R语言单变量分析实战:汇总统计(Summary Statistics).频率表(Frequency Table).图表(charts: boxplot.histogram.density) 目录

  8. R语言Box-Cox变换实战(Box-Cox Transformation):将非正态分布数据转换为正态分布数据、计算最佳λ、变换后构建模型

    R语言Box-Cox变换实战(Box-Cox Transformation):将非正态分布数据转换为正态分布数据.计算最佳λ.变换后构建模型 目录

  9. R语言Goldfeld-Quandt检验实战:检验回归模型中是否存在异方差性(heteroscedasticity)、发生了异常差(heteroscedasticity)问题如何解决

    R语言Goldfeld-Quandt检验实战:检验回归模型中是否存在异方差性(heteroscedasticity).发生了异常差(heteroscedasticity)问题如何解决 目录

最新文章

  1. 嵌入式程序员面试时应该知道的16个问题
  2. mysql master thread_mysql innodb master_thread伪代码整理
  3. Java程序员从笨鸟到菜鸟之(八十六)跟我学jquery(二)大话jquery选择器
  4. 一个服务器上配置多个tomcat
  5. JavaWeb调用python脚本(可传参)
  6. python根据ip获取地理位置_使用python根据ip获取目标地理位置信息
  7. Linux 实用命令
  8. Java jdk下载及安装
  9. Failed to load class org.slf4j.impl.StaticLoggerBinder
  10. Java8 - Stream API快速入门
  11. rlocfind matlab,绘制根轨迹的MATLAB函数介绍
  12. 全国地名联动选择的程序|地名三级联动(原创JS无数据库19K优化版)
  13. 086-Python生成Wifi二维码 一扫联网
  14. 浓缩就是精华——21行python实现输入法自动提示(带过程举例,附录也精彩)
  15. 伪装文件病毒分析-流氓软件
  16. 计算机音乐谱子 追光者,岑宁儿《追光者》简谱
  17. 科普 | 抖音服务器带宽有多大,为什么能够供那么多人同时刷?
  18. 《视觉锤》 读书笔记
  19. 大型OA协同办公系统 - 利用力软工作流引擎实现复杂业务流程
  20. 凶残的挖矿脚本,奴役我数千机器!

热门文章

  1. 集成融云直播聊天室(官方集成以及自我总结)
  2. springcloud——zuul
  3. Linux记录用户操作日志
  4. c语言302是什么错误,错误: 程序中有游离的‘\302’ ‘\240’等
  5. ipython的介绍
  6. Python 判断密码等级的强弱,不使用正则表达式
  7. wordpress伪静态如何支持中文(目前不支持分类目录中文)
  8. 现在所在时代处于计算机网络的第几个阶段,计算机进入普及阶段是从()时代起。...
  9. Redis List
  10. 郭金东旗下江苏钟山化工参加2019聚氨酯行业大会并受到表彰