Skip to content

4 组件

在大型数字设计中,电路通常被组织成一组 组件,这些组件可以分层构建,每个组件都有输入和输出端口,这些端口类似于集成电路(IC)上的引脚。组件通过输入输出信号进行连接,并且可能包含子组件,从而形成层次化的结构。最外层的组件,连接到芯片的物理引脚,称为 顶层组件

本章将详细介绍如何在 Chisel 中描述组件,包括组件的定义、实例化和连接方法。为便于理解,示例代码以简单的模块(如加法器和寄存器)为例进行说明。

4.1 Chisel 中的组件是模块

在 Chisel 中,组件 被称为 模块(Module),模块继承自 Module 类,并定义了一个接口字段 io。接口通过 Bundle 类定义,封装在 IO() 方法中。Bundle 包含输入和输出端口的定义,通过 Input()Output() 来指明端口方向。

端口方向的定义是从模块的视角出发,例如:

  • Input():表示输入端口。
  • Output():表示输出端口。

示例:加法器模块

我们通过一个简单的加法器模块来展示如何定义一个组件,如图 4.1 所示。加法器有两个输入(ab)和一个输出(y)。以下是加法器模块的 Chisel 代码:

scala
class Adder extends Module {
  val io = IO(new Bundle {
    val a = Input(UInt(8.W))    // 8位无符号输入端口a
    val b = Input(UInt(8.W))    // 8位无符号输入端口b
    val y = Output(UInt(8.W))   // 8位无符号输出端口y
  })

  io.y := io.a + io.b           // 输出y等于a与b的和
}

模块解析

  1. 定义接口
    • 使用 Bundle 定义输入和输出端口。
    • 端口方向通过 Input()Output() 指定。
  2. 连接逻辑
    • 使用 := 运算符将加法运算结果赋给输出端口 y
    • io.aio.b 使用 点标记法(dot notation) 访问。
  3. 模块结构
    • 输入端口:ab,各为 8 位无符号整数。
    • 输出端口:y,8 位无符号整数,结果为 a + b

模块的连接和组合

通过将多个模块连接在一起,我们可以构建更复杂的电路。例如,可以将上述的加法器模块与寄存器模块结合,构建一个简单的 计数器。计数器的工作原理为:

  1. 使用加法器模块 对当前计数值加 1。
  2. 使用寄存器模块 存储当前的计数值,并在下一时钟周期更新。

这样的组合展示了 Chisel 设计的灵活性和模块化特性。下一节将介绍如何基于这些基本模块构建层次化的电路系统。

4.2 嵌套组件

在数字设计中,较大的系统通常是由多个子组件逐层嵌套构建的。这种分层结构将复杂的电路设计拆分成更小、更易管理的模块。

本节通过一个 计数器 示例,展示如何将不同组件组合在一起,构建复杂电路的 模块化设计。通过嵌套多个模块,我们可以实现更高级的功能,并提高代码的可重用性。

示例:寄存器组件

我们首先定义一个 寄存器 组件,如 图 4.2 所示,寄存器用于存储数据并在时钟信号的上升沿更新。以下是寄存器的 Chisel 代码:

scala
class Register extends Module {
  val io = IO(new Bundle {
    val d = Input(UInt(8.W))   // 8位输入数据
    val q = Output(UInt(8.W))  // 8位输出数据
  })

  val reg = RegInit(0.U)       // 8位寄存器,初始化为0
  reg := io.d                  // 将输入数据d赋给寄存器
  io.q := reg                  // 输出寄存器数据q
}

组合加法器和寄存器:计数器组件

接下来,我们定义一个 计数器 组件(Count10),该组件由一个加法器和一个寄存器组成:

  • 加法器:实现加 1 操作。
  • 寄存器:存储当前计数值。
  • 复位逻辑:当计数值达到最大值(9)时,复位为 0。

图 4.3 展示了计数器的结构,以下是 Chisel 代码:

scala
class Count10 extends Module {
  val io = IO(new Bundle {
    val dout = Output(UInt(8.W))   // 输出计数值
  })

  val add = Module(new Adder())    // 实例化加法器模块
  val reg = Module(new Register()) // 实例化寄存器模块

  // 读取寄存器的输出
  val count = reg.io.q

  // 加法器输入
  add.io.a := 1.U                  // 加数为常数1
  add.io.b := count                // 另一个加数为当前计数值
  val result = add.io.y            // 加法器的结果

