Skip to content

fpga: ULX3S v3.1.8 smoke + 1 Mbaud UART bridge to exact lean_silicon_lsc1 (MinCore) - #16

Merged
Th0rgal merged 50 commits into
mainfrom
fpga/ulx3s-smoke-uart-mincore
Jul 26, 2026
Merged

fpga: ULX3S v3.1.8 smoke + 1 Mbaud UART bridge to exact lean_silicon_lsc1 (MinCore)#16
Th0rgal merged 50 commits into
mainfrom
fpga/ulx3s-smoke-uart-mincore

Conversation

@Th0rgal

@Th0rgal Th0rgal commented Jul 25, 2026

Copy link
Copy Markdown
Member

ONE-HOUR HARDWARE SPRINT fallback after Codex mission 39f96ddc failed at OAuth. Goal: maximize Thomas's chance to later run physical SET/XOR/MUL on confirmed ULX3S v3.1.8 / LFE5U-85F over US1 UART.

Never merge. Never program hardware.

Scope / ownership

Confirmed board facts used

  • v3.1.8, LFE5U-85F (IDCODE 0x41113043), 25 MHz G2, LED0 B2, US1: M1 (FPGA RX), L4 (FPGA TX)
  • LPF is pin-subset hypothesis from ulx3s-misc v316; v3.1.8 applicability labelled as such
  • SDRAM unused; SRAM-only; never use -f

Architecture (hard rule)

  • UART is harness plumbing outside the exact ASIC 8-bit ready/valid boundary
  • Every byte and handshake traverses ui_in/uo_out/uio_* of lean_silicon_lsc1
  • Current ASIC top wraps leanvm_b_stream_alu (MinCore), not v1 packet executor — preserved

Deliverables (stop at verified boundary)

P0 smoke

  • Minimal 25 MHz counter/LED top + minimal LPF
  • Real reproducible flow: yosys → nextpnr-ecp5 --85k --package CABGA381 → ecppack (no --timing-allow-fail)
  • Tool versions, exact commands, post-route max freq/slack, bitstream SHA256 archived

P1 UART + bridge + driver

  • 1 Mbaud UART RX/TX, 2-flop sync, framing error handling
  • uart_bridge.sv instantiates exact lean_silicon_lsc1; buffering + backpressure + POR
  • fpga_harness/ulx3s_uart.py: explicit --port only, SET/XOR/MUL fixture with independent expected for SET/XOR, stale drain, timeout, status/clear

P2 tests

  • Icarus tb_uart_bridge.sv compiles and runs
  • make fpga-boundary, make fpga-harness, make sim all green
  • Same byte-stream intent exercised in RTL sim and existing Python MinCore/model oracle

P3 docs

  • docs/ULX3S_SMOKE_AND_UART.md: detached head, artefacts + checksums, expected output, power-cycle recovery, SRAM-only openFPGALoader -b ulx3s <bit>, explicit -f prohibition
  • No instruction to execute before independent review
  • No CPU/ISA/datapath validation claims beyond evidence

Real build artefacts (this run)

  • Smoke bit: results/ulx3s-smoke-uart-20260725/ulx3s_smoke.bit SHA 96eb9eda7421bac902eacaeced21eee0db9a80b8f2f2effdb52b515d68e0b2e3
  • Bridge bit: results/ulx3s-smoke-uart-20260725/ulx3s_bridge.bit SHA eacee77bdca486f173cacdb56709e442942eb85f9111eabcb96026976f8bae51
  • Smoke timing: Max frequency ... 291.97 MHz (PASS at 25.00 MHz)
  • Bridge timing: Max frequency ... 154.37 MHz (PASS at 25.00 MHz)

Tests / exits

  • boundary_check: 0 errors, OK
  • fpga-harness: 101 tests OK
  • sim: 163 tests OK
  • iverilog tb_uart_bridge: compiled + ran to $finish

Supported transaction sequence (MinCore)

SET128 (0x03), XOR128 (0x01), MUL128 (0x02), STATUS (0x7e), CLEAR (0x7d). All via exact ui/uio ready/valid.

Blockers / limits

  • No physical board in sprint env
  • LPF is hypothesis only (G2/B2/M1/L4 stable; full pinout/banks unverified)
  • No Verilator full bridge TB (Icarus skeleton only)
  • Driver has no advanced host flow control beyond TX_READY

Bounded claims

  • Reproducible OSS CAD flow from exact source at 03926bb
  • Exact lean_silicon_lsc1 pin contract instantiated with no wide bypass
  • Timing numbers at 25 MHz target
  • Clean boundary + unit + sim tests
  • Nothing here proves bytes crossed a physical ULX3S

@codex please review.


Note

Medium Risk
Large FPGA/harness addition with archived binaries and build tooling; datapath is explicitly unvalidated on silicon, but mistaken programming or treating archive evidence as hardware proof would be operationally risky.

Overview
Adds a ULX3S v3.1.8 deliverable: P0 LED smoke and P1 1 Mbaud UART design that wraps lean_silicon_lsc1 on the 8-bit ready/valid pins (no wide bypass), plus fpga_harness/ulx3s_uart.py for explicit-port SET/XOR/MUL/STATUS with oracle checks.

