From f8f39811893b1a9f38dc61084c44ec7627f89152 Mon Sep 17 00:00:00 2001 From: "Agent 1 (RTL Architect)" Date: Wed, 6 May 2026 12:38:32 -0300 Subject: [PATCH 1/5] =?UTF-8?q?fix(infra):=20upgrade=20yosys=200.15=20?= =?UTF-8?q?=E2=86=92=20OSS=20CAD=20Suite=202026-05-06=20(closes=20#36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cicd/Dockerfile pinned yosys-0.15.tar.gz from 2021-09-15, six years stale. Upstream yosys is at v0.64 (2026-04-09) on a monthly release cadence, and the 2023-08-31 unification commit moved synth_ecp5 into a thin wrapper around `synth_lattice -family ecp5`. None of that landed in 0.15. Surfaced by Stays #33 (yosys ECP5 day-1 recon, 2026-05-06): "The MAST CI Dockerfile pins yosys 0.15 — needs upgrading to v0.64 or OSS CAD Suite latest. Trivial Stream 3 PR." Choice: Option B (OSS CAD Suite). Three options were on the table: A. Pin yosys ≥ v0.64 source-built. Smaller scope but doesn't pin compatible nextpnr-ecp5 + prjtrellis HEADs, so the image still drifts version-by-version against the rest of the open ECP5 flow. B. OSS CAD Suite tarball (CHOSEN). Single date-pinned download (2026-05-06) ships yosys v0.64 + nextpnr-ecp5 + prjtrellis + verilator + iverilog + cocotb-ext as a tested-together bundle. This is what every production open-FPGA ECP5 reference uses (OrangeCrab, Trellis Board, ULX3S, Versa-ECP5, ECPIX-5, LiteX). The Dockerfile shrinks: one wget + one tar replaces a from-source `make -j` build. C. Drop the Dockerfile entirely. Rejected: it is still referenced by `.circleci/config.yml` (8 jobs) and by `cicd/build-docker.sh`, so removing it without removing those would break legacy CircleCI consumers. The active CI is `.github/workflows/ci.yml` (GHA), but the Docker image surface is non-zero. Smoke test (proves the path works on real CI): A new GHA job `yosys-ecp5-smoke` runs: yosys -p "read_verilog -sv src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" against `src/int/mul_pipeline_32bit.sv` (a self-contained 124-line module that does not pull in the AXI4 fabric). The job uploads the produced JSON as an artefact and prints the yosys version into the GitHub step summary. If this passes, the yosys + lattice techlib half of the rev-A open ECP5 flow is live. `src/global_mem_controller.sv` was the alternative target named in the issue, but it instantiates `axi4_mem_model` from `verif/`, which would turn the smoke into a multi-file integration test. The 32-bit multiplier exercises arith_map_ccu2c.v (carry chain) and the LUT4 path — a representative -85F primitive coverage check — without that fan-out. OSS CAD Suite URL pattern verified against the GitHub release API: tag = 2026-05-06 (created 2026-05-04) asset name = oss-cad-suite-linux-x64-20260506.tgz Refs: - Issue popsolutions/MAST#36 - Stays PR #33 (Agent 4 yosys ECP5 day-1 recon) - yosys v0.64 release notes (2026-04-09) - OSS CAD Suite releases (YosysHQ/oss-cad-suite-build) Authored by Agent 1 (RTL Architect). Signed-off-by: Agent 1 (RTL Architect) --- .github/workflows/ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++ cicd/Dockerfile | 28 +++++++++++++++------ 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44caa63..a5552ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,3 +89,57 @@ jobs: fi done } >> $GITHUB_STEP_SUMMARY + + yosys-ecp5-smoke: + name: yosys synth_ecp5 smoke (closes #36) + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache OSS CAD Suite + id: oss-cad-cache + uses: actions/cache@v4 + with: + path: ~/.local/oss-cad-suite + key: oss-cad-suite-2026-05-06-${{ runner.os }}-x64 + + - name: Download OSS CAD Suite (only on cache miss) + if: steps.oss-cad-cache.outputs.cache-hit != 'true' + run: | + mkdir -p ~/.local + wget -q https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2026-05-06/oss-cad-suite-linux-x64-20260506.tgz \ + -O /tmp/oss-cad-suite.tgz + tar -xzf /tmp/oss-cad-suite.tgz -C ~/.local + rm /tmp/oss-cad-suite.tgz + + - name: Add OSS CAD Suite to PATH and verify yosys version + run: | + echo "$HOME/.local/oss-cad-suite/bin" >> $GITHUB_PATH + $HOME/.local/oss-cad-suite/bin/yosys -V + + - name: Run synth_ecp5 smoke pass on src/int/mul_pipeline_32bit.sv + run: | + # Smoke-tests the open ECP5 synth path against a rev-A RTL module. + # Picks a self-contained module (mul_pipeline_32bit) so the smoke + # test does not pull in the AXI4 fabric. If this passes, the + # yosys + lattice techlib half of the open ECP5 flow is live for + # rev-A. See cicd/Dockerfile and #36. + mkdir -p /tmp/smoke + yosys -p "read_verilog -sv src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ + | tee /tmp/smoke/yosys.log + test -s /tmp/smoke/mul_pipeline_32bit.json + { + echo "## yosys ECP5 smoke result" + echo "" + echo "- yosys version: \`$(yosys -V | head -1)\`" + echo "- json size: \`$(wc -c < /tmp/smoke/mul_pipeline_32bit.json) bytes\`" + } >> $GITHUB_STEP_SUMMARY + + - name: Upload smoke artefacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: yosys-ecp5-smoke + path: /tmp/smoke/ + if-no-files-found: warn diff --git a/cicd/Dockerfile b/cicd/Dockerfile index 0861396..a3038c7 100644 --- a/cicd/Dockerfile +++ b/cicd/Dockerfile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: CC-BY-SA-4.0 # this is used for cicd FROM ubuntu:20.04 @@ -74,13 +75,26 @@ RUN wget ftp://ftp.icarus.com/pub/eda/verilog//v11/verilog-11.0.tar.gz && \ # cd .. && \ # rm -Rf iverilog -# yosys -RUN wget https://github.com/YosysHQ/yosys/archive/refs/tags/yosys-0.15.tar.gz && \ - tar -xf yosys-0.15.tar.gz && cd yosys-yosys-0.15 && \ - make -j $(nproc) && \ - make install && \ - cd .. && \ - rm -Rf yosys-yosys-0.15 +# yosys + nextpnr-ecp5 + prjtrellis (via OSS CAD Suite) +# +# Replaces the 2021-vintage `yosys-0.15.tar.gz` source build (closes #36). +# OSS CAD Suite ships yosys + nextpnr + prjtrellis (and verilator, iverilog, +# cocotb-ext) as one date-pinned tarball, which is what every production open- +# FPGA ECP5 reference (OrangeCrab, Trellis Board, ULX3S, Versa-ECP5, ECPIX-5, +# LiteX) actually uses. See docs/upstream-contributions/2026-05-06-yosys-ecp5.md +# (Stays #33) for the day-1 recon that surfaced this upgrade. +# +# Pinned to the 2026-05-06 nightly. Bump the date when refreshing — keep the +# pin so the image is reproducible. yosys version inside this build: v0.64 +# (released 2026-04-09). +ARG OSS_CAD_SUITE_DATE=2026-05-06 +ARG OSS_CAD_SUITE_DATE_NODASH=20260506 +RUN wget -q "https://github.com/YosysHQ/oss-cad-suite-build/releases/download/${OSS_CAD_SUITE_DATE}/oss-cad-suite-linux-x64-${OSS_CAD_SUITE_DATE_NODASH}.tgz" -O /tmp/oss-cad-suite.tgz && \ + mkdir -p /opt && \ + tar -xzf /tmp/oss-cad-suite.tgz -C /opt && \ + rm /tmp/oss-cad-suite.tgz && \ + /opt/oss-cad-suite/bin/yosys -V +ENV PATH="/opt/oss-cad-suite/bin:${PATH}" # verilator RUN git clone https://github.com/verilator/verilator && \ From ddf95d807c41b498495fee1cedc913b207ae1114 Mon Sep 17 00:00:00 2001 From: "Agent 1 (RTL Architect)" Date: Wed, 6 May 2026 12:41:32 -0300 Subject: [PATCH 2/5] fix(ci): smoke pass needs src/assert.sv for `assert_known macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mul_pipeline_32bit.sv:65 uses the `assert_known macro defined in src/assert.sv. yosys's preprocessor pulls macros only from files explicitly passed to read_verilog (no automatic include path), so the smoke command needs both files. Order matters: assert.sv first so the macro is in scope when the module body parses. Confirmed against the prior CI failure on the v0.64+187 OSS CAD Suite build: src/int/mul_pipeline_32bit.sv:65: ERROR: Unimplemented compiler directive or undefined macro `assert_known. That same yosys version DID install cleanly and report `Yosys 0.64+187 (git sha1 49b4f6d81, clang++ 18.1.8 -fPIC -O3)`, so the OSS CAD Suite upgrade itself is verified — only the smoke command was missing the macro file. Signed-off-by: Agent 1 (RTL Architect) --- .github/workflows/ci.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5552ee..5489cd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,12 +121,15 @@ jobs: - name: Run synth_ecp5 smoke pass on src/int/mul_pipeline_32bit.sv run: | # Smoke-tests the open ECP5 synth path against a rev-A RTL module. - # Picks a self-contained module (mul_pipeline_32bit) so the smoke - # test does not pull in the AXI4 fabric. If this passes, the - # yosys + lattice techlib half of the open ECP5 flow is live for - # rev-A. See cicd/Dockerfile and #36. + # Picks mul_pipeline_32bit (124 lines, no module instances) so the + # smoke test does not pull in the AXI4 fabric. The module references + # the `assert_known` macro from src/assert.sv, so we feed both files + # to read_verilog (assert.sv first so the macros are in scope when + # the module body is parsed). If this passes, the yosys + lattice + # techlib half of the open ECP5 flow is live for rev-A. + # See cicd/Dockerfile and #36. mkdir -p /tmp/smoke - yosys -p "read_verilog -sv src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ + yosys -p "read_verilog -sv src/assert.sv src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ | tee /tmp/smoke/yosys.log test -s /tmp/smoke/mul_pipeline_32bit.json { From c10394c59d017e5a6a1ea2146d9562825d3d5db6 Mon Sep 17 00:00:00 2001 From: "Agent 1 (RTL Architect)" Date: Wed, 6 May 2026 12:44:10 -0300 Subject: [PATCH 3/5] fix(ci): -D no-op the assert macros for yosys smoke MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second smoke iteration revealed the next layer: src/int/mul_pipeline_32bit.sv:65: ERROR: Unimplemented compiler directive or undefined macro `__FILE__`. src/assert.sv's `assert_known` macro expands to `__FILE__` and `__LINE__`, which yosys's preprocessor does not support (verilog-2005 behaviour; cf. yosys#1075). iverilog and verilator do support them, so the asserts work in real verification runs — but for a synth-only smoke we just no-op the macros via `-Dassert_known(VAL)=` and `-Dassert(VAL)=`. The module body never reaches `__FILE__` / `__LINE__`. This lets the smoke prove the synth path, which is its only job: yosys reads SV → synth_lattice -family ecp5 → write_json. Signed-off-by: Agent 1 (RTL Architect) --- .github/workflows/ci.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5489cd2..75cc45f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,14 +122,19 @@ jobs: run: | # Smoke-tests the open ECP5 synth path against a rev-A RTL module. # Picks mul_pipeline_32bit (124 lines, no module instances) so the - # smoke test does not pull in the AXI4 fabric. The module references - # the `assert_known` macro from src/assert.sv, so we feed both files - # to read_verilog (assert.sv first so the macros are in scope when - # the module body is parsed). If this passes, the yosys + lattice - # techlib half of the open ECP5 flow is live for rev-A. - # See cicd/Dockerfile and #36. + # smoke test does not pull in the AXI4 fabric. + # + # The module references the `assert_known macro defined in + # src/assert.sv. That macro expands to `__FILE__` and `__LINE__`, + # which yosys's preprocessor does NOT support (verilog-2005 + # behaviour; cf. yosys#1075). For a synth-only smoke we redefine + # the assert macros to no-ops via -D so the preprocessor never + # walks into the unsupported builtins. iverilog and verilator (the + # simulators that exercise these RTL files in real verification) + # do support `__FILE__` / `__LINE__`, so this redefinition is + # smoke-test-local and does not affect any real verification run. mkdir -p /tmp/smoke - yosys -p "read_verilog -sv src/assert.sv src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ + yosys -p "read_verilog -sv -Dassert_known(VAL)= -Dassert(VAL)= src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ | tee /tmp/smoke/yosys.log test -s /tmp/smoke/mul_pipeline_32bit.json { From 72110bf2148c4456c8ab61f14a7cf04320e29468 Mon Sep 17 00:00:00 2001 From: "Agent 1 (RTL Architect)" Date: Wed, 6 May 2026 12:47:04 -0300 Subject: [PATCH 4/5] fix(ci): wrap smoke RTL in no-op-assert smoke_top for yosys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third smoke iteration: src/int/mul_pipeline_32bit.sv:65: ERROR: Unimplemented compiler directive or undefined macro `assert_known. yosys's `-D` flag for read_verilog accepts only value macros (`-Dname=val`), not function-like macros (`-Dname(args)=val`). So the previous attempt at `-Dassert_known(VAL)=` was silently dropped. Switched to writing a tiny smoke_top.sv wrapper that: 1. `define`s assert_known and assert as no-ops before include, 2. `include`s the real module file. This is the verilog-preprocessor-canonical way of overriding macros. The wrapper lives in /tmp/smoke (build artefact), not in src/, so the RTL itself is untouched — the task brief explicitly forbids modifying RTL or test files, and we don't. Signed-off-by: Agent 1 (RTL Architect) --- .github/workflows/ci.yml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75cc45f..aaec86b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,17 +124,29 @@ jobs: # Picks mul_pipeline_32bit (124 lines, no module instances) so the # smoke test does not pull in the AXI4 fabric. # - # The module references the `assert_known macro defined in - # src/assert.sv. That macro expands to `__FILE__` and `__LINE__`, - # which yosys's preprocessor does NOT support (verilog-2005 - # behaviour; cf. yosys#1075). For a synth-only smoke we redefine - # the assert macros to no-ops via -D so the preprocessor never - # walks into the unsupported builtins. iverilog and verilator (the + # The module uses `assert_known` from src/assert.sv, which expands + # to `__FILE__` and `__LINE__`. yosys's preprocessor does NOT + # support those builtins (verilog-2005 behaviour; cf. yosys#1075), + # and `-D` does not support function-like macros, so we cannot + # simply no-op the call site. Instead we write a tiny smoke + # wrapper that defines `assert_known` to a no-op BEFORE including + # the RTL — that lets the existing macro be silently overridden + # at the smoke entry point. iverilog and verilator (the # simulators that exercise these RTL files in real verification) - # do support `__FILE__` / `__LINE__`, so this redefinition is + # do support the `__FILE__` / `__LINE__` builtins, so this is # smoke-test-local and does not affect any real verification run. mkdir -p /tmp/smoke - yosys -p "read_verilog -sv -Dassert_known(VAL)= -Dassert(VAL)= src/int/mul_pipeline_32bit.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json /tmp/smoke/mul_pipeline_32bit.json" \ + cat > /tmp/smoke/smoke_top.sv <<'SV' + // SPDX-License-Identifier: CC-BY-SA-4.0 + // Auto-generated by .github/workflows/ci.yml (yosys-ecp5-smoke job). + // Defines no-op assert macros so yosys's preprocessor does not + // walk into `__FILE__` / `__LINE__` (unsupported in yosys). + `define assert_known(VAL) + `define assert(VAL) + `include "mul_pipeline_32bit.sv" + SV + cp src/int/mul_pipeline_32bit.sv /tmp/smoke/mul_pipeline_32bit.sv + (cd /tmp/smoke && yosys -p "read_verilog -sv -I. smoke_top.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json mul_pipeline_32bit.json") \ | tee /tmp/smoke/yosys.log test -s /tmp/smoke/mul_pipeline_32bit.json { From 688c39308b63e856d860150e1c8455335e2936e5 Mon Sep 17 00:00:00 2001 From: "Agent 1 (RTL Architect)" Date: Wed, 6 May 2026 12:49:53 -0300 Subject: [PATCH 5/5] fix(ci): switch smoke target to dadda_32bit32 (truly self-contained) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth smoke iteration: mul_pipeline_32bit.sv:81: ERROR: Can't resolve task name `\mul_pipeline_cycle_32bit_2bpc'. mul_pipeline_32bit.sv calls a task defined in another file, so it is not actually self-contained. Switched the smoke target to src/generated/dadda_32bit32.sv: 1420 lines of pure combinational logic, no module instances, no task/function calls into other files, no include directives, and no `assert_known macro references — so the preprocessor never walks into the unsupported `__FILE__ / `__LINE__ builtins. Bonus: the Dadda multiplier is exactly the kind of LUT4 + carry-chain (arith_map_ccu2c.v) workload rev-A's matrix engine will eventually stress on -85F, so the smoke is representative as well as clean. Removed the smoke_top wrapper from /tmp/smoke since it is no longer needed. Signed-off-by: Agent 1 (RTL Architect) --- .github/workflows/ci.yml | 48 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaec86b..6d56646 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,42 +118,36 @@ jobs: echo "$HOME/.local/oss-cad-suite/bin" >> $GITHUB_PATH $HOME/.local/oss-cad-suite/bin/yosys -V - - name: Run synth_ecp5 smoke pass on src/int/mul_pipeline_32bit.sv + - name: Run synth_ecp5 smoke pass on src/generated/dadda_32bit32.sv run: | # Smoke-tests the open ECP5 synth path against a rev-A RTL module. - # Picks mul_pipeline_32bit (124 lines, no module instances) so the - # smoke test does not pull in the AXI4 fabric. # - # The module uses `assert_known` from src/assert.sv, which expands - # to `__FILE__` and `__LINE__`. yosys's preprocessor does NOT - # support those builtins (verilog-2005 behaviour; cf. yosys#1075), - # and `-D` does not support function-like macros, so we cannot - # simply no-op the call site. Instead we write a tiny smoke - # wrapper that defines `assert_known` to a no-op BEFORE including - # the RTL — that lets the existing macro be silently overridden - # at the smoke entry point. iverilog and verilator (the - # simulators that exercise these RTL files in real verification) - # do support the `__FILE__` / `__LINE__` builtins, so this is - # smoke-test-local and does not affect any real verification run. + # Target: src/generated/dadda_32bit32.sv — a 1420-line generated + # 32x32 → 32-bit Dadda multiplier. Properties that make it the + # right smoke target: + # * Self-contained: no module instantiations, no task/function + # calls into other files, no `include, no SV interfaces, no + # packages. + # * Does NOT reference the assert.sv macros, so the yosys + # preprocessor does not walk into the unsupported `__FILE__` / + # `__LINE__` builtins (cf. yosys#1075). + # * Pure combinational logic exercises the LUT4 / carry-chain + # (arith_map_ccu2c.v) primitive coverage on the ECP5 backend + # — exactly the path rev-A's matrix engine will eventually + # stress. + # + # If this passes, the yosys + lattice techlib half of the open + # ECP5 flow is live for rev-A. See cicd/Dockerfile and #36. mkdir -p /tmp/smoke - cat > /tmp/smoke/smoke_top.sv <<'SV' - // SPDX-License-Identifier: CC-BY-SA-4.0 - // Auto-generated by .github/workflows/ci.yml (yosys-ecp5-smoke job). - // Defines no-op assert macros so yosys's preprocessor does not - // walk into `__FILE__` / `__LINE__` (unsupported in yosys). - `define assert_known(VAL) - `define assert(VAL) - `include "mul_pipeline_32bit.sv" - SV - cp src/int/mul_pipeline_32bit.sv /tmp/smoke/mul_pipeline_32bit.sv - (cd /tmp/smoke && yosys -p "read_verilog -sv -I. smoke_top.sv; synth_lattice -family ecp5 -top mul_pipeline_32bit; write_json mul_pipeline_32bit.json") \ + yosys -p "read_verilog -sv src/generated/dadda_32bit32.sv; synth_lattice -family ecp5 -top dadda_32bit32; write_json /tmp/smoke/dadda_32bit32.json" \ | tee /tmp/smoke/yosys.log - test -s /tmp/smoke/mul_pipeline_32bit.json + test -s /tmp/smoke/dadda_32bit32.json { echo "## yosys ECP5 smoke result" echo "" echo "- yosys version: \`$(yosys -V | head -1)\`" - echo "- json size: \`$(wc -c < /tmp/smoke/mul_pipeline_32bit.json) bytes\`" + echo "- top module: \`dadda_32bit32\`" + echo "- json size: \`$(wc -c < /tmp/smoke/dadda_32bit32.json) bytes\`" } >> $GITHUB_STEP_SUMMARY - name: Upload smoke artefacts