  // 复位逻辑,计数到9后归零
  val next = Mux(count === 9.U, 0.U, result)

  // 将下一个计数值写入寄存器
  reg.io.d := next

  // 输出当前计数值
  io.dout := count
}

代码解析

  1. 模块实例化
    • 使用 Module(new ModuleName()) 实例化子模块。
    • addreg 分别为加法器和寄存器模块的实例。
  2. 信号连接
    • 使用点标记法连接输入和输出信号,例如 add.io.areg.io.q
    • 加法器的输入连接常数 1.U 和寄存器的输出 count
  3. 复位逻辑
    • 使用 Mux 实现复位逻辑,当计数值达到 9 时重置为 0,否则将加法器的结果作为下一个计数值。
  4. 输出
    • 输出当前计数值 count,通过寄存器模块的输出端口 reg.io.q 实现。

嵌套组件的层次结构

通过组合不同模块,我们可以构建更复杂的电路。例如,图 4.4 展示了一个更复杂的设计,包含多个子组件(CompA、CompB、CompC 和 CompD)。这种分层设计可以总结为以下几点:

  • 模块化:将电路分解为独立的子模块,每个模块负责特定功能。
  • 可重用性:模块可以在不同设计中复用,减少代码冗余。
  • 可维护性:分层设计使得电路更易于理解、维护和调试。
  • 层次结构:顶层模块通过实例化子模块实现更复杂的功能,子模块之间通过信号连接。

4.2.1 分层设计的理念

在数字设计中,复杂电路是由多个小组件(模块)通过信号连接组合而成的。每个组件负责特定的功能,这种分工使得设计变得清晰、易于维护。

  • 模块:在 Chisel 中,模块是通过 class Module 定义的,模块拥有输入输出端口(通过 Bundle 定义)。
  • 嵌套模块:一个模块可以包含其他模块(子模块),通过实例化其他模块实现嵌套设计。
  • 信号连接:模块间通过输入输出端口进行信号连接。
  • 顶层模块:最高层的模块是系统的顶层模块,负责将所有子模块组合并连接到输入输出端口。

示例设计:我们通过定义 CompACompBCompCCompD 四个子模块,然后组合成更复杂的模块,最终构建一个 TopLevel 模块。

4.2.2 定义基础组件

组件 A:CompA

组件 A 拥有两个输入端口 inainb,两个输出端口 xy。以下是组件 A 的 Chisel 定义:

scala
class CompA extends Module {
  val io = IO(new Bundle {
    val ina = Input(UInt(8.W))   // 输入端口 a
    val inb = Input(UInt(8.W))   // 输入端口 b
    val x   = Output(UInt(8.W))  // 输出端口 x
    val y   = Output(UInt(8.W))  // 输出端口 y
  })

  // 组件 A 的功能定义
  io.x := io.ina + io.inb        // 计算和,输出到 x
  io.y := io.ina - io.inb        // 计算差,输出到 y
}

组件 B:CompB

组件 B 拥有两个输入端口 in1in2,一个输出端口 out。以下是组件 B 的定义:

scala
class CompB extends Module {
  val io = IO(new Bundle {
    val in1 = Input(UInt(8.W))   // 输入端口 1
    val in2 = Input(UInt(8.W))   // 输入端口 2
    val out = Output(UInt(8.W))  // 输出端口
  })

  // 组件 B 的功能定义
  io.out := io.in1 & io.in2      // 逻辑 AND 操作
}

4.2.3 嵌套组件:组件 C

组件 C 是一个更复杂的模块,它实例化了组件 A 和组件 B,并将它们的信号连接起来。图 4.4 展示了组件 C 的结构。

以下是组件 C 的 Chisel 定义:

scala
class CompC extends Module {
  val io = IO(new Bundle {
    val ina = Input(UInt(8.W))   // 输入端口 a
    val inb = Input(UInt(8.W))   // 输入端口 b
    val inc = Input(UInt(8.W))   // 输入端口 c
    val out = Output(UInt(8.W))  // 输出端口
  })

  // 实例化组件 A 和 B
  val compA = Module(new CompA())
  val compB = Module(new CompB())

  // 连接组件 A
  compA.io.ina := io.ina
  compA.io.inb := io.inb
  val tempX = compA.io.x        // 获取组件 A 的输出 x

