Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Note that the Python ecosystem itself doesn’t really adhere to Semantic Versioning. Dependency updates are automatically managed with Renovate Bot on Sentinel’s two branches to detect dependency breakage that warrants a new release/yank.

The Sentinel repository has two active branches: main and next. Releases and patches live on main, and development takes place on next. next is developed against the upstream/git versions of Amaranth and associated packages. These include, but are not limited to:

If necessary, patches from main can be forward-ported to next, and commits from next and be backported to main for a point-release. When possible, releases are made against Amaranth packages on PyPI, even though development is done against the Amaranth git repos.

Unreleased

0.1.0-beta.2 - 2026-05-09

This is the first version of Sentinel with the import name sentinel_cpu, as well as the first version which is published to PyPI. Codeberg now provides CI via its Forgejo Actions runners. No functional change to Sentinel is intended compared to 0.1.0-beta.1.

The large diff for this release is a bit misleading. The only changes to the Python source files and docs are to rename sentinel to sentinel_cpu as appropriate, and fixing line lengths as a result of said name change. CI workflow files had to be split and optimized due to Codeberg limitations (particularly the 10 minute runner limit as of 5-8-2026), but the tests themselves are the same as those that were run on Github Actions. Additionally, CI now runs new build, publish, and release steps.

Added

  • asset, build, publish (publish.yml) and release jobs for CI.

    • assets generates release notes and Verilog for Forgejo releases.

    • build creates the Python package (wheel/sdist).

    • publish.yml uploads the package to TestPyPI or PyPI, for releases.

    • release takes the outputs of the assets step and publishes them to Forgejo releases.

      • For now, the release step also uploads the wheel/sdist from the build step.

  • Use pytest-xdist to run ci-basic tests in parallel.

  • list_sby_status supports partial runs when invoked with doit -s (for use with CI).

  • Add CONTRIBUTING.md, to make clear that AI contributions are not allowed.

Changed

  • The import package name of Sentinel has changed from sentinel to sentinel_cpu.

  • ci-quickstart no longer compiles the Rust firmware/requires Rust- it was probably overkill.

  • Allow caching of pdm and yowasp for ci-quickstart job to ease runner usage.

    • I called this ci-minimal in the commit… oops!

  • Split ci-basic lint and doc building steps into their own jobs.

    • Python lint, Rust lint, and doc building are separate jobs so they can run reliably on codeberg-tiny.

    • They are now all enabled for each CI run, not just releases.

  • Move RISC-V Formal (ci-rvformal) and RISCOF (ci-riscof) jobs into their own reusable workflows.

    • Because there is currently (5-8-2026) a 10 minute runner limit on Codeberg, split the jobs into < 10 minute chunks using a matrix.

      • In contrast to what I suggest in general, running split jobs invokes doit tasks directly instead of using pdm; the equivalent pdm scripts assume you run the entire CI flow in one command. This should be fixed once runners with longer limits on Codeberg are available.

    • Both ci-rvformal and ci-riscof can manually be triggered. ci-rvformal also runs weekly via a cron script.

  • Use setup-pdm exclusively to setup Python environments in CI; it transitively calls setup-python anyway.

  • Use a Forgejo-compatible fork of setup-rust-toolchain to install Rust.

  • Start transitioning away from requiring RISC-V GCC Ubuntu package for CI; the install time can be rather variable for the limited CI time.

    • We’ve been using LLVM to link Rust code in ci-basic for a while now. In fact, requiring RISC-V GCC in ci-basic may have always been an oversight.

    • For ci-rvformal, use tinyrv instead of riscv64-unknown-elf-objdump to generate disassemblies for traces.

    • ci-riscof still requires/hardcode gcc-riscv64-unknown-elf, but RISCOF is deprecated. So that usage may be removed eventually.

Fixed

  • Specify Amaranth 0.5.8 as minimum version in pyproject.toml.

  • Use unique keys for each CI job for pdm caching to avoid thrashing.

  • Update sent_latest_tag, which in turn fixes the outdated Sentinel version that was dynamically substituted inside last release’s docs.

  • list_sby_status correctly handles an ERROR return code from sby.

  • Specify that doc and examples groups need to be installed for building docs in conf.py PackageNotFoundError handler. Also do a minimal check for the examples group.

