实例化VHDL and Verilog IP
blackbox允许用户通过指定其接口将现有的 VHDL/Verilog 组件集成到设计中。由模拟器或合成器来正确地进行细化。
定义blackbox
下面显示了如何定义blackbox的示例:
// Define a Ram as a BlackBox class Ram_1w_1r(wordWidth: Int, wordCount: Int) extends BlackBox { // Add VHDL Generics / Verilog parameters to the blackbox // You can use String, Int, Double, Boolean, and all SpinalHDL base // types as generic values addGeneric("wordCount", wordCount) addGeneric("wordWidth", wordWidth) // Define IO of the VHDL entity / Verilog module val io = new Bundle { val clk = in Bool() val wr = new Bundle { val en = in Bool() val addr = in UInt (log2Up(wordCount) bit) val data = in Bits (wordWidth bit) } val rd = new Bundle { val en = in Bool() val addr = in UInt (log2Up(wordCount) bit) val data = out Bits (wordWidth bit) } } // Map the current clock domain to the io.clk pin mapClockDomain(clock=io.clk) }
在VHDL中类型的信号Bool
将被转换成std_logic
和Bits
成std_logic_vector
。如果你想得到std_ulogic
,你必须使用 aBlackBoxULogic
而不是BlackBox
。在 Verilog 中,BlackBoxUlogic
没有效果。
class Ram_1w_1r(wordWidth: Int, wordCount: Int) extends BlackBoxULogic { ... }
Generics
有两种不同的方式来声明泛型:
class Ram(wordWidth: Int, wordCount: Int) extends BlackBox { addGeneric("wordCount", wordCount) addGeneric("wordWidth", wordWidth) // OR val generic = new Generic { val wordCount = Ram.this.wordCount val wordWidth = Ram.this.wordWidth } }
实例化一个blackbox
实例化 一个BlackBox
就像实例化 一个 Component
:
// Create the top level and instantiate the Ram class TopLevel extends Component { val io = new Bundle { val wr = new Bundle { val en = in Bool() val addr = in UInt (log2Up(16) bit) val data = in Bits (8 bit) } val rd = new Bundle { val en = in Bool() val addr = in UInt (log2Up(16) bit) val data = out Bits (8 bit) } } // Instantiate the blackbox val ram = new Ram_1w_1r(8,16) // Connect all the signals io.wr.en <> ram.io.wr.en io.wr.addr <> ram.io.wr.addr io.wr.data <> ram.io.wr.data io.rd.en <> ram.io.rd.en io.rd.addr <> ram.io.rd.addr io.rd.data <> ram.io.rd.data } object Main { def main(args: Array[String]): Unit = { SpinalVhdl(new TopLevel) } }
时钟和复位映射
在黑盒定义中,必须明确定义时钟和复位线。要将 a 的信号映射ClockDomain
到黑匣子的相应输入,您可以使用mapClockDomain
或mapCurrentClockDomain
函数。mapClockDomain
具有以下参数:
mapCurrentClockDomain
有几乎相同的参数,mapClockDomain
但没有clockDomain。
class MyRam(clkDomain: ClockDomain) extends BlackBox { val io = new Bundle { val clkA = in Bool() ... val clkB = in Bool() ... } // Clock A is map on a specific clock Domain mapClockDomain(clkDomain, io.clkA) // Clock B is map on the current clock domain mapCurrentClockDomain(io.clkB) }
io前缀
为了避免黑盒的每个IO上都有前缀“io_”,可以使用noIoPrefix()
如下所示的函数:
// Define the Ram as a BlackBox class Ram_1w_1r(wordWidth: Int, wordCount: Int) extends BlackBox { val generic = new Generic { val wordCount = Ram_1w_1r.this.wordCount val wordWidth = Ram_1w_1r.this.wordWidth } val io = new Bundle { val clk = in Bool() val wr = new Bundle { val en = in Bool() val addr = in UInt (log2Up(_wordCount) bit) val data = in Bits (_wordWidth bit) } val rd = new Bundle { val en = in Bool() val addr = in UInt (log2Up(_wordCount) bit) val data = out Bits (_wordWidth bit) } } noIoPrefix() mapCurrentClockDomain(clock=io.clk) }
重命名黑盒的所有 io
可以在编译时使用该函数重命名BlackBox
或 的IO 。此函数在编译期间采用无参数函数,对于添加重命名过程很有用,如以下示例所示:Component``addPrePopTask
class MyRam() extends Blackbox { val io = new Bundle { val clk = in Bool() val portA = new Bundle{ val cs = in Bool() val rwn = in Bool() val dIn = in Bits(32 bits) val dOut = out Bits(32 bits) } val portB = new Bundle{ val cs = in Bool() val rwn = in Bool() val dIn = in Bits(32 bits) val dOut = out Bits(32 bits) } } // Map the clk mapCurrentClockDomain(io.clk) // Remove io_ prefix noIoPrefix() // Function used to rename all signals of the blackbox private def renameIO(): Unit = { io.flatten.foreach(bt => { if(bt.getName().contains("portA")) bt.setName(bt.getName().repalce("portA_", "") + "_A") if(bt.getName().contains("portB")) bt.setName(bt.getName().repalce("portB_", "") + "_B") }) } // Execute the function renameIO after the creation of the component addPrePopTask(() => renameIO()) } // This code generate these names: // clk // cs_A, rwn_A, dIn_A, dOut_A // cs_B, rwn_B, dIn_B, dOut_B
添加 RTL 源
使用该功能,addRTLPath()
您可以将 RTL 源与黑匣子相关联。生成 SpinalHDL 代码后,可以调用该函数mergeRTLSource
将所有源合并在一起。
class MyBlackBox() extends Blackbox { val io = new Bundle { val clk = in Bool() val start = in Bool() val dIn = in Bits(32 bits) val dOut = out Bits(32 bits) val ready = out Bool() } // Map the clk mapCurrentClockDomain(io.clk) // Remove io_ prefix noIoPrefix() // Add all rtl dependencies addRTLPath("./rtl/RegisterBank.v") // Add a verilog file addRTLPath(s"./rtl/myDesign.vhd") // Add a vhdl file addRTLPath(s"${sys.env("MY_PROJECT")}/myTopLevel.vhd") // Use an environement variable MY_PROJECT (System.getenv("MY_PROJECT")) } ... val report = SpinalVhdl(new MyBlackBox) report.mergeRTLSource("mergeRTL") // Merge all rtl sources into mergeRTL.vhd and mergeRTL.v files
VHDL - 无数字类型
如果你只想std_logic_vector
在你的黑盒组件中使用,你可以将标签添加noNumericType
到黑盒中。
class MyBlackBox() extends BlackBox{ val io = new Bundle { val clk = in Bool() val increment = in Bool() val initValue = in UInt(8 bits) val counter = out UInt(8 bits) } mapCurrentClockDomain(io.clk) noIoPrefix() addTag(noNumericType) // Only std_logic_vector }
上面的代码将生成以下 VHDL:
component MyBlackBox is port( clk : in std_logic; increment : in std_logic; initValue : in std_logic_vector(7 downto 0); counter : out std_logic_vector(7 downto 0) ); end component;
reference
- spinal HDL官方指南