Start Board Implementation - new-board bring-up
SkillDev toolsThe user invokes `/start-board-implementation` to begin bringing up a NEW board/ROM in CERF. It acquires the ROM (naming the board is enough - no path required), checks IDA MCP, then independently confirms - from the ROM bytes and the internet - the board identity, the SoC/CPU family, what CERF already supports, any reusable SoC, and whether the ROM is on the public manifest. It shows a fixed emoji readiness table + session estimate and asks `[yes|no]`. On `yes` it seeds a cross-session tracking doc and drives the boot-driven bring-up loop (build → run → read the first fault → research → implement fully → `/verify` → repeat). Invoke when the user types `/start-board-implementation`.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Start Board Implementation - new-board bring-up skill
What this skill tells your AI
The instructions your AI receives, as published by gweslab/cerf in .claude/skills/start-board-implementation/SKILL.md and read by ahel’s review.
Obey CLAUDE.md and every agent_docs/ page; this ritual never overrides them.
Governing principle: the ROM is the only trusted starting point, and YOU establish every fact yourself. A stated SoC/board is a hint, not a fact - confirm board, SoC, CPU, and CERF's existing support from the ROM bytes + the internet before writing a line. A bring-up that starts on an unverified assumption wastes dozens of sessions.
Phase 0 - Welcome
Open with a brief, warm welcome + thank-you BEFORE Gate A1:
🎉 Welcome - and thank you for contributing a new board to CERF! I'll start from your ROM, confirm the board/SoC facts myself, and lay out what the bring-up will take before we commit to it.
A line or two, then straight into the gates. The welcome never delays a gate.
Phase A - Gates
Gate A1 - Acquire the ROM (fail only if nothing resolves)
The user does not have to type a path - naming the board ("implement simpad
sl4") is enough. All regular dev ROMs live under bundled/devices/, so that
listing is your index.
- Board name given →
ls bundled/devices/and match by FAMILY, not exact string (do NOT depend oncerf.json- optional, usually absent for a user's own ROM). Tolerate variant/generation/letter/separator differences: "simpad cl4", "simpad sl4", "SIMpad SL4" all resolve to thesimpad_sl4_*bundles. You are identifying the board, not string-equality testing a folder. - Path inside
bundled/devices/→ stat it, use it. - Path outside
bundled/devices/→ ask whether to copy it intobundled/devices/first (where every dev ROM lives); proceed once placed, or if the user says to run it in place. - One candidate → record its ROM path, continue.
- Several candidates for the same board (NORMAL - a board commonly ships
multiple ROM generations) → SUCCESS, not a problem. Same
Board::; name the generations, take the newest (or ask one short "which generation?"), continue.
HARD RULE - candidates found ≠ "absent". If ls surfaced any plausible
bundle for the named family, you have RESOLVED - you may NEVER report "no ROM /
not found" while holding a list of matching bundles. A variant/letter token
mismatch against bundles that are clearly the same family is a RESOLVE, never a
fail. Never assert presence/absence from memory; ls in THIS run and read it
like a human picking the obvious match.
Only if ls produced ZERO plausible candidates - STOP and FAIL:
❌ I couldn't find any ROM for "" under
bundled/devices/. Either sync it via the launcher or point me at the ROM path directly, then re-run/start-board-implementation.
Gate A2 - IDA MCP connectivity (warn + ask if absent)
Reverse engineering confirms every fact below and drives the whole loop.
-
The presence check IS the "list instances" call.
ToolSearchqueryida, find the instance-list tool, load its schema, CALL it. Whether the tool is available to call is the entire test - callable = IDA MCP running; no such tool = not running. -
Call succeeds → ✅ running. 0 open instances is NORMAL - IDA is usually closed and ROMs unextracted at the start; the call succeeding is the proof, not the count. You bring IDA up yourself when a step needs it (Phase B / B1).
-
No list-instances tool to call at all → not running. Do not fail - warn and ask:
⚠️ No IDA MCP detected. Without reverse engineering I'm badly limited - bring-up is decompile-driven (cracking the kernel OEMAddressTable for page tables, decoding which driver touches each register), and going blind tends to dead-end and burn far more sessions. The MCP server lives in this repo:
tools/ida_server.py(load inside IDA) +tools/claude_ida.py(the MCP client);tools/open_ida.py --wait <pe>opens a module. Install those and reconnect for a real shot.Continue anyway without RE?
[yes|no]No → stop. Yes → continue, marking the RE-dependent table rows ⚠️.
Phase B - Establish the facts (you, from the ROM + the internet)
Minimum traversal to fill the table. You are IDENTIFYING, not implementing. Each fact gets a source.
- B0 - Which acceptance pipeline? Before extraction, settle what the file IS
(
agent_docs/rom_acceptance.md): flat XIP, a recognised container (B000FF / NOSAJ / ARNOLD), or a whole-storage dump the guest's own boot path reads. Check the leading magic, the presence ofECECmarkers (a CE2-era image legitimately has none →ResolveRomhdrStructural), and whether the image is a raw bus capture needing normalization (aliasing, wrap, pad). This decides whether B1's extractor can even run. - B1 - Extract & inventory the modules. The ROM is usually NOT extracted yet -
you do it:
tools/extract_bundles.pyproduces per-module PEs underreferences/extracted-roms/<device>/<rom>/fs/Windows/. Open a module withpython tools/open_ida.py --wait <pe>(--waitblocks until it's usable; background it if you have parallel research). Note kernel/coredll/gwes/filesys/ device/driver module names - driver names are SoC tells (e.g.*_mx31.dll→ i.MX31). - B2 - Confirm the declared board is the right one. The declared
board_idselects theBoardContext(agent_docs/rules.md§ "Per-device facts come from the ROM"); confirm that declaration names the silicon actually in the ROM before writing aBoardContextasserting it.meta.soc_familyand the user's word are hints, never facts. Evidence, in order of weight: the B1 driver/module names (an OEM BSP names its drivers after its own silicon); the register bases the OAL actually addresses; then the OEM's model string if one appears in the blob. A byte match is evidence only once you have shown the bytes are the string and not arbitrary data. - B3 - Board already in CERF? Read the
Boardenum incerf/boards/board_context.h; listcerf/boards/+bundled/devices/. Match the B2 identity → fully present / different-ROM-revision / absent. - B4 - SoC / CPU family. From the identity + driver/OEM names, determine the
SoC, its CPU architecture (
CpuArch::Arm/CpuArch::Mips- which JIT engine the board runs), and the specific core with its ISA level (ARM720T, ARM920T, SA-11xx, ARM1136, Cortex-A8; R3000A/R3900/R4100/R5000-class MIPS, …). Confirm via the internet (datasheet / Linuxarch/arm/mach-*orarch/mips/, QEMU, device specs) - not the user's word. - B5 - SoC implemented in CERF? Reusable SoC? Read the
SocFamilyenum; listcerf/socs/+cerf/cpu/. Is this SoC present? Is the core's strategy set undercerf/cpu/<core>/-ArmProcessorConfig/CoprocEmitteron ARM,MipsProcessorConfig/MipsCp0Emitteron MIPS? If absent, is there a close relative sharing silicon/core? Reuse is the difference between a short and a long bring-up - name it. - B6 - Public manifest.
WebFetchhttps://cerf.cx/cerf-bundles/manifest.json. Listed → officially distributed (tell the user). Not listed → user's own ROM; if it's a genuinely unusual board, suggest they submit it (CERF Discord orcerf@dz3n.net) so other devs benefit - a suggestion, never a requirement.
Phase C - Readiness table (FIXED structure - identical for everyone)
Emit exactly this. Same columns/rows/order every run. Finding = the fact + its
source; Status = one emoji (✅ present/reusable/good · ⚠️ caution/new
work/unconfirmed · ❌ missing/blocker).
## /start-board-implementation - bring-up readiness
| # | Check | Finding | Status |
|---|--------------------------------|-------------------------------------------|--------|
| 1 | ROM acquired | <bundle / path> | ✅/❌ |
| 2 | IDA MCP connectivity | <running, N instances | not running> | ✅/⚠️ |
| 3 | Board identity | <declared board.id> confirmed by <tells> | ✅/⚠️ |
| 4 | Board already in CERF | <Board::X exists | absent> | ✅/❌ |
| 5 | SoC / CPU family | <SoC>, <core>, <CpuArch + isa level> | ✅/⚠️ |
| 6 | SoC implemented in CERF | <cerf/socs/<x> present | absent> | ✅/❌ |
| 7 | Reusable / similar SoC or core | <what reuses what | none - from scratch> | ✅/⚠️ |
| 8 | On public remote manifest | <listed | not listed - user ROM> | ✅/⚠️ |
Under the table, the session estimate from rows 6-7:
- SoC supported + peripherals reusable → ~5-40 sessions (board complexity still swings this widely).
- New CPU/SoC from scratch → ~40-100+ sessions, impossible to bound up front.
Then ask on its own line:
Start the bring-up?
[yes|no]
STOP and wait. Do not begin work, do not create any document, until yes.
Phase D - On yes: seed tracking, teach the workflow, start
D1 - /tracking create (the yes authorizes this one write)
Invoke the tracking skill's CREATE for a new board bring-up doc. It's an
umbrella / progress tracker (many independent peripheral workstreams), so
keep it a coarse index. Seed it with:
TASK & WHY- bring up board<X>; why it matters; empty FORBIDDEN CONCLUSIONS / BANNED APPROACHES.- A standing
VERIFY GATEline (survives compaction): "Every finished implementation chunk - a peripheral, anArmProcessorConfig/CoprocEmitter, thePageTableBuilder, theBoardContext, the LCD/INTC/timer/ DMA/touch models - is run through/verifyBEFORE the next chunk, verdict recorded in that session's/tracking update(CODE STATE, verbatim with file:line). Never skip the gate on JIT/MMU/CPU changes." - A
PROCEDUREline pointing at the durable method: "Follow the bring-up loop and ground rules in.claude/skills/start-board-implementation/SKILL.md§ The bring-up loop." (Plus the committed reference set:CLAUDE.md,agent_docs/rules.md,agent_docs/debugging.md,agent_docs/code_style.md.) Session 0- paste the Phase C readiness table verbatim (identity + source, SoC/CPU, what CERF has, reuse plan, manifest, estimate). The durable baseline a compacted agent resumes from. Real work starts at Session 1.
The yes authorizes this single create only - not standing authorization; every
later write needs its own /tracking update from the user.
D2 - Teach the cross-session workflow (say this to the user)
For a productive multi-session bring-up:
- End of each session:
/tracking update(you invoke it).- Then
/compact.- Then
/tracking restorenext session to reload the world.One protecting rule: never run
/trackingbecause I asked you to. If I propose updating the tracking doc, that's a bailout - only YOU decide when a session ends. I never raise the tracking document myself.
D3 - Run the bring-up loop (the procedure below)
Pick the entry point from the table, then run § The bring-up loop:
- New SoC/core (rows 6-7 ❌) → start at the CPU/JIT strategy set for the
board's
CpuArch: authorArmProcessorConfig+CoprocEmitter, orMipsProcessorConfig+MipsCp0Emitter, from the CPU architecture reference manual + the core TRM (downloaded toreferences/<soc>/first), then thePageTableBuilder/ memory map, then the peripheral loop. - SoC supported (rows 6-7 ✅) → start at the
BoardContext(a new concrete reporting the board's constants; give the board an id, add it to theBoardContextid table, and setboard.id+rom.primaryin the bundle's cerf.json) + thePageTableBuilder(crack the kernel's OEMAddressTable in IDA), then the peripheral loop.
The bring-up loop (the engine)
The kernel boots, hits an unimplemented register/MMIO address, and
HaltUnsupportedAccess fatals with the PA + guest PC. One blocker per cycle,
each step obeying the rules in CLAUDE.md / agent_docs/ (don't restate them -
follow them):
- Build (
CLAUDE.md§ Build) - confirm it actually succeeded. - Run with a SHORT GNU
timeoutand a per-task--log-file(agent_docs/debugging.md§ Timeout). Never background cerf; never read stdout. - Read the FIRST
FATAL|unsupported|unmapped|rejected|Haltline - that PA + guest PC is the blocker. - Decode the PA → peripheral block from the datasheet memory map.
- Research it the nuclear-bisection way (
agent_docs/debugging.md): find the register in the SoC manual, decompile the guest PC that touched it for the exact semantics, write the citation excerpt underreferences/<soc>/. - Implement the blocker fully - no fake-success stub; the mandatory-real
peripherals are never stubbed (
agent_docs/rules.md§ Board Implementation). /verify, fix anyCRITICAL PROBLEM FOUND;- Repeat until GUI, then interaction - the user-observable bring-up target.
Signals
- GitHub stars
- 91
- Forks
- 9
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
start-board-implementation- Source
- github.com/gweslab/cerf