1
0

Initial commit.

This commit is contained in:
SiFive
2016-11-29 04:08:44 -08:00
commit 7916ef5249
42 changed files with 3763 additions and 0 deletions

View File

@ -0,0 +1,105 @@
// See LICENSE for license details.
package sifive.blocks.devices.mockaon
import Chisel._
import config._
import regmapper._
import uncore.tilelink2._
import rocketchip.PeripheryBusConfig
import sifive.blocks.util.GenericTimer
case class MockAONConfig(
address: BigInt = BigInt(0x10000000),
nBackupRegs: Int = 16) {
def size: Int = 0x1000
def regBytes: Int = 4
def wdogOffset: Int = 0
def rtcOffset: Int = 0x40
def backupRegOffset: Int = 0x80
def pmuOffset: Int = 0x100
}
trait HasMockAONParameters {
val params: (MockAONConfig, Parameters)
val c = params._1
implicit val p = params._2
}
class MockAONPMUIO extends Bundle {
val vddpaden = Bool(OUTPUT)
val dwakeup = Bool(INPUT)
}
class MockAONMOffRstIO extends Bundle {
val hfclkrst = Bool(OUTPUT)
val corerst = Bool(OUTPUT)
}
trait MockAONBundle extends Bundle with HasMockAONParameters {
// Output of the Power Management Sequencer
val moff = new MockAONMOffRstIO ()
// This goes out to wrapper
// to be combined to create aon_rst.
val wdog_rst = Bool(OUTPUT)
// This goes out to wrapper
// and comes back as our clk
val lfclk = Clock(OUTPUT)
val pmu = new MockAONPMUIO
val lfextclk = Clock(INPUT)
val resetCauses = new ResetCauses().asInput
}
trait MockAONModule extends Module with HasRegMap with HasMockAONParameters {
val io: MockAONBundle
// the expectation here is that Chisel's implicit reset is aonrst,
// which is asynchronous, so don't use synchronous-reset registers.
val rtc = Module(new RTC)
val pmu = Module(new PMU(new DevKitPMUConfig))
io.moff <> pmu.io.control
io.pmu.vddpaden := pmu.io.control.vddpaden
pmu.io.wakeup.dwakeup := io.pmu.dwakeup
pmu.io.wakeup.awakeup := Bool(false)
pmu.io.wakeup.rtc := rtc.io.ip(0)
pmu.io.resetCauses := io.resetCauses
val pmuRegMap = {
val regs = pmu.io.regs.wakeupProgram ++ pmu.io.regs.sleepProgram ++
Seq(pmu.io.regs.ie, pmu.io.regs.cause, pmu.io.regs.sleep, pmu.io.regs.key)
for ((r, i) <- regs.zipWithIndex)
yield (c.pmuOffset + c.regBytes*i) -> Seq(r.toRegField())
}
interrupts(1) := rtc.io.ip(0)
val wdog = Module(new WatchdogTimer)
io.wdog_rst := wdog.io.rst
wdog.io.corerst := pmu.io.control.corerst
interrupts(0) := wdog.io.ip(0)
// If there are multiple lfclks to choose from, we can mux them here.
io.lfclk := io.lfextclk
val backupRegs = Seq.fill(c.nBackupRegs)(Reg(UInt(width = c.regBytes * 8)))
val backupRegMap =
for ((reg, i) <- backupRegs.zipWithIndex)
yield (c.backupRegOffset + c.regBytes*i) -> Seq(RegField(reg.getWidth, RegReadFn(reg), RegWriteFn(reg)))
regmap((backupRegMap ++
GenericTimer.timerRegMap(wdog, c.wdogOffset, c.regBytes) ++
GenericTimer.timerRegMap(rtc, c.rtcOffset, c.regBytes) ++
pmuRegMap):_*)
}
class MockAON(c: MockAONConfig)(implicit val p: Parameters)
extends TLRegisterRouter(c.address, interrupts = 2, size = c.size, beatBytes = p(PeripheryBusConfig).beatBytes, concurrency = 1)(
new TLRegBundle((c, p), _) with MockAONBundle)(
new TLRegModule((c, p), _, _) with MockAONModule)

