FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

AXIバス by Chisel

f:id:msyksphinz:20170806234951p:plain

Rocket-Chipは、主にTileLinkとAXIバスによって記述されており、TileLinkがRocketChipに近い方、AXIが外部バスに出ていく方として記述されているのだけれども、AXI4のバスもChiselで書かれているようだ。 とりあえず見てみたが、正直なんだか良く分からない。Scala苦手だ。。。

こういうのを購入しないとダメかしら。

Scalaスケーラブルプログラミング第3版

Scalaスケーラブルプログラミング第3版

ChiselでのAXIバスの定義

GenericParameterizedBundleというクラスを継承する形で定義されている。

  • rocket-chip/src/main/scala/util/GenericParameterizedBundle.scala
abstract class GenericParameterizedBundle[+T <: Object](val params: T) extends Bundle
{
...

例えば、AXIのAチャネルは以下のように定義されている。

  • rocket-chip/src/main/scala/amba/axi4/Bundles.scala
abstract class AXI4BundleA(params: AXI4BundleParameters) extends AXI4BundleBase(params)
{
  val id     = UInt(width = params.idBits)
  val addr   = UInt(width = params.addrBits)
  val len    = UInt(width = params.lenBits)  // number of beats - 1
  val size   = UInt(width = params.sizeBits) // bytes in beat = 2^size
  val burst  = UInt(width = params.burstBits)
  val lock   = UInt(width = params.lockBits)
  val cache  = UInt(width = params.cacheBits)
  val prot   = UInt(width = params.protBits)
  val qos    = UInt(width = params.qosBits)  // 0=no QoS, bigger = higher priority
  val user = if (params.userBits > 0) Some(UInt(width = params.userBits)) else None
  // val region = UInt(width = 4) // optional

  // Number of bytes-1 in this operation
  def bytes1(x:Int=0) = {
    val maxShift = 1 << params.sizeBits
    val tail = UInt((BigInt(1) << maxShift) - 1)
    (Cat(len, tail) << size) >> maxShift
  }
}

次に、RegisterRouterというレジスタスライスの定義を見てみる。 regmapという定義がされており、どうやらこれが信号がレジスタそのものになっているようだ。

  • rocket-chip/src/main/scala/amba/axi4/RegisterRouter.scala
  def regmap(mapping: RegField.Map*) = {
    val ar = bundleIn(0).ar
    val aw = bundleIn(0).aw
    val w  = bundleIn(0).w
    val r  = bundleIn(0).r
    val b  = bundleIn(0).b

    val params = RegMapperParams(log2Up((address.mask+1)/beatBytes), beatBytes, ar.bits.params.idBits + ar.bits.params.userBits)
    val in = Wire(Decoupled(new RegMapperInput(params)))

    // Prefer to execute reads first
    in.valid := ar.valid || (aw.valid && w.valid)
    ar.ready := in.ready
    aw.ready := in.ready && !ar.valid && w .valid
    w .ready := in.ready && !ar.valid && aw.valid

    val ar_extra = Cat(Seq(ar.bits.id) ++ ar.bits.user.toList)
    val aw_extra = Cat(Seq(aw.bits.id) ++ aw.bits.user.toList)
    val in_extra = Mux(ar.valid, ar_extra, aw_extra)
    val addr = Mux(ar.valid, ar.bits.addr, aw.bits.addr)
    val mask = MaskGen(ar.bits.addr, ar.bits.size, beatBytes)

    in.bits.read  := ar.valid
    in.bits.index := addr >> log2Ceil(beatBytes)
    in.bits.data  := w.bits.data
    in.bits.mask  := Mux(ar.valid, mask, w.bits.strb)
    in.bits.extra := in_extra

    // Invoke the register map builder and make it Irrevocable
    val out = Queue.irrevocable(
      RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*),
      entries = 2)

    // No flow control needed
    out.ready := Mux(out.bits.read, r.ready, b.ready)
    r.valid := out.valid &&  out.bits.read
    b.valid := out.valid && !out.bits.read

    val out_id = if (r.bits.params.idBits == 0) UInt(0) else (out.bits.extra >> ar.bits.params.userBits)

    r.bits.id   := out_id
    r.bits.data := out.bits.data
    r.bits.last := Bool(true)
    r.bits.resp := AXI4Parameters.RESP_OKAY
    r.bits.user.foreach { _ := out.bits.extra }

    b.bits.id   := out_id
    b.bits.resp := AXI4Parameters.RESP_OKAY
    b.bits.user.foreach { _ := out.bits.extra }
  }