Combine Coreplex and System Module Hierarchies (#875)
* coreplex collapse: peripherals now in coreplex * coreplex: better factoring of TLBusWrapper attachement points * diplomacy: allow monitorless :*= and :=* * rocket: don't connect monitors to tile tim slave ports * rename chip package to system * coreplex: only sbus has a splitter * TLFragmenter: Continuing my spot battles on requires without explanatory strings * pbus: toFixedWidthSingleBeatSlave * tilelink: more verbose requires * use the new system package for regression * sbus: add more explicit FIFO attachment points * delete leftover top-level utils * cleanup ResetVector and RTC
This commit is contained in:
@ -61,11 +61,11 @@ object DsbRegAddrs{
|
||||
|
||||
// We want DATA to immediately follow PROGBUF so that we can
|
||||
// use them interchangeably.
|
||||
def PROGBUF(cfg:DebugModuleConfig) = {DATA - (cfg.nProgramBufferWords * 4)}
|
||||
def PROGBUF(cfg:DebugModuleParams) = {DATA - (cfg.nProgramBufferWords * 4)}
|
||||
|
||||
// We want abstract to be immediately before PROGBUF
|
||||
// because we auto-generate 2 instructions.
|
||||
def ABSTRACT(cfg:DebugModuleConfig) = PROGBUF(cfg) - 8
|
||||
def ABSTRACT(cfg:DebugModuleParams) = PROGBUF(cfg) - 8
|
||||
|
||||
def FLAGS = 0x400
|
||||
def ROMBASE = 0x800
|
||||
@ -112,7 +112,7 @@ import DebugAbstractCommandType._
|
||||
* supportHartArray : Whether or not to implement the hart array register.
|
||||
**/
|
||||
|
||||
case class DebugModuleConfig (
|
||||
case class DebugModuleParams (
|
||||
nDMIAddrSize : Int = 7,
|
||||
nProgramBufferWords: Int = 16,
|
||||
nAbstractDataWords : Int = 4,
|
||||
@ -152,17 +152,17 @@ case class DebugModuleConfig (
|
||||
|
||||
}
|
||||
|
||||
object DefaultDebugModuleConfig {
|
||||
object DefaultDebugModuleParams {
|
||||
|
||||
def apply(xlen:Int /*TODO , val configStringAddr: Int*/): DebugModuleConfig = {
|
||||
new DebugModuleConfig().copy(
|
||||
def apply(xlen:Int /*TODO , val configStringAddr: Int*/): DebugModuleParams = {
|
||||
new DebugModuleParams().copy(
|
||||
nAbstractDataWords = (if (xlen == 32) 1 else if (xlen == 64) 2 else 4)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case object DMKey extends Field[DebugModuleConfig]
|
||||
case object DebugModuleParams extends Field[DebugModuleParams]
|
||||
|
||||
// *****************************************
|
||||
// Module Interfaces
|
||||
@ -192,7 +192,7 @@ class DMIResp( ) extends Bundle {
|
||||
* Therefore it has the 'flipped' version of this.
|
||||
*/
|
||||
class DMIIO(implicit val p: Parameters) extends ParameterizedBundle()(p) {
|
||||
val req = new DecoupledIO(new DMIReq(p(DMKey).nDMIAddrSize))
|
||||
val req = new DecoupledIO(new DMIReq(p(DebugModuleParams).nDMIAddrSize))
|
||||
val resp = new DecoupledIO(new DMIResp).flip()
|
||||
}
|
||||
|
||||
@ -443,7 +443,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int)(implicit p:
|
||||
|
||||
lazy val module = new LazyModuleImp(this){
|
||||
|
||||
val cfg = p(DMKey)
|
||||
val cfg = p(DebugModuleParams)
|
||||
|
||||
val nComponents = getNComponents()
|
||||
|
||||
|
262
src/main/scala/devices/debug/DebugTransport.scala
Normal file
262
src/main/scala/devices/debug/DebugTransport.scala
Normal file
@ -0,0 +1,262 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.debug
|
||||
|
||||
import Chisel._
|
||||
|
||||
import freechips.rocketchip.config._
|
||||
import freechips.rocketchip.jtag._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
case class JtagDTMConfig (
|
||||
idcodeVersion : Int, // chosen by manuf.
|
||||
idcodePartNum : Int, // Chosen by manuf.
|
||||
idcodeManufId : Int, // Assigned by JEDEC
|
||||
// Note: the actual manufId is passed in through a wire.
|
||||
// Do not forget to wire up io.jtag_mfr_id through your top-level to set the
|
||||
// mfr_id for this core.
|
||||
// If you wish to use this field in the config, you can obtain it along
|
||||
// the lines of p(JtagDTMKey).idcodeManufId.U(11.W).
|
||||
debugIdleCycles : Int)
|
||||
|
||||
case object JtagDTMKey extends Field[JtagDTMConfig]
|
||||
|
||||
class JtagDTMKeyDefault extends JtagDTMConfig(
|
||||
idcodeVersion = 0,
|
||||
idcodePartNum = 0,
|
||||
idcodeManufId = 0,
|
||||
debugIdleCycles = 5) // Reasonable guess for synchronization.
|
||||
|
||||
object dtmJTAGAddrs {
|
||||
def IDCODE = 0x1
|
||||
def DTM_INFO = 0x10
|
||||
def DMI_ACCESS = 0x11
|
||||
}
|
||||
|
||||
class DMIAccessUpdate(addrBits: Int) extends Bundle {
|
||||
val addr = UInt(width = addrBits)
|
||||
val data = UInt(width = DMIConsts.dmiDataSize)
|
||||
val op = UInt(width = DMIConsts.dmiOpSize)
|
||||
|
||||
override def cloneType = new DMIAccessUpdate(addrBits).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class DMIAccessCapture(addrBits: Int) extends Bundle {
|
||||
val addr = UInt(width = addrBits)
|
||||
val data = UInt(width = DMIConsts.dmiDataSize)
|
||||
val resp = UInt(width = DMIConsts.dmiRespSize)
|
||||
|
||||
override def cloneType = new DMIAccessCapture(addrBits).asInstanceOf[this.type]
|
||||
|
||||
}
|
||||
|
||||
class DTMInfo extends Bundle {
|
||||
val reserved1 = UInt(15.W)
|
||||
val dmireset = Bool()
|
||||
val reserved0 = UInt(1.W)
|
||||
val dmiIdleCycles = UInt(3.W)
|
||||
val dmiStatus = UInt(2.W)
|
||||
val debugAddrBits = UInt(6.W)
|
||||
val debugVersion = UInt(4.W)
|
||||
}
|
||||
|
||||
/** A wrapper around JTAG providing a reset signal and manufacturer id. */
|
||||
class SystemJTAGIO extends Bundle {
|
||||
val jtag = new JTAGIO(hasTRSTn = false).flip
|
||||
val reset = Bool(INPUT)
|
||||
val mfr_id = UInt(INPUT, 11)
|
||||
}
|
||||
|
||||
class DebugTransportModuleJTAG(debugAddrBits: Int, c: JtagDTMConfig)
|
||||
(implicit val p: Parameters) extends Module {
|
||||
|
||||
val io = new Bundle {
|
||||
val dmi = new DMIIO()(p)
|
||||
val jtag = Flipped(new JTAGIO(hasTRSTn = false)) // TODO: re-use SystemJTAGIO here?
|
||||
val jtag_reset = Bool(INPUT)
|
||||
val jtag_mfr_id = UInt(INPUT, 11)
|
||||
val fsmReset = Bool(OUTPUT)
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Reg and Wire Declarations
|
||||
|
||||
val dtmInfo = Wire(new DTMInfo)
|
||||
|
||||
val busyReg = RegInit(Bool(false))
|
||||
val stickyBusyReg = RegInit(Bool(false))
|
||||
val stickyNonzeroRespReg = RegInit(Bool(false))
|
||||
|
||||
val skipOpReg = Reg(init = Bool(false)) // Skip op because we're busy
|
||||
val downgradeOpReg = Reg(init = Bool(false)) // downgrade op because prev. failed.
|
||||
|
||||
val busy = Wire(Bool())
|
||||
val nonzeroResp = Wire(Bool())
|
||||
|
||||
val busyResp = Wire(new DMIAccessCapture(debugAddrBits))
|
||||
val nonbusyResp = Wire(new DMIAccessCapture(debugAddrBits))
|
||||
val dmiResp = Wire(new DMIAccessCapture(debugAddrBits))
|
||||
val nopResp = Wire(new DMIAccessCapture(debugAddrBits))
|
||||
|
||||
|
||||
val dmiReqReg = Reg(new DMIReq(debugAddrBits))
|
||||
val dmiReqValidReg = Reg(init = Bool(false));
|
||||
|
||||
val dmiStatus = Wire(UInt(width = 2))
|
||||
|
||||
//--------------------------------------------------------
|
||||
// DTM Info Chain Declaration
|
||||
|
||||
dmiStatus := Cat(stickyNonzeroRespReg, stickyNonzeroRespReg | stickyBusyReg)
|
||||
|
||||
dtmInfo.debugVersion := 1.U // This implements version 1 of the spec.
|
||||
dtmInfo.debugAddrBits := UInt(debugAddrBits)
|
||||
dtmInfo.dmiStatus := dmiStatus
|
||||
dtmInfo.dmiIdleCycles := UInt(c.debugIdleCycles)
|
||||
dtmInfo.reserved0 := 0.U
|
||||
dtmInfo.dmireset := false.B // This is write-only
|
||||
dtmInfo.reserved1 := 0.U
|
||||
|
||||
val dtmInfoChain = Module (CaptureUpdateChain(gen = new DTMInfo()))
|
||||
dtmInfoChain.io.capture.bits := dtmInfo
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Debug Access Chain Declaration
|
||||
|
||||
val dmiAccessChain = Module(CaptureUpdateChain(genCapture = new DMIAccessCapture(debugAddrBits),
|
||||
genUpdate = new DMIAccessUpdate(debugAddrBits)))
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Debug Access Support
|
||||
|
||||
// Busy Register. We become busy when we first try to send a request.
|
||||
// We stop being busy when we accept a response.
|
||||
|
||||
when (io.dmi.req.valid) {
|
||||
busyReg := Bool(true)
|
||||
}
|
||||
when (io.dmi.resp.fire()) {
|
||||
busyReg := Bool(false)
|
||||
}
|
||||
|
||||
// We are busy during a given CAPTURE
|
||||
// if we haven't received a valid response yet or if we
|
||||
// were busy last time without a reset.
|
||||
// busyReg will still be set when we check it,
|
||||
// so the logic for checking busy looks ahead.
|
||||
busy := (busyReg & !io.dmi.resp.valid) | stickyBusyReg;
|
||||
|
||||
// Downgrade/Skip. We make the decision to downgrade or skip
|
||||
// during every CAPTURE_DR, and use the result in UPDATE_DR.
|
||||
// The sticky versions are reset by write to dmiReset in DTM_INFO.
|
||||
when (dmiAccessChain.io.update.valid) {
|
||||
skipOpReg := Bool(false)
|
||||
downgradeOpReg := Bool(false)
|
||||
}
|
||||
when (dmiAccessChain.io.capture.capture) {
|
||||
skipOpReg := busy
|
||||
downgradeOpReg := (!busy & nonzeroResp)
|
||||
stickyBusyReg := busy
|
||||
stickyNonzeroRespReg := nonzeroResp
|
||||
}
|
||||
when (dtmInfoChain.io.update.valid) {
|
||||
when (dtmInfoChain.io.update.bits.dmireset) {
|
||||
stickyNonzeroRespReg := Bool(false)
|
||||
stickyBusyReg := Bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Especially for the first request, we must consider dtmResp.valid,
|
||||
// so that we don't consider junk in the FIFO to be an error response.
|
||||
// The current specification says that any non-zero response is an error.
|
||||
// But there is actually no case in the current design where you SHOULD get an error,
|
||||
// as we haven't implemented Bus Masters or Serial Ports, which are the only cases errors
|
||||
// can occur.
|
||||
nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp != UInt(0)))
|
||||
assert(!nonzeroResp, "There is no reason to get a non zero response in the current system.");
|
||||
assert(!stickyNonzeroRespReg, "There is no reason to have a sticky non zero response in the current system.");
|
||||
|
||||
busyResp.addr := UInt(0)
|
||||
busyResp.resp := Fill(DMIConsts.dmiRespSize, 1.U) // Generalizing busy to 'all-F'
|
||||
busyResp.data := UInt(0)
|
||||
|
||||
dmiResp.addr := dmiReqReg.addr
|
||||
dmiResp.resp := io.dmi.resp.bits.resp
|
||||
dmiResp.data := io.dmi.resp.bits.data
|
||||
|
||||
nopResp.addr := UInt(0)
|
||||
nopResp.resp := UInt(0)
|
||||
nopResp.data := UInt(0)
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Debug Access Chain Implementation
|
||||
|
||||
dmiAccessChain.io.capture.bits := Mux(busy, busyResp, Mux(io.dmi.resp.valid, dmiResp, nopResp))
|
||||
when (dmiAccessChain.io.update.valid) {
|
||||
skipOpReg := Bool(false)
|
||||
downgradeOpReg := Bool(false)
|
||||
}
|
||||
when (dmiAccessChain.io.capture.capture) {
|
||||
skipOpReg := busy
|
||||
downgradeOpReg := (!busy & nonzeroResp)
|
||||
stickyBusyReg := busy
|
||||
stickyNonzeroRespReg := nonzeroResp
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Drive Ready Valid Interface
|
||||
|
||||
val dmiReqValidCheck = Wire(init = Bool(false))
|
||||
assert(!(dmiReqValidCheck && io.dmi.req.fire()), "Conflicting updates for dmiReqValidReg, should not happen.");
|
||||
|
||||
when (dmiAccessChain.io.update.valid) {
|
||||
when (skipOpReg) {
|
||||
// Do Nothing
|
||||
}.elsewhen (downgradeOpReg || (dmiAccessChain.io.update.bits.op === DMIConsts.dmi_OP_NONE)) {
|
||||
//Do Nothing
|
||||
dmiReqReg.addr := UInt(0)
|
||||
dmiReqReg.data := UInt(0)
|
||||
dmiReqReg.op := UInt(0)
|
||||
}.otherwise {
|
||||
dmiReqReg := dmiAccessChain.io.update.bits
|
||||
dmiReqValidReg := Bool(true)
|
||||
dmiReqValidCheck := Bool(true)
|
||||
}
|
||||
}
|
||||
|
||||
when (io.dmi.req.fire()) {
|
||||
dmiReqValidReg := Bool(false)
|
||||
}
|
||||
|
||||
io.dmi.resp.ready := dmiAccessChain.io.capture.capture
|
||||
io.dmi.req.valid := dmiReqValidReg
|
||||
|
||||
// This is a name-based, not type-based assignment. Do these still work?
|
||||
io.dmi.req.bits := dmiReqReg
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Actual JTAG TAP
|
||||
val idcode = Wire(init = new JTAGIdcodeBundle().fromBits(0.U))
|
||||
idcode.always1 := 1.U
|
||||
idcode.version := c.idcodeVersion.U
|
||||
idcode.partNumber := c.idcodePartNum.U
|
||||
idcode.mfrId := io.jtag_mfr_id
|
||||
|
||||
val tapIO = JtagTapGenerator(irLength = 5,
|
||||
instructions = Map(dtmJTAGAddrs.DMI_ACCESS -> dmiAccessChain,
|
||||
dtmJTAGAddrs.DTM_INFO -> dtmInfoChain),
|
||||
icode = Some(dtmJTAGAddrs.IDCODE)
|
||||
)
|
||||
|
||||
tapIO.idcode.get := idcode
|
||||
tapIO.jtag <> io.jtag
|
||||
|
||||
tapIO.control.jtag_reset := io.jtag_reset
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Reset Generation (this is fed back to us by the instantiating module,
|
||||
// and is used to reset the debug registers).
|
||||
|
||||
io.fsmReset := tapIO.output.reset
|
||||
|
||||
}
|
123
src/main/scala/devices/debug/Periphery.scala
Normal file
123
src/main/scala/devices/debug/Periphery.scala
Normal file
@ -0,0 +1,123 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.debug
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.{Field, Parameters}
|
||||
import freechips.rocketchip.coreplex.HasPeripheryBus
|
||||
import freechips.rocketchip.devices.tilelink._
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.jtag._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
/** A knob selecting one of the two possible debug interfaces */
|
||||
case object IncludeJtagDTM extends Field[Boolean]
|
||||
|
||||
/** A wrapper bundle containing one of the two possible debug interfaces */
|
||||
class DebugIO(implicit p: Parameters) extends ParameterizedBundle()(p) {
|
||||
val clockeddmi = (!p(IncludeJtagDTM)).option(new ClockedDMIIO().flip)
|
||||
val systemjtag = (p(IncludeJtagDTM)).option(new SystemJTAGIO)
|
||||
val ndreset = Bool(OUTPUT)
|
||||
val dmactive = Bool(OUTPUT)
|
||||
}
|
||||
|
||||
/** Either adds a JTAG DTM to system, and exports a JTAG interface,
|
||||
* or exports the Debug Module Interface (DMI), based on a global parameter.
|
||||
*/
|
||||
trait HasPeripheryDebug extends HasPeripheryBus {
|
||||
val module: HasPeripheryDebugModuleImp
|
||||
|
||||
val debug = LazyModule(new TLDebugModule())
|
||||
|
||||
debug.node := pbus.toVariableWidthSlaves
|
||||
}
|
||||
|
||||
trait HasPeripheryDebugBundle {
|
||||
implicit val p: Parameters
|
||||
|
||||
val debug: DebugIO
|
||||
|
||||
def connectDebug(c: Clock, r: Bool, out: Bool) {
|
||||
debug.clockeddmi.foreach { d =>
|
||||
val dtm = Module(new SimDTM).connect(c, r, d, out)
|
||||
}
|
||||
debug.systemjtag.foreach { sj =>
|
||||
val jtag = Module(new JTAGVPI).connect(sj.jtag, sj.reset, r, out)
|
||||
sj.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait HasPeripheryDebugModuleImp extends LazyMultiIOModuleImp with HasPeripheryDebugBundle {
|
||||
val outer: HasPeripheryDebug
|
||||
|
||||
val debug = IO(new DebugIO)
|
||||
|
||||
debug.clockeddmi.foreach { dbg => outer.debug.module.io.dmi <> dbg }
|
||||
|
||||
val dtm = debug.systemjtag.map { sj =>
|
||||
val dtm = Module(new DebugTransportModuleJTAG(p(DebugModuleParams).nDMIAddrSize, p(JtagDTMKey)))
|
||||
dtm.io.jtag <> sj.jtag
|
||||
|
||||
dtm.clock := sj.jtag.TCK
|
||||
dtm.io.jtag_reset := sj.reset
|
||||
dtm.io.jtag_mfr_id := sj.mfr_id
|
||||
dtm.reset := dtm.io.fsmReset
|
||||
|
||||
outer.debug.module.io.dmi.dmi <> dtm.io.dmi
|
||||
outer.debug.module.io.dmi.dmiClock := sj.jtag.TCK
|
||||
outer.debug.module.io.dmi.dmiReset := ResetCatchAndSync(sj.jtag.TCK, sj.reset, "dmiResetCatch")
|
||||
dtm
|
||||
}
|
||||
|
||||
debug.ndreset := outer.debug.module.io.ctrl.ndreset
|
||||
debug.dmactive := outer.debug.module.io.ctrl.dmactive
|
||||
|
||||
// TODO in inheriting traits: Set this to something meaningful, e.g. "component is in reset or powered down"
|
||||
outer.debug.module.io.ctrl.debugUnavail.foreach { _ := Bool(false) }
|
||||
}
|
||||
|
||||
class SimDTM(implicit p: Parameters) extends BlackBox {
|
||||
val io = new Bundle {
|
||||
val clk = Clock(INPUT)
|
||||
val reset = Bool(INPUT)
|
||||
val debug = new DMIIO
|
||||
val exit = UInt(OUTPUT, 32)
|
||||
}
|
||||
|
||||
def connect(tbclk: Clock, tbreset: Bool, dutio: ClockedDMIIO, tbsuccess: Bool) = {
|
||||
io.clk := tbclk
|
||||
io.reset := tbreset
|
||||
dutio.dmi <> io.debug
|
||||
dutio.dmiClock := tbclk
|
||||
dutio.dmiReset := tbreset
|
||||
|
||||
tbsuccess := io.exit === UInt(1)
|
||||
when (io.exit >= UInt(2)) {
|
||||
printf("*** FAILED *** (exit code = %d)\n", io.exit >> UInt(1))
|
||||
stop(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JTAGVPI(implicit val p: Parameters) extends BlackBox {
|
||||
val io = new Bundle {
|
||||
val jtag = new JTAGIO(hasTRSTn = false)
|
||||
val enable = Bool(INPUT)
|
||||
val init_done = Bool(INPUT)
|
||||
}
|
||||
|
||||
def connect(dutio: JTAGIO, jtag_reset: Bool, tbreset: Bool, tbsuccess: Bool) = {
|
||||
dutio <> io.jtag
|
||||
|
||||
dutio.TRSTn.foreach{ _:= false.B}
|
||||
jtag_reset := tbreset
|
||||
|
||||
io.enable := ~tbreset
|
||||
io.init_done := ~tbreset
|
||||
|
||||
// Success is determined by the gdbserver
|
||||
// which is controlling this simulation.
|
||||
tbsuccess := Bool(false)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user