  // 连接组件 B
  compB.io.in1 := tempX
  compB.io.in2 := io.inc
  io.out := compB.io.out        // 将组件 B 的输出作为 CompC 的输出
}

解释

  1. 模块实例化:使用 Module(new CompA())Module(new CompB()) 实例化子模块。
  2. 信号传递:通过 io 端口将输入信号 inainb 连接到 CompA,然后将 CompA 的输出 x 传递给 CompB 的输入。
  3. 输出结果:将 CompB 的输出 out 作为 CompC 的最终输出。

组件 D:CompD

组件 D 是一个简单的模块,只有一个输入端口 in 和一个输出端口 out

scala
class CompD extends Module {
  val io = IO(new Bundle {
    val in = Input(UInt(8.W))    // 输入端口
    val out = Output(UInt(8.W))  // 输出端口
  })

  // 组件 D 的功能
  io.out := io.in                // 直接传递输入信号
}

4.2.4 顶层模块:TopLevel

TopLevel 模块将所有子模块组合在一起,并连接信号。以下是顶层模块的定义:

scala
class TopLevel extends Module {
  val io = IO(new Bundle {
    val ina = Input(UInt(8.W))   // 输入端口 a
    val inb = Input(UInt(8.W))   // 输入端口 b
    val inc = Input(UInt(8.W))   // 输入端口 c
    val out = Output(UInt(8.W))  // 输出端口
  })

  // 实例化组件 C 和 D
  val c = Module(new CompC())
  val d = Module(new CompD())

  // 连接组件 C 的输入
  c.io.ina := io.ina
  c.io.inb := io.inb
  c.io.inc := io.inc

  // 连接组件 D,将 C 的输出传递给 D
  d.io.in := c.io.out
  io.out := d.io.out             // 最终输出
}

4.2.5 设计解析

  1. 分层模块设计
    • CompACompB 是基础模块,分别实现加法、减法和逻辑 AND。
    • CompC 将基础模块组合起来,实现更复杂的功能。
    • CompD 是一个简单的信号传递模块。
  2. 顶层模块整合
    • TopLevel 模块将 CompCCompD 组合在一起,完成整个设计。
  3. 信号传递
    • 通过输入端口向子模块提供信号,子模块间的信号通过输出端口传递。
    • 最终的结果输出到顶层模块的输出端口。

4.2.6 总结

嵌套组件设计是 Chisel 编程中的核心思想之一,它使得复杂电路的设计和实现变得更加模块化和可管理。通过逐步组合基础模块,我们可以构建功能强大的系统:

  • 模块重用:基础组件可以在不同设计中重复使用,减少冗余代码。
  • 信号连接:使用 io 端口传递信号,实现模块间通信。
  • 层次结构:通过顶层模块整合多个子模块,形成完整系统。

本节的示例展示了如何定义、组合和嵌套多个模块,为实现更复杂的电路设计提供了基础框架。

4.3 算术逻辑单元 (ALU)

在数字电路中,算术逻辑单元(Arithmetic Logic Unit, ALU)是核心组成部分之一,特别是在微处理器、中央处理单元 (CPU) 等计算电路中。图 4.5 展示了一个 ALU 的符号示意图。

4.3.1 ALU 的基本功能

ALU 的作用是对输入数据进行各种算术和逻辑操作,例如:

  • 算术操作:加法、减法。
  • 逻辑操作:逻辑与(AND)、逻辑或(OR)、逻辑异或(XOR)等。

ALU 的组成

  • 数据输入:通常包含两个数据输入端口,分别标记为 ab
  • 功能选择输入:通过一个功能选择信号(如 fn)来决定具体的运算操作。
  • 输出端口:ALU 的计算结果输出到端口 y

注意:ALU 通常是一个组合逻辑电路,没有时序状态元素。然而,为了便于扩展,ALU 也可以添加额外的输出信号来表示运算结果的属性,例如 零值检测符号位 等。

4.3.2 16 位 ALU 示例

以下 Chisel 代码展示了一个简单的 ALU 设计:

  • 输入端口 ab 为 16 位数据。
  • 功能选择端口 fn 为 2 位数据。
  • 输出端口 y 为 16 位数据。

该 ALU 支持以下四种操作:

  1. fn = 0:加法操作 (a + b)。
  2. fn = 1:减法操作 (a - b)。
  3. fn = 2:逻辑或操作 (a | b)。
  4. fn = 3:逻辑与操作 (a & b)。

