Skip to main content

speja

CI Documentation PyPI License

Catch it earlier. Earlier than simulation.

speja (Swedish, said roughly "SPAY-ah") means to scout, to keep a lookout: to spot trouble before it reaches you.

A VHDL formatter and linter in Rust. Every bug has a price that goes up the longer it takes to find: a moment in your editor, a coffee in CI, an afternoon in a waveform viewer, a respin on silicon. speja moves what it can to the cheap end of that scale: the moment you save the file.

Some of what it reports a simulator would have told you eventually: an index outside its array, a value outside its subtype, a process that can never suspend. Eventually means after you have written the testbench, elaborated the design and waited for the run, and only if the run reaches that line. Some of it no simulator will ever tell you, because the design elaborates perfectly well and simply does not mean what it says.

It runs the rule set, command line and configuration of the VHDL Style Guide (VSG), adds a formatter that fixes what it reports, and adds a lint layer that VSG has no equivalent of.

A default lint run reports definite errors only: things that cannot work, rather than things worth a look. Everything that depends on what you meant is one line of configuration away.

Status: beta. The style layer implements VSG 3.35's rule set and is tested against more than 11,000 real-world files; expect layout changes before 1.0. The lint layer (speja lint) is newer and its rule set is still growing.

Install

pip install speja            # Linux, Windows and macOS wheels, Python 3.10+
uv tool install speja        # or
cargo install --path .        # from source (Rust 1.95 or newer)

Standalone binaries for Linux (static, x86_64 and aarch64), Windows (x64 and arm64) and macOS (arm64 and x86_64) are attached to each release, with a SHA256SUMS file.

Everything VSG accepts, unchanged

