当前位置:网站首页>吃透Chisel语言.30.Chisel进阶之通信状态机(二)——FSMD:以Popcount为例
吃透Chisel语言.30.Chisel进阶之通信状态机(二)——FSMD:以Popcount为例
2022-08-02 06:45:00 【github-3rr0r】
Chisel进阶之通信状态机(二)——FSMD:以Popcount为例
上一篇文章以闪光灯为例,介绍了通信状态机的写法,用于将大的复杂的状态机分解为小的多个相互通信的状态机来实现,可以保证使用资源更少,维护、修改也更容易。不过上一篇文章中的通信状态机之间的通信都是控制信号,还未涉及数据信号。这一篇文章就一起学习带数据通路的状态机,并以Popcount计数器为例进行介绍。
带数据通路的状态机
通信状态机的典型例子就是带数据通路的状态机,这种状态机有专门的名字,即FSMD(Finite-State Machine with Datapath,带数据通路的有限状态机)。其中状态机控制数据通路,数据通路执行计算。FSMD的输入有来自环境的输入和来自数据通路的输入,其中来自环境的输入会输入到数据通路,数据通路又生成数据。下图就是一个典型的FSMD:

Popcount的例子
上图的例子其实就是个计算Popcount的FSMD,这个Popcount也叫Hamming Weight(汉明权重),指的是一个二进制串中1的数量。
Popcount单元包含数据输入din和结果输出popCount,两个都连接到数据通路。对于输入输出,我们使用ready-valid握手协议。当发送端数据有效的时候,valid信号被设置,当接受端可以接受数据的时候,ready信号被设置。当两个信号都被设置的时候,数据传输就会发生。握手信号连接到FSM上,FSM连接到数据通路上,包括FSM到数据通路的控制信号和数据通路到FSM的状态信号。
下一步我们就可以设计这个FSM了,首先从状态转换图开始,也就是上面给出的例子图。FSM从Idle状态开始,等待输入。当数据到达的时候,会给出一个valid信号,FSM会进入到Load状态来读取一个移位寄存器。然后FSM进入下一个状态Count,这里二进制串中的1会被顺序计数。这里我们使用一个移位寄存器,一个加法器,一个累加器寄存器,以及一个向下计数器来完成计数。当向下计数器到零的时候,计算就完成了,FSM进入下一个状态Done,此时带valid信号的FSM信号就给出了,FSM信号中包含了将要被使用的popCount值。当收到了接收端的ready信号后,FSM就转移到Idle状态,准备计算下一个popCount。
下面的代码是顶层模块的描述,会对FSM和数据部分进行初始化,并将他们连接起来:
class PopCount extends Module {
val io = IO(new Bundle {
// 输入数据有效
val dinValid = Input(Bool())
// 可以接收数据
val dinReady = Output(Bool())
// 输入数据
val din = Input(UInt(8.W))
// 输出结果有效
val popCountValid = Output(Bool())
// 可以输出数据
val popCountReady = Input(Bool())
// 输出结果
val popCount = Output(UInt(4.W))
})
// fsm部分
val fsm = Module(new PopCountFSM)
// 数据通路部分
val data = Module(new PopCountDataPath)
// fsm和顶层接口的连接
fsm.io.dinValid := io.dinValid
io.dinReady := fsm.io.dinReady
io.popCountValid := fsm.io.popCountValid
fsm.io.popCountReady := io.popCountReady
// 数据通路和顶层接口的连接
data.io.din := io.din
io.popCount := data.io.popCount
// 数据通路和fsm之间的连接
data.io.load := fsm.io.load
fsm.io.done := data.io.done
}
注释简单地说明了顶层模块代码的含义,这里就不多说了。下面开始看数据通路的构造,下图是数据通路部分的示意图:

