文章目录
- 1.1、简介
- 1.2、Input / output 定义
- 1.3、修剪信号(Pruned signals)
- 1.4、参数化硬件(在Verilog中称为“Parameter”)
- 1.5、合成组件名称
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对象上的printPruned
和printPrunedIo
函数收集所有已删除和无用信号的列表:
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
来替换此约定以使用自定义名称。这在与外部组件进行接口时特别有用。其他方法分别称为getName
、setPartialName
和getPartialName
。
合成时,每个模块都获得定义它的Scala类的名称。您也可以使用setDefinitionName
覆盖此设置。