Issie logo Issie

SimTypes Module

Types and nested modules

Type/Module Description

SimulationBudget

How much memory a simulation may take, and of which kind. Here rather than beside the code that spends it because two different parts of the simulator spend it: GraphMerger, which expands the design into a graph, and FastCreate, which allocates the step arrays. Both come out of the same memory and GraphMerger is compiled first. Sizes are float and not int64 on purpose. Fable compiles int64 to BigInt, so every comparison here would allocate one - a poor thing to spend on deciding whether a design is too big. A float carries integers exactly to 2^53 and the largest number reached here is a few hundred GB.

BigIntState

Used for efficiency reasons. For a given normal simulation these arrays show whether the corresponding component input or output is a bigint or a unint32 type bus, and therefore show IOArray array is used for the data.

Driver

Convenience array used so that waveform simulation can access component outputs (drivers) without a Map lookup

FData

FastComponent

FastComponent represents a physical component in a simulation. Because sheets can be instantiated in multiple places a given sheet component can have multiple FastComponents in the simulation. Arrays on FastComponent are filled up with simulation data per clock step as a clocked simulation progresses. Equality is by reference: a FastComponent is a mutable object with an identity, holding step arrays that can run to megabytes, so comparing two of them field by field would be both meaningless and ruinous. It also carries its own reducers, which are functions and so have no structural equality at all.

FastSimulation

GatherData

Scaffolding for building a FastSimulation, and alive only while one is built. It used to be four `Map`s: the flattened design indexed the several ways the phases after the flatten needed it, all of them keyed structurally by (ComponentId, access path). Every one of those keys cost a boxed comparison per tree level of every lookup, and the build does millions of lookups - a measured fifth of a 480,000-component build went on one of them. They are now a single index space instead: the flatten creates each FastComponent, stamps it with its position in `Comps`, and expresses every link it finds as those indices. Nothing here is keyed by anything but an int, and what a built simulation offers the rest of the program - the store in gather order, and one map from a design-time name into it - is made once at the end, from this. One store and one index space, holding the FastComponents themselves: custom against ordinary is a PREDICATE over it, never a second store. Splitting them is the obvious tidy-up and it is what would break this - the indices the links carry would then mean two different things. Deliberately not kept after the build. It holds a SimulationComponent per component INSTANCE through the FastComponents it stores, so on a large design it is one of the biggest things the simulator ever allocates, and a FastSimulation left holding one made every later edit slower by giving each major GC all of it to trace.

IOArray

This type represents an array of time steps of simulation data. In any simulation, for a given IOArray, only one of the three step stores will be used. For (very strong) efficiency reasons this cannot be implemented as a disjoint union: the code that reads and writes IOArray elements will access the appropriate store. Truthtable simulations use FDataStep everywhere. Normal simulations use the UInt32 or BigInt store according to the size of the relevant bus. The uint32 and bigint stores are REGIONS OF SHARED SLABS, not arrays of their own: step s of this IO lives at `slab[StepBase + s]`, and `StepLength` steps belong to it. FastCreate's arena packs every step region of a build into a few large slabs (under both runtimes), so a design has dozens of step allocations rather than one per output port, and a port's data is named by an integer offset. Always go through the members below - indexing a slab without adding StepBase reads another port's data, which no bounds check will ever catch.

RunOutcome

What a run of a fast simulation did. Not a rate. It used to return cycles per millisecond, with None meaning both "nothing to do" and "finished" - so a caller could not tell those apart, and the one caller that used the number divided by it to guess how long the rest would take. Guessing elapsed time from work done is what breaks when a machine sleeps mid-run: the rate collapses towards zero and instant work is predicted to take minutes. This says what happened and nothing more.

SheetPort

Type used to tie component ports to simulation data for advanved wavefor simulation features.

SimulatedDesign

The design a simulation was built FROM, as opposed to the simulation built from it. **Everything here is the size of the design somebody drew.** A design of seven sheets is seven sheets here however many instances it expands to - and a design that expands to 49,152 sheet instances is exactly the case this exists for. The renderer needs the design to draw a selector, a hierarchy and a set of names; it does not need the expansion, and holding one to get at the other is what made the renderer's memory grow with a simulation running elsewhere. The instance queries below are the point of the type. An instance is an `InstancePath`, a list of the custom-component ids entered on the way down to it, and every question anyone asks about one - which sheet is it a copy of, what is it labelled, what is inside it - is answered by walking that path through the DESIGN. Each step is a lookup in one sheet's components, so a query costs the depth of the path and the size of a sheet, and never the expansion. They used to be answered from maps built over every instance in the simulation: 208,896 entries on largeTest, for questions only ever asked about the handful of instances on screen. The design the simulation was BUILT from, not the one on the canvas now. An edit to the schematic is meant to change nothing in a running waveform simulation until it is restarted or refreshed, and reading the live project instead moved half the selector and left the rest.

SimulationData

- Top level data tracking a simulation

SimulationRunStatus

document current status of a simulation as used by waveform simulator

StepArray<'T>

