feat(riscv): bare-metal startup + linker script generators#89
Open
avrabe wants to merge 1 commit intofeat/riscv-i32-selectorfrom
Open
feat(riscv): bare-metal startup + linker script generators#89avrabe wants to merge 1 commit intofeat/riscv-i32-selectorfrom
avrabe wants to merge 1 commit intofeat/riscv-i32-selectorfrom
Conversation
Track B3 — adds the runtime infrastructure that turns synth's RISC-V .text into a bootable firmware ELF. ## startup.rs — RiscVStartupGenerator Emits C source (with inline RISC-V asm) that: 1. Disables interrupts (mie, mip = 0) 2. Sets sp/gp/s11 (linear-memory base) from linker symbols 3. Installs `_trap_entry` into mtvec (direct mode) 4. Optionally enables the FPU via mstatus.FS = 1 5. Copies .data from flash to RAM 6. Zeroes .bss 7. Calls main(); spins via `wfi` if main returns The trap entry saves the AAPCS-style caller-saved set (ra, t0..t6, a0..a7), marshals mcause/mepc/mtval into a0/a1/a2, and dispatches to a weak C function `synth_trap_handler` that the firmware can override. `StartupConfig` exposes three knobs: external_irq_count (PLIC wiring), trap_returns (mret vs spin in default), enable_fpu (mstatus FPU enable). ## linker_script.rs — RiscVLinkerScriptGenerator Emits a GNU-`ld` script that: - Places `.text._reset` at the entry point so the reset vector is at the start of flash. - Aligns `.text._trap_entry` to 64 bytes (works for both mtvec direct and vectored modes — vectored requires 64-byte alignment). - Splits `.data` between flash (load address) and RAM (run address) so the startup copy loop has well-defined bounds. - Provides `_stack_top`, `_data_start`/`_data_end`/`_data_load`, `_bss_start`/`_bss_end`, `__linear_memory_base`/`__linear_memory_end`, and `__global_pointer$` (= `_data_start + 0x800`, the standard RV small-data anchor). - `LinkerScriptConfig` configures flash/ram origins, linear-memory size, and stack size. ## Memory model The wasm linear-memory base lives in **s11** — chosen because the RV psABI marks it callee-saved, so all selector-emitted load/store sequences (`add tmp, s11, addr; lw rd, offset(tmp)`) work without per-callee re-loading. The startup code pre-loads s11 from `__linear_memory_base`, which the linker script positions in RAM. ## Test summary * 16 new tests (8 in startup, 8 in linker_script). Total RISC-V tests now 98 (was 82). * All workspace tests still pass. * `cargo clippy --workspace --all-targets -- -D warnings` clean. ## Out of scope (B4) * CLI integration of `--link` for RISC-V (riscv64-unknown-elf-gcc invocation) * PLIC wiring (external_irq_count is exposed but not yet emitted) * Calculator demo running on Renode RV32 with full firmware * Kiln-builtins-style host bridge for RV32 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Track B3 — adds the runtime infrastructure that turns synth's RISC-V .text into a bootable firmware ELF. Stacked on #88.
What this adds
`startup.rs` — `RiscVStartupGenerator`
Emits C source (with inline RISC-V asm) that:
Trap entry saves caller-saved registers, marshals mcause/mepc/mtval into a0/a1/a2, dispatches to a weak `synth_trap_handler` (firmware-overridable).
`linker_script.rs` — `RiscVLinkerScriptGenerator`
Emits a GNU-`ld` script that:
Memory model
The wasm linear-memory base lives in s11 — chosen because the RV psABI marks it callee-saved, so all selector load/store sequences (`add tmp, s11, addr; lw rd, offset(tmp)`) work without per-callee re-loading.
Test summary
Out of scope (B4)
🤖 Generated with Claude Code