保留名称
将描述 SpinalHDL 如何将名称从 Scala 代码传播到生成的硬件。了解它们应该使您能够尽可能多地保留这些名称以生成可理解的网表。
可命名基类
所有可以在 SpinalHDL 中命名的东西都扩展了 Nameable 基类。
所以在实践中,以下类扩展了 Nameable :
- Component
- Area
- Data (UInt, SInt, Bundle, …)
Nameable API 的例子:
class MyComponent extends Component{ val a, b, c, d = Bool() b.setName("rawrr") // Force name c.setName("rawrr", weak = true) // Propose a name, will not be applied if a stronger name is already applied d.setCompositeName(b, postfix = "wuff") // Force toto to be named as b.getName() + _wuff" }
将生成:
module MyComponent ( ); wire a; wire rawrr; wire c; wire rawrr_wuff; endmodule
从 Scala 提取名称 1.4.0版本以来,SpinalHDL 使用了一个 scala 编译器插件,该插件可以在构造类的过程中每次定义一个新的 val 时提供一次回调。 下面的例子展示了 SpinalHDL 本身是如何实现的
//spinal.idslplugin.ValCallback is the Scala compiler plugin feature which will provide the callbacks class Component extends spinal.idslplugin.ValCallback{ override def valCallback[T](ref: T, name: String) : T = { println(s"Got $ref named $name") // Here we just print what we got as a demo. ref } } class UInt class Bits class MyComponent extends Component{ val two = 2 val wuff = "miaou" val toto = new UInt val rawrr = new Bits } object Debug3 extends App{ new MyComponent() // ^ This will print : // Got 2 named two // Got miaou named wuff // Got spinal.tester.code.sandbox.UInt@691a7f8f named toto // Got spinal.tester.code.sandbox.Bits@161b062a named rawrr }
使用 ValCallback 的“自省”特性,SpinalHDL 的组件类能够知道它们的内容和名称。但这也意味着,如果希望获得某个名称,并且只依赖于这个自动命名特性,那么对 Data (UInt,SInt,…)实例的引用应该存储在 Component val 中的某个地方。
class MyComponent extends Component { val a,b = in UInt(8 bits) // Will be properly named val toto = out UInt(8 bits) // same def doStuff(): Unit = { val tmp = UInt(8 bits) // This will not be named, as it isn't stored anywhere in a component val (but there is a solution explained later) tmp := 0x20 toto := tmp } doStuff() }
将产生:
module MyComponent ( input [7:0] a, input [7:0] b, output [7:0] toto ); //Note that the tmp signal defined in scala was "shortcuted" by SpinalHDL, as it was unamed and technicaly "shortcutable" assign toto = 8'h20; endmodule
组件中的区域 命名系统中的一个重要方面是,您可以在组件内部定义新的命名空间并进行操作
class MyComponent extends Component { val logicA = new Area{ //This define a new namespace named "logicA val toggle = Reg(Bool) //This register will be named "logicA_toggle" toggle := !toggle } }
将会产生:
module MyComponent ( input clk, input reset ); reg logicA_toggle; always @ (posedge clk) begin logicA_toggle <= (! logicA_toggle); end endmodule
函数中的区域
你也可以定义一个函数,这个函数将创建一个新的区域,这个区域将为它的所有内容提供一个名称空间:
class MyComponent extends Component { def isZero(value: UInt) = new Area { val comparator = value === 0 } val value = in UInt (8 bits) val someLogic = isZero(value) val result = out Bool() result := someLogic.comparator }
这将产生:
module MyComponent ( input [7:0] value, output result ); wire someLogic_comparator; assign someLogic_comparator = (value == 8'h0); assign result = someLogic_comparator; endmodule
函数中的复合
增加了 SpinalHDL 1.5.0,复合允许你创建一个范围,将使用作为前缀另一个命名:
class MyComponent extends Component { //Basicaly, a Composite is an Area that use its construction parameter as namespace prefix def isZero(value: UInt) = new Composite(value) { val comparator = value === 0 }.comparator //Note we don't return the Composite, but the element of the composite that we are interested in val value = in UInt (8 bits) val result = out Bool() result := isZero(value) }
将产生:
module MyComponent ( input [7:0] value, output result ); wire value_comparator; assign value_comparator = (value == 8'h0); assign result = value_comparator; endmodule
复合链
你也可以链接复合类型:
class MyComponent extends Component { def isZero(value: UInt) = new Composite(value) { val comparator = value === 0 }.comparator def inverted(value: Bool) = new Composite(value) { val inverter = !value }.inverter val value = in UInt(8 bits) val result = out Bool() result := inverted(isZero(value)) }
将产生:
module MyComponent ( input [7:0] value, output result ); wire value_comparator; wire value_comparator_inverter; assign value_comparator = (value == 8'h0); assign value_comparator_inverter = (! value_comparator); assign result = value_comparator_inverter; endmodule
Bundle函数中的复合
这种行为在实现 bundle 实用程序时非常有用。例如,在 spinal.lib 中。流类定义如下:
class Stream[T <: Data](val payloadType : HardType[T]) extends Bundle { val valid = Bool() val ready = Bool() val payload = payloadType() def queue(size: Int): Stream[T] = new Composite(this){ val fifo = new StreamFifo(payloadType, size) fifo.io.push << self // 'self' refer to the Composite construction argument (this in that example). It avoid having to do a boring 'Stream.this' }.fifo.io.pop def m2sPipe(): Stream[T] = new Composite(this) { val m2sPipe = Stream(payloadType) val rValid = RegInit(False) val rData = Reg(payloadType) self.ready := (!m2sPipe.valid) || m2sPipe.ready when(self.ready) { rValid := self.valid rData := self.payload } m2sPipe.valid := rValid m2sPipe.payload := rData }.m2sPipe }
它允许嵌套调用,同时保留名称:
class MyComponent extends Component { val source = slave(Stream(UInt(8 bits))) val sink = master(Stream(UInt(8 bits))) sink << source.queue(size = 16).m2sPipe() }
将会产生:
module MyComponent ( input source_valid, output source_ready, input [7:0] source_payload, output sink_valid, input sink_ready, output [7:0] sink_payload, input clk, input reset ); wire source_fifo_io_pop_ready; wire source_fifo_io_push_ready; wire source_fifo_io_pop_valid; wire [7:0] source_fifo_io_pop_payload; wire [4:0] source_fifo_io_occupancy; wire [4:0] source_fifo_io_availability; wire source_fifo_io_pop_m2sPipe_valid; wire source_fifo_io_pop_m2sPipe_ready; wire [7:0] source_fifo_io_pop_m2sPipe_payload; reg source_fifo_io_pop_rValid; reg [7:0] source_fifo_io_pop_rData; StreamFifo source_fifo ( .io_push_valid (source_valid ), //i .io_push_ready (source_fifo_io_push_ready ), //o .io_push_payload (source_payload ), //i .io_pop_valid (source_fifo_io_pop_valid ), //o .io_pop_ready (source_fifo_io_pop_ready ), //i .io_pop_payload (source_fifo_io_pop_payload ), //o .io_flush (1'b0 ), //i .io_occupancy (source_fifo_io_occupancy ), //o .io_availability (source_fifo_io_availability ), //o .clk (clk ), //i .reset (reset ) //i ); assign source_ready = source_fifo_io_push_ready; assign source_fifo_io_pop_ready = ((1'b1 && (! source_fifo_io_pop_m2sPipe_valid)) || source_fifo_io_pop_m2sPipe_ready); assign source_fifo_io_pop_m2sPipe_valid = source_fifo_io_pop_rValid; assign source_fifo_io_pop_m2sPipe_payload = source_fifo_io_pop_rData; assign sink_valid = source_fifo_io_pop_m2sPipe_valid; assign source_fifo_io_pop_m2sPipe_ready = sink_ready; assign sink_payload = source_fifo_io_pop_m2sPipe_payload; always @ (posedge clk or posedge reset) begin if (reset) begin source_fifo_io_pop_rValid <= 1'b0; end else begin if(source_fifo_io_pop_ready)begin source_fifo_io_pop_rValid <= source_fifo_io_pop_valid; end end end always @ (posedge clk) begin if(source_fifo_io_pop_ready)begin source_fifo_io_pop_rData <= source_fifo_io_pop_payload; end end endmodule
Unamed signal handling 非扩展信号处理
1.5.0以来,对于最终没有名称的信号,SpinalHDL 将找到一个由未加名称的信号驱动的信号,并将其名称传播。这可以产生有用的结果,只要你没有太大的未开发的东西。
属于这种未经管理的信号的名称是: _ zz _ + drivenSignal.getName ()。
注意,当代后端需要将某些特定表达式或长链表达式分解为多个信号时,也使用这种命名模式。
Verilog expression splitting 表达式分裂
有一个表达式的实例(例如: the + operator) ,SpinalHDL 需要用专门的信号来表达,以匹配 Scala API 的行为:
有一个表达式的实例(例如: the + operator) ,SpinalHDL 需要用专门的信号来表达,以匹配 Scala API 的行为: class MyComponent extends Component { val a,b,c,d = in UInt(8 bits) val result = a + b + c + d }
将会产生:
module MyComponent ( input [7:0] a, input [7:0] b, input [7:0] c, input [7:0] d ); wire [7:0] _zz_result; wire [7:0] _zz_result_1; wire [7:0] result; assign _zz_result = (_zz_result_1 + c); assign _zz_result_1 = (a + b); assign result = (_zz_result + d); endmodule
Verilog long expression splitting 长表达式分裂
这里有一个很长的表达式链如何被 SpinalHDL 分裂的例子:
class MyComponent extends Component { val conditions = in Vec(Bool, 64) val result = conditions.reduce(_ || _) // Do a logical or between all the conditions elements }
将会产生:
module MyComponent ( input conditions_0, input conditions_1, input conditions_2, input conditions_3, ... input conditions_58, input conditions_59, input conditions_60, input conditions_61, input conditions_62, input conditions_63 ); wire _zz_result; wire _zz_result_1; wire _zz_result_2; wire result; assign _zz_result = ((((((((((((((((_zz_result_1 || conditions_32) || conditions_33) || conditions_34) || conditions_35) || conditions_36) || conditions_37) || conditions_38) || conditions_39) || conditions_40) || conditions_41) || conditions_42) || conditions_43) || conditions_44) || conditions_45) || conditions_46) || conditions_47); assign _zz_result_1 = ((((((((((((((((_zz_result_2 || conditions_16) || conditions_17) || conditions_18) || conditions_19) || conditions_20) || conditions_21) || conditions_22) || conditions_23) || conditions_24) || conditions_25) || conditions_26) || conditions_27) || conditions_28) || conditions_29) || conditions_30) || conditions_31); assign _zz_result_2 = (((((((((((((((conditions_0 || conditions_1) || conditions_2) || conditions_3) || conditions_4) || conditions_5) || conditions_6) || conditions_7) || conditions_8) || conditions_9) || conditions_10) || conditions_11) || conditions_12) || conditions_13) || conditions_14) || conditions_15); assign result = ((((((((((((((((_zz_result || conditions_48) || conditions_49) || conditions_50) || conditions_51) || conditions_52) || conditions_53) || conditions_54) || conditions_55) || conditions_56) || conditions_57) || conditions_58) || conditions_59) || conditions_60) || conditions_61) || conditions_62) || conditions_63); endmodule
When statement condition 语句条件
When (cond){}语句条件生成为名为 when _ + fileName + line 的分隔信号。Switch 语句也将执行类似的操作。
//In file Test.scala class MyComponent extends Component { val value = in UInt(8 bits) val isZero = out(Bool()) val counter = out(Reg(UInt(8 bits))) isZero := False when(value === 0){ //At line 117 isZero := True counter := counter + 1 } }
将会产生:
module MyComponent ( input [7:0] value, output reg isZero, output reg [7:0] counter, input clk, input reset ); wire when_Test_l117; always @ (*) begin isZero = 1'b0; if(when_Test_l117)begin isZero = 1'b1; end end assign when_Test_l117 = (value == 8'h0); always @ (posedge clk) begin if(when_Test_l117)begin counter <= (counter + 8'h01); end end endmodule
作为最后的手段
最后,如果一个信号没有名称(匿名信号) ,SpinalHDL 将寻找一个由匿名信号驱动的命名信号,并将其作为名称后缀:
class MyComponent extends Component { val enable = in Bool() val value = out UInt(8 bits) def count(cond : Bool): UInt = { val ret = Reg(UInt(8 bits)) // This register is not named (on purpose for the example) when(cond){ ret := ret + 1 } return ret } value := count(enable) }
将会产生:
module MyComponent ( input enable, output [7:0] value, input clk, input reset ); reg [7:0] _zz_value; //Name given to the register in last resort by looking what was driven by it assign value = _zz_value; always @ (posedge clk) begin if(enable)begin _zz_value <= (_zz_value + 8'h01); end end endmodule
这种最后的命名略读并不是所有情况下的理想选择,但是可以有所帮助。
reference
- spinal HDL官方手册