1
0

Merge pull request #567 from ucb-bar/dts

Dts - Device Tree Output
This commit is contained in:
Wesley W. Terpstra 2017-03-02 21:51:51 -08:00 committed by GitHub
commit 46369850cf
36 changed files with 754 additions and 228 deletions

View File

@ -49,13 +49,12 @@ trait HasCoreplexParameters {
lazy val cbusConfig = p(CBusConfig)
lazy val l1tol2Config = p(L1toL2Config)
lazy val nTiles = tilesParams.size
lazy val hasSupervisor = tilesParams.exists(_.core.useVM) // TODO ask andrew about this
lazy val l2Config = p(BankedL2Config)
}
case class CoreplexParameters(implicit val p: Parameters) extends HasCoreplexParameters
abstract class BareCoreplex(implicit p: Parameters) extends LazyModule
abstract class BareCoreplex(implicit p: Parameters) extends LazyModule with BindingScope
abstract class BareCoreplexBundle[+L <: BareCoreplex](_outer: L) extends GenericParameterizedBundle(_outer) {
val outer = _outer

View File

@ -11,6 +11,7 @@ import util._
trait CoreplexNetwork extends HasCoreplexParameters {
val module: CoreplexNetworkModule
def bindingTree: ResourceMap
val l1tol2 = LazyModule(new TLXbar)
val l1tol2_beatBytes = l1tol2Config.beatBytes
@ -40,6 +41,53 @@ trait CoreplexNetwork extends HasCoreplexParameters {
mmio :=
TLWidthWidget(l1tol2_beatBytes)(
l1tol2.node)
val root = new Device {
def describe(resources: ResourceBindings): Description = {
val width = resources("width").map(_.value)
Description("/", Map(
"#address-cells" -> width,
"#size-cells" -> width,
"model" -> Seq(ResourceString(p(DTSModel))),
"compatible" -> (p(DTSModel) +: p(DTSCompat)).map(s => ResourceString(s + "-dev"))))
}
}
val soc = new Device {
def describe(resources: ResourceBindings): Description = {
val width = resources("width").map(_.value)
Description("soc", Map(
"#address-cells" -> width,
"#size-cells" -> width,
"compatible" -> (p(DTSModel) +: p(DTSCompat)).map(s => ResourceString(s + "-soc")),
"ranges" -> Nil))
}
}
val cpus = new Device {
def describe(resources: ResourceBindings): Description = {
Description("cpus", Map(
"#address-cells" -> Seq(ResourceInt(1)),
"#size-cells" -> Seq(ResourceInt(0)),
"timebase-frequency" -> Seq(ResourceInt(p(DTSTimebase)))))
}
}
ResourceBinding {
val managers = l1tol2.node.edgesIn.headOption.map(_.manager.managers).getOrElse(Nil)
val max = managers.flatMap(_.address).map(_.max).max
val width = ResourceInt((log2Ceil(max)+31) / 32)
Resource(root, "width").bind(width)
Resource(soc, "width").bind(width)
Resource(cpus, "null").bind(ResourceString(""))
managers.foreach { case manager =>
val value = manager.toResource
manager.resources.foreach { case resource =>
resource.bind(value)
}
}
}
}
trait CoreplexNetworkBundle extends HasCoreplexParameters {
@ -54,16 +102,17 @@ trait CoreplexNetworkModule extends HasCoreplexParameters {
val outer: CoreplexNetwork
val io: CoreplexNetworkBundle
println("\nGenerated Address Map")
println("Generated Address Map")
for (manager <- outer.l1tol2.node.edgesIn(0).manager.managers) {
val prot = (if (manager.supportsGet) "R" else "") +
(if (manager.supportsPutFull) "W" else "") +
(if (manager.executable) "X" else "") +
(if (manager.supportsAcquireB) " [C]" else "")
manager.address.foreach { a =>
println(f"\t${manager.name}%s ${a.base}%x - ${a.base+a.mask+1}%x, $prot")
AddressRange.fromSets(manager.address).foreach { r =>
println(f"\t${manager.name}%s ${r.base}%x - ${r.base+r.size}%x, $prot")
}
}
println("")
}
/////

View File

@ -15,7 +15,7 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork {
val module: CoreplexRISCVPlatformModule
val debug = LazyModule(new TLDebugModule())
val plic = LazyModule(new TLPLIC(hasSupervisor, maxPriorities = 7))
val plic = LazyModule(new TLPLIC(maxPriorities = 7))
val clint = LazyModule(new CoreplexLocalInterrupter)
debug.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node)
@ -24,10 +24,7 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork {
plic.intnode := intBar.intnode
lazy val configString = {
val managers = l1tol2.node.edgesIn(0).manager.managers
rocketchip.GenerateConfigString(p, clint, plic, managers)
}
lazy val dts = DTS(bindingTree)
}
trait CoreplexRISCVPlatformBundle extends CoreplexNetworkBundle {
@ -50,6 +47,6 @@ trait CoreplexRISCVPlatformModule extends CoreplexNetworkModule {
val rtcLast = Reg(init = Bool(false), next=rtcSync)
outer.clint.module.io.rtcTick := Reg(init = Bool(false), next=(rtcSync & (~rtcLast)))
println(s"\nGenerated Configuration String\n${outer.configString}")
ElaborationArtefacts.add("cfg", outer.configString)
println(outer.dts)
ElaborationArtefacts.add("dts", outer.dts)
}

View File

@ -23,16 +23,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
private val crossing = p(RocketCrossing)
private val configs = p(RocketTilesKey)
private val rocketTileIntNodes = configs.map { _ => IntInternalOutputNode() }
rocketTileIntNodes.foreach { _ := plic.intnode }
private def wireInterrupts(x: TileInterrupts, i: Int) {
x := clint.module.io.tiles(i)
x.debug := debug.module.io.debugInterrupts(i)
x.meip := rocketTileIntNodes(i).bundleOut(0)(0)
x.seip.foreach { _ := rocketTileIntNodes(i).bundleOut(0)(1) }
}
val rocketWires: Seq[HasRocketTilesBundle => Unit] = configs.zipWithIndex.map { case (c, i) =>
val pWithExtra = p.alterPartial {
case TileKey => c
@ -41,50 +31,62 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
case PAddrBits => l1tol2.node.edgesIn(0).bundle.addressBits
}
// Hack debug interrupt into a node (future debug module should use diplomacy)
val debugNode = IntInternalInputNode(IntSourcePortSimple())
val intBar = LazyModule(new IntXbar)
intBar.intnode := debugNode
intBar.intnode := clint.intnode // msip+mtip
intBar.intnode := plic.intnode // meip
if (c.core.useVM) intBar.intnode := plic.intnode // seip
crossing match {
case Synchronous => {
val tile = LazyModule(new RocketTile(c)(pWithExtra))
val tile = LazyModule(new RocketTile(c, i)(pWithExtra))
val buffer = LazyModule(new TLBuffer)
buffer.node :=* tile.masterNode
l1tol2.node :=* buffer.node
tile.slaveNode :*= cbus.node
tile.intNode := intBar.intnode
(io: HasRocketTilesBundle) => {
// leave clock as default (simpler for hierarchical PnR)
tile.module.io.hartid := UInt(i)
tile.module.io.resetVector := io.resetVector
wireInterrupts(tile.module.io.interrupts, i)
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
}
}
case Asynchronous(depth, sync) => {
val wrapper = LazyModule(new AsyncRocketTile(c)(pWithExtra))
val wrapper = LazyModule(new AsyncRocketTile(c, i)(pWithExtra))
val sink = LazyModule(new TLAsyncCrossingSink(depth, sync))
val source = LazyModule(new TLAsyncCrossingSource(sync))
sink.node :=* wrapper.masterNode
l1tol2.node :=* sink.node
wrapper.slaveNode :*= source.node
wrapper.intNode := intBar.intnode
source.node :*= cbus.node
(io: HasRocketTilesBundle) => {
wrapper.module.clock := io.tcrs(i).clock
wrapper.module.reset := io.tcrs(i).reset
wrapper.module.io.hartid := UInt(i)
wrapper.module.io.resetVector := io.resetVector
wireInterrupts(wrapper.module.io.interrupts, i)
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
}
}
case Rational => {
val wrapper = LazyModule(new RationalRocketTile(c)(pWithExtra))
val wrapper = LazyModule(new RationalRocketTile(c, i)(pWithExtra))
val sink = LazyModule(new TLRationalCrossingSink(util.FastToSlow))
val source = LazyModule(new TLRationalCrossingSource)
sink.node :=* wrapper.masterNode
l1tol2.node :=* sink.node
wrapper.slaveNode :*= source.node
wrapper.intNode := intBar.intnode
source.node :*= cbus.node
(io: HasRocketTilesBundle) => {
wrapper.module.clock := io.tcrs(i).clock
wrapper.module.reset := io.tcrs(i).reset
wrapper.module.io.hartid := UInt(i)
wrapper.module.io.resetVector := io.resetVector
wireInterrupts(wrapper.module.io.interrupts, i)
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
}
}
}

View File

@ -0,0 +1,117 @@
// See LICENSE.SiFive for license details.
package diplomacy
import Chisel._
import config._
case object DTSModel extends Field[String]
case object DTSCompat extends Field[Seq[String]] // -dev, -soc
case object DTSTimebase extends Field[BigInt] // Clock frequency of clint RTC (use 0 if you don't know it)
object DTS
{
def apply(res: ResourceValue): String = "/dts-v1/;\n\n" + helper(res, "", defaultCells).mkString("")
private val nodeStartChars = (('a' to 'z') ++ ('A' to 'Z')).toSet
private val nodeChars = (('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9') ++ Seq(',', '.', '_', '+', '-', '@')).toSet
def legalNode(x: String): Boolean =
x == "/" || (!x.isEmpty && x.size < 32 && nodeStartChars.contains(x(0)) && x.forall(nodeChars.contains(_)))
// the DTS spec does not list '-', but uses it everywhere ...
private val propChars = (('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9') ++ Seq(',', '.', '_', '+', '?', '#', '-')).toSet
def legalProperty(x: String): Boolean =
x == "/" || (!x.isEmpty && x.size < 32 && x.forall(propChars.contains(_)))
// The DTS spec doesn't say what is allowed in a string, so just use our own judgement
private val strChars = (('#' to '[') ++ (']' to '~') ++ Seq(' ', '!')).toSet
def legalString(x: String): Boolean = x.forall(strChars.contains(_))
private case class Cells(
parentAddress: Int,
parentSize: Int,
selfAddress: Int,
selfSize: Int)
private val defaultCells = Cells(2, 1, 2, 1)
private def fmtCell(x: BigInt, cells: Int): Seq[String] = {
val cellbits = 32
val mask = (BigInt(1) << cellbits) - 1
(0 until cells).reverse map { case i => "0x%x".format((x >> (i*cellbits)) & mask) }
}
private def fmtAddress(x: ResourceAddress, cells: Cells): Seq[String] = {
val ranges = AddressRange.fromSets(x.address)
ranges.flatMap { case AddressRange(base, size) =>
fmtCell(base, cells.parentAddress) ++ fmtCell(size, cells.parentSize)
}
}
private def fmtMapping(x: ResourceMapping, cells: Cells): Seq[String] = {
val ranges = AddressRange.fromSets(x.address)
ranges.flatMap { case AddressRange(base, size) =>
fmtCell(base+x.offset, cells.selfAddress) ++ fmtCell(base, cells.parentAddress) ++ fmtCell(size, cells.selfSize)
}
}
private def fmtString(x: ResourceString): Seq[String] = {
require (legalString(x.value), s"The string '${x.value}' contains chars probably unwise for use in a DTS string")
Seq("\"" + x.value + "\"")
}
private def fmtMap(x: ResourceMap, indent: String, cells: Cells): Seq[String] = {
val (nodes, props) = x.value.partition(_ match {
case (_, Seq(ResourceMap(_, _))) => true
case _ => false
})
def getInt(x: ResourceValue) = x match {
case ResourceInt(value) => Some(value.toInt)
case _ => None
}
val selfAddress = x.value.getOrElse("#address-cells", Nil).headOption.flatMap(getInt)
val selfSize = x.value.getOrElse("#size-cells", Nil).headOption.flatMap(getInt)
val myCells = Cells(
parentAddress = cells.selfAddress,
parentSize = cells.selfSize,
selfAddress = selfAddress.getOrElse(defaultCells.selfAddress),
selfSize = selfSize .getOrElse(defaultCells.selfSize))
props.flatMap { case (k, seq) =>
require (legalProperty(k), s"The string '${k}' is not a legal DTS property name")
seq.headOption match {
case None => Seq(indent, k, ";\n")
case Some(ResourceString(_)) => {
seq.foreach { r => r match {
case ResourceString(_) => Unit
case _ => require(false, s"The property '${k}' has values of conflicting type: ${seq}")
} }
Seq(indent, k, " = ", seq.flatMap(z => helper(z, "", myCells)).mkString(", "), ";\n")
}
case Some(_) => {
seq.foreach { r => r match {
case ResourceMap(_, _) => require(false, s"The property '${k}' has values of conflicting type: ${seq}")
case ResourceString(_) => require(false, s"The property '${k}' has values of conflicting type: ${seq}")
case _ => Unit
} }
Seq(indent, k, " = <", seq.flatMap(z => helper(z, "", myCells)).mkString(" "), ">;\n")
}
}
}.toList ++
nodes.flatMap { case (k, Seq(s: ResourceMap)) =>
require (legalNode(k), s"The string '${k}' is not a legal DTS node name")
Seq(indent) ++ s.labels.map(_ + ": ").filter(_ => !indent.isEmpty) ++ // labels on root not allowed
Seq(k, " {\n") ++ helper(s, indent + "\t", myCells) ++ Seq(indent, "};\n")
}
}
private def helper(res: ResourceValue, indent: String, cells: Cells): Seq[String] = res match {
case x: ResourceAddress => fmtAddress(x, cells)
case x: ResourceMapping => fmtMapping(x, cells)
case x: ResourceInt => Seq(x.value.toString)
case x: ResourceString => fmtString(x)
case x: ResourceReference => Seq("&" + x.value)
case x: ResourceMap => fmtMap(x, indent, cells)
}
}

View File

@ -302,8 +302,8 @@ class MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
numPI: Range.Inclusive = 1 to 999)
extends MixedNode(inner, outer)(numPO, numPI)
{
require (numPO.end >= 1, s"${name} does not accept outputs${lazyModule.line}")
require (numPI.end >= 1, s"${name} does not accept inputs${lazyModule.line}")
// require (numPO.end >= 1, s"${name} does not accept outputs${lazyModule.line}")
// require (numPI.end >= 1, s"${name} does not accept inputs${lazyModule.line}")
val externalIn: Boolean = true
val externalOut: Boolean = true

View File

@ -76,6 +76,32 @@ object TransferSizes {
implicit def asBool(x: TransferSizes) = !x.none
}
// Use AddressSet instead -- this is just for pretty printing
case class AddressRange(base: BigInt, size: BigInt) extends Ordered[AddressRange]
{
val end = base + size
require (base >= 0)
require (size > 0)
def compare(x: AddressRange) = {
val primary = (this.base - x.base).signum
val secondary = (x.size - this.size).signum
if (primary != 0) primary else secondary
}
def contains(x: AddressRange) = base <= x.base && x.end <= end
def union(x: AddressRange): Option[AddressRange] = {
if (base > x.end || x.base > end) {
None
} else {
val obase = if (base < x.base) base else x.base
val oend = if (end > x.end) end else x.end
Some(AddressRange(obase, oend-obase))
}
}
}
// AddressSets specify the address space managed by the manager
// Base is the base address, and mask are the bits consumed by the manager
// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff
@ -132,6 +158,32 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet]
"AddressSet(0x%x, ~0x%x)".format(base, ~mask)
}
}
def toRanges = {
require (finite)
val size = alignment
val fragments = mask & ~(size-1)
val bits = bitIndexes(fragments)
(BigInt(0) until (BigInt(1) << bits.size)).map { i =>
val off = bitIndexes(i).foldLeft(base) { case (a, b) => a.setBit(bits(b)) }
AddressRange(off, size)
}
}
}
object AddressRange
{
def fromSets(seq: Seq[AddressSet]): Seq[AddressRange] = unify(seq.flatMap(_.toRanges))
def unify(seq: Seq[AddressRange]): Seq[AddressRange] = {
if (seq.isEmpty) return Nil
val ranges = seq.sorted
ranges.tail.foldLeft(Seq(ranges.head)) { case (head :: tail, x) =>
head.union(x) match {
case Some(z) => z :: tail
case None => x :: head :: tail
}
}.reverse
}
}
object AddressSet

View File

@ -0,0 +1,189 @@
// See LICENSE.SiFive for license details.
package diplomacy
import Chisel._
import config._
import scala.collection.immutable.{ListMap,SortedMap}
sealed trait ResourceValue
final case class ResourceAddress(address: Seq[AddressSet], r: Boolean, w: Boolean, x: Boolean) extends ResourceValue
final case class ResourceMapping(address: Seq[AddressSet], offset: BigInt) extends ResourceValue
final case class ResourceInt(value: BigInt) extends ResourceValue
final case class ResourceString(value: String) extends ResourceValue
final case class ResourceReference(value: String) extends ResourceValue
final case class ResourceMap(value: Map[String, Seq[ResourceValue]], labels: Seq[String] = Nil) extends ResourceValue
/* If device is None, the value is global */
case class Binding(device: Option[Device], value: ResourceValue)
case class ResourceBindings(map: Map[String, Seq[Binding]])
{
def apply(key: String): Seq[Binding] = map.getOrElse(key, Nil)
}
case class Description(name: String, mapping: Map[String, Seq[ResourceValue]])
abstract class Device
{
def describe(resources: ResourceBindings): Description
val label = "L" + Device.index.toString
Device.index = Device.index + 1
}
object Device
{
private var index: Int = 0
}
trait DeviceInterrupts
{
this: Device =>
val alwaysExtended = false
def describeInterrupts(resources: ResourceBindings): Map[String, Seq[ResourceValue]] = {
val int = resources("int")
int.foreach { b => require (b.device.isDefined, "Device ${devname} property 'int' is missing user device") }
val parents = int.map(_.device.get).distinct
val simple = parents.size == 1 && !alwaysExtended
val parent =
if (!simple) None else
Some("interrupt-parent" -> Seq(ResourceReference(parents.head.label)))
val interrupts =
if (!simple) None else
Some("interrupts" -> int.map(_.value))
val interrupts_extended =
if (simple || parents.isEmpty) None else
Some("interrupts-extended" -> int.flatMap(b => Seq(ResourceReference(b.device.get.label), b.value)))
ListMap() ++ parent ++ interrupts ++ interrupts_extended
}
def int = Seq(Resource(this, "int"))
}
trait DeviceRegName
{
this: Device =>
val prefix = "soc/" // nearly everything on-chip belongs here
def describeName(devname: String, resources: ResourceBindings): String = {
val reg = resources("reg")
require (!reg.isEmpty, "Device is missing the 'reg' property")
reg.head.value match {
case x: ResourceAddress => s"${prefix}${devname}@${x.address.head.base.toString(16)}"
case _ => require(false, "Device has the wrong type of 'reg' property (${reg.head})"); ""
}
}
def reg = Seq(Resource(this, "reg"))
}
class SimpleDevice(devname: String, devcompat: Seq[String]) extends Device with DeviceInterrupts with DeviceRegName
{
def describe(resources: ResourceBindings): Description = {
val name = describeName(devname, resources)
val int = describeInterrupts(resources)
val compat =
if (devcompat.isEmpty) None else
Some("compatible" -> devcompat.map(ResourceString(_)))
Description(name,
ListMap("reg" -> resources("reg").map(_.value))
++ compat ++ int)
}
}
class MemoryDevice extends Device with DeviceRegName
{
override val prefix = ""
def describe(resources: ResourceBindings): Description = {
Description(describeName("memory", resources), ListMap(
"reg" -> resources("reg").map(_.value),
"device_type" -> Seq(ResourceString("memory"))))
}
}
case class Resource(owner: Device, key: String)
{
def bind(user: Device, value: ResourceValue) {
val scope = BindingScope.active.get
scope.resourceBindings = (this, Some(user), value) +: scope.resourceBindings
}
def bind(value: ResourceValue) {
val scope = BindingScope.active.get
scope.resourceBindings = (this, None, value) +: scope.resourceBindings
}
}
trait BindingScope
{
this: LazyModule =>
private val parentScope = BindingScope.find(parent)
protected[diplomacy] var resourceBindingFns: Seq[() => Unit] = Nil
protected[diplomacy] var resourceBindings: Seq[(Resource, Option[Device], ResourceValue)] = Nil
private case class ExpandedValue(path: Seq[String], labels: Seq[String], value: Seq[ResourceValue])
private def eval() {
require (LazyModule.stack.isEmpty, "May not evaluate binding while still constructing LazyModules")
parentScope.foreach { _.eval() }
resourceBindings = parentScope.map(_.resourceBindings).getOrElse(Nil)
BindingScope.active = Some(this)
resourceBindingFns.reverse.foreach { _() }
BindingScope.active = None
resourceBindingFns = Nil
}
private def makeTree(list: Seq[ExpandedValue]): Seq[ResourceValue] = {
val (values_p, keys_p) = list.partition(_.path.isEmpty)
val values = values_p.flatMap(_.value)
val labels = values_p.flatMap(_.labels)
val keys = keys_p.groupBy(_.path.head).toList.map { case (key, seq) =>
(key -> makeTree(seq.map { x => x.copy(path = x.path.tail) }))
// case ExpandedValue(keys, values) => ExpandedValue(keys.tail, values) }))
}
if (keys.isEmpty) values else ResourceMap(SortedMap(keys:_*), labels) +: values
}
private def expand(path: Seq[String], values: Seq[ResourceValue]): Seq[ExpandedValue] = {
ExpandedValue(path, Nil, Nil) +:
values.flatMap {
case ResourceMap(map, labels) =>
ExpandedValue(path, labels, Nil) +:
map.toList.flatMap { case (key, values) => expand(path :+ key, values) }
case z => Seq(ExpandedValue(path, Nil, Seq(z)))
}
}
def bindingTree: ResourceMap = {
eval()
val map: Map[Device, ResourceBindings] =
resourceBindings.reverse.groupBy(_._1.owner).mapValues(seq => ResourceBindings(
seq.groupBy(_._1.key).mapValues(_.map(z => Binding(z._2, z._3)))))
val tree = makeTree(map.toList.flatMap { case (d, m) =>
val Description(name, mapping) = d.describe(m)
val tokens = name.split("/").toList
expand(tokens, Seq(ResourceMap(mapping, Seq(d.label)))) })
ResourceMap(SortedMap("/" -> tree))
}
}
object BindingScope
{
protected[diplomacy] var active: Option[BindingScope] = None
protected[diplomacy] def find(m: Option[LazyModule] = LazyModule.stack.headOption): Option[BindingScope] = m.flatMap {
case s: BindingScope => Some(s)
case x => find(x.parent)
}
}
object ResourceBinding
{
def apply(block: => Unit) {
val scope = BindingScope.find()
require (scope.isDefined, "ResourceBinding must be called from within a BindingScope")
scope.get.resourceBindingFns = { () => block } +: scope.get.resourceBindingFns
}
}

View File

@ -9,4 +9,14 @@ package object diplomacy
case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix"
case _ => ""
}
def bitIndexes(x: BigInt, tail: Seq[Int] = Nil): Seq[Int] = {
require (x >= 0)
if (x == 0) {
tail.reverse
} else {
val lowest = x.lowestSetBit
bitIndexes(x.clearBit(lowest), lowest +: tail)
}
}
}