Wrapper to allow arrays to be resized for longer simulations while keeping the links between inputs and outputs

StepCost

What one clock cycle of a design costs in step arrays, kept apart by which memory it comes from. The two are not interchangeable, and measurably so - though not in the way performance.memory suggests. usedJSHeapSize counts Uint32Arrays at every size, so it cannot tell the two apart; what separates them is the LIMIT. Uint32Array allocation ran to 15.5GB on a 32GB machine against a jsHeapSizeLimit of 3.7GB, so those are bounded by the machine and not by V8's pointer compression cage. Buses wider than 32 bits are held as a plain array of BigInt, which is ordinary heap - 400MB of values cost 454MB - inside that 4GB cage, shared with the model, the design and the waveforms. So a design can be refused for the second while nowhere near the first. FastCreate.stepBytesForWidth works out one port's share; FastCreate.stepCostOfGraph totals it.

StepIndex

Where one clock step sits in the circular simulation arrays: the step number itself, its index into the arrays, and the index of the step before it. All three follow from the step number, so the simulation loop works them out once per step and hands the same value to every component - numStep % maxArraySize is an integer division, and it used to be redone for every component of every step. A struct so that passing it costs nothing.

WaveTables

Whether a build makes the tables only a waveform VIEWER needs: WaveComps, Drivers and WaveIndex. Nothing that RUNS a simulation reads any of them - not the run loop, not the reducers, not FastExtract, not RamView - so a simulator that only runs and is read over a wire can leave them out. They are sized by the EXPANSION, one entry per port of every instance, which is exactly what a remote simulator exists to keep out of the process that draws. The custom-component port linking that used to be part of the same phase is not optional and happens either way: it re-points a custom component's ports at the arrays of the Input and Output components inside it, which is what makes those ports readable at all.

Functions and values

Function or value Description

clockTickNumber_

Full Usage: clockTickNumber_

Returns: Lens<SimulationData, int>
Returns: Lens<SimulationData, int>

emptySimulatedDesign

Full Usage: emptySimulatedDesign

Returns: SimulatedDesign

A design with nothing in it: what a renderer holds before anything has been simulated.

Returns: SimulatedDesign

extractLabel label

Full Usage: extractLabel label

Parameters:
Returns: string
label : ComponentLabel
Returns: string

fastSim_

Full Usage: fastSim_

Returns: Lens<SimulationData, FastSimulation>
Returns: Lens<SimulationData, FastSimulation>

graph_

Full Usage: graph_

Returns: Lens<SimulationData, SimulationGraph>
Returns: Lens<SimulationData, SimulationGraph>

mapItems map

Full Usage: mapItems map

Parameters:
    map : Map<'a, 'b>

Returns: ('a * 'b) array
map : Map<'a, 'b>
Returns: ('a * 'b) array

mapKeys map

Full Usage: mapKeys map

Parameters:
    map : Map<'a, 'b>

Returns: 'a array
map : Map<'a, 'b>
Returns: 'a array

mapValues map

Full Usage: mapValues map

Parameters:
    map : Map<'a, 'b>

Returns: 'b array
map : Map<'a, 'b>
Returns: 'b array

numberBase_

Full Usage: numberBase_

Returns: Lens<SimulationData, NumberBase>
Returns: Lens<SimulationData, NumberBase>

shortPSComp comp

Full Usage: shortPSComp comp

Parameters:
Returns: string
comp : SimulationComponent
Returns: string

sprintSimComponent sComp

Full Usage: sprintSimComponent sComp

Parameters:
Returns: string
sComp : SimulationComponent
Returns: string

stepGet arr i

Full Usage: stepGet arr i

Parameters:
    arr : 'a array
    i : int

Returns: 'a
Modifiers: inline
Type parameters: 'a

Step-store access without a Fable bounds check. Under Fable an ordinary arr[i] on a local binding compiles to fable-library's item/setItem, which profiling once found taking 70% of all simulation time (see EvalCompiled's getA/setA, the same shim for the same reason). The old per-IO step arrays escaped it by accident - property-chain access emits a raw index - but the members below bind `this` to a local, so without this shim every read on the simulator's hot path became an item() call, measured at 3x whole-simulation cost. Every index is StepBase + a step below StepLength, in range by construction; under .NET these stay ordinary checked accesses.

arr : 'a array
i : int
Returns: 'a

stepIndexOf maxArraySize numStep

Full Usage: stepIndexOf maxArraySize numStep

Parameters:
    maxArraySize : int
    numStep : int

Returns: StepIndex
Modifiers: inline
maxArraySize : int
numStep : int
Returns: StepIndex

stepSet arr i v

Full Usage: stepSet arr i v

Parameters:
    arr : 'a array
    i : int
    v : 'a

Modifiers: inline
Type parameters: 'a
arr : 'a array
i : int
v : 'a

tryGetCompLabel compId sg

Full Usage: tryGetCompLabel compId sg

Parameters:
Returns: string
compId : ComponentId
sg : SimulationGraph
Returns: string

Type something to start searching.