Skip to content

Commit

Permalink
New BundleMap (#3419)
Browse files Browse the repository at this point in the history
* implement BundleMap with new Connectable API

* migrate to new BundleMap API
  • Loading branch information
sequencer authored Jul 27, 2023
1 parent 4f0c6ef commit 7c9670b
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 179 deletions.
5 changes: 1 addition & 4 deletions src/main/scala/amba/axis/Bundles.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ case class AXISIdField (width: Int) extends SimpleBundleField(AXISId) (Output(
case class AXISDestField(width: Int) extends SimpleBundleField(AXISDest)(Output(UInt(width.W)), 0.U)
case class AXISKeepField(width: Int) extends SimpleBundleField(AXISKeep)(Output(UInt(width.W)), ~0.U(width.W))
case class AXISStrbField(width: Int) extends SimpleBundleField(AXISStrb)(Output(UInt(width.W)), ~0.U(width.W))
case class AXISDataField(width: Int) extends BundleField(AXISData) {
def data = Output(UInt(width.W))
def default(x: UInt): Unit = { x := DontCare }
}
case class AXISDataField(width: Int) extends BundleField[UInt](AXISData, Output(UInt(width.W)), _ := DontCare)

class AXISBundleBits(val params: AXISBundleParameters) extends BundleMap(AXISBundle.keys(params)) {
def last = if (params.hasLast) apply(AXISLast) else true.B
Expand Down
29 changes: 12 additions & 17 deletions src/main/scala/amba/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,18 @@ package object amba {
}

case object AMBAProt extends ControlKey[AMBAProtBundle]("amba_prot")
case class AMBAProtField() extends BundleField(AMBAProt) {
def data = Output(new AMBAProtBundle)
def default(x: AMBAProtBundle): Unit = {
x.bufferable := false.B
x.modifiable := false.B
x.readalloc := false.B
x.writealloc := false.B
x.privileged := true.B
x.secure := true.B
x.fetch := false.B
}
}

case class AMBAProtField() extends BundleField[AMBAProtBundle](AMBAProt, Output(new AMBAProtBundle), x => {
x.bufferable := false.B
x.modifiable := false.B
x.readalloc := false.B
x.writealloc := false.B
x.privileged := true.B
x.secure := true.B
x.fetch := false.B
})

// Used to convert a TileLink corrupt signal into an AMBA user bit
case object AMBACorrupt extends DataKey[Bool]("corrupt")
case class AMBACorruptField() extends BundleField(AMBACorrupt) {
def data = Output(Bool())
def default(x: Bool): Unit = { x := false.B }
}
}
case class AMBACorruptField() extends BundleField[Bool](AMBACorrupt, Output(Bool()), x => x := false.B)
}
11 changes: 4 additions & 7 deletions src/main/scala/tilelink/RegisterRouter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,10 @@ class TLRegisterRouterExtraBundle(val sourceBits: Int, val sizeBits: Int) extend
}

case object TLRegisterRouterExtra extends ControlKey[TLRegisterRouterExtraBundle]("tlrr_extra")
case class TLRegisterRouterExtraField(sourceBits: Int, sizeBits: Int) extends BundleField(TLRegisterRouterExtra) {
def data = Output(new TLRegisterRouterExtraBundle(sourceBits, sizeBits))
def default(x: TLRegisterRouterExtraBundle) = {
x.size := 0.U
x.source := 0.U
}
}
case class TLRegisterRouterExtraField(sourceBits: Int, sizeBits: Int) extends BundleField[TLRegisterRouterExtraBundle](TLRegisterRouterExtra, Output(new TLRegisterRouterExtraBundle(sourceBits, sizeBits)), x => {
x.size := 0.U
x.source := 0.U
})

/** TLRegisterNode is a specialized TL SinkNode that encapsulates MMIO registers.
* It provides functionality for describing and outputting metdata about the registers in several formats.
Expand Down
11 changes: 4 additions & 7 deletions src/main/scala/tilelink/ToAXI4.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ class AXI4TLStateBundle(val sourceBits: Int) extends Bundle {
}

case object AXI4TLState extends ControlKey[AXI4TLStateBundle]("tl_state")
case class AXI4TLStateField(sourceBits: Int) extends BundleField(AXI4TLState) {
def data = Output(new AXI4TLStateBundle(sourceBits))
def default(x: AXI4TLStateBundle) = {
x.size := 0.U
x.source := 0.U
}
}
case class AXI4TLStateField(sourceBits: Int) extends BundleField[AXI4TLStateBundle](AXI4TLState, Output(new AXI4TLStateBundle(sourceBits)), x => {
x.size := 0.U
x.source := 0.U
})

/** TLtoAXI4IdMap serves as a record for the translation performed between id spaces.
*
Expand Down
185 changes: 41 additions & 144 deletions src/main/scala/util/BundleMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
package freechips.rocketchip.util

import chisel3._
import chisel3.experimental.DataMirror
import scala.collection.immutable.ListMap
import scala.collection.mutable.HashMap
import chisel3.reflect.DataMirror

import scala.collection.immutable.SeqMap
import scala.collection.immutable.HashMap

/* BundleMaps include IOs for every BundleField they are constructed with.
* A given BundleField in a BundleMap is accessed by a BundleKey.
Expand Down Expand Up @@ -35,21 +36,14 @@ sealed trait BundleFieldBase {
}
}

/* Always extends BundleField with a case class.
* This will ensure that there is an appropriate equals() operator to detect name conflicts.
*/
abstract class BundleField[T <: Data](val key: BundleKey[T]) extends BundleFieldBase
{
def data: T
def default(x: T): Unit
abstract class BundleField[T <: Data](val key: BundleKey[T], typeT: => T, val default: T => Unit) extends BundleFieldBase {
def data: T = typeT
def defaultFlip(x: T): Unit = {}
def setDataDefault(x: Data): Unit = default(x.asInstanceOf[T])
def setDataDefaultFlip(x: Data): Unit = defaultFlip(x.asInstanceOf[T])
}

abstract class SimpleBundleField[T <: Data](key: BundleKey[T])(typeT: => T, defaultT: => T) extends BundleField(key)
{
def data = typeT
def default(x: T): Unit = { x := defaultT }
}
abstract class SimpleBundleField[T <: Data](key: BundleKey[T])(typeT: => T, defaultT: => T) extends BundleField(key, typeT, { x: T => x := defaultT })

object BundleField {
/* Consider an arbiter that receives two request streams A and B and combines them to C.
Expand Down Expand Up @@ -100,11 +94,11 @@ abstract class DataKey [T <: Data](name: String) extends BundleKey[T](name) wi
* Generally, this categorization belongs in different BundleMaps
*/

class BundleMap(val fields: Seq[BundleFieldBase]) extends Record with CustomBulkAssignable {
class BundleMap(val fields: Seq[BundleFieldBase]) extends Record {
// All fields must have distinct key.names
require(fields.map(_.key.name).distinct.size == fields.size)

val elements: ListMap[String, Data] = ListMap(fields.map { bf => bf.key.name -> chisel3.experimental.DataMirror.internal.chiselTypeClone(bf.data) } :_*)
val elements: SeqMap[String, Data] = SeqMap(fields.map { bf => bf.key.name -> chisel3.reflect.DataMirror.internal.chiselTypeClone(bf.data) } :_*)

// A BundleMap is best viewed as a map from BundleKey to Data
def keydata: Seq[(BundleKeyBase, Data)] = (fields zip elements) map { case (field, (_, data)) => (field.key, data) }
Expand All @@ -121,142 +115,45 @@ class BundleMap(val fields: Seq[BundleFieldBase]) extends Record with CustomBulk
out :<= this
out
}

// Assign all outputs of this from either:
// outputs of that (if they exist)
// or the default value for the BundleField
def assignL(that: CustomBulkAssignable): Unit = { // this/bx :<= that/by
require(that.isInstanceOf[BundleMap], s"Illegal attempt to drive BundleMap ${this} :<= non-BundleMap ${that}")
val bx = this
val by = that.asInstanceOf[BundleMap]
val hy = HashMap(by.elements.toList:_*)
(bx.fields zip bx.elements) foreach { case (field, (_, vx)) =>
hy.lift(field.key.name) match {
case Some(vy) => FixChisel3.descendL(vx, vy)
case None => DataMirror.specifiedDirectionOf(vx) match {
case SpecifiedDirection.Output => field.setDataDefault(vx)
case SpecifiedDirection.Input => ()
case _ => require(false, s"Attempt to assign ${bx} :<= ${by}, where RHS is missing directional field ${field}")
}
}
}
// it's ok to have excess elements in 'hy'
}

// Assign all inputs of that from either:
// inputs of this (if they exist)
// or the default value for the BundleField
def assignR(that: CustomBulkAssignable): Unit = { // this/bx :=> that/by
require(that.isInstanceOf[BundleMap], s"Illegal attempt to drive BundleMap ${this} :=> non-BundleMap ${that}")
def bx = this
def by = that.asInstanceOf[BundleMap]
val hx = HashMap(bx.elements.toList:_*)
(by.fields zip by.elements) foreach { case (field, (_, vy)) =>
hx.lift(field.key.name) match {
case Some(vx) => FixChisel3.descendR(vx, vy)
case None => DataMirror.specifiedDirectionOf(vy) match {
case SpecifiedDirection.Output => ()
case SpecifiedDirection.Input => field.setDataDefault(vy)
case _ => require (false, s"Attempt to assign ${bx} :=> ${by}, where LHS is missing directional field ${field}")
}
}
}
// it's ok to have excess elements in 'hx'
}

// Assign only those outputs of this which exist as outputs in that
def partialAssignL(that: BundleMap): Unit = {
val h = HashMap(that.keydata:_*)
keydata foreach { case (key, vx) =>
h.lift(key).foreach { vy => FixChisel3.descendL(vx, vy) }
}
}

// Assign only those inputs of that which exist as inputs in this
def partialAssignR(that: BundleMap): Unit = {
val h = HashMap(keydata:_*)
that.keydata foreach { case (key, vy) =>
h.lift(key).foreach { vx => FixChisel3.descendL(vx, vy) }
}
}
}

object BundleMap {
def apply(fields: Seq[BundleFieldBase] = Nil) = new BundleMap(fields)
}

trait CustomBulkAssignable {
def assignL(that: CustomBulkAssignable): Unit // Custom implementation of :<=
def assignR(that: CustomBulkAssignable): Unit // Custom implementaiton of :=>
}

// Implement the primitives of bulk assignment, :<= and :=>
object FixChisel3 {
// Used by :<= for child elements to switch directionality
def descendL(x: Data, y: Data): Unit = {
DataMirror.specifiedDirectionOf(x) match {
case SpecifiedDirection.Unspecified => assignL(x, y)
case SpecifiedDirection.Output => assignL(x, y); assignR(y, x)
case SpecifiedDirection.Input => ()
case SpecifiedDirection.Flip => assignR(y, x)
}
}

// Used by :=> for child elements to switch directionality
def descendR(x: Data, y: Data): Unit = {
DataMirror.specifiedDirectionOf(y) match {
case SpecifiedDirection.Unspecified => assignR(x, y)
case SpecifiedDirection.Output => ()
case SpecifiedDirection.Input => assignL(y, x); assignR(x, y)
case SpecifiedDirection.Flip => assignL(y, x)
/** Sets the default values of all bundle map elements that are aligned w.r.t. d */
def setAlignedDefaults[T <: Data](c: Connectable[T]): Connectable[T] = {
DataMirror.collectAlignedDeep(c.base) { case member: BundleMap =>
member.fields.foreach { f =>
f.setDataDefault(member.elements(f.key.name))
}
}
c
}

// The default implementation of 'x :<= y'
// Assign all output fields of x from y
def assignL(x: Data, y: Data): Unit = {
(x, y) match {
case (cx: CustomBulkAssignable, cy: CustomBulkAssignable) => cx.assignL(cy)
case (vx: Vec[_], vy: Vec[_]) => {
require (vx.size == vy.size, s"Assignment between vectors of unequal length (${vx.size} != ${vy.size})")
(vx zip vy) foreach { case (ex, ey) => descendL(ex, ey) }
/** Sets the default values of all bundle map elements that are flipped w.r.t. d */
def setFlippedDefaults[T <: Data](c: Connectable[T]): Connectable[T] = {
DataMirror.collectFlippedDeep(c.base) { case member: BundleMap =>
member.fields.foreach { f =>
f.setDataDefault(member.elements(f.key.name))
}
case (rx: Record, ry: Record) => {
val hy = HashMap(ry.elements.toList:_*)
rx.elements.foreach { case (key, vx) =>
require (hy.contains(key), s"Attempt to assign ${x} :<= ${y}, where RHS is missing field ${key}")
descendL(vx, hy(key))
}
hy --= rx.elements.keys
require (hy.isEmpty, s"Attempt to assign ${x} :<= ${y}, where RHS has excess field ${hy.last._1}")
}
case (vx: Vec[_], DontCare) => vx.foreach { case ex => descendL(ex, DontCare) }
case (rx: Record, DontCare) => rx.elements.foreach { case (_, dx) => descendL(dx, DontCare) }
case _ => x := y // assign leaf fields (UInt/etc)
}
c
}

// The default implementation of 'x :=> y'
// Assign all input fields of y from x
def assignR(x: Data, y: Data) = {
(x, y) match {
case (cx: CustomBulkAssignable, cy: CustomBulkAssignable) => cx.assignR(cy)
case (vx: Vec[_], vy: Vec[_]) => {
require (vx.size == vy.size, s"Assignment between vectors of unequal length (${vx.size} != ${vy.size})")
(vx zip vy) foreach { case (ex, ey) => descendR(ex, ey) }
/** Waives all bundle map elements */
def waive[T <: Data](c: Connectable[T]): Connectable[T] = {
val bundleFields = DataMirror
.collectMembers(c.base) { case member: BundleMap =>
member.getElements
}
case (rx: Record, ry: Record) => {
val hx = HashMap(rx.elements.toList:_*)
ry.elements.foreach { case (key, vy) =>
require (hx.contains(key), s"Attempt to assign ${x} :=> ${y}, where RHS has excess field ${key}")
descendR(hx(key), vy)
}
hx --= ry.elements.keys
require (hx.isEmpty, s"Attempt to assign ${x} :=> ${y}, where RHS is missing field ${hx.last._1}")
}
case (DontCare, vy: Vec[_]) => vy.foreach { case ey => descendR(DontCare, ey) }
case (DontCare, ry: Record) => ry.elements.foreach { case (_, dy) => descendR(DontCare, dy) }
case _ => // no-op for leaf fields
}
.flatten
Connectable(c.base, bundleFields.toSet)
}
/** Waives all bundle map elements and sets the default values of all bundle map elements that are aligned w.r.t. d */
def waiveAndSetAlignedDefaults[T <: Data](c: Connectable[T]): Connectable[T] = {
setAlignedDefaults(c)
waive(c)
}
/** Waives all bundle map elements and sets the default values of all bundle map elements that are flipped w.r.t. d */
def waiveAndSetFlippedDefaults[T <: Data](c: Connectable[T]): Connectable[T] = {
setFlippedDefaults(c)
waive(c)
}
}

0 comments on commit 7c9670b

Please sign in to comment.