吃透Chisel语言.29.Chisel进阶之通信状态机(一)——通信状态机:以闪光灯为例
Chisel进阶之通信状态机(一)——通信状态机:以闪光灯为例
上一部分我们学习了单个有限状态机的Chisel描述方法,但是单个有限状态机通常很难描述稍微有点复杂的数字设计。在这种情况下,可以把问题划分为两个或更多的小的、简单的FSM。这个FSM之间使用信号进行通信,某个FSM的输出是另一个FSM的输入,这个FSM又观察另一个FSM的输出。把一个大的FSM分割成简单的小FSM的做法叫作分解FSM。通信FSM通常直接根据设计规格直接设计,因为如果用首先单个FSM实现设计的话就太大了。这一篇文章就一起来学习通信状态机的设计实现。
通信FSM——以闪光灯为例
为了讨论通信FSM,我们使用闪光灯的例子,这个闪光灯有一个输入start
和一个输出light
,该闪光灯的设计规格如下:
- 当
start
在一个周期内为高电平时,闪烁开始; - 会闪烁三次;
- 一次闪烁中,
light
为on
持续6个周期,为off
持续4个周期; - 序列之后,FSM会将
light
置为off
并等待下一个start
;
直接用一个FSM实现这个闪光灯的话,该FSM会有27中状态:1个是等待输入的初始状态,3×63\times 63×6个关于on
状态的状态,2×42\times 42×4个关于off
状态的状态,共27中状态。我们这里就不写这种实现的代码了。
那我们可以把这个大FSM分解为两个小FSM:一个主FSM用于实现闪烁逻辑,一个定时器FSM用于实现等待,下图展示了这两个FSM之间的组合:
定时器FSM会倒数6个周期或4个周期,用于生成想要的时序,定时器的规格如下:
- 当
timerLoad
被设置的时候,定时器加载一个值到倒数计数器,不依赖于状态; timerSelect
会选择在加载5或3;timerDone
会在计数器完成倒数的时候被设置并保持被设置状态;- 其他情况,定时器会进行倒数。
下面的代码就展示了该闪光灯的定时器FSM和主FSM实现:
val timerReg = RegInit(0.U)// 计时器连接
val timerLoad = WireDefault(false.B) // 给load信号后启动定时器
val timerSelect = WireDefault(true.B) // 选择6周期循环或4周期循环
val timerDone = Wire(Bool())timerDone := timerReg === 0.U
timerLoad := timerDone// 定时器FSM(倒数计数器)
when(!timerDone) {timerReg := timerReg - 1.U
}
when(timerLoad) {when(timerSelect) {timerReg := 5.U} .otherwise {timerReg := 3.U}
}val off :: flash1 :: space1 :: flash2 :: space2 :: flash3 :: Nil = Enum(6)
val stateReg = RegInit(off)val light = WireDefault(false.B) // FSM的输出// 主FSM
switch(stateReg) {is(off) {timerLoad := true.BtimerSelect := true.Bwhen (start) {stateReg := flash1}}is (flash1) {timerSelect := false.Blight := true.Bwhen (timerDone) { stateReg := space1}}is (space1) {when (timerDone) { stateReg := flash2}}is (flash2) {timerSelect := false.Blight := true.Bwhen (timerDone) { stateReg := space2}}is (space2) {when (timerDone) { stateReg := flash3}}is (flash3) {timerSelect := false.Blight := true.Bwhen (timerDone) { stateReg := off}}
}
代码就不解释了,就提示一点,switch语句里面没有赋值的wire
都会赋默认值,所有space1
和space2
里面可以省略timerSelect
和light
的赋值,但建议最好别这么写。
不过这个解决方案在主FSM中还是有冗余代码,flash1
、flash2
和flash3
状态的功能一致,space1
和space2
也是如此。我们可以把这几种flash
状态分解到第二个计数器里面,那么主FSM就只有三个状态了off
、flash
和space
。下图就展示了新的通信FSM设计:
现在图里面有两个计数FSM了,一个用于计数on
和off
间隔的时钟周期数,另一个用于计数剩下的flash
次数。下面的代码就是向下计数FSM部分:
val cntReg = RegInit(0.U)
cntDone := cntReg === 0.U// 向下计数FSM
when(cntLoad) {cntReg := 2.U}
when(cntDecr) {cntReg := cntReg - 1.U}
注意计数器加载了2以闪烁三次,因为它会倒数剩下的闪烁次数,并在定时器完成是递减到space
状态。下面的代码展示了完整的新的闪光灯FSM实现:
import chisel3._
import chisel3.util._class SimpleFsm extends Module {val io = IO(new Bundle {val start = Input(Bool())val light = Output(Bool())val state = Output(UInt()) // 用于调试})val light = WireDefault(false.B)val timerLoad = WireDefault(false.B)val timerSelect = WireDefault(true.B)val timerDone = Wire(Bool())val timerReg = RegInit(0.U)timerDone := timerReg === 0.UtimerLoad := timerDonewhen(!timerDone) {timerReg := timerReg - 1.U}when(timerLoad) {when(timerSelect) {timerReg := 5.U}.otherwise {timerReg := 3.U}}val cntLoad = WireDefault(false.B)val cntDecr = WireDefault(true.B)val cntDone = Wire(Bool())val cntReg = RegInit(0.U)cntDone := cntReg === 0.Uwhen(cntLoad) { cntReg := 2.U }when(cntDecr) { cntReg := cntReg - 1.U }val off :: flash :: space :: Nil = Enum(3)val stateReg = RegInit(off)switch(stateReg) {is(off) {timerLoad := true.BtimerSelect := true.BcntLoad := true.Bwhen(io.start) { stateReg := flash }}is(flash) {cntLoad := false.BtimerSelect := false.Blight := true.Bwhen(timerDone & !cntDone) { stateReg := space }when(timerDone & cntDone) { stateReg := off }}is(space) {cntDecr := timerDonewhen(timerDone) { stateReg := flash }}}io.light := lightio.state := stateReg
}object MyModule extends App {println(getVerilogString(new SimpleFsm()))
}
除了将主FSM的状态减少为3中以外,我们新的配置也更加可配置了。没有任何一个FSM会需要在我们想改变on
、off
时长的时候被修改。
测试代码如下:
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpecclass SimpleTestExpect extends AnyFlatSpec with ChiselScalatestTester {"DUT" should "pass" in {test(new SimpleFsm) { dut =>dut.clock.step()println(dut.io.state.peekInt(), dut.io.light.peekBoolean())dut.io.start.poke(true.B)for (a <- 0 until 50) {dut.clock.step()dut.io.start.poke(false.B)println(a, dut.io.state.peekInt(), dut.io.light.peekBoolean())}}}
}
观察测试的输出,可知测试通过。
结语
这一篇文章我们以闪光灯电路为例,探索了通信电路,尤其是通信FSM的写法,FSM之间仅交换了控制信号。不过电路之间还可以交换数据,对于数据的协调交换,我们需要使用握手信号,这一部分的最后一篇文章我们就会一起学习用于单向数据交换的控制流的ready-valid
接口。下一篇文章,我们将会学习带数据通路的状态机,从手动指定ready-valid
信号开始。
吃透Chisel语言.29.Chisel进阶之通信状态机(一)——通信状态机:以闪光灯为例相关推荐
- 吃透Chisel语言.30.Chisel进阶之通信状态机(二)——FSMD:以Popcount为例
Chisel进阶之通信状态机(二)--FSMD:以Popcount为例 上一篇文章以闪光灯为例,介绍了通信状态机的写法,用于将大的复杂的状态机分解为小的多个相互通信的状态机来实现,可以保证使用资源更少 ...
- 吃透Chisel语言.32.Chisel进阶之硬件生成器(一)——Chisel中的参数化
Chisel进阶之硬件生成器(一)--Chisel中的参数化 Chisel区别于其他硬件描述语言的最强大的地方在于,我们可以用Chisel写硬件生成器.对于老一点的硬件描述语言,比如VHDL和Veri ...
- 吃透Chisel语言.31.Chisel进阶之通信状态机(三)——Ready-Valid接口:定义、时序和Chisel中的实现
Chisel进阶之通信状态机(三)--Ready-Valid接口:定义.时序和Chisel中的实现 上一篇文章以Popcount为例,介绍了带数据通路的有限状态机FSMD的写法与实现,对于后面写复杂的 ...
- 吃透Chisel语言.33.Chisel进阶之硬件生成器(二)——Chisel组合逻辑电路生成:以BCD编码表为例
Chisel进阶之硬件生成器(二)--Chisel组合逻辑电路生成:以BCD编码表为例 上一篇文章我们学习了两种类型的变量在Chisel之中的使用,然后分别介绍了Chisel中四种参数化的方法,对于我 ...
- 吃透Chisel语言.38.Chisel实战之以FIFO为例(三)——几种FIFO的变体的Chisel实现
Chisel实战之以FIFO为例(三)--几种FIFO的变体的Chisel实现 上一篇文章对实现串口通信的最小系统做了简单阐述,然后从发送端开始,实现了基于单字节FIFO缓冲区的发送端,接着类似地实现 ...
- 吃透Chisel语言.39.Chisel实战之单周期RISC-V处理器实现(上)——需求分析和初步设计
Chisel实战之单周期RISC-V处理器实现(上)--需求分析和初步设计 需求分析 首先明确我们要做的是什么,这个在标题里面已经说明了,我们要做的是一个单周期RISC-V处理器. 但光是个短语不足以 ...
- 吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块
Chisel模块详解(五)--Chisel中使用Verilog模块 上一篇文章讲述了用函数实现轻量级模块的方法,可以大幅度提升编码效率.Chisel中也提供了一些好用的函数,方便我们编写代码,也方便C ...
- 吃透Chisel语言.15.Chisel模块详解(二)——Chisel模块嵌套和ALU实现
Chisel模块详解(二)--Chisel模块嵌套和ALU实现 稍微复杂点的硬件设计就需要用嵌套的模块层级来构建了,上一篇文章中实现的计数器其实就是个例子,计数器内部嵌套了一个寄存器.一个Mux和一个 ...
- 吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现
Chisel实战之以FIFO为例(一)--FIFO Buffer 这一部分,我们将以FIFO以及FIFO的各种变体为例,进行Chisel数字设计的实战.这一部分的实战会以小规模的数字设计为例,比如一个 ...
最新文章
- (天国之扉文章抢救) 1/10/2003 总结?总结!
- android 如何使用android:supportsRtl属性
- C++学习笔记:(六)public、protected、private继承详解
- MySQL查询select实例 【笔记】
- js 在html中新建个节点,javascript节点是什么?
- Accerlerated c++ 答案
- C++ vector和list的区别
- swift网络编程入门应用:天气预报
- 如何下载并使用别人写的库——jupyter notebook
- 吉林大学超星MOOC学习通高级语言程序设计 C++ 实验00 熟悉开发环境(2021级)
- java实现二维码的生成与解析
- 如何设计并实施CRM与ERP的整合方案?
- 迅雷链总工程师来鑫:区块链3.0需解决4大难题
- 02年六代雅阁的整备质量_雅阁对不起,我不能爱你。
- 河北软件职业技术学院计算机应用工程系,河北软件职业技术学院第四届模特大赛举办成功...
- 实现手机app控制威联通QNAP的NAS设备中Aria2启动运行
- labview与单片机正弦信号_labview与单片机串口通信
- AFMG全球战略发布会,一场与豪华纯电智能跑车的零距离之约
- selenium webdriver的testNG框架的介绍及使用
- Vista下的UAC功能
热门文章
- facade设计模式
- 18天1000万次迭代,AI生成的照片你能分辨真假吗?
- sql注入pythonpoco_SQL注入原理与解决方法代码示例
- 网站建设有哪些核心要点和方法
- stm32f103VCT6全新机器 做stm32开发板 原理图以及主要器件规格书
- sct分散加载文件格式与应用
- (centos7-x86)编译安装zabbix6.0LTS+Mariadb10.5+Apache+php7.4【安装完整版】
- 抽象工厂模式(三):抽象工厂模式概述
- 清华大学计算机系2015分数线,2015年清华大学录取分数线
- tinyint 范围