Add ChipTop to enable real chip configs with IO cells, etc. (#480)

This adds an additional layer (ChipTop) between the System module and the TestHarness. The IOBinder API is now changed to take only a single parameter (an Any) and return a 3 things: The IO port(s), the IO cell(s), and a function to call inside the test harness, which is analogous to the old IOBinder function, except that it takes a TestHarness object as an argument instead of (clock, reset, success).
* A new Top-level module, ChipTop, has been created. ChipTop instantiates a "system" module specified by BuildSystem.
* BuildTop now builds a ChipTop dut module in the TestHarness by default
* A new BuildSystem key has been added, which by default builds DigitalTop (previously just called Top)
* The IOBinders API has changed. IOBinders are now called inside of ChipTop and return a tuple3 of (IO ports, IO cells, harness functions). The harness functions are now called inside the TestHarness (this is analogous to the previous IOBinder functions).
* IO cell models have been included in ChipTop. These can be replaced with real IO cells for tapeout, or used as-is for simulation.
* The default for the TOP make variable is now ChipTop (was Top)
This commit is contained in:
John Wright
2020-04-01 14:03:56 -07:00
committed by GitHub
parent 3d253c0f67
commit 1f98c84210
23 changed files with 444 additions and 166 deletions

View File

@@ -0,0 +1,102 @@
package chipyard
import chisel3._
import scala.collection.mutable.{ArrayBuffer}
import freechips.rocketchip.config.{Parameters, Field}
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.util.{ResetCatchAndSync}
import chipyard.config.ConfigValName._
import chipyard.iobinders.{IOBinders, TestHarnessFunction, IOBinderTuple}
import barstools.iocell.chisel._
case object BuildSystem extends Field[Parameters => RawModule]((p: Parameters) => Module(LazyModule(new DigitalTop()(p)).suggestName("system").module))
/**
* The base class used for building chips. This constructor instantiates a module specified by the BuildSystem parameter,
* named "system", which is an instance of DigitalTop by default. The default clock and reset for "system" are set by two
* wires, "systemClock" and "systemReset", which are intended to be driven by traits mixed-in with this base class.
*/
abstract class BaseChipTop()(implicit val p: Parameters) extends RawModule with HasTestHarnessFunctions {
// A publicly accessible list of IO cells (useful for a floorplanning tool, for example)
val iocells = ArrayBuffer.empty[IOCell]
// A list of functions to call in the test harness
val harnessFunctions = ArrayBuffer.empty[TestHarnessFunction]
// The system clock
val systemClock = Wire(Input(Clock()))
// The system reset (synchronous to clock)
val systemReset = Wire(Input(Bool()))
// The system module specified by BuildSystem
val system = withClockAndReset(systemClock, systemReset) { p(BuildSystem)(p) }
// Call all of the IOBinders and provide them with a default clock and reset
withClockAndReset(systemClock, systemReset) {
val (_ports, _iocells, _harnessFunctions) = p(IOBinders).values.map(_(system)).flatten.unzip3
// We ignore _ports for now...
iocells ++= _iocells.flatten
harnessFunctions ++= _harnessFunctions.flatten
}
}
/**
* A simple clock and reset implementation that punches out clock and reset ports with the same
* names as the implicit clock and reset for standard Module classes. Reset is synchronous to
* clock, which may not be a good idea to use for tapeouts.
*/
trait HasChipTopSimpleClockAndReset { this: BaseChipTop =>
val (clock, systemClockIO) = IOCell.generateIOFromSignal(systemClock, Some("iocell_clock"))
val (reset, systemResetIO) = IOCell.generateIOFromSignal(systemReset, Some("iocell_reset"))
iocells ++= systemClockIO
iocells ++= systemResetIO
// Add a TestHarnessFunction that connects clock and reset
harnessFunctions += { (th: TestHarness) => {
// Connect clock; it's not done implicitly with RawModule
clock := th.clock
// Connect reset; it's not done implicitly with RawModule
// Note that we need to use dutReset, not harnessReset
reset := th.dutReset
Nil
} }
}
/**
* Variant of HasChipTopSimpleClockAndReset that adds a reset synchronizer so that the top-level reset
* can be asynchronous with clock, which is useful for tapeout configs.
*/
trait HasChipTopSimpleClockAndCaughtReset { this: BaseChipTop =>
val asyncResetCore = Wire(Input(Bool()))
systemReset := ResetCatchAndSync(systemClock, asyncResetCore)
val (clock, systemClockIO) = IOCell.generateIOFromSignal(systemClock, Some("iocell_clock"))
val (areset, asyncResetIO) = IOCell.generateIOFromSignal(asyncResetCore, Some("iocell_areset"))
iocells ++= systemClockIO
iocells ++= asyncResetIO
// Add a TestHarnessFunction that connects clock and areset
harnessFunctions += { (th: TestHarness) => {
// Connect clock; it's not done implicitly with RawModule
clock := th.clock
// Connect reset; it's not done implicitly with RawModule
// Note that we need to use dutReset, not harnessReset
areset := th.dutReset
Nil
} }
}
class ChipTop()(implicit p: Parameters) extends BaseChipTop()(p)
with HasChipTopSimpleClockAndReset
class ChipTopCaughtReset()(implicit p: Parameters) extends BaseChipTop()(p)
with HasChipTopSimpleClockAndCaughtReset

View File

@@ -21,7 +21,7 @@ import hwacha.{Hwacha}
import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._
import chipyard.{BuildTop}
import chipyard.{BuildTop, BuildSystem, ChipTopCaughtReset}
/**
* TODO: Why do we need this?
@@ -65,8 +65,8 @@ class WithL2TLBs(entries: Int) extends Config((site, here, up) => {
))
})
class WithTracegenTop extends Config((site, here, up) => {
case BuildTop => (p: Parameters) => Module(LazyModule(new tracegen.TraceGenSystem()(p)).suggestName("Top").module)
class WithTracegenSystem extends Config((site, here, up) => {
case BuildSystem => (p: Parameters) => Module(LazyModule(new tracegen.TraceGenSystem()(p)).suggestName("Top").module)
})
@@ -150,3 +150,13 @@ class WithControlCore extends Config((site, here, up) => {
)
case MaxHartIdBits => log2Up(up(RocketTilesKey, site).size + up(BoomTilesKey, site).size + 1)
})
/**
* Config fragment to use ChipTopCaughtReset as the top module, which adds a reset synchronizer to
* the top-level reset, allowing it to be asynchronous with the clock.
* NOTE: You must remember to set TOP=WithChipTopCaughtReset when building with this config
*/
class WithChipTopCaughtReset extends Config((site, here, up) => {
case BuildTop => (p: Parameters) => Module(new ChipTopCaughtReset()(p).suggestName("top"))
})

View File

@@ -11,8 +11,8 @@ import freechips.rocketchip.devices.tilelink._
// BOOM and/or Rocket Top Level Systems
// ------------------------------------
// DOC include start: Top
class Top(implicit p: Parameters) extends System
// DOC include start: DigitalTop
class DigitalTop(implicit p: Parameters) extends System
with testchipip.CanHaveTraceIO // Enables optionally adding trace IO
with testchipip.CanHaveBackingScratchpad // Enables optionally adding a backing scratchpad
with testchipip.CanHavePeripheryBlockDevice // Enables optionally adding the block device
@@ -23,10 +23,10 @@ class Top(implicit p: Parameters) extends System
with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget
with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget
{
override lazy val module = new TopModule(this)
override lazy val module = new DigitalTopModule(this)
}
class TopModule[+L <: Top](l: L) extends SystemModule(l)
class DigitalTopModule[+L <: DigitalTop](l: L) extends SystemModule(l)
with testchipip.CanHaveTraceIOModuleImp
with testchipip.CanHavePeripheryBlockDeviceModuleImp
with testchipip.CanHavePeripherySerialModuleImp
@@ -35,4 +35,4 @@ class TopModule[+L <: Top](l: L) extends SystemModule(l)
with icenet.CanHavePeripheryIceNICModuleImp
with chipyard.example.CanHavePeripheryGCDModuleImp
with freechips.rocketchip.util.DontTouch
// DOC include end: Top
// DOC include end: DigitalTop

View File

@@ -1,19 +1,22 @@
package chipyard.iobinders
package chipyard
package object iobinders {
import chisel3._
import chisel3.experimental.{Analog, IO}
import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.devices.debug._
import freechips.rocketchip.subsystem._
import freechips.rocketchip.system._
import freechips.rocketchip.util._
import sifive.blocks.devices.gpio._
import sifive.blocks.devices.uart._
import barstools.iocell.chisel._
import testchipip._
import icenet._
import icenet.{CanHavePeripheryIceNICModuleImp, SimNetwork, NicLoopback, NICKey}
import tracegen.{HasTraceGenTilesModuleImp}
import scala.reflect.{ClassTag}
@@ -26,23 +29,32 @@ import scala.reflect.{ClassTag}
// IO connection behavior for tops matching that trait. We use strings to enable
// composition and overriding of IOBinders, much like how normal Keys in the config
// system are used/ At elaboration, the testharness traverses this set of functions,
// and functions which match the type of the Top are evaluated.
// and functions which match the type of the DigitalTop are evaluated.
// You can add your own binder by adding a new (key, fn) pair, typically by using
// the OverrideIOBinder or ComposeIOBinder macros
// DOC include start: IOBinders
case object IOBinders extends Field[Map[String, (Clock, Bool, Bool, Any) => Seq[Any]]](
Map[String, (Clock, Bool, Bool, Any) => Seq[Any]]().withDefaultValue((c: Clock, r: Bool, s: Bool, t: Any) => Nil)
// This type describes a function callable on the TestHarness instance. Its return type is unused.
type TestHarnessFunction = (chipyard.TestHarness) => Seq[Any]
// IOBinders will return a Seq of this tuple, which contains three fields:
// 1. A Seq containing all IO ports created by the IOBinder function
// 2. A Seq containing all IO cell modules created by the IOBinder function
// 3. An optional function to call inside the test harness (e.g. to connect the IOs)
type IOBinderTuple = (Seq[Data], Seq[IOCell], Option[TestHarnessFunction])
case object IOBinders extends Field[Map[String, (Any) => Seq[IOBinderTuple]]](
Map[String, (Any) => Seq[IOBinderTuple]]().withDefaultValue((Any) => Nil)
)
// This macro overrides previous matches on some Top mixin. This is useful for
// binders which drive IO, since those typically cannot be composed
class OverrideIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
class OverrideIOBinder[T](fn: => (T) => Seq[IOBinderTuple])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
case IOBinders => up(IOBinders, site) + (tag.runtimeClass.toString ->
((clock: Clock, reset: Bool, success: Bool, t: Any) => {
((t: Any) => {
t match {
case top: T => fn(clock, reset, success, top)
case system: T => fn(system)
case _ => Nil
}
})
@@ -51,12 +63,12 @@ class OverrideIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit ta
// This macro composes with previous matches on some Top mixin. This is useful for
// annotation-like binders, since those can typically be composed
class ComposeIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
class ComposeIOBinder[T](fn: => (T) => Seq[IOBinderTuple])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
case IOBinders => up(IOBinders, site) + (tag.runtimeClass.toString ->
((clock: Clock, reset: Bool, success: Bool, t: Any) => {
((t: Any) => {
t match {
case top: T => (up(IOBinders, site)(tag.runtimeClass.toString)(clock, reset, success, top)
++ fn(clock, reset, success, top))
case system: T => (up(IOBinders, site)(tag.runtimeClass.toString)(system)
++ fn(system))
case _ => Nil
}
})
@@ -65,74 +77,146 @@ class ComposeIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit tag
// DOC include end: IOBinders
object AddIOCells {
/**
* Add IO cells to a SiFive GPIO devices and name the IO ports.
* @param gpios A Seq of GPIO port bundles
* @param genFn A callable function to generate a DigitalGPIOCell module to use
* @return Returns a tuple of (a 2D Seq of Analog IOs corresponding to individual GPIO pins; a 2D Seq of IOCell module references)
*/
def gpio(gpios: Seq[GPIOPortIO], genFn: () => DigitalGPIOCell = IOCell.genericGPIO): (Seq[Seq[Analog]], Seq[Seq[IOCell]]) = {
gpios.zipWithIndex.map({ case (gpio, i) =>
gpio.pins.zipWithIndex.map({ case (pin, j) =>
val g = IO(Analog(1.W))
g.suggestName("gpio_${i}_${j}")
val iocell = genFn()
iocell.suggestName(s"iocell_gpio_${i}_${j}")
iocell.io.o := pin.o.oval
iocell.io.oe := pin.o.oe
iocell.io.ie := pin.o.ie
pin.i.ival := iocell.io.i
iocell.io.pad <> g
(g, iocell)
}).unzip
}).unzip
}
/**
* Add IO cells to a SiFive UART devices and name the IO ports.
* @param gpios A Seq of UART port bundles
* @return Returns a tuple of (A Seq of top-level UARTPortIO IOs; a 2D Seq of IOCell module references)
*/
def uart(uartPins: Seq[UARTPortIO]): (Seq[UARTPortIO], Seq[Seq[IOCell]]) = {
uartPins.zipWithIndex.map({ case (u, i) =>
val (port, ios) = IOCell.generateIOFromSignal(u, Some(s"iocell_uart_${i}"))
port.suggestName(s"uart_${i}")
(port, ios)
}).unzip
}
/**
* Add IO cells to a debug module and name the IO ports.
* @param gpios A PSDIO 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)
*/
def debug(psd: PSDIO, debugOpt: Option[DebugIO]): (PSDIO, Option[DebugIO], Seq[IOCell]) = {
val (psdPort, psdIOs) = IOCell.generateIOFromSignal(psd, Some("iocell_psd"))
val optTuple = debugOpt.map(d => IOCell.generateIOFromSignal(d, Some("iocell_debug")))
val debugPortOpt: Option[DebugIO] = optTuple.map(_._1)
val debugIOs: Seq[IOCell] = optTuple.map(_._2).toSeq.flatten
debugPortOpt.foreach(_.suggestName("debug"))
psdPort.suggestName("psd")
(psdPort, debugPortOpt, psdIOs ++ debugIOs)
}
/**
* Add IO cells to a serial module and name the IO ports.
* @param serial A SerialIO bundle
* @return Returns a tuple of (Top-level SerialIO IO; a list of IOCell module references)
*/
def serial(serial: SerialIO): (SerialIO, Seq[IOCell]) = {
val (port, ios) = IOCell.generateIOFromSignal(serial, Some("iocell_serial"))
port.suggestName("serial")
(port, ios)
}
}
// DOC include start: WithGPIOTiedOff
class WithGPIOTiedOff extends OverrideIOBinder({
(c, r, s, top: HasPeripheryGPIOModuleImp) => top.gpio.map(gpio => gpio.pins.map(p => p.i.ival := false.B)); Nil
(system: HasPeripheryGPIOModuleImp) => {
val (ports2d, ioCells2d) = AddIOCells.gpio(system.gpio)
val harnessFn = (th: chipyard.TestHarness) => { ports2d.flatten.foreach(_ <> AnalogConst(0)); Nil }
Seq((ports2d.flatten, ioCells2d.flatten, Some(harnessFn)))
}
})
// DOC include end: WithGPIOTiedOff
class WithUARTAdapter extends OverrideIOBinder({
(system: HasPeripheryUARTModuleImp) => {
val (ports, ioCells2d) = AddIOCells.uart(system.uart)
val harnessFn = (th: chipyard.TestHarness) => { UARTAdapter.connect(ports)(system.p); Nil }
Seq((ports, ioCells2d.flatten, Some(harnessFn)))
}
})
class WithSimBlockDevice extends OverrideIOBinder({
(c, r, s, top: CanHavePeripheryBlockDeviceModuleImp) => top.connectSimBlockDevice(c, r); Nil
(system: CanHavePeripheryBlockDeviceModuleImp) => system.connectSimBlockDevice(system.clock, system.reset.asBool); Nil
})
class WithBlockDeviceModel extends OverrideIOBinder({
(c, r, s, top: CanHavePeripheryBlockDeviceModuleImp) => top.connectBlockDeviceModel(); Nil
(system: CanHavePeripheryBlockDeviceModuleImp) => system.connectBlockDeviceModel(); Nil
})
class WithLoopbackNIC extends OverrideIOBinder({
(c, r, s, top: CanHavePeripheryIceNICModuleImp) => top.connectNicLoopback(); Nil
(system: CanHavePeripheryIceNICModuleImp) => system.connectNicLoopback(); Nil
})
class WithSimNIC extends OverrideIOBinder({
(c, r, s, top: CanHavePeripheryIceNICModuleImp) => top.connectSimNetwork(c, r); Nil
})
class WithUARTAdapter extends OverrideIOBinder({
(c, r, s, top: HasPeripheryUARTModuleImp) => {
val defaultBaudRate = 115200 // matches sifive-blocks uart baudrate
top.uart.zipWithIndex.foreach{ case (dut_io, i) =>
val uart_sim = Module(new UARTAdapter(i, defaultBaudRate)(top.p))
uart_sim.io.uart.txd := dut_io.txd
dut_io.rxd := uart_sim.io.uart.rxd
}
Nil
}
(system: CanHavePeripheryIceNICModuleImp) => system.connectSimNetwork(system.clock, system.reset.asBool); Nil
})
// DOC include start: WithSimAXIMem
class WithSimAXIMem extends OverrideIOBinder({
(c, r, s, top: CanHaveMasterAXI4MemPortModuleImp) => top.connectSimAXIMem(); Nil
(system: CanHaveMasterAXI4MemPortModuleImp) => system.connectSimAXIMem(); Nil
})
// DOC include end: WithSimAXIMem
class WithBlackBoxSimMem extends OverrideIOBinder({
(clock, reset, _, top: CanHaveMasterAXI4MemPortModuleImp) => {
(top.mem_axi4 zip top.outer.memAXI4Node).foreach { case (io, node) =>
val memSize = top.p(ExtMem).get.master.size
val lineSize = top.p(CacheBlockBytes)
(system: CanHaveMasterAXI4MemPortModuleImp) => {
(system.mem_axi4 zip system.outer.memAXI4Node).foreach { case (io, node) =>
val memSize = system.p(ExtMem).get.master.size
val lineSize = system.p(CacheBlockBytes)
(io zip node.in).foreach { case (axi4, (_, edge)) =>
val mem = Module(new SimDRAM(memSize, lineSize, edge.bundle))
mem.io.axi <> axi4
mem.io.clock := clock
mem.io.reset := reset
mem.io.clock := system.clock
mem.io.reset := system.reset
}
}; Nil
}
})
class WithSimAXIMMIO extends OverrideIOBinder({
(c, r, s, top: CanHaveMasterAXI4MMIOPortModuleImp) => top.connectSimAXIMMIO(); Nil
(system: CanHaveMasterAXI4MMIOPortModuleImp) => system.connectSimAXIMMIO(); Nil
})
class WithDontTouchPorts extends OverrideIOBinder({
(c, r, s, top: DontTouch) => top.dontTouchPorts(); Nil
(system: DontTouch) => system.dontTouchPorts(); Nil
})
class WithTieOffInterrupts extends OverrideIOBinder({
(c, r, s, top: HasExtInterruptsBundle) => top.tieOffInterrupts(); Nil
(system: HasExtInterruptsModuleImp) => {
val (port, ioCells) = IOCell.generateIOFromSignal(system.interrupts, Some("iocell_interrupts"))
port.suggestName("interrupts")
val harnessFn = (th: chipyard.TestHarness) => { port := 0.U; Nil }
Seq((Seq(port), ioCells, Some(harnessFn)))
}
})
class WithTieOffL2FBusAXI extends OverrideIOBinder({
(c, r, s, top: CanHaveSlaveAXI4PortModuleImp) => {
top.l2_frontend_bus_axi4.foreach(axi => {
(system: CanHaveSlaveAXI4PortModuleImp) => {
system.l2_frontend_bus_axi4.foreach(axi => {
axi.tieoff()
experimental.DataMirror.directionOf(axi.ar.ready) match {
case ActualDirection.Input =>
@@ -142,6 +226,7 @@ class WithTieOffL2FBusAXI extends OverrideIOBinder({
axi.aw.bits := DontCare
axi.ar.bits := DontCare
axi.w.bits := DontCare
case _ => throw new Exception("Unknown AXI port direction")
}
})
Nil
@@ -149,38 +234,62 @@ class WithTieOffL2FBusAXI extends OverrideIOBinder({
})
class WithTiedOffDebug extends OverrideIOBinder({
(c, r, s, top: HasPeripheryDebugModuleImp) => {
Debug.tieoffDebug(top.debug, top.psd)
// tieoffDebug doesn't actually tie everything off :/
top.debug.foreach(_.clockeddmi.foreach({ cdmi => cdmi.dmi.req.bits := DontCare }))
Nil
(system: HasPeripheryDebugModuleImp) => {
val (psdPort, debugPortOpt, ioCells) = AddIOCells.debug(system.psd, system.debug)
val harnessFn = (th: chipyard.TestHarness) => {
Debug.tieoffDebug(debugPortOpt, psdPort)
// tieoffDebug doesn't actually tie everything off :/
debugPortOpt.foreach(_.clockeddmi.foreach({ cdmi => cdmi.dmi.req.bits := DontCare }))
Nil
}
Seq((Seq(psdPort) ++ debugPortOpt.toSeq, ioCells, Some(harnessFn)))
}
})
class WithSimSerial extends OverrideIOBinder({
(c, r, s, top: CanHavePeripherySerialModuleImp) => {
val ser_success = top.connectSimSerial()
when (ser_success) { s := true.B }
Nil
class WithSimDebug extends OverrideIOBinder({
(system: HasPeripheryDebugModuleImp) => {
val (psdPort, debugPortOpt, ioCells) = AddIOCells.debug(system.psd, system.debug)
val harnessFn = (th: chipyard.TestHarness) => {
val dtm_success = Wire(Bool())
Debug.connectDebug(debugPortOpt, psdPort, th.clock, th.harnessReset, dtm_success)(system.p)
when (dtm_success) { th.success := true.B }
th.dutReset := th.harnessReset | debugPortOpt.map { debug => AsyncResetReg(debug.ndreset).asBool }.getOrElse(false.B)
Nil
}
Seq((Seq(psdPort) ++ debugPortOpt.toSeq, ioCells, Some(harnessFn)))
}
})
class WithTiedOffSerial extends OverrideIOBinder({
(c, r, s, top: CanHavePeripherySerialModuleImp) => top.tieoffSerial(); Nil
(system: CanHavePeripherySerialModuleImp) => system.serial.map({ serial =>
val (port, ioCells) = AddIOCells.serial(serial)
val harnessFn = (th: chipyard.TestHarness) => {
SerialAdapter.tieoff(port)
Nil
}
Seq((Seq(port), ioCells, Some(harnessFn)))
}).getOrElse(Nil)
})
class WithSimSerial extends OverrideIOBinder({
(system: CanHavePeripherySerialModuleImp) => system.serial.map({ serial =>
val (port, ioCells) = AddIOCells.serial(serial)
val harnessFn = (th: chipyard.TestHarness) => {
val ser_success = SerialAdapter.connectSimSerial(port, th.clock, th.harnessReset)
when (ser_success) { th.success := true.B }
Nil
}
Seq((Seq(port), ioCells, Some(harnessFn)))
}).getOrElse(Nil)
})
class WithSimDebug extends OverrideIOBinder({
(c, r, s, top: HasPeripheryDebugModuleImp) => {
val dtm_success = Wire(Bool())
top.reset := r | top.debug.map { debug => AsyncResetReg(debug.ndreset) }.getOrElse(false.B)
Debug.connectDebug(top.debug, top.psd, c, r, dtm_success)(top.p)
when (dtm_success) { s := true.B }
Nil
class WithTraceGenSuccessBinder extends OverrideIOBinder({
(system: HasTraceGenTilesModuleImp) => {
val (successPort, ioCells) = IOCell.generateIOFromSignal(system.success, Some("iocell_success"))
successPort.suggestName("success")
val harnessFn = (th: chipyard.TestHarness) => { when (successPort) { th.success := true.B }; Nil }
Seq((Seq(successPort), ioCells, Some(harnessFn)))
}
})
class WithTraceGenSuccessBinder extends OverrideIOBinder({
(c, r, s, top: HasTraceGenTilesModuleImp) => when (top.success) { s := true.B }; Nil
})
}

View File

@@ -1,23 +1,21 @@
package chipyard
import chisel3._
import chisel3.experimental._
import firrtl.transforms.{BlackBoxResourceAnno, BlackBoxSourceHelper}
import freechips.rocketchip.diplomacy.LazyModule
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.config.{Field, Parameters}
import freechips.rocketchip.util.GeneratorApp
import freechips.rocketchip.devices.debug.{Debug}
import chipyard.iobinders.{TestHarnessFunction}
import chipyard.config.ConfigValName._
import chipyard.iobinders.{IOBinders}
// -------------------------------
// BOOM and/or Rocket Test Harness
// -------------------------------
case object BuildTop extends Field[Parameters => Any]((p: Parameters) => Module(LazyModule(new Top()(p)).suggestName("top").module))
case object BuildTop extends Field[Parameters => HasTestHarnessFunctions]((p: Parameters) => Module(new ChipTop()(p)))
trait HasTestHarnessFunctions {
val harnessFunctions: Seq[TestHarnessFunction]
}
class TestHarness(implicit val p: Parameters) extends Module {
val io = IO(new Bundle {
@@ -26,5 +24,15 @@ class TestHarness(implicit val p: Parameters) extends Module {
val dut = p(BuildTop)(p)
io.success := false.B
p(IOBinders).values.map(fn => fn(clock, reset.asBool, io.success, dut))
// dutReset can be overridden via a harnessFunction, but by default it is just reset
val dutReset = Wire(Bool())
dutReset := reset
dut.harnessFunctions.foreach(_(this))
def success = io.success
def harnessReset = this.reset.asBool
}

View File

@@ -6,21 +6,21 @@ import freechips.rocketchip.rocket.{DCacheParams}
class TraceGenConfig extends Config(
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTraceGenSuccessBinder ++
new chipyard.config.WithTracegenTop ++
new chipyard.config.WithTracegenSystem ++
new tracegen.WithTraceGen(List.fill(2) { DCacheParams(nMSHRs = 0, nSets = 16, nWays = 2) }) ++
new freechips.rocketchip.system.BaseConfig)
class NonBlockingTraceGenConfig extends Config(
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTraceGenSuccessBinder ++
new chipyard.config.WithTracegenTop ++
new chipyard.config.WithTracegenSystem ++
new tracegen.WithTraceGen(List.fill(2) { DCacheParams(nMSHRs = 2, nSets = 16, nWays = 2) }) ++
new freechips.rocketchip.system.BaseConfig)
class BoomTraceGenConfig extends Config(
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTraceGenSuccessBinder ++
new chipyard.config.WithTracegenTop ++
new chipyard.config.WithTracegenSystem ++
new tracegen.WithBoomTraceGen(List.fill(2) { DCacheParams(nMSHRs = 8, nSets = 16, nWays = 2) }) ++
new freechips.rocketchip.subsystem.WithInclusiveCache ++
new freechips.rocketchip.system.BaseConfig)
@@ -28,7 +28,7 @@ class BoomTraceGenConfig extends Config(
class NonBlockingTraceGenL2Config extends Config(
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTraceGenSuccessBinder ++
new chipyard.config.WithTracegenTop ++
new chipyard.config.WithTracegenSystem ++
new tracegen.WithL2TraceGen(List.fill(2)(DCacheParams(nMSHRs = 2, nSets = 16, nWays = 4))) ++
new freechips.rocketchip.subsystem.WithInclusiveCache ++
new freechips.rocketchip.system.BaseConfig)
@@ -36,7 +36,7 @@ class NonBlockingTraceGenL2Config extends Config(
class NonBlockingTraceGenL2RingConfig extends Config(
new chipyard.iobinders.WithBlackBoxSimMem ++
new chipyard.iobinders.WithTraceGenSuccessBinder ++
new chipyard.config.WithTracegenTop ++
new chipyard.config.WithTracegenSystem ++
new tracegen.WithL2TraceGen(List.fill(2)(DCacheParams(nMSHRs = 2, nSets = 16, nWays = 4))) ++
new testchipip.WithRingSystemBus ++
new freechips.rocketchip.subsystem.WithInclusiveCache ++

View File

@@ -7,10 +7,11 @@ import chisel3.experimental.annotate
import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.devices.debug.HasPeripheryDebugModuleImp
import freechips.rocketchip.subsystem.{CanHaveMasterAXI4MemPortModuleImp}
import freechips.rocketchip.devices.debug.{Debug, HasPeripheryDebugModuleImp}
import freechips.rocketchip.subsystem.{CanHaveMasterAXI4MemPortModuleImp, HasExtInterruptsModuleImp}
import freechips.rocketchip.tile.{RocketTile}
import sifive.blocks.devices.uart.HasPeripheryUARTModuleImp
import sifive.blocks.devices.gpio.{HasPeripheryGPIOModuleImp}
import testchipip.{CanHavePeripherySerialModuleImp, CanHavePeripheryBlockDeviceModuleImp, CanHaveTraceIOModuleImp}
import icenet.CanHavePeripheryIceNICModuleImp
@@ -29,56 +30,55 @@ import chipyard.iobinders.{IOBinders, OverrideIOBinder, ComposeIOBinder}
import chipyard.HasChipyardTilesModuleImp
class WithSerialBridge extends OverrideIOBinder({
(c, r, s, target: CanHavePeripherySerialModuleImp) =>
target.serial.map(s => SerialBridge(target.clock, s)(target.p)).toSeq
(system: CanHavePeripherySerialModuleImp) =>
system.serial.foreach(s => SerialBridge(system.clock, s)(system.p)); Nil
})
class WithNICBridge extends OverrideIOBinder({
(c, r, s, target: CanHavePeripheryIceNICModuleImp) =>
target.net.map(n => NICBridge(target.clock, n)(target.p)).toSeq
(system: CanHavePeripheryIceNICModuleImp) =>
system.net.foreach(n => NICBridge(system.clock, n)(system.p)); Nil
})
class WithUARTBridge extends OverrideIOBinder({
(c, r, s, target: HasPeripheryUARTModuleImp) =>
target.uart.map(u => UARTBridge(target.clock, u)(target.p)).toSeq
(system: HasPeripheryUARTModuleImp) =>
system.uart.foreach(u => UARTBridge(system.clock, u)(system.p)); Nil
})
class WithBlockDeviceBridge extends OverrideIOBinder({
(c, r, s, target: CanHavePeripheryBlockDeviceModuleImp) =>
target.bdev.map(b => BlockDevBridge(target.clock, b, target.reset.toBool)(target.p)).toSeq
(system: CanHavePeripheryBlockDeviceModuleImp) =>
system.bdev.foreach(b => BlockDevBridge(system.clock, b, system.reset.toBool)(system.p)); Nil
})
class WithFASEDBridge extends OverrideIOBinder({
(c, r, s, t: CanHaveMasterAXI4MemPortModuleImp) => {
implicit val p = t.p
(t.mem_axi4 zip t.outer.memAXI4Node).flatMap({ case (io, node) =>
(system: CanHaveMasterAXI4MemPortModuleImp) => {
implicit val p = system.p
(system.mem_axi4 zip system.outer.memAXI4Node).flatMap({ case (io, node) =>
(io zip node.in).map({ case (axi4Bundle, (_, edge)) =>
val nastiKey = NastiParameters(axi4Bundle.r.bits.data.getWidth,
axi4Bundle.ar.bits.addr.getWidth,
axi4Bundle.ar.bits.id.getWidth)
FASEDBridge(t.clock, axi4Bundle, t.reset.toBool,
FASEDBridge(system.clock, axi4Bundle, system.reset.toBool,
CompleteConfig(p(firesim.configs.MemModelKey), nastiKey, Some(AXI4EdgeSummary(edge))))
})
}).toSeq
})
Nil
}
})
class WithTracerVBridge extends OverrideIOBinder({
(c, r, s, target: CanHaveTraceIOModuleImp) => target.traceIO match {
case Some(t) => t.traces.map(tileTrace => TracerVBridge(tileTrace)(target.p))
case None => Nil
}
(system: CanHaveTraceIOModuleImp) =>
system.traceIO.foreach(_.traces.map(tileTrace => TracerVBridge(tileTrace)(system.p))); Nil
})
class WithTraceGenBridge extends OverrideIOBinder({
(c, r, s, target: HasTraceGenTilesModuleImp) =>
Seq(GroundTestBridge(target.clock, target.success)(target.p))
(system: HasTraceGenTilesModuleImp) =>
GroundTestBridge(system.clock, system.success)(system.p); Nil
})
class WithFireSimMultiCycleRegfile extends ComposeIOBinder({
(c, r, s, target: HasChipyardTilesModuleImp) => {
target.outer.tiles.map {
(system: HasChipyardTilesModuleImp) => {
system.outer.tiles.map {
case r: RocketTile => {
annotate(MemModelAnnotation(r.module.core.rocketImpl.rf.rf))
r.module.fpuOpt.foreach(fpu => annotate(MemModelAnnotation(fpu.fpuImpl.regfile)))
@@ -100,13 +100,31 @@ class WithFireSimMultiCycleRegfile extends ComposeIOBinder({
}
})
class WithTiedOffSystemGPIO extends OverrideIOBinder({
(system: HasPeripheryGPIOModuleImp) =>
system.gpio.foreach(_.pins.foreach(_.i.ival := false.B)); Nil
})
class WithTiedOffSystemDebug extends OverrideIOBinder({
(system: HasPeripheryDebugModuleImp) => {
Debug.tieoffDebug(system.debug, system.psd)
// tieoffDebug doesn't actually tie everything off :/
system.debug.foreach(_.clockeddmi.foreach({ cdmi => cdmi.dmi.req.bits := DontCare }))
Nil
}
})
class WithTiedOffSystemInterrupts extends OverrideIOBinder({
(system: HasExtInterruptsModuleImp) =>
system.interrupts := 0.U; Nil
})
// Shorthand to register all of the provided bridges above
class WithDefaultFireSimBridges extends Config(
new chipyard.iobinders.WithGPIOTiedOff ++
new chipyard.iobinders.WithTiedOffDebug ++
new chipyard.iobinders.WithTieOffInterrupts ++
new WithTiedOffSystemGPIO ++
new WithTiedOffSystemDebug ++
new WithTiedOffSystemInterrupts ++
new WithSerialBridge ++
new WithNICBridge ++
new WithUARTBridge ++

View File

@@ -9,7 +9,7 @@ import freechips.rocketchip.diplomacy.{LazyModule}
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge}
import chipyard.{BuildTop}
import chipyard.{BuildSystem}
import chipyard.iobinders.{IOBinders}
// Determines the number of times to instantiate the DUT in the harness.
@@ -26,14 +26,14 @@ class FireSim(implicit val p: Parameters) extends RawModule {
val reset = WireInit(false.B)
withClockAndReset(clock, reset) {
// Instantiate multiple instances of the DUT to implement supernode
val targets = Seq.fill(p(NumNodes))(p(BuildTop)(p))
val targets = Seq.fill(p(NumNodes))(p(BuildSystem)(p))
val peekPokeBridge = PeekPokeBridge(clock, reset)
// A Seq of partial functions that will instantiate the right bridge only
// if that Mixin trait is present in the target's class instance
//
// Apply each partial function to each DUT instance
for ((target) <- targets) {
p(IOBinders).values.map(fn => fn(clock, reset.asBool, false.B, target))
p(IOBinders).values.map(_(target))
}
}
}

View File

@@ -14,7 +14,7 @@ import boom.common.{BoomTilesKey, BoomCrossingKey}
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock}
import firesim.configs._
import chipyard.{BuildTop, Top, TopModule}
import chipyard.{BuildSystem, DigitalTop, DigitalTopModule}
import chipyard.config.ConfigValName._
import chipyard.iobinders.{IOBinders}
@@ -64,7 +64,7 @@ class WithSingleRationalTileDomain(multiplier: Int, divisor: Int) extends Config
class HalfRateUncore extends WithSingleRationalTileDomain(2,1)
class WithFiresimMulticlockTop extends Config((site, here, up) => {
case BuildTop => (p: Parameters) => Module(LazyModule(new FiresimMulticlockTop()(p)).suggestName("Top").module)
case BuildSystem => (p: Parameters) => Module(LazyModule(new FiresimMulticlockTop()(p)).suggestName("system").module)
})
// Complete Config
@@ -74,12 +74,12 @@ class FireSimQuadRocketMulticlockConfig extends Config(
new FireSimQuadRocketConfig)
// Top Definition
class FiresimMulticlockTop(implicit p: Parameters) extends chipyard.Top
class FiresimMulticlockTop(implicit p: Parameters) extends chipyard.DigitalTop
{
override lazy val module = new FiresimMulticlockTopModule(this)
}
class FiresimMulticlockTopModule[+L <: Top](l: L) extends chipyard.TopModule(l) with HasFireSimClockingImp
class FiresimMulticlockTopModule[+L <: DigitalTop](l: L) extends chipyard.DigitalTopModule(l) with HasFireSimClockingImp
// Harness Definition
class FireSimMulticlockPOC(implicit val p: Parameters) extends RawModule {
@@ -88,14 +88,14 @@ class FireSimMulticlockPOC(implicit val p: Parameters) extends RawModule {
val reset = WireInit(false.B)
withClockAndReset(refClock, reset) {
// Instantiate multiple instances of the DUT to implement supernode
val targets = Seq.fill(p(NumNodes))(p(BuildTop)(p))
val targets = Seq.fill(p(NumNodes))(p(BuildSystem)(p))
val peekPokeBridge = PeekPokeBridge(refClock, reset)
// A Seq of partial functions that will instantiate the right bridge only
// if that Mixin trait is present in the target's class instance
//
// Apply each partial function to each DUT instance
for ((target) <- targets) {
p(IOBinders).values.map(fn => fn(refClock, reset.asBool, false.B, target))
p(IOBinders).values.map(_(target))
}
targets.collect({ case t: HasAdditionalClocks => t.clocks := clockBridge.io.clocks })
}

View File

@@ -24,7 +24,6 @@ import testchipip.WithRingSystemBus
import firesim.bridges._
import firesim.configs._
import chipyard.{BuildTop}
import chipyard.config.ConfigValName._
class WithBootROM extends Config((site, here, up) => {