Part A: 组合逻辑
组合逻辑描述
在Chisel中,组合逻辑电路最简单的表示方法是将组合逻辑赋值给一个val
,并可以通过这个val
引用。
1 | val e = ( a & b ) | c |
以上表达式右侧的逻辑被命名为e
,对于val
的重新赋值是不允许的。
Chisel当然也支持使用条件语句生成的组合逻辑。这样的电路以Wire
定义(在Verilog中,我们以Reg定义,在[email protected](*)
块中对其赋值)。此后,我们使用条件操作,例如when
,来描述这个电路的行为。
1 | val w = Wire(UInt()) |
when
语句还可以后接otherwise
语句。
1 | when (cond) { |
还可以插入elsewhen
,注意代码中的”.”是必要的。
1 | when (cond) { |
如果cond
仅仅依赖于一个信号,建议还是使用switch
语句。
对于复杂的逻辑,我们应该对Wire赋予初值,以避免锁存器。
1 | val w = WireDefault(0.U) |
需要注意的是,Scala中的if
,else
是控制流代码,是用于控制生成硬件的条件的,并不能用来描述组合逻辑,也并不会生成硬件。
Example: Decoder
Decoder一般可以使用switch
语句来进行描述。
1 | import chisel3.util._ |
注意:就算在switch
里面列举了所有的可能,我们仍然需要给某个信号赋予初值,为了防止生成锁存器的可能,Chisel严禁使用不完备的赋值。
Part B: 时序逻辑
时序逻辑描述:寄存器
在Chisel中,上图寄存器可以被这样描述:
1 | val q = RegNext(d) |
我们可以注意到,这个寄存器并没有显式地声明时钟、声明复位,端口被默认的连接到了全局时钟上,注意:Chisel不支持异步复位。
RegNext会自动根据输入信号推断寄存器的宽度。
还可以使用以下的形式,声明一个寄存器,并赋值:
1 | val q = Reg(UInt(4.W)) |
由于在Chisel中,并不存在阻塞赋值和非阻塞赋值,也不存在
always
块,所以难以某个赋值语句是在对寄存器赋值还是组合逻辑赋值。- 推荐的命名规范是,寄存器信号以
Reg
结尾! - 并且,在Scala推荐的命名规范中,建议采用驼峰命名法。
- 推荐的命名规范是,寄存器信号以
在声明寄存器时,还可以对寄存器指定初始值。
1 | val valReg = RegInit(0.U(4.W)) |
- 声明一个带使能的寄存器:
1 | val enableReg = RegInit(0.U(4.W)) |
- 寄存器甚至可以在表达式中声明,甚至作为表达式的一个部分进行使用,但此时,这是一个匿名的寄存器:
1 | val risingEdge = din & !RegNext(din) |
此处有一个思考,之前使用
val
来定义某个逻辑,不管是寄存器也好,还是组合逻辑也好,本质上是对某个硬件节点进行命名操作。此处在表达式中使用RegNext,相当于创建了一个新的寄存器,并匿名地引用它的值,其用法和组合逻辑有点类似。
Example: Counter
一个最简单的计数器:
1 | val cntReg = RegInit(0.U(8.W)) |
或者,我们可以使用一个多选器来决定计数器的输入:
1 | val cntReg = RegInit(0.U(8.W)) |
再高层次地,我们使用一个计数器生成器:
1 | def genCounter(n: Int) = { |
注意函数的最后一行,代表返回一个创建好的硬件节点。
Counter使用实例
1 | // tick是一个三分频时钟,其占空比不为0.5 |
其中,tick
是一个占空比不为50%的时钟,控制一个更慢的计数器进行更新操作。
更优化的计数器
我们转变思路,将计数器转变为递减计数器。从N-1数到0,转变为N-2数到-1,计满的情况,仅需要看计数器最高位即可。
Example: PWM
PWM调制,时钟占空比随着时间的变化而变化。
以下的代码可以生成占空比为0.7的PWM波形:
1 | def pwm(nrCycles: Int, din: UInt) = { |
上述代码即为一个可复用的、轻量的代码。它接收两个参数,一个是时钟周期的数量(nrCycles
),另一个是给出了脉冲宽度(din
)。
注意:函数的最后一行是返回值。
1 | val FREQ = 100000000 // a 100 MHz clock input |
Example: 移位寄存器
1 | val shiftReg = Reg(UInt(4.W)) |
Cat
用于串接输入输出,对于Reg和Wire的位选,使用括号,也就是Scala中的Apply()
方法。
存储器
存储器可以用寄存器阵列进行构建,也可以使用SRAM。在ASIC设计中,使用memory compiler
进行设计;在FPGA设计中,片上拥有block RAM资源。
Chisel提供了存储器构建器SyncReadMem
,对于同时读写一个地址,Chisel将其定义为未定义的行为,此时,我们可以手动进行旁路。
1 | class MemoryWithForwarding() extends Module { |
Chisel也提供了Mem
,异步读,同步写,类似于FPGA中的distributed RAM
。