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:
@@ -122,8 +122,12 @@ lazy val testchipip = (project in file("generators/testchipip"))
|
||||
.dependsOn(rocketchip, sifive_blocks)
|
||||
.settings(commonSettings)
|
||||
|
||||
lazy val iocell = (project in file("./tools/barstools/iocell/"))
|
||||
.dependsOn(chisel)
|
||||
.settings(commonSettings)
|
||||
|
||||
lazy val chipyard = conditionalDependsOn(project in file("generators/chipyard"))
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities,
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities, iocell,
|
||||
sha3, // On separate line to allow for cleaner tutorial-setup patches
|
||||
gemmini, icenet, tracegen, ariane)
|
||||
.settings(commonSettings)
|
||||
|
||||
@@ -20,7 +20,7 @@ include $(base_dir)/generators/tracegen/tracegen.mk
|
||||
#########################################################################################
|
||||
lookup_srcs = $(shell find -L $(1)/ -name target -prune -o -iname "*.$(2)" -print 2> /dev/null)
|
||||
|
||||
SOURCE_DIRS = $(addprefix $(base_dir)/,generators sims/firesim/sim)
|
||||
SOURCE_DIRS = $(addprefix $(base_dir)/,generators sims/firesim/sim tools/barstools/iocell)
|
||||
SCALA_SOURCES = $(call lookup_srcs,$(SOURCE_DIRS),scala)
|
||||
VLOG_SOURCES = $(call lookup_srcs,$(SOURCE_DIRS),sv) $(call lookup_srcs,$(SOURCE_DIRS),v)
|
||||
|
||||
|
||||
@@ -2,17 +2,28 @@ Tops, Test-Harnesses, and the Test-Driver
|
||||
===========================================
|
||||
|
||||
The three highest levels of hierarchy in a Chipyard
|
||||
SoC are the Top (DUT), ``TestHarness``, and the ``TestDriver``.
|
||||
The Top and ``TestHarness`` are both emitted by Chisel generators.
|
||||
SoC are the ``ChipTop`` (DUT), ``TestHarness``, and the ``TestDriver``.
|
||||
The ``ChipTop`` and ``TestHarness`` are both emitted by Chisel generators.
|
||||
The ``TestDriver`` serves as our testbench, and is a Verilog
|
||||
file in Rocket Chip.
|
||||
|
||||
|
||||
Top/DUT
|
||||
ChipTop/DUT
|
||||
-------------------------
|
||||
|
||||
The top-level module of a Rocket Chip SoC is composed via cake-pattern.
|
||||
Specifically, "Tops" extend a ``System``, which extends a ``Subsystem``, which extends a ``BaseSubsystem``.
|
||||
``ChipTop`` is the top-level module that instantiates the ``System`` submodule, usually an instance of the concrete class ``DigitalTop``.
|
||||
The vast majority of the design resides in the ``System``.
|
||||
Other components that exist inside the ``ChipTop`` layer are generally IO cells, clock receivers and multiplexers, reset synchronizers, and other analog IP that needs to exist outside of the ``System``.
|
||||
The ``IOBinders`` are responsible for instantiating the IO cells and defining the test harness collateral that connects to the top-level ports.
|
||||
Most of these types of devices can be instantiated using custom ``IOBinders``, so the provided ``ChipTop`` and ``ChipTopCaughtReset`` classes are sufficient.
|
||||
However, if needed, the ``BaseChipTop`` abstract class can be extended for building more custom ``ChipTop`` designs.
|
||||
|
||||
|
||||
System/DigitalTop
|
||||
-------------------------
|
||||
|
||||
The system module of a Rocket Chip SoC is composed via cake-pattern.
|
||||
Specifically, ``DigitalTop`` extends a ``System``, which extends a ``Subsystem``, which extends a ``BaseSubsystem``.
|
||||
|
||||
|
||||
BaseSubsystem
|
||||
|
||||
@@ -77,10 +77,10 @@ It is used in the Rocket Chip SoC library and Chipyard framework in merging mult
|
||||
This example shows the Chipyard default top that composes multiple traits together into a fully-featured SoC with many optional components.
|
||||
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: Top
|
||||
:end-before: DOC include end: Top
|
||||
:start-after: DOC include start: DigitalTop
|
||||
:end-before: DOC include end: DigitalTop
|
||||
|
||||
|
||||
There are two "cakes" or mixins here. One for the lazy module (ex. ``CanHavePeripherySerial``) and one for the lazy module
|
||||
@@ -88,8 +88,8 @@ implementation (ex. ``CanHavePeripherySerialModuleImp`` where ``Imp`` refers to
|
||||
all the logical connections between generators and exchanges configuration information among them, while the
|
||||
lazy module implementation performs the actual Chisel RTL elaboration.
|
||||
|
||||
In the ``Top`` example class, the "outer" ``Top`` instantiates the "inner"
|
||||
``TopModule`` as a lazy module implementation. This delays immediate elaboration
|
||||
In the ``DigitalTop`` example class, the "outer" ``DigitalTop`` instantiates the "inner"
|
||||
``DigitalTopModule`` as a lazy module implementation. This delays immediate elaboration
|
||||
of the module until all logical connections are determined and all configuration information is exchanged.
|
||||
The ``System`` outer base class, as well as the
|
||||
``CanHavePeriphery<X>`` outer traits contain code to perform high-level logical
|
||||
@@ -102,8 +102,9 @@ For example, the ``CanHavePeripherySerialModuleImp`` trait optionally physically
|
||||
the ``SerialAdapter`` module, and instantiates queues.
|
||||
|
||||
In the test harness, the SoC is elaborated with
|
||||
``val dut = Module(LazyModule(Top))``.
|
||||
After elaboration, the result will be a ``Top`` module, which contains a
|
||||
``val dut = p(BuildTop)(p)``.
|
||||
|
||||
After elaboration, the system submodule of ``ChipTop`` will be a ``DigitalTop`` module, which contains a
|
||||
``SerialAdapter`` module (among others), if the config specified for that block to be instantiated.
|
||||
|
||||
From a high level, classes which extend ``LazyModule`` *must* reference
|
||||
|
||||
@@ -15,10 +15,10 @@ that writes zeros to the memory at a configured address.
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/example/InitZero.scala
|
||||
:language: scala
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: Top
|
||||
:end-before: DOC include end: Top
|
||||
:start-after: DOC include start: DigitalTop
|
||||
:end-before: DOC include end: DigitalTop
|
||||
|
||||
We use ``TLHelper.makeClientNode`` to create a TileLink client node for us.
|
||||
We then connect the client node to the memory system through the front bus (fbus).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
IOBinders
|
||||
=========
|
||||
|
||||
In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to determine what modules to bind to the IOs of a ``Top`` in the ``TestHarness``.
|
||||
In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to instantiate IO cells in the ``ChipTop`` layer and determine what modules to bind to the IOs of a ``ChipTop`` in the ``TestHarness``.
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
|
||||
:language: scala
|
||||
@@ -9,11 +9,27 @@ In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to determine what
|
||||
:end-before: DOC include end: IOBinders
|
||||
|
||||
|
||||
This special key solves the problem of duplicating test-harnesses for each different ``Top`` type.
|
||||
This special key solves the problem of duplicating test-harnesses for each different ``System`` type.
|
||||
You could just as well create a custom harness module that attaches IOs explicitly.
|
||||
Instead, the ``IOBinders`` key provides a map from Scala traits to attachment behaviors.
|
||||
Each ``IOBinder`` returns a tuple of three values: the list of ``ChipTop`` ports created by the ``IOBinder``, the list of all IO cell modules instantiated by the ``IOBinder``, and an optional function to be called inside the test harness.
|
||||
This function is responsible for instantiating logic inside the ``TestHarness`` to appropriately drive the ``ChipTop`` IO ports created by the ``IOBinder``.
|
||||
Conveniently, because the ``IOBinder`` is generating the port, it may also use the port inside this function, which prevents the ``BaseChipTop`` code from ever needing to access the port ``val``, thus having the ``IOBinder`` house all port specific code.
|
||||
This scheme prevents the need to have two separate binder functions for each ``System`` trait.
|
||||
When creating custom ``IOBinders`` it is important to use ``suggestName`` to name ports; otherwise Chisel will raise an exception trying to name the IOs.
|
||||
The example ``IOBinders`` demonstrate this.
|
||||
|
||||
You could just as well create a custom harness module that attaches IOs explicitly. Instead, the IOBinders key provides a map from Scala traits to attachment behaviors.
|
||||
As an example, the ``WithGPIOTiedOff`` IOBinder creates IO cells for the GPIO module(s) instantiated in the ``System``, then punches out new ``Analog`` ports for each one.
|
||||
The test harness simply ties these off, but additional logic could be inserted to perform some kind of test in the ``TestHarness``.
|
||||
|
||||
For example, the ``WithSimAXIMemTiedOff`` IOBinder specifies that any ``Top`` which matches ``CanHaveMasterAXI4MemPortModuleImp`` will have a ``SimAXIMem`` connected.
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: WithGPIOTiedOff
|
||||
:end-before: DOC include end: WithGPIOTiedOff
|
||||
|
||||
|
||||
``IOBinders`` also do not need to create ports. Some ``IOBinders`` can simply insert circuitry inside the ``ChipTop`` layer.
|
||||
For example, the ``WithSimAXIMemTiedOff`` IOBinder specifies that any ``System`` which matches ``CanHaveMasterAXI4MemPortModuleImp`` will have a ``SimAXIMem`` connected inside ``ChipTop``.
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
|
||||
:language: scala
|
||||
|
||||
@@ -34,25 +34,25 @@ Accessing the value stored in the key is easy in Chisel, as long as the ``implic
|
||||
Traits
|
||||
------
|
||||
|
||||
Typically, most custom blocks will need to modify the behavior of some pre-existing block. For example, the GCD widget needs the ``Top`` module to instantiate and connect the widget via Tilelink, generate a top-level ``gcd_busy`` port, and connect that to the module as well. Traits let us do this without modifying the existing code for the ``Top``, and enables compartmentalization of code for different custom blocks.
|
||||
Typically, most custom blocks will need to modify the behavior of some pre-existing block. For example, the GCD widget needs the ``DigitalTop`` module to instantiate and connect the widget via Tilelink, generate a top-level ``gcd_busy`` port, and connect that to the module as well. Traits let us do this without modifying the existing code for the ``DigitalTop``, and enables compartmentalization of code for different custom blocks.
|
||||
|
||||
Top-level traits specify that the ``Top`` has been parameterized to read some custom key and optionally instantiate and connect a widget defined by that key. Traits **should not** mandate the instantiation of custom logic. In other words, traits should be written with ``CanHave`` semantics, where the default behavior when the key is unset is a no-op.
|
||||
Top-level traits specify that the ``DigitalTop`` has been parameterized to read some custom key and optionally instantiate and connect a widget defined by that key. Traits **should not** mandate the instantiation of custom logic. In other words, traits should be written with ``CanHave`` semantics, where the default behavior when the key is unset is a no-op.
|
||||
|
||||
Top-level traits should be defined and documented in subprojects, alongside their corresponding keys. The traits should then be added to the ``Top`` being used by Chipyard.
|
||||
Top-level traits should be defined and documented in subprojects, alongside their corresponding keys. The traits should then be added to the ``DigitalTop`` being used by Chipyard.
|
||||
|
||||
Below we see the traits for the GCD example. The Lazy trait connects the GCD module to the Diplomacy graph, while the Implementation trait causes the ``Top`` to instantiate an additional port and concretely connect it to the GCD module.
|
||||
Below we see the traits for the GCD example. The Lazy trait connects the GCD module to the Diplomacy graph, while the Implementation trait causes the ``DigitalTop`` to instantiate an additional port and concretely connect it to the GCD module.
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: GCD lazy trait
|
||||
:end-before: DOC include end: GCD imp trait
|
||||
|
||||
These traits are added to the default ``Top`` in Chipyard.
|
||||
These traits are added to the default ``DigitalTop`` in Chipyard.
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: Top
|
||||
:end-before: DOC include end: Top
|
||||
:start-after: DOC include start: DigitalTop
|
||||
:end-before: DOC include end: DigitalTop
|
||||
|
||||
Config Fragments
|
||||
----------------
|
||||
|
||||
@@ -87,21 +87,21 @@ For peripherals which instantiate a concrete module, or which need to be connect
|
||||
:start-after: DOC include start: GCD imp trait
|
||||
:end-before: DOC include end: GCD imp trait
|
||||
|
||||
Constructing the Top and Config
|
||||
-------------------------------
|
||||
Constructing the DigitalTop and Config
|
||||
--------------------------------------
|
||||
|
||||
Now we want to mix our traits into the system as a whole.
|
||||
This code is from ``generators/chipyard/src/main/scala/Top.scala``.
|
||||
This code is from ``generators/chipyard/src/main/scala/DigitalTop.scala``.
|
||||
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
|
||||
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
|
||||
:language: scala
|
||||
:start-after: DOC include start: Top
|
||||
:end-before: DOC include end: Top
|
||||
:start-after: DOC include start: DigitalTop
|
||||
:end-before: DOC include end: DigitalTop
|
||||
|
||||
Just as we need separate traits for ``LazyModule`` and module implementation, we need two classes to build the system.
|
||||
The ``Top`` class contains the set of traits which parameterize and define the ``Top``. Typically these traits will optionally add IOs or peripherals to the ``Top``.
|
||||
The ``Top`` class includes the pre-elaboration code and also a ``lazy val`` to produce the module implementation (hence ``LazyModule``).
|
||||
The ``TopModule`` class is the actual RTL that gets synthesized.
|
||||
The ``DigitalTop`` class contains the set of traits which parameterize and define the ``DigitalTop``. Typically these traits will optionally add IOs or peripherals to the ``DigitalTop``.
|
||||
The ``DigitalTop`` class includes the pre-elaboration code and also a ``lazy val`` to produce the module implementation (hence ``LazyModule``).
|
||||
The ``DigitalTopModule`` class is the actual RTL that gets synthesized.
|
||||
|
||||
|
||||
|
||||
|
||||
102
generators/chipyard/src/main/scala/ChipTop.scala
Normal file
102
generators/chipyard/src/main/scala/ChipTop.scala
Normal 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
|
||||
@@ -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"))
|
||||
})
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ++
|
||||
|
||||
@@ -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 ++
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Submodule generators/icenet updated: f227228474...b1f957e6eb
Submodule generators/testchipip updated: 30d44252e8...d06d7c7dc2
@@ -5,7 +5,7 @@ index a633066..3df8b74 100644
|
||||
@@ -124,7 +124,7 @@ lazy val testchipip = (project in file("generators/testchipip"))
|
||||
|
||||
lazy val chipyard = conditionalDependsOn(project in file("generators/chipyard"))
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities,
|
||||
.dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities, iocell,
|
||||
- sha3, // On separate line to allow for cleaner tutorial-setup patches
|
||||
+// sha3, // On separate line to allow for cleaner tutorial-setup patches
|
||||
gemmini, icenet, tracegen, ariane)
|
||||
|
||||
Submodule tools/barstools updated: 63d74bc177...db6776367c
@@ -36,7 +36,7 @@ ifeq ($(SUB_PROJECT),chipyard)
|
||||
CONFIG_PACKAGE ?= $(SBT_PROJECT)
|
||||
GENERATOR_PACKAGE ?= $(SBT_PROJECT)
|
||||
TB ?= TestDriver
|
||||
TOP ?= Top
|
||||
TOP ?= ChipTop
|
||||
endif
|
||||
# for Rocket-chip developers
|
||||
ifeq ($(SUB_PROJECT),rocketchip)
|
||||
|
||||
Reference in New Issue
Block a user