0
点赞
收藏
分享

微信扫一扫

spinal HDL - 04 - Spinal HDL - 组件、层次结构和区域


写在前面

本文主要介绍了spinal HDL的组件、层次结构和区域。

组件和层次结构(Component and hierarchy)

就像在 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 Bool()
val sum, cout = out 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 { … }
建议在​​​Bundle​​​被叫方声明外部端口​​io​​​。如果你命名你的 bundle ​​io​​,SpinalHDL 将检查它的所有元素是否被定义为输入或输出。

输入/输出定义

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

Syntax

Description

Return

in Bool()/out Bool()

创建输入 Bool/输出 Bool

Bool

in/out Bits/UInt/SInt[(x bit)]

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

Bits/UInt/SInt

in/out(T)

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

T

master/slave(T)

此语法由​​spinal.lib​​​库提供(如果您使用该​​slave​​​语法注释对象,则​​spinal.lib.slave​​​改为导入)。T 应该扩展​​IMasterSlave​

T

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

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

修剪信号

SpinalHDL 只生成直接或间接驱动顶级实体输出所需的东西。

所有其他信号(无用的)都从 RTL 生成中删除,并插入到修剪过的信号列表中。您可以通过生成的对象上的​​printPruned​​​和​​printPrunedIo​​​函数获取此列表​​SpinalReport​​:

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]
}
}

如果您出于调试原因想在生成的 RTL 中保留修剪后的信号,您可以使用该​​keep​​信号的函数:

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).keep()

unusedSignal := 0
unusedSignal2 := unusedSignal
}

object Main {
def main(args: Array[String]) {
SpinalVhdl(new TopLevel).printPruned()
// This will report nothing
}
}

参数化硬件(VHDL 中的“Generic”,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 {
...
}

合成成分名称

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

合成时,每个模块都会获得定义它的 Scala 类的名称。您也可以使用​​setDefinitionName​​.

区域(Area)

有时,创建一个​​Component​​来定义一些逻辑是矫枉过正的,因为你:

  • 需要定义所有的构造参数和IO(verbosity,duplication)
  • 拆分您的代码(超出需要)

对于这种情况,您可以使用 an​​Area​​来定义一组信号/逻辑:

class UartCtrl extends Component {
...
val timer = new Area {
val counter = Reg(UInt(8 bit))
val tick = counter === 0
counter := counter - 1
when(tick) {
counter := 100
}
}

val tickCounter = new Area {
val value = Reg(UInt(3 bit))
val reset = False
when(timer.tick) { // Refer to the tick from timer area
value := value + 1
}
when(reset) {
value := 0
}
}

val stateMachine = new Area {
...
}
}

Reference

  1. spinal HDL官方文档


举报

相关推荐

0 条评论