View File

@ -0,0 +1,42 @@
// See LICENSE for license details.
package sifive.blocks.devices.mockaon
import Chisel._
import diplomacy.LazyModule
import rocketchip.{TopNetwork,TopNetworkModule}
import uncore.tilelink2.{IntXing, TLAsyncCrossingSource, TLFragmenter}
import coreplex._
trait PeripheryMockAON extends TopNetwork {
val mockAONConfig: MockAONConfig
val coreplex: CoreplexRISCVPlatform
// We override the clock & Reset here so that all synchronizers, etc
// are in the proper clock domain.
val aon = LazyModule(new MockAONWrapper(mockAONConfig))
val aon_int = LazyModule(new IntXing)
aon.node := TLAsyncCrossingSource()(TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node))
aon_int.intnode := aon.intnode
intBus.intnode := aon_int.intnode
}
trait PeripheryMockAONBundle {
val aon = new MockAONWrapperBundle()
}
trait PeripheryMockAONModule {
this: TopNetworkModule {
val outer: PeripheryMockAON
val io: PeripheryMockAONBundle
} =>
io.aon <> outer.aon.module.io
// Explicit clock & reset are unused in MockAONWrapper.
// Tie to check this assumption.
outer.aon.module.clock := Bool(false).asClock
outer.aon.module.reset := Bool(true)
outer.coreplex.module.io.rtcToggle := outer.aon.module.io.rtc.asUInt.toBool
}

View File