0.1.0-beta.1 - 2026-04-22

This is the last version with the import name sentinel. Future releases will be importable under sentinel_cpu. See #88 for more information.

Amaranth 0.5.8 is now the minimum required version to use Sentinel, either directly from this repo or as a dependency. Sentinel no longer requires amaranth-soc, so that future releases can be uploaded to PyPI. This will last until amaranth-soc gets a PyPI release. If amaranth-soc is available in your Python environment, Sentinel will use it.

Due to issues with Github, I’ve decided to migrate the repo to Codeberg. Although functional, I did not intend to cut a release in this state. However, I’ve accumulated a number of changes since the last release, so I might as well start with a fresh slate for Codeberg. No Codeberg-specific changes are in this release, aside from updating doc URLs.

Minimal Forgejo CI is functional in a separate branch. I’m holding off merging it until the next release as part of Codeberg-specific changes; this will make writing the CHANGELOG and diffing easier for me. I’ve manually ensured all test suites pass before releasing. References to CI in this release refer to Github Actions CI.

Added

  • Parallel support in RISCOF test suite.

  • Run the relevant privileged tests of RISCOF test suite. Sentinel passes!

    • This required sending a patch to upstream RISCOF!

  • Add RISC-V Formal tests for experimenting with how IRQs affect CSRs (via the irq group).

    • Presently, these are not run by default, as from RISC-V Formal’s doesn’t really handle interrupts; interrupts are not exceptions, and the PC checks are too strict for non-exceptions.

  • Add pre-generated Verilog for all tagged versions via Releases.

  • Skip resource-intensive CI if only documentation was changed for a commit.

  • Add turnkey docs for programmers who want to simply use Sentinel without exploring the source code.

Changed

  • Update all test submodules to most recent commits as of mid Janurary 2025. Also recompile SAIL.

    • Mostly symbolic, as the submodules didn’t really change in a way that affect Sentinel, save for the aforementioned RISCOF patch.

  • Microcode improvements:

    • All shift ops have latency reduced by 1 cycle.

    • Branches are predicted not taken, and branches taken are predicted to have no exceptions. End result is latency and inverse-throughput of branch instructions decrease both by 1 CPI.

    • Predict that exceptions don’t occur for JAL/JALR. Latency decreases by 2 CPI, inverse-throughput decreases by 1 CPI for JAL/JALR.

    • Rename CONDTEST_ALU_CMP_FAILED to the more-general CONDTEST_ALU_ZERO. We can always bring it back later if needed.

  • Attempt to swap out AttoSoC peripherals with those available in amaranth-soc or amaranth-stdio.

    • Swapped peripherals include: WishboneSRAM

    • No functional changes, just better for the demo to use the Amaranth support packages when possible to facilitate good practice.

    • Introduction of WishboneSRAM requires ~150 extra LUTs, so this change was reverted until deciding what to do with iCEStick.

  • Optimize OP_IMM decoding (resource usage) using z3.

  • Generally refactor AttoSoC to look nicer as part of fixing the inline objcopy implementation.

    • Future work on examples may split out common functionality in AttoSoC into multiple files.

  • When building a source dist, try to match the contents of tarballs/zip files generated by Github, Codeberg, or other forges.

    • I believe forge tarballs/zips (ie. the files generated by default for tags and releases) are generated from a shallow clone, but I don’t remember for sure.

    • The idea is I want to save server space, save time generating the sdists, and to minimize confusion from having both sdists and forge source tarballs/zips. However, this is a best-effort basis! E.g. an sdist will have access to version info that a plain-old tarball/zip does not.

  • Polyfill amaranth-soc usage (e.g. wishbone.Signature) using amaranth alone.

    • sentinel.formal.FormalTop now gets its signature by inspecting sentinel.top.Top’s signature.

  • sentinel.gen.generate has been renamed to sentinel.gen.cli, as part of rewriting the whole sentinel.gen module.

  • Point all [git+]https://github.com/cr1901/sentinel/ URLs in docs to [git+]https://codeberg.org/cr1901/sentinel/.

