【SpinalHDL快速入门】5.1、SpinalHDL组织结构之Component和hierarchy

文章介绍了SpinalHDL如何定义和使用组件,包括在不绑定端口的情况下实例化,输入/输出的定义,以及如何处理修剪信号和参数化硬件。此外,还提到了组件名称的合成规则和自定义方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1、简介

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

class AdderCell() extends Component {
  // 建议在名为“io”的Bundle中声明外部端口
  val io = new Bundle {
    val a, b, cin = in Bool()
    val sum, cout = out Bool()
  }
  // 执行一些逻辑
  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 {
  ...
  // 创建2个AdderCell实例
  val cell0 = new AdderCell()
  val cell1 = new AdderCell()
  cell1.io.cin := cell0.io.cout   // Connect cout of cell0 to cin of cell1

  // 另一个例子,它创建了一个 ArrayCell 实例的数组
  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 { ... }:建议在名为io的Bundle中声明外部端口。如果您将Bundle命名为io,则SpinalHDL将检查其所有元素是否定义为输入或输出

1.2、Input / output 定义

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

在这里插入图片描述
组件互连需要遵循一些规则:

  • 组件只能读取子组件的输出和输入信号。
  • 与 VHDL 不同,组件可以读取其自身的输出端口值。

如果出于某种原因需要从层次结构较远的位置读取信号(例如进行调试或暂时性修补),可以通过使用 some.where.else.theSignal.pull() 返回的值来实现。

1.3、修剪信号(Pruned signals)

SpinalHDL将生成所有命名信号及其依赖关系,同时从RTL生成中删除所有无用的匿名/零宽度信号。

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

class TopLevel extends Component {
  val io = new Bundle {
    val a,b = in UInt(8 bits)
    val result = out 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]
  }
}

1.4、参数化硬件(在Verilog中称为“Parameter”)

如果您想将组件参数化,可以按照以下方式向组件的构造函数传递参数:

class MyAdder(width: BitCount) extends Component {
  val io = new Bundle {
    val a, b   = in UInt(width)
    val result = out 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")
}

1.5、合成组件名称

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

合成时,每个模块都获得定义它的Scala类的名称。您也可以使用setDefinitionName覆盖此设置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值