代码实现

scala
import chisel3._
import chisel3.util._

class Alu extends Module {
  val io = IO(new Bundle {
    val a  = Input(UInt(16.W))   // 输入数据 a
    val b  = Input(UInt(16.W))   // 输入数据 b
    val fn = Input(UInt(2.W))    // 功能选择信号
    val y  = Output(UInt(16.W))  // 输出数据 y
  })

  // 设置默认输出值
  io.y := 0.U

  // 使用 switch/is 语句实现功能选择
  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 }  // 逻辑与操作
  }
}

4.3.3 Chisel 中的 switch/is 语句

在该设计中,switchis 语句是 Chisel 提供的一种高级构建方法,用于实现多路选择器功能。这种结构在定义组合逻辑的选择表时非常有用。

  1. switch
    • 允许对一个信号进行多分支判断。
    • 等价于其他编程语言中的 case 语句或多路选择结构。
  2. is
    • switch 内部,is 用于匹配信号的具体值,并执行相应的逻辑。

4.3.4 功能解析

  1. 输入/输出定义

    • 通过 Bundle 定义输入输出信号,包括两个 16 位数据输入端口 ab、一个 2 位功能选择输入端口 fn,以及一个 16 位输出端口 y
  2. 默认值

    • 由于 switch 语句是组合逻辑的一部分,为了避免未定义输出导致的锁存器(Latch),我们先将 io.y 设置为默认值 0.U
  3. 操作选择

    • 根据

      io.fn

      的值,选择不同的操作:

      • fn = 0:执行加法 a + b
      • fn = 1:执行减法 a - b
      • fn = 2:执行逻辑或 a | b
      • fn = 3:执行逻辑与 a & b
  4. 模块的可扩展性

    • 通过增加 switch 语句中的分支,可以轻松扩展 ALU 功能,例如添加逻辑异或(XOR)、比较操作(如大于、小于等)。

导入必要的工具包

在使用 switchis 语句时,需要导入 chisel3.util 包,这是 Chisel 提供的实用工具包:

scala
import chisel3.util._

小结

本节通过一个 16 位 ALU 示例,展示了 Chisel 如何实现算术逻辑单元的核心功能。通过 switchis 语句,可以高效地定义多路选择逻辑。该 ALU 设计的特点包括:

  • 结构清晰:输入、输出和功能选择信号的定义一目了然。
  • 功能灵活:可以轻松扩展更多操作。
  • 易于测试:可以使用 ChiselTest 为 ALU 编写单元测试,验证不同功能的正确性。

这种模块化设计方法为实现更复杂的计算单元(如完整的处理器)打下了基础。

4.4 批量连接 (Bulk Connections)

在设计具有多个输入/输出 (IO) 端口的模块时,手动逐一连接信号可能会显得繁琐且容易出错。为了解决这个问题,Chisel 提供了 批量连接操作符 <><> 可以自动连接两个模块中 同名 的端口,而无需逐一手动指定。这一特性极大地简化了层级模块之间的信号连接,特别是在大型设计中。

工作原理

  • <> 会自动匹配两边的 Bundle 中名称相同的字段,并进行连接。
  • 如果某个字段在其中一个 Bundle 中不存在,则该字段会被忽略,不会连接。
  • 这种机制非常适用于模块层次化设计中各个子模块之间的快速连接。

示例:三级流水线设计

假设我们正在实现一个简单的 流水线处理器,包括 取指阶段 (Fetch)译码阶段 (Decode)执行阶段 (Execute)。以下是每个模块的定义和接口。

取指阶段 (Fetch)

scala
class Fetch extends Module {
  val io = IO(new Bundle {
    val instr = Output(UInt(32.W))  // 指令输出
    val pc    = Output(UInt(32.W))  // 程序计数器输出
  })
  // ... Implementation of fetch
}

取指阶段提供两个输出信号:instr(指令)和 pc(程序计数器)。

译码阶段 (Decode)

scala
class Decode extends Module {
  val io = IO(new Bundle {
    val instr = Input(UInt(32.W))   // 指令输入
    val pc    = Input(UInt(32.W))   // 程序计数器输入
    val aluOp = Output(UInt(5.W))   // ALU 操作码输出
    val regA  = Output(UInt(32.W))  // 寄存器 A 输出
    val regB  = Output(UInt(32.W))  // 寄存器 B 输出
  })
  // ... Implementation of decode
}

