当前位置:网站首页>吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现
吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现
2022-08-10 11:36:00 【github-3rr0r】
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 := full
dataReg := io.enq.din
}
} .elsewhen(stateReg === full) {
when(io.deq.read) {
stateReg := empty
dataReg := 0.U // 单纯方便在波形图中看是不是空了
}
} .otherwise {
// 也应该没有“否则”的状态了
}
io.enq.full := stateReg === full
io.deq.empty := stateReg === empty
io.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.dout
buffers(i + 1).io.enq.write := buffers(i).io.deq.read
buffers(i).io.deq.read := ~buffers(i + 1).io.enq.full
}
io.enq <> buffers(0).io.enq
io.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的深度。对于这两个问题,在下下篇文章中也会得到解决。
边栏推荐
- 搜索--09
- LeetCode 138. 复制带随机指针的链表
- 网络基础(第一节)
- Licking Exercise - 58 Verifying Binary Search Trees
- Since the media hot style title how to write?Taught you how to write the title
- LeetCode50天刷题计划(Day 18—— 搜索旋转排序数组(8.50-12.00)
- LeetCode 21. Merge two ordered linked lists
- 因为找不到lombok而找不到符号log
- 态路小课堂丨如何为CXP光模块选择光纤跳线?
- codevs 2370 Small room tree (LCA)
猜你喜欢

个推数据资产管理经验 | 教你打造数据质量心电图,智能检测数据“心跳”异常

再有人问你分布式事务,把这篇扔给他

Does your child lack self-discipline?Ape Counseling: Pay attention to "blank" in the schedule to give children more control

VSCode remote connection server error: Could not establish connection to "xxxxxx" possible error reasons and solutions

LeetCode50天刷题计划(Day 18—— 搜索旋转排序数组(8.50-12.00)
Database management tool: dynamic read-write separation

Flutter气泡框实现

So delicious!Since using this interface artifact, my team efficiency has increased by 60%!

石墨文档打开文档时快速定位到上次写的位置

mpf6_Time Series Data_quandl_correct kernel PCA_AIC_BIC_trend_log_return_seasonal_decompose_sARIMAx_ADFull
随机推荐
制品库是什么?
SMIC CIM localization project suspended?Rising software: not shut down, changed to remote development!
一文读懂NFT数字藏品为何风靡全球?
LeetCode 369. Plus One Linked List
Licking Exercise - 60 Maximum key-value sum of binary search subtrees
LeetCode 61. Rotating linked list
codevs 2370 Small room tree (LCA)
leetcode 823. Binary Trees With Factors(因子二叉树)
第六届”蓝帽杯“全国大学生网络安全技能大赛半决赛部分WriteUp
7. Instant-ngp
It is rumored that Samsung 3nm has won the second customer, and the current production capacity is in short supply
Analysis of the implementation principle of UUID from the perspective of source code
Excel function formulas - LOOKUP function
【LeetCode】640. 求解方程
LT8911EXB MIPI CSI/DSI转EDP信号转换
ssm框架搭建过程[通俗易懂]
LeetCode 369. Plus One Linked List(链表加1)
CodeForces - 628D (数位dp)
IDC第一的背后,阿里云在打造怎样的一朵“视频云”?
LeetCode 86. 分隔链表