Skip to content

Commit

Permalink
Merge pull request #664 from ucb-bar/fix-debug-ios
Browse files Browse the repository at this point in the history
Only punch realistic subset of DebugIO through chiptop | default to JTAG+Serial
  • Loading branch information
jerryz123 authored Sep 4, 2020
2 parents 942d881 + 3258fd8 commit 178a0e3
Show file tree
Hide file tree
Showing 16 changed files with 152 additions and 120 deletions.
38 changes: 19 additions & 19 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ jobs:
steps:
- prepare-rtl:
project-key: "chipyard-rocket"
prepare-chipyard-dmirocket:
executor: main-env
steps:
- prepare-rtl:
project-key: "chipyard-dmirocket"
prepare-chipyard-sha3:
executor: main-env
steps:
Expand All @@ -225,11 +230,6 @@ jobs:
steps:
- prepare-rtl:
project-key: "chipyard-boom"
prepare-rocketchip:
executor: main-env
steps:
- prepare-rtl:
project-key: "rocketchip"
prepare-chipyard-blkdev:
executor: main-env
steps:
Expand Down Expand Up @@ -297,6 +297,11 @@ jobs:
steps:
- run-tests:
project-key: "chipyard-rocket"
chipyard-dmirocket-run-tests:
executor: main-env
steps:
- run-tests:
project-key: "chipyard-dmirocket"
chipyard-sha3-run-tests:
executor: main-env
steps:
Expand All @@ -323,11 +328,6 @@ jobs:
steps:
- run-tests:
project-key: "chipyard-boom"
rocketchip-run-tests:
executor: main-env
steps:
- run-tests:
project-key: "rocketchip"
chipyard-hwacha-run-tests:
executor: main-env
steps:
Expand Down Expand Up @@ -451,6 +451,11 @@ workflows:
- install-riscv-toolchain
- install-verilator

- prepare-chipyard-dmirocket:
requires:
- install-riscv-toolchain
- install-verilator

- prepare-chipyard-sha3:
requires:
- install-riscv-toolchain
Expand All @@ -476,11 +481,6 @@ workflows:
- install-riscv-toolchain
- install-verilator

- prepare-rocketchip:
requires:
- install-riscv-toolchain
- install-verilator

- prepare-chipyard-blkdev:
requires:
- install-riscv-toolchain
Expand Down Expand Up @@ -547,6 +547,10 @@ workflows:
requires:
- prepare-chipyard-rocket

- chipyard-dmirocket-run-tests:
requires:
- prepare-chipyard-dmirocket

- chipyard-sha3-run-tests:
requires:
- prepare-chipyard-sha3
Expand All @@ -567,10 +571,6 @@ workflows:
requires:
- prepare-chipyard-boom

- rocketchip-run-tests:
requires:
- prepare-rocketchip

- chipyard-hwacha-run-tests:
requires:
- prepare-chipyard-hwacha
Expand Down
1 change: 1 addition & 0 deletions .circleci/defaults.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ LOCAL_FIRESIM_DIR=$LOCAL_CHIPYARD_DIR/sims/firesim/sim
# key value store to get the build strings
declare -A mapping
mapping["chipyard-rocket"]=""
mapping["chipyard-dmirocket"]=" CONFIG=dmiRocketConfig"
mapping["chipyard-sha3"]=" CONFIG=Sha3RocketConfig"
mapping["chipyard-streaming-fir"]=" CONFIG=StreamingFIRRocketConfig"
mapping["chipyard-streaming-passthrough"]=" CONFIG=StreamingPassthroughRocketConfig"
Expand Down
3 changes: 3 additions & 0 deletions .circleci/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ case $1 in
chipyard-rocket)
run_bmark ${mapping[$1]}
;;
chipyard-dmirocket)
run_bmark ${mapping[$1]}
;;
chipyard-boom)
run_bmark ${mapping[$1]}
;;
Expand Down
34 changes: 2 additions & 32 deletions docs/Advanced-Concepts/Chip-Communication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,38 +130,8 @@ Using the JTAG Interface
------------------------

The main way to use JTAG with a Rocket Chip based system is to instantiate the Debug Transfer Module (DTM)
and configure it to use a JTAG interface (by default the DTM is setup to use the DMI interface mentioned above).

Creating a DTM+JTAG Config
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First, a DTM config must be created for the system that you want to create.
This step is similar to the DMI simulation section within the :ref:`Starting the TSI or DMI Simulation` section.
The configuration is very similar to a DMI-based configuration. The main difference
is the addition of the ``WithJtagDTM`` config fragment that configures the instantiated DTM to use the JTAG protocol as the
bringup method.