译码阶段接收取指阶段的 instrpc 作为输入,输出 ALU 操作码 (aluOp) 以及两个寄存器值 (regAregB)。

执行阶段 (Execute)

scala
class Execute extends Module {
  val io = IO(new Bundle {
    val aluOp  = Input(UInt(5.W))   // ALU 操作码输入
    val regA   = Input(UInt(32.W))  // 寄存器 A 输入
    val regB   = Input(UInt(32.W))  // 寄存器 B 输入
    val result = Output(UInt(32.W)) // 计算结果输出
  })
  // ... Implementation of execute
}

执行阶段接收来自译码阶段的 aluOpregAregB,并输出计算结果 result

使用 <> 批量连接模块

下面我们创建流水线处理器的顶层模块,将三个阶段模块连接起来。

scala
class Pipeline extends Module {
  val io = IO(new Bundle {
    val result = Output(UInt(32.W)) // 最终计算结果输出
  })

  // 实例化三个阶段的子模块
  val fetch   = Module(new Fetch())
  val decode  = Module(new Decode())
  val execute = Module(new Execute())

  // 使用批量连接操作符 `<>` 自动连接各阶段
  fetch.io <> decode.io    // 连接 Fetch 和 Decode
  decode.io <> execute.io  // 连接 Decode 和 Execute
  io <> execute.io         // 将顶层模块的 IO 连接到 Execute 输出
}

连接细节

  1. fetch.io <> decode.io
    • 自动将 Fetch 模块的输出 instrpc 连接到 Decode 模块的输入端口。
  2. decode.io <> execute.io
    • 自动将 Decode 模块的输出 aluOpregAregB 连接到 Execute 模块的输入端口。
  3. io <> execute.io
    • 将顶层模块的 result 信号与 Execute 模块的 result 端口连接起来。

通过 <>,我们无需手动为每个端口编写连接代码,Chisel 会自动匹配端口名称进行连接,大大减少了代码量并提高了可读性。

优点总结

  • 简化连接:批量连接操作符 <> 可以自动匹配同名端口,减少手动连接代码。
  • 提高可读性:代码更简洁、直观,便于理解模块之间的信号流。
  • 便于维护:如果增加或修改接口,只需保持端口名称一致,批量连接操作无需调整。

注意事项

  1. 端口命名一致<> 只能匹配名称相同的端口,如果名称不同,则不会自动连接。
  2. 未连接端口:如果某个端口没有匹配到对应的连接,Chisel 会发出警告(例如使用 DontCare 解决)。
  3. Bundle 结构<> 适用于包含多个字段的 Bundle,适合模块之间批量连接复杂的接口。

4.5 外部模块 (External Modules)

在硬件设计中,有时需要引用使用 Verilog 或其他硬件描述语言编写的模块,或者直接确保生成的 Verilog 符合综合工具的特定结构。为此,Chisel 提供了对 外部模块 的支持,主要通过两种机制实现:

  • BlackBox:用于定义 Verilog 模块占位符,生成单独的 Verilog 文件。
  • ExtModule:与 BlackBox 类似,但 不会生成源文件,仅用作占位符,通常直接映射到综合工具提供的原语(如 FPGA 厂商提供的器件库)。

这两种机制非常适用于引入诸如 XilinxIntel 的器件原语(例如时钟缓冲器、输入缓冲器等)。

4.5.1 BlackBox 示例

BlackBox 可以用来封装任意 Verilog 模块,并将其作为一个占位符进行实例化。例如,我们要引用一个名为 BUFCE 的 Verilog 模块,它是一个时钟缓冲器,定义如下:

scala
class BUFCE extends BlackBox(Map("SIM_DEVICE" -> "7SERIES")) {
  val io = IO(new Bundle {
    val I  = Input(Clock())   // 输入时钟
    val CE = Input(Bool())    // 使能信号
    val O  = Output(Clock())  // 输出时钟
  })
}

BlackBox 特点

  • Map 中的键值对会传递给 Verilog 模块的参数。
  • io 定义了 Verilog 模块的输入/输出接口。

4.5.2 ExtModule 示例

