Skip to content

Integrate qualified PR #24 packetized LSC-1 onto main#26

Open
Th0rgal wants to merge 76 commits into
mainfrom
integration/pr24-lsc1-main
Open

Integrate qualified PR #24 packetized LSC-1 onto main#26
Th0rgal wants to merge 76 commits into
mainfrom
integration/pr24-lsc1-main

Conversation

@Th0rgal

@Th0rgal Th0rgal commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

Integrates the qualified packetized LSC-1 work from merged PR #24 onto current main while preserving the unrelated commits already on main.

The merge conflicts were duplicate-history conflicts in the ULX3S harness, status, test Makefile, and checksum inventory. They were resolved conservatively by retaining PR #24's later hardened versions and regenerating the repository checksum inventory. Main's PR #25 documentation/logo commits remain in the ancestry and were not altered.

Validation

Passed locally:

  • make PYTHON=.venv/bin/python check
    • executable models and repository consistency passed
    • 268 FPGA-harness tests passed
    • checksum verification passed
    • structural/interface/gate-count/smoke checks passed
  • LEANVM_B_UPSTREAM=/tmp/leanVM-b-pr24 make PYTHON=.venv/bin/python sim
    • stream ALU, raw UART bridge, packet UART bridge, packet frontend, and mutation tests passed
    • packet RTL differential: 4/4 passed, including the adversarial 103-byte JUMP inverse and both XOR backsolve directions
  • PATH=... LEANVM_B_UPSTREAM=/tmp/leanVM-b-pr24 make PYTHON=.venv/bin/python m2-differential
    • 64 seeded Cargo-vetted cases plus controller edge regressions passed against frozen upstream c308034ab78619b39a59d26f3dc60e7df5b52649
  • elan toolchain install leanprover/lean4:v4.32.1 && make lean
    • Lean builds passed (14-job package build and explicit 13-job LeanVMBMinCore target)
  • sby -f gf8_mul.sby
    • passed
  • Yosys hierarchy/check/synthesis for lean_silicon_lsc1
    • passed with zero reported design problems
  • python tools/generate_checksums.py --check
    • passed
  • proof-placeholder scan
    • no sorry, admit, or axiom in the checked Lean source set

Local formal limitations (authoritative GitHub CI is requested):

  • sby -f gf128_serialize.sby could not run its engines: SMT Solver 'yices-smt2' not found in path.
  • The bound-check preflight and sby -f stream_alu_mul_pulse.sby could not elaborate: slang.so: cannot open shared object file: No such file or directory.
  • The local distro Yosys M2 synthesis command was started after hierarchy elaboration but was stopped because that older frontend expanded the controller path impractically slowly; GitHub CI's pinned OSS CAD suite remains the authoritative lint/synthesis run.

Boundaries


Note

High Risk
Large RTL and protocol-surface change at the tapeout top, with new control paths (retire, jump, deref) and host/FPGA integration; correctness relies on differential tests, not full formal ISA correspondence.

Overview
Product top is now lsc1_packet_frontend on lean_silicon_lsc1, with the raw stream ALU moved to diagnostic lean_silicon_lsc1_mincore. New lsc1_stream_adapter and lsc1_field_encoder connect packet ops to the existing MinCore byte stream.

The frontend grows from SET/XOR/MUL to NEGOTIATE, STATUS, DEREF (Cell/Pc/Fp), and JUMP, using the stream adapter instead of a standalone GF128 multiplier; RX payload width and body capture are extended for large frames (e.g. JUMP inverse witness).

Host runtime integrates Deref and Jump (pointer map, branch proposals, inverse witnesses); the frozen fixture now reaches full MATCH on memory and cycles. lsc1_packet_uart.py drives the physical packet lane (BREAK abort, capability checks, optional full program run).

ULX3S: uart_bridge PACKET_MODE (no 0x7f in-band abort in packet mode), ulx3s_packet_top + build_packet_uart.sh, UART RX wait_idle after framing errors. hardware_preflight.py adds non-programming host evidence; fpga-preflight is optional outside default check.

