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

Attach TLError LogicalTreeNodes to subsystem LogicalTreeNode #2410

Merged
merged 10 commits into from
Apr 16, 2020
29 changes: 21 additions & 8 deletions src/main/scala/devices/tilelink/CanHaveBuiltInDevices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,36 @@ trait HasBuiltInDeviceParams {
val errorDevice: Option[DevNullParams]
}

/* Optionally add some built-in devices to a bus wrapper */
trait CanHaveBuiltInDevices { this: TLBusWrapper =>
sealed trait BuiltInDevices {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This more of a question for me to learn more about Scala than about whether this is the right or wrong thing to do.

Since this is only used in one place, in an anonymous class instance, what's the practical difference between defining this as a sealed trait + anonymous class instance vs. defining a (final) case class and assigning the members normally? I think I can appreciate this technique being used for non-sealed traits, but the fact that this is sealed is interesting to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I the main difference I care about vs a final case class is that the case class generates a bunch of methods and extends stuff that I don't think are appropriate/useful. like case class extends serializable which TLError and TLZero definitely are not and it generates stuff like unapply that I don't want exposed because that API will probably be broken.

sealed is for similar reasons. to prevent anyone else from creating BuiltInDevices outside of attachBuiltInDevices and just narrow down the API surface area.

just my personal preference, maybe I'm being a too cautious or this isn't the right approach? open to changing this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just my personal preference, maybe I'm being a too cautious or this isn't the right approach? open to changing this.

No, I think it's a good idea to limit API surface area. I was mostly wondering sealed trait vs final case class and not sealed trait vs (not sealed) trait. I hadn't thought of the extra methods and traits that case classes provide as being extra stuff that consumers could possibly depend on that would become later liabilities, but now that you point it out, I agree, especially because this data structure is, at the moment, an arbitrary bundle of "stuff", and we may want to reserve the right to change this in the future.

def errorOpt: Option[TLError]
def zeroOpt: Option[TLZero]
}

def attachBuiltInDevices(params: HasBuiltInDeviceParams) {
params.errorDevice.foreach { dnp => LazyScope("wrapped_error_device") {
object BuiltInDevices {
def attach(
params: HasBuiltInDeviceParams with HasTLBusParams,
outwardNode: TLOutwardNode)(implicit p: Parameters) = new BuiltInDevices {
val errorOpt = params.errorDevice.map { dnp => LazyScope("wrapped_error_device") {
val error = LazyModule(new TLError(
params = dnp,
beatBytes = beatBytes))
beatBytes = params.beatBytes))

error.node := TLBuffer() := outwardNode
error
}}

params.zeroDevice.foreach { addr => LazyScope("wrapped_zero_device") {
val zeroOpt = params.zeroDevice.map { addr => LazyScope("wrapped_zero_device") {
val zero = LazyModule(new TLZero(
address = addr,
beatBytes = beatBytes))
zero.node := TLFragmenter(beatBytes, blockBytes) := TLBuffer() := outwardNode
beatBytes = params.beatBytes))
zero.node := TLFragmenter(params.beatBytes, params.blockBytes) := TLBuffer() := outwardNode
zero
}}
}
}

/* Optionally add some built-in devices to a bus wrapper */
trait CanHaveBuiltInDevices {
def builtInDevices: BuiltInDevices
}

2 changes: 1 addition & 1 deletion src/main/scala/devices/tilelink/DevNull.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ case class DevNullParams(
* They may discard writes, refuse to respond to requests, issue error responses,
* or otherwise violate 'expected' memory behavior.
*/
abstract class DevNullDevice(params: DevNullParams, minLatency: Int, beatBytes: Int, device: SimpleDevice)
abstract class DevNullDevice(params: DevNullParams, minLatency: Int, beatBytes: Int, protected val device: SimpleDevice)
(implicit p: Parameters)
extends LazyModule with HasClockDomainCrossing {
val xfer = if (params.maxTransfer > 0) TransferSizes(1, params.maxTransfer) else TransferSizes.none
Expand Down
20 changes: 20 additions & 0 deletions src/main/scala/devices/tilelink/Error.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,32 @@ import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
import scala.math.min

import freechips.rocketchip.diplomaticobjectmodel.{DiplomaticObjectModelAddressing, HasLogicalTreeNode}
import freechips.rocketchip.diplomaticobjectmodel.logicaltree.LogicalTreeNode
import freechips.rocketchip.diplomaticobjectmodel.model.{OMErrorDevice, OMComponent}

/** Adds a /dev/null slave that generates TL error response messages */
class TLError(params: DevNullParams, buffer: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters)
extends DevNullDevice(params,
minLatency = if (buffer) 1 else 0,
beatBytes, new SimpleDevice("error-device", Seq("sifive,error0")))
with HasLogicalTreeNode
{
lazy val logicalTreeNode: LogicalTreeNode = new LogicalTreeNode(() => Some(device)) {
def getOMComponents(resourceBindings: ResourceBindings, children: Seq[OMComponent] = Nil) = {
val Description(name, mapping) = device.describe(resourceBindings)
val memRegions = DiplomaticObjectModelAddressing.getOMMemoryRegions(name, resourceBindings, None)
val interrupts = DiplomaticObjectModelAddressing.describeInterrupts(name, resourceBindings)
Seq(OMErrorDevice(
memoryRegions = memRegions.map(_.copy(
name = "errordevice",
description = "Error Device"
)),
interrupts = interrupts
))
}
}

lazy val module = new LazyModuleImp(this) {
import TLMessages._
import TLPermissions._
Expand Down
24 changes: 23 additions & 1 deletion src/main/scala/devices/tilelink/Zero.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink.TLMessages

import freechips.rocketchip.diplomaticobjectmodel.{DiplomaticObjectModelAddressing, HasLogicalTreeNode}
import freechips.rocketchip.diplomaticobjectmodel.logicaltree.LogicalTreeNode
import freechips.rocketchip.diplomaticobjectmodel.model.{OMZeroDevice, OMComponent}

/** This /dev/null device accepts single beat gets/puts, as well as atomics.
* Response data is always 0. Reequests to write data have no effect.
*/
Expand All @@ -22,7 +26,25 @@ class TLZero(address: AddressSet, beatBytes: Int = 4)(implicit p: Parameters)
mayDenyPut = false),
minLatency = 1,
beatBytes = beatBytes,
device = new SimpleDevice("rom", Seq("ucbbar,cacheable-zero0"))) {
device = new SimpleDevice("rom", Seq("ucbbar,cacheable-zero0")))
with HasLogicalTreeNode
{

lazy val logicalTreeNode: LogicalTreeNode = new LogicalTreeNode(() => Some(device)) {
def getOMComponents(resourceBindings: ResourceBindings, children: Seq[OMComponent] = Nil) = {
val Description(name, mapping) = device.describe(resourceBindings)
val memRegions = DiplomaticObjectModelAddressing.getOMMemoryRegions(name, resourceBindings, None)
val interrupts = DiplomaticObjectModelAddressing.describeInterrupts(name, resourceBindings)
Seq(OMZeroDevice(
memoryRegions = memRegions.map(_.copy(
name = "zerodevice",
description = "Zero Device"
)),
interrupts = interrupts
))
}
}

lazy val module = new LazyModuleImp(this) {
val (in, edge) = node.in(0)

Expand Down
10 changes: 10 additions & 0 deletions src/main/scala/diplomaticobjectmodel/model/OMErrorDevice.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// See LICENSE.SiFive for license details.

package freechips.rocketchip.diplomaticobjectmodel.model


case class OMErrorDevice(
memoryRegions: Seq[OMMemoryRegion],
interrupts: Seq[OMInterrupt],
_types: Seq[String] = Seq("OMErrorDevice", "OMDevice", "OMComponent", "OMCompoundType")
) extends OMDevice
10 changes: 10 additions & 0 deletions src/main/scala/diplomaticobjectmodel/model/OMZeroDevice.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// See LICENSE.SiFive for license details.

package freechips.rocketchip.diplomaticobjectmodel.model


case class OMZeroDevice(
memoryRegions: Seq[OMMemoryRegion],
interrupts: Seq[OMInterrupt],
_types: Seq[String] = Seq("OMZeroDevice", "OMDevice", "OMComponent", "OMCompoundType")
) extends OMDevice
20 changes: 19 additions & 1 deletion src/main/scala/subsystem/BaseSubsystem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ case object ResetAsynchronousFull extends SubsystemResetScheme
case object SubsystemResetSchemeKey extends Field[SubsystemResetScheme](ResetSynchronous)

/** Base Subsystem class with no peripheral devices or ports added */
abstract class BaseSubsystem(implicit p: Parameters) extends BareSubsystem
abstract class BaseSubsystem(implicit p: Parameters) extends BareSubsystem
with Attachable {

override val module: BaseSubsystemModuleImp[BaseSubsystem]
Expand Down Expand Up @@ -135,6 +135,24 @@ abstract class BaseSubsystem(implicit p: Parameters) extends BareSubsystem
}

lazy val logicalTreeNode = new SubsystemLogicalTreeNode()

private val buses = Seq(
sbus,
pbus,
fbus,
mbus,
cbus
)

buses.foreach { bus =>
val builtIn = bus.builtInDevices
builtIn.errorOpt.foreach { error =>
LogicalModuleTree.add(logicalTreeNode, error.logicalTreeNode)
}
builtIn.zeroOpt.foreach { zero =>
LogicalModuleTree.add(logicalTreeNode, zero.logicalTreeNode)
}
}
}


Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/subsystem/FrontBus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ class FrontBus(params: FrontBusParams)(implicit p: Parameters)
with CanHaveBuiltInDevices
with CanAttachTLMasters
with HasTLXbarPhy {
attachBuiltInDevices(params)
val builtInDevices: BuiltInDevices = BuiltInDevices.attach(params, outwardNode)
}
2 changes: 1 addition & 1 deletion src/main/scala/subsystem/MemoryBus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class MemoryBus(params: MemoryBusParams)(implicit p: Parameters)
if (params.replicatorMask == 0) xbar.node else { xbar.node :=* RegionReplicator(params.replicatorMask) }
def outwardNode: TLOutwardNode = ProbePicker() :*= xbar.node
def busView: TLEdge = xbar.node.edges.in.head
attachBuiltInDevices(params)
val builtInDevices: BuiltInDevices = BuiltInDevices.attach(params, outwardNode)

def toDRAMController[D,U,E,B <: Data]
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/subsystem/PeripheryBus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class PeripheryBus(params: PeripheryBusParams, name: String)(implicit p: Paramet
def outwardNode: TLOutwardNode = node
def busView: TLEdge = fixer.node.edges.in.head

attachBuiltInDevices(params)
val builtInDevices: BuiltInDevices = BuiltInDevices.attach(params, outwardNode)

def toTile
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/subsystem/SystemBus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters)
def outwardNode: TLOutwardNode = system_bus_xbar.node
def busView: TLEdge = system_bus_xbar.node.edges.in.head

attachBuiltInDevices(params)
val builtInDevices: BuiltInDevices = BuiltInDevices.attach(params, outwardNode)

def fromTile
(name: Option[String], buffer: BufferParams = BufferParams.none, cork: Option[Boolean] = None)
Expand Down