Firmware

The firmware lives in src/ and is built with the Raspberry Pi Pico SDK. It targets the Pico 2 (PICO_BOARD=pico2) and bundles a small JSON parser (cJSON) for the v1 Hardware API.

What it does (auto-run)

  1. Auto-start on USB connect: clock, ROM emulation, and RESET release happen automatically. No commands are needed for the CPU to run.

  2. Generate PHI2: a GP28 square wave at 1 kHz by default (~1 ms per cycle) for faster CI and capture. The read command accepts optional phi2_hz (0.1–1000 Hz).

  3. Reset control: GP27 starts as OUTPUT LOW, then releases to INPUT (the pull-up runs the CPU) once USB is connected.

  4. ROM emulation: a 32 KB rom_image[] in SRAM, mapped to CPU $8000–$FFFF. When A15 = 1 and RWB is high (read cycle), drive GP15–GP22 from rom_image[addr & 0x7FFF]; otherwise the data bus is Hi-Z so the CPU can drive writes. Implemented with GPIO polling (reliable at the supported 0.1–1000 Hz PHI2 range; PIO + DMA is a future upgrade for higher clocks).

  5. Built-in demo program: matches Romulan demo.txt — writes $05 then $14 then $08 to RAM-side stores and ends in STP ($DB) so capture stops cleanly.

  6. Hardware API: structured host control over USB-CDC serial (see Hardware API). The read command returns batched cycles events (up to batch_size cycles per read_event poll); drive can force the data bus for diagnostics.

Build

export PICO_SDK_PATH=~/vsarm/pico-sdk   # your SDK checkout path
cd src
mkdir -p build && cd build
cmake ..
make

The build produces build/piclone.uf2. The project bundles pico_sdk_import.cmake, so no separate SDK-import step is needed. See Host Tools for flashing and uploading ROMs.

C API reference (Doxygen)

The following is generated from the firmware sources by Doxygen and rendered via Breathe.

Hardware API (hardware_api.h)

Functions

void hardware_api_init(const hw_context_t *ctx)
void hardware_api_handle_enq(void)
void hardware_api_on_bus_cycle(uint16_t addr, uint8_t data, bool rwb_pin)
void hardware_api_poll(void)
uint16_t hardware_api_last_addr(void)
bool hardware_api_is_reading(void)
bool hardware_api_monitor_enabled(void)
bool hardware_api_exchange_active(void)
bool hardware_api_drive_enabled(void)
uint8_t hardware_api_drive_value(void)
void hardware_api_diag_read_reset(uint32_t *calls, uint32_t *edges, uint32_t *high_no_edge, uint32_t *low_no_edge, uint32_t *alarm_fires)
struct hw_context_t
#include <hardware_api.h>

Public Members

uint8_t *rom_image
size_t rom_size
bool *rom_active
float *current_hz
void (*reset_assert)(void)
void (*reset_release)(void)

Serial framing (protocol.h)

Defines

CTRL_ENQ
CTRL_ACK
CTRL_STX
CTRL_EOT
CTRL_NACK
PROTO_JSON_MAX
PROTO_BINARY_MAX

Typedefs

typedef void (*proto_idle_fn)(void)

Functions

void proto_set_idle_hook(proto_idle_fn fn)
void proto_idle_pump(void)
bool proto_read_byte(uint8_t *out, uint32_t timeout_ms)
bool proto_write_byte(uint8_t b)
bool proto_send_ack(void)
bool proto_send_nack(void)
bool proto_read_frame_payload(uint8_t *buf, size_t buf_size, size_t *out_len)
bool proto_send_frame(const uint8_t *payload, size_t len)