CI / build: Yosys top includes all packet RTL modules; gate-countmincore-state-count; sim adds packet UART bridge and sim/test_packet_frontend_rtl_differential.py (seeded byte-exact model vs RTL). Docs and info.yaml reflect implemented scalar executor vs pending BLAKE3/services.

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

Th0rgal added 30 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]
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.
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.
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.
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.
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.
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.
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.
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.
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.
- 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
@Th0rgal

Th0rgal commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

@codex review

@cursor

cursor Bot commented Jul 27, 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_e6a41744-849d-4cde-b94a-d365b470a2e2)

@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: ba03b5783b

ℹ️ 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 asic_core/rtl/lean_silicon_lsc1.sv
@cursor

cursor Bot commented Jul 27, 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_6796a7ab-264a-4e2a-9830-d273e9d5c798)

@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: 29356f1c9e

ℹ️ 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 asic_core/rtl/lsc1_packet_frontend.sv Outdated
Comment thread fpga_harness/hardware_preflight.py Outdated
@cursor

cursor Bot commented Jul 27, 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_e35afee6-39df-45ea-b249-3374f2ec090d)

@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: b76a5a0c46

ℹ️ 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 host/runtime.py Outdated
Comment thread fpga_harness/host/lsc1_packet_uart.py
Comment thread asic_core/rtl/lsc1_packet_frontend.sv Outdated
@cursor

cursor Bot commented Jul 27, 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_cfeedd55-d119-4380-988a-0ec531f8fe71)

@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: 21483f5f3d

ℹ️ 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 docs/STATUS.md
@cursor

cursor Bot commented Jul 27, 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_d742c525-07e7-4a6b-a82c-e12e5f9bf7f2)

@cursor

cursor Bot commented Jul 27, 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_bf8ca0d1-6b9e-4232-8993-9e5d099d0634)

@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

txn_id = frame_payload[0 +: 32];
pc = frame_payload[32 +: 32];
fp = frame_payload[64 +: 32];
profile = frame_payload[12*8 +: 8];

P2 Badge Keep decoder faults unbound until parsing succeeds

For a malformed instruction payload such as a JUMP whose taken byte is 2, decode_request_payload raises before returning a decoded request, so the executable endpoint emits the fault with transaction ID 0. The RTL assigns the payload transaction ID here before completing those validations and later passes it to emit_fault, making fault payload bytes 0–3 differ from the model even though the corrected detail byte is 3; retain transaction ID 0 for payload-decoding faults until the full payload has parsed successfully.

ℹ️ 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 on lines 504 to 505
end else if (result_pending) begin
emit_fault(BAD_STATE, frame_payload[0 +: 32], 0);

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 Decode instruction payloads before rejecting pending state

When a result is pending and the next correctly framed instruction contains an invalid profile, cell, or JUMP taken byte, this guard immediately returns BAD_STATE; the normative path in sim/lsc1_transaction.py::_dispatch first calls decode_request_payload, so it returns the corresponding payload fault before _handle_instruction checks transaction state. Parse and validate the fixed-size instruction payload before applying the pending-state guard to preserve byte-exact model behavior.

Useful? React with 👍 / 👎.

Comment on lines 525 to 528
if (frame_payload[13*8 +: 8] != 0) begin
decision_ok = 1'b0; decision_fault = BAD_FLAGS; decision_detail = 1;
end else if (profile > 1) begin
decision_ok = 1'b0; decision_fault = BAD_PROFILE;

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 Validate the transaction profile before its flags byte

When an instruction preamble contains both a profile greater than 1 and a nonzero transaction-flags byte, the normative _read_preamble reads and validates the profile first and therefore returns BAD_PROFILE; this ordering tests the flags first and returns BAD_FLAGS. Reverse these two validations so malformed multi-error frames remain byte-for-byte consistent with the executable protocol decoder.

Useful? React with 👍 / 👎.

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