Что означают операторы:*= и:=*?

Я вижу некоторые примеры в RocketChip, но не могу найти информацию в справочнике по API

  masterNode :=* tlOtherMastersNode
  DisableMonitors { implicit p => tlSlaveXbar.node :*= slaveNode }

2 ответа

Решение

Это не долотные операторы. Вместо этого они определены и используются Rocket Chip's diplomacy пакет. Это сокращенные операторы для выполнения различных типов привязки между дипломатическими узлами.

Опубликованной документации по API для этого не существует, но вы можете начать поискать в diplomacy пакет. Соответствующее место, где они определены, src/main/scala/diplomacy/Nodes.scala,

Запрос тянуть комментарий по этому API очень infomative.

Обычно A := B создает пару главного и подчиненного портов в A и B.A :=* B означает, что количество пар портов определяется количеством B := Other, а также A :*= B наоборот.

Самая нелогичная часть состоит в том, что дублирование ссылок достигается не дублированием промежуточного модуля, а расширением списка портов промежуточным модулем.

Я написал простой пример для изучения поведения звездообразных соединителей.

В следующем фрагменте кода TLIdentifyNode соединяет 3 TLClientNode, используя :=, а затем он подключается к узлу перекладины с помощью :=*как мастер на перекладине. Между тем, TLIdentifyNode соединяет 2 TLManagerNode, используя:=, а затем он подключается к тому же узлу перекладины с помощью :*= как мазь для перекладины.

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) {
  }
}

Соответствующий код Verilog ManagerConnector это (вкратце):

module ManagerConnector(
  `tilelink_bundle(auto_in_0),
  `tilelink_bundle(auto_in_1),
  `tilelink_bundle(auto_out_0),
  `tilelink_bundle(auto_out_1)
);
// ...
endmodule

Мы можем видеть diplomacyframework отвечает только за согласование параметров, создание списка портов и подключение портов. Дублирование введено* соединение гарантируется общим шаблоном кода:

(node.in zip node.out) foreach { ... }

На мой взгляд, этот API упрощает соединение между перекрестным узлом и различными узлами внутри определенного модуля и поддерживает согласованный синтаксис соединения.

[Ссылка] Заметка для чтения ракетного чипа: https://github.com/cnrv/rocket-chip-read/blob/master/diplomacy/Nodes.md

Может быть полезно прочитать документацию по дипломатии по lowrisc: https://www.lowrisc.org/docs/diplomacy/

Другие вопросы по тегам