Build and evidence: fpga/ulx3s/build_smoke.sh and build_uart.sh run yosys → nextpnr (25 MHz LPF) → ecppack, then publish atomically into results/ulx3s-smoke-uart-20260725/ with SHA256 manifests, source manifests, and route logs. New tools/source_provenance.py, atomic_publish.py, and portable_build_support.py back locking, checksums, and squash-safe verification. .gitignore keeps scratch FPGA outputs out of git but allows archived results/**/*.log.

CI and tests: fetch-depth: 0 on checkout for provenance. test/tb_uart_bridge.sv is wired into make sim. Harness tests cover provenance, atomic publish, portable builds, and UART driver edge cases. pyserial==3.5 added to requirements.txt.

Docs/status: docs/ULX3S_SMOKE_AND_UART.md records limits (SRAM-only, no hardware validation, 0x7f abort in payload). Harness README/BUILD_PLAN/INVENTORY and board_detect now say bitstreams exist but datapath remains not-validated without a programmed board and byte log.

Reviewed by Cursor Bugbot for commit a94cd1a. Bugbot is set up for automated code reviews on this repo. Configure here.

Th0rgal added 2 commits July 25, 2026 21:17
- Minimal 25 MHz counter/LED smoke_top + LPF (v3.1.8 pin-subset hypothesis)
- UART RX/TX at 1 Mbaud, 2-flop sync, framing error handling
- uart_bridge.sv: exact lean_silicon_lsc1 instantiation, byte buffering, POR, backpressure
- New driver: fpga_harness/ulx3s_uart.py (explicit --port only)
- Icarus TB skeleton: test/tb_uart_bridge.sv
- SRAM-only build scripts; explicit prohibition of -f documented
- Real yosys/nextpnr/ecppack run; 291.97 MHz post-route; bitstream SHA captured

Ownership: PR #13 (host runtime), PR #15 (board_detect/SHA256SUMS) untouched.
Worktree: fpga/ulx3s-smoke-uart-mincore @ 03926bb

[skip ci] sprint deliverable
- Exact commands, tool versions, timing (291.97 MHz smoke / 160.26 MHz bridge)
- Bitstream SHAs recorded
- SRAM-only openFPGALoader recipe with explicit -f prohibition
- Supported MinCore transaction list
- Bounded claims only; no hardware execution evidence

[skip ci]
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_9458fc2c-f5d5-4295-8f37-26c6c498bbc5)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 353a22814b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga/ulx3s/uart_bridge.sv Outdated
Comment thread fpga/ulx3s/uart_tx.sv
Comment thread fpga/ulx3s/build_uart.sh Outdated
Comment thread fpga_harness/ulx3s_uart.py Outdated
Comment thread fpga/ulx3s/Makefile
Comment thread results/ulx3s-smoke-uart-20260725/SHA256SUMS Outdated
Comment thread docs/ULX3S_SMOKE_AND_UART.md Outdated
Every response byte was being corrupted or lost on the way to the host, and
none of the checked-in verification could see it.

RTL:
- uart_tx released tx_ready at bit_cnt == 8, so data bit 7 lasted one 25 MHz
  clock instead of a full baud interval. A receiver sampled every zero-valued
  bit 7 as one (a SET echo of 00 arrived as 80). Ready now releases at 9.
- The bridge's power-on "synchroniser" only ever shifted in ones, so rst_n was
  never low and no register in the bridge or the core was ever initialised.
  The core latched x into its error state and answered nothing. Replaced with a
  zero-initialised shift register that genuinely holds reset for 8 edges.
- Both handshake directions now use a one-deep skid buffer whose ready
  deasserts on the handshake that fills it. Previously RX_VALID was pulsed
  against a ready sampled a cycle early (dropping bytes that arrived mid
  transaction) and TX_READY mirrored the serialiser a cycle late (letting the
  core hand over a second byte that overwrote the first).
- Added a sticky rx_overrun probe that drives no logic, so the bench can assert
  no byte was ever displaced.

Verification:
- Replaced the tb_uart_bridge liveness window with a real 1 Mbaud 8N1 receiver.
  It fails on a missing byte, a short final bit, a bad stop bit, a wrong or
  extra byte, and on rx_overrun, across STATUS, SET, XOR, MUL, unknown opcode
  and abort recovery. Wired it into `make sim`, which previously never ran it.
- ulx3s_uart.py --tx mul printed the board's reply and exited 0 whatever it
  was. It now computes the expected GF(2^128) product with both independent
  oracles in sim/model.py, cross-checks them, and gates the exit status.
  Added tests that feed complete but wrong 16-byte responses and require exit 1.

Build and evidence:
- build_smoke.sh and build_uart.sh resolved sources and output through one ".."
  too many, so the bridge build could not find any RTL and evidence landed
  outside the repository. Both now resolve the repository root from their own
  location and archive artefacts under the names the manifests use.
- Committed the executable bit so `make -C fpga/ulx3s smoke` runs, and pointed
  the uart target at build_uart.sh instead of an echo.
