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.dinValidio.dinReady := fsm.io.dinReadyio.popCountValid := fsm.io.popCountValidfsm.io.popCountReady := io.popCountReady// 数据通路和顶层接口的连接data.io.din := io.dinio.popCount := data.io.popCount// 数据通路和fsm之间的连接data.io.load := fsm.io.loadfsm.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.Uwhen (!done) {counterReg := counterReg - 1.U}when (io.load) {dataReg := io.dinpopCountReg := 0.UcounterReg := 8.U}// 调试用printf("%b %d\t", dataReg, popCountReg)io.popCount := popCountRegio.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.Bio.dinReady := false.Bio.popCountValid := false.Bswitch(stateReg) {is(idle) {io.dinReady := true.Bwhen(io.dinValid) {io.load := true.BstateReg := count}}is(count) {when(io.done) {stateReg := done}}is(done) {io.popCountValid := true.Bwhen(io.popCountReady) {stateReg := idle}}}// 调试用printf("state: %b\n", stateReg)
}

这一部分的代码和之前状态机的代码类似,就不解释了。下面是测试代码:

import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpecclass 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:  00   0    state:  0
10010011   0    state:  11001001   1    state:  1100100   2    state:  110010   2    state:  11001   2    state:  1100   3    state:  110   3    state:  11   3    state:  10   4    state:  10   4    state: 100   4    state: 100   4    state: 100   4    state:  0
10010011   0    state:  11001001   1    state:  1

测试通过。

结语

这一篇文章以Popcount为例,介绍了带数据通路的有限状态机FSMD的写法与实现,对于后面写复杂的系统有很关键的指导意义。我们可以注意到,在FSMD的实现中,状态机之间的通信我们使用了Ready-Valid握手协议,这是一种常见的通信接口协议,但每次都这么写显然有点复杂。而Chisel中自带了Ready-Valid相关的函数DecoupledIO,用于对数据信号进行Ready-Valid协议的封装,下一篇文章我们就来学习这个重要又方便的函数。

吃透Chisel语言.30.Chisel进阶之通信状态机(二)——FSMD:以Popcount为例相关推荐

  1. 吃透Chisel语言.31.Chisel进阶之通信状态机(三)——Ready-Valid接口:定义、时序和Chisel中的实现

    Chisel进阶之通信状态机(三)--Ready-Valid接口:定义.时序和Chisel中的实现 上一篇文章以Popcount为例,介绍了带数据通路的有限状态机FSMD的写法与实现,对于后面写复杂的 ...

  2. 吃透Chisel语言.29.Chisel进阶之通信状态机(一)——通信状态机:以闪光灯为例

    Chisel进阶之通信状态机(一)--通信状态机:以闪光灯为例 上一部分我们学习了单个有限状态机的Chisel描述方法,但是单个有限状态机通常很难描述稍微有点复杂的数字设计.在这种情况下,可以把问题划 ...

  3. 吃透Chisel语言.32.Chisel进阶之硬件生成器(一)——Chisel中的参数化

    Chisel进阶之硬件生成器(一)--Chisel中的参数化 Chisel区别于其他硬件描述语言的最强大的地方在于,我们可以用Chisel写硬件生成器.对于老一点的硬件描述语言,比如VHDL和Veri ...

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

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

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

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

  6. 吃透Chisel语言.39.Chisel实战之单周期RISC-V处理器实现(上)——需求分析和初步设计

    Chisel实战之单周期RISC-V处理器实现(上)--需求分析和初步设计 需求分析 首先明确我们要做的是什么,这个在标题里面已经说明了,我们要做的是一个单周期RISC-V处理器. 但光是个短语不足以 ...

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

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

  8. 吃透Chisel语言.15.Chisel模块详解(二)——Chisel模块嵌套和ALU实现

    Chisel模块详解(二)--Chisel模块嵌套和ALU实现 稍微复杂点的硬件设计就需要用嵌套的模块层级来构建了,上一篇文章中实现的计数器其实就是个例子,计数器内部嵌套了一个寄存器.一个Mux和一个 ...

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

    Chisel实战之以FIFO为例(一)--FIFO Buffer 这一部分,我们将以FIFO以及FIFO的各种变体为例,进行Chisel数字设计的实战.这一部分的实战会以小规模的数字设计为例,比如一个 ...

最新文章

  1. Python加密—AES加密(2)
  2. Unity中对象池的使用
  3. DevExpress 11.1.6 重编译详细过程
  4. 性能之巅:Linux网络性能分析工具-netstat,ifconfig,nicstat,traceroute,tcpdump
  5. linux设备模型之Class
  6. kfaka storm写入mysql_flume+kafka+storm+mysql架构设计
  7. 北京Php月收入2w,给你北京户口,前提要辞掉月薪2w的工作,在月薪5千左右的岗位干10年,你干吗?...
  8. 原生态mysql_MySQL 原生操作-速查
  9. 神经网络用作分类器(附代码matlab)
  10. MVC 中通用导出页面数据到Excel
  11. Reading Thinking in Java #3
  12. 激活工具也带毒,一批携带病毒的“小马激活工具”被火绒拦截
  13. adb 通过WiFi连接小米8手机
  14. win10 linux重置密码,Win10重置Linux子系统用户密码教程
  15. python抓取微博评论破亿_《战狼Ⅱ》破50亿 Python爬虫抓取获取12万条影评分析看它在说...
  16. Dynamics CRM2013 Server2012R2下部署ADFS和IFD遇到的问题
  17. vulnhub之FirstBlood: 1
  18. 小米手机的刷机经验教训
  19. K12526 找双亲和孩子
  20. 文件夹打包成pkg_linux如何解压tar.gz到指定文件夹或目录

热门文章

  1. 【Java】Java8特性官网学习之Stream的前世今生
  2. java学习之路 之 多线程练习题
  3. navicat导入数据库脚本文件报错集合
  4. BTS测试实验室 --- 注入关攻略
  5. 基于STM32单片机智能自动伸缩衣架雨滴重量光强温度检测伸缩速度可调-蓝牙版
  6. C语言之标准(KRC 、c89、c99、c11)
  7. 使用powerdesigner生成物理模型图
  8. [TFF学习]官方教程jupyter运行记录_联邦学习之图像分类任务_1
  9. 使用以太坊geth客户端调用智能合约时报“Invalid Address“错误的解决方法
  10. QQ游戏百万人同时在线服务器架构实现