chisel的命名高级用法

从历史上看,Chisel 在可靠地捕获信号名称方面遇到了麻烦。造成这种情况的原因是
(1)主要依靠反射来查找名称
(2)使用@chiselName行为不可靠的宏。

Chisel 3.4 引入了一个自定义的 Scala 编译器插件,它可以在声明信号名称时实现可靠和自动捕获信号名称。此外,此版本还大量使用了新的前缀 API,它可以更稳定地命名从函数调用以编程方式生成的信号。

本文档解释了现在在 Chisel 中如何命名信号和模块名称。有关如何解决系统名称稳定性问题的手册示例,请参阅命名手册

编译器插件

// Imports used by the following examples
import chisel3._
import chisel3.experimental.{prefix, noPrefix}
import chisel3.stage.ChiselStage

随着 Chisel 3.4 的发布,用户应该在他们的 build.sbt 设置中添加以下行以获得改进的命名:

// chiselVersion is the String version (eg. "3.4.0")
addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % chiselVersion cross CrossVersion.full)

这个插件将在 Scala 编译器的 ‘typer’ 阶段之后运行。它查找任何形式 为val x = yx类型为chisel3.Datachisel3.MemBase或的用户代码chisel3.experimental.BaseModule。对于符合此标准的每一行,它都会重写该行。在以下示例中,注释行是上面的行被重写的内容。

如果该行在捆绑声明中或者是模块实例化,则将其重写以将右侧替换为对 的调用autoNameRecursively,该调用命名信号/模块。

class MyBundle extends Bundle {
  val foo = Input(UInt(3.W))
  // val foo = autoNameRecursively("foo")(Input(UInt(3.W)))
}
class Example1 extends Module {
  val io = IO(new MyBundle())
  // val io = autoNameRecursively("io")(IO(new MyBundle()))
}
module Example1(
  input        clock,
  input        reset,
  input  [2:0] io_foo
);
endmodule

否则,它会被重写以包含名称作为在执行 val 声明右侧时生成的任何信号的前缀:

class Example2 extends Module {
  val in = IO(Input(UInt(2.W)))
  // val in = autoNameRecursively("in")(prefix("in")(IO(Input(UInt(2.W)))))

  val out = IO(Output(UInt(2.W)))
  // val out = autoNameRecursively("out")(prefix("out")(IO(Output(UInt(2.W)))))

  def inXin() = in * in

  val add = 3.U + inXin()
  // val add = autoNameRecursively("add")(prefix("add")(3.U + inXin()))
  // Note that the intermediate result of the multiplication is prefixed with `add`