@ -0,0 +1,181 @@
// See LICENSE for license details.
package sifive.blocks.devices.mockaon
import Chisel._
import config._
import diplomacy._
import uncore.tilelink2._
import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl, GPIOInputPinCtrl}
import sifive.blocks.util.{DeglitchShiftRegister, ResetCatchAndSync}
import util._
/* The wrapper handles the Clock and Reset Generation for The AON block itself,
and instantiates real pad controls (aka pull-ups)*/
class MockAONWrapperPMUIO extends Bundle {
val dwakeup_n = new GPIOPin()
val vddpaden = new GPIOPin()
}
class MockAONWrapperPadsIO extends Bundle {
val erst_n = new GPIOPin()
val lfextclk = new GPIOPin()
val pmu = new MockAONWrapperPMUIO()
}
class MockAONWrapperBundle extends Bundle {
val pads = new MockAONWrapperPadsIO()
val rsts = new MockAONMOffRstIO()
}
class MockAONWrapper(c: MockAONConfig)(implicit val p: Parameters) extends LazyModule {
val node = TLAsyncInputNode()
val intnode = IntOutputNode()
val aon = LazyModule (new MockAON(c)(p))
// We only need to isolate the signals
// coming from MOFF to AON,
// since AON is never off while MOFF is on.
// The MOFF is on the "in" side of the Isolation.
// AON is on the "out" side of the Isolation.
def isoOut(iso: Bool, x: UInt): UInt = IsoZero(iso, x)
def isoIn(iso: Bool, x: UInt): UInt = x
val isolation = LazyModule(new TLIsolation(fOut = isoOut, fIn = isoIn))
val crossing = LazyModule(new TLAsyncCrossingSink(depth = 1))
isolation.node := node
crossing.node := isolation.node
val crossing_monitor = (aon.node := crossing.node)
// crossing lives outside in Periphery
intnode := aon.intnode
lazy val module = new LazyModuleImp(this) {
val io = new MockAONWrapperBundle {
val in = node.bundleIn
val ip = intnode.bundleOut
val rtc = Clock(OUTPUT)
}
val aon_io = aon.module.io
val pads = io.pads
// -----------------------------------------------
// Generation of aonrst
// -----------------------------------------------
// ERST
val erst = ~ GPIOInputPinCtrl(pads.erst_n, pue = Bool(true))
aon_io.resetCauses.erst := erst
aon_io.resetCauses.wdogrst := aon_io.wdog_rst
// PORRST
val porrst = Bool(false) // TODO
aon_io.resetCauses.porrst := porrst
//--------------------------------------------------
// Drive "Mostly Off" Reset Signals (these
// are synchronized inside MOFF as needed)
//--------------------------------------------------
io.rsts.hfclkrst := aon_io.moff.hfclkrst
io.rsts.corerst := aon_io.moff.corerst
//--------------------------------------------------
// Generate the LFCLK input to AON
// This is the same clock that is driven to this
// block as 'clock'.
//--------------------------------------------------
// LFCLK Override
// Note that the actual mux lives inside AON itself.
// Therefore, the lfclk which comes out of AON is the
// true clock that AON and AONWrapper are running off of.
val lfextclk = GPIOInputPinCtrl(pads.lfextclk, pue=Bool(true))
aon_io.lfextclk := lfextclk.asClock
// Drive AON's clock and Reset
val lfclk = aon_io.lfclk
val aonrst_catch = Module (new ResetCatchAndSync(3))
aonrst_catch.reset := erst | aon_io.wdog_rst
aonrst_catch.clock := lfclk
aon.module.reset := aonrst_catch.io.sync_reset
aon.module.clock := lfclk
//--------------------------------------------------
// TL2 Register Access Interface
//--------------------------------------------------
// Safely cross TL2 into AON Domain
// Ensure that both are reset and clocked
// at the same time.
// Note that aon.moff.corerst is synchronous
// to aon.module.clock, so this is safe.
val crossing_slave_reset = ResetCatchAndSync(lfclk,
aon.module.io.moff.corerst | aon.module.reset)
crossing.module.clock := lfclk
crossing.module.reset := crossing_slave_reset
crossing_monitor.foreach { lm =>
lm.module.clock := lfclk
lm.module.reset := crossing_slave_reset
}
// Note that aon.moff.corerst is synchronous
// to aon.module.clock, so this is safe.
isolation.module.io.iso_out := aon.module.io.moff.corerst
isolation.module.io.iso_in := Bool(true)
//--------------------------------------------------
// PMU <--> pads Interface
//--------------------------------------------------
val dwakeup_n_async = GPIOInputPinCtrl(pads.pmu.dwakeup_n, pue=Bool(true))
val dwakeup_deglitch = Module (new DeglitchShiftRegister(3))
dwakeup_deglitch.clock := lfclk
dwakeup_deglitch.io.d := ~dwakeup_n_async
aon.module.io.pmu.dwakeup := dwakeup_deglitch.io.q
GPIOOutputPinCtrl(pads.pmu.vddpaden, aon.module.io.pmu.vddpaden)
//--------------------------------------------------
// Connect signals to MOFF
//--------------------------------------------------
io.rtc := aon_io.lfclk
}
}
// -----------------------------------------------
// Isolation Cells
// -----------------------------------------------
class IsoZero extends Module {
val io = new Bundle {
val in = Bool(INPUT)
val iso = Bool(INPUT)
val out = Bool(OUTPUT)
}
io.out := io.in & ~io.iso
}
object IsoZero {
def apply (iso: Bool, in: UInt): UInt = {
val w = in.getWidth
val isos: List[IsoZero] = List.tabulate(in.getWidth)(
x => Module(new IsoZero).suggestName(s"iso_$x")
)
for ((z, i) <- isos.zipWithIndex) {
z.io.in := in(i)
z.io.iso := iso
}
isos.map(_.io.out).asUInt
}
}

View File

