From 5ee91908940e1b440ac04ed65d3b0e81fa178edd Mon Sep 17 00:00:00 2001
From: Jeremy Lau <30300826+fdxmw@users.noreply.github.com>
Date: Tue, 23 Jun 2026 16:19:43 -0700
Subject: [PATCH 01/12] Deploy GitHub pages
(https://ucsbarchlab.github.io/PyRTL) from GitHub actions with Sphinx.
This replaces the old `gh-pages` branch workflow, and generates the HTML with
Sphinx so we don't have to manually edit HTML. GitHub Pages' `index.html` is
now generated from `www/index.rst`, which is much easier to edit. Sphinx
automatically runs from GitHub actions when changes are pushed to `www/`.
Also:
* Added simulation scripts to all GitHub pages code samples.
* Moved the larger code samples to external `.py` files so `ruff` can
auto-format them and check them for lint.
* Added many type hints.
* Added many PyRTL documentation and example links.
* Fixed many typos.
* Upgraded some GitHub actions workflows.
* Remove Sphinx version information and copyright from documentation footers.
---
.github/workflows/pages-deploy.yml | 34 +++
.github/workflows/python-release.yml | 2 +-
.github/workflows/python-test.yml | 2 +-
docs/conf.py | 5 +-
justfile | 8 +-
pyproject.toml | 1 +
uv.lock | 37 ++-
www/adder.py | 37 +++
www/conf.py | 43 ++++
www/fir.py | 22 ++
www/gcd.py | 39 +++
www/index.rst | 363 +++++++++++++++++++++++++++
www/maxn.py | 22 ++
www/mul.py | 23 ++
www/ripple-carry.py | 34 +++
15 files changed, 666 insertions(+), 6 deletions(-)
create mode 100644 .github/workflows/pages-deploy.yml
create mode 100644 www/adder.py
create mode 100644 www/conf.py
create mode 100644 www/fir.py
create mode 100644 www/gcd.py
create mode 100644 www/index.rst
create mode 100644 www/maxn.py
create mode 100644 www/mul.py
create mode 100644 www/ripple-carry.py
diff --git a/.github/workflows/pages-deploy.yml b/.github/workflows/pages-deploy.yml
new file mode 100644
index 00000000..e46f6007
--- /dev/null
+++ b/.github/workflows/pages-deploy.yml
@@ -0,0 +1,34 @@
+name: Deploy to github.io
+
+on:
+ push:
+ paths:
+ - www/**
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - uses: astral-sh/setup-uv@v6
+ - name: Run Sphinx
+ run: uv run just www
+ - name: Upload static files as artifact
+ id: deployment
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: www/_build/html
+
+ deploy:
+ needs: build
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml
index 58a97bb2..04c87c97 100644
--- a/.github/workflows/python-release.yml
+++ b/.github/workflows/python-release.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml
index e1a0cad7..82a41c39 100644
--- a/.github/workflows/python-test.yml
+++ b/.github/workflows/python-test.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
diff --git a/docs/conf.py b/docs/conf.py
index c9d4bc56..fab1848c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -23,8 +23,6 @@
# -- Project information -----------------------------------------------------
project = "PyRTL"
-copyright = "2026, Timothy Sherwood"
-author = "Timothy Sherwood"
# -- General configuration ---------------------------------------------------
@@ -96,6 +94,9 @@
],
}
html_logo = "brand/pyrtl_logo.png"
+html_show_sphinx = False
+html_show_copyright = False
+html_show_sourcelink = False
# Force a light blue background color for inheritance-diagrams. The default is
# transparent, which does not work well with Furo's dark mode.
diff --git a/justfile b/justfile
index 406833f3..f290fce6 100644
--- a/justfile
+++ b/justfile
@@ -1,7 +1,7 @@
# PyRTL uses `just` instead of `make` because:
# * `make` is not installed by default on Windows.
# * `uv` can install `just` on all supported platforms from PyPI.
-presubmit: tests docs
+presubmit: tests docs www
tests:
# Run `pytest` with the latest version of Python supported by PyRTL,
@@ -36,3 +36,9 @@ docs:
#
# Output: docs/_build/html/index.html
uv run sphinx-build -M html docs/ docs/_build
+
+www:
+ # Run `sphinx-build` to generate github.io webpage.
+ #
+ # Output: www/_build/html/index.html
+ uv run sphinx-build -M html www/ www/_build
diff --git a/pyproject.toml b/pyproject.toml
index c42d638a..8127d23c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -127,4 +127,5 @@ dev = [
"sphinx>=7.4.7",
"sphinx-autodoc-typehints>=2.3.0",
"sphinx-copybutton>=0.5.2",
+ "sphinx-design>=0.6.1",
]
diff --git a/uv.lock b/uv.lock
index 2dfe81b7..cac79b06 100644
--- a/uv.lock
+++ b/uv.lock
@@ -321,7 +321,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
@@ -553,6 +553,8 @@ dev = [
{ name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "sphinx-autodoc-typehints", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "sphinx-copybutton" },
+ { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
[package.metadata]
@@ -575,6 +577,7 @@ dev = [
{ name = "sphinx", specifier = ">=7.4.7" },
{ name = "sphinx-autodoc-typehints", specifier = ">=2.3.0" },
{ name = "sphinx-copybutton", specifier = ">=0.5.2" },
+ { name = "sphinx-design", specifier = ">=0.6.1" },
]
[[package]]
@@ -879,6 +882,38 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" },
]
+[[package]]
+name = "sphinx-design"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+dependencies = [
+ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" },
+]
+
+[[package]]
+name = "sphinx-design"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.12'",
+ "python_full_version == '3.11.*'",
+]
+dependencies = [
+ { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
+ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350, upload-time = "2026-01-19T13:12:51.077Z" },
+]
+
[[package]]
name = "sphinxcontrib-applehelp"
version = "2.0.0"
diff --git a/www/adder.py b/www/adder.py
new file mode 100644
index 00000000..4b17638a
--- /dev/null
+++ b/www/adder.py
@@ -0,0 +1,37 @@
+import pyrtl
+
+
+def fa(
+ x: pyrtl.WireVector, y: pyrtl.WireVector, cin: pyrtl.WireVector
+) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
+ """Full adder."""
+ sum = x ^ y ^ cin
+ cout = x & y | y & cin | x & cin
+ return sum, cout
+
+
+def adder(
+ a: pyrtl.WireVector, b: pyrtl.WireVector, cin: pyrtl.WireVector
+) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
+ """n-bit ripple carry adder with carry in and carry out."""
+ a, b = pyrtl.match_bitwidth(a, b)
+
+ sum = [None] * a.bitwidth
+ for i in range(a.bitwidth):
+ sum[i], cout = fa(a[i], b[i], cin)
+ cin = cout
+
+ full_sum = pyrtl.concat_list(sum)
+ return full_sum, cout
+
+
+a = pyrtl.Input(name="a", bitwidth=4)
+b = pyrtl.Input(name="b", bitwidth=4)
+sum = pyrtl.Output(name="sum", bitwidth=8)
+
+sum_, cout_ = adder(a, b, pyrtl.Const(0))
+sum <<= sum_
+
+sim = pyrtl.Simulation()
+sim.step_multiple({"a": [1, 2, 3], "b": [2, 3, 4]})
+sim.tracer.render_trace()
diff --git a/www/conf.py b/www/conf.py
new file mode 100644
index 00000000..e8bb7ec4
--- /dev/null
+++ b/www/conf.py
@@ -0,0 +1,43 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+
+project = "PyRTL"
+
+# -- General configuration ---------------------------------------------------
+
+master_doc = "index"
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ["sphinx.ext.intersphinx", "sphinx_copybutton", "sphinx_design"]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# Enable links to Python standard library classes (str, list, dict, etc).
+intersphinx_mapping = {
+ "python": ("https://docs.python.org/3", None),
+ "pyrtl": ("https://pyrtl.readthedocs.io/en/latest/", None),
+}
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_baseurl = "https://ucsbarchlab.github.io/PyRTL"
+html_theme = "alabaster"
+html_theme_options = {"nosidebar": True}
+html_show_sphinx = False
+html_show_copyright = False
+html_show_sourcelink = False
diff --git a/www/fir.py b/www/fir.py
new file mode 100644
index 00000000..81d72e02
--- /dev/null
+++ b/www/fir.py
@@ -0,0 +1,22 @@
+import pyrtl
+
+
+def fir(x: pyrtl.WireVector, bs: list[int]):
+ rwidth = x.bitwidth # Bitwidth of the registers.
+ ntaps = len(bs) # Number of coefficients.
+
+ zs = [x] + [pyrtl.Register(rwidth) for _ in range(ntaps - 1)]
+ for i in range(1, ntaps):
+ zs[i].next <<= zs[i - 1]
+
+ # Produce the final sum of products.
+ return sum(z * b for z, b in zip(zs, bs, strict=True))
+
+
+x = pyrtl.Input(name="x", bitwidth=8)
+y = pyrtl.Output(name="y", bitwidth=8)
+y <<= fir(x, bs=[0, 1])
+
+sim = pyrtl.Simulation()
+sim.step_multiple({"x": [0, 9, 18, 8, 17, 7, 16, 6, 15, 5]})
+sim.tracer.render_trace()
diff --git a/www/gcd.py b/www/gcd.py
new file mode 100644
index 00000000..b36deeb1
--- /dev/null
+++ b/www/gcd.py
@@ -0,0 +1,39 @@
+import pyrtl
+
+
+def gcd(
+ a: pyrtl.WireVector, b: pyrtl.WireVector, begin: pyrtl.WireVector
+) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
+ x = pyrtl.Register(bitwidth=len(a))
+ y = pyrtl.Register(bitwidth=len(b))
+ done = pyrtl.WireVector(bitwidth=1)
+
+ with pyrtl.conditional_assignment:
+ with begin:
+ x.next |= a
+ y.next |= b
+ with x > y:
+ x.next |= x - y
+ with y > x:
+ y.next |= y - x
+ with pyrtl.otherwise:
+ done |= True
+ return x, done
+
+
+a = pyrtl.Input(name="a", bitwidth=8)
+b = pyrtl.Input(name="b", bitwidth=8)
+begin = pyrtl.Input(name="begin", bitwidth=1)
+
+x = pyrtl.Output(name="x", bitwidth=8)
+done = pyrtl.Output(name="done", bitwidth=1)
+
+x_, done_ = gcd(a, b, begin)
+x <<= x_
+done <<= done_
+
+sim = pyrtl.Simulation()
+sim.step({"a": 12, "b": 9, "begin": True})
+while not sim.inspect("done"):
+ sim.step({"a": 0, "b": 0, "begin": False})
+sim.tracer.render_trace()
diff --git a/www/index.rst b/www/index.rst
new file mode 100644
index 00000000..aba07c61
--- /dev/null
+++ b/www/index.rst
@@ -0,0 +1,363 @@
+:hide-toc:
+
+.. div:: sd-text-center sd-text-light
+ :style: background-image: linear-gradient(180deg, #2980B9, #3a4e5c, #3a4e5c);
+
+ .. image:: ../docs/brand/pyrtl_logo.png
+
+ register-transfer-level hardware design and simulation
+
+ .. grid:: 2 2 4 4
+ :gutter: 0
+
+ .. grid-item::
+
+ .. button-link:: http://pyrtl.readthedocs.org/
+ :color: light
+ :outline:
+
+ Read The Docs
+
+ .. grid-item::
+
+ .. button-link:: https://github.com/UCSBarchlab/PyRTL/tree/development/examples
+ :color: light
+ :outline:
+
+ See Examples
+
+ .. grid-item::
+
+ .. button-link:: https://github.com/UCSBarchlab/PyRTL
+ :color: light
+ :outline:
+
+ View on GitHub
+
+ .. grid-item::
+
+ .. button-link:: https://mybinder.org/v2/gh/UCSBarchlab/PyRTL/development?filepath=%2Fipynb-examples%2F
+ :color: light
+ :outline:
+
+ Run Notebook
+
+.. raw:: html
+
+
+
+PyRTL
+=====
+
+.. raw:: html
+
+
+
+Getting Started
+---------------
+
+``pip install pyrtl``
+
+
+PyRTL Features
+--------------
+
+PyRTL provides a collection of classes for Pythonic `register-transfer level
+`_ design, simulation,
+tracing, and testing suitable for teaching and research. Simplicity, usability,
+clarity, and extensibility rather than performance or optimization is the
+overarching goal. Features include:
+
+* Elaboration-through-execution, meaning all of Python can be used including
+ introspection
+* Design, instantiate, and simulate all in one file and without leaving Python
+* Export to, or import from, common HDLs (BLIF-in, Verilog-out currently
+ supported)
+* Examine execution with waveforms on the terminal or export to a .vcd as
+ projects scale
+* Elaboration, synthesis, and basic optimizations all included
+* Small and well-defined internal core structure means writing new transforms
+ is easier
+* Batteries included means many useful components are already available and
+ more are coming every week
+
+**New in 1.0.0:** The :mod:`~pyrtl.rtllib.float` module generates floating
+point hardware!
+
+Here are some simple examples of PyRTL in action. These examples implement the
+same functionality as those highlighted in the wonderful related work `Chisel
+`_, which in turn allows us to see the stylistic
+differences between the approaches.
+
+.. tab-set::
+
+ .. tab-item:: FIR
+
+ A finite impulse response filter: ``fir`` generates a sequential circuit
+ that accepts inputs ``x`` and a list of coefficients ``bs``. From the
+ `Wikipedia FIR description
+ `_, the list
+ ``zs`` is the registers required to implement the delay. ``fir`` returns
+ an output ``y`` which is the resulting sum of products and is valid every
+ cycle (since the design is naturally fully pipelined). The code below
+ provides everything needed to instantiate, simulate, and visualize the
+ resulting design.
+
+ .. literalinclude:: fir.py
+
+ .. tab-item:: GCD
+
+ A greatest common denominator calculator: ``gcd`` generates a sequential
+ circuit that saves inputs ``a`` and ``b`` when ``begin`` goes high, and
+ then, while ``begin`` is low, calculates the GCD through iterative
+ subtraction. The function returns two :class:`WireVectors
+ `:, one which holds the GCD when the computation is
+ ``done``, and the other which is a boolean ``done`` signal.
+
+ .. literalinclude:: gcd.py
+
+ .. tab-item:: MaxN
+
+ ``max_n`` generates hardware that identifies the largest of N input
+ values. This example makes use of Python's `notation for handling
+ multiple inputs
+ `_
+ by packing them into a :class:`list`. It also demonstrates that the full
+ power of Python is available to you in PyRTL, including functional tools
+ like :func:`~functools.reduce`, which is used to chain together multiple
+ ``max_2`` elements into a bigger ``max_n``.
+
+ .. literalinclude:: maxn.py
+
+ .. tab-item:: Mul
+
+ ``mul`` generates a small 4 x 4 multiplier with a simple table lookup.
+ The first two lines simply check that the inputs are each 4-bits wide.
+ ``romdata`` is a Python function that calculates the values we want
+ stored in the ROM, as a function of the ROM address.
+ :class:`~pyrtl.RomBlock` automatically initializes the ROM with values
+ computed by ``romdata``. The generated hardware simply :func:`concats
+ ` the two 4-bit inputs into an 8-bit ROM address and
+ returns the value stored in the ROM at that address.
+
+ .. literalinclude:: mul.py
+
+ .. tab-item:: Adder
+
+ The classic ripple-carry adder: ``adder`` generates a ripple carry adder
+ of arbitrary length including both carry in and carry out. The `full
+ adder `_
+ (``fa``) takes 1-bit inputs and produces 1-bit outputs. We iteratively
+ create full adders and link the carry in of each new full adder to the
+ carry out of the last full adder. ``sum`` is a Python :class:`list` that
+ keeps track of the wires carrying the sum bits. The final ``full_sum`` is
+ produced by concatenating the wires in ``sum`` with
+ :func:`~pyrtl.concat_list`.
+
+ .. literalinclude:: adder.py
+
+PyRTL can also produce visualizations of your design with
+:func:`~pyrtl.block_to_svg`, such as this graph of the GCD circuit above:
+
+.. image:: ../docs/images/gcd-graph.png
+
+The 10,000 Foot Overview
+------------------------
+
+At a high level, PyRTL builds hardware that you `explicitly define`. If you are
+looking for a tool to take your random Python code and turn it into hardware,
+you will have to look elsewhere: this is `not` `HLS
+`_. Instead, PyRTL helps
+you concisely and precisely describe a digital hardware structure, which you
+already have worked out in detail, in Python.
+
+PyRTL restricts you to a set of reasonable digital designs practices: the clock
+and resets are implicit, block memories are synchronous by default, there are
+no "undriven" states, and un-registered feedback loops are not allowed. Instead
+of worrying about these "analog-ish" tricks that are horrible ideas in modern
+processes anyways, PyRTL lets you treat hardware design like a software
+problem: build recursive hardware, write introspective containers, and have fun
+building digital designs again!
+
+To the user it provides a set of Python classes that allow them to Pythonically
+express their hardware designs. For example, with :class:`~pyrtl.WireVector`
+you get a structure that acts very much like a Python list of 1-bit wires, so
+that ``mywire[0:-1]`` selects everything except the most-significant-bit. Of
+course you can add, subtract, and multiply these :class:`WireVectors
+` or concat multiple bit-vectors end-to-end as well.
+
+You can even put :class:`WireVectors ` in Python collections
+and process them in bulk. For example, if ``x`` is a :class:`list` of
+:class:`WireVectors `, and you want to multiply each of them
+by 2 and sum them into a :class:`~pyrtl.WireVector` ``y``::
+
+ y = sum([elem * 2 for elem in x])
+
+Hardware comprehensions are surprisingly useful. We'll cover an example in more
+detail below, but if you just want to play around with PyRTL `try Jupyter
+Notebooks on any of our examples on MyBinder
+`_.
+
+Hello N-bit Ripple-Carry Adder!
+-------------------------------
+
+While adders are a builtin primitive for PyRTL, most people doing RTL are
+familiar with the idea of a `Ripple-Carry Adder
+`_ and so it is useful to
+see how you might express one in PyRTL. Rather than the typical `Verilog
+introduction to fixed 4-bit adders
+`_, let's go ahead and build an
+`arbitrary` bitwidth adder.
+
+.. literalinclude:: ripple-carry.py
+
+The code above includes an adder generator with Python-style slices on wires
+(``ripple_add``), an instantiation of a :class:`~pyrtl.Register` (used as a
+counter with ``ripple_add``), and all the code needed to simulate the design,
+generate a waveform, and render it to the terminal. The way this particular
+code works is described more in `Example 2
+`_.
+When you run it, it should look like this (you can see the counter going from 0
+to 7 and repeating):
+
+.. image:: ../docs/screenshots/pyrtl-counter.png
+
+A Few Gotchas
+-------------
+
+While Python is an amazing language, DSLs in Python are always forced to make a
+few compromises which can sometimes catch users in some unexpected ways. Watch
+out for these "somewhat surprising features":
+
+* PyRTL never uses any of the "in-place arithmetic assignments" such as ``+=``
+ or ``&=`` in the traditional ways. Instead only ``<<=`` and ``|=`` are
+ defined and they are used for wire-assignment and conditional-wire-assignment
+ respectively (more on both of these in `Example 3
+ `_).
+
+
+ If you declare ``x = WireVector(bitwidth=3)`` and ``y =
+ WireVector(bitwidth=5)``, how do you assign ``x`` the value of ``y + 1``? If
+ you do ``x = y + 1`` that will replace the old definition of ``x`` entirely.
+ Instead you need to write ``x <<= y + 1`` which you can read as "``x`` gets
+ its value from ``y + 1``".
+
+* The example above also shows off another aspect of PyRTL. The
+ :attr:`~pyrtl.WireVector.bitwidth` of ``y`` is 5. The
+ :attr:`~pyrtl.WireVector.bitwidth` of ``y + 1`` is actually 6 (PyRTL infers
+ this automatically). But then when you assign ``x <<= y + 1`` you are taking a
+ 6-bit value and assigning it to a 3-bit value. This is completely legal, and
+ the value will be :meth:`truncated `, so only the
+ least significant bits will be assigned. Mind your bitwidths.
+
+* PyRTL provides some handy functions on :class:`WireVectors
+ `, including ``==`` and ``<`` which evaluate to a new
+ :class:`~pyrtl.WireVector` with :attr:`~pyrtl.WireVector.bitwidth` 1 to hold
+ the result of the comparison. The bitwise operators ``&``, ``|``, ``~`` and
+ ``^`` are also defined (however logic operations such as ``and`` and ``not``
+ are not defined). A really tricky gotcha happens when you start combining
+ these operators. Consider::
+
+ doit = ready & state == 3
+
+ In Python, the bitwise ``&`` operator has higher precedence than ``==``, thus Python
+ parses this as::
+
+ doit = (ready & state) == 3
+
+ which is probably not what you intended! Make sure to use parentheses when
+ using comparisons with logic operations to be clear::
+
+ doit = ready & (state == 3)
+
+* In PyRTL, all :class:`WireVectors ` are unsigned. For
+ example, if you do a comparison with ``<``, that comparison will be
+ `unsigned`. You must explicitly call functions like :func:`~pyrtl.signed_lt`
+ for signed comparisons. Similarly, if you pass a :class:`~pyrtl.WireVector`
+ to a function that requires more bits that you have provided, the
+ :class:`~pyrtl.WireVector` will be zero-extended by default. You must
+ explicitly call :meth:`~pyrtl.WireVector.sign_extended` to sign-extend.
+ `Example 1.1
+ `_
+ shows more examples of how to do signed arithmetic in PyRTL.
+
+Related Projects
+----------------
+
+`Amaranth (previously nMigen) `_
+ Another Python hardware project providing an open-source toolchain that has
+ a lot of wonderful stuff for working with FPGAs in particular. It has
+ support for evaluation board definitions, a System-on-Chip toolkit, and
+ more. I think it has a similar philosophy of trying to be easy to learn and
+ use and simplify the design of complex hardware with reusable components.
+ Amaranth (at the time of writing) has much better support on the back end
+ for a variety of real devices and low level stuff like managing clock
+ domains, but I think PyRTL provides some value in getting going right in
+ the command line and how it handles memories etc. I would be eager to see
+ the power of these tools combined in some way!
+
+`Chisel `_
+ A project with similar goals to PyRTL but based on Scala instead of Python.
+ Scala provides some very helpful embedded language features and a rich type
+ system. Chisel is (like PyRTL) a elaborate-through-execution hardware
+ design language. With support for signed types, named hierarchies of wires
+ useful for hardware protocols, and a neat control structure call ``when``
+ that inspired our :data:`~pyrtl.conditional_assignment` contexts, Chisel is
+ a powerful tool used in some great research projects including RISC-V.
+ Unlike Chisel, PyRTL has concentrated on a simple to use and complete tool
+ chain which is useful for instructional projects, and provides a clearly
+ defined and relatively easy-to-manipulate intermediate structure in the
+ class :class:`~pyrtl.Block` which allows rapid prototyping of hardware
+ analysis routines which can then be co-designed with the architecture.
+
+`SpinalHDL `_
+ A different approach to HDL in Scala, very much aligned with the way PyRTL
+ is built. Invented independently, it is neat to see the convergent
+ evolution which, I think, points to something deeper about hardware design.
+ It has a lot of support and really well thought out structures.
+
+`MyHDL `_
+ Another neat Python hardware project built around generators and
+ decorators. The semantics of this embedded language are close to Verilog
+ and unlike PyRTL, MyHDL allows asynchronous logic and higher level
+ modeling. Much like Verilog, only a structural "convertible subset" of the
+ language can be automatically synthesized into real hardware. PyRTL
+ requires all logic to be both synchronous and synthesizable which avoids a
+ common trap for beginners, it elaborates the design during execution
+ allowing the full power of Python in describing recursive or complex
+ hardware structures, and it allows for hardware synthesis, simulation, test
+ bench creation, and optimization all in the same framework.
+
+`Yosys `_
+ An open source tool for Verilog RTL synthesis. It supports a huge subset of
+ Verilog-2005 and provides a basic set of synthesis algorithms. The goals of
+ this tool are quite different from PyRTL, but the two play together very
+ nicely. PyRTL's :func:`~pyrtl.output_to_verilog` produces Verilog that can
+ be synthesized with Yosys. Similarly, PyRTL's
+ :func:`~pyrtl.input_from_verilog` uses Yosys to synthesize complex Verilog
+ designs to a simple library of gates, before importing into PyRTL.
+
+`PyMTL3 (a.k.a. Mamba) `_
+ A beta stage "open-source Python-based hardware generation, simulation, and
+ verification framework with multi-level hardware modeling support". One of
+ the neat things about this project is that they are trying to allow
+ simulation, modeling, and verification at multiple different levels of the
+ design from the functional level, the cycle-close level, and down to the
+ register-transfer level (where PyRTL really is built to play). Like MyHDL
+ they do some meta-programming tricks like parsing the Python AST to allow
+ executable software descriptions to be (under certain restrictions, sort of
+ like Verilog) automatically converted into implementable hardware. PyRTL,
+ on the other hand, is about providing a limited and composable set of data
+ structures for specifying an RTL implementation, thus avoiding the
+ distinction between synthesizable and non-synthesizable code (the execution
+ is the elaboration step).
+
+`ClaSH `_
+ An embedded hardware description language in Haskell. Like PyRTL it
+ provides an approach suitable for both combinational and synchronous
+ sequential circuits and transforms these high-level descriptions to
+ low-level synthesizable Verilog HDL. Unlike PyRTL, designs are statically
+ typed (like VHDL), yet with a very high degree of type inference, enabling
+ both safe and fast prototying using concise descriptions. If you like
+ functional programming and hardware also check out `Lava
+ `_.
diff --git a/www/maxn.py b/www/maxn.py
new file mode 100644
index 00000000..cec198ac
--- /dev/null
+++ b/www/maxn.py
@@ -0,0 +1,22 @@
+from functools import reduce
+
+import pyrtl
+
+
+def max_n(*inputs):
+ def max_2(x, y):
+ return pyrtl.select(x > y, x, y)
+
+ return reduce(max_2, inputs)
+
+
+a = pyrtl.Input(name="a", bitwidth=8)
+b = pyrtl.Input(name="b", bitwidth=8)
+c = pyrtl.Input(name="c", bitwidth=8)
+max = pyrtl.Output(name="max", bitwidth=8)
+
+max <<= max_n(a, b, c)
+
+sim = pyrtl.Simulation()
+sim.step_multiple({"a": [1, 5, 9], "b": [2, 6, 7], "c": [3, 4, 8]})
+sim.tracer.render_trace()
diff --git a/www/mul.py b/www/mul.py
new file mode 100644
index 00000000..c65b8695
--- /dev/null
+++ b/www/mul.py
@@ -0,0 +1,23 @@
+import pyrtl
+
+
+def mul(x: pyrtl.WireVector, y: pyrtl.WireVector) -> pyrtl.WireVector:
+ assert x.bitwidth == 4
+ assert y.bitwidth == 4
+
+ def romdata(addr: int) -> int:
+ return (addr >> 4) * (addr & 0xF)
+
+ tbl = pyrtl.RomBlock(bitwidth=8, addrwidth=8, romdata=romdata)
+ return tbl[pyrtl.concat(x, y)]
+
+
+a = pyrtl.Input(name="a", bitwidth=4)
+b = pyrtl.Input(name="b", bitwidth=4)
+product = pyrtl.Output(name="product", bitwidth=8)
+
+product <<= mul(a, b)
+
+sim = pyrtl.Simulation()
+sim.step_multiple({"a": [1, 2, 3], "b": [2, 3, 4]})
+sim.tracer.render_trace()
diff --git a/www/ripple-carry.py b/www/ripple-carry.py
new file mode 100644
index 00000000..21d7d7e2
--- /dev/null
+++ b/www/ripple-carry.py
@@ -0,0 +1,34 @@
+import pyrtl
+
+
+def one_bit_add(
+ a: pyrtl.WireVector, b: pyrtl.WireVector, carry_in: pyrtl.WireVector | int
+) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
+ assert len(a) == len(b) == 1 # `len` returns the bitwidth.
+ sum = a ^ b ^ carry_in # WireVector operators build the hardware.
+ carry_out = a & b | a & carry_in | b & carry_in
+ return sum, carry_out
+
+
+def ripple_add(
+ a: pyrtl.WireVector, b: pyrtl.WireVector, carry_in: pyrtl.WireVector | int = 0
+) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
+ a, b = pyrtl.match_bitwidth(a, b)
+ if len(a) == 1:
+ sumbits, carry_out = one_bit_add(a, b, carry_in)
+ else:
+ lsbit, ripplecarry = one_bit_add(a[0], b[0], carry_in)
+ msbits, carry_out = ripple_add(a[1:], b[1:], ripplecarry)
+ sumbits = pyrtl.concat(msbits, lsbit)
+ return sumbits, carry_out
+
+
+# Instantiate an adder into a 3-bit counter.
+counter = pyrtl.Register(name="counter", bitwidth=3)
+sum, carry_out = ripple_add(counter, pyrtl.Const(1))
+counter.next <<= sum
+
+# Simulate the instantiated design for 15 cycles.
+sim = pyrtl.Simulation()
+sim.step_multiple(nsteps=15)
+sim.tracer.render_trace()
From a1cbd4c4501fa0ecd043bede919428df3d5a8fb7 Mon Sep 17 00:00:00 2001
From: Jeremy Lau <30300826+fdxmw@users.noreply.github.com>
Date: Tue, 23 Jun 2026 18:13:51 -0700
Subject: [PATCH 02/12] Minor improvements:
* Test `www` examples.
* Update READMEs.
---
docs/README.md | 8 +++-----
tests/test_examples.py | 22 ++++++++++++----------
www/README.md | 40 ++++++++++++++++++++++++++++++++++++++++
www/gcd.py | 4 ++--
www/index.rst | 9 +++++----
5 files changed, 62 insertions(+), 21 deletions(-)
create mode 100644 www/README.md
diff --git a/docs/README.md b/docs/README.md
index 52f03c75..14511667 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -24,11 +24,9 @@ Follow the instructions on this page to build a local copy of PyRTL's
documentation. This is useful for verifying that PyRTL's documentation still
renders correctly after making a local change.
-There is additional PyRTL documentation in the [`gh-pages`
-branch](https://github.com/UCSBarchlab/PyRTL/tree/gh-pages). This additional
-documentation is pushed to https://ucsbarchlab.github.io/PyRTL/ by the
-`pages-build-deployment` GitHub Action. This additional documentation is
-written HTML and is not described further in this README.
+There is additional PyRTL documentation in [GitHub
+Pages](https://ucsbarchlab.github.io/PyRTL/), see
+[www/README.md](https://github.com/UCSBarchlab/PyRTL/blob/development/www/README.md).
## Testing Documentation Examples
diff --git a/tests/test_examples.py b/tests/test_examples.py
index 0a741ed0..e786e84d 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -1,4 +1,5 @@
import glob
+import itertools
import os
import subprocess
@@ -6,20 +7,21 @@
import pyrtl
-"""
-Tests all of the files in the example folder
-
-Note that this file is structure dependent, so don't forget to change it if the relative
-location of the examples changes
-"""
-
@pytest.mark.parametrize(
- "file", glob.iglob(os.path.dirname(__file__) + "/../examples/*.py")
+ "file",
+ itertools.chain(
+ glob.iglob(os.path.dirname(__file__) + "/../examples/*.py"),
+ glob.iglob(os.path.dirname(__file__) + "/../www/*.py"),
+ ),
)
def test_all_examples(file):
+ """Test all scripts in ``examples/`` and ``www/``.
+
+ This just checks that all scripts terminate with exit status 0.
+ """
pyrtl.reset_working_block()
try:
subprocess.check_output(["python", file])
- except subprocess.CalledProcessError as e:
- raise e
+ except subprocess.CalledProcessError:
+ pytest.fail(f"Failed to execute {file}")
diff --git a/www/README.md b/www/README.md
new file mode 100644
index 00000000..a2256f59
--- /dev/null
+++ b/www/README.md
@@ -0,0 +1,40 @@
+# PyRTL's GitHub Pages Webpage
+
+PyRTL's GitHub Pages webpage is at https://ucsbarchlab.github.io/PyRTL/ .
+
+The sources for this webpage are in this `www` directory. The page is built
+with [Sphinx](https://www.sphinx-doc.org/en/master/), and written in
+[reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html).
+The main Sphinx configuration file is
+[`www/conf.py`](https://github.com/UCSBarchlab/PyRTL/blob/development/www/conf.py),
+and source for the main page is
+[`www/index.rst`](https://github.com/UCSBarchlab/PyRTL/blob/development/www/index.rst)
+
+Follow the instructions on this page to build a local copy of PyRTL's webpage.
+This is useful for verifying that PyRTL's webpage still renders correctly after
+making a local change.
+
+There is additional PyRTL documentation in [Read the
+Docs](https://pyrtl.readthedocs.io/), see
+[docs/README.md](https://github.com/UCSBarchlab/PyRTL/blob/development/docs/README.md).
+
+## Running Sphinx
+
+Run Sphinx with the provided
+[`justfile`](https://github.com/UCSBarchlab/PyRTL/blob/development/justfile),
+from the repository's root directory:
+
+```shell
+# Run Sphinx to build PyRTL's webpage.
+$ uv run just www
+```
+
+This builds a local copy of PyRTL's webpage in `www/_build/html`.
+`www/_build/html/index.html` is the home page.
+
+## GitHub Actions Workflow
+
+When a commit is pushed that changes `www/`, the
+[`pages-deploy`](https://github.com/UCSBarchlab/PyRTL/blob/development/.github/workflows/pages-deploy.yml)
+workflow automatically runs Sphinx to regenerate the webpage and deploy it to
+GitHub Pages.
diff --git a/www/gcd.py b/www/gcd.py
index b36deeb1..5a78cad9 100644
--- a/www/gcd.py
+++ b/www/gcd.py
@@ -4,8 +4,8 @@
def gcd(
a: pyrtl.WireVector, b: pyrtl.WireVector, begin: pyrtl.WireVector
) -> tuple[pyrtl.WireVector, pyrtl.WireVector]:
- x = pyrtl.Register(bitwidth=len(a))
- y = pyrtl.Register(bitwidth=len(b))
+ x = pyrtl.Register(bitwidth=a.bitwidth)
+ y = pyrtl.Register(bitwidth=b.bitwidth)
done = pyrtl.WireVector(bitwidth=1)
with pyrtl.conditional_assignment:
diff --git a/www/index.rst b/www/index.rst
index aba07c61..3eebf3db 100644
--- a/www/index.rst
+++ b/www/index.rst
@@ -109,10 +109,11 @@ differences between the approaches.
A greatest common denominator calculator: ``gcd`` generates a sequential
circuit that saves inputs ``a`` and ``b`` when ``begin`` goes high, and
- then, while ``begin`` is low, calculates the GCD through iterative
- subtraction. The function returns two :class:`WireVectors
- `:, one which holds the GCD when the computation is
- ``done``, and the other which is a boolean ``done`` signal.
+ then, while ``begin`` is low, calculates the GCD with `Euclid's algorithm
+ `_. The function
+ returns two :class:`WireVectors `:, one which holds the
+ GCD when the computation is ``done``, and the other which is a boolean
+ ``done`` signal.
.. literalinclude:: gcd.py
From 50073f53991f3efe56b406649f670f40f0d17207 Mon Sep 17 00:00:00 2001
From: Jeremy Lau <30300826+fdxmw@users.noreply.github.com>
Date: Tue, 23 Jun 2026 20:20:43 -0700
Subject: [PATCH 03/12] Add screenshots for all `render_trace` outputs. Update
GCD block graph and improve formatting.
---
docs/README.md | 2 +-
docs/images/gcd-graph.png | Bin 85012 -> 0 bytes
docs/images/gcd-graph.svg | 513 +++++++++++++++++++++++++++++++++++++
docs/screenshots/adder.png | Bin 0 -> 5729 bytes
docs/screenshots/fir.png | Bin 0 -> 34005 bytes
docs/screenshots/gcd.png | Bin 0 -> 5849 bytes
docs/screenshots/maxn.png | Bin 0 -> 6832 bytes
docs/screenshots/mul.png | Bin 0 -> 5467 bytes
www/README.md | 2 +-
www/index.rst | 68 ++---
10 files changed, 554 insertions(+), 31 deletions(-)
delete mode 100644 docs/images/gcd-graph.png
create mode 100644 docs/images/gcd-graph.svg
create mode 100644 docs/screenshots/adder.png
create mode 100644 docs/screenshots/fir.png
create mode 100644 docs/screenshots/gcd.png
create mode 100644 docs/screenshots/maxn.png
create mode 100644 docs/screenshots/mul.png
diff --git a/docs/README.md b/docs/README.md
index 14511667..8b674342 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -26,7 +26,7 @@ renders correctly after making a local change.
There is additional PyRTL documentation in [GitHub
Pages](https://ucsbarchlab.github.io/PyRTL/), see
-[www/README.md](https://github.com/UCSBarchlab/PyRTL/blob/development/www/README.md).
+[`www/README.md`](https://github.com/UCSBarchlab/PyRTL/blob/development/www/README.md).
## Testing Documentation Examples
diff --git a/docs/images/gcd-graph.png b/docs/images/gcd-graph.png
deleted file mode 100644
index f8985ff9d2e5ab6fcdacdbb96b00e4eb7f13e6d7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 85012
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