1
0

Refactor package hierarchy and remove legacy bus protocol implementations (#845)

* Refactors package hierarchy.

Additionally:
  - Removes legacy ground tests and configs
  - Removes legacy bus protocol implementations
  - Removes NTiles
  - Adds devices package
  - Adds more functions to util package
This commit is contained in:
Henry Cook
2017-07-07 10:48:16 -07:00
committed by GitHub
parent c28c23150d
commit 4c595d175c
238 changed files with 1347 additions and 10978 deletions

View File

@ -0,0 +1,92 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.regmapper._
import freechips.rocketchip.tile.XLen
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
import scala.math.{min,max}
object ClintConsts
{
def msipOffset(hart: Int) = hart * msipBytes
def timecmpOffset(hart: Int) = 0x4000 + hart * timecmpBytes
def timeOffset = 0xbff8
def msipBytes = 4
def timecmpBytes = 8
def size = 0x10000
def timeWidth = 64
def regWidth = 32
def ints = 2
}
case class ClintParams(baseAddress: BigInt = 0x02000000)
{
def address = AddressSet(baseAddress, ClintConsts.size-1)
}
class CoreplexLocalInterrupter(params: ClintParams)(implicit p: Parameters) extends LazyModule
{
import ClintConsts._
// clint0 => at most 4095 devices
val device = new SimpleDevice("clint", Seq("riscv,clint0")) {
override val alwaysExtended = true
}
val node = TLRegisterNode(
address = Seq(params.address),
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 nTiles = intnode.edgesOut.size
val timecmp = Seq.fill(nTiles) { Seq.fill(timeWidth/regWidth)(Reg(UInt(width = regWidth))) }
val ipi = Seq.fill(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

@ -0,0 +1,57 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
class TLError(address: Seq[AddressSet], beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
{
val device = new SimpleDevice("error-device", Seq("sifive,error0"))
val node = TLManagerNode(Seq(TLManagerPortParameters(
Seq(TLManagerParameters(
address = address,
resources = device.reg("mem"),
supportsGet = TransferSizes(1, beatBytes),
supportsPutPartial = TransferSizes(1, beatBytes),
supportsPutFull = TransferSizes(1, beatBytes),
supportsArithmetic = TransferSizes(1, beatBytes),
supportsLogical = TransferSizes(1, beatBytes),
supportsHint = TransferSizes(1, beatBytes),
fifoId = Some(0))), // requests are handled in order
beatBytes = beatBytes,
minLatency = 1))) // no bypass needed for this device
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
}
import TLMessages._
val opcodes = Vec(AccessAck, AccessAck, AccessAckData, AccessAckData, AccessAckData, HintAck)
val in = io.in(0)
val a = Queue(in.a, 1)
val d = in.d
a.ready := d.ready
d.valid := a.valid
d.bits.opcode := opcodes(a.bits.opcode)
d.bits.param := UInt(0)
d.bits.size := a.bits.size
d.bits.source := a.bits.source
d.bits.sink := UInt(0)
d.bits.addr_lo := a.bits.address
d.bits.data := UInt(0)
d.bits.error := a.bits.opcode =/= Hint // Hints may not error
// Tie off unused channels
in.b.valid := Bool(false)
in.c.ready := Bool(true)
in.e.ready := Bool(true)
}
}

View File

@ -0,0 +1,233 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import Chisel.ImplicitConversions._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.regmapper._
import freechips.rocketchip.tile.XLen
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
import scala.math.min
class GatewayPLICIO extends Bundle {
val valid = Bool(OUTPUT)
val ready = Bool(INPUT)
val complete = Bool(INPUT)
}
class LevelGateway extends Module {
val io = new Bundle {
val interrupt = Bool(INPUT)
val plic = new GatewayPLICIO
}
val inFlight = Reg(init=Bool(false))
when (io.interrupt && io.plic.ready) { inFlight := true }
when (io.plic.complete) { inFlight := false }
io.plic.valid := io.interrupt && !inFlight
}
object PLICConsts
{
def maxDevices = 1023
def maxHarts = 15872
def priorityBase = 0x0
def pendingBase = 0x1000
def enableBase = 0x2000
def hartBase = 0x200000
def claimOffset = 4
def priorityBytes = 4
def enableOffset(i: Int) = i * ((maxDevices+7)/8)
def hartOffset(i: Int) = i * 0x1000
def enableBase(i: Int):Int = enableOffset(i) + enableBase
def hartBase(i: Int):Int = hartOffset(i) + hartBase
def size = hartBase(maxHarts)
require(hartBase >= enableBase(maxHarts))
}
case class PLICParams(baseAddress: BigInt = 0xC000000, maxPriorities: Int = 7)
{
require (maxPriorities >= 0)
def address = AddressSet(baseAddress, PLICConsts.size-1)
}
/** Platform-Level Interrupt Controller */
class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule
{
// 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)))
Description(name, mapping ++ extra)
}
}
val node = TLRegisterNode(
address = Seq(params.address),
device = device,
beatBytes = p(XLen)/8,
undefZero = true,
concurrency = 1) // limiting concurrency handles RAW hazards on claim registers
val intnode = IntNexusNode(
numSourcePorts = 0 to 1024,
numSinkPorts = 0 to 1024,
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Seq(Resource(device, "int"))))) },
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
/* Negotiated sizes */
def nDevices: Int = intnode.edgesIn.map(_.source.num).sum
def nPriorities = min(params.maxPriorities, nDevices)
def nHarts = intnode.edgesOut.map(_.source.num).sum
// 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
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) {
val io = new Bundle {
val tl_in = node.bundleIn
val devices = intnode.bundleIn
val harts = intnode.bundleOut
}
// 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(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)
require(nDevices <= PLICConsts.maxDevices)
require(nHarts > 0 && nHarts <= PLICConsts.maxHarts)
// For now, use LevelGateways for all TL2 interrupts
val gateways = Vec((false.B +: interrupts).map { case i =>
val gateway = Module(new LevelGateway)
gateway.io.interrupt := i
gateway.io.plic
})
val priority =
if (nPriorities > 0) Reg(Vec(nDevices+1, UInt(width=log2Up(nPriorities+1))))
else Wire(init=Vec.fill(nDevices+1)(UInt(1)))
val threshold =
if (nPriorities > 0) Reg(Vec(nHarts, UInt(width = log2Up(nPriorities+1))))
else Wire(init=Vec.fill(nHarts)(UInt(0)))
val pending = Reg(init=Vec.fill(nDevices+1){Bool(false)})
val enables = Reg(Vec(nHarts, Vec(nDevices+1, Bool())))
def findMax(x: Seq[UInt]): (UInt, UInt) = {
if (x.length > 1) {
val half = 1 << (log2Ceil(x.length) - 1)
val left = findMax(x take half)
val right = findMax(x drop half)
MuxT(left._1 >= right._1, left, (right._1, UInt(half) | right._2))
} else (x.head, UInt(0))
}
val maxDevs = Reg(Vec(nHarts, UInt(width = log2Up(pending.size))))
for (hart <- 0 until nHarts) {
val effectivePriority = (UInt(1) << priority(0).getWidth) +:
(for (((p, en), pri) <- (pending zip enables(hart) zip priority).tail)
yield Cat(p && en, pri))
val (maxPri, maxDev) = findMax(effectivePriority)
maxDevs(hart) := maxDev
harts(hart) := Reg(next = maxPri) > Cat(UInt(1), threshold(hart))
}
def priorityRegField(x: UInt) = if (nPriorities > 0) RegField(32, x) else RegField.r(32, x)
val priorityRegFields = Seq(PLICConsts.priorityBase -> priority.map(p => priorityRegField(p)))
val pendingRegFields = Seq(PLICConsts.pendingBase -> pending .map(b => RegField.r(1, b)))
val enableRegFields = enables.zipWithIndex.map { case (e, i) =>
PLICConsts.enableBase(i) -> e.map(b => RegField(1, b))
}
// When a hart reads a claim/complete register, then the
// device which is currently its highest priority is no longer pending.
// This code exploits the fact that, practically, only one claim/complete
// register can be read at a time. We check for this because if the address map
// were to change, it may no longer be true.
// Note: PLIC doesn't care which hart reads the register.
val claimer = Wire(Vec(nHarts, Bool()))
assert((claimer.asUInt & (claimer.asUInt - UInt(1))) === UInt(0)) // One-Hot
val claiming = Vec.tabulate(nHarts){i => Mux(claimer(i), UIntToOH(maxDevs(i), nDevices+1), UInt(0))}
val claimedDevs = Vec(claiming.reduceLeft( _ | _ ).toBools)
((pending zip gateways) zip claimedDevs) foreach { case ((p, g), c) =>
g.ready := !p
when (c || g.valid) { p := !c }
}
// When a hart writes a claim/complete register, then
// the written device (as long as it is actually enabled for that
// hart) is marked complete.
// This code exploits the fact that, practically, only one claim/complete register
// can be written at a time. We check for this because if the address map
// were to change, it may no longer be true.
// Note -- PLIC doesn't care which hart writes the register.
val completer = Wire(Vec(nHarts, Bool()))
assert((completer.asUInt & (completer.asUInt - UInt(1))) === UInt(0)) // One-Hot
val completerDev = Wire(UInt(width = log2Up(nDevices + 1)))
val completedDevs = Mux(completer.reduce(_ || _), UIntToOH(completerDev, nDevices+1), UInt(0))
(gateways zip completedDevs.toBools) foreach { case (g, c) =>
g.complete := c
}
val hartRegFields = Seq.tabulate(nHarts) { i =>
PLICConsts.hartBase(i) -> Seq(
priorityRegField(threshold(i)),
RegField(32,
RegReadFn { valid =>
claimer(i) := valid
(Bool(true), maxDevs(i))
},
RegWriteFn { (valid, data) =>
assert(completerDev === data.extract(log2Ceil(nDevices+1)-1, 0),
"completerDev should be consistent for all harts")
completerDev := data.extract(log2Ceil(nDevices+1)-1, 0)
completer(i) := valid && enables(i)(completerDev)
Bool(true)
}
)
)
}
node.regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*)
priority(0) := 0
pending(0) := false
for (e <- enables)
e(0) := false
}
}

