Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New BundleMap #3419

Merged
merged 2 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
Loading