@ -0,0 +1,152 @@
// See LICENSE for license details.
package sifive.blocks.devices.mockaon
import Chisel._
import Chisel.ImplicitConversions._
import util._
import sifive.blocks.util.SRLatch
import sifive.blocks.util.{SlaveRegIF}
class WakeupCauses extends Bundle {
val awakeup = Bool()
val dwakeup = Bool()
val rtc = Bool()
val reset = Bool()
}
class ResetCauses extends Bundle {
val wdogrst = Bool()
val erst = Bool()
val porrst = Bool()
}
class PMUSignals extends Bundle {
val hfclkrst = Bool()
val corerst = Bool()
val reserved1 = Bool()
val vddpaden = Bool()
val reserved0 = Bool()
}
class PMUInstruction extends Bundle {
val sigs = new PMUSignals
val dt = UInt(width = 4)
}
class PMUConfig(wakeupProgramIn: Seq[Int],
sleepProgramIn: Seq[Int]) {
val programLength = 8
val nWakeupCauses = new WakeupCauses().elements.size
val wakeupProgram = wakeupProgramIn.padTo(programLength, wakeupProgramIn.last)
val sleepProgram = sleepProgramIn.padTo(programLength, sleepProgramIn.last)
require(wakeupProgram.length == programLength)
require(sleepProgram.length == programLength)
}
class DevKitPMUConfig extends PMUConfig( // TODO
Seq(0x1f0, 0x0f8, 0x030),
Seq(0x0f0, 0x1f0, 0x1d0, 0x1c0))
class PMURegs(c: PMUConfig) extends Bundle {
val ie = new SlaveRegIF(c.nWakeupCauses)
val cause = new SlaveRegIF(32)
val sleep = new SlaveRegIF(32)
val key = new SlaveRegIF(32)
val wakeupProgram = Vec(c.programLength, new SlaveRegIF(32))
val sleepProgram = Vec(c.programLength, new SlaveRegIF(32))
}
class PMUCore(c: PMUConfig)(resetIn: Bool) extends Module(_reset = resetIn) {
val io = new Bundle {
val wakeup = new WakeupCauses().asInput
val control = Valid(new PMUSignals)
val resetCause = UInt(INPUT, log2Ceil(new ResetCauses().getWidth))
val regs = new PMURegs(c)
}
val run = Reg(init = Bool(true))
val awake = Reg(init = Bool(true))
val unlocked = {
val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key)
RegEnable(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, Bool(false), io.regs.key.write.valid || writeAny)
}
val wantSleep = RegEnable(Bool(true), Bool(false), io.regs.sleep.write.valid && unlocked)
val pc = Reg(init = UInt(0, log2Ceil(c.programLength)))
val wakeupCause = Reg(init = UInt(0, log2Ceil(c.nWakeupCauses)))
val ie = RegEnable(io.regs.ie.write.bits, io.regs.ie.write.valid && unlocked) | 1 /* POR always enabled */
val insnWidth = new PMUInstruction().getWidth
val wakeupProgram = c.wakeupProgram.map(v => Reg(init = UInt(v, insnWidth)))
val sleepProgram = c.sleepProgram.map(v => Reg(init = UInt(v, insnWidth)))
val insnBits = Mux(awake, wakeupProgram(pc), sleepProgram(pc))
val insn = new PMUInstruction().fromBits(insnBits)
val count = Reg(init = UInt(0, 1 << insn.dt.getWidth))
val tick = (count ^ (count + 1))(insn.dt)
val npc = pc +& 1
val last = npc >= c.programLength
io.control.valid := run && !last && tick
io.control.bits := insn.sigs
when (run) {
count := count + 1
when (tick) {
count := 0
require(isPow2(c.programLength))
run := !last
pc := npc
}
}.otherwise {
val maskedWakeupCauses = ie & io.wakeup.asUInt
when (!awake && maskedWakeupCauses.orR) {
run := true
awake := true
wakeupCause := PriorityEncoder(maskedWakeupCauses)
}
when (awake && wantSleep) {
run := true
awake := false
wantSleep := false
}
}
io.regs.cause.read := wakeupCause | (io.resetCause << 8)
io.regs.ie.read := ie
io.regs.key.read := unlocked
io.regs.sleep.read := 0
for ((port, reg) <- (io.regs.wakeupProgram ++ io.regs.sleepProgram) zip (wakeupProgram ++ sleepProgram)) {
port.read := reg
when (port.write.valid && unlocked) { reg := port.write.bits }
}
}
class PMU(val c: PMUConfig) extends Module {
val io = new Bundle {
val wakeup = new WakeupCauses().asInput
val control = new PMUSignals().asOutput
val regs = new PMURegs(c)
val resetCauses = new ResetCauses().asInput
}
val core = Module(new PMUCore(c)(resetIn = Reg(next = Reg(next = reset))))
io <> core.io
core.io.wakeup.reset := false // this is implied by resetting the PMU
// during aonrst, hold all control signals high
val latch = ~AsyncResetReg(~core.io.control.bits.asUInt, core.io.control.valid)
io.control := io.control.fromBits(latch)
core.io.resetCause := {
val cause = io.resetCauses.asUInt
val latches = for (i <- 0 until cause.getWidth) yield {
val latch = Module(new SRLatch)
latch.io.set := cause(i)
latch.io.reset := (0 until cause.getWidth).filter(_ != i).map(cause(_)).reduce(_||_)
latch.io.q
}
OHToUInt(latches)
}
}