View File

@ -0,0 +1,52 @@
// See LICENSE.SiFive for license details.
// See LICENSE.Berkeley for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
{
val node = TLManagerNode(beatBytes, TLManagerParameters (
address = List(AddressSet(base, size-1)),
resources = resources,
regionType = RegionType.UNCACHED,
executable = executable,
supportsGet = TransferSizes(1, beatBytes),
fifoId = Some(0)))
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
}
val contents = contentsDelayed
val wrapSize = 1 << log2Ceil(contents.size)
require (wrapSize <= size)
val in = io.in(0)
val edge = node.edgesIn(0)
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
in.d.valid := in.a.valid
in.a.ready := in.d.ready
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
in.d.bits := edge.AccessAck(in.a.bits, UInt(0), Mux(high.orR, UInt(0), rom(index)))
// Tie off unused channels
in.b.valid := Bool(false)
in.c.ready := Bool(true)
in.e.ready := Bool(true)
}
}

View File

@ -0,0 +1,84 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
// Do not use this for synthesis! Only for simulation.
class TLTestRAM(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),
supportsPutPartial = TransferSizes(1, beatBytes),
supportsPutFull = TransferSizes(1, beatBytes),
fifoId = Some(0))), // requests are handled in order
beatBytes = beatBytes)))
// We require the address range to include an entire beat (for the write mask)
require ((address.mask & (beatBytes-1)) == beatBytes-1)
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
}
def bigBits(x: BigInt, tail: List[Boolean] = List.empty[Boolean]): List[Boolean] =
if (x == 0) tail.reverse else bigBits(x >> 1, ((x & 1) == 1) :: tail)
val mask = bigBits(address.mask >> log2Ceil(beatBytes))
val in = io.in(0)
val edge = node.edgesIn(0)
val addrBits = (mask zip edge.addr_hi(in.a.bits).toBools).filter(_._1).map(_._2)
val memAddress = Cat(addrBits.reverse)
val mem = Mem(1 << addrBits.size, Vec(beatBytes, Bits(width = 8)))
// "Flow control"
in.a.ready := in.d.ready
in.d.valid := in.a.valid
val hasData = edge.hasData(in.a.bits)
val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
in.d.bits := edge.AccessAck(in.a.bits, UInt(0))
in.d.bits.data := Cat(mem(memAddress).reverse)
in.d.bits.opcode := Mux(hasData, TLMessages.AccessAck, TLMessages.AccessAckData)
when (in.a.fire() && hasData) { mem.write(memAddress, wdata, in.a.bits.mask.toBools) }
// Tie off unused channels
in.b.valid := Bool(false)
in.c.ready := Bool(true)
in.e.ready := Bool(true)
}
}
/** Synthesizeable unit testing */
import freechips.rocketchip.unittest._
class TLRAMZeroDelay(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule {
val fuzz = LazyModule(new TLFuzzer(txns))
val model = LazyModule(new TLRAMModel("ZeroDelay"))
val ram = LazyModule(new TLTestRAM(AddressSet(0x0, 0x3ff), beatBytes = ramBeatBytes))
model.node := fuzz.node
ram.node := TLDelayer(0.25)(model.node)
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
io.finished := fuzz.module.io.finished
}
}
class TLRAMZeroDelayTest(ramBeatBytes: Int, txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
io.finished := Module(LazyModule(new TLRAMZeroDelay(ramBeatBytes, txns)).module).io.finished
}

View File

@ -0,0 +1,48 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.devices.tilelink
import Chisel._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
class TLZero(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
{
val device = new SimpleDevice("rom", Seq("ucbbar,cacheable-zero0"))
val node = TLManagerNode(Seq(TLManagerPortParameters(
Seq(TLManagerParameters(
address = List(address),
resources = device.reg("mem"),
regionType = RegionType.UNCACHED,
executable = executable,
supportsGet = TransferSizes(1, beatBytes),
supportsPutPartial = TransferSizes(1, beatBytes),
supportsPutFull = TransferSizes(1, beatBytes),
fifoId = Some(0))), // requests are handled in order
beatBytes = beatBytes,
minLatency = 1))) // no bypass needed for this device
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
}
val in = io.in(0)
val edge = node.edgesIn(0)
val a = Queue(in.a, 2)
val hasData = edge.hasData(a.bits)
a.ready := in.d.ready
in.d.valid := a.valid
in.d.bits := edge.AccessAck(a.bits, UInt(0))
in.d.bits.opcode := Mux(hasData, TLMessages.AccessAck, TLMessages.AccessAckData)
// Tie off unused channels
in.b.valid := Bool(false)
in.c.ready := Bool(true)
in.e.ready := Bool(true)
}
}