Issie logo Issie

WaveProvider Module

Which simulator answers the waveform viewer, and - when it is the .NET sidecar - fetching what the view draws. The shape of it: the sidecar builds and runs the simulation, and the renderer asks it for one window - the samples the current view shows, for the waves it is showing - every time that view changes. `SidecarClient.simRead` takes exactly the viewer's own (StartCycle, SamplingZoom, ShownCycles) triple, so a view at any zoom is ONE request rather than one per wave, and the reply is read with no copy. What comes back becomes a `WaveData.Fetched` source, and the drawing code is none the wiser (see WaveSlice). Two requests, not one, and for a reason: the drawn window holds a sample every SamplingZoom cycles, but the cursor sits on an exact cycle which at any zoom above 1 falls between those samples. So the cursor column - every shown wave at that one cycle - is fetched alongside it. Both are sub-millisecond: the measured round trip is 0.3-0.6 ms and a typical window is a few hundred kB at 350-800 MB/s, against waveform generation that is budgeted at 50 ms a slice. **Not yet here**: buses wider than 32 bits, which `simRead` refuses, so their waves come back with no data and are drawn empty. That is the next thing to add, along with the RAM tables, which need a command of their own.

Functions and values

Function or value Description

cyclesSimulated inRenderer sidecarClock fs

Full Usage: cyclesSimulated inRenderer sidecarClock fs

Parameters:
Returns: int

How many cycles the simulation being shown has actually been run for. Whichever simulator is running is the one that knows. Asking the renderer's FastSimulation while the sidecar simulates gives zero however far the sidecar has gone, which is not a number anything should act on: it put the cursor back to cycle 0 when a progress bar was cancelled.

inRenderer : bool
sidecarClock : int
fs : FastSimulation
Returns: int

fetchProbeValue epoch fs wi cycle

Full Usage: fetchProbeValue epoch fs wi cycle

Parameters:
Returns: Promise<bigint option>

One signal's value at one cycle: what the schematic probe shows. A read like any other - the same by-handle read, one signal and one sample of it - so the probe uses the mechanism everything else uses rather than a transport of its own. It was briefly a BLOCKING read over a second transport, so that the value could be had inside the render that draws it. That cost 2.2ms against this path's 0.2ms, because a synchronous XMLHttpRequest is the only thing that can block a renderer's thread and Chromium does not make it quick. The label appearing one render later is not something a user can see.

epoch : int
fs : FastSimulation
wi : WaveIndexT
cycle : int
Returns: Promise<bigint option>

fetchWavesFor epoch fs waves window

Full Usage: fetchWavesFor epoch fs waves window

Parameters:
Returns: Promise<Result<unit, SidecarFailure>>

Fetch some waves over the window they are drawn over, and put them where the viewer reads them. **One fetch at a time**, which the CALLER enforces with `WaveSimModel.FetchInProgress`. A fetch is asked for whenever a wave is not holding its window - every checkbox tick and scroll step - so without that a second chain starts while the first is still running, and the two interleave build, run and read against one session: the second chain's build resets the simulation under the first chain's read. The sidecar serves them in arrival order and cannot tell that they belong to different views. One request covers every wave asked for, so the number of round trips is one per view rather than one per wave, and the waves that arrive together are drawable together.

epoch : int
fs : FastSimulation
waves : WaveIndexT list
window : Window
Returns: Promise<Result<unit, SidecarFailure>>

selectSimulator inRenderer epoch localLookup localClock

Full Usage: selectSimulator inRenderer epoch localLookup localClock

Parameters:
    inRenderer : bool
    epoch : int
    localLookup : SignalHandle -> IOArray option
    localClock : unit -> int

Choose the simulator for this refresh, and say what the renderer's own one reads through. Called once per refresh so that nothing below has to branch on which simulator is running. **The session decides, and nothing else has to.** This took a `newSimulation` flag from the caller as well, which said the design or its shape had changed and so what was cached had been read from a simulation that no longer exists. That is exactly what a different epoch says, and the epoch says it without anyone having to remember to pass it: a build issues a new one, so a cache of the old one is emptied here on the next refresh whether or not the refresh knew it was the first after a build. `epoch` is 0 when there is no session, which is a session number no build ever issues, so the cache of a stopped simulation is not mistaken for a live one either.

inRenderer : bool
epoch : int
localLookup : SignalHandle -> IOArray option
localClock : unit -> int

wavesToFetch inRenderer handles window

Full Usage: wavesToFetch inRenderer handles window

Parameters:
Returns: SignalHandle list

The waves that are not holding the window they are about to be drawn over, and so have to be asked for. Derived, on every refresh, from what the cache holds and what the view asks for. Nothing records which waves are outstanding, because nothing needs to: a wave needs fetching exactly when it has not got the cycles it is being drawn over. None of them, ever, when the renderer is simulating: its cache reads through to step arrays that are already in memory, so there is nothing to fetch and nothing to wait for. That asymmetry is the only thing that distinguishes the two simulators here, and it is why the caller needs no flag of its own.

inRenderer : bool
handles : SignalHandle list
window : Window
Returns: SignalHandle list

Type something to start searching.