Simulation

A Waveflow design is, first, a Python program you can run. Before any C++ is generated, you assemble your components, wire them together with interfaces, and simulate the whole system — checking that the data flows, the protocol handshakes, and the results are correct. That is the milestone: simulate a whole system in Python.

Discrete-event simulation (PySim)

The simulation is discrete-event (DES): nothing advances on a fixed clock tick — instead the runtime jumps from one scheduled event to the next, so time only moves when something happens. This is what makes a transaction-level model fast: a 1024-word burst is one timed event, not 1024 clock steps.

Waveflow builds this on SimPy. The Simulation object owns a single simpy.Environment and the list of participating objects; every simulation entity is a SimObj that registers itself with that Simulation and borrows its environment. You rarely touch SimPy directly — SimObj wraps the primitives you need (self.timeout(...), self.process(...), self.event()).

The three-phase lifecycle

Simulation.run_sim() drives every registered SimObj through pre_simrun_procpost_sim, in registration order (error_cleanup() runs on every object if the run raises, so files and loggers close). That per-object lifecycle — and the on_start / @sim_only component specifics — is on the SimObj page; this is the system view: how the hooks are driven across all objects at once.

In this section

  • SimObj — the base object every simulation entity is, its three-phase lifecycle, and a toy two-SimObj producer/consumer simulation.
  • Process generators — SimPy’s yield-based concurrency for newcomers: events, pausing, parallel spawn, return values, and ProcessGen[T] hints.
  • Running a simulation — instantiate components, wire them with Interface objects, build a Simulation, and call run_sim().
  • Timing modelClock and how components / interfaces express cycle latency (the forward model that produces the timeline).
  • Logging — the Logger SimObj: recording timestamped events to a CSV for inspection and timing analysis.

See also

  • Interfaces — the transactional connections components are wired through.
  • Hardware Components — declaring the components (ports, HwParam) that a simulation runs.
  • Timing Analysis Toolsanalyzing the timeline a simulation produces (this section is the model that produces it).
  • Build System — running a simulation as a step in a BuildDag.

The simulation models a design in Python. Turning that design into synthesizable C++ — the generated kernel structure and the hand-written kernel bodies — is the codegen arc: Component Code Generation and Custom Hooks.


Table of contents

  • SimObj - The SimObj base: everything in a simulation is a SimObj that registers with the owning Simulation, borrows its single simpy.Environment, and uses the wrapped primitives (self.timeout, self.process, self.event). A runnable toy two-SimObj producer/consumer shows a bare simulation before any interface or HwComponent.
  • Process generators - A quick guide to SimPy's generator-based concurrency for readers new to it: what an event and a process generator are, how yield pauses a process, when behavior must be a generator, how to spawn a non-blocking parallel process, how to return a value (proc.value vs yield from), and how to type-hint with ProcessGen[T].
  • Running a simulation - Assemble and run a system in Python: instantiate components against one Simulation, wire their ports with Interface objects (.bind), and call run_sim() to drive pre_sim / run_proc / post_sim. Results are read off the objects afterward.
  • Timing model - The forward timing model that produces the simulated timeline — Clock (frequency / period), how interfaces charge transfer latency in cycles, and how a component models compute latency with self.timeout / self.action. (Analyzing the result is Timing Analysis.)
  • Logging - Record timestamped events during a simulation: Logger is a SimObj that writes a flushed CSV (a leading time column from env.now plus your declared fields); log(**fields) records a row, get_tv(field) reads back (times, values). NullLogger is the no-op default.

This site uses Just the Docs, a documentation theme for Jekyll.