Fixed

  • Fix a microcode typo that caused the CSRRCI instruction to be clear bits when it shouldn’t have.

    • Add handwritten test case to CI, since upstream and RISCOF don’t test interrupts, and RISC-V Formal makes assumptions that don’t work well with interrupts.

    • Open m5meta issue, as future typos can be caught at assemble-time.

  • Improve inline objcopy implementation in AttoSoC example to work for ELF files with more complex layouts.

    • This fix is a precaution; the attosoc Rust firmware worked just with the old implementation which assumes loadable segments are all adjacent.

  • Increase the UART speed to reduce bit timer width; demo bitstream fits into 1280 LUTs again!

  • Documentation now makes clear that MIP.MEIP bit is read-only.

  • Change checks.cfg paths so that paths parse correctly when running RISC-V Formal with a Python compiled by MSVC.

  • Pytest tests now ignore RISCOF dirs when RISCOF is checked out.

Removed

  • The constant -1 is no longer produced by the constant generator; it can easily be synthesized by subtracting 1 on the ALU’s B input from 0 on the ALU’s A input.

0.1.0-beta - 2025-01-12

Amaranth 0.5.4 is now the minimum required version to use Sentinel, either directly from this repo or as a dependency.

Sentinel still tracks upstream amaranth-soc. For the time being, use Sentinel with caution outside of environments without lockfiles, and prefer installing Sentinel within a PDM environment:

pdm add sentinel@git+https://codeberg.org/cr1901/sentinel@main

I also cannot do a PyPI release for the same reason.

Between March and September 2024, I stopped being able to consistently fit AttoSoC onto IceStick; the demo overflows between 1280-1300 ICESTORM_LCs (maximum 1280 on device). According to bisect, the changes introduced by Amaranth commit 1d2b9c3 pessimized AttoSoC just enough to not fit. However, right now (2024-09-26) releases are not blocked on the demo fitting on IceStick. Assume the IceStick demo is unreliable in this release. I will look into this later.

Added

  • Add ruff linter to dev dependencies and CI.

    • Releases require linting to pass, including this release!

  • Set up lint rules for Rust/rustdoc and Ruff.

    • This release and future ones will require passing a lint check for code and docstrings in CI. Non-release runs won’t run the lint step.

    • lint PDM composite script can optionally take arguments, like -e to do Rust lint step on error in Python lint step.

    • Basic fixes can be applied using the lint-fix PDM script.

    • sentinel-rt is preemptively set up for cargo fix to work.

    • doc PDM script can optionally take arguments, like -n to run sphinx-build in nitpicky mode. This catches more broken yet benign cross-refs than the doc-auto script will. In the case of Amaranth IP in particular, several broken cross-refs are expected due to how Components are documented.

  • Test only installing production dependencies, importing, and Verilog generation in CI.

  • Documentation has been written! It is hosted on ReadTheDocs.

    • A few doctests have been added for good measure.

    • ReadTheDocs handles building docs. However, this release and future ones require docs to build and doctests to pass in CI. Non-release runs won’t run the docs step.

    • Microcode assembly file is not handled for correctness by sphinx. Out of date/misleading comments were fixed.

      If microcode and sphinx documentation don’t match, sphinx docs take priority unless otherwise noted.

  • YoWASP support for importing Top, Verilog generation and in-tree demos.

    • Minimal prodouction dependencies/Quickstart is tested in CI using YoWASP.

  • Allow firmware override/random BRAM content generation for AttoSoC demo designs which are generated on a remote machine.

  • Add AttoSoC demo support for Arty A7 35T, Cmod S7, and iCEBreaker v1.0 boards.