.. literalinclude:: ../../generators/chipyard/src/main/scala/config/RocketConfigs.scala
:language: scala
:start-after: DOC include start: JtagRocket
:end-before: DOC include end: JtagRocket

Building a DTM+JTAG Simulator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After creating the config, call the ``make`` command like the following to build a simulator for your RTL:

.. code-block:: bash
cd sims/verilator
# or
cd sims/vcs
make CONFIG=jtagRocketConfig
In this example, the simulation will use the config that you previously specified, as well as set
the other parameters that are needed to satisfy the build system. After that point, you
should have a JTAG enabled simulator that you can attach to using OpenOCD and GDB!
and configure it to use a JTAG interface. The default Chipyard designs instantiate the DTM and configure it
to use JTAG. You may attach OpenOCD and GDB to any of the default JTAG-enabled designs.

Debugging with JTAG
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
9 changes: 8 additions & 1 deletion generators/chipyard/src/main/scala/ConfigFragments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import freechips.rocketchip.config.{Field, Parameters, Config}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.diplomacy.{LazyModule, ValName}
import freechips.rocketchip.devices.tilelink.{BootROMLocated}
import freechips.rocketchip.devices.debug.{Debug}
import freechips.rocketchip.devices.debug.{Debug, ExportDebug, DebugModuleKey, DMI}
import freechips.rocketchip.groundtest.{GroundTestSubsystem}
import freechips.rocketchip.tile._
import freechips.rocketchip.rocket.{RocketCoreParams, MulDivParams, DCacheParams, ICacheParams}
Expand Down Expand Up @@ -163,3 +163,10 @@ class WithTileDividedClock extends Config((site, here, up) => {
case ClockingSchemeKey => ClockingSchemeGenerators.harnessDividedClock
})

class WithDMIDTM extends Config((site, here, up) => {
case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI))
})

class WithNoDebug extends Config((site, here, up) => {
case DebugModuleKey => None
})
137 changes: 92 additions & 45 deletions generators/chipyard/src/main/scala/IOBinders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package chipyard
package object iobinders {

import chisel3._
import chisel3.util.experimental.{BoringUtils}
import chisel3.experimental.{Analog, IO}

import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImpLike}
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.jtag.{JTAGIO}
import freechips.rocketchip.subsystem._
import freechips.rocketchip.system.{SimAXIMem}
import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4SlaveNode, AXI4MasterNode, AXI4EdgeParameters}
Expand Down Expand Up @@ -163,30 +165,62 @@ object AddIOCells {
}