- The LPF had no frequency constraint, so a "25 MHz" claim was really nextpnr's
  12 MHz default. Constrained it; both designs still close (291.97 / 154.37 MHz).
- results/ SHA256SUMS named smoke.bit for a file archived as ulx3s_smoke.bit, so
  the integrity check could not run. The scripts now emit and self-verify both
  manifests. Regenerated ulx3s_bridge.bit from the repaired RTL; the digest is
  reproducible across three independent builds.
- .gitignore's blanket *.log dropped the synthesis and route logs the manifest
  promised. Exempted the results directory and archived them.

Not addressed by design: 0x7f still cannot appear as a payload byte, since the
bridge treats it as an abort before command framing. Escaping it would change
the wire protocol. Documented as a limitation instead.
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ca273a2d-9c95-43be-9729-f38c932e9664)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01e704685f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga/ulx3s/build_uart.sh Outdated
Comment thread fpga_harness/ulx3s_uart.py Outdated
Comment thread fpga/ulx3s/Makefile Outdated
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_15fe7362-d16b-4981-8c84-5c81e3797117)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 078e55e385

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py
Comment thread fpga_harness/ulx3s_uart.py
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_31457b2a-5692-4615-90c4-01bda0720d97)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63a2054099

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
status is the default --tx, and it was the only command whose reply was
printed without being checked. A misbuilt bridge emitting four arbitrary
bytes passed the default board-smoke invocation with exit 0, so the check
that is supposed to catch a broken build was the one command that could
not fail.

Compare against 01 01 0f 08 and exit 1 on mismatch, matching how set, xor
and mul already gate their exit status. A test parses status_byte() out of
leanvm_b_stream_alu.sv so the host constant cannot drift from the RTL.
@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_cf1f8fe8-ae59-48fe-a24d-fd5c3b8cc6f8)

@Th0rgal

Th0rgal commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

Verification at dc3254f79445f31ce16c196c6d615fffad32724f

Sole blocker (r3651225367, unchecked STATUS reply) is fixed and resolved. Evidence below was produced on this head; nothing was taken on trust from a previous run.

Source rebuild — bit-exact reproduction

Installed the pinned toolchain and rebuilt both targets from source in a clean tree. Tool versions match tool_versions_uart.txt exactly (Yosys 0.67+94 sha1 7defa5186-dirty, nextpnr-0.10-100-gfb95bb8e, ecppack 1.4-79-g56bb170).

artifact rebuilt sha256 committed result
ulx3s_bridge.bit eacee77bdca486f173cacdb56709e442942eb85f9111eabcb96026976f8bae51 same byte-identical
ulx3s_smoke.bit 96eb9eda7421bac902eacaeced21eee0db9a80b8f2f2effdb52b515d68e0b2e3 same byte-identical
smoke.config 8243770de3fb1cb65f8ef0ee345addab105cf7e219d0c3be5cf009743e27f59f same match
smoke.svf 0a2dcd734f194819b6483b6d5a7e394677eb30634760d46a62d4f5542cdec944 same match

timing_uart.txt and SHA256SUMS_bridge.txt also came back byte-identical.

Timing closure at 25 MHz — gate proven non-vacuous

FREQUENCY PORT "clk" 25.000000 MHz in the LPF, nextpnr-ecp5 invoked with no --timing-allow-fail, under set -eu.

  • bridge: 161.47 / 154.37 MHz, PASS at 25.00 MHz
  • smoke: 352.61 / 291.97 MHz, PASS at 25.00 MHz

A passing timing report is only worth something if the tool would actually have failed, so I re-ran place-and-route with the constraint raised to 400 MHz: nextpnr exits 1 with ERROR: ... (FAIL at 400.00 MHz). The gate is real, and without the LPF FREQUENCY line the router would silently fall back to its 12 MHz default.

Pin / safe-output audit

nextpnr places TRELLIS_IO: 4/365, and the bitstream config contains exactly four BASE_TYPE pin configurations — two outputs only:

  • led B2 OUTPUT_LVCMOS33
  • uart_tx L4 OUTPUT_LVCMOS33 DRIVE=4
  • clk G2 INPUT_LVCMOS33
  • uart_rx M1 INPUT_LVCMOS33 PULLMODE=UP

No SDRAM, flash, SPI or stray GPIO is constrained or driven. ecppack is called with no flash options; no -f appears in any build path.

Deterministic checks (repeated runs, identical results)

  • make check — exit 0: 163 model tests, 123 harness tests, repo consistency OK, SHA256SUMS matches tracked source artifacts
  • make fpga-boundary — the exact 8-bit ready/valid ASIC boundary holds; uio_oe mask 0b10110110 unchanged
  • make simPASS: leanVM-b MinCore stream ALU, tb_uart_bridge PASSED
  • CI on the previous head: executable-models, formal-and-lint, lean, systemverilog all green

Compatibility

No RTL, LPF or build-script change in this commit — host Python, docs and the generated SHA256SUMS only. Compatible with #17, which independently uses the same 01010f08 signature; the only shared file is the generated SHA256SUMS.