Changed

  • All simulations have been ported to adhere to RFC #36 and #62.

    • This resulted in more code-sharing between the handwritten and upstream pytest simulations, as well as the RISCOF framework tests, such as a simulated memory process.

      Additionally, except for the appropriately-named test_soc file, the AttoSoC example is no longer used in tests!

  • Use pytest-amaranth-sim plugin for Amaranth-specific fixtures.

  • AttoSoC example was ported to adhere to #70.

  • Current OSS CAD Suite version for CI is 2024-11-01.

  • Example Rust firmware greatly improved- a Rule 110 visualization over serial port suggested by @mcclure!

  • Various refactors that do not change code functionality:

    • Rearrange top-level connections based on purpose.

    • Data alignment logic was split into Components.

    • An attempt was made to hoist out muxed inputs to the Datapath. However, this did not optimize well. The Component- DataPathSrcMux- is kept around as dead code in case it optimizes better in future versions.

    • Move ALU source muxes into their own Components.

    • Insn class to serve as the moral equivalent of an Amaranth View for RISC-V instructions.

    • Decoder was split into several Components. In fact, the decoder was effectively rewritten; the large Switch statement of the previous versions was unreadable. The split components include:

      • CSR Access Control

      • Exception Control

      • Mapping ROM

      • Immediate Generation

      AFAIR, none of the above refactors changed LUT usage meaningfully.

    • Additionally, the Control Unit’s Signature was overhauled.

  • Manually specify the set of files to include in source dist. Specifically, exclude submodules and extra test files, and include config files, examples, and sentinel-rt.

  • Tweak Rust firmware DoIt tasks to allow choosing bus type, call cargo directly instead of invoking pdm again.

  • All UCodeROM fields are now verified against microcode file before generating a Signature, not just enums.

    • Rename UCodeROM enum_map constructor parameter to field_map.

  • Lots of small Ruff and Rust/rustdoc lints applied, so that CI passes :).

    • Some files are ignored for lint. See pyproject.toml for justifications.

  • Development/style guidelines have been refined, and changes made to the reflect them:

    • Component Signatures are now generated using annotations at the class level when possible; previously, they were instance attributes. This should have no functional change.

    • Nested classes within Insn class are public.

    • Most functions of sentinel.gen are private.

    • Nest Signature object bindings into the objects that “own” them. This paves the way to add a few more Signature object bindings next release.

  • Control unit Signature has been greatly improved; it uses nested Signature Members to logically organize microcode signals by purpose.