ExtModule 是另一种形式的外部模块,它不会生成任何 Verilog 文件,而是直接作为占位符被综合工具识别。 下面是一个定义外部模块 alt_inbuf 的示例:

scala
class alt_inbuf extends ExtModule(Map(
  "io_standard" -> "1.0 V",
  "location" -> "IOBANK_1",
  "enable_bus_hold" -> "on",
  "weak_pull_up_resistor" -> "off",
  "termination" -> "parallel 50 ohms"
)) {
  val io = IO(new Bundle {
    val i = Input(Bool())
    val o = Output(Bool())
  })
}

ExtModule 特点

  • Map 参数可传递给综合工具,以配置模块参数(如位置、IO 标准等)。
  • 不会生成 Verilog 文件,直接在综合工具中替换为已存在的器件或模块。

4.5.3 定义内联的 BlackBox

BlackBox 还支持 内联 Verilog 代码,可以直接将 Verilog 代码写在 Chisel 文件中。例如,我们实现一个 32 位加法器:

定义 BlackBox 的 IO 接口

scala
class BlackBoxAdderIO extends Bundle {
  val a    = Input(UInt(32.W))
  val b    = Input(UInt(32.W))
  val cin  = Input(Bool())
  val sum  = Output(UInt(32.W))
  val cout = Output(Bool())
}

内联 Verilog 代码

scala
class InlineBlackBoxAdder extends HasBlackBoxInline {
  val io = IO(new BlackBoxAdderIO)
  setInline("InlineBlackBoxAdder.v",
    s"""
    |module InlineBlackBoxAdder(a, b, cin, cout, sum);
    |  input  [31:0] a, b;
    |  input  cin;
    |  output [31:0] sum;
    |  output cout;
    |  wire [32:0] full_sum;
    |
    |  assign full_sum = a + b + cin;
    |  assign sum = full_sum[31:0];
    |  assign cout = full_sum[32];
    |endmodule
    """.stripMargin
  )
}

要点

  • 使用 setInline 方法将 Verilog 代码作为字符串传递给 BlackBox。
  • stripMargin 确保字符串格式正确。

实例化 BlackBox 模块

scala
class InlineAdder extends Module {
  val io = IO(new BlackBoxAdderIO)
  val adder = Module(new InlineBlackBoxAdder)
  io <> adder.io
}

4.5.4 引用外部 Verilog 文件

除了内联的方式,BlackBox 还支持引用外部 Verilog 文件。这种情况下,Verilog 源文件应放在项目的 resources 文件夹中。

scala
class ResourceBlackBoxAdder extends HasBlackBoxResource {
  val io = IO(new BlackBoxAdderIO)
  addResource("/ResourceBlackBoxAdder.v")
}
  • addResource:指定 Verilog 文件的路径,相对于 resources 文件夹。
  • 文件会在生成的 Verilog 工程中自动包含。

通过路径引用外部文件

scala
class PathBlackBoxAdder extends HasBlackBoxPath {
  val io = IO(new BlackBoxAdderIO)
  addPath("./src/main/resources/PathBlackBoxAdder.v")
}
  • addPath:支持指定任意路径的 Verilog 文件。

4.5.5 实例化和测试 BlackBox

BlackBox 模块与普通模块的使用方法完全相同,都是通过 Module() 进行实例化:

scala
class InlineAdder extends Module {
  val io = IO(new BlackBoxAdderIO)
  val adder = Module(new InlineBlackBoxAdder)
  io <> adder.io
}

test(new InlineAdder) { dut =>
  dut.io.a.poke(10.U)
  dut.io.b.poke(15.U)
  dut.io.cin.poke(false.B)
  dut.clock.step()
  println(s"Result: ${dut.io.sum.peek()}")
}

总结

  1. BlackBox 和 ExtModule 提供了引入外部 Verilog 模块的机制,分别用于生成独立 Verilog 文件或占位符映射原语。
  2. BlackBox 支持多种形式,包括:
    • 内联 Verilog 代码 (HasBlackBoxInline)。
    • 引用资源文件 (HasBlackBoxResource)。
    • 路径引用外部文件 (HasBlackBoxPath)。
  3. 实例化外部模块 和普通 Chisel 模块没有区别,均使用 Module() 进行实例化。
  4. 这种机制使 Chisel 设计能够轻松集成已有的 Verilog 设计和厂商提供的原语模块,增强了设计的复用性和灵活性。