Skip to main content

simantic

Python control of the Simantic simulators — the firmware engine hosted in your process, circuits via analog-cli. Everything the CLIs can do, as objects and method calls: start a board or a multi-machine scenario, advance virtual time by exact amounts, inject UART/GPIO/CAN/radio, read memory and RTOS state, and run as many simulations in parallel as you have cores. pytest is one way to use it, not a requirement.

Alpha — not stable. Version 0.1.x. The API, the CLI surface, and the report schema may change without a deprecation period, and any release may break the previous one. Pin an exact version (simantic==0.1.0) if you depend on it. Not recommended for production pipelines yet.

pip install simantic

That is the whole setup for Python. The first Sim(...) fetches the simulation engine (Simantic.Core plus a private .NET runtime — nothing else to install) into ~/.simantic/engine/<version>/, checksum-verified against the public release manifest. simantic install fetches it up front, along with the sim and analog-cli binaries if you also want the command-line tools.

A Simantic account (simantic auth) is needed for one thing: resolving MCU models by name (mcu="STM32F401RE"), which are fetched from your account and cached in ~/.sim_cache. A platform file you supply (repl=) needs no account at all.

simantic auth opens a browser tab to sign in — like gh auth login — and stores the resulting token in ~/.sim_id, the same file the CLIs use, so one login covers all of them. In a script or CI, pass --token or pipe one in (echo $TOKEN | simantic auth) instead of opening a browser. Create a token on the dashboard's /account/api page. --no-browser falls back to an interactive prompt for a pasted token.

Every download — engine or binary — is verified against the checksum in the release manifest. The package on PyPI contains only Python; the simulators are never in the wheel.

Already have the binaries? Point $SIMANTIC_ANALOG_CLI and $SIMANTIC_SIM at them, or put them on PATH — both take precedence over a managed install. simantic status shows what is authenticated and which binary each name resolves to.

Drive a simulation

A Sim is a live simulation you control. Time advances only when you ask, so a script is deterministic and your think-time is free:

from simantic import Sim

with Sim(elf="fw.elf", mcu="STM32F401RE", uart="usart2") as sim:
    sim.expect("ready")
    sim.inject_gpio("gpioc", 13, True)      # press the user button
    m = sim.expect("button pressed")
    assert m.virtual_seconds < 0.010        # within 10 virtual ms
    assert sim.read_u32("press_count") == 1

The same class runs multi-machine scenarios with scripted peers (Sim(scenario={...})). The engine lives in your process (one emulation per process), so a parameter sweep is a ProcessPoolExecutor over plain functions. See docs/session-api.md and examples/.

One-shot runs ("run 5 s, give me the transcript") are run_firmware(...).

Using it from pytest (optional)

Sim needs no plugin — construct it inside any test. If you also keep manifests, installing the package registers two collectors that turn them into individually addressable pytest items:

  • *.sim.toml — one item per [[test]] table (analog)
  • test.yaml — one item per fixture (firmware)
$ pytest hardware/ firmware/
hardware/psu/psu.sim.toml::schematic-erc                PASSED
hardware/psu/psu.sim.toml::rails-op                     PASSED
hardware/psu/psu.sim.toml::startup-settling             FAILED
hardware/psu/psu.sim.toml::board-drc                    SKIPPED (no .kicad_pcb)
firmware/tests/gpio-loopback/test.yaml::gpio-loopback   PASSED

Because these are ordinary pytest items you get -k filtering, --junitxml for CI, xdist parallelism, and per-test durations. Failures print the runner's own explanation rather than a Python traceback:

startup-settling (tran): fail
  FAIL settle-time: V(OUT) measured 0.0082 (expected max 0.006, margin -0.0022)

Tests that cannot run in the current environment skip rather than fail — a missing binary, an unconfigured server, an analysis the installed CLI does not support, a check inapplicable to the project. A red run means a simulation ran and disagreed with its expectations.

Library

Circuits

import simantic

report = simantic.run_tests("hardware/psu")
print(f"{report.summary.passed}/{report.summary.total} passed")

for m in report.test("rails-op").measurements:
    print(m.describe())   # out-dc: V(OUT) measured 1.597 (expected eq 1.597 +/- 0.02, margin 0.02)

