121 lines
4.8 KiB
Markdown
121 lines
4.8 KiB
Markdown
# CoreMark — EEMBC's standard embedded benchmark
|
||
|
||
CoreMark 1.0 ported to the W65816 / Apple IIgs target. Source is
|
||
vendored under `coremark-src/` from
|
||
[github.com/eembc/coremark](https://github.com/eembc/coremark).
|
||
|
||
CoreMark exercises three distinct algorithm families:
|
||
|
||
1. **Linked list traversal + insert/sort** (`core_list_join.c`)
|
||
2. **Matrix init + multiply** (`core_matrix.c`)
|
||
3. **State machine** processing a string (`core_state.c`)
|
||
|
||
…plus utility code (CRC, RNG) in `core_util.c`. Total ~2000 LOC.
|
||
|
||
This is the embedded benchmark vendors publish CoreMark/MHz scores
|
||
against (Cortex-M0, AVR, RISC-V, ...). It's a useful cross-platform
|
||
sanity check on our backend's code-quality.
|
||
|
||
## Files
|
||
|
||
- `coremark-src/` — vendored EEMBC source (read-only)
|
||
- `core_portme.h` / `.c` — W65816 porting layer (timing, malloc,
|
||
printf bridge)
|
||
- `build.sh` — compile the 5 core .c files + portme
|
||
- `runCoreMark.sh` — build + link + run under MAME
|
||
|
||
## Building
|
||
|
||
```bash
|
||
bash tests/coremark/build.sh --layer2
|
||
```
|
||
|
||
`--layer2` enables `-mllvm -w65816-dbr-safe-ptrs` (smaller, faster
|
||
pointer code; correct here because CoreMark only touches malloc/static-
|
||
array memory). It is optional: CoreMark plus the runtime it pulls in
|
||
(printf, soft-float) no longer fits the ~44 KB below the IO window at
|
||
`0xC000` either way, so `runCoreMark.sh` links it multi-segment like the
|
||
Lua test (`--segment-cap`, extra segments at bank 4+) and runs it through
|
||
`scripts/runMultiSeg.sh`.
|
||
|
||
Default iteration count is 3. CoreMark only validates a run of at
|
||
least 10 seconds, and one iteration takes about 10.8 s of emulated time
|
||
(2026-09), so 3 leaves room for the compiler to get faster. Override
|
||
for longer runs:
|
||
|
||
```bash
|
||
ITERATIONS=10 bash tests/coremark/runCoreMark.sh
|
||
```
|
||
|
||
## Running
|
||
|
||
```bash
|
||
bash tests/coremark/runCoreMark.sh --layer2
|
||
```
|
||
|
||
This is a correctness gate, not just a benchmark. core_portme.c sends
|
||
CoreMark's console output to `$02:6000` and takes its clock from a
|
||
millisecond counter at `$02:5010` that the MAME harness
|
||
(`scripts/runMultiSeg.sh`) updates every frame; bare metal runs with
|
||
IRQs off, so libc's VBL `clock()` never moves. The script fails unless
|
||
CoreMark reports "Correct operation validated" (its own CRC checks plus
|
||
the 10-second rule) and the seed, list, matrix, state and final CRCs
|
||
match the values a host build of the same sources prints. CoreMark's
|
||
own "Total time" and "Iterations/Sec" are the score.
|
||
|
||
The run ends with `0xC0DE` written to `$025000` (`0xBEEF` there means
|
||
it started but never finished); elapsed ticks are at `$025002`.
|
||
|
||
**Note:** running CoreMark under MAME inside this project's restricted
|
||
shell crashes MAME (same SIGSEGV as Lua's full interpreter run —
|
||
see `feedback_lua_compile_test.md`). The build produces a valid
|
||
binary; the run only works in an unrestricted shell. Workaround: copy
|
||
`coreMark.bin` out of the sandbox and run with the same
|
||
`runInMame.sh` invocation directly.
|
||
|
||
## Size vs Calypsi (5 core files, ITERATIONS=1, PERFORMANCE_RUN)
|
||
|
||
| File | Ours (L2+threshold=50) | Calypsi 5.16 | Ratio |
|
||
|------|----------------------:|-------------:|------:|
|
||
| core_list_join.o | 10,008 | 9,073 | 1.10× |
|
||
| core_main.o | 11,588 | 19,772 | 0.59× |
|
||
| core_matrix.o | 10,660 | 11,078 | 0.96× |
|
||
| core_state.o | 7,256 | 9,944 | 0.73× |
|
||
| core_util.o | 3,156 | 4,631 | 0.68× |
|
||
| **TOTAL** | **42,668** | **54,498** | **0.78×** |
|
||
|
||
We beat Calypsi by 22% on CoreMark overall. (Since the inline-
|
||
threshold dropped from 75 to 50 target-wide, `core_matrix.o` improved
|
||
from 1.37× → 0.96× by no longer inlining 5 nested-loop helpers.)
|
||
|
||
## Notes on the porting layer
|
||
|
||
- `ee_u32` is `unsigned long` (not `unsigned int` — on W65816 `int` is
|
||
16-bit; `long` is 32-bit). CoreMark depends on 32-bit `ee_u32` for
|
||
CRC and timing math.
|
||
- `MEM_METHOD = MEM_STATIC` — a single 2 KB static array in BSS.
|
||
Avoids dynamic alloc and the resulting heap-management overhead.
|
||
- `start_time` / `stop_time` use `clock()` which returns the 60 Hz VBL
|
||
counter. `EE_TICKS_PER_SEC = 60`.
|
||
- `HAS_FLOAT = 1` — CoreMark uses double precision for the score
|
||
calculation; our soft-double handles it.
|
||
- `MULTITHREAD = 1` — single-context. The IIgs doesn't have threads.
|
||
|
||
## Comparing builds
|
||
|
||
Lua and CoreMark together cover roughly disjoint code patterns:
|
||
|
||
| Pattern | Lua | CoreMark |
|
||
|---|---|---|
|
||
| VM dispatch | yes (`luaV_execute` 30+ case switch) | no |
|
||
| Recursive descent parsing | yes (`lparser.c`) | no |
|
||
| String + hash table | yes | no |
|
||
| Linked-list traversal + sort | (small) | yes |
|
||
| Matrix init + multiply | no | yes |
|
||
| State machine | (JSON tokenizer in smoke) | yes (formal CoreMark state) |
|
||
| CRC | yes (in smoke) | yes |
|
||
| Recursion-heavy | yes | no |
|
||
|
||
So they complement each other for backend coverage. Both now compile
|
||
to under-or-near Calypsi size with the standard Layer 2 + threshold=75
|
||
config.
|