View File

@ -11,6 +11,7 @@ import util._
import Chisel.ImplicitConversions._
case class RocketCoreParams(
bootFreqHz: BigInt = 0,
useVM: Boolean = true,
useUser: Boolean = false,
useDebug: Boolean = true,

View File

@ -23,12 +23,85 @@ case class RocketTileParams(
require(dcache.isDefined)
}
class RocketTile(val rocketParams: RocketTileParams)(implicit p: Parameters) extends BaseTile(rocketParams)(p)
class RocketTile(val rocketParams: RocketTileParams, val hartid: Int)(implicit p: Parameters) extends BaseTile(rocketParams)(p)
with CanHaveLegacyRoccs // implies CanHaveSharedFPU with CanHavePTW with HasHellaCache
with CanHaveScratchpad { // implies CanHavePTW with HasHellaCache with HasICacheFrontend
nDCachePorts += 1 // core TODO dcachePorts += () => module.core.io.dmem ??
val device = new Device {
def ofInt(x: Int) = Seq(ResourceInt(BigInt(x)))
def ofStr(x: String) = Seq(ResourceString(x))
def describe(resources: ResourceBindings): Description = {
val block = p(CacheBlockBytes)
val m = if (rocketParams.core.mulDiv.nonEmpty) "m" else ""
val a = if (rocketParams.core.useAtomics) "a" else ""
val f = if (rocketParams.core.fpu.nonEmpty) "f" else ""
val d = if (rocketParams.core.fpu.nonEmpty && p(XLen) > 32) "d" else ""
val c = if (rocketParams.core.useCompressed) "c" else ""
val s = if (rocketParams.core.useVM) "s" else ""
val isa = s"rv${p(XLen)}i$m$a$f$d$c$s"
val dcache = rocketParams.dcache.map(d => Map(
"d-tlb-size" -> ofInt(d.nTLBEntries),
"d-tlb-sets" -> ofInt(1),
"d-cache-block-size" -> ofInt(block),
"d-cache-sets" -> ofInt(d.nSets),
"d-cache-size" -> ofInt(d.nSets * d.nWays * block))).getOrElse(Map())
val icache = rocketParams.icache.map(i => Map(
"i-tlb-size" -> ofInt(i.nTLBEntries),
"i-tlb-sets" -> ofInt(1),
"i-cache-block-size" -> ofInt(block),
"i-cache-sets" -> ofInt(i.nSets),
"i-cache-size" -> ofInt(i.nSets * i.nWays * block))).getOrElse(Map())
// Find all the caches
val outer = masterNode.edgesOut
.flatMap(_.manager.managers)
.filter(_.supportsAcquireB)
.flatMap(_.resources.headOption)
.map(_.owner.label)
.distinct
val nextlevel: Option[(String, Seq[ResourceValue])] =
if (outer.isEmpty) None else
Some("next-level-cache" -> outer.map(l => ResourceReference(l)).toList)
Description(s"cpus/cpu@${hartid}", Map(
"reg" -> resources("reg").map(_.value),
"device_type" -> ofStr("cpu"),
"compatible" -> ofStr("riscv"),
"status" -> ofStr("okay"),
"clock-frequency" -> Seq(ResourceInt(rocketParams.core.bootFreqHz)),
"riscv,isa" -> ofStr(isa),
"mmu-type" -> ofStr(p(PgLevels) match {
case 2 => "riscv,sv32"
case 3 => "riscv,sv39"
case 4 => "riscv,sv48" }),
"tlb-split" -> Nil,
"interrupt-controller" -> Nil,
"#interrupt-cells" -> ofInt(1))
++ dcache ++ icache ++ nextlevel)
}
}
ResourceBinding {
Resource(device, "reg").bind(ResourceInt(BigInt(hartid)))
// debug, msip, mtip, meip, seip offsets in CSRs
val intMap = Seq(65535, 3, 7, 11, 9)
intNode.edgesIn.flatMap(_.source.sources).map { case s =>
for (i <- s.range.start until s.range.end) {
intMap.lift(i).foreach { j =>
s.resources.foreach { r =>
r.bind(device, ResourceInt(j))
}
}
}
}
}
override lazy val module = new RocketTileModule(this)
}
@ -40,7 +113,6 @@ class RocketTileModule(outer: RocketTile) extends BaseTileModule(outer, () => ne
with CanHaveScratchpadModule {
val core = Module(p(BuildCore)(outer.p))
core.io.interrupts := io.interrupts
core.io.hartid := io.hartid
outer.frontend.module.io.cpu <> core.io.imem
outer.frontend.module.io.resetVector := io.resetVector
@ -55,6 +127,13 @@ class RocketTileModule(outer: RocketTile) extends BaseTileModule(outer, () => ne
core.io.rocc.interrupt := lr.module.io.core.interrupt
}
// Decode the interrupt vector
core.io.interrupts.debug := io.interrupts(0)(0)
core.io.interrupts.msip := io.interrupts(0)(1)
core.io.interrupts.mtip := io.interrupts(0)(2)
core.io.interrupts.meip := io.interrupts(0)(3)
core.io.interrupts.seip.foreach { _ := io.interrupts(0)(4) }
// TODO eliminate this redundancy
val h = dcachePorts.size
val c = core.dcacheArbPorts
@ -66,8 +145,8 @@ class RocketTileModule(outer: RocketTile) extends BaseTileModule(outer, () => ne
ptwOpt foreach { ptw => ptw.io.requestor <> ptwPorts }
}
class AsyncRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends LazyModule {
val rocket = LazyModule(new RocketTile(rtp))
class AsyncRocketTile(rtp: RocketTileParams, hartid: Int)(implicit p: Parameters) extends LazyModule {
val rocket = LazyModule(new RocketTile(rtp, hartid))
val masterNode = TLAsyncOutputNode()
val source = LazyModule(new TLAsyncCrossingSource)
@ -79,23 +158,27 @@ class AsyncRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends Laz
rocket.slaveNode :*= sink.node
sink.node :*= slaveNode
val intNode = IntInputNode()
val xing = LazyModule(new IntXing(3))
rocket.intNode := xing.intnode
xing.intnode := intNode
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val master = masterNode.bundleOut
val slave = slaveNode.bundleIn
val interrupts = intNode.bundleIn
val hartid = UInt(INPUT, p(XLen))
val interrupts = new TileInterrupts()(p).asInput
val resetVector = UInt(INPUT, p(XLen))
}
rocket.module.io.interrupts := ShiftRegister(io.interrupts, 3)
// signals that do not change:
rocket.module.io.hartid := io.hartid
rocket.module.io.resetVector := io.resetVector
}
}
class RationalRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends LazyModule {
val rocket = LazyModule(new RocketTile(rtp))
class RationalRocketTile(rtp: RocketTileParams, hartid: Int)(implicit p: Parameters) extends LazyModule {
val rocket = LazyModule(new RocketTile(rtp, hartid))
val masterNode = TLRationalOutputNode()
val source = LazyModule(new TLRationalCrossingSource)
@ -107,15 +190,19 @@ class RationalRocketTile(rtp: RocketTileParams)(implicit p: Parameters) extends
rocket.slaveNode :*= sink.node
sink.node :*= slaveNode
val intNode = IntInputNode()
val xing = LazyModule(new IntXing(1))
rocket.intNode := xing.intnode
xing.intnode := intNode
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val master = masterNode.bundleOut
val slave = slaveNode.bundleIn
val interrupts = intNode.bundleIn
val hartid = UInt(INPUT, p(XLen))
val interrupts = new TileInterrupts()(p).asInput
val resetVector = UInt(INPUT, p(XLen))
}
rocket.module.io.interrupts := ShiftRegister(io.interrupts, 1)
// signals that do not change:
rocket.module.io.hartid := io.hartid
rocket.module.io.resetVector := io.resetVector

View File

@ -20,6 +20,10 @@ import DefaultTestSuites._
import config._
class BasePlatformConfig extends Config((site, here, up) => {
// DTS descriptive parameters
case DTSModel => "ucbbar,rocketchip-unknown"
case DTSCompat => Nil
case DTSTimebase => BigInt(0)
// TileLink connection parameters
case TLMonitorBuilder => (args: TLMonitorArgs) => Some(LazyModule(new TLMonitor(args)))
case TLFuzzReadyValid => false
@ -139,6 +143,11 @@ class WithExtMemSize(n: Long) extends Config((site, here, up) => {
case ExtMem => up(ExtMem, site).copy(size = n)
})
class WithDTS(model: String, compat: Seq[String]) extends Config((site, here, up) => {
case DTSModel => model
case DTSCompat => compat
})
class WithScratchpad extends Config(new WithNMemoryChannels(0) ++ new WithDataScratchpad(16384))
class DefaultFPGASmallConfig extends Config(new WithNSmallCores(1) ++ new DefaultFPGAConfig)

View File

@ -51,8 +51,14 @@ trait HasPeripheryParameters {
trait PeripheryExtInterrupts {
this: HasTopLevelNetworks =>
private val device = new Device with DeviceInterrupts {
def describe(resources: ResourceBindings): Description = {
Description("soc/offchip-interrupts", describeInterrupts(resources))
}
}
val nExtInterrupts = p(NExtTopInterrupts)
val extInterrupts = IntInternalInputNode(nExtInterrupts)
val extInterrupts = IntInternalInputNode(IntSourcePortSimple(num = nExtInterrupts, resources = device.int))
val extInterruptXing = LazyModule(new IntXing)
intBus.intnode := extInterruptXing.intnode
@ -84,6 +90,8 @@ trait PeripheryMasterAXI4Mem {
private val channels = p(BankedL2Config).nMemoryChannels
private val lineBytes = p(CacheBlockBytes)
private val device = new MemoryDevice
val mem_axi4 = AXI4BlindOutputNode(Seq.tabulate(channels) { channel =>
val base = AddressSet(config.base, config.size-1)
val filter = AddressSet(channel * lineBytes, ~((channels-1) * lineBytes))
@ -91,6 +99,7 @@ trait PeripheryMasterAXI4Mem {
AXI4SlavePortParameters(
slaves = Seq(AXI4SlaveParameters(
address = base.intersect(filter).toList,
resources = device.reg,
regionType = RegionType.UNCACHED, // cacheable
executable = true,
supportsWrite = TransferSizes(1, 256), // The slave supports 1-256 byte transfers
@ -160,9 +169,11 @@ trait PeripheryMasterAXI4MMIO {
this: HasTopLevelNetworks =>
private val config = p(ExtBus)
private val device = new SimpleDevice("mmio", Nil)
val mmio_axi4 = AXI4BlindOutputNode(Seq(AXI4SlavePortParameters(
slaves = Seq(AXI4SlaveParameters(
address = List(AddressSet(BigInt(config.base), config.size-1)),
resources = device.reg,
executable = true, // Can we run programs on this memory?
supportsWrite = TransferSizes(1, 256), // The slave supports 1-256 byte transfers
supportsRead = TransferSizes(1, 256),
@ -227,9 +238,11 @@ trait PeripheryMasterTLMMIO {
this: HasTopLevelNetworks =>
private val config = p(ExtBus)
private val device = new SimpleDevice("mmio", Nil)
val mmio_tl = TLBlindOutputNode(Seq(TLManagerPortParameters(
managers = Seq(TLManagerParameters(
address = List(AddressSet(BigInt(config.base), config.size-1)),
resources = device.reg,
executable = true,
supportsGet = TransferSizes(1, cacheBlockBytes),
supportsPutFull = TransferSizes(1, cacheBlockBytes),
@ -292,7 +305,7 @@ trait PeripheryBootROM {
private val bootrom_address = 0x1000
private val bootrom_size = 0x1000
private lazy val bootrom_contents = GenerateBootROM(p, bootrom_address, coreplex.configString)
private lazy val bootrom_contents = GenerateBootROM(p, bootrom_address, coreplex.dts)
val bootrom = LazyModule(new TLROM(bootrom_address, bootrom_size, bootrom_contents, true, peripheryBusConfig.beatBytes))
bootrom.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)
}

View File

@ -52,50 +52,18 @@ class GlobalVariable[T] {
def get: T = { require(assigned); variable }
}
object GenerateConfigString {
def apply(p: Parameters, clint: CoreplexLocalInterrupter, plic: TLPLIC, peripheryManagers: Seq[TLManagerParameters]) = {
val c = CoreplexParameters()(p)
val res = new StringBuilder
res append plic.globalConfigString
res append clint.globalConfigString
res append "core {\n"
c.tilesParams.zipWithIndex.map { case(t, i) =>
val isa = {
val m = if (t.core.mulDiv.nonEmpty) "m" else ""
val a = if (t.core.useAtomics) "a" else ""
val f = if (t.core.fpu.nonEmpty) "f" else ""
val d = if (t.core.fpu.nonEmpty && p(XLen) > 32) "d" else ""
val c = if (t.core.useCompressed) "c" else ""
val s = if (t.core.useVM) "s" else ""
s"rv${p(XLen)}i$m$a$f$d$c$s"
}
res append s" $i {\n"
res append " 0 {\n"
res append s" isa $isa;\n"
res append clint.hartConfigStrings(i)
res append plic.hartConfigStrings(i)
res append " };\n"
res append " };\n"
}
res append "};\n"
peripheryManagers.foreach { manager => res append manager.dts }
res append '\u0000'
res.toString
}
}
object GenerateBootROM {
def apply(p: Parameters, address: BigInt, configString: String) = {
def apply(p: Parameters, address: BigInt, dts: String) = {
val romdata = Files.readAllBytes(Paths.get(p(BootROMFile)))
val rom = ByteBuffer.wrap(romdata)
rom.order(ByteOrder.LITTLE_ENDIAN)
require(address == address.toInt)
val configStringAddr = address.toInt + rom.capacity
val dtsAddr = address.toInt + rom.capacity
require(rom.getInt(12) == 0,
"Config string address position should not be occupied by code")
rom.putInt(12, configStringAddr)
rom.array() ++ (configString.getBytes.toSeq)
"DTS address position should not be occupied by code")
rom.putInt(12, dtsAddr)
rom.array() ++ (dts.getBytes.toSeq)
}
}

View File

@ -53,11 +53,13 @@ trait HasTileLinkMasterPort extends HasTileParameters {
implicit val p: Parameters
val module: HasTileLinkMasterPortModule
val masterNode = TLOutputNode()
val intNode = IntSinkNode(IntSinkPortSimple())
}
trait HasTileLinkMasterPortBundle {
val outer: HasTileLinkMasterPort
val master = outer.masterNode.bundleOut
val interrupts = outer.intNode.bundleIn
}
trait HasTileLinkMasterPortModule {
@ -73,7 +75,6 @@ abstract class BaseTile(tileParams: TileParams)(implicit p: Parameters) extends
class BaseTileBundle[+L <: BaseTile](_outer: L) extends BareTileBundle(_outer)
with HasTileLinkMasterPortBundle {
val hartid = UInt(INPUT, p(XLen))
val interrupts = new TileInterrupts()(p).asInput
val resetVector = UInt(INPUT, p(XLen))
}

View File

@ -9,6 +9,7 @@ import scala.math.max
case class AHBSlaveParameters(
address: Seq[AddressSet],
resources: Seq[Resource] = Nil,
regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[BaseNode] = Seq(),

View File

@ -77,7 +77,7 @@ object AHBRegisterNode
abstract class AHBRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
{
val node = AHBRegisterNode(address, concurrency, beatBytes, undefZero, executable)
val intnode = uncore.tilelink2.IntSourceNode(interrupts)
val intnode = uncore.tilelink2.IntSourceNode(uncore.tilelink2.IntSourcePortSimple(num = interrupts))
}
case class AHBRegBundleArg(interrupts: util.HeterogeneousBag[Vec[Bool]], in: util.HeterogeneousBag[AHBBundle])(implicit val p: Parameters)

View File

@ -9,6 +9,7 @@ import scala.math.max
case class APBSlaveParameters(
address: Seq[AddressSet],
resources: Seq[Resource] = Nil,
regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[BaseNode] = Seq(),

View File

@ -61,7 +61,7 @@ object APBRegisterNode
abstract class APBRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
{
val node = APBRegisterNode(address, concurrency, beatBytes, undefZero, executable)
val intnode = uncore.tilelink2.IntSourceNode(interrupts)
val intnode = uncore.tilelink2.IntSourceNode(uncore.tilelink2.IntSourcePortSimple(num = interrupts))
}
case class APBRegBundleArg(interrupts: util.HeterogeneousBag[Vec[Bool]], in: util.HeterogeneousBag[APBBundle])(implicit val p: Parameters)

View File

@ -9,6 +9,7 @@ import scala.math.max
case class AXI4SlaveParameters(
address: Seq[AddressSet],
resources: Seq[Resource] = Nil,
regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[BaseNode] = Seq(),

View File

@ -82,7 +82,7 @@ object AXI4RegisterNode
abstract class AXI4RegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
{
val node = AXI4RegisterNode(address, concurrency, beatBytes, undefZero, executable)
val intnode = uncore.tilelink2.IntSourceNode(interrupts)
val intnode = uncore.tilelink2.IntSourceNode(uncore.tilelink2.IntSourcePortSimple(num = interrupts))
}
case class AXI4RegBundleArg(interrupts: util.HeterogeneousBag[Vec[Bool]], in: util.HeterogeneousBag[AXI4Bundle])(implicit val p: Parameters)

View File

@ -20,6 +20,7 @@ case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)(
slaves = mp.managers.map { m =>
AXI4SlaveParameters(
address = m.address,
resources = m.resources,
regionType = m.regionType,
executable = m.executable,
nodePath = m.nodePath,

View File

@ -849,7 +849,7 @@ trait DebugModule extends Module with HasDebugModuleParameters with HasRegMap {
*/
class TLDebugModule(address: BigInt = 0)(implicit p: Parameters)
extends TLRegisterRouter(address, beatBytes=p(XLen)/8, executable=true)(
extends TLRegisterRouter(address, "debug", Nil, beatBytes=p(XLen)/8, executable=true)(
new TLRegBundle((), _ ) with DebugModuleBundle)(
new TLRegModule((), _, _) with DebugModule)

View File

@ -53,57 +53,52 @@ object PLICConsts
}
/** Platform-Level Interrupt Controller */
class TLPLIC(supervisor: Boolean, maxPriorities: Int, address: BigInt = 0xC000000)(implicit p: Parameters) extends LazyModule
class TLPLIC(maxPriorities: Int, address: BigInt = 0xC000000)(implicit p: Parameters) extends LazyModule
{
val contextsPerHart = if (supervisor) 2 else 1
require (maxPriorities >= 0)
// plic0 => max devices 1023
val device = new SimpleDevice("interrupt-controller", Seq("riscv,plic0")) {
override val alwaysExtended = true
override def describe(resources: ResourceBindings): Description = {
val Description(name, mapping) = super.describe(resources)
val extra = Map(
"interrupt-controller" -> Nil,
"riscv,ndev" -> Seq(ResourceInt(nDevices)),
"#interrupt-cells" -> Seq(ResourceInt(1)),
"#address-cells" -> Seq(ResourceInt(0)))
Description(name, mapping ++ extra)
}
}
val node = TLRegisterNode(
address = AddressSet(address, PLICConsts.size-1),
device = device,
beatBytes = p(XLen)/8,
undefZero = false)
val intnode = IntNexusNode(
numSourcePorts = 0 to 1024,
numSinkPorts = 0 to 1024,
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) },
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Seq(Resource(device, "int"))))) },
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
/* Negotiated sizes */
def nDevices = intnode.edgesIn.map(_.source.num).sum
def nDevices: Int = intnode.edgesIn.map(_.source.num).sum
def nPriorities = min(maxPriorities, nDevices)
def nHarts = intnode.edgesOut.map(_.source.num).sum
def context(i: Int, mode: Char) = mode match {
case 'M' => i * contextsPerHart
case 'S' => require(supervisor); i * contextsPerHart + 1
}
def claimAddr(i: Int, mode: Char) = address + PLICConsts.hartBase(context(i, mode)) + PLICConsts.claimOffset
def threshAddr(i: Int, mode: Char) = address + PLICConsts.hartBase(context(i, mode))
def enableAddr(i: Int, mode: Char) = address + PLICConsts.enableBase(context(i, mode))
// Assign all the devices unique ranges
lazy val sources = intnode.edgesIn.map(_.source)
lazy val flatSources = (sources zip sources.map(_.num).scanLeft(0)(_+_).init).map {
case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o)))
}.flatten
// Create the global PLIC config string
lazy val globalConfigString = Seq(
s"plic {\n",
s" priority 0x${address.toString(16)};\n",
s" pending 0x${(address + PLICConsts.pendingBase).toString(16)};\n",
s" ndevs ${nDevices};\n",
s"};\n").mkString
// Create the per-Hart config string
lazy val hartConfigStrings = Seq.tabulate(intnode.edgesOut.size) { i => (Seq(
s" plic {\n",
s" m {\n",
s" ie 0x${enableAddr(i, 'M').toString(16)};\n",
s" thresh 0x${threshAddr(i, 'M').toString(16)};\n",
s" claim 0x${claimAddr(i, 'M').toString(16)};\n",
s" };\n") ++ (if (!supervisor) Seq() else Seq(
s" s {\n",
s" ie 0x${enableAddr(i, 'S').toString(16)};\n",
s" thresh 0x${threshAddr(i, 'S').toString(16)};\n",
s" claim 0x${claimAddr(i, 'S').toString(16)};\n",
s" };\n")) ++ Seq(
s" };\n")).mkString
ResourceBinding {
flatSources.foreach { s => s.resources.foreach { r =>
// +1 because interrupt 0 is reserved
(s.range.start until s.range.end).foreach { i => r.bind(device, ResourceInt(i+1)) }
} }
}
lazy val module = new LazyModuleImp(this) {
@ -113,21 +108,17 @@ class TLPLIC(supervisor: Boolean, maxPriorities: Int, address: BigInt = 0xC00000
val harts = intnode.bundleOut
}
// Assign all the devices unique ranges
val sources = intnode.edgesIn.map(_.source)
val flatSources = (sources zip sources.map(_.num).scanLeft(0)(_+_).init).map {
case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o)))
}.flatten
// Compact the interrupt vector the same way
val interrupts = (intnode.edgesIn zip io.devices).map { case (e, i) => i.take(e.source.num) }.flatten
// This flattens the harts into an MSMSMSMSMS... or MMMMM.... sequence
val harts = io.harts.flatten
println("\nInterrupt map:")
println(s"Interrupt map (${nHarts} harts ${nDevices} interrupts):")
flatSources.foreach { s =>
// +1 because 0 is reserved, +1-1 because the range is half-open
println(s" [${s.range.start+1}, ${s.range.end}] => ${s.name}")
}
println("")
require (nDevices == interrupts.size)
require (nHarts == harts.size)

View File

@ -17,11 +17,6 @@ import tile.XLen
/** Number of tiles */
case object NTiles extends Field[Int]
class CoreplexLocalInterrupts extends Bundle {
val mtip = Bool()
val msip = Bool()
}
object ClintConsts
{
def msipOffset(hart: Int) = hart * msipBytes
@ -30,70 +25,68 @@ object ClintConsts
def msipBytes = 4
def timecmpBytes = 8
def size = 0x10000
def timeWidth = 64
def regWidth = 32
def ints = 2
}
trait MixCoreplexLocalInterrupterParameters {
implicit val p: Parameters
}
trait CoreplexLocalInterrupterBundle extends Bundle with MixCoreplexLocalInterrupterParameters {
val tiles = Vec(p(NTiles), new CoreplexLocalInterrupts).asOutput
val rtcTick = Bool(INPUT)
}
trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCoreplexLocalInterrupterParameters {
val io: CoreplexLocalInterrupterBundle
val address: AddressSet
val timeWidth = 64
val regWidth = 32
val time = Seq.fill(timeWidth/regWidth)(Reg(init=UInt(0, width = regWidth)))
when (io.rtcTick) {
val newTime = time.asUInt + UInt(1)
for ((reg, i) <- time zip (0 until timeWidth by regWidth))
reg := newTime >> i
}
val timecmp = Seq.fill(p(NTiles)) { Seq.fill(timeWidth/regWidth)(Reg(UInt(width = regWidth))) }
val ipi = Seq.fill(p(NTiles)) { RegInit(UInt(0, width = 1)) }
for ((tile, i) <- io.tiles zipWithIndex) {
tile.msip := ipi(i)(0)
tile.mtip := time.asUInt >= timecmp(i).asUInt
}
/* 0000 msip hart 0
* 0004 msip hart 1
* 4000 mtimecmp hart 0 lo
* 4004 mtimecmp hart 0 hi
* 4008 mtimecmp hart 1 lo
* 400c mtimecmp hart 1 hi
* bff8 mtime lo
* bffc mtime hi
*/
regmap(
0 -> makeRegFields(ipi),
ClintConsts.timecmpOffset(0) -> makeRegFields(timecmp.flatten),
ClintConsts.timeOffset -> makeRegFields(time))
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
}
/** Power, Reset, Clock, Interrupt */
// Magic TL2 Incantation to create a TL2 Slave
class CoreplexLocalInterrupter(address: BigInt = 0x02000000)(implicit p: Parameters)
extends TLRegisterRouter(address, size = ClintConsts.size, beatBytes = p(XLen)/8, undefZero = true)(
new TLRegBundle((), _) with CoreplexLocalInterrupterBundle)(
new TLRegModule((), _, _) with CoreplexLocalInterrupterModule)
class CoreplexLocalInterrupter(address: BigInt = 0x02000000)(implicit p: Parameters) extends LazyModule
{
val globalConfigString = Seq(
s"rtc {\n",
s" addr 0x${(address + ClintConsts.timeOffset).toString(16)};\n",
s"};\n").mkString
val hartConfigStrings = (0 until p(NTiles)).map { i => Seq(
s" timecmp 0x${(address + ClintConsts.timecmpOffset(i)).toString(16)};\n",
s" ipi 0x${(address + ClintConsts.msipOffset(i)).toString(16)};\n").mkString
import ClintConsts._
// clint0 => at most 4095 devices
val device = new SimpleDevice("clint", Seq("riscv,clint0")) {
override val alwaysExtended = true
}
val node = TLRegisterNode(
address = AddressSet(address, size-1),
device = device,
beatBytes = p(XLen)/8)
val intnode = IntNexusNode(
numSourcePorts = 0 to 1024,
numSinkPorts = 0 to 0,
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(ints, Seq(Resource(device, "int"))))) },
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val rtcTick = Bool(INPUT)
val int = intnode.bundleOut
val in = node.bundleIn
}
val time = Seq.fill(timeWidth/regWidth)(Reg(init=UInt(0, width = regWidth)))
when (io.rtcTick) {
val newTime = time.asUInt + UInt(1)
for ((reg, i) <- time zip (0 until timeWidth by regWidth))
reg := newTime >> i
}
val timecmp = Seq.fill(p(NTiles)) { Seq.fill(timeWidth/regWidth)(Reg(UInt(width = regWidth))) }
val ipi = Seq.fill(p(NTiles)) { RegInit(UInt(0, width = 1)) }
io.int.zipWithIndex.foreach { case (int, i) =>
int(0) := ipi(i)(0) // msip
int(1) := time.asUInt >= timecmp(i).asUInt // mtip
}
/* 0000 msip hart 0
* 0004 msip hart 1
* 4000 mtimecmp hart 0 lo
* 4004 mtimecmp hart 0 hi
* 4008 mtimecmp hart 1 lo
* 400c mtimecmp hart 1 hi
* bff8 mtime lo
* bffc mtime hi
*/
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
node.regmap(
0 -> makeRegFields(ipi),
timecmpOffset(0) -> makeRegFields(timecmp.flatten),
timeOffset -> makeRegFields(time))
}
}

View File

@ -14,8 +14,11 @@ import config._
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
{
val device = new SimpleDevice("rom", Nil)
val node = TLManagerNode(beatBytes, TLManagerParameters(
address = List(AddressSet(base, size-1)),
resources = device.reg,
regionType = RegionType.UNCACHED,
executable = executable,
supportsGet = TransferSizes(1, beatBytes),

View File

@ -34,6 +34,7 @@ trait ExampleModule extends HasRegMap
}
// Create a concrete TL2 version of the abstract Example slave
class TLExample(params: ExampleParams)(implicit p: Parameters) extends TLRegisterRouter(params.address, 4)(
class TLExample(params: ExampleParams)(implicit p: Parameters)
extends TLRegisterRouter(params.address, "somedev", Seq("ucbbar,random-interface"), 4)(
new TLRegBundle(params, _) with ExampleBundle)(
new TLRegModule(params, _, _) with ExampleModule)

View File

@ -18,20 +18,22 @@ case class IntRange(start: Int, end: Int)
def overlaps(x: IntRange) = start < x.end && x.start < end
def offset(x: Int) = IntRange(x+start, x+end)
}
object IntRange
{
implicit def apply(end: Int): IntRange = apply(0, end)
}
case class IntSourceParameters(
range: IntRange,
nodePath: Seq[BaseNode] = Seq())
range: IntRange,
resources: Seq[Resource] = Seq(),
nodePath: Seq[BaseNode] = Seq())
{
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
}
case class IntSinkParameters(
nodePath: Seq[BaseNode] = Seq())
nodePath: Seq[BaseNode] = Seq())
{
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
}
@ -44,8 +46,19 @@ case class IntSourcePortParameters(sources: Seq[IntSourceParameters])
// The interrupts must perfectly cover the range
require (sources.isEmpty || sources.map(_.range.end).max == num)
}
object IntSourcePortSimple
{
def apply(num: Int = 1, ports: Int = 1, sources: Int = 1, resources: Seq[Resource] = Nil) =
if (num == 0) Nil else
Seq.fill(ports)(IntSourcePortParameters(Seq.fill(sources)(IntSourceParameters(range = IntRange(0, num), resources = resources))))
}
case class IntSinkPortParameters(sinks: Seq[IntSinkParameters])
object IntSinkPortSimple
{
def apply(ports: Int = 1, sinks: Int = 1) =
Seq.fill(ports)(IntSinkPortParameters(Seq.fill(sinks)(IntSinkParameters())))
}
case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters)
@ -74,10 +87,8 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In
}
case class IntIdentityNode() extends IdentityNode(IntImp)
case class IntSourceNode(num: Int) extends SourceNode(IntImp)(
if (num == 0) Seq() else Seq(IntSourcePortParameters(Seq(IntSourceParameters(num)))))
case class IntSinkNode() extends SinkNode(IntImp)(
Seq(IntSinkPortParameters(Seq(IntSinkParameters()))))
case class IntSourceNode(portParams: Seq[IntSourcePortParameters]) extends SourceNode(IntImp)(portParams)
case class IntSinkNode(portParams: Seq[IntSinkPortParameters]) extends SinkNode(IntImp)(portParams)
case class IntNexusNode(
sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters,
@ -89,11 +100,11 @@ case class IntNexusNode(
case class IntOutputNode() extends OutputNode(IntImp)
case class IntInputNode() extends InputNode(IntImp)
case class IntBlindOutputNode() extends BlindOutputNode(IntImp)(Seq(IntSinkPortParameters(Seq(IntSinkParameters()))))
case class IntBlindInputNode(num: Int) extends BlindInputNode(IntImp)(Seq(IntSourcePortParameters(Seq(IntSourceParameters(num)))))
case class IntBlindOutputNode(portParams: Seq[IntSinkPortParameters]) extends BlindOutputNode(IntImp)(portParams)
case class IntBlindInputNode(portParams: Seq[IntSourcePortParameters]) extends BlindInputNode(IntImp)(portParams)
case class IntInternalOutputNode() extends InternalOutputNode(IntImp)(Seq(IntSinkPortParameters(Seq(IntSinkParameters()))))
case class IntInternalInputNode(num: Int) extends InternalInputNode(IntImp)(Seq(IntSourcePortParameters(Seq(IntSourceParameters(num)))))
case class IntInternalOutputNode(portParams: Seq[IntSinkPortParameters]) extends InternalOutputNode(IntImp)(portParams)
case class IntInternalInputNode(portParams: Seq[IntSourcePortParameters]) extends InternalInputNode(IntImp)(portParams)
class IntXbar()(implicit p: Parameters) extends LazyModule
{
@ -116,7 +127,7 @@ class IntXbar()(implicit p: Parameters) extends LazyModule
}
}
class IntXing()(implicit p: Parameters) extends LazyModule
class IntXing(sync: Int = 3)(implicit p: Parameters) extends LazyModule
{
val intnode = IntIdentityNode()
@ -127,7 +138,7 @@ class IntXing()(implicit p: Parameters) extends LazyModule
}
(io.in zip io.out) foreach { case (in, out) =>
out := RegNext(RegNext(RegNext(in)))
out := (0 to sync).foldLeft(in) { case (a, _) => RegNext(a) }
}
}
}

View File

@ -9,6 +9,7 @@ import util.RationalDirection
case class TLManagerParameters(
address: Seq[AddressSet],
resources: Seq[Resource] = Seq(),
regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[BaseNode] = Seq(),
@ -22,8 +23,7 @@ case class TLManagerParameters(
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: TransferSizes = TransferSizes.none,
// If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order
fifoId: Option[Int] = None,
customDTS: Option[String]= None)
fifoId: Option[Int] = None)
{
require (!address.isEmpty)
address.foreach { a => require (a.finite) }
@ -53,20 +53,16 @@ case class TLManagerParameters(
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
// Generate the config string (in future device tree)
lazy val dts = customDTS.getOrElse {
val header = s"${name} {\n"
val middle = address.map { a =>
require (a.contiguous) // Config String is not so flexible
" addr 0x%x;\n size 0x%x;\n".format(a.base, a.mask+1)
}
val footer = "}\n"
header + middle.reduce(_ + _) + footer
}
// The device had better not support a transfer larger than it's alignment
val minAlignment = address.map(_.alignment).min
require (minAlignment >= maxTransfer)
def toResource: ResourceAddress = {
ResourceAddress(address,
r = supportsAcquireB || supportsGet,
w = supportsAcquireT || supportsPutFull,
x = executable)
}
}
case class TLManagerPortParameters(

View File

@ -8,10 +8,18 @@ import diplomacy._
import regmapper._
import scala.math.{min,max}
class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
class TLRegisterNode(
address: AddressSet,
device: Device,
deviceKey: String = "reg",
concurrency: Int = 0,
beatBytes: Int = 4,
undefZero: Boolean = true,
executable: Boolean = false)
extends TLManagerNode(Seq(TLManagerPortParameters(
Seq(TLManagerParameters(
address = Seq(address),
resources = Seq(Resource(device, deviceKey)),
executable = executable,
supportsGet = TransferSizes(1, beatBytes),
supportsPutPartial = TransferSizes(1, beatBytes),
@ -72,18 +80,26 @@ class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int =
object TLRegisterNode
{
def apply(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false) =
new TLRegisterNode(address, concurrency, beatBytes, undefZero, executable)
def apply(
address: AddressSet,
device: Device,
deviceKey: String = "reg",
concurrency: Int = 0,
beatBytes: Int = 4,
undefZero: Boolean = true,
executable: Boolean = false) =
new TLRegisterNode(address, device, deviceKey, concurrency, beatBytes, undefZero, executable)
}
// These convenience methods below combine to make it possible to create a TL2
// register mapped device from a totally abstract register mapped device.
// See GPIO.scala in this directory for an example
abstract class TLRegisterRouterBase(val address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
abstract class TLRegisterRouterBase(devname: String, devcompat: Seq[String], val address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean)(implicit p: Parameters) extends LazyModule
{
val node = TLRegisterNode(address, concurrency, beatBytes, undefZero, executable)
val intnode = IntSourceNode(interrupts)
val device = new SimpleDevice(devname, devcompat)
val node = TLRegisterNode(address, device, "reg", concurrency, beatBytes, undefZero, executable)
val intnode = IntSourceNode(IntSourcePortSimple(num = interrupts, resources = Seq(Resource(device, "int"))))
}
case class TLRegBundleArg(interrupts: util.HeterogeneousBag[Vec[Bool]], in: util.HeterogeneousBag[TLBundle])(implicit val p: Parameters)
@ -106,11 +122,19 @@ class TLRegModule[P, B <: TLRegBundleBase](val params: P, bundleBuilder: => B, r
def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*)
}
class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp]
(val base: BigInt, val interrupts: Int = 0, val size: BigInt = 4096, val concurrency: Int = 0, val beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
class TLRegisterRouter[B <: TLRegBundleBase, M <: LazyModuleImp](
val base: BigInt,
val devname: String,
val devcompat: Seq[String],
val interrupts: Int = 0,
val size: BigInt = 4096,
val concurrency: Int = 0,
val beatBytes: Int = 4,
val undefZero: Boolean = true,
val executable: Boolean = false)
(bundleBuilder: TLRegBundleArg => B)
(moduleBuilder: (=> B, TLRegisterRouterBase) => M)(implicit p: Parameters)
extends TLRegisterRouterBase(AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero, executable)
extends TLRegisterRouterBase(devname, devcompat, AddressSet(base, size-1), interrupts, concurrency, beatBytes, undefZero, executable)
{
require (isPow2(size))
// require (size >= 4096) ... not absolutely required, but highly recommended

View File

@ -214,7 +214,7 @@ trait RRTest0Module extends HasRegMap
regmap(RRTest0Map.map:_*)
}
class RRTest0(address: BigInt)(implicit p: Parameters) extends TLRegisterRouter(address, 0, 32, 0, 4)(
class RRTest0(address: BigInt)(implicit p: Parameters) extends TLRegisterRouter(address, "test0", Nil, 0, 32, 0, 4)(
new TLRegBundle((), _) with RRTest0Bundle)(
new TLRegModule((), _, _) with RRTest0Module)
@ -251,7 +251,7 @@ trait RRTest1Module extends Module with HasRegMap
regmap(map:_*)
}
class RRTest1(address: BigInt)(implicit p: Parameters) extends TLRegisterRouter(address, 0, 32, 6, 4)(
class RRTest1(address: BigInt)(implicit p: Parameters) extends TLRegisterRouter(address, "test1", Nil, 0, 32, 6, 4)(
new TLRegBundle((), _) with RRTest1Bundle)(
new TLRegModule((), _, _) with RRTest1Module)

View File

@ -9,9 +9,12 @@ import util._
class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
{
val device = new MemoryDevice
val node = TLManagerNode(Seq(TLManagerPortParameters(
Seq(TLManagerParameters(
address = List(address),
resources = device.reg,
regionType = RegionType.UNCACHED,
executable = executable,
supportsGet = TransferSizes(1, beatBytes),

View File

@ -19,6 +19,7 @@ case class TLToAHBNode() extends MixedAdapterNode(TLImp, AHBImp)(
val managers = slaves.map { case s =>
TLManagerParameters(
address = s.address,
resources = s.resources,
regionType = s.regionType,
executable = s.executable,
nodePath = s.nodePath,

View File

@ -19,6 +19,7 @@ case class TLToAPBNode() extends MixedAdapterNode(TLImp, APBImp)(
val managers = slaves.map { case s =>
TLManagerParameters(
address = s.address,
resources = s.resources,
regionType = s.regionType,
executable = s.executable,
nodePath = s.nodePath,

View File

@ -23,6 +23,7 @@ case class TLToAXI4Node(idBits: Int) extends MixedAdapterNode(TLImp, AXI4Imp)(
managers = p.slaves.map { case s =>
TLManagerParameters(
address = s.address,
resources = s.resources,
regionType = s.regionType,
executable = s.executable,
nodePath = s.nodePath,

View File

@ -8,9 +8,12 @@ import diplomacy._
class TLZero(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
{
val device = new SimpleDevice("rom", Seq("ucbbar,cacheable-zero"))
val node = TLManagerNode(Seq(TLManagerPortParameters(
Seq(TLManagerParameters(
address = List(address),
resources = device.reg,
regionType = RegionType.UNCACHED,
executable = executable,
supportsGet = TransferSizes(1, beatBytes),