  out := add + 1.U
}
module Example2(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [3:0] _add_T = in * in; // @[naming.md 48:20]
  wire [3:0] add = 4'h3 + _add_T; // @[naming.md 50:17]
  wire [3:0] _out_T_1 = add + 4'h1; // @[naming.md 54:14]
  assign out = _out_T_1[1:0]; // @[naming.md 54:7]
endmodule

请注意,如果硬件类型嵌套在Option或 的子类型中,则命名也有效Iterable

class Example3 extends Module {
  val in = IO(Input(UInt(2.W)))
  // val in = autoNameRecursively("in")(prefix("in")(IO(Input(UInt(2.W)))))

  val out = IO(Output(UInt()))
  // val out = autoNameRecursively("out")(prefix("out")(IO(Output(UInt(2.W)))))

  def inXin() = in * in

  val opt = Some(3.U + inXin())
  // Note that the intermediate result of the inXin() is prefixed with `opt`:
  // val opt = autoNameRecursively("opt")(prefix("opt")(Some(3.U + inXin())))

  out := opt.get + 1.U
}
module Example3(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [3:0] out
);
  wire [3:0] _opt_T = in * in; // @[naming.md 74:20]
  wire [3:0] opt = 4'h3 + _opt_T; // @[naming.md 76:22]
  assign out = opt + 4'h1; // @[naming.md 80:18]
endmodule

还有一个轻微的变体 ( autoNameRecursivelyProduct) 用于使用 unapply 提供的名称命名硬件:

class UnapplyExample extends Module {
  def mkIO() = (IO(Input(UInt(2.W))), IO(Output(UInt())))
  val (in, out) = mkIO()
  // val (in, out) = autoNameRecursivelyProduct(List(Some("in"), Some("out")))(mkIO())

  out := in
}
module UnapplyExample(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  assign out = in; // @[naming.md 98:7]
endmodule

请注意,编译器插件在这些情况下不会插入前缀,因为前缀应该是什么是不明确的。鼓励需要前缀的用户提供如下所述的前缀。

前缀

如上所示,编译器插件会自动尝试为您的一些信号添加前缀。但是,您作为用户也可以添加自己的前缀。这尤其适用于 ECO 类型的修复,您需要向模块添加一些逻辑但又不想影响模块中的其他名称。

在以下示例中,我们在附加逻辑前面加上“ECO”,其中Example4是 pre-ECO 和Example5post-ECO:

class Example4 extends Module {
  val in = IO(Input(UInt(2.W)))
  val out = IO(Output(UInt()))

  val add = in + in + in

  out := add + 1.U
}

class Example5 extends Module {
  val in = IO(Input(UInt(2.W)))
  val out = IO(Output(UInt()))

  val add = in + in + in

  out := prefix("ECO") { add + 1.U + in }
}
module Example4(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [1:0] _add_T_1 = in + in; // @[naming.md 115:16]
  wire [1:0] add = _add_T_1 + in; // @[naming.md 115:21]
  assign out = add + 2'h1; // @[naming.md 117:14]
endmodule
module Example5(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [1:0] _add_T_1 = in + in; // @[naming.md 125:16]
  wire [1:0] add = _add_T_1 + in; // @[naming.md 125:21]
  wire [1:0] _out_ECO_T_1 = add + 2'h1; // @[naming.md 127:30]
  assign out = _out_ECO_T_1 + in; // @[naming.md 127:36]
endmodule

另请注意,前缀是相互附加的(包括编译器插件生成的前缀):

class Example6 extends Module {
  val in = IO(Input(UInt(2.W)))
  val out = IO(Output(UInt()))

  val add = prefix("foo") { in + in + in }

  out := add
}
module Example6(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [1:0] _add_foo_T_1 = in + in; // @[naming.md 147:32]
  assign out = _add_foo_T_1 + in; // @[naming.md 147:37]
endmodule

有时您可能想要禁用前缀。如果您正在编写库函数并且不想要前缀行为,则可能会发生这种情况。在这种情况下,您可以使用noPrefix对象:

class Example7 extends Module {
  val in = IO(Input(UInt(2.W)))
  val out = IO(Output(UInt()))

  val add = noPrefix { in + in + in }

  out := add
}
module Example7(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [1:0] _T_1 = in + in; // @[naming.md 166:27]
  assign out = _T_1 + in; // @[naming.md 166:32]
endmodule

建议信号的名称(或模块的实例名称)

如果要指定信号的名称,可以随时使用.suggestNameAPI。请注意,建议的名称仍将作为前缀(包括插件)。您始终可以使用该noPrefix对象来剥离它。

class Example8 extends Module {
  val in = IO(Input(UInt(2.W)))
  val out = IO(Output(UInt()))

  val add = (in + (in + in).suggestName("foo"))

  out := add
}
module Example8(
  input        clock,
  input        reset,
  input  [1:0] in,
  output [1:0] out
);
  wire [1:0] add_foo = in + in; // @[naming.md 185:23]
  assign out = in + add_foo; // @[naming.md 185:17]
endmodule

设置模块名称

如果要指定模块的名称(而不是模块的实例名称),您始终可以覆盖该desiredName 值。请注意,您可以通过模块的参数参数化名称。这是使您的模块名称更稳定的好方法,强烈建议您这样做。

class Example9(width: Int) extends Module {
  override val desiredName = s"EXAMPLE9WITHWIDTH$width"
  val in = IO(Input(UInt(width.W)))
  val out = IO(Output(UInt()))

  val add = (in + (in + in).suggestName("foo"))

  out := add
}
module EXAMPLE9WITHWIDTH8(
  input        clock,
  input        reset,
  input  [7:0] in,
  output [7:0] out
);
  wire [7:0] add_foo = in + in; // @[naming.md 205:23]
  assign out = in + add_foo; // @[naming.md 205:17]
endmodule
module EXAMPLE9WITHWIDTH1(
  input   clock,
  input   reset,
  input   in,
  output  out
);
  wire  add_foo = in + in; // @[naming.md 205:23]
  assign out = in + add_foo; // @[naming.md 205:17]
endmodule

反射命名

无论编译器插件是否启用,在 Chisel 构造一个模块后,它都会尝试命名该模块的所有成员。这将命名作为模块类的字段的所有 val,但不会命名嵌套函数或范围中的任何 val。

如果插件成功命名一个信号,反射命名将什么都不做。我们计划在未来的 Chisel 版本中弃用所有反射命名,但保留它以允许插件命名是可选的(但推荐)。

例如,以下模块中的信号在嵌套范围内;插件成功命名它们,但反射命名不能:

class Example10 extends Module {
  {
    val in = IO(Input(UInt(3.W)))
    val out = IO(Output(UInt()))

    val add = in + in

    out := add
  }
}

@chiselName

不再推荐使用此宏,因为它的功能完全被编译器插件取代。随意从您的chisel设计中删除!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值