组件和层次结构

像在VHDL和Verilog中一样,可以使用组件构建设计层次结构。然而,在SpinalHDL中,不需要在实例化时绑定它们的端口:

class AdderCell() extends Component {
  // Declaring external ports in a Bundle called `io` is recommended
  val io = new Bundle {
    val a, b, cin = in port Bool()
    val sum, cout = out port Bool()
  }
  // Do some logic
  io.sum := io.a ^ io.b ^ io.cin
  io.cout := (io.a & io.b) | (io.a & io.cin) | (io.b & io.cin)
}

class Adder(width: Int) extends Component {
  ...
  // Create 2 AdderCell instances
  val cell0 = new AdderCell()
  val cell1 = new AdderCell()
  cell1.io.cin := cell0.io.cout   // Connect cout of cell0 to cin of cell1

  // Another example which creates an array of ArrayCell instances
  val cellArray = Array.fill(width)(new AdderCell())
  cellArray(1).io.cin := cellArray(0).io.cout   // Connect cout of cell(0) to cin of cell(1)
  ...
}

小技巧

val io = new Bundle { ... }
建议在名为 ioBundle 中声明外部端口。如果您将线束命名为 io,SpinalHDL 将检查其所有元素是否定义为输入或输出。

小技巧

如果更符合您的风格,您也可以使用 Module 语法而不是 Component (它们是相同的东西)

输入/输出定义

定义输入和输出的语法如下:

语法

描述

返回类型

in port Bool()
out port Bool()

创建输入 Bool/输出 Bool

Bool

in Bits/UInt/SInt[(x bits)]
out Bits/UInt/SInt[(x bits)]
in Bits(3 bits)

创建相应类型的输入/输出端口

Bits/UInt/SInt

in(T)
out(T)
out UInt(7 bits)

对于所有其他数据类型,您可能需要在其周围添加一些括号。这是 Scala 的限制。

T

master(T)
slave(T)
master(Bool())

此语法由 spinal.lib 库提供(如果您使用 slave 语法标注对象,则应导入 spinal.lib.slave)。 T 必须继承自 IMasterSlave。一些参考文档在 这里 。您实际上可能不需要括号,因此写成 master T 也可以。

T

组件之间的互连需要遵循一些规则:

  • 组件只能 读取 子组件的输出和输入信号。

  • 组件可以读取自己的输出端口值(与 VHDL 不同)。

小技巧

如果由于某种原因您需要从层次结构中较深的位置读取信号(例如用于调试或临时补丁),您可以使用 some.where.else.theSignal.pull() 函数返回的信号来完成此操作

裁剪信号

SpinalHDL 将生成所有命名信号及其依赖性,而所有无用的匿名/零位宽信号将从 RTL 生成中删除。

您可以通过生成的 SpinalReport 对象上的 printPrunedprintPrunedIo 函数收集所有已删除的无用信号列表:

class TopLevel extends Component {
  val io = new Bundle {
    val a,b = in port UInt(8 bits)
    val result = out port UInt(8 bits)
  }

  io.result := io.a + io.b

  val unusedSignal = UInt(8 bits)
  val unusedSignal2 = UInt(8 bits)

  unusedSignal2 := unusedSignal
}

object Main {
  def main(args: Array[String]) {
    SpinalVhdl(new TopLevel).printPruned()
    //This will report :
    //  [Warning] Unused wire detected : toplevel/unusedSignal : UInt[8 bits]
    //  [Warning] Unused wire detected : toplevel/unusedSignal2 : UInt[8 bits]
  }
}

参数化硬件(VHDL 中的“Generic”,Verilog 中的“Parameter”)

如果你想参数化你的组件,你可以将参数传递给组件的构造函数,如下所示:

class MyAdder(width: BitCount) extends Component {
  val io = new Bundle {
    val a, b   = in port UInt(width)
    val result = out port UInt(width)
  }
  io.result := io.a + io.b
}

object Main {
  def main(args: Array[String]) {
    SpinalVhdl(new MyAdder(32 bits))
  }
}

如果您有多个参数,最好给出一个专用的配置类,如下所示:

case class MySocConfig(axiFrequency  : HertzNumber,
                       onChipRamSize : BigInt,
                       cpu           : RiscCoreConfig,
                       iCache        : InstructionCacheConfig)

class MySoc(config: MySocConfig) extends Component {
  ...
}

您可以在配置中添加功能以及对配置属性的要求:

case class MyBusConfig(addressWidth: Int, dataWidth: Int) {
  def bytePerWord = dataWidth / 8
  def addressType = UInt(addressWidth bits)
  def dataType = Bits(dataWidth bits)

  require(dataWidth == 32 || dataWidth == 64, "Data width must be 32 or 64")
}

备注

这种参数化完全发生在 SpinalHDL 代码生成的实例细化过程中。生成的 HDL 代码不包含使用HDL语言泛化特性的内容。此处描述的方法不会使用 VHDL 泛型或 Verilog 参数。

另请参阅 Blackbox 了解有关该机制支持的更多信息。

综合后组件名称

在模块内,每个组件都有一个名称,称为“部分名称”。 “完整”名称是通过将每个组件的父名称与“_”连接起来构建的,例如:io_clockDomain_reset。您可以使用 setName 将按此约定生成的名称替换为自定义的。这在与外部组件连接时特别有用。其他方法分别称为 getNamesetPartialNamegetPartialName

综合时,每个模块都会获得定义它的 Scala 类的名称。您也可以调用 setDefinitionName 函数来覆盖它。