|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
The EK-RA8D2 has no on-chip radio. Wi-Fi and Bluetooth are provided by an ESP32-C6 wired to the RA8D2 as a wireless co-processor. The C6 runs Espressif's esp-hosted-mcu network_adapter firmware; the RA8D2 is the host. A SPI transport carries the esp-hosted framing between them.
This document is the design-level architecture. Two concerns are deliberately split out:
Both sides of the link are the same upstream project at the same pinned commit: the C6 runs the peripheral-side firmware, the RA8D2 links the host driver, and a matching protocol version is what makes them speak.
| Side | Device | Software |
|---|---|---|
| Host | RA8D2 (Cortex-M85) | Upstream esp-hosted host driver (vendored SOUP) + the first-party RA8 + ThreadX port at port/esp-hosted/ |
| Co-processor | ESP32-C6 | Pinned esp-hosted-mcu network_adapter SOUP + first-party mdl_service component |
The C6 is a mixed image. The pinned upstream application supplies the radio, network stack, SPI transport, and outer RPC envelope. One checked patch exposes a bounded synchronous CustomRpc response hook, and the first-party mdl_service component supplies a pull-based HTTPS artifact transfer. The build refuses patch drift and asserts the strong service symbol exists in the final ELF. The C6 image is never linked into the RA8D2 firmware binary – the two remain separate images that meet only on the wire.
The RA8D2 side is a mix by design: the protocol driver is the same upstream project (vendored at libs/third_party/esp-hosted/, so the framing and RPC encoding cannot drift from the peripheral side), while everything that touches RA8D2 hardware – the SPI transfers, GPIO, timers, tasks and memory – is first-party port code held to the full project bar.
esp-hosted-mcu multiplexes Wi-Fi data, Bluetooth HCI, and a control channel over a single physical link. This project uses the SPI transport (CONFIG_ESP_SPI_HOST_INTERFACE), with the RA8D2 as the SPI controller and the C6 as the SPI peripheral.
Two out-of-band GPIOs pace the link. Their idle levels differ, which is easy to get wrong and was got wrong here (see the side-band section below):
The consequence for any bring-up tool: HANDSHAKE can be identified by watching it move, and DATA_READY cannot.
Inclusive signal names; the pins are the single source of truth in ../../coprocessor/esp32c6/pins.env.
| Signal | ESP32-C6 GPIO | Direction |
|---|---|---|
| CS (Chip Select) | GPIO0 | host -> C6 |
| COPI (Controller Out) | GPIO1 | host -> C6 |
| CIPO (Controller In) | GPIO2 | C6 -> host |
| SCK (clock) | GPIO3 | host -> C6 |
| DATA_READY | GPIO4 | C6 -> host |
| HANDSHAKE | GPIO6 | C6 -> host |
| RESET | disconnected | n/a |
RESET is left disconnected (-1) in this bring-up: the C6 is reset by power cycling. A future revision may wire a host-driven reset line so the host can recover the C6 without a power cycle.
The C6 is soldered to Pmod1 (J26). Which MCU pin carries each J26 signal is not fixed: EK-RA8D2 v1 UM Rev 1.01 Table 17 p 26 shows pins J26-1..J26-4 are muxed on the board, and Table 18 p 26 selects the mux position from SW4-1 / SW4-2.
Every row below was scope-qualified end to end at the J26 hole, and the link then came up at SPI mode 3 / 1 MHz with zero bad checksums. The same map is machine-readable in ../../coprocessor/esp32c6/pins.env (RA8_PIN_* / RA8_J26_*), which is the source of truth.
| J26 | Signal (C6 GPIO) | RA8D2 pin, Pmod1 SPI position | RA8D2 pin, UART position |
|---|---|---|---|
| 1 | CS (GPIO0) | P804 (SS2/IRQ14) | P800 (CTS2) |
| 2 | COPI (GPIO1) | P801 (COPI2/TXD2) | P801 |
| 3 | CIPO (GPIO2) | P802 (CIPO2/RXD2) | P802 |
| 4 | SCK (GPIO3) | P803 (SCK2) | P804 (RTS2) |
| 7 | HANDSHAKE (GPIO6) | P006 (IRQ11-DS) | P006 |
| 8 | DATA_READY (GPIO4) | P402 | P402 |
| 9 | not connected (future EN / reset) | P412 | P412 |
| 10 | not connected | P413 | P413 |
J26-5 and J26-11 are ground; there is no 3V3 wire, as the C6 is self-powered over its own USB. J26-9 is the hole reserved for the host-driven reset line that C6_PIN_RESET=-1 records as absent.
The controller is SCI2 in Simple-SPI mode (k_ra8_board_pmod1_sci_channel), with the chip-select owned as a GPIO so one assertion spans a whole esp-hosted frame. Board symbols: k_ra8_board_pmod1_spi_* and k_ra8_board_pmod1_irq / _reset / _gpio_a / _gpio_b.
Note the trap in the UART position: J26-1 becomes P800 and J26-4 becomes P804, so a controller that drives P804 as chip-select is really feeding the C6's clock pin, and the C6 never sees a chip-select at all.
| Switch | Required | Meaning (UM Table 3 p 16, Table 18 p 26) |
|---|---|---|
| SW4-1 | OFF | Pmod1 Mode Select 1 |
| SW4-2 | OFF | Pmod1 Mode Select 2; OFF+OFF selects SPI |
| SW4-3 | ON | Octo-SPI Inactive – frees P801..P804 for Pmod1 |
| SW4-4 | OFF | Arduino / mikroBUS inactive (SW4-3 ON + SW4-4 ON is invalid) |
This bank is the single highest-risk setting on the board for this link. With SW4-4 ON and SW4-3 OFF the Octo-SPI flash owns the Pmod1 SPI pins and the U6 / U9 bus switches stay open, so J26-1..J26-4 are not electrically connected to the MCU – while the MCU, the board and the C6 all appear perfectly healthy. That misreading, not a wiring fault, was the whole first-light outage.
SW4-4 OFF also takes the Arduino and mikroBUS connectors offline, so the LSM6DSO IMU Click cannot be used at the same time as the C6 link. That is a genuine board-level trade-off, not an oversight.
SW4-3 is a hardware-only analog mux: the U15 PI4IOE5V6408 expander can sense and override the other SW4 lines, but a whole-output-space sweep (issue #44, recorded on ra8_board_io_expander_set_octospi_active) established that its GPIOs are not in the Octo-SPI path.
P006 carries HANDSHAKE and P402 carries DATA_READY, established on the bench by examples/ek_ra8d2/hw_validated/c6/c6_spi_probe from the C6's own behaviour rather than from a wiring note. The two took different evidence, for the reason given in the transport section above:
That second identification was nearly missed, and the near-miss is the interesting part. DATA_READY transitions once per boot and then sits still, and that single transition falls outside any transaction's sampling window, so a rule that counts pre/mid/post transitions within a transaction scores it zero forever. The probe's first heuristic did exactly that, read the stillness as absence, and named the floating, unconnected P413 instead on a single noise sample.
The probe now settles the question electrically rather than by catching that one transition: c6_probe_pull_contest reads each side-band pin with the RA8D2's internal pull-up engaged, and a pin still reading low is being sunk by a driver on the far end – something no floating pin can imitate, requiring no cooperation from the C6 and no lucky timing. That mechanism has not yet been exercised on hardware; the map above does not rest on it. See the probe's README for the full record, including the superseded first diagnosis.
Because the C6 firmware is flashed once and independently, an RA8D2 firmware update does not touch the C6, and vice versa.
The upstream host driver source is vendored at libs/third_party/esp-hosted/ (host driver + shared protocol, with the upstream ESP-IDF/FreeRTOS port deliberately left out), and the first-party replacement for that port is at port/esp-hosted/. It supplies the port_esp_hosted_host_*.h header contracts, the ESP-IDF compatibility headers the core includes by name, and the implementations behind the hosted_osi_funcs_t vtable – all enumerated in ../SOUP/esp-hosted-host.md, which also records exactly which vendored translation units compile today and what blocks the rest.
cmake/esp_hosted.cmake wires it up behind RA8_USE_ESP_HOSTED; its consumers are the apps under examples/ek_ra8d2/hw_validated/c6/, so the cross-build gate covers it on every push and just hil::c6 re-runs it on the bench. The port:
Separately, the first-light bench instrument for the raw link is examples/ek_ra8d2/hw_validated/c6/c6_spi_probe. It hand-decodes the payload header from the same pinned upstream spec, but it is deliberately an app rather than a driver: its job was to prove the wire, resolve the Pmod1 mux position and identify the side-band pins, not to become the transport.
The validating bench run established three rungs. They remain re-runnable with just hil::c6, but this record does not claim that the current commit has been rerun:
Two protocol facts from that bring-up that the layers above should be built on:
port/esp-hosted/inc/ra8_esp_hosted_pins.h states every RA8-side pin fact about the link – chip select, HANDSHAKE, DATA_READY and reset – as board-layer enumerators, and port/esp-hosted/src/ra8_esp_hosted_pins.c states which of those pins the package routes to an ICU external-interrupt channel. Nothing else in the port, the build or the example app re-encodes a pin. When the rebuilt harness is characterised, those two files are the change.
That routing table matters because the ICU inputs are concentrated on port 0: of the Pmod1 side-band nets, only P006 has a channel (IRQ11). The port therefore gives an ICU-routed pin a hardware edge and services a pin without a channel through a bounded software edge detector, and the vendored driver sees the same callback either way.
The RA8D2 has no integrated radio; a companion connectivity device is the only route to Wi-Fi / Bluetooth on this board. esp-hosted-mcu is Espressif's supported, proven-in-use solution for exactly this host-plus-co-processor topology, which is why it is accepted as SOUP rather than reimplemented.
The C6 Wi-Fi MAC/PHY is not documented in Espressif public datasheets or the C6 technical reference manual – only the wired peripherals are. Every production Wi-Fi / Bluetooth stack for the part (ESP-IDF, Zephyr, NuttX, the Rust esp-radio crates) links the same Espressif binary blobs (libpp, libnet80211, libphy, libcoexist, and the BLE controller library); there is no open reimplementation of the radio, and the reverse-engineering efforts that do exist support open networks only, on the classic Xtensa ESP32, and still need the blob to bring up the RF front end. A hand-written first-party driver for the one peripheral the C6 exists to provide is therefore not achievable. The blobs are also what the module FCC modular grant rests on, so shipping Espressif stock firmware keeps that grant intact.
Keeping the radio/transport portion pinned as SOUP while isolating the first-party service behind a generated, bounded wire contract makes the trust boundary explicit and preserves the FCC modular grant.