数据din首先输入到shf寄存器中。在加载数据的时候,cnt寄存器置零。为了统计1的数量,regData寄存会右移(图片中的shf),最低有效位在每个时钟周期都加到regPopCount上(图片中的cnt)。还有个寄存器图中没有画出来,它执行倒数计数,直到输入中所有的位都以最低有效位的形式移出,计数器为0的时候就表明popCount计算结束了。此时FSM会切换到Done状态,在popCountReady信号被设置时输出结果信号。当结果被读取时,通过设置popCountValid信号输出数据并让FSM切换回Idle状态。下面是数据通路部分的Chisel代码实现:
class PopCountDataPath extends Module {
val io = IO(new Bundle {
val din = Input(UInt(8.W))
val load = Input(Bool())
val popCount = Output(UInt(4.W))
val done = Output(Bool())
})
val dataReg = RegInit(0.U(8.W))
val popCountReg = RegInit(0.U(4.W))
val counterReg = RegInit(0.U(4.W))
dataReg := 0.U ## dataReg(7, 1)
popCountReg := popCountReg + dataReg(0)
val done = counterReg === 0.U
when (!done) {
counterReg := counterReg - 1.U
}
when (io.load) {
dataReg := io.din
popCountReg := 0.U
counterReg := 8.U
}
// 调试用
printf("%b %d\t", dataReg, popCountReg)
io.popCount := popCountReg
io.done := done
}
在load信号有效时,regData寄存器会加载输入,regPopCount寄存器会复位到0,计数寄存器regCount会设置为需要被移位的位数。否则,regData寄存右移,被移出的最低有效位会加到regPopCount寄存器上,倒数计数器regCount自减一。当计数器为零时,regPopCount的值就是要计算的popCount。
而PopCountFSM有三种状态,从idle开始。当输入数据有效信号dinValid被设置时,FSM会切换到count状态,并等待数据通路完成计算,当popCount有效时,FSM切换到done状态,直到接收到popCntReady信号并传送完数据,再切换为idle状态,等待下一轮计算。FSM部分的Chisel实现如下:
class PopCountFSM extends Module {
val io = IO(new Bundle {
val dinValid = Input(Bool())
val dinReady = Output(Bool())
val popCountValid = Output(Bool())
val popCountReady = Input(Bool())
val load = Output(Bool())
val done = Input(Bool())
})
val idle :: count :: done :: Nil = Enum(3)
val stateReg = RegInit(idle)
io.load := false.B
io.dinReady := false.B
io.popCountValid := false.B
switch(stateReg) {
is(idle) {
io.dinReady := true.B
when(io.dinValid) {
io.load := true.B
stateReg := count
}
}
is(count) {
when(io.done) {
stateReg := done
}
}
is(done) {
io.popCountValid := true.B
when(io.popCountReady) {
stateReg := idle
}
}
}
// 调试用
printf("state: %b\n", stateReg)
}
这一部分的代码和之前状态机的代码类似,就不解释了。下面是测试代码:
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class SimpleTestExpect extends AnyFlatSpec with ChiselScalatestTester {
"DUT" should "pass" in {
test(new PopCount) {
dut =>
dut.clock.step()
dut.io.din.poke("b10010011".U)
dut.io.dinValid.poke(true.B)
for (a <- 0 until 12) {
dut.clock.step()
}
dut.io.popCountReady.poke(true.B)
dut.clock.step()
dut.clock.step()
dut.clock.step()
dut.clock.step()
}
}
}
输出如下:
0 0 state: 0
0 0 state: 0
10010011 0 state: 1
1001001 1 state: 1
100100 2 state: 1
10010 2 state: 1
1001 2 state: 1
100 3 state: 1
10 3 state: 1
1 3 state: 1
0 4 state: 1
0 4 state: 10
0 4 state: 10
0 4 state: 10
0 4 state: 0
10010011 0 state: 1
1001001 1 state: 1
测试通过。
结语
这一篇文章以Popcount为例,介绍了带数据通路的有限状态机FSMD的写法与实现,对于后面写复杂的系统有很关键的指导意义。我们可以注意到,在FSMD的实现中,状态机之间的通信我们使用了Ready-Valid握手协议,这是一种常见的通信接口协议,但每次都这么写显然有点复杂。而Chisel中自带了Ready-Valid相关的函数DecoupledIO,用于对数据信号进行Ready-Valid协议的封装,下一篇文章我们就来学习这个重要又方便的函数。
边栏推荐
- PHP Warning: putenv() has been disabled for security reasons in phar
- 自然语言处理 文本预处理(下)(张量表示、文本数据分析、文本特征处理等)
- request.getSession(), the story
- punch day05
- Servlet
- 关于ue4.27像素流送打包后的本地服务器问题
- PHP Warning: putenv() has been disabled for security reasons in phar
- CAT1 4G+Ethernet development board Tencent cloud mobile phone WeChat applet display temperature and delivery control
- 你认同这个观点吗?大多数企业的数字化都只是为了缓解焦虑
- Gradle系列——Gradle插件(基于Gradle文档7.5)day3-2
猜你喜欢
![(Notes are not completed) [Graph Theory] Traversal of graphs](/img/1d/d2909dcfa0ab5c207005971a7b4a2d.gif)
(Notes are not completed) [Graph Theory] Traversal of graphs

_2_顺序表

Pagoda+FastAdmin 404 Not Found

CAT1 4G+Ethernet development board Tencent cloud mobile phone WeChat applet display temperature and delivery control

结构体大小计算--结构体内存对齐

速看!PMP新考纲、PMBOK第七版解读

交换--STP协议

2020美亚团队赛复盘

【心电信号】基于matlab心率检测【含Matlab源码 1993期】

宝塔+FastAdmin 404 Not Found
随机推荐
SQL执行顺序
每周推荐短视频:为什么产品开发需要数字化?如何做到数字化?
Revitalize rural circular economy and digital chain to link agricultural "ecological chain"
2022夏暑假每日一题(六)
根据一个字段的内容去更新另一个字段的数据,这样的sql语句该怎么样书写
request.getSession(),的故事
Two good php debug tutorials
August 2022 plan, focusing on ue4 video tutorials
Leetcode Weekly 304
Analysis of GCC compiler technology
【云原生】如何快速部署Kubernetes
[Dataset][VOC] Eyewear dataset 6000 in VOC format
CSRF-跨站请求伪造-相关知识
PMP新考纲考试内容介绍
队列题目:无法吃午餐的学生数量
正则表达式的理解学习
数据库概论之MySQL表的增删改查2
punch day05
ADS通信--倍福PLC和C#TextBox控件实现数据绑定的方法
【故障诊断分析】基于matlab FFT轴承故障诊断(包络谱)【含Matlab源码 2002期】