Scope — what this does not claim

No board was attached and no hardware was touched at any point: no JTAG, UART, bitstream load or flash. Everything above is simulation, source build and static audit. This is not physical validation, not full CPU or formal verification of the complete core, and not tapeout readiness or GDS. The .bit is proven reproducible, not proven correct on silicon.

@Th0rgal

Th0rgal commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please re-review at head dc3254f79445f31ce16c196c6d615fffad32724f. The prior finding (unchecked STATUS reply) is addressed in that commit; all earlier threads are resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dc3254f794

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
The usage example shipped `--tx 03`, the SET128 opcode byte, but argparse
only accepts command names. The first command a new user copies exited 2
before opening the port, so the documented entry point never worked.

Point the example at `--tx set` and give a status example alongside it.
PAYLOAD_BYTES now backs both the argparse choices and the width check,
so the accepted commands and their payload widths cannot drift apart.

Guarded by tests that pull every `python3 -m fpga_harness.ulx3s_uart`
line out of the module docstring and ULX3S_SMOKE_AND_UART.md, parse each
one, and check the payload width matches the command.
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_204889d2-fda7-42f2-8054-d6ac3ccecd3d)

uart_bridge.sv derives its abort pulse from any received 0x7f, not only
from one in command position, so a 0x7f inside an operand tears the
transaction down mid-flight. Reproduced in simulation: a SET128 whose
payload byte 5 is 0x7f comes back as 15 bytes with 0xe0 from byte 5 on.

128-bit operands are arbitrary data, so ordinary vectors reach this --
about 6% of random SET payloads and 12% of random XOR/MUL operand pairs.
Previously the host printed MATCH: False and exited 1, which is the exit
status for "the board answered wrong" and points an investigation at the
silicon instead of the link.

Reject such operands up front and exit 2, and write the limitation down
as an open bridge design question rather than an LSC-1 property. The ASIC
takes ABORT on uio_in[6], a pin, and has no in-band abort byte, so this
is the bridge's framing to settle. No RTL is changed here, so the
committed bitstreams and their checksums still describe the shipped gates.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 218b6f9f1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_bb64b50e-4fd7-4841-8c17-0b9314031171)

@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

Review findings on dc3254f7d4c3fa0

Scope was simulation / source-build / review only. No FPGA, JTAG, UART device, bitstream load, flash or SRAM access of any kind. Nothing here is a physical-validation claim.

1. --tx 03 usage example — fixed (218b6f9)

Reproduced, fixed, mutation-checked, thread resolved. Details in the thread.

2. 0x7f is not carried transparently through the bridge — open, not fixed

While checking PR #17 compatibility I found a defect that I do not think should be patched silently, because the fix is a protocol decision and would invalidate the committed bitstreams.

fpga/ulx3s/uart_bridge.sv:84 derives the abort pulse from any received 0x7f:

wire rx_is_abort = rx_byte_ok && (uart_rx_data == 8'h7f);

The comment at line 9 says "host command byte 0x7f", but the implementation does not distinguish command position from payload position. So a 0x7f inside a 128-bit operand aborts the transaction mid-flight.

Reproduced in Icarus against the unmodified RTL — a SET128 whose payload byte 5 is 0x7f:

FAIL SET with 7f in payload: missing TX, expected 16 byte(s), received 15
FAIL SET with 7f in payload: byte 5 is e0, expected 7f
FAIL SET with 7f in payload: byte 6 is e0, expected 5c
... (0xe0 for every remaining byte)

The abort drops the byte, the core resynchronises, and each following payload byte is decoded as an opcode, yielding 0xe0.

tb_uart_bridge.sv does not catch this: it tests 0x7f mid-command as abort-and-recover, and none of its SET/XOR/MUL vectors happen to contain 0x7f (i^0x5a spans 0x5a..0x55; 0xa5+i spans 0xa5..0xb4).

Operands are arbitrary data, so this is reachable by ordinary vectors: ~6% of random SET payloads and ~12% of random XOR/MUL operand pairs. Before this PR's change the host printed MATCH: False and exited 1 — the same exit status as "the board returned a wrong product" — which would send an investigation at the silicon rather than at the link.

What I did (d4c3fa0): host-side only. The driver now refuses such an operand up front and exits 2 (tooling limit), reporting the exact offsets, and the limitation is written down in ULX3S_SMOKE_AND_UART.md. Six new tests cover it. No RTL changed, so ulx3s_bridge.bit and its SHA256 still describe the shipped gates.

What I did not do, deliberately: change the framing. Making 0x7f transparent means either teaching the bridge the per-opcode byte counts (duplicating the core FSM inside a bridge that is currently protocol-agnostic), or byte-stuffing on both ends. Both are protocol changes needing re-synthesis and fresh bitstreams, which I cannot produce here — yosys, nextpnr-ecp5 and ecppack are all absent from this container. That is your call, not mine to make quietly.

Worth noting for #17 compatibility: fpga_harness/host/mincore_uart.py:311 raises AbortUnavailable("ABORT is a separate synchronous hardware pin; raw UART has no abort byte"). That is correct for the bare ASIC — ABORT is uio_in[6]. The in-band 0x7f is a bridge invention layered on top, so the two tools disagree about the link precisely because this framing is unsettled.

Merge-readiness: still blocked

Criterion State
Source build reproducible Not verified here — yosys / nextpnr-ecp5 / ecppack absent from this container
Strict 25 MHz closure, no allow-fail Script is correct (set -eu, no --timing-allow-fail, FREQUENCY PORT "clk" 25 MHz in LPF); archived logs report 154.37 MHz PASS — but not re-run by me
Bitstream SHA256 / provenance sha256sum -c passes on all 4 committed artefacts; ecppack_uart.log is written by the build script but not committed and not in SHA256SUMS
LPF / pins reviewed G2/B2/M1/L4 consistent with top level. wifi_gpio0 is not constrained or driven — on ULX3S that is the usual keep-alive; flagging for a board owner, I cannot test it
Safe-output audit No SDRAM/flash pins constrained; no flash tool invoked; -f never passed
0x7f transparency Open — item 2 above
PR #17 compatibility File sets are disjoint except SHA256SUMS, which will conflict textually on the second merge; resolve with make checksums

Verification run

make check                → exit 0 (163 sim + 134 harness tests OK)
make -C test sim          → PASS: leanVM-b MinCore stream ALU / tb_uart_bridge PASSED
sha256sum -c SHA256SUMS   → 4/4 OK (bitstreams, config, svf)

pyserial applies the timeout set in open_port to every individual read,
so threading args.timeout into recv_exact bounded each read rather than
the call. A byte landing just before the deadline bought the read after
it a whole fresh timeout.

Measured against a virtual clock, a 2.00 s budget took 3.90 s: one byte
at t=1.9 s, then a second read that blocked the full 2 s. A link that
dribbles bytes stretches it further, since every arrival re-arms.

Re-arm the port from the time actually left before each read and restore
the caller's value afterwards, so the next transaction is unaffected.
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_fbf468e6-43b3-43d8-bc8d-0e313bc03290)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad278d5853

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_42ee78d8-68d4-47e1-8050-05c0d7da8ab3)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4305b5d274

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga/ulx3s/build_smoke.sh Outdated
@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex review

