A formal, executable ISA specification of the PowerPC 601 — the first PowerPC processor, the bridge between POWER and PowerPC, and the CPU of the original Power Macintosh — written in Sail, the ISA description language used for the official RISC-V, CHERI and Arm specification models.
The specification is written from the primary source, Motorola's MPC601
RISC Microprocessor User's Manual, cited throughout the model by section
(§6.8.3.2), table (Table 3-40), figure and chapter-10 instruction page.
Where the manual is ambiguous or contradicts itself — and on the 601 it does
so more than once — the model states the conflict, argues which reading
governs, and says so in the source rather than picking silently. The manual
is not redistributed here; you will need your own copy to follow the
citations.
The model is structured for more than one core: everything architecture-common
lives in model/, and each implementation supplies the hooks in
model/ppc_core_iface.sail from its own directory under model/cores/. Only
the 601 exists today; the 603 and 604 are the reason the seam is there.
AI tools were used in the making of this project — writing the specification, drafting documentation, and checking it against the manual. That is worth stating plainly up front, because a specification is offered as a reference and you should know how it was produced before trusting it as ground truth.
| Piece | State |
|---|---|
| Instruction AST, decoder and execute semantics — 187 instructions | done |
The 28 POWER holdovers the 601 keeps (abs, doz, mul/div with MQ, the *q shifts, lscbx, clcs) |
done |
Disassembler — a bidirectional assembly mapping, objdump-style syntax, every instruction covered |
done |
| Floating point — IEEE 754 single/double from scratch, exact intermediates, full FPSCR and exception model | done |
| MMU — BATs, segment registers, hashed page table, I/O controller interface segments, protection, R/C bits | done |
| Exceptions — alignment, DSI/ISI, program, FP unavailable, decrementer, system call, trace, run mode, checkstop | done |
| RTC, DEC and the 601's dual SPR numbering | done |
| Debug facilities — IABR/DABR address compare | partial: IABR compare modes 111 and 100 unimplemented |
| Cores other than the 601 | interface scaffolded, only p601 implemented |
| Cache model | out of scope — dcbz has its architectural effect, the rest are no-ops |
| TLB | deliberately not modelled; translation walks the page table afresh each access |
| Devices and platform | out of scope — the harness offers a halt call and a final state dump |
This model has not been validated against real PowerPC 601 hardware. Everything below is synthetic: it checks the model against the manual and against itself, not against silicon.
- Typecheck and coverage (
make check) — the model must typecheck with zero errors and zero warnings, and everyunion clause astmust have exactly onemapping clause assembly. Sail does not require a scattered mapping to be total, so that second check is done by the build rather than the compiler; a missing disassembly clause would otherwise only surface as a match failure at runtime. - Test programs (
make test) — 53 PowerPC assembly programs intest/, assembled and linked by a real PowerPC cross toolchain, run under the generated emulator to a halt, with the full architectural state diffed against a checked-in.expectedfile. Two things are worth noting about them. First, the assembler is itself a check: several tests record encodings GNUasrefuses, which is how the model's encdec clauses get an independent opinion. Second, the expected values are derived by hand from the manual — each program's header works out what the answer must be and why, before the code — so they are not merely a snapshot of what the model happened to print. - Disassembler exercise (
make test-disasm) — every test program is re-run with tracing on, which puts theassemblymapping over every instruction actually executed.
What this does not cover: instruction timing, the cache and bus protocols, the boundedly-undefined cases where the model necessarily picks one legal answer, anything the manual gets wrong about the silicon, and every corner no test happens to reach. Measurements from a real 601 are the single most valuable thing anyone could contribute — see CONTRIBUTING.md.
Places where the model knowingly differs from the manual, all marked TODO in
the source:
- Invalid instruction forms trap. §5.4.7 is explicit that "instructions
using an invalid instruction form do not take a program exception, but
instead cause results that are boundedly undefined". Reserved-field
violations currently decode to
ILLEGALand raise a program exception instead. This is one decision affecting many instructions. - Undefined SPR bits read back. Table 2-14 requires SDR1, HID0 and HID15/PIR to return zero in their undefined bit positions; they currently keep whatever was written. (FPSCR, which the same table covers, is masked correctly.)
- IABR compare modes
111and100are not implemented. - HID0[LM] takes effect immediately. §2.4.3 requires software to bracket
the
mtsprwith threesyncinstructions either side; that binds the program rather than the processor, and the pipeline behaviour it guards against cannot arise in a model that completes one instruction at a time. - No TLB, so reference and change bits are written on every access rather than on a miss — the same values, differing only in bus traffic, which is not modelled either.
model/prelude.sail— stdlib imports, bit-numbering convention, helpersmodel/ppc_types.sail— common types, fault and access kindsmodel/ppc_core_iface.sail— the hooks an implementation must providemodel/ppc_regs.sail— architected registers (GPR, CR, XER, MSR, FPSCR, SRs)model/ppc_softfloat.sail— IEEE 754 arithmetic built from scratchmodel/ppc_mem.sail— flat physical memory, endianness, address mungingmodel/ppc_mmu.sail— translation: direct, BAT, segment + hashed page tablemodel/ppc_exceptions.sail— the exception model and vector dispatchmodel/ppc_sprs.sail— architecture-common SPR read/write clausesmodel/ppc_insts_*.sail— semantics by family: integer, load/store, branch, floating point, system, cachemodel/ppc_assembly.sail— theassemblymapping (disassembler/assembler)model/ppc_step.sail— fetch/decode/execute and interrupt deliverymodel/ppc_debug.sail— the harness halt convention and state dumpmodel/main.sail— emulator entry pointmodel/cores/p601/— the 601: its registers, SPRs, MMU quirks, POWER instructions and core-interface implementationtest/— assembly test programs and their expected final state
File order matters to the build: Sail requires declaration before use, and the
scattered instruction and SPR definitions are opened and closed by
ppc_insts_begin.sail / ppc_insts_end.sail. The ordered list lives in the
Makefile.
Needs the sail compiler (developed against 0.20.2; the relocatable
binary release works without
an OCaml toolchain), z3, a C compiler, libgmp-dev and zlib. The test
programs additionally need a PowerPC cross toolchain — on Debian/Ubuntu,
apt install gcc-powerpc-linux-gnu.
make check # typecheck + assembly-clause coverage
make emulator # build the C emulator -> build/ppc_p601
make run # build and run the embedded smoke test
make test # assemble test/*.S and diff against test/*.expected
make test-disasm # re-run those programs with tracing, exercising the disassembler
make cleanCore selection is a variable, make CORE=p601 (the default, and currently the
only one).
- Bit numbering. PowerPC numbers bits big-endian — bit 0 is the most
significant — while Sail works best with
decindexing. The model usesdecthroughout and every bitfield carries the manual's numbers in a comment, so the two can be checked against each other. The conversion issail index = 31 - manual index. - The manual's vocabulary, and no width parameter. Names in the model
are the ones the manual uses:
wordfor 32 bits,ridx/fridxfor therA/frAoperand fields,Completion/COMPLETEDbecause the manual describes instructions as completing and never once says "retire",MemoryAccessbecause it says "memory access" rather than the later architecture's "storage access". There is deliberately no register-width parameter. PowerPC fixes its data lengths — a byte, half word, word and double word are 8, 16, 32 and 64 bits on every implementation — and only some registers follow the implementation width: the manual says the LR is "32 bits wide in 32-bit implementations" (§1.3.2.1.8) but that the CR is flatly "a 32-bit register" (§2.2.4), and that the segment registers are "present only in 32-bit implementations" (§6.1.11). One alias standing for all of those would be wrong for most of them, so the model spells 32 bitswordand leaves the distinction to be drawn properly if a 64-bit core is ever added. The 601, 603 and 604 are all 32-bit implementations. - Floating point from scratch. Sail's bundled float library declares its
operations as external primitives the C backend cannot implement, and the
pure-Sail one has no multiply, divide or fused multiply-add. Binding
Berkeley SoftFloat through C glue would add a build dependency and tie the
model to the C backend, so the prover backends could not run floating point
at all. Instead a finite value is carried as an exact
(sign, significand, exponent)triple over arbitrary-precision Sail integers, so every operation but division is computed exactly and the "infinitely precise intermediate result" of §2.5.1 is literal rather than emulated with guard and sticky bits. Rounding happens once. PowerPC's habit of delivering an enabled overflow or underflow with the exponent adjusted by a constant then falls out as one addition. - No TLB, on purpose. Walking the page table on every access is
observationally equivalent to a TLB that is always coherent with memory,
which is what
tlbieexists to arrange. - "Undefined" means unchanged. Where the architecture leaves a register undefined the model leaves it alone rather than inventing a value, which is what lets a test observe that an instruction did not write it.
- Flat binaries, not ELF. The Sail C runtime's ELF loader accepts only ARM, RISC-V and x86 objects, so test programs are loaded as flat binaries with the entry point supplied separately until that is fixed upstream.
MIT — see LICENSE.
PowerPC is a trademark of International Business Machines Corporation. All trademarks referenced in this project are the property of their respective owners and are used for identification purposes only; no endorsement by or affiliation with the trademark holders is claimed.