speja -f src/*.vhd -c vsg.yaml --fix

The arguments, the configuration file, the reports and the exit codes are VSG's, and VSG documents them. Existing scripts and CI jobs keep working, so this README covers only what speja adds on top. Differences are listed in compatibility.

What speja adds

A real formatter. Source is parsed once into a lossless syntax tree and printed in one canonical layout, like rustfmt or Black. Every layout rule is fixed rather than reported, long lines are folded at structural boundaries, and running --fix twice changes nothing. All violations are reported at once and fixed in a single pass: no repeated runs, no rule-order dependencies.

Safety. Formatted output is re-parsed and must contain exactly the same tokens and comments; files with syntax errors are never changed. Fixes VSG does not apply by default need --unsafe_fixes.

Speed. Real-world VHDL is checked at about 3.4 MB/s on one core, in parallel worker processes.

Beyond style: speja lint

Rules that need names resolved, which a per-file style checker cannot do:

speja lint --recursive src                  # the lint layer
speja --recursive src --check style,lint    # both in one run
lint_600 -- Signal 'flag' is not assigned on every path of this combinational process,
            which infers a latch
lint_601 -- Signal 'result' is assigned by 2 concurrent statements (lines 34, 38)
lint_001 -- The signal 'b' is not read in the sensitivity list (first read at line 10)

Sensitivity lists, unused declarations, latch inference, multiple drivers, combinational loops, state machines, vector widths, clock-domain crossings, and the type and name diagnostics of a real front end. Every rule states the evidence behind it and reports nothing when that evidence is missing.

Most of these resolve names across files, which needs a library map. Without one they are skipped, and the run says so. See static analysis and project setup.

Adopting a rule set on existing code

speja --recursive src --generate_waivers waivers.yaml   # accept what exists today
speja --recursive src --waivers waivers.yaml            # from now on, only new violations

Waivers record a rule, a file glob, lines and a reason, and never affect the exit code.

Options speja adds

Option Meaning
lint, --check style,lint run the lint layer, or both layers
--lint_configuration FILE (-lc) configuration for the lint layer only
--waivers, --generate_waivers, --show_waived accept known violations
--unsafe_fixes also apply fixes VSG does not apply by default; review the result
--diff with --fix, print a unified diff instead of changing files
--range START:END with --fix, change only these lines
--stdin_filename PATH name of the --stdin input, for configuration lookup and reports
--sarif FILE SARIF 2.1.0, for GitHub code scanning
--sonarqube FILE SonarQube generic issue JSON
--recursive check the VHDL files in directories and their subdirectories
--list_rules, --statistics what each rule is, and how often each fired

Configuration keys speja adds live under a speja: block: reflow_comments, testbench_files, testbench_libraries, synchronizers, and a rule: block per kind of file.

Editors and coding agents

Two servers over stdio, both the same engine as the command line, so everything is told the same thing about the same file under the same configuration.

speja lsp                        # a language server: diagnostics, formatting, quick fixes
speja mcp                        # an MCP server, for a coding agent

The language server is meant to run beside vhdl_ls rather than instead of it, and advertises only what speja is: it answers no completion, hover or definition request. A VS Code extension ships it, and adds editing actions built on VHDL-LS, such as instantiating an entity and declaring a port map's signals. Those need VHDL-LS running; lint and format do not.

The MCP server gives a coding agent three tools, lint, format and explain_rule:

claude mcp add speja -- speja mcp
{ "mcpServers": { "speja": { "command": "speja", "args": ["mcp"] } } }

lint and format take a file path or a buffer. A buffer is how an agent checks what it is about to write before writing it, which is one step earlier still; format with write fixes a file in place without moving it through the conversation.

For Claude Code there is a plugin, which installs a vsg skill and registers the MCP server:

/plugin marketplace add ru551n/speja
/plugin install speja@speja

The skill tells an agent to format and check VHDL before committing it, how to read a finding's class, and not to treat a run that skipped the library map as a clean file. speja itself still has to be on PATH.

CI

A GitHub Action posts annotations and suggested changes; GitLab CI gets the code-quality report, and SonarQube the generic issue JSON. Jenkins reads the SARIF file through Warnings-NG. Every report format works anywhere.

- uses: ru551n/speja@v0.14.4
  with:
    args: --recursive src

Documentation

speja.readthedocs.io: what speja adds on top of VSG. The rules, their options and the configuration file are VSG's own and are linked to rather than repeated.

Disclosure: this code was written by an LLM

Most of this repository was written by Claude, directed and reviewed by a human. Treat that as a reason to check it rather than a reason to trust it, so here is what there is to check against. None of it depends on the code having been written well.

It cannot reach anything. No network, proven four ways in CI on every change: no dependency is an HTTP or TLS client, the binary imports no symbol that can reach a host, everything runs inside an empty network namespace, and strace records zero network syscalls. You can run those same checks against the binary you downloaded in about five minutes: running in an airgap.

It cannot quietly mangle your files. Formatted output is re-parsed and must contain exactly the same tokens and comments, in the same order, before anything is written. A mismatch is an internal error and your file is left untouched, so a formatter bug costs you a run rather than a file. Files with syntax errors are never modified, writes are atomic through a temporary file in the same directory, and a file whose output equals its input is not rewritten at all.

Its blast radius is small. #![forbid(unsafe_code)]. No privileges, no service, no daemon, no telemetry. The only state it keeps is a 2.3 MB cache of the embedded ieee and std sources, written once per version. The only time it starts another program is --local_rules, which runs the VSG on your PATH, because only VSG can run VSG's Python plugins.

It is checked against reality, not against itself. The formatter is compared with VSG's own output over a corpus of real VHDL, weekly. Every lint rule must report nothing on three real projects unless a person has confirmed each finding is a genuine defect, and those counts are a CI gate. Fuzzing runs nightly. Agreement is published per rule rather than claimed.

Known limits, stated rather than buried. A dependency (vhdl_lang) can stack-overflow on pathological input, which aborts the process before anything is written. A file you made read-only is still replaced, because atomic writes need permission on the directory rather than on the file. Both are in the airgap page.

Cautious first run? --fix --diff changes nothing and prints what it would do, --backup keeps a copy beside each file, and a run without --fix never writes anything at all.

Relationship to VSG

speja is an independent Rust implementation of a VHDL formatter and style checker that aims for compatibility with the rules and configuration of the VHDL Style Guide (VSG). It is not affiliated with, endorsed by, or maintained by the VHDL Style Guide project or its maintainers.

speja was inspired by the VHDL Style Guide (VSG) project by Jeremiah Leary and contributors. speja contains no VSG code; VSG is used only as a behavioural reference.

Every release states the VSG version it targets, and speja --version prints it.

Contributing

See CONTRIBUTING.md for the build, test and corpus commands, how compatibility is measured, and what CI runs.

Licence

Either Apache License, Version 2.0 or MIT, at your option. Third-party dependencies are listed in THIRD_PARTY_LICENSES.md and in NOTICE; the VHDL parser, vhdl_syntax from the rust_hdl project, is MPL-2.0 and is used as an unmodified dependency.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in speja, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Release files for speja 0.14.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for speja 0.14.4
File Size Uploaded
speja-0.14.4.tar.gz 1.1 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for speja 0.14.4
File
speja-0.14.4-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
speja-0.14.4-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
speja-0.14.4-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
speja-0.14.4-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
speja-0.14.4-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
speja-0.14.4-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
speja-0.14.4-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
speja-0.14.4-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 35.7 MB

Release files / speja-0.14.4.tar.gz

Download URL speja-0.14.4.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
a3ac96c77789cf5292aa9db5e5ecb3daaa8ccd020b313abf63c93ee09fb840a8
BLAKE2b-256 checksum
How to use checksums
c6e6d7b54450bc569f9cb916c7e5a7a5c017c557a2422ffb6ef1b428997f2eb7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-win_arm64.whl

Download URL speja-0.14.4-py3-none-win_arm64.whl
Size 4.3 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
1c1cc147c7045a4a15ebaea2a02cfdb7da8c50f1099ce118a18e55c022eda522
BLAKE2b-256 checksum
How to use checksums
c223444d80966920f50ec524b3de504b3b7b7bd2addf5a0a62971e420485eb2d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-win_amd64.whl

Download URL speja-0.14.4-py3-none-win_amd64.whl
Size 4.7 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
c7b19e40a340e5b0c1fd01c252b438691ff3de0a8f0b22fd2575b6e3f3e5b5e2
BLAKE2b-256 checksum
How to use checksums
329689b2e7ae5aa4a5d854e92ce64c28eb344f264103ca1041cdf50f53451f1a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-musllinux_1_2_x86_64.whl

Download URL speja-0.14.4-py3-none-musllinux_1_2_x86_64.whl
Size 4.5 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
2f5ad86691061d17cdb86bb006cd76b0e3e99729b6acff0aea77833c41e5e61f
BLAKE2b-256 checksum
How to use checksums
aa568e9d980f9f87487fc42ddfe676e03bbe15ca22caa1f4d14be4d68b3b0ad5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-musllinux_1_2_aarch64.whl

Download URL speja-0.14.4-py3-none-musllinux_1_2_aarch64.whl
Size 4.2 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
59f90ef004a669cb228a566a198269e6c1e6320b1d7f0a8dd77b62f8b3fd710d
BLAKE2b-256 checksum
How to use checksums
3da1fb1a1a830299276aaaf75c9788674ad702378bf529042a1cf590a9791f89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-manylinux_2_28_x86_64.whl

Download URL speja-0.14.4-py3-none-manylinux_2_28_x86_64.whl
Size 4.4 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
d18709277dc2b932738f3d517fed824e8e0162857d85e3e82b923afe1f00e84b
BLAKE2b-256 checksum
How to use checksums
ed81c45183ac57d1fcc3aa5f38a9a900c2f5d72d5ebcf4ba3867e2ea2ec67918
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-manylinux_2_28_aarch64.whl

Download URL speja-0.14.4-py3-none-manylinux_2_28_aarch64.whl
Size 4.1 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
85ff25cca997ab4b8ef8dbb95d15f229e99f28da4aff52a0aeb2effc9bb6fcf0
BLAKE2b-256 checksum
How to use checksums
d767497693a6fae155abf8837ab96113746042d2dd4444608bfd70e3ce386172
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-macosx_11_0_arm64.whl

Download URL speja-0.14.4-py3-none-macosx_11_0_arm64.whl
Size 4.1 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
b925451a428770d77650c6b384bcddf9b2e052f8a5e277c789c8eaabf5a39ab8
BLAKE2b-256 checksum
How to use checksums
02485cbf7f779b92df81317c61e488770d8c0dec5f35e917455c5472e7c0dd25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / speja-0.14.4-py3-none-macosx_10_12_x86_64.whl

Download URL speja-0.14.4-py3-none-macosx_10_12_x86_64.whl
Size 4.3 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
a437abb4aef671f8429e0318b4f6c9cc6512721b405f5a10efb0b744abffd373
BLAKE2b-256 checksum
How to use checksums
cb862c482c9c7a9bdf422dbb2aed7c924308e946699b92e0d575828de3e2bfca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release history Release notifications | RSS feed

0.14.5

9 release files

This release

0.14.4 This release

9 release files

0.14.3

9 release files

0.14.2

9 release files

0.14.1

9 release files

0.14.0

9 release files

0.13.0

9 release 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