RA4M1 · R7FA4M1AB3CFM
Docs
What this emulator covers: every row is a feature of the Arduino UNO R4 Minima variant, whether the silicon has it, what the emulator does with it, and how it is proven. Full proofs live in the repo as cargo test, 127 + 90 green.
Coverage
“Proof” names the Rust test or the live Demo tab that exercises the model. Live tabs replay their proof firmware in the browser; register proofs hold MMIO behavior with no Arduino consumer. “Source” tells you what that firmware is: the real Arduino library, or a bare-metal image written straight against FSP registers.
| Feature | Minima HW | Status | Firmware source | Proof / note |
|---|---|---|---|---|
| Cortex-M4F CPU | yes | Done | hand-assembled Thumb | 37 legacy CPU/system tests |
| NVIC / SysTick / SCB / MPU / FPU | yes | Done | reused ARM models | VTOR keeps bits[31:7] |
| SYSTEM / MSTP / ICU / ELC | yes | Done | hand-assembled MMIO | IELSR routing incl. DTCE; pin-edge injection |
| PORT + PFS GPIO | yes | Done | Arduino + bare-metal | real PCNTR layout; live grid on the Demo page; component API (board_info, gpio_read_pin/set_pin_input/button_press, arduino_pin_to_port_bit) |
| attachInterrupt | yes | Done | Arduino API | all 16 lines, button-sketch proof; Signal tab |
| SCI0-9 UART | yes | Done | Arduino API | byte-exact; Serial + Serial1 (SCI2) proofs; Serial + Signal tabs; sci_poll ready bits |
| SCI simple-SPI | yes | Done | bare-metal | shift + loopback jig |
| SPI0/1 master | yes (D11-13 = ch1) | Done | Arduino API | polled SPRF + loopback; SPI tab; spi_exchange byte bridge |
| SPI slave | mode | Done | bare-metal | MSTR=0 staging + exchange; SPI tab |
| Virtual SD (SPI mode) | card-dependent | Done | Arduino API | CMD0/8/55/41/58/17/24; SD tab + MBR dump |
| I2C master | yes (Wire = IIC1) | Done | Arduino API | EEPROM slave @0x50; Wire tab; i2c_exchange bus bridge |
| I2C slave | mode | Done | bare-metal slave + Arduino master | shared-bus fabric; Wire tab |
| GPT0-13 | yes | Done | bare-metal + Arduino | real offsets; wrap/compare/overflow events |
| GPT PWM output | yes (D3/D5/D6/D9/D10/D11) | Done | bare-metal | GTIO function 0 via PFS; 25% duty on D6; PWM tab |
| AGT0-5 | yes | Done | Arduino (millis) | 1 ms IRQs drive millis() in Blink |
| ADC14 | yes | Done | bare-metal + jig | convert + channel override; Signal tab readout |
| DAC12 | yes | Done | bare-metal + Arduino | retained output; AnalogWave sine target |
| Analog read+write (Arduino API) | yes (A0=ch9, DAC12) | Done | Arduino API | ADDR9/DADR0/LED proof + Analog tab (live slider) |
| RTC + alarm | yes | Done | Arduino API | BCD rollover + event 38; RTC + RTC alarm tabs; rtc_read_bcd poll |
| DMAC memcopy / DTC repeat | yes | Done | bare-metal + Arduino | sync path; GPT->DAC proof + AnalogWave sine tab; dma_pending_count/take_pending/set_completed/check_completion + dtc_has_pending |
| WDT / IWDT | yes | Done | Arduino API | refresh-holds + expiry-latches proofs; WDT tab; wdt_poll countdown |
| CRC / DOC | yes | Done | bare-metal MMIO | IEEE-802.3, follower + compare |
| OPAMP / ACMP | yes | Done | Arduino + bare-metal | follower + compare; OPAMP lib proof; Signal tab |
| USBFS CDC serial | yes (native USB) | Done | Arduino API | enumerate / hello / echo; Serial + Echo tabs |
| USB HID keyboard | yes | Done | Arduino API | report descriptor + INT-IN report proof; HID tab |
| USB suspend / resume | yes | Done | Arduino API | DVSQ SUSPx, RESM, weak-callback proof; Serial tab tail |
| CTSU self + mutual touch | yes | Done | Arduino-style + bare-metal | counters + END event; CTSU tab |
| CAN0 mailbox + FIFO + errors | yes (CAN1: no events) | Done | Arduino API | loopback, MB24 FIFO, isError proof; CAN + CAN err tabs; can_send_frame/take_rx_fifo bus bridge |
| analogWrite (GPT0 PWM API) | yes (D6 = GTIOCB) | Done | Arduino API | GTCCRD buffer + GTBER transfer; analogWrite tab |
| AnalogWave sine (GPT→DTC→DAC) | yes | Done | Arduino API | analogWave.sine(10), 24 samples into DADR; AnalogWave tab |
| tone() (GPT PERIODIC IRQ) | yes | Done | Arduino API | 440 Hz toggle on D13; tone tab |
| SoftwareSerial 9600 loopback | lib (GPIO+GPT+DMAC) | Done | Arduino API | 0xA5 TX→RX via soft wire; SoftSerial tab |
| CAN1 mailbox | yes (no ELC events) | Done | bare-metal | polled self-test loopback proof (CAN0-routed IRQs stay quiet); misc tab |
| DAC8 8-bit | yes (no Arduino API) | Done | bare-metal | retain + DAM gate proof; misc tab |
| TSN calibration | yes | Done | bare-metal | fixed trim constants + ADC temp-path mock-consumer proof |
| SLCDC segment LCD | yes (no panel) | Done | bare-metal | regs + 64B display RAM + LCDON/digit-5 mock-panel proof |
| KINT key interrupt | yes | Done | bare-metal + jig | key-press jig into event 69 + proof; misc tab |
| SSI0 audio FIFO | yes (I2S lib broken) | Done | bare-metal | TX drain + RX pattern proof; misc tab; ssi_exchange sample bridge |
| SSI1 audio FIFO | yes (FSP mask = SSI0 only) | Done | bare-metal | same model, own slot/FIFOs; register proof |
| GPT OPS / POEG0-3 / R_DMA | stubs (no Arduino consumer) | Done | bare-metal | accept-and-retain + SSF-halt + DMST-memcopy mock proof |
| LED matrix 12x8 | GPIO-driven | Done | bare-metal | charlieplex render; smiley proof + Matrix tab |
| LED matrix heart (Arduino lib) | WiFi-variant pins, same silicon | Done | Arduino API | exact heart reconstruction via GPIO trace + Matrix tab |
| Dataflash 8KB + FACI | yes | Done | bare-metal + Arduino | program/erase/blankcheck; EEPROM tab |
| Arduino EEPROM lib | via dataflash | Done | Arduino API | round-trip proof; EEPROM tab |
| I2S audio (SSI) | no driver | Left | — | SSI layer done bare-metal; in-tree I2S lib fails to compile on 1.6.0 (r_i2s_api.h + r_ssi.h both absent — reproduced with arduino-cli) |
| USB audio class | no (CFG_TUD_AUDIO=0) | Left | — | Minima tusb_config.h compiles TinyUSB with audio off (CDC/HID/DFU only); closed in the MSC mock tail (no CTRT, bulk stays BOT) |
| USB MSC transport | no Arduino lib (CFG_TUD_MSC=0) | Done | scripted | BOT CBW/CSW + SCSI INQUIRY/READ_CAPACITY/READ10/WRITE10 mock chip over bulk pipes |
| FAT / LittleFS on-chip | constrained | Left | — | impossible: FAT needs ≥64 sectors but dataflash is 8×1KB blocks (FATFileSystem assert fires); LFS block-range assert fires the same way |
| SDHI 4-bit SD host | no (not on RA4M1) | Left | — | zero SDMMC/SDHI symbols in R7FA4M1AB.h — no 4-bit bus/ADMA exists to host FAT-over-SD |
| RTC alarm (Arduino lib) | yes | Done | Arduino API | setAlarmCallback :00 match + callback; RTC alarm tab |
| CAN bus-off recovery | yes | Done | Arduino API | BOEF + halt-clear + TX again; CAN err tab |
| EK-RA4M1 bare-metal target | eval kit (same silicon) | Done | bare-metal | zero-boot + P106 LED1; EK-RA4M1 tab + board selector |
| UNO R4 WiFi board | separate board (RA4M1 + ESP32-S3) | Parked | — | RA4M1 side is this core; ESP32-S3 bridge + LED matrix assist have no S3 code yet. Board picker shows WiFi parked; Matrix GPIO render stays live. |
| LED-matrix assist (WiFi) | WiFi board only | Parked | — | needs the S3 assist path; Minima renders from RA GPIO instead |
Proof inventory
Snapshot core core/ra4m1-periph-wasm: 127 tests (37 legacy CPU/system + 90 R4 proofs). Small core core/ra4m1-core: 90 R4 proofs (89 mirror + EK zero-boot). Always run single-threaded — shared globals (SYS, INSTRUCTION_COUNT, UART buffer) flake in parallel.
cargo test --manifest-path core/ra4m1-periph-wasm/Cargo.toml --lib -- --test-threads=1
cargo test --manifest-path core/ra4m1-core/Cargo.toml --lib -- --test-threads=1
cargo build --manifest-path Cargo.toml
R4 proofs (src/ra4m1.rs, both cores)
Boot and clock: ra4m1_boots_from_zero, clock_stub_accepts_boot_writes. GPIO and timers: ra4m1_map_port_output_retained, ra4m1_map_gpt_counts_and_matches, ra4m1_map_agt_counts, ra4m1_map_elc_routes_software_event. Analog and time: ra4m1_map_adc_converts_channel, ra4m1_map_dac_output_retained, ra4m1_map_rtc_ticks_seconds, ra4m1_map_crc_and_doc, ra4m1_map_opamp_follower_and_acmp. Firmware end-to-end: ra4m1_firmware_blinky_via_mmio, ra4m1_arduino_blink_boots, ra4m1_arduino_blink_toggles_led, ra4m1_wire_ok, ra4m1_spi_ok, ra4m1_can_ok, ra4m1_dtc_ok, ra4m1_analogwave_ok, ra4m1_pwm_ok, ra4m1_analogwrite_ok, ra4m1_tone_ok, ra4m1_softserial_ok, ra4m1_matrix_ok, ra4m1_analog_ok, ra4m1_mtx_ok. Buses and storage: ra4m1_map_sci_tx_reaches_console, ra4m1_map_sci_echo_path, ra4m1_map_sci_spi_loopback, ra4m1_map_spi_loopback, ra4m1_map_i2c_eeprom, ra4m1_map_i2c_slave, ra4m1_wire_slave_ok, ra4m1_map_spi_slave, ra4m1_spi_slave_ok, ra4m1_map_sd_card, ra4m1_sd_ok, ra4m1_map_can_loopback, ra4m1_map_can_fifo, ra4m1_can_fifo_ok, ra4m1_map_can_errors, ra4m1_can_error_ok, ra4m1_map_dtc_repeat. USB: ra4m1_map_usb_tx_reaches_capture, ra4m1_usb_enumerates_cdc, ra4m1_usb_serial_hello, ra4m1_usb_cdc_echo, ra4m1_map_ctsu_measures, ra4m1_map_usb_suspend_resume, ra4m1_usb_hid_keyboard, ra4m1_usb_suspend_resume_ok, ra4m1_map_usb_msc_mock. System and misc: ra4m1_map_dmac_mem_to_mem, ra4m1_map_dataflash_program_erase, ra4m1_eeprom_ok, ra4m1_ite_add_imm_preserves_flags, ra4m1_map_icu_pin_irq, ra4m1_attach_interrupt, ra4m1_serial1_echo, ra4m1_map_extra_channels, ra4m1_rtc_firmware, ra4m1_map_rtc_alarm, ra4m1_wdt_refresh, ra4m1_wdt_expire, ra4m1_opamp_firmware, ra4m1_map_ctsu_mutual, ra4m1_ctsu_mutual_ok, ra4m1_map_can1_loopback, ra4m1_can1_ok, ra4m1_map_dac8, ra4m1_dac8_ok, ra4m1_map_tsn, ra4m1_map_slcdc, ra4m1_map_kint, ra4m1_kint_ok, ra4m1_map_ssi, ra4m1_ssi_ok, ra4m1_component_gpio_pins, ra4m1_component_can_frame, ra4m1_component_ssi_bridge, ra4m1_component_spi_exchange, ra4m1_component_i2c_exchange, ra4m1_component_dma_queue, ra4m1_component_sci_wdt_rtc, ra4m1_rtc_alarm_ok, ra4m1_map_can_busoff_recovery, ra4m1_can_busoff_ok, ra4m1_ek_ra4m1_zero_boot_p106_led, ra4m1_map_ssi1, ra4m1_map_gpt_protect_dma.
Known quirks
- Run tests with
--test-threads=1: sharedSYS, instruction count, and UART buffer flake in parallel. rx_injectraises BRDY like hardware; CCPL is a strobe; CFIFO OUT drains are single bytes.- RIIC: ST auto-sets MST/TRS; TDRE+TXI fire on START; enables fire latched flags; the first RXI read is a dummy; restart-write has no completion event.
- Virtual time runs on instruction count; ADC converts instantly; CAN loopback needs self-test mode; I2C slave jig lives at 0x50; SPI loopback is a test jig (open bus reads 0xFF); DTC transfers complete in the CPU run loop while staged.
- GPT compares are crossing-detected; unprogrammed all-ones compares are bounded by the period so they never false-fire on wrap.
- PORT and PFS are separate instances keyed by an explicit flag; PWM pins follow GPT latches only when PFS PSEL selects a GPT function.
- PCNTR1 word is PDR low half + PODR high half (settled against the FSP disassembly: turnLed tristates with a 16-bit mask, so direction is low). Level readers use the high half; stale levels on tristated rails never decode.
- Arduino-lib matrix on-windows are ~15 instructions wide, so the heart proof reconstructs from a GPIO write-trace jig (same idea as the USB TX capture), not chunk sampling.
- DTC repeat/block length reads CRAL (R_DTC doubles length into CRAL+CRAH, so a programmed 24 reads back 0x1818).
- Sub-word reads return unmasked packs in JS — mask with
% 256. Data registers (ICDRR/SPDR) are never polled; a read would eat the firmware's byte. - D13 is P111 (PORT1 bit 11), not bit 13.
Build and test
Top workspace builds Minima only; WiFi is parked and excluded. demo/pkg/ + demo/fw/ are generated (git-ignored); only index.html, docs.html, about.html, app.js, styles.css, build.sh are tracked.
./demo/build.sh
python3 -m http.server -d demo 8901
cargo build --manifest-path Cargo.toml
Vendored firmware (core/blinky/r4*.bin) is built with arduino-cli 1.6.0 and committed; the Pages workflow rebuilds pkg+fw and publishes demo/.