spinal HDL - 07 - 重命名

简介: spinal HDL - 07 - 重命名

保留名称


将描述 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


  1. spinal HDL官方手册
目录
相关文章
|
7月前
|
存储 C语言 索引
c程序结构
c程序结构
43 1
|
7月前
|
算法 异构计算
m基于FPGA的Alamouti编码verilog实现,包含testbench测试文件
m基于FPGA的Alamouti编码verilog实现,包含testbench测试文件
86 5
|
7月前
|
编译器 C语言
C程序结构
C程序结构。
33 1
|
算法 异构计算
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
166 0
|
算法 异构计算
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
322 0
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
|
机器学习/深度学习 算法
Verilog实现FIR低通滤波器,vivado平台开发,包含testbench
Verilog实现FIR低通滤波器,vivado平台开发,包含testbench
174 0
|
算法 异构计算
Verilog HDL函数与任务的使用
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
125 0
Verilog HDL函数与任务的使用
|
Scala
spinal HDL - 05 - Spinal HDL - 函数和时钟域
spinal HDL - 05 - Spinal HDL - 函数和时钟域
303 0
spinal HDL - 05 - Spinal HDL - 函数和时钟域
|
自然语言处理 算法 异构计算
Verilog HDL基本语法规则
本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
257 0
Verilog HDL基本语法规则
|
芯片 异构计算
Verilog语法之测试文件
要测试我们设计的模块功能是否正常,最直接的办法就是烧写到FPGA芯片中进行验证,但是这种方式往往结果并不直观,且出现问题后也不容易定位。为提高工作效率,我们可通过电脑仿真的方式进行功能验证,待仿真通过后,再烧写到FPGA中,这样可以快速排除电路中存在的绝大多数bug。在电脑上进行仿真,除了我们设计的功能模块之外,还需要另一模块——testbench,用于产生被测模块所需的激励信号。由于testbench只是用于电脑端的仿真,而不需要最终综合成电路,因此其写法更加灵活,可以使用verilog语法中的一些不可综合的语句,如initial、#、$display、$readmemb、forever等。
269 0