A failing test is data, not an exception: it arrives in the report with its measured value, declared bounds, and margin. Only conditions that prevent a run at all — bad project, missing kicad-cli, invalid testplan — raise AnalogCliError.

Firmware

The shortest path is a pytest fixture — no manifest, no flags:

def test_firmware_boots(pyrite):
    run = pyrite("build/zephyr.elf", board="stm32f401",
                 expect=["Hello World!"], expect_absent=["FAULT"])
    assert run.passed, run.failure_report()

pyrite runs the ELF offline on the pure-Rust backend and hands back the UART transcript. The fixture skips when no binary is installed, so a suite stays green on a machine that has not run smtc install pyrite.

The same runner is available as a plain function, and sim has its own:

run = simantic.run_firmware(
    "build/zephyr.elf",
    mcu="STM32F401RE",          # resolved by the backend through your account
    expect=["RESULT: PASS"],
    expect_absent=["RESULT: FAIL"],
)
assert run.passed, run.failure_report()

sim emits no structured report — the only observable is UART text — so the verdict is substring matching, the same contract test.yaml manifests use. Pass repl= instead of mcu= for a platform file you author yourself.

MCU models are not distributed with this package: mcu= resolves them through your account. If you have a local model library, set $SIMANTIC_MCU_LIB to resolve from it instead — which is also what applying a fixture's overlay fragment requires.

Some installations need a separate simulation server. When one does, the SDK raises ServerNotConfigured and the pytest plugin skips, rather than reporting a firmware failure.

Telemetry

When you are authenticated, a completed pytest session reports its shape to your account: how many simulator tests ran, how many passed, failed, or skipped, plus this package's version, your Python version, OS, and CPU architecture. One request per pytest invocation, never per test.

It also counts which calls you make — SDK functions, MCP tool names, and smtc subcommands, by name only. These are buffered in ~/.simantic/usage.jsonl and uploaded as counts at most once an hour, so no simulation ever waits on the network. You can read that file at any time; it is one JSON object per line and contains nothing but call names.

It does not send file paths, project names, test names, firmware, or simulation output. Those are yours.

export SIMANTIC_TELEMETRY=0     # or DO_NOT_TRACK=1

smtc status prints exactly what is sent and whether it is on. Reporting is best-effort: if it fails, is blocked, or you are offline, your tests are unaffected and nothing is printed.

Compatibility

Speaks the analog-cli.test-report/1 schema. Additive fields within that revision are tolerated; a breaking revision raises ReportError rather than silently misreading a report.

Multi-machine test.yaml fixtures — those with a machines: map — need the --scenario runner and are not driven yet; they report as skips.

License

MIT. The simulators it drives are separate software under their own terms.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simantic-0.2.0.tar.gz (53.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simantic-0.2.0-py3-none-any.whl (43.4 kB view details)

Uploaded Python 3

File details

Details for the file simantic-0.2.0.tar.gz.

File metadata

  • Download URL: simantic-0.2.0.tar.gz
  • Upload date:
  • Size: 53.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for simantic-0.2.0.tar.gz
Algorithm Hash digest
SHA256 160621245641451802d6ff6bee46265ad43015740f3f629510a53bdbf6207935
MD5 629f276218c4ce397f140007927b0885
BLAKE2b-256 4e972993300017118ad28920cf4361a71b09e1820ffa04e83a3932e049e92679

See more details on using hashes here.

Provenance

The following attestation bundles were made for simantic-0.2.0.tar.gz:

Publisher: ci.yml on simantic-dev/pippy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file simantic-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: simantic-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 43.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for simantic-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b541c4ead50160fdcb2db64a5541d8beea43e6224ec69c5492659b2dd73d56b1
MD5 550fb92c4635aed0709692bc56e9cfc3
BLAKE2b-256 1507486d4982af0ebc98e563cb588b491cd1fcb622fa422d35c1bac6ca33ed24

See more details on using hashes here.

Provenance

The following attestation bundles were made for simantic-0.2.0-py3-none-any.whl:

Publisher: ci.yml on simantic-dev/pippy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.1

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page