一个更大的数字设计通常以分层的方式被构造成一组组件。每个组件都有一个带有输入和输出线的接口,有时称为端口。这些类似于集成电路(IC)上的输入和输出引脚。各部件通过输入和输出接线连接。组件可以包含子组件以构建层次结构。连接到芯片上的物理引脚的最外层组件称为顶层组件。
Chisel中的组件是Modules
硬件组件在Chisel中称为模块。每个模块扩展类Module并包含接口的字段io。接口由包装到IO()调用中的Bundle定义。Bundle包含表示模块的输入和输出端口的字段。通过将字段包装到对Input()或Output()的调用中来给出方向。方向是从零部件本身的视图开始。
我们展示了一个示例设计,其中我们使用两个组件构建计数器:加法器和寄存器。
图4.1显示了加法器组件的原理图。它有两个输入(a和B)和一个输出(y)。清单4.1显示了加法器的Chisel定义。输入和输出信号用点表示法访问,例如io. a,因为它们是io Bundle的一部分。
图4.2显示了另一个简单的组件,一个8位寄存器。这个组件的Chisel代码如清单4.2所示。
现在,我们用这两个组件构建一个计数器,从0到9计数并重复。图4.3显示了计数器的原理图。我们使用加法器将count的值加1。多路复用器在该和与0之间进行选择。
嵌套组件
从中到高复杂度的硬件设计是由嵌套组件的层次结构构建的。图4.4显示了这种示例设计的结构。组件C具有三个输入端口和两个输出端口。组件本身由两个子组件组装而成:B和C,其连接到C的输入和输出。A的一个输出连接到B的一个输入。组件D与组件C处于相同的层次结构级别,并连接到组件C。
算术逻辑单元
计算电路的核心部件之一,例如,微处理器是一个算术逻辑单元,简称ALU。图4.5显示了ALU的符号。
ALU有两个数据输入,在图中标记为a和b,一个函数输入fn,以及一个输出,标记为y。ALU对a和b进行运算,并在输出端提供结果。输入fn选择对a和b的操作。运算通常是一些算术运算,如加法和减法,以及一些逻辑函数,如与、或、异或。这就是为什么它被称为ALU。
ALU通常是没有任何状态元素的组合电路。ALU还可以具有附加输出以用信号通知结果的属性,诸如零或符号。
以下代码显示了一个具有16位输入和输出的ALU,支持:加法、减法、或、与和运算,由2位fn信号选择。
class Alu extends Module{
val io = IO(new Bundle{
val a = Input(UInt(16.W))
val b = Input(UInt(16.W))
val fn = Input(UInt(2.W))
val y = Output(UInt(16.W))
})
io.y := 0.U
switch(io.fn){
is(0.U) {io.y := io.a + io.b}
is(1.U) {io.y := io.a - io.b}
is(2.U) {io.y := io.a | io.b}
is(3.U) {io.y := io.a & io.b}
}
}
批量连接
为了连接具有多个IO端口的组件,Chisel提供了批量连接操作符<>。这个操作符在两个方向上连接束的部分。Chisel使用叶字段的名称进行连接。如果名称丢失,则表示未连接。
fetch.io <> decode.io
decode.io <> execute.io
io <> execute.io
外部模块
因为Chisel的功能相对Verilog来说还不完善,所以设计人员在当前版本下无法实现的功能,就需要用Verilog来实现。在这种情况下,可以使用Chisel的BlackBox功能,它的作用就是向Chisel代码提供了用Verilog设计的电路的接口,使得Chisel层面的代码可以通过模块的端口来进行交互。