:*= 和 :=* 运算符的含义是什么?
What is the meaning of :*= and :=* operators?
我在 RocketChip, but could not find info in the API reference
中看到了一些示例
masterNode :=* tlOtherMastersNode
DisableMonitors { implicit p => tlSlaveXbar.node :*= slaveNode }
这些不是 Chisel 运算符。相反,它们由 Rocket Chip 的 diplomacy
包定义和使用。这些是 shorthand 运算符,用于在外交节点之间进行不同类型的绑定。
没有已发布的 API 文档,但您可以开始在 diplomacy
包中四处寻找。定义这些的相关位置是 src/main/scala/diplomacy/Nodes.scala
.
阅读 lowrisc 关于外交的文档可能会有用:https://www.lowrisc.org/docs/diplomacy/
这个 API 上的 pull request comment 非常有用。
通常,A := B
在A和B中创建一对主从端口。
A :=* B
表示端口对的个数由B := Other
的个数决定,
A :*= B
反之亦然。
最违反直觉的部分是链接的复制不是通过
中间模块重复,但中间模块扩展了端口列表。
我写了一个简单的例子来探索星形连接器的行为。
在下面的代码片段中,一个 TLIdentifyNode 使用 :=
连接 3 个 TLClientNode,
然后它使用 :=*
作为交叉开关的主节点连接到交叉开关节点。
同时,一个 TLIdentifyNode 使用 :=
连接 2 个 TLManagerNode,
然后它使用 :*=
作为 crossbar 的 salve 连接到同一个 crossbar 节点。
import chisel3._
import chisel3.core.dontTouch
import freechips.rocketchip.config._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
class ClientConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class ManagerConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class Client(implicit p: Parameters) extends LazyModule {
val node = TLClientNode(
portParams = Seq(
TLClientPortParameters(Seq(
TLClientParameters("myclient1", IdRange(0, 1), supportsGet = TransferSizes(4), supportsProbe = TransferSizes(4))
))
)
)
override lazy val module = new LazyModuleImp(this) {
node.out.foreach { case(bundle, edge) =>
val (legal, a) = edge.Get(0.U, 0x1000.U, 2.U)
bundle.a.bits := a
bundle.a.valid := legal
bundle.d.ready := true.B
dontTouch(bundle)
}
}
}
class Manager(base: Int)(implicit p: Parameters) extends LazyModule {
val node = TLManagerNode(Seq(TLManagerPortParameters(Seq(TLManagerParameters(
address = Seq(AddressSet(base, 0xffff)),
supportsGet = TransferSizes(4)
)), beatBytes = 4)))
override lazy val module = new LazyModuleImp(this) {
node.in.foreach { case (bundle, edge) =>
when (bundle.a.fire()) {
val d = edge.AccessAck(bundle.a.bits, 0xdeadbeafL.U)
bundle.d.bits := d
bundle.d.valid := true.B
}
bundle.a.ready := true.B
}
}
}
class Playground(implicit p: Parameters) extends LazyModule {
val xbr = TLXbar()
val clientConnectors = LazyModule(new ClientConnector())
val managerConnectors = LazyModule(new ManagerConnector())
val clients = Seq.fill(3) { LazyModule(new Client()).node }
val managers = Seq.tabulate(2) { i: Int => LazyModule(new Manager(0x10000 * i)).node }
clients.foreach(clientConnectors.node := _)
managers.foreach(_ := managerConnectors.node)
managerConnectors.node :*= xbr
xbr :=* clientConnectors.node
override lazy val module = new LazyModuleImp(this) {
}
}
ManagerConnector
对应的verilog代码为(简述):
module ManagerConnector(
`tilelink_bundle(auto_in_0),
`tilelink_bundle(auto_in_1),
`tilelink_bundle(auto_out_0),
`tilelink_bundle(auto_out_1)
);
// ...
endmodule
我们可以看到diplomacy
框架只负责参数协商、端口列表生成和端口连接。 *
连接引入的重复由公共代码模式保证:
(node.in zip node.out) foreach { ... }
在我看来,这个API简化了crossbar节点与特定模块内部各个节点的连接,并保持了连接语法的一致。
[参考]一篇rocketchip读书笔记:https://github.com/cnrv/rocket-chip-read/blob/master/diplomacy/Nodes.md
我在 RocketChip, but could not find info in the API reference
中看到了一些示例 masterNode :=* tlOtherMastersNode
DisableMonitors { implicit p => tlSlaveXbar.node :*= slaveNode }
这些不是 Chisel 运算符。相反,它们由 Rocket Chip 的 diplomacy
包定义和使用。这些是 shorthand 运算符,用于在外交节点之间进行不同类型的绑定。
没有已发布的 API 文档,但您可以开始在 diplomacy
包中四处寻找。定义这些的相关位置是 src/main/scala/diplomacy/Nodes.scala
.
阅读 lowrisc 关于外交的文档可能会有用:https://www.lowrisc.org/docs/diplomacy/
这个 API 上的 pull request comment 非常有用。
通常,A := B
在A和B中创建一对主从端口。
A :=* B
表示端口对的个数由B := Other
的个数决定,
A :*= B
反之亦然。
最违反直觉的部分是链接的复制不是通过 中间模块重复,但中间模块扩展了端口列表。
我写了一个简单的例子来探索星形连接器的行为。
在下面的代码片段中,一个 TLIdentifyNode 使用 :=
连接 3 个 TLClientNode,
然后它使用 :=*
作为交叉开关的主节点连接到交叉开关节点。
同时,一个 TLIdentifyNode 使用 :=
连接 2 个 TLManagerNode,
然后它使用 :*=
作为 crossbar 的 salve 连接到同一个 crossbar 节点。
import chisel3._
import chisel3.core.dontTouch
import freechips.rocketchip.config._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
class ClientConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class ManagerConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class Client(implicit p: Parameters) extends LazyModule {
val node = TLClientNode(
portParams = Seq(
TLClientPortParameters(Seq(
TLClientParameters("myclient1", IdRange(0, 1), supportsGet = TransferSizes(4), supportsProbe = TransferSizes(4))
))
)
)
override lazy val module = new LazyModuleImp(this) {
node.out.foreach { case(bundle, edge) =>
val (legal, a) = edge.Get(0.U, 0x1000.U, 2.U)
bundle.a.bits := a
bundle.a.valid := legal
bundle.d.ready := true.B
dontTouch(bundle)
}
}
}
class Manager(base: Int)(implicit p: Parameters) extends LazyModule {
val node = TLManagerNode(Seq(TLManagerPortParameters(Seq(TLManagerParameters(
address = Seq(AddressSet(base, 0xffff)),
supportsGet = TransferSizes(4)
)), beatBytes = 4)))
override lazy val module = new LazyModuleImp(this) {
node.in.foreach { case (bundle, edge) =>
when (bundle.a.fire()) {
val d = edge.AccessAck(bundle.a.bits, 0xdeadbeafL.U)
bundle.d.bits := d
bundle.d.valid := true.B
}
bundle.a.ready := true.B
}
}
}
class Playground(implicit p: Parameters) extends LazyModule {
val xbr = TLXbar()
val clientConnectors = LazyModule(new ClientConnector())
val managerConnectors = LazyModule(new ManagerConnector())
val clients = Seq.fill(3) { LazyModule(new Client()).node }
val managers = Seq.tabulate(2) { i: Int => LazyModule(new Manager(0x10000 * i)).node }
clients.foreach(clientConnectors.node := _)
managers.foreach(_ := managerConnectors.node)
managerConnectors.node :*= xbr
xbr :=* clientConnectors.node
override lazy val module = new LazyModuleImp(this) {
}
}
ManagerConnector
对应的verilog代码为(简述):
module ManagerConnector(
`tilelink_bundle(auto_in_0),
`tilelink_bundle(auto_in_1),
`tilelink_bundle(auto_out_0),
`tilelink_bundle(auto_out_1)
);
// ...
endmodule
我们可以看到diplomacy
框架只负责参数协商、端口列表生成和端口连接。 *
连接引入的重复由公共代码模式保证:
(node.in zip node.out) foreach { ... }
在我看来,这个API简化了crossbar节点与特定模块内部各个节点的连接,并保持了连接语法的一致。
[参考]一篇rocketchip读书笔记:https://github.com/cnrv/rocket-chip-read/blob/master/diplomacy/Nodes.md