Skip to content

SIL emulator

The software-in-the-loop (SIL) system tests the complete firmware ↔ UI integration with no physical hardware. It executes the Propeller 2 image on an instruction-set simulator. The machine's pins are nets, and the physics models sit on those nets. The host on the protocol link is the host's Chrome, every page and worker held to the board's clock by embsim's chrome-cdp node; or a PTY, for a serial console. The control app connects to the virtual serial port exactly as it would to a real board.

One system runs it:

  • The machine's project files (SIL/mad.toml and the others, under embsim run): the Edge carrier and the P2-EC32MB module from their netlists, the ISS as the module's processor booting the firmware image (pio run -e MaD) off the module's flash, the DS2 add-on and the machine on the carrier's connectors, the host on the carrier's control port J4 (or, on the serial-boot files that flash it, the debug header J1). The e2e suite runs here (make e2e, on mad-cosim.toml); so do the scenarios that move the carriage, on the same file (make e2e-motion), SERVO-stall, on that file with a hard stop on its carriage, mad-hard-stop-cosim.toml (make e2e-stall, MaD #298), FW-ISS, the app's flasher against the mask ROM, on mad-serial-boot-cosim.toml (make e2e-rom), the playgrounds, on headed copies (make playground, playground-rom), and mad-catalog's tests.

This is not a mock

The firmware under test is the image you flash. The ISS executes its instructions, including WRPIN / DIR / OUT, so a wrong smart-pin mode or a wrong clkfreq shows up on the wire. The screenshots in this documentation were produced by driving the app against this emulator.

embsim — a reusable framework

The board is embsim. A part is a node: it publishes a drive and receives a sense. MaD supplies the core (the ISS) and the bench around it. The generic crates carry no MaD protocol and no gantry geometry.

flowchart TB
    catalog["<b>mad-catalog</b> / <b>mad-runner</b><br/>MaD's kinds; runs the project files"]
    iss["<b>p2iss</b> / <b>p2core</b><br/>the P2 image, instruction by instruction"]
    board["<b>embsim-board</b><br/>nets, drives, host PTY"]
    models["<b>embsim-models</b><br/>ADC · stepper · encoder · end switch"]
    core["<b>embsim-core</b> — virtual clock · serial PTY"]
    madmodels["<b>models</b><br/>gantry · sample · strain gauge"]

    catalog --> iss --> board
    catalog --> models
    catalog --> madmodels
    board --> core
    models --> core
    madmodels --> models

The dependency graph is acyclic — no generic crate depends on a project crate. MaD-specific code lives in mad-catalog/, mad-runner/, p2iss/, p2core/, models/, and the protocol crate (Protocol/rust/). See the embsim README.

The simulation chain

When the firmware commands the motor, a chain of callbacks turns pulses into forces and feeds them back as sensor readings:

flowchart TB
    pulse["step pin, periodic drive"] --> motor["stepper model<br/>(position over time)"]
    motor --> enc["encoder (position feedback)"]
    motor --> limit["end switches"]
    motor --> gantry["gantry"]
    gantry --> sample["sample model<br/>force = f(displacement)"]
    sample --> gauge["strain gauge"]
    gauge --> adc["ADS122U04 ADC"]
    adc -->|"serial levels"| fw["firmware reads force"]
    limit --> fw
    enc --> fw

The ADC, stepper, encoder, and end switch are generic (embsim-models). The gantry, sample, and strain gauge are MaD-specific (SIL/models/). Pin numbers are the firmware's (HW_pins.h); the project files reach them through the carrier's netlist (mad-catalog/netlists/mad_edge.net).

The virtual serial port

The host end sits on the protocol nets at 2,000,000 baud 8N1: on the project, the carrier's control port J4, through the isolator IC2 to P30/P32, as on the machine (the serial-boot files and the reflash file put it on the debug header J1, P63/P62, where the Prop Plug sits, to flash). It is one of two components:

  • Chrome on the board's clock (chrome-cdp: mad-cosim.toml's HOST, make e2e; mad-playground.toml's, headed, make playground). Every page's navigator.serial is the node's shim, whose one port is this line (USB 0403:6001 on J4, 0403:6015 on J1), by Chrome's own rules; every page and dedicated worker lives the board's time, a 1 ms quantum at a time over DevTools, so the app's 2 s response timeout is 2 s of the board's time. Its rail pins are wired to a 3.3 V rail and the board's ground.
  • A PTY (host-serial: mad.toml's HOST, embsim run mad.toml --pty PATH; make playground-pty, embsim run mad.toml --pty /tmp/tty.iss), for a serial console or a program. A browser behind it (the WS↔PTY bridge) runs at host speed while the ISS runs at a few percent of real time, so it measures the host, not the machine.

On real hardware the app uses Web Serial directly. See SIL testing.

One emulator per process

The virtual clock and the host PTY are process-global, so there is one emulator per OS process. To run several, run several processes — which is why the E2E suite uses workers: 1.