Issie logo Issie

SimInterface Module

 What the waveform viewer and the step simulator ask of a simulator, and what a simulator
 promises them - whichever process it runs in.

 **Make more of the system synchronous rather than accept asynchrony and manage it.** Effort
 spent moving work to where it can be answered at once is repaid; complexity taken on to
 tolerate an answer that is not there yet never goes away. This is the Elmish discipline, and it
 holds up surprisingly well in Issie - with memoisation where recomputing would be too slow, and
 a few deliberate breaks in places where it does not matter. A simulator in another process is
 the hardest thing to hold to it, and it can still be held: what is genuinely slow becomes a
 message to start and a message on completion, with a state in the model between the two, and
 everything else stays a question with an answer. That takes care and judgement rather than a
 blanket rule, which is why it is written down here rather than assumed.

 **Interrogating a simulator is synchronous, and there is no cache between.**

 Almost everything the UI asks a simulator is a small question about structure - which instances
 are inside this one, which ports does this instance offer, what is this port called and how
 wide is it - and the answer is wanted from inside `view`, where nothing can await.

 Only three things take long enough that they cannot be answered while a frame is being drawn,
 and none of them is a question:

   PRE-BUILD, where there is no simulation yet to ask;
   BUILD, which elaborates the design;
   RUN, which simulates it.

 All three are commands, the renderer issues them, and so the renderer knows perfectly well when
 it is in one. That is the whole of the problem: every synchronous interrogation answers
 immediately in those three states - nothing yet - and the UI draws what it has, which is what
 it must do anyway while a build is running. Everywhere else the answer is there to be given.

 So there is no read-through cache, no per-question "not yet", and no await in view code. An
 earlier design here had all three, on the assumption that talking to another process must make
 every question asynchronous. It does not: what makes a question asynchronous is being unable to
 answer it, and the three states above are exactly the ones where that is true and are known in
 advance.

 **What makes that possible is WHERE the answers live.** A question the renderer answers from
 what it holds is synchronous by construction. It holds two things:

   the DESIGN (`SimTypes.SimulatedDesign`), which settles every question about structure - which
   sheet an instance is a copy of, what it is labelled, what is inside it, how many of it there
   are - all by walking the design, none by expanding it;

   and what the last BUILD produced about ports: which of them carry a waveform, what each is
   called, and how wide it is. Widths are why this cannot come from the design alone - a
   parameterised sheet resolves them per instance - but they are settled by the build, so they
   are known before the first frame that asks.

 **Nothing displayed yet is a fine answer**, because it becomes a displayed one. A waveform with
 no data draws nothing and is drawn again when its data lands; where the sidecar simulates that
 is every view until the first fetch returns. WaveSimSVGs goes further and draws the last window
 that ARRIVED rather than freezing on the last one that matched, so a fast scroll keeps moving.

 The one place a lot of asking could collide is the waveform data itself, and it does not: every
 wave a refresh is missing goes in ONE command, with at most one RAM behind it, sequentially in
 one promise, under the one FetchInProgress bit that stops the next message asking again
 (WaveSimTop.fetchWhatIsMissing). So the number of things in flight is one, whatever the number
 of waveforms - which is what keeps the reads above answerable without arbitration.

 The one thing that is genuinely per-instance AND per-build is where a port's data LIES. That is
 only ever needed twice: to issue a fetch, which happens in the update function where an await
 is already happening, and thereafter as a KEY into what the fetch returned - `Wave.DriverIndex`
 indexing `WaveData` and `WaveDrawn`, both of which are renderer-side stores of data already in
 hand. Neither is a question put to a simulator during a render.

 **Why this interface is narrow and the old one was not.** What the renderer uses today is not
 an interface at all: it is field access into a `FastSimulation` - the component store,
 `Drivers`, `WaveIndex`. Every one of those is proportional to the
 EXPANDED simulation, so promoting them to an interface would oblige a remote simulator to
 rebuild all of it renderer-side, which is the cost this exists to remove. So the interface
 asks small questions - which instances are inside this one, which ports does this instance
 offer, read me this window - and a `FastSimulation` becomes the private business of the
 implementation that has one.

 **Widths come from here and nowhere else.** Parameters are resolved when a design is
 elaborated and can change widths, so a width is a fact about the elaborated INSTANCE, not
 about the sheet it instantiates. The renderer must never infer one from a design.

 **Truth tables are not one of the two, and never will be.** They always use the renderer's own
 simulator, whatever `Model.SimulateInRenderer` says. A truth table is combinational: it builds
 one sheet with an array of two cycles (`TruthTableView`, which already asks for exactly the
 two `ModelHelpers.Constants.rendererArraySizeWhenSidecarSimulates` allows), then drives the
 inputs and reads the outputs once per ROW. That is thousands of round trips for a simulation
 small enough to have been built in the renderer anyway - the expansion the sidecar exists to
 keep out of this process is not what a truth table has. So the mutual exclusion between the
 waveform and step simulators does not extend to truth tables, and neither does this interface.

Types

Type Description

ISimulator

A simulator, wherever it runs. Every member is asynchronous because one implementation is a separate process. An in-renderer implementation returns already-resolved promises, which costs a microtask and buys one code path in the caller. Not yet here, and deliberately: reading a RAM's contents, which needs a row type that is declared in the waveform UI and has to move first; and the simulation errors a build can return, which are threaded through as they are today.

SimInstance

One elaborated instance of a sheet, as the selector needs to show it. DESIGN-sized: one of these per instance the selector is DRAWING, never one per instance the design expands to. The distinction is the whole memory argument - a design that expands to 49,152 copies of a sheet still only ever draws a handful of them at once.

SimIo

A top-level input or output of the simulated sheet, with its value at one cycle.

SimPort

One port of one elaborated component that a waveform can be taken of. Carries its own width AND its own labels. The width because of elaboration (see the module note). The labels because deriving them needs the simulation too: an IOLabel's name and width come from which member of a same-named group actually drives the net, which is a fact about the built simulation and does not exist in the renderer when the sidecar is simulating. `PortCompType` rather than the UI's `ComponentGroup`: that type is declared in ModelType, which compiles long after this, and classifying a component is a pure function of its type.

SimStateEntry

One clocked component's state at one cycle, for the step simulator's state pane.

SimViewer

A Viewer anywhere in the hierarchy, with its value at one cycle.

StepPanelSnapshot

Everything the step simulator's panel shows at one cycle. One value, because it is fetched in one request. The panel currently re-reads inputs, outputs, viewers and state separately on every render, from inside `view`; over a wire that would be four round trips per repaint, so the unit of transfer is the panel and not the field.

Type something to start searching.