From 4c37e25fa314bf3c60ea41a27a6a43eddd93c55d Mon Sep 17 00:00:00 2001 From: w1ne <14119286+w1ne@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:29:35 +0200 Subject: [PATCH] =?UTF-8?q?feat(examples):=20ESP32-C3=20Super=20Mini=20bli?= =?UTF-8?q?nky=20=E2=80=94=20the=20fallback=20demo=20for=20bare=20C3=20lab?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Playground has no prod compiler, so a shared/agent lab only runs from a pre-built binary. Bare esp32c3-supermini labs had none, so every C3 lab opened from the hosted MCP died with "Cannot run: no firmware". This adds the canonical GPIO8 blink example whose committed ELF the Playground can fall back to (app-side wiring lands separately). Drives the plain R/W GPIO_OUT/GPIO_ENABLE registers (silicon-valid) because the C3 gpio block is the declarative register file without W1TS/W1TC side effects. The e2e test also pins rv32 C.JAL decoding: the blinky is small enough that the linker emits a compressed jal from _start to main. Co-Authored-By: Claude Fable 5 --- crates/cli/tests/e2e_esp32c3_blinky.rs | 75 ++++++++++++++++++ examples/esp32c3-blinky/README.md | 51 ++++++++++++ examples/esp32c3-blinky/firmware/Makefile | 42 ++++++++++ examples/esp32c3-blinky/firmware/c3.ld | 69 ++++++++++++++++ examples/esp32c3-blinky/firmware/c3_uart.c | 58 ++++++++++++++ examples/esp32c3-blinky/firmware/c3_uart.h | 23 ++++++ .../firmware/esp32c3_blinky.elf | Bin 0 -> 10804 bytes examples/esp32c3-blinky/firmware/main.c | 35 ++++++++ examples/esp32c3-blinky/firmware/startup.S | 50 ++++++++++++ examples/esp32c3-blinky/system.yaml | 16 ++++ examples/esp32c3-blinky/test-blink.yaml | 21 +++++ 11 files changed, 440 insertions(+) create mode 100644 crates/cli/tests/e2e_esp32c3_blinky.rs create mode 100644 examples/esp32c3-blinky/README.md create mode 100644 examples/esp32c3-blinky/firmware/Makefile create mode 100644 examples/esp32c3-blinky/firmware/c3.ld create mode 100644 examples/esp32c3-blinky/firmware/c3_uart.c create mode 100644 examples/esp32c3-blinky/firmware/c3_uart.h create mode 100755 examples/esp32c3-blinky/firmware/esp32c3_blinky.elf create mode 100644 examples/esp32c3-blinky/firmware/main.c create mode 100644 examples/esp32c3-blinky/firmware/startup.S create mode 100644 examples/esp32c3-blinky/system.yaml create mode 100644 examples/esp32c3-blinky/test-blink.yaml diff --git a/crates/cli/tests/e2e_esp32c3_blinky.rs b/crates/cli/tests/e2e_esp32c3_blinky.rs new file mode 100644 index 000000000..ab888bc8c --- /dev/null +++ b/crates/cli/tests/e2e_esp32c3_blinky.rs @@ -0,0 +1,75 @@ +// LabWired - Firmware Simulation Platform +// Copyright (C) 2026 Andrii Shylenko +// +// This software is released under the MIT License. +// See the LICENSE file in the project root for full license information. +// +// End-to-end coverage for examples/esp32c3-blinky: the canonical ESP32-C3 +// Super Mini "hello world" (GPIO8 user LED blink + UART narration). This is +// the demo binary the Playground falls back to when a shared/agent C3 lab +// carries no firmware of its own, so a regression here means every shared +// bare C3 lab stops running ("Cannot run: no firmware" class of bug). +// +// Runs the committed firmware ELF through the committed scenario script via +// the `labwired test` CLI — no cross-compiler toolchain needed. It also +// pins rv32 C.JAL decoding: the blinky is small enough that the linker emits +// a compressed jal from _start to main, which a CJ-immediate decode bug once +// sent off into unmapped flash. + +use std::path::PathBuf; +use std::process::Command; +use std::time::{SystemTime, UNIX_EPOCH}; + +fn repo_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .canonicalize() + .expect("canonicalize repo root") +} + +#[test] +fn esp32c3_blinky_blinks_gpio8_and_narrates_over_uart() { + let root = repo_root(); + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let out_dir = std::env::temp_dir().join(format!("labwired-c3-blinky-{nonce}")); + std::fs::create_dir_all(&out_dir).expect("create out dir"); + + let output = Command::new(env!("CARGO_BIN_EXE_labwired")) + .current_dir(&root) + .args([ + "test", + "--script", + "examples/esp32c3-blinky/test-blink.yaml", + "--no-uart-stdout", + "--output-dir", + out_dir.to_str().unwrap(), + ]) + .output() + .expect("execute labwired"); + + let result_json = + std::fs::read_to_string(out_dir.join("result.json")).expect("read result.json"); + let result: serde_json::Value = serde_json::from_str(&result_json).expect("parse result.json"); + let uart = std::fs::read_to_string(out_dir.join("uart.log")).unwrap_or_default(); + let _ = std::fs::remove_dir_all(&out_dir); + + assert_eq!( + result["status"].as_str(), + Some("pass"), + "blinky scenario failed (exit {:?})\n--- stderr ---\n{}\n--- uart ---\n{}", + output.status.code(), + String::from_utf8_lossy(&output.stderr), + uart, + ); + + // The loop must actually toggle, not print once and wedge. + let ons = uart.matches("LED ON").count(); + let offs = uart.matches("LED OFF").count(); + assert!( + ons >= 2 && offs >= 2, + "expected several LED ON/OFF transitions, got {ons} on / {offs} off\n--- uart ---\n{uart}", + ); +} diff --git a/examples/esp32c3-blinky/README.md b/examples/esp32c3-blinky/README.md new file mode 100644 index 000000000..925c11002 --- /dev/null +++ b/examples/esp32c3-blinky/README.md @@ -0,0 +1,51 @@ +# ESP32-C3 Super Mini Blinky + +The canonical "hello world" for the ESP32-C3 Super Mini: bare-metal rv32imc +firmware that enables GPIO8 (the board's user LED) as an output and toggles it +forever, narrating each transition over UART0. + +This is also the demo binary the LabWired Playground falls back to when a +shared or agent-generated C3 lab carries no firmware of its own, so shared +`esp32c3-supermini` labs run out of the box instead of dying with +"Cannot run: no firmware". + +## Run it + +```sh +labwired test --script examples/esp32c3-blinky/test-blink.yaml +``` + +Expected UART: + +``` +C3 BLINKY BOOT +LED ON +LED OFF +LED ON +... +``` + +## Rebuild the firmware + +Requires the Espressif RISC-V GCC toolchain (`riscv32-esp-elf-gcc`, from +PlatformIO or ESP-IDF): + +```sh +cd examples/esp32c3-blinky/firmware +make # produces esp32c3_blinky.elf +``` + +The firmware drives the plain R/W `GPIO_OUT` / `GPIO_ENABLE` registers rather +than the `W1TS`/`W1TC` aliases: the C3's gpio block is the declarative register +file (no write-1-to-set side effects), and full-value writes are equally valid +on real silicon. + +## Files + +- `firmware/main.c` — GPIO8 blink + UART narration +- `firmware/startup.S`, `firmware/c3.ld` — bare-metal C3 boot (shared with the + Leo air-quality example: `_start` sets gp/sp, copies `.data`, zeroes `.bss`) +- `firmware/c3_uart.{c,h}` — polled UART0 debug output +- `system.yaml` — C3 Super Mini system: `status_led` board_io on GPIO8 +- `test-blink.yaml` — headless scenario: boot banner, LED transitions, and + `GPIO_ENABLE` bit 8 asserted diff --git a/examples/esp32c3-blinky/firmware/Makefile b/examples/esp32c3-blinky/firmware/Makefile new file mode 100644 index 000000000..11e345d4b --- /dev/null +++ b/examples/esp32c3-blinky/firmware/Makefile @@ -0,0 +1,42 @@ +# ESP32-C3 Super Mini blinky firmware build. +# +# Compiles a minimal bare-metal GPIO8 blink for the C3 (riscv32imc) with the +# Espressif RISC-V GCC toolchain, producing a bare ELF the LabWired simulator +# boots directly (no boot ROM). +# +# Toolchain: riscv32-esp-elf-gcc (from PlatformIO or ESP-IDF). Override CC if it +# lives elsewhere, e.g.: +# make CC=$$HOME/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-gcc + +ifneq ($(origin CC),command line) +DISCOVERED_CC := $(shell command -v riscv32-esp-elf-gcc 2>/dev/null) +ifeq ($(strip $(DISCOVERED_CC)),) +DISCOVERED_CC := $(firstword $(wildcard \ + $(HOME)/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-gcc \ + $(HOME)/.platformio/tools/toolchain-riscv32-esp/bin/riscv32-esp-elf-gcc \ + $(HOME)/.espressif/tools/riscv32-esp-elf/*/riscv32-esp-elf/bin/riscv32-esp-elf-gcc)) +endif +CC := $(DISCOVERED_CC) +endif + +CFLAGS := -march=rv32imc_zicsr_zifencei -mabi=ilp32 -ffreestanding -O2 -g \ + -Wall -Wextra -ffunction-sections -fdata-sections -I. +LDFLAGS := -march=rv32imc_zicsr_zifencei -mabi=ilp32 -nostartfiles \ + -Wl,--gc-sections -T c3.ld -lgcc + +APP_SRC := startup.S c3_uart.c main.c + +ELF := esp32c3_blinky.elf + +all: $(ELF) + +$(ELF): $(APP_SRC) c3.ld + @if [ -z "$(strip $(CC))" ]; then \ + echo "ERROR: riscv32-esp-elf-gcc not found. Install via PlatformIO or ESP-IDF,"; \ + echo " or pass CC=/path/to/riscv32-esp-elf-gcc."; exit 1; fi + $(CC) $(CFLAGS) $(APP_SRC) $(LDFLAGS) -o $@ + +clean: + rm -f $(ELF) *.o + +.PHONY: all clean diff --git a/examples/esp32c3-blinky/firmware/c3.ld b/examples/esp32c3-blinky/firmware/c3.ld new file mode 100644 index 000000000..e6b418343 --- /dev/null +++ b/examples/esp32c3-blinky/firmware/c3.ld @@ -0,0 +1,69 @@ +/* ESP32-C3 (rv32imc) linker script for the LabWired sim. + * + * Matches the chip yaml memory map (configs/chips/esp32c3.yaml): + * FLASH (IROM) : 0x42000000, 4 MB — code + rodata (XIP, loaded by paddr) + * RAM : 0x3FC80000, 400 KB — data + bss + stack + * + * The simulator loads each PT_LOAD segment by its physical address (LMA), so + * .data is placed in RAM (VMA) but loaded from flash (LMA); startup copies it. + */ +ENTRY(_start) + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x42000000, LENGTH = 4M + RAM (rwx) : ORIGIN = 0x3FC80000, LENGTH = 400K +} + +_stack_top = ORIGIN(RAM) + LENGTH(RAM); + +SECTIONS +{ + .text : + { + KEEP(*(.init)) + *(.text*) + *(.gnu.linkonce.t.*) + } > FLASH + + .rodata : + { + *(.rodata*) + *(.srodata*) + *(.gnu.linkonce.r.*) + . = ALIGN(4); + } > FLASH + + /* Global-pointer anchor: centred so ±2 KB gp-relative addressing covers the + * small-data sections. */ + . = ALIGN(8); + PROVIDE(__global_pointer$ = . + 0x800); + + _sidata = LOADADDR(.data); + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data*) + *(.sdata*) + . = ALIGN(4); + _edata = .; + } > RAM AT > FLASH + + .bss (NOLOAD) : + { + . = ALIGN(4); + _sbss = .; + *(.sbss*) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > RAM + + . = ALIGN(8); + PROVIDE(end = .); + PROVIDE(_end = .); + + /DISCARD/ : { *(.eh_frame*) *(.riscv.attributes) *(.comment) *(.note*) } +} diff --git a/examples/esp32c3-blinky/firmware/c3_uart.c b/examples/esp32c3-blinky/firmware/c3_uart.c new file mode 100644 index 000000000..1567b7972 --- /dev/null +++ b/examples/esp32c3-blinky/firmware/c3_uart.c @@ -0,0 +1,58 @@ +/* ESP32-C3 polled debug UART — see c3_uart.h. */ +#include "c3_uart.h" + +#define UART0_TX (*(volatile uint8_t *)0x60000000u) + +void uart_putc(char c) { UART0_TX = (uint8_t)c; } + +void uart_puts(const char *s) { + while (*s) { + uart_putc(*s++); + } +} + +void uart_puti(int32_t v) { + char buf[12]; + int i = 0; + uint32_t u; + if (v < 0) { + uart_putc('-'); + u = (uint32_t)(-(int64_t)v); + } else { + u = (uint32_t)v; + } + if (u == 0) { + uart_putc('0'); + return; + } + while (u > 0) { + buf[i++] = (char)('0' + (u % 10u)); + u /= 10u; + } + while (i > 0) { + uart_putc(buf[--i]); + } +} + +void uart_putfix2(int32_t v_x100) { + int32_t whole; + int32_t frac; + if (v_x100 < 0) { + uart_putc('-'); + v_x100 = -v_x100; + } + whole = v_x100 / 100; + frac = v_x100 % 100; + uart_puti(whole); + uart_putc('.'); + uart_putc((char)('0' + (frac / 10))); + uart_putc((char)('0' + (frac % 10))); +} + +void uart_puthex(uint32_t n, int width) { + static const char hexd[] = "0123456789ABCDEF"; + int i; + for (i = (width - 1) * 4; i >= 0; i -= 4) { + uart_putc(hexd[(n >> i) & 0xFu]); + } +} diff --git a/examples/esp32c3-blinky/firmware/c3_uart.h b/examples/esp32c3-blinky/firmware/c3_uart.h new file mode 100644 index 000000000..af81ed9ab --- /dev/null +++ b/examples/esp32c3-blinky/firmware/c3_uart.h @@ -0,0 +1,23 @@ +/* Polled debug UART for the ESP32-C3 in the LabWired simulator. + * + * The simulator maps a generic UART model at 0x60000000 (UART0). Its Stm32F1 + * register layout treats a byte write at offset 0 as a TX-data write, so a + * single `*(volatile uint8_t*)0x60000000 = c` transmits one character — the + * same path the firmware-esp32c3-demo crate uses. The simulator's test runner + * captures every transmitted byte into its UART log for `uart_contains` + * assertions. */ +#ifndef C3_UART_H +#define C3_UART_H + +#include + +void uart_putc(char c); +void uart_puts(const char *s); +/* Print a signed integer in decimal. */ +void uart_puti(int32_t v); +/* Print a fixed-point value given as value*100 (e.g. 4910 -> "49.10"). */ +void uart_putfix2(int32_t v_x100); +/* Print `n` as exactly `width` hex chars (upper-case, zero-padded). */ +void uart_puthex(uint32_t n, int width); + +#endif /* C3_UART_H */ diff --git a/examples/esp32c3-blinky/firmware/esp32c3_blinky.elf b/examples/esp32c3-blinky/firmware/esp32c3_blinky.elf new file mode 100755 index 0000000000000000000000000000000000000000..4b2aa9bbe64c0a34c6aa9d1fb408b7b3636213af GIT binary patch literal 10804 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 literal 0 HcmV?d00001 diff --git a/examples/esp32c3-blinky/firmware/main.c b/examples/esp32c3-blinky/firmware/main.c new file mode 100644 index 000000000..0c56268c0 --- /dev/null +++ b/examples/esp32c3-blinky/firmware/main.c @@ -0,0 +1,35 @@ +/* ESP32-C3 Super Mini blinky. + * + * Toggles GPIO8 — the Super Mini's user LED — via the plain R/W GPIO_OUT and + * GPIO_ENABLE registers, logging each transition over UART0. Bare-metal + * rv32imc; the LabWired simulator boots the ELF directly (no boot ROM). The + * C3's gpio block is the declarative register file, so the demo drives the + * full-value registers (silicon-valid) rather than the W1TS/W1TC aliases. + */ +#include + +#include "c3_uart.h" + +#define GPIO_BASE 0x60004000u +#define GPIO_OUT (*(volatile uint32_t *)(GPIO_BASE + 0x04u)) +#define GPIO_ENABLE (*(volatile uint32_t *)(GPIO_BASE + 0x20u)) + +#define LED_PIN 8u + +static void delay(void) { + for (volatile uint32_t i = 0; i < 20000u; i++) { + } +} + +int main(void) { + uart_puts("C3 BLINKY BOOT\n"); + GPIO_ENABLE |= 1u << LED_PIN; + for (;;) { + GPIO_OUT |= 1u << LED_PIN; + uart_puts("LED ON\n"); + delay(); + GPIO_OUT &= ~(1u << LED_PIN); + uart_puts("LED OFF\n"); + delay(); + } +} diff --git a/examples/esp32c3-blinky/firmware/startup.S b/examples/esp32c3-blinky/firmware/startup.S new file mode 100644 index 000000000..e61e0678e --- /dev/null +++ b/examples/esp32c3-blinky/firmware/startup.S @@ -0,0 +1,50 @@ +/* Minimal ESP32-C3 (RISC-V rv32imc) startup. + * + * The LabWired simulator loads this bare ELF and jumps to its entry point + * (`_start`) — there is no boot ROM in the C3 sim path. We set up the global + * and stack pointers, clear .bss, copy .data from flash (LMA) to RAM (VMA), + * then call main(). On return we spin. + */ + .section .init, "ax" + .global _start + .type _start, @function +_start: + /* Set up the global pointer (linker-relaxation anchor). */ + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + /* Stack pointer = top of RAM (set by the linker as _stack_top). */ + la sp, _stack_top + + /* Copy .data from flash (LMA) to RAM (VMA). */ + la a0, _sdata + la a1, _edata + la a2, _sidata +1: + bgeu a0, a1, 2f + lw a3, 0(a2) + sw a3, 0(a0) + addi a0, a0, 4 + addi a2, a2, 4 + j 1b +2: + + /* Zero .bss. */ + la a0, _sbss + la a1, _ebss +3: + bgeu a0, a1, 4f + sw zero, 0(a0) + addi a0, a0, 4 + j 3b +4: + + /* main(). */ + call main + + /* If main returns, spin forever. */ +5: + j 5b + .size _start, . - _start diff --git a/examples/esp32c3-blinky/system.yaml b/examples/esp32c3-blinky/system.yaml new file mode 100644 index 000000000..b1411a297 --- /dev/null +++ b/examples/esp32c3-blinky/system.yaml @@ -0,0 +1,16 @@ +# LabWired - ESP32-C3 Super Mini blinky. +# +# The canonical "hello world" for the C3 Super Mini: the firmware enables +# GPIO8 (the board's user LED) as an output and toggles it forever, narrating +# each transition over UART0. This is the demo binary the Playground falls +# back to when a shared/agent C3 lab carries no firmware of its own. +name: "esp32c3-blinky" +chip: "../../configs/chips/esp32c3.yaml" +external_devices: [] +board_io: + - id: "status_led" + kind: "led" + peripheral: "gpio" + pin: 8 + signal: "output" + active_high: true diff --git a/examples/esp32c3-blinky/test-blink.yaml b/examples/esp32c3-blinky/test-blink.yaml new file mode 100644 index 000000000..1f9f59ace --- /dev/null +++ b/examples/esp32c3-blinky/test-blink.yaml @@ -0,0 +1,21 @@ +# Headless run of the ESP32-C3 blinky firmware. +# +# Asserts the firmware actually drives the LED pin: GPIO8 is enabled as an +# output (GPIO_ENABLE, 0x60004020, bit 8) and the blink loop runs long enough +# to log several on/off transitions over UART0. +schema_version: "1.0" +inputs: + system: "./system.yaml" + firmware: "./firmware/esp32c3_blinky.elf" +limits: + max_steps: 400000 +assertions: + - uart_contains: "C3 BLINKY BOOT" + - uart_contains: "LED ON" + - uart_contains: "LED OFF" + # GPIO_ENABLE bit 8: the LED pin is configured as an output. + - memory_value: + address: 0x60004020 + expected_value: 0x00000100 + mask: 0x00000100 + - expected_stop_reason: max_steps