保留名称的方法
本页将描述 SpinalHDL 如何将名称从 scala 代码传播到生成的硬件 RTL。您应该了解它们,从而尽可能保留这些名称,以生成可理解的网表。
Nameable 基类
所有可以在 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
一般来说,您实际上并不需要访问该 API,除非您出于调试原因或出于详细说明的目的想要做一些精巧的工作。
从 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、…) 实例的引用应存储在组件的某个 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
组件中的区域
命名系统的一个重要方面是您可以在组件内定义新的名称空间并进行操作
例如通过 Area :
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
函数中的复合区(Composite)
SpinalHDL 1.5.0 中添加了复合区,它允许您创建一个范围,该范围将用作另一个 Nameable 的前缀:
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)的函数中的复合区
在实现线束工具时,此行为非常有用。例如,在 spin.lib.Stream
类中定义如下:
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' refers to the Composite construction argument ('this' in
// the example). It avoids 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
未命名信号处理
从 1.5.0 开始,对于最终没有名称的信号,SpinalHDL 将找到由该信号驱动的信号并传播其名称。只要您没有太多未命名的东西,这就可以产生有用的结果。
这种未命名信号的名称是:_zz_ +drivenSignal.getName()
请注意,当生成后端需要将某些特定表达式或表达式链分解为多个信号时,也会使用此命名模式。
Verilog 表达式分割
下例中, 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 长表达式分割
下例说明了 SpinalHDL 如何分割一个长表达式链:
class MyComponent extends Component {
val conditions = in Vec(Bool(), 64)
// Perform a logical OR between all the condition elements
val result = conditions.reduce(_ || _)
// For Bits/UInt/SInt signals the 'orR' methods implements this reduction operation
}
会生成
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 语句条件
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
);
// Name given to the register in last resort by looking what was driven by it
reg [7:0] _zz_value;
assign value = _zz_value;
always @ (posedge clk) begin
if(enable)begin
_zz_value <= (_zz_value + 8'h01);
end
end
endmodule
最后的命名方法并不适合所有情况,但可以提供帮助。
请注意,以下划线开头的信号不会存储在 Verilator 波形中(这是故意的)