ATSAMD21G18A (Microchip SAM D21) — Arduino Zero / Feather M0 class
A 48-pin Cortex-M0+ with 256 KB flash at 0x0 and 32 KB SRAM at 0x2000_0000,
running at 48 MHz once the DFLL closes its loop. It is the silicon behind the
Arduino Zero and MKR line, the Adafruit Feather M0 / QT Py / Trinket M0 family,
and CircuitPython's original target.
This is the first Microchip part in the engine, and two things about the family are unlike anything already onboarded — both places where a wrong model fails silently rather than loudly:
- SERCOM is one block that becomes three peripherals.
CTRLA.MODEdecides whether an instance is a USART, an SPI controller or an I²C controller. This profile models USART mode; a SERCOM put into another mode goes inert rather than answering an SPI driver out of a USART register map. - PORT's SET/CLR/TGL registers are aliases, not state. Silicon reads all
four DIR aliases back as
DIRand all four OUT aliases back asOUT. A model that stored them separately reads back the last write mask, so a driver that sets a pin throughDIRSETand then read-modify-writesDIRloses every other pin on the port — with no error.
The register truth here is Microchip's own ATSAMD21G18A.svd, which ships under
Apache-2.0 and is vendored at tests/fixtures/real_world/atsamd21g18a.svd.
Every base address and IRQ number in the chip yaml is checked against it by
svd_conformance, with zero justified deviations.
Status at a glance
| Aspect | Status |
|---|---|
| Chip yaml | configs/chips/atsamd21g18a.yaml |
| System yaml | configs/systems/arduino-zero.yaml |
| Reference firmware | crates/firmware-samd21-demo/ (bare-metal, no HAL) |
| Committed ELF | tests/fixtures/samd21-smoke.elf — runs from a clone plus the CLI, no toolchain |
| Validation | examples/samd21-smoke/io-smoke.yaml (7 checks) + atsamd21_peripheral_estate (4) |
| Tier | sim-validated — real PORT/SERCOM models, green CI, no silicon diff |
What is proven
A bare-metal firmware performing the real SAM D21 bring-up in datasheet order — deliberately not through a HAL, so what passes is a driver's actual register traffic rather than one convenience wrapper:
NVMCTRL.CTRLBwait states, before the clock rises.- The
SYSCTRL.PCLKSRready-flag poll. - GCLK generator 0 from OSC8M, then
SERCOM0_CORErouted to it — threeSYNCBUSYspins. PM.APBCMASK— SERCOM0's APB clock is genuinely off at reset (APBCMASK resets to0x0001_0000, ADC only).- One
PORT.WRCONFIGstore muxing PA10/PA11 to peripheral function C (SERCOM0 PAD[2]/PAD[3]), plus PA17 as a plain output. - SERCOM0 as a USART —
CTRLBbeforeCTRLA.ENABLE, as the datasheet requires. - Console output, then the LED pad driven.
The gate is not the banner. Three of the seven checks are memory_value
reads of the PORT registers the firmware configured — DIR bit 17,
PINCFG[10] = PMUXEN|INEN, and PMUX[5]'s low nibble = 2 — which printed
text cannot fake. Non-vacuity was checked by flipping the expected PMUX nibble:
the run fails with expected 0x3, got 0x22 while every UART line still passes.
What is NOT proven
Stated plainly, because a fidelity table that only lists successes is marketing:
- No silicon diff. Nothing here has been compared against a real SAM D21
over SWD. Every ✅ below means the model satisfies the firmware we have run,
not the model matches silicon. The board is
NOT_SHIPPEDin the coverage ratchet for exactly this reason. - Nothing is clock-gated.
PMis a register bank, not a clock-gate controller. A peripheral answers whether or not itsAPBCMASKbit is set, so firmware that forgets to unmask a SERCOM works here and fails on hardware. This is permissive, and it is recorded rather than quietly accepted. SYNCBUSYalways reads 0 andINTFLAG.DREis derived fromCTRLA.ENABLE. Synchronisation and transmission are instantaneous in this model. Those are modelling truths, not silicon ones: firmware that measures a sync delay or a baud-paced byte will not see it.- No narration wire. TX bytes reach the console sink and the bus trace, but
no
PadLinescell is published, so a logic-analyzer wire probe on a SERCOM has nothing to bind to. - Throughput. 2246.0 Ir/step, the highest in the fleet, with batch width 1.0 — this bus is not walk-free, because the SERCOM's IRQ edge-detection needs the per-cycle walk and walk-deletion is all-or-nothing per bus.
Support matrix
| Block | State | Notes |
|---|---|---|
| Cortex-M0+ core, NVIC, SysTick | ✅ | Shared Arm models |
| PORT (GROUP 0/1) | ✅ behavioural | DIR/OUT + aliases, IN, WRCONFIG, PMUX, PINCFG |
| SERCOM0–5, USART mode | ✅ behavioural | 8N1, TX/RX, DRE/TXC/RXC, IRQ on the enabled-flag edge |
| SERCOM SPI / I²C modes | ❌ | Instance goes inert rather than answering wrongly |
| SYSCTRL, GCLK, PM, WDT, NVMCTRL | ⚠️ declarative | Register banks; ready flags modelled, no clock tree |
EIC (attachInterrupt) |
❌ | Window unmapped — an access is reported, not absorbed |
| USB | ❌ | A sketch whose console is SerialUSB prints nothing yet |
| TCC0–2, TC3–5, ADC, DAC, AC, I²S, DMAC, EVSYS, RTC, DSU | ❌ | Declared not modelled in the chip yaml |
Pins
configs/chips/atsamd21g18a.yaml declares an explicit 64-entry pins: map
(PA0–PA31, PB0–PB31). It is declared rather than left to the label
parse because the parse is wrong for this part twice over: it rejects any
bit above 15, and PA17 is LED_BUILTIN; and it builds port names as
gpio<letter>, while these ports are gpio_a/gpio_b because one LabWired
port is one PORT GROUP.
The map says which port register and bit a label resolves to. It does not assert package bond-out — the G18A is a 48-pin part and does not bring all 64 pads out.
| Board label | Pin | Role |
|---|---|---|
D13 / LED_BUILTIN |
PA17 | User LED (board_io led0) |
| D1 / TX | PA10 | SERCOM0 PAD[2], PMUX C |
| D0 / RX | PA11 | SERCOM0 PAD[3], PMUX C |
How to run
# From a clone, with the released CLI — no toolchain needed:
labwired run \
--firmware tests/fixtures/samd21-smoke.elf \
--chip configs/chips/atsamd21g18a.yaml
# The full gate, including the PORT register assertions:
labwired test --script examples/samd21-smoke/io-smoke.yaml
To rebuild the firmware, build from the crate directory, not the workspace
root — its -Tlink.x comes from its own .cargo/config.toml, and a root build
links entry 0x0 and boots nowhere while the build succeeds:
Related
- Board onboarding playbook
- Onboarding candidates — SAMD51/SAME54 are the cheap follow-ons now that the SAM family exists
- Validation status