Please review the fresh exact head 1fab44eb0e5b3bdbad1c7e6f6c4f0a24e0b1e86f only. This addresses the three current findings: pinned pyserial runtime dependency, deadline-bounded serial flush with adversarial regressions, and repository-wide ULX3S status reconciliation without hardware-validation claims. No hardware/JTAG/UART/bitstream action was performed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fab44eb0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/README.md
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_855e3726-b10e-409a-b684-5568321a29b5)

@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex Please review the current PR head b5bcaa0 only; prior review receipts predate this push.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: b5bcaa00c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e8e4d594-229d-45c4-8c31-a88d4da8b05a)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0717a3f68e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b1e048a2-7c73-46ea-b2c4-edbaf1bb1013)

@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex review

Fresh exact-head review requested for edf5c26bc219870590493fec930cce84277a37ad only. This commits the shared transaction deadline for every ULX3S serial write, validates partial write counts, and adds adversarial slow/partial-write regressions.

Fresh verification on this head: make check (261 model tests, 234 harness tests, checksum validation), make sim (including UART bridge and mutation suite), make formal, and make lean all pass. SHA256SUMS was regenerated by the documented target and revalidated. No FPGA/JTAG/UART device/bitstream load/flash/hardware action was performed.

The previously open dependency and repository-status threads were verified as already addressed in the preceding head and are now resolved; the serial-write deadline thread is addressed here and resolved. All CI/review/build receipts from before this push are stale. Hardware validation remains explicitly closed; do not merge on this request.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: edf5c26bc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
if hasattr(ser, "write_timeout"):
ser.write_timeout = remaining
written = _call_within(
lambda: ser.write(data[offset:]),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve requested inter-byte pacing

When inter_byte_delay is nonzero and the serial backend accepts the full buffer—as pyserial normally does—this writes all bytes in one call, advances offset to len(data), and skips the delay condition below. Callers requesting paced transmission therefore receive no inter-byte gaps, unlike the previous byte-at-a-time implementation; limit each write to one byte when pacing is enabled while retaining the shared deadline.

Useful? React with 👍 / 👎.

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_73c8ecd6-c444-4f28-906b-046335d9cbf7)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de296a5724

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py
Comment thread fpga_harness/ulx3s_uart.py
- Add _validate_timeout rejecting math.inf/NaN/<=0 with clear ValueError (addresses Codex line ~382 thread)
- Replace time.time() with time.monotonic() in drain() and recv_exact() (addresses Codex line ~128 thread)
- Add adversarial TimeoutValidationTest covering inf, nan, negative, zero, large finite
- Existing tests updated for monotonic clock

Refs: PR #16 Codex review threads on timeout handling
@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex review on the new head — both timeout threads addressed

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_96b331f6-b7f8-4136-8515-dad01c0e2719)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c68877c6b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread fpga_harness/ulx3s_uart.py Outdated
Comment on lines +246 to +247
ser.timeout = remaining
chunk = ser.read(n - len(buf))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound response reads with the worker deadline