View File

@ -0,0 +1,58 @@
// See LICENSE for license details.
package sifive.blocks.devices.mockaon
import Chisel._
import Chisel.ImplicitConversions._
import util.AsyncResetReg
import sifive.blocks.util.{SlaveRegIF, GenericTimer}
object WatchdogTimer {
def writeAnyExceptKey(regs: Bundle, keyReg: SlaveRegIF): Bool = {
regs.elements.values.filter(_ ne keyReg).map({
case v: Vec[SlaveRegIF] @unchecked => v.map(_.write.valid).reduce(_||_)
case s: SlaveRegIF => s.write.valid
}).reduce(_||_)
}
val key = 0x51F15E
}
class WatchdogTimer extends GenericTimer {
protected def countWidth = 31
protected def cmpWidth = 16
protected def ncmp = 1
protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.bits(12), io.regs.cfg.write.valid && unlocked)(0)
override protected lazy val countAwake = AsyncResetReg(io.regs.cfg.write.bits(13), io.regs.cfg.write.valid && unlocked)(0)
protected lazy val countEn = {
val corerstSynchronized = Reg(next = Reg(next = io.corerst))
countAlways || (countAwake && !corerstSynchronized)
}
override protected lazy val rsten = AsyncResetReg(io.regs.cfg.write.bits(8), io.regs.cfg.write.valid && unlocked)(0)
protected lazy val ip = RegEnable(io.regs.cfg.write.bits(28) || elapsed(0), (io.regs.cfg.write.valid && unlocked) || elapsed(0))
override protected lazy val unlocked = {
val writeAny = WatchdogTimer.writeAnyExceptKey(io.regs, io.regs.key)
AsyncResetReg(io.regs.key.write.bits === WatchdogTimer.key && !writeAny, io.regs.key.write.valid || writeAny)(0)
}
protected lazy val feed = {
val food = 0xD09F00D
unlocked && io.regs.feed.write.valid && io.regs.feed.write.bits === food
}
lazy val io = new GenericTimerIO {
val corerst = Bool(INPUT)
val rst = Bool(OUTPUT)
}
io.rst := AsyncResetReg(Bool(true), rsten && elapsed(0))
}
class RTC extends GenericTimer {
protected def countWidth = 48
protected def cmpWidth = 32
protected def ncmp = 1
protected def countEn = countAlways
override protected lazy val ip = Reg(next = elapsed(0))
override protected lazy val zerocmp = Bool(false)
protected lazy val countAlways = AsyncResetReg(io.regs.cfg.write.bits(12), io.regs.cfg.write.valid && unlocked)(0)
protected lazy val feed = Bool(false)
lazy val io = new GenericTimerIO
}