Fixed

  • Use full git hash a7fa902a5f70c7d53a654d850f745e36821fbb78 for logLUTs benchmarking dependency (got weird errors trying to get PDM to download it using the short hash as of Sept. 2024).

  • Fixed UnusedElaboratable warning in test_ucode_layout_gen test (thanks @kivikkak!)

  • Sentinel uses amaranth_soc.wishbone, but this wasn’t reflected in production dependencies.

  • Fix license field in `pyproject.toml. It was always meant to be BSD-2-Clause, but I just accepted the defaults when originally initializing the project with PDM :).

  • Remove regfile module. This was accidentally committed before initial release and never used.

  • Do not use back-to-back write/read optimization for store instructions until it’s clear to me that it’s within spec.

  • Various fixes where registers/signals did not get appropriate values on non-power-on resets:

    • PC would either be initialized to 0 or 4, depending on instruction.

    • MCAUSE register was not cleared to 0, as privileged spec requires for implementations that do not distinguish reset conditions.

    • Wishbone CYC and STB would remain asserted after reset if the reset happened in the middle of a bus cycle; WB spec requires CYC and STB to deassert upon SYSCON reset.

    • Init code after reset now requires one extra cycle (5 cycles total) before fetching the first instruction from the user program.

    • Tests for these cases have been added to CI.

  • doit tasks would sometimes use the wrong Python interpreter; use sys.exectuable to indicate “this interpreter” as recommended instead.

Removed

  • Remove special Rust PDM scripts by using .config/cargo.toml.

  • Remove the Simulator fixture and --vcds option to pytest as provided by this package. Instead, use equivalent fixtures/options provided by pytest-amaranth-sim.

  • Flake8 linting has been completely replaced with Ruff. We enable preview mode to take advantage of unstable rules that were provided by flake8.

0.1.0-alpha.1 - 2024-03-12

First release after the next/main split.

Added

  • Test gateware generation in CI.

  • Updates available for dependencies are tracked with Renovate Bot.

    • Applies to both main and next.

  • The yosys version for Verilog/RTLIL/gateware generation is also tracked in CI using a new workflow.

  • Demo SoC example can optionally use peripherals whose registers are implemented using the amaranth-soc CSR bus.

    • Only fits on HX8K Evaluation board at present.

    • Peripherals are at different addresses when using the CSR bus; the Rust firmware can detect which version of the gateware is loaded by querying the MIP register after reset.

  • Demo prints addresses of peripherals in table format.

  • Rust Demo SoC can be simulated as part of Pytest tests.

    • Disabled by default, pass --runsoc to pytest to enabled.

  • Implement remote Demo SoC builds using paramiko.

  • Add usage as a Python dependency in README.md.

Changed

  • Replace ELF generator with pyelftools.

    • This simplifies the inline objcopy implementation.

  • GPIO peripheral in SoC demo has input, output, and output-enable ports now.

  • Use a dynamic Python version (no releases ever saw the manual behavior).

  • dodo.py dependency graph has been cleaned up a bit.

  • Start improving Signatures used throughout Sentinel core.

    • Mostly limited to Decoder, ExceptionRouter, and ALU for now.

    • Many signals remain to be wired up to formal harness via Signatures, rather that the formal harness reaching into Sentinel.

  • Python, Rust, and CI dependencies have been updated to most recent versions.

  • Rust firmware development DoIt tasks are no longer targeted to IceStick only.

    • HX8K development board support is up to parity with IceStick.

Fixed

  • All Amaranth deprecations for the upcoming version 0.5 have been addressed, up to commit 715a8d4.

  • Correct gen usage outside pdm in README.md.

  • dodo.py no longer errors when removing directory trees that don’t exist.

    • Removing dirs is required as part of RISCOF test cleanup.

  • Load-bearing optimization implemented in 39005c1 that saves ~30 LUTs in SoC demo.

    • Check to see if this can be removed/is no longer necessary.

0.1.0-alpha - 2023-11-29

Initial release. This is a retroactive release, created just before the next/main split.

PDM should be used for interacting with this repo; type pdm run --list for a list of commands. pdm will defer to DoIt for complex tasks such as orchestrating RISC-V Formal.

Pytest is used for basic testing, and Flake8 is used for linting. Due to code cleanups required, as well as some tooling still being in flux, no documentation beyond the CHANGELOG.md and README.md is provided yet.

Due to git dependencies, this release is only usable within the pdm environment. Specifically, the SoC example, tests, and Verilog generation works, but using Sentinel as a dependency of another Python/Amaranth/pdm project does not work.

Added

  • Working SoC example for Lattice IceStick and iCE40-HX8K Breakout Board.

    • Includes custom Amaranth peripherals and a contrived Rust firmware.

      Run using pdm demo (Assembly firmware) or pdm demo-rust (Rust firmware).

    • Demo is using special options to yosys to make Sentinel fit onto IceStick.

    • Microcode written using the m5meta microcode assembler, version 1.x.

      • An upcoming version 2.x of m5meta is expected in the moderate-near future. I plan to rewrite the microcode then, and the rewrite will not be compatible with version 1.x.

  • Working RISC-V soft-core implementing the RV32I_Zcsr specification.

    • Core passes the RISC-V Formal verification tests, as well as the tests used by RISC-V International as part of the RISCOF framework.

  • Bespoke test suite with my own custom tests and pytest fixtures for simulating Sentinel with Amaranth’s Python simulator.

    • Many of the pytest test come from an older test suite no-longer used by RISC-V International. I feel they are fine as a first layer of tests to detect immediate breakage of Sentinel.

  • CI using Github Actions tests using pytest, RISC-V Formal, RISCOF, and demo synthesis every push and PR.

    • The demo test is allowed to fail due to lack of space on Icestick due to different compilers optimizing yosys slightly differently. See #2.

  • A nice logo by Tokino Kei :).