/**
* Add IO cells to a debug module and name the IO ports.
* @param psd A PSDIO bundle
* @param resetctrlOpt An optional ResetCtrlIO bundle
* @param debugOpt An optional DebugIO bundle
* @return Returns a tuple3 of (Top-level PSDIO IO; Optional top-level DebugIO IO; a list of IOCell module references)
* Add IO cells to a debug module and name the IO ports, for debug IO which must go off-chip
* For on-chip debug IO, drive them appropriately
* Mostly copied from rocket-chip/src/main/scala/devices/debug/Periphery.scala
* @param system A BaseSubsystem that might have a debug module
* @return Returns a tuple2 of (Generated debug io ports, Generated IOCells)
*/
def debug(psd: PSDIO, resetctrlOpt: Option[ResetCtrlIO], debugOpt: Option[DebugIO])(implicit p: Parameters):
(PSDIO, Option[ResetCtrlIO], Option[DebugIO], Seq[IOCell]) = {
val (psdPort, psdIOs) = IOCell.generateIOFromSignal(
psd, Some("iocell_psd"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync)
val debugTuple = debugOpt.map(d =>
IOCell.generateIOFromSignal(d, Some("iocell_debug"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync))
val debugPortOpt: Option[DebugIO] = debugTuple.map(_._1)
val debugIOs: Seq[IOCell] = debugTuple.map(_._2).toSeq.flatten
debugPortOpt.foreach(_.suggestName("debug"))

val resetctrlTuple = resetctrlOpt.map(d =>
IOCell.generateIOFromSignal(d, Some("iocell_resetctrl"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync))
val resetctrlPortOpt: Option[ResetCtrlIO] = resetctrlTuple.map(_._1)
val resetctrlIOs: Seq[IOCell] = resetctrlTuple.map(_._2).toSeq.flatten
resetctrlPortOpt.foreach(_.suggestName("resetctrl"))

psdPort.suggestName("psd")
(psdPort, resetctrlPortOpt, debugPortOpt, psdIOs ++ debugIOs ++ resetctrlIOs)
def debug(system: HasPeripheryDebugModuleImp)(implicit p: Parameters): (Seq[Bundle], Seq[IOCell]) = {
system.debug.map { debug =>
val tlbus = system.outer.asInstanceOf[BaseSubsystem].locateTLBusWrapper(p(ExportDebug).slaveWhere)
val debug_clock = Wire(Clock()).suggestName("debug_clock")
val debug_reset = Wire(Reset()).suggestName("debug_reset")
debug_clock := false.B.asClock // must provide default assignment to avoid firrtl unassigned error
debug_reset := false.B // must provide default assignment to avoid firrtl unassigned error
BoringUtils.bore(tlbus.module.clock, Seq(debug_clock))
BoringUtils.bore(tlbus.module.reset, Seq(debug_reset))

// We never use the PSDIO, so tie it off on-chip
system.psd.psd.foreach { _ <> 0.U.asTypeOf(new PSDTestMode) }
system.resetctrl.map { rcio => rcio.hartIsInReset.map { _ := debug_reset.asBool } }
system.debug.map { d =>
// Tie off extTrigger
d.extTrigger.foreach { t =>
t.in.req := false.B
t.out.ack := t.out.req
}
// Tie off disableDebug
d.disableDebug.foreach { d => d := false.B }
// Drive JTAG on-chip IOs
d.systemjtag.map { j =>
j.reset := debug_reset
j.mfr_id := system.p(JtagDTMKey).idcodeManufId.U(11.W)
j.part_number := system.p(JtagDTMKey).idcodePartNum.U(16.W)
j.version := system.p(JtagDTMKey).idcodeVersion.U(4.W)
}
}
Debug.connectDebugClockAndReset(Some(debug), debug_clock)(system.p)

// Add IOCells for the DMI/JTAG/APB ports
val dmiTuple = debug.clockeddmi.map { d =>
IOCell.generateIOFromSignal(d, Some("iocell_dmi"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync)
}
dmiTuple.map(_._1).foreach(_.suggestName("dmi"))

val jtagTuple = debug.systemjtag.map { j =>
IOCell.generateIOFromSignal(j.jtag, Some("iocell_jtag"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync)
}
jtagTuple.map(_._1).foreach(_.suggestName("jtag"))

val apbTuple = debug.apb.map { a =>
IOCell.generateIOFromSignal(a, Some("iocell_apb"), abstractResetAsAsync = p(GlobalResetSchemeKey).pinIsAsync)
}
apbTuple.map(_._1).foreach(_.suggestName("apb"))

val allTuples = (dmiTuple ++ jtagTuple ++ apbTuple).toSeq
(allTuples.map(_._1).toSeq, allTuples.flatMap(_._2).toSeq)
}.getOrElse((Nil, Nil))
}

/**
Expand Down Expand Up @@ -364,40 +398,53 @@ class WithTieOffL2FBusAXI extends OverrideIOBinder({
}
})

// TODO we need to rethink what "Tie-off-debug" means. The current system punches out
// excessive IOs.
class WithTiedOffDebug extends OverrideIOBinder({
class WithSimDebug extends OverrideIOBinder({
(system: HasPeripheryDebugModuleImp) => {
val (psdPort, resetctrlOpt, debugPortOpt, ioCells) =
AddIOCells.debug(system.psd, system.resetctrl, system.debug)(system.p)
val (ports, iocells) = AddIOCells.debug(system)(system.p)
val harnessFn = (th: HasHarnessSignalReferences) => {
Debug.tieoffDebug(debugPortOpt, resetctrlOpt, Some(psdPort))(system.p)
// tieoffDebug doesn't actually tie everything off :/
debugPortOpt.foreach { d =>
d.clockeddmi.foreach({ cdmi => cdmi.dmi.req.bits := DontCare; cdmi.dmiClock := th.harnessClock })
d.dmactiveAck := DontCare
d.clock := th.harnessClock // TODO fix: This should be driven from within the chip
val dtm_success = WireInit(false.B)
when (dtm_success) { th.success := true.B }
ports.map {
case d: ClockedDMIIO =>
val dtm = Module(new SimDTM()(system.p)).connect(th.harnessClock, th.harnessReset.asBool, d, dtm_success)
case j: JTAGIO =>
val jtag = Module(new SimJTAG(tickDelay=3)).connect(j, th.harnessClock, th.harnessReset.asBool, ~(th.harnessReset.asBool), dtm_success)
case _ =>
require(false, "We only support DMI or JTAG simulated debug connections")
}
Nil
}
Seq((Seq(psdPort) ++ resetctrlOpt ++ debugPortOpt.toSeq, Nil, Some(harnessFn)))
Seq((ports, iocells, Some(harnessFn)))
}
})

// TODO we need to rethink what this does. The current system punches out excessive IOs.
// Some of the debug clock/reset should be driven from on-chip
class WithSimDebug extends OverrideIOBinder({
class WithTiedOffDebug extends OverrideIOBinder({
(system: HasPeripheryDebugModuleImp) => {
val (psdPort, resetctrlPortOpt, debugPortOpt, ioCells) =
AddIOCells.debug(system.psd, system.resetctrl, system.debug)(system.p)
val (ports, iocells) = AddIOCells.debug(system)(system.p)
val harnessFn = (th: HasHarnessSignalReferences) => {
val dtm_success = Wire(Bool())
Debug.connectDebug(debugPortOpt, resetctrlPortOpt, psdPort, th.harnessClock, th.harnessReset.asBool, dtm_success)(system.p)
when (dtm_success) { th.success := true.B }
th.dutReset := th.harnessReset.asBool | debugPortOpt.map { debug => AsyncResetReg(debug.ndreset).asBool }.getOrElse(false.B)
ports.map {
case d: ClockedDMIIO =>
d.dmi.req.valid := false.B
d.dmi.req.bits := DontCare
d.dmi.resp.ready := true.B
d.dmiClock := false.B.asClock
d.dmiReset := true.B
case j: JTAGIO =>
j.TCK := true.B.asClock
j.TMS := true.B
j.TDI := true.B
j.TRSTn.foreach { r => r := true.B }
case a: ClockedAPBBundle =>
a.tieoff()
a.clock := false.B.asClock
a.reset := true.B.asAsyncReset
a.psel := false.B
a.penable := false.B
case _ => require(false)
}
Nil
}
Seq((Seq(psdPort) ++ debugPortOpt.toSeq, ioCells, Some(harnessFn)))
Seq((ports, iocells, Some(harnessFn)))
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ class AbstractConfig extends Config(
new chipyard.iobinders.WithUARTAdapter ++ // display UART with a SimUARTAdapter
new chipyard.iobinders.WithTieOffInterrupts ++ // tie off top-level interrupts
new chipyard.iobinders.WithBlackBoxSimMem ++ // drive the master AXI4 memory with a blackbox DRAMSim model
new chipyard.iobinders.WithTiedOffDebug ++ // tie off debug (since we are using SimSerial for testing)
new chipyard.iobinders.WithSimDebug ++ // attach SimJTAG
new chipyard.iobinders.WithSimSerial ++ // drive TSI with SimSerial for testing
new testchipip.WithTSI ++ // use testchipip serial offchip link
new chipyard.config.WithBootROM ++ // use default bootrom
new chipyard.config.WithUART ++ // add a UART
new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs
new chipyard.config.WithNoSubsystemDrivenClocks ++ // drive the subsystem diplomatic clocks from ChipTop instead of using implicit clocks
new freechips.rocketchip.subsystem.WithJtagDTM ++ // set the debug module to expose a JTAG port
new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip)
new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip)
new freechips.rocketchip.subsystem.WithInclusiveCache ++ // use Sifive L2 cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ class ArianeConfig extends Config(

class dmiArianeConfig extends Config(
new chipyard.iobinders.WithTiedOffSerial ++ // Tie off the serial port, override default instantiation of SimSerial
new chipyard.iobinders.WithSimDebug ++ // add SimDebug and use it to drive simulation, override default tie-off debug
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
new ariane.WithNArianeCores(1) ++ // single Ariane core
new chipyard.config.AbstractConfig)
12 changes: 2 additions & 10 deletions generators/chipyard/src/main/scala/config/RocketConfigs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,10 @@ class GemminiRocketConfig extends Config(
new chipyard.config.AbstractConfig)
// DOC include end: GemminiRocketConfig

// DOC include start: JtagRocket
class jtagRocketConfig extends Config(
new chipyard.iobinders.WithSimDebug ++ // add SimDebug, in addition to default SimSerial
new freechips.rocketchip.subsystem.WithJtagDTM ++ // sets DTM communication interface to JTAG
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
new chipyard.config.AbstractConfig)
// DOC include end: JtagRocket

// DOC include start: DmiRocket
class dmiRocketConfig extends Config(
new chipyard.iobinders.WithTiedOffSerial ++ // tie-off serial, override default add SimSerial
new chipyard.iobinders.WithSimDebug ++ // add SimDebug, override default tie-off debug
new chipyard.iobinders.WithTiedOffSerial ++ // don't use serial to drive the chip, since we use DMI instead
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
new chipyard.config.AbstractConfig)
// DOC include end: DmiRocket
Expand Down
Loading

0 comments on commit 178a0e3

Please sign in to comment.