组件和层次结构
像在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 { ... }
io
的 Bundle
中声明外部端口。如果您将线束命名为 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) |
对于所有其他数据类型,您可能需要在其周围添加一些括号。这是 Scala 的限制。 |
T |
master(T) slave(T) master(Bool()) |
此语法由 |
T |
组件之间的互连需要遵循一些规则:
组件只能 读取 子组件的输出和输入信号。
组件可以读取自己的输出端口值(与 VHDL 不同)。
小技巧
如果由于某种原因您需要从层次结构中较深的位置读取信号(例如用于调试或临时补丁),您可以使用 some.where.else.theSignal.pull()
函数返回的信号来完成此操作
裁剪信号
SpinalHDL will generate all the named signals and their dependencies, while all the useless anonymous / zero width ones are removed from the RTL generation.
您可以通过生成的 SpinalReport
对象上的 printPruned
和 printPrunedIo
函数收集所有已删除的无用信号列表:
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
将按此约定生成的名称替换为自定义的。这在与外部组件连接时特别有用。其他方法分别称为 getName
、setPartialName
和 getPartialName
。
综合时,每个模块都会获得定义它的 Scala 类的名称。您也可以调用 setDefinitionName
函数来覆盖它。