evidence · 3.5 KiB

heartbeat-host.md

Committed at docs/evidence/second-core/heartbeat-host.md. This is what grounds the requirements that cite it.

Open raw source

Physical CPU0 heartbeat: host preparation only

Prepared 2026-09-26 America/Chicago; no board release was run for this file. The working tree was impl/small-core-heartbeat-20260926 at base ba23c967. bash tools/test-small-core-heartbeat.sh passed on the host. With SMALL_CORE_OUTPUT_DIR=/tmp/k230-small-core-heartbeat-host, the generated 80-byte heartbeat.bin had SHA-256 11d675ac6a4993c5ea820603a59778993487a478d07b1e30d3e9545ae22ba133. The temporary output is a host artifact, not a committed firmware image.

Source-backed physical path

The pinned k230_linux_sdk revision is 1104236db4d1e47873bd68924f912747b820228c (nix/k230-sdk-src.nix). nix/uboot-k230.nix copies that U-Boot overlay and uses k230_canmv_v3_defconfig. In its board/canaan/common/sdk_autoconf.h:16, CONFIG_LINUX_RUN_CORE_ID is 1. k230_spl.c:150 loads U-Boot; k230_img.c:276-285 releases physical CPU1 into that U-Boot image and parks physical CPU0 in wfi. Thus the normal U-Boot prompt is on physical CPU1, supported by source and the later RVV/256 KiB L2 Linux observation.

The same overlay’s arch/riscv/cpu/k230/cpu.c:125-166 defines boot_baremetal 0, which writes physical CPU0’s vector at 0x91102100, asserts/deasserts CPU0_RST_CTL at 0x91101004, and flushes the specified image range. The built U-Boot ELF in /nix/store/zzz867rp8drrwkhibj0rlqc04sk9g7f8-uboot-k230_canmv_v3_defconfig-riscv64-unknown-linux-gnu-2022.10 contains that command (nm and a string search). The vendor default environment and this project’s firmware/stage1/tdisplay.env:29 load a physical CPU0 bare-metal image at 0x07000000 before that command. This grounds the scratch load address, not a claim that the new payload has run.

The 80-byte program loads at 0x07000000. It writes CPU0’s read-only mhartid and misa CSRs at 0x07002008 and 0x07002010, and emits the same monotonic 64-bit counter at 0x07002000, 0x07003000, and 0x07004000. Those three output pages lie within the vendor’s CPU0 bare-metal scratch range and outside the actual 80-byte file. The program issues fence rw,rw and Canaan U-Boot’s documented l2cache.ciall encoding (cache.c:158-161, 0x0170000b) after each update. Each output page is read once from the CPU1 U-Boot prompt, separated in time, to avoid relying on a second read of a line CPU1 may retain in its own cache. This is a bounded diagnostic protocol, not a demonstrated CPU-to-CPU coherency contract.

llvm-objdump and readelf confirmed entry 0x07000000, the output symbol at 0x07002000, scalar RV64 ELF attributes without V, CSR reads, the three stores, and the L2 maintenance word. The host check proves only layout and instruction selection. It does not prove that CPU0 starts, that its L2 flush publishes to CPU1, or that the board’s U-Boot reads those pages freshly.

The live Linux memory snapshot collected by the coordinator at 2026-09-27T03:34:24Z (/tmp/k230-core-memory-result.txt) describes 1 GiB at 0x00000000..0x3fffffff, with 0x10000000..0x103fffff reserved for the framebuffer and no second-core reservation. It does not reserve these probe pages for Linux; this first test stays at the U-Boot prompt and ends in a reset. A Linux-coexistence candidate must use a separate DT reservation and read-only observer before it can boot.

python3 -m unittest discover -s tests -p test_ums_target.py passed 16 host tests. It proves the UMS helper’s refusal guards, not that a disposable card or a recovery rehearsal was available during this preparation.