When a disconnecting or stalled serial backend blocks inside read() despite its mutable timeout, this direct call never returns, so the monotonic deadline is never checked again and --timeout cannot terminate the transaction. This is the same backend behavior already guarded for writes, flushes, and in_waiting; run the read through _call_within with remaining so the CLI can abandon the port at the configured deadline.

Useful? React with 👍 / 👎.

Comment thread fpga_harness/ulx3s_uart.py Outdated
Comment on lines +161 to +163
if hasattr(ser, "timeout"):
ser.timeout = remaining
out.extend(ser.read(min(waiting, max_bytes - len(out))))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound stale-input reads as well as availability queries

When in_waiting reports bytes but the subsequent backend read wedges—for example after a disconnect or because another reader consumed the snapshot—assigning ser.timeout alone does not protect against an implementation that blocks despite that value. The direct read() can therefore outlive the drain's settling deadline indefinitely; wrap it with _call_within using the remaining budget, just as the preceding in_waiting query is wrapped.

Useful? React with 👍 / 👎.

Comment thread fpga_harness/ulx3s_uart.py
@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex review

Fresh independent review requested for exact head a94cd1a5e276ecd02ff82648166ab3869cd8ac8a only. This is a generated root SHA256SUMS repair; all prior receipts predate this normal push. No hardware action was performed.

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_2c3916d8-fc21-46bf-8771-b366e232f1da)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: a94cd1a5e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_73958b49-69cc-4ff8-8c93-0d2a6000a9ce)

@Th0rgal

Th0rgal commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@codex review

Fresh independent review requested for exact head a622528c7599cafee608f8ae3ced80fa8e848bf9 only. This closes the current UART deadline findings: all reads are worker-bounded and timeouts beyond threading.TIMEOUT_MAX are rejected; adversarial regressions and the root SHA-256 inventory are included. No FPGA source/artifact and no hardware action.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: a622528c75

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Th0rgal
Th0rgal merged commit 84f15ab into main Jul 26, 2026
9 checks passed
Th0rgal added a commit that referenced this pull request Jul 27, 2026
* fpga: add ULX3S v3.1.8 smoke + UART bridge skeleton (P0/P1)

- Minimal 25 MHz counter/LED smoke_top + LPF (v3.1.8 pin-subset hypothesis)
- UART RX/TX at 1 Mbaud, 2-flop sync, framing error handling
- uart_bridge.sv: exact lean_silicon_lsc1 instantiation, byte buffering, POR, backpressure
- New driver: fpga_harness/ulx3s_uart.py (explicit --port only)
- Icarus TB skeleton: test/tb_uart_bridge.sv
- SRAM-only build scripts; explicit prohibition of -f documented
- Real yosys/nextpnr/ecppack run; 291.97 MHz post-route; bitstream SHA captured

Ownership: PR #13 (host runtime), PR #15 (board_detect/SHA256SUMS) untouched.
Worktree: fpga/ulx3s-smoke-uart-mincore @ 03926bb

[skip ci] sprint deliverable

* docs: ULX3S_SMOKE_AND_UART.md + local artefact manifest (P3)

- Exact commands, tool versions, timing (291.97 MHz smoke / 160.26 MHz bridge)
- Bitstream SHAs recorded
- SRAM-only openFPGALoader recipe with explicit -f prohibition
- Supported MinCore transaction list
- Bounded claims only; no hardware execution evidence

[skip ci]

* fpga: repair UART bridge datapath, build paths and evidence integrity

Every response byte was being corrupted or lost on the way to the host, and
none of the checked-in verification could see it.

RTL:
- uart_tx released tx_ready at bit_cnt == 8, so data bit 7 lasted one 25 MHz
  clock instead of a full baud interval. A receiver sampled every zero-valued
  bit 7 as one (a SET echo of 00 arrived as 80). Ready now releases at 9.
- The bridge's power-on "synchroniser" only ever shifted in ones, so rst_n was
  never low and no register in the bridge or the core was ever initialised.
  The core latched x into its error state and answered nothing. Replaced with a
  zero-initialised shift register that genuinely holds reset for 8 edges.
- Both handshake directions now use a one-deep skid buffer whose ready
  deasserts on the handshake that fills it. Previously RX_VALID was pulsed
  against a ready sampled a cycle early (dropping bytes that arrived mid
  transaction) and TX_READY mirrored the serialiser a cycle late (letting the
  core hand over a second byte that overwrote the first).
- Added a sticky rx_overrun probe that drives no logic, so the bench can assert
  no byte was ever displaced.

Verification:
- Replaced the tb_uart_bridge liveness window with a real 1 Mbaud 8N1 receiver.
  It fails on a missing byte, a short final bit, a bad stop bit, a wrong or
  extra byte, and on rx_overrun, across STATUS, SET, XOR, MUL, unknown opcode
  and abort recovery. Wired it into `make sim`, which previously never ran it.
