Skip to content

Commit

Permalink
Support views of ports in ChiselSim (#4107)
Browse files Browse the repository at this point in the history
Also fix reifySingleData to return the Data itself if it is not a view.

(cherry picked from commit ca49d57)

# Conflicts:
#	src/main/scala/chisel3/simulator/package.scala
  • Loading branch information
jackkoenig authored and mergify[bot] committed May 29, 2024
1 parent de4d945 commit 32e53a0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,15 @@ package object dataview {
* @note An Aggregate may be a view of unrelated [[Data]] (eg. like a Seq or tuple) and thus this
* there is no single Data representing the Target and this function will return None
* @return The single Data target of this view or None if a single Data doesn't exist
* @note Returns Some(_) of the argument if it is not a view
*/
private[chisel3] def reifySingleData(data: Data): Option[Data] = {
val candidate: Option[Data] =
data.topBindingOpt match {
case None => None
case Some(ViewBinding(target)) => Some(target)
case Some(AggregateViewBinding(lookup)) => lookup.get(data)
case Some(_) => None
case Some(_) => Some(data)
}
candidate.flatMap { d =>
// Candidate may itself be a view, keep tracing in those cases
Expand Down
68 changes: 68 additions & 0 deletions src/main/scala/chisel3/simulator/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,83 @@ package chisel3

import svsim._
import chisel3.reflect.DataMirror
import chisel3.experimental.dataview.reifySingleData
import scala.collection.mutable
import java.nio.file.{Files, Path, Paths}

package object simulator {
implicit class SimulationController(controller: Simulation.Controller) {
def port(data: Data): Simulation.Port = {
<<<<<<< HEAD
val context = Simulator.dynamicSimulationContext.value.get
assert(context.controller == controller)
context.simulationPorts(data)
=======
// TODO, we can support non 1-1 views, but it will require changing this API to return a Seq[Port]
// and packing/unpacking the BigInt literal representation.
val reified = reifySingleData(data).getOrElse {
val url = "https://github.com/chipsalliance/chisel/issues/new/choose"
throw new Exception(
s"Cannot poke $data as is a view that does not map to a single Data. " +
s"Please file an issue at $url requesting support for this use case."
)
}
simulationPorts(reified)
}

// -- Peek/Poke API Support

// When using the low-level API, the user must explicitly call `controller.completeInFlightCommands()` to ensure that all commands are executed. When using a higher-level API like peek/poke, we handle this automatically.
private var shouldCompleteInFlightCommands: Boolean = false
private[simulator] def completeSimulation() = {
if (shouldCompleteInFlightCommands) {
shouldCompleteInFlightCommands = false
controller.completeInFlightCommands()
}
}

// The peek/poke API implicitly evaluates on the first peek after one or more pokes. This is _only_ for peek/poke and using `controller` directly will not provide this behavior.
private var evaluateBeforeNextPeek: Boolean = false
private[simulator] def willEvaluate() = {
evaluateBeforeNextPeek = false
}
private[simulator] def willPoke() = {
shouldCompleteInFlightCommands = true
evaluateBeforeNextPeek = true
}
private[simulator] def willPeek() = {
shouldCompleteInFlightCommands = true
if (evaluateBeforeNextPeek) {
willEvaluate()
controller.run(0)
}
}
}
private[simulator] object AnySimulatedModule {
private val dynamicVariable = new scala.util.DynamicVariable[Option[AnySimulatedModule]](None)
def withValue[T](module: AnySimulatedModule)(body: => T): T = {
require(dynamicVariable.value.isEmpty, "Nested simulations are not supported.")
dynamicVariable.withValue(Some(module))(body)
}
def current: AnySimulatedModule = dynamicVariable.value.get
}

implicit class ChiselSimulation(simulation: Simulation) {
def runElaboratedModule[T, U](
elaboratedModule: ElaboratedModule[T],
conservativeCommandResolution: Boolean = false,
verbose: Boolean = false,
traceEnabled: Boolean = false,
executionScriptLimit: Option[Int] = None
)(body: SimulatedModule[T] => U
): U = {
simulation.run(conservativeCommandResolution, verbose, traceEnabled, executionScriptLimit) { controller =>
val module = new SimulatedModule(elaboratedModule, controller)
AnySimulatedModule.withValue(module) {
body(module)
}
}
>>>>>>> ca49d5779 (Support views of ports in ChiselSim (#4107))
}
}

Expand Down
23 changes: 23 additions & 0 deletions src/test/scala/chiselTests/simulator/SimulatorSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,28 @@ class SimulatorSpec extends AnyFunSpec with Matchers {
(actualSV should not).include("emptyBundle")
actualSV should include("bundle_x")
}

it("support peeking and poking FlatIO ports and other views of ports") {
import chisel3.experimental.dataview._
class SimpleModule extends Module {
val io = FlatIO(new Bundle {
val in = Input(UInt(8.W))
val out = Output(UInt(8.W))
})
val viewOfClock = clock.viewAs[Clock]
val delay = RegNext(io.in)
io.out := delay
}
new VerilatorSimulator("test_run_dir/simulator/flat_io_ports")
.simulate(new SimpleModule) { module =>
import PeekPokeAPI._
val dut = module.wrapped
dut.io.in.poke(12.U)
dut.viewOfClock.step(1)
dut.io.out.peek()
dut.io.out.expect(12)
}
.result
}
}
}

0 comments on commit 32e53a0

Please sign in to comment.