吃透Chisel语言.21.Chisel时序电路(一)——Chisel寄存器(Register)详解

Chisel时序电路(一)——Chisel寄存器(Register)详解

上一部分我们学习了简单的组合电路,这一部分介绍时序电路,与组合电路相比多了时序和状态,但也相比来说也并不是很难的东西。而时序电路对于数字设计来说特别重要,我们当然可以构造一个单周期的复杂系统,比如实现一个单周期的RISC-V处理器。但是由于这一个周期要做的事情太多太多,因此周期很长,时钟频率上不去,性能自然高不了。而时序电路是流水线的基础,通过将复杂电路划分为若干流水线阶段,既可以提高时钟频率,又可以利用指令级并行,能够极大提升数字电路性能。因此,我们必须深刻理解时序,这一篇文章我们就从最基本的寄存器开始。

关于时序电路

时序电路是输出依赖于输入和先前状态的电路,而组合电路的输出仅依赖于输入。由于我们专注于同步设计(带时钟的设计),所以我们谈及时序电路的时候通常都指的是同步时序电路。我们当然也可以用异步逻辑和反馈来构建时序电路,但是这是特定领域的话题,在Chisel中也没法表达异步时序电路。为了构建时序逻辑,我们需要用于存储电路状态信息的元素,这个元素就是寄存器,也就是这部分第一篇文章的内容。

寄存器

构建时序电路最基础的元素就是寄存器。寄存器在数字逻辑电路中,本质上就是个D触发器的集合。D触发器会在时钟的上升沿捕获输入并存放输入作为输出。换句话来说,寄存器会在时钟的上升沿用输入的值更新输出的值。

下面的示意图就是一个寄存器:

在这里插入图片描述

它包含了一个输入D和一个输出Q,而且每个寄存器都有一个clock时钟信号。因为这个全局时钟信号在同步电路中是连接到所有的寄存器的,所以在我们的示意图中一般不会特别标识出来。方框下面的三角形符号就表示时钟输入,而且指示这个一个寄存器。在后面的示意图里面就不显式标识寄存器的时钟信号了。在Chisel中,我们不需要显式地连接寄存器的时钟输入,这一点跟示意图不画同步时钟信号是异曲同工之处。

Chisel中的寄存器

前面以及使用过寄存器了,在Chisel中,输入为d输出为q的寄存器表示如下:

val q = RegNext(d)

需要注意的是,我们不需要给寄存器连接时钟信号,Chisel会隐式地自己连接。寄存器的输入输出可以是任意的复杂类型,比如vector或者bundle这种。

寄存器也可以分两步来定义:

val delayReg = Reg(UInt(4.W))
delayReg := delayIn

首先我们定义寄存器并命名,然后我们连接信号delayIn到寄存的输入。注意寄存器命名的时候应该包含一个Reg后缀,这个命名习惯才好,这主要是为了区分组合电路和时序电路。还需要注意,Scala(也包括Chisel)中的命名都是用的驼峰命名法(CamelCase)。变量名通常小写字母开通,类通常用大写字母开头。

寄存器也可以在复位的时候初始化。reset信号和clock信号一样,在Chisel中是隐式的。我们可以给寄存器一个复位值,比如说零,把这个复位值通过寄存器构造器RegInit的参数给定即可。而寄存器的输入连接到一个Chisel赋值语句。

val valReg = RegInit(0.U(4.W))
valReg := inVal

Chisel中复位信号的默认是实现是同步复位信号。对于同步复位而言不需要对D触发器进行修改,只需要在输入端加一个Mux就行了,这个Mux在复位的初始值和数据之间进行选择。下图就是具有同步复位功能的寄存器示意图,复位信号用于驱动寄存器输入端的Mux:

在这里插入图片描述

Chisel后面也会支持异步复位了,但是相关内容还在开发。不过由于同步复位其实很常用,因此FPGA触发器会包含一个同步复位(和设置)输入信号来不浪费LUT资源用于创建Mux。

寄存器的波形图和时序特性

时序电路的值随着时间的变化而变化,因此他们的行为可以用信号随时间变化的图来描述,这个图就叫做波形图或时序图。下图就是一个具有复位输入并输入了一些数据的寄存器的波形图,时间是从左向右前进的:

在这里插入图片描述

波形图的顶端是驱动电路的时钟信号。在第一个时钟周期,复位之前,寄存器的内容时未定义的,在第二个时钟周期,复位信号置为高,这个时钟周期的上升沿(位置B)寄存器接受了初始值0,而输入信号3被忽略了。在下一个时钟周期,复位信号置为低,inVal的值在下一个时钟周期的上升沿(位置C)捕获。在之后的时钟周期中,复位信号保持为0,在复位信号为0的情况下,寄存器的输出会在寄存器输入的一个周期延时后更新。

波形图是图形化地确定电路行为的牛逼工具,尤其是在更复杂的电路中,比如有很多操作并行执行、数据在电路中流水线地传输等,时序图都很方便。前面讲解过,Chisel测试器也可以在测试中生成波形文件,可用于后面用波形图查看器显示并调试。

带使能信号的寄存器

寄存器的典型设计模式是带使能信号的寄存器,只有在使能信号为true(高电平)的时候,寄存器才会捕获输入值,否则维持旧值。使能信号的实现和同步复位是类似的,也只需要在寄存器的输入端加一个Mux,这个Mux的一个输入是输入数据,另一个输入是寄存器的输出反馈值。下面就是带使能信号的寄存器的示意图:

在这里插入图片描述

因为这也是个常见的设计模式,因此现代FPGA的触发器也都包含一个专用使能信号输入,不需要额外的资源。而下图是带使能信号的寄存器的波形图:

在这里插入图片描述

绝大多数时间,使能信号enable都是高电平的(true),此时寄存器输出会以一个时钟周期延时跟随输入变化。而在第4个时钟周期,使能信号置低电平,此时寄存器输出会在D处的上升沿保持值5

带是使能信号的寄存器可以在Chisel中以几行代码表示,用条件更新语句就行了:

val enableReg = Reg(UInt(4.W))

when (enable) {
    enbaleReg := inVal
}

当然了,因为带使能信号的寄存器太常见了,所以Chisel中也有直接构造这种寄存器的函数RegEnable,它的第一个参数是寄存器输入,第二个参数就是使能信号:

val enableReg2 = RegEnable(inVal, enable)

带使能信号的寄存器也是可以有复位信号的:

val resetEnableReg = RegInit(0.U(4.W))

when (enable) {
    resetEnableReg := inVal
}

这种寄存器也很常见,因此Chisel中的RegEnable函数还有个三参数版本,第一个参数是寄存器输入,第二个参数是复位初始值,而第三个信号则是使能信号:

val resetEnableReg2 = RegEnable(inVal, 0.U(4.W), enable)

寄存器还可以在不命名的情况下作为表达式的一部分,比如下面的电路就可以检测一个信号的上升沿,方法是比较该信号当前值和上个时钟周期的值:

val risingEdge = din & !RegNext(din)

这个表达式中,只有当前时钟周期该信号为1,且上个时钟周期该信号为0时,表达式结果才为1。这个RegNext就是将参数作为输入,返回结果是寄存器的输出。

结语

到目前为止,我们已经讲完了寄存器的所有基本用法。作为时序电路的基础,寄存器非常重要,不是光靠学会基本用法就能拿捏的,而且还有很多特定用法的寄存器,比如计数器、移位寄存器等。从下一篇文章开始,我们将学习计数器、移位寄存器等特殊寄存器,以便更好地从例子中理解寄存器的用法,也为后面打下坚实基础。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值