- ulx3s_uart.py --tx mul printed the board's reply and exited 0 whatever it
  was. It now computes the expected GF(2^128) product with both independent
  oracles in sim/model.py, cross-checks them, and gates the exit status.
  Added tests that feed complete but wrong 16-byte responses and require exit 1.

Build and evidence:
- build_smoke.sh and build_uart.sh resolved sources and output through one ".."
  too many, so the bridge build could not find any RTL and evidence landed
  outside the repository. Both now resolve the repository root from their own
  location and archive artefacts under the names the manifests use.
- Committed the executable bit so `make -C fpga/ulx3s smoke` runs, and pointed
  the uart target at build_uart.sh instead of an echo.
- The LPF had no frequency constraint, so a "25 MHz" claim was really nextpnr's
  12 MHz default. Constrained it; both designs still close (291.97 / 154.37 MHz).
- results/ SHA256SUMS named smoke.bit for a file archived as ulx3s_smoke.bit, so
  the integrity check could not run. The scripts now emit and self-verify both
  manifests. Regenerated ulx3s_bridge.bit from the repaired RTL; the digest is
  reproducible across three independent builds.
- .gitignore's blanket *.log dropped the synthesis and route logs the manifest
  promised. Exempted the results directory and archived them.

Not addressed by design: 0x7f still cannot appear as a payload byte, since the
bridge treats it as an abort before command framing. Escaping it would change
the wire protocol. Documented as a limitation instead.

* fpga: make UART evidence and host drain fail-safe

* fpga: resynchronize UART host transactions

* fpga: validate the fixed STATUS signature in the UART host driver

status is the default --tx, and it was the only command whose reply was
printed without being checked. A misbuilt bridge emitting four arbitrary
bytes passed the default board-smoke invocation with exit 0, so the check
that is supposed to catch a broken build was the one command that could
not fail.

Compare against 01 01 0f 08 and exit 1 on mismatch, matching how set, xor
and mul already gate their exit status. A test parses status_byte() out of
leanvm_b_stream_alu.sv so the host constant cannot drift from the RTL.

* fpga: make the advertised UART driver command line runnable

The usage example shipped `--tx 03`, the SET128 opcode byte, but argparse
only accepts command names. The first command a new user copies exited 2
before opening the port, so the documented entry point never worked.

Point the example at `--tx set` and give a status example alongside it.
PAYLOAD_BYTES now backs both the argparse choices and the width check,
so the accepted commands and their payload widths cannot drift apart.

Guarded by tests that pull every `python3 -m fpga_harness.ulx3s_uart`
line out of the module docstring and ULX3S_SMOKE_AND_UART.md, parse each
one, and check the payload width matches the command.

* fpga: refuse UART operands the bridge cannot carry intact

uart_bridge.sv derives its abort pulse from any received 0x7f, not only
from one in command position, so a 0x7f inside an operand tears the
transaction down mid-flight. Reproduced in simulation: a SET128 whose
payload byte 5 is 0x7f comes back as 15 bytes with 0xe0 from byte 5 on.

128-bit operands are arbitrary data, so ordinary vectors reach this --
about 6% of random SET payloads and 12% of random XOR/MUL operand pairs.
Previously the host printed MATCH: False and exited 1, which is the exit
status for "the board answered wrong" and points an investigation at the
silicon instead of the link.

Reject such operands up front and exit 2, and write the limitation down
as an open bridge design question rather than an LSC-1 property. The ASIC
takes ABORT on uio_in[6], a pin, and has no in-band abort byte, so this
is the bridge's framing to settle. No RTL is changed here, so the
committed bitstreams and their checksums still describe the shipped gates.

* fpga: bound recv_exact by its deadline, not by each read

pyserial applies the timeout set in open_port to every individual read,
so threading args.timeout into recv_exact bounded each read rather than
the call. A byte landing just before the deadline bought the read after
it a whole fresh timeout.

Measured against a virtual clock, a 2.00 s budget took 3.90 s: one byte
at t=1.9 s, then a second read that blocked the full 2 s. A link that
dribbles bytes stretches it further, since every arrival re-arms.

Re-arm the port from the time actually left before each read and restore
the caller's value afterwards, so the next transaction is unaffected.

* fpga: classify UART transaction timeouts

* fpga: stage build evidence until success

* fpga: publish build archive atomically

* build: include publisher tests in source manifest

* fpga: serialize shared archive publishers

* fpga: reject surplus response bytes and record artefact provenance

recv_exact returned the instant the nth byte landed, so a reply that was
wrong but happened to start with the expected prefix left its surplus
queued and the harness exited 0. Route every command through a settling
reader that requires the link to fall silent, and classify a straggler as
a transport fault rather than a match.

The docs blamed the branch base for the archived bitstreams, but that
revision contains no fpga/ulx3s file and so cannot have built them. Record
the source revision and per-input digests from the build scripts
themselves, and name the revision the artefacts actually reproduce from.

* build: do not claim a match against an unidentified revision

When the recorder ran outside a git checkout, every per-input comparison
was skipped and the dirty flag stayed false, so the manifest reported
inputs-match-revision: yes against a revision it had just recorded as
unknown. Report the match as unknown in that case, so the archive claims
only what was actually compared.

* build: record the build recipe among the provenance inputs

The recorder only digested $SOURCES and $LPF, so a build run from a locally
edited build_smoke.sh or build_uart.sh -- different yosys or nextpnr flags,
identical RTL -- still wrote inputs-match-revision: yes. The archive then
attributed its bitstream to the recipe at HEAD, which never ran and cannot
reproduce it.

Both flows now hand their own script to the recorder. The test helper reads the
recorded input set back out of the recorder invocation instead of restating it,
so a build that drops an input fails rather than quietly narrowing what the
archive claims to cover.

* results: re-record source manifests over the widened input set

The archived manifests predate the recipe becoming a recorded input, so they
covered only the RTL and the LPF. Regenerated by the recorder itself from a
clean checkout of 62e96e7, and the documented artefact revision moved to the
revision that actually holds those inputs.

Only the recorded input set moved: every RTL and LPF digest is byte-identical
to the previous manifests, and no synthesis command changed, so the archived
bitstreams are unaffected.

* docs: clarify ULX3S provenance reachability

* docs: make ULX3S artifact provenance reachable

* fix: publish ULX3S artifacts atomically on macOS

* fix: regenerate checksum inventory

* Make ULX3S build support portable

* Refresh ULX3S portable build evidence

* fix: address four Codex P1/P2 threads for macOS atomic publish

- tools/atomic_publish.py: use AT_FDCWD_DARWIN (-2) for renameatx_np on macOS
- tools/portable_build_support.py: start build in own session and forward
  cancellation via os.killpg so grandchildren (yosys/nextpnr) are signalled
- results/.../tool_versions*.txt: restore pinned OSS CAD Suite 0.67+94 / 0.10 / 1.4-79
- docs/ULX3S_SMOKE_AND_UART.md + manifests: update published SHA-256 to
  eb3d81ac... (smoke) and 7272b0d8... (bridge) matching refreshed artefacts

Preserves: reproducible source at a472265, 25 MHz timing closure, pin/output
safety (G2/B2/M1/L4 only), exact SHA256/provenance, protocol correctness
(lean_silicon_lsc1 MinCore, no packet lanes touched).

Focused tests exercising fixed modules: OK.
Gate checks (smoke, boundary, consistency, checksums): OK.
Provenance tests report expected mismatch for portable_build_support.py
because committed artefacts record the pre-fix digest; bitstream regeneration
is out of scope for this repair.

Refs: discussion_r3651892558, discussion_r3651892560,
      discussion_r3651892562, discussion_r3651892564

* build: regenerate ULX3S evidence provenance

* fix(provenance): correct docs digests and add atomic_publish.py to source manifests

* fix(provenance): correct docs digests and add atomic_publish.py to source manifests

* fix(provenance): record reachable artefact revision; add squash ancestry/reachability check

* build: refresh ULX3S source evidence after merged provenance fix

* fix(docs): correct provenance SHA and macOS verification path

* fix(provenance): record manifest generator as a build input

* build: regenerate provenance manifests and checksums

* fix: reject empty checksum manifests

* docs: align ULX3S provenance revision

* rtl: integrate packetized LSC-1 FPGA path

* test: preserve historical FPGA provenance

* fix bounded drains and build input revalidation

* build: archive packet FPGA routing outputs

* refresh ULX3S source provenance

* ci: run RTL differential in SystemVerilog job

* fix: isolate canonical packet host imports

* host: execute DEREF and JUMP programs

* rtl: execute DEREF and JUMP packets

* ci: include field encoder in source closure

* build: refresh source checksums

* fpga: bound UART drain availability queries

* build: regenerate ULX3S evidence with clean Yosys

* fix: make ULX3S UART host path installable and bounded

* docs: reconcile ULX3S harness status snapshots

* fix: reconcile ULX3S evidence status

* fix: bound ULX3S UART writes by transaction deadline

* fix: preserve paced UART writes

* fix(uart): reject non-finite timeouts, use monotonic clock for deadlines

- Add _validate_timeout rejecting math.inf/NaN/<=0 with clear ValueError (addresses Codex line ~382 thread)
- Replace time.time() with time.monotonic() in drain() and recv_exact() (addresses Codex line ~128 thread)
- Add adversarial TimeoutValidationTest covering inf, nan, negative, zero, large finite
- Existing tests updated for monotonic clock

Refs: PR #16 Codex review threads on timeout handling

* Fix LSC1 packet inverse and XOR backsolve paths

* Fix differential test checksum

* build: refresh checksum inventory

* fix(uart): bound all serial reads by deadline

* Bound packet UART exchanges by deadline

* Validate physical scalar results before retire

* Validate retirement acknowledgement

* Require fresh bounded packet startup

* Tighten physical program completion checks

* fpga: add safe Linux ULX3S preflight evidence

* docs: scope MinCore state count accurately

* fix: align jump faults and keep preflight signal-neutral

* fix review trace and protocol accounting

* docs: align host comparison status

* chore: refresh checksum inventory

* fix: decode instruction payloads before state guard

* fix: align remaining packet fault precedence
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant