|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Shared contract for the EK-RA8D2 <-> ESP32-C6 esp-hosted SPI probe. More...
Go to the source code of this file.
Data Structures | |
| struct | c6_sideband_sample_t |
| One simultaneous reading of every side-band pin. More... | |
| struct | c6_probe_stats_t |
| Running evidence gathered across every transaction. More... | |
Functions | |
| void | c6_probe_puts (const char *text) |
| Emit a bounded, NUL-terminated ASCII literal on the console. | |
| void | c6_probe_put_u32 (uint32_t value) |
| Emit an unsigned integer in decimal. | |
| void | c6_probe_put_hex (uint32_t value, uint8_t digits) |
| Emit a value as fixed-width lowercase hexadecimal. | |
| ra8_err_t | c6_probe_sideband_init (void) |
| Claim the four Pmod1 side-band pins as no-pull digital inputs. | |
| void | c6_probe_sample_sideband (c6_sideband_sample_t *out) |
| Read every Pmod1 side-band pin into one sample. | |
| void | c6_probe_print_sideband (const char *label, const c6_sideband_sample_t *s) |
| Print a labelled side-band sample as label P006=1 P402=0 .... | |
| void | c6_probe_vote (const c6_sideband_sample_t *pre, const c6_sideband_sample_t *mid, const c6_sideband_sample_t *post, c6_probe_stats_t *st) |
| Fold one transaction's three samples into the classification votes. | |
| uint8_t | c6_probe_best (const uint32_t *votes, uint8_t ignore, uint32_t min_votes) |
| Pick the side-band pin that won the vote outright, or report none. | |
| ra8_err_t | c6_probe_pull_contest (c6_probe_stats_t *st) |
| Find which side-band pins an external driver is holding low. | |
| uint8_t | c6_probe_sunk_pin (const c6_probe_stats_t *st, uint8_t ignore) |
| Name the one pin that lost every read of the pull-up contest. | |
| void | c6_probe_resolve_map (const c6_probe_stats_t *st, uint8_t *hs_idx, uint8_t *dr_idx) |
| Resolve the HANDSHAKE and DATA_READY side-band map from the accumulated evidence. | |
| const char * | c6_probe_sideband_name (uint8_t idx) |
| Name a side-band pin for the console. | |
| c6_wire_kind_t | c6_probe_wire_kind (uint8_t after_high, uint8_t after_low) |
| Classify one Pmod1 net from its two drive-and-release samples. | |
| void | c6_probe_wire_test (void) |
| Run the drive-and-release test over every Pmod1 muxed net. | |
| uint8_t | c6_probe_cs_hunt (c6_probe_stats_t *st) |
| Find which MCU pin the board's Pmod1 mux has wired to the C6's chip-select, and identify HANDSHAKE at the same time. | |
| ra8_err_t | c6_probe_spi_pins_init (void) |
| Route the Pmod1 SPI pins to SCI2 and own the chip-select as GPIO. | |
| bool | c6_probe_sweep_mode (ra8_spi_mode_t mode, uint32_t pclka_hz, c6_probe_stats_t *st, uint8_t *hs_idx) |
| Run the transaction burst for one SPI mode. | |
Shared contract for the EK-RA8D2 <-> ESP32-C6 esp-hosted SPI probe.
This header is the probe's own contract: its budgets, the Pmod1 side-band and muxed-net maps, the evidence it accumulates, and the entry point of each module. The esp-hosted wire format it decodes is a separate concern and lives in c6_proto.h, which this header includes.
The probe is split into one module per concern, all driven by main.c:
Nothing from esp-hosted-mcu is vendored; every protocol constant is hand-decoded from the pinned upstream tree (commit 949bb30, firmware 2.12.11) and cites its source in c6_proto.h. The side-band semantics this file relies on come from upstream's docs/spi_full_duplex.md and slave/main/spi_slave_api.c – what the C6 drives. LEGACY-OK: upstream path
[Ring 6 / App] {World: S}
Definition in file c6_probe.h.
| enum c6_probe_budget_t : uint8_t |
Statically provable loop bounds (NASA Power of 10 Rule 2).
Every loop in the probe counts against one of these, so no loop bound depends on data received from the C6 – a peripheral that answers with garbage can waste a bounded amount of time and nothing more.
k_c6_probe_min_votes is an evidence bar rather than a loop bound, and it exists because of a real mis-identification on the bench: an unconnected side-band pin floats, and a single noise transition on it is indistinguishable from one real edge, so a mapping claimed on one vote named a floating pin while the truly-connected pin sat unresolved. A mapping is therefore claimed only from repeated, agreeing evidence.
Definition at line 105 of file c6_probe.h.
| enum c6_probe_fmt_t : uint8_t |
Radix and field-width constants used by the console formatters.
Kept at file scope rather than inside each formatter: a function-local typedef that a body only reads through its enumerators trips -Werror=unused-local-typedefs under the project warning profile.
Definition at line 135 of file c6_probe.h.
| enum c6_probe_tunable_t : uint32_t |
Timing and clock budgets for the probe.
k_c6_probe_sck_hz starts an order of magnitude below the 5 MHz that esp-hosted-mcu docs/spi_full_duplex.md section 4.1 recommends for evaluation, because this is the first time the link has ever been clocked. k_c6_probe_boot_wait_ms covers the C6 booting its own bootloader plus the esp-hosted application after a shared power cycle – the RA8D2 is out of reset long before the C6 has queued anything.
Definition at line 70 of file c6_probe.h.
| enum c6_sideband_idx_t : uint8_t |
Index into the Pmod1 side-band pin table.
Pmod1 exposes four signals besides the four SPI lines, and unlike SPI pins 1..4 they are not affected by the board's Pmod1 mode mux. Which of them the C6's DATA_READY (its GPIO4) and HANDSHAKE (its GPIO6) are soldered to is exactly what this app determines, so all four are sampled and none is assumed.
| Enumerator | |
|---|---|
| k_c6_sb_irq | Pmod1.7 IRQ, P006. |
| k_c6_sb_reset | Pmod1.8 RESET, P402. |
| k_c6_sb_gpio_a | Pmod1.9 GPIO, P412. |
| k_c6_sb_gpio_b | Pmod1.10 GPIO, P413. |
| k_c6_sb_count | Side-band pin count. |
Definition at line 172 of file c6_probe.h.
| enum c6_wire_idx_t : uint8_t |
Index into the Pmod1 muxed-net table.
EK-RA8D2 UM Table 17 p 26 shows Pmod1 pins 1..4 are muxed on the board: which MCU pin reaches J26 depends on SW4-1 / SW4-2 (UM Table 18 p 26) and, for the Octo-SPI overlap, on SW4-3 (UM Table 3 p 16). Only in the SPI position does the set {P804, P801, P802, P803} reach J26 pins 1..4; the UART position swaps J26-1 to P800 and J26-4 to P804, and the I2C position routes J26-3 / J26-4 to P512 / P511 entirely. All five candidates are exercised so the log states which mux position the board is actually in rather than assuming one.
Definition at line 221 of file c6_probe.h.
| enum c6_wire_kind_t : uint8_t |
What the drive-and-release test says is on the far end of a net.
Each net is driven high, released to a no-pull input and sampled, then driven low, released and sampled again. A net with nothing on it holds whatever it was last driven to on its own parasitic capacitance; a net terminated by a pull resistor or an active driver snaps back to that termination's level instead.
Definition at line 250 of file c6_probe.h.
| uint8_t c6_probe_best | ( | const uint32_t * | votes, |
| uint8_t | ignore, | ||
| uint32_t | min_votes ) |
Pick the side-band pin that won the vote outright, or report none.
A winner must clear two bars, and both exist because the bench produced the failure they prevent. It must reach min_votes, so one transition on a floating pin cannot name a mapping; and it must be a strict winner, so a tie is reported as unresolved rather than silently broken by table order. Reporting "unresolved" is a useful answer here – claiming the wrong pin is not.
| [in] | votes | Per-pin vote counters. |
| [in] | ignore | Index to skip, or k_c6_sb_count to skip nothing. |
| [in] | min_votes | Smallest vote count that may claim a mapping; must be at least one. |
| k_c6_sb_count | No pin reached min_votes, the top score was tied, votes was NULL, or min_votes was zero. |
Definition at line 180 of file c6_sideband.c.
References k_c6_sb_count.
Referenced by c6_probe_resolve_map(), c6_probe_run(), and c6_probe_sweep_mode().
| uint8_t c6_probe_cs_hunt | ( | c6_probe_stats_t * | st | ) |
Find which MCU pin the board's Pmod1 mux has wired to the C6's chip-select, and identify HANDSHAKE at the same time.
The C6 image sets CONFIG_ESP_SPI_DEASSERT_HS_ON_CS=y, so its chip-select edge interrupt drops HANDSHAKE the moment chip-select is asserted and re-raises it once the next transaction is queued (gpio_disable_hs_isr_handler / spi_post_setup_cb in the C6's peripheral-side SPI driver). Asserting each muxed-net candidate in turn and watching for a side-band pin to drop therefore needs no clock, no payload and no working data path: the pin that provokes the drop is the real chip-select, and the pin that drops is HANDSHAKE.
| [in,out] | st | Statistics block; handshake votes are accumulated here. |
| k_c6_wire_count | No candidate provoked a side-band response. |
Definition at line 485 of file c6_sideband.c.
References c6_probe_puts(), internal_cs_try(), k_c6_wire_count, and k_c6_wire_label.
Referenced by main().
| void c6_probe_print_sideband | ( | const char * | label, |
| const c6_sideband_sample_t * | s ) |
Print a labelled side-band sample as label P006=1 P402=0 ....
| [in] | label | Short prefix, already indented by the caller. |
| [in] | s | Sample to print; ignored when NULL. |
Definition at line 141 of file c6_sideband.c.
References c6_probe_put_u32(), c6_probe_puts(), c6_probe_sideband_name(), k_c6_sb_count, and c6_sideband_sample_t::level.
Referenced by internal_cs_try(), internal_one_xfer(), and main().
|
nodiscard |
Find which side-band pins an external driver is holding low.
Re-claims each side-band pin as an input with the RA8D2's internal pull-up engaged, reads it k_c6_probe_pull_samples times, and restores it to a no-pull input. A pin with nothing on it is pulled high and reads high every time; a pin that keeps reading low is losing a current fight to something off-chip, which nothing floating can imitate. That is the strongest "this pin is connected" evidence the probe can gather, and it needs no cooperation from the C6 whatsoever.
It is what identifies DATA_READY. An esp-hosted peripheral with an empty transmit queue holds DATA_READY low by design, so across a whole probe run that pin moves once at most, and usually where nothing is sampling. The pin that matters most is therefore the one a transition-counting rule cannot see – and the one this rule reads without ambiguity.
One line per pin is printed, so the raw count is in the log even when the verdict is unresolved.
| [in,out] | st | Statistics block; pull_low and pull_samples are written. |
| k_ra8_ok | Every pin was tested and restored to a no-pull input. |
| k_ra8_err_null_ptr | st was NULL. |
| k_ra8_err_gpio_conflict | A side-band pin is owned elsewhere. |
Definition at line 269 of file c6_sideband.c.
References c6_probe_put_u32(), c6_probe_puts(), c6_probe_sideband_name(), internal_pull_read(), k_c6_probe_pull_samples, k_c6_sb_count, k_c6_sideband_pin, k_ra8_err_null_ptr, k_ra8_ok, c6_probe_stats_t::pull_low, and c6_probe_stats_t::pull_samples.
Referenced by c6_probe_run().
| void c6_probe_put_hex | ( | uint32_t | value, |
| uint8_t | digits ) |
Emit a value as fixed-width lowercase hexadecimal.
| [in] | value | Value to print. |
| [in] | digits | Number of nibbles to emit (1..8). |
Definition at line 56 of file c6_console.c.
References k_c6_fmt_hex_alpha, k_c6_fmt_hex_bits, k_c6_fmt_hex_mask, k_c6_fmt_hex_max, and ra8_board_uart_console_write().
Referenced by c6_probe_dump_payload(), and c6_probe_print_header().
| void c6_probe_put_u32 | ( | uint32_t | value | ) |
Emit an unsigned integer in decimal.
| [in] | value | Value to print; zero prints as a single 0. |
Emit an unsigned integer in decimal.
Definition at line 38 of file c6_console.c.
References k_c6_fmt_dec_digits, k_c6_fmt_dec_radix, and ra8_board_uart_console_write().
Referenced by c6_probe_print_header(), c6_probe_print_map(), c6_probe_print_sideband(), c6_probe_print_verdict(), c6_probe_pull_contest(), c6_probe_sweep_mode(), c6_probe_wire_test(), and internal_one_xfer().
| void c6_probe_puts | ( | const char * | text | ) |
Emit a bounded, NUL-terminated ASCII literal on the console.
Measures the literal with a statically bounded scan (k_c6_probe_str_max) rather than calling strlen, keeping NASA Power of 10 Rule 2 provable without linking newlib string code.
| [in] | text | NUL-terminated ASCII string; ignored when NULL. |
Definition at line 25 of file c6_console.c.
References k_c6_probe_str_max, and ra8_board_uart_console_write().
Referenced by c6_probe_cs_hunt(), c6_probe_dump_payload(), c6_probe_print_header(), c6_probe_print_map(), c6_probe_print_sideband(), c6_probe_print_verdict(), c6_probe_pull_contest(), c6_probe_run(), c6_probe_setup_or_halt(), c6_probe_sweep_mode(), c6_probe_wire_test(), internal_cs_try(), internal_one_xfer(), internal_report_frame(), and main().
| void c6_probe_resolve_map | ( | const c6_probe_stats_t * | st, |
| uint8_t * | hs_idx, | ||
| uint8_t * | dr_idx ) |
Resolve the HANDSHAKE and DATA_READY side-band map from the accumulated evidence.
The whole identification policy lives here, in one place, ranked by how hard the evidence is to fake:
Any step that cannot decide yields k_c6_sb_count, and the caller prints unresolved. That is deliberate: the previous policy took the highest vote unconditionally, and a single noise transition on an unconnected pin was enough to publish a wrong pin map that read exactly like a measured one.
| [in] | st | Accumulated evidence. |
| [out] | hs_idx | Resolved HANDSHAKE index, or k_c6_sb_count. |
| [out] | dr_idx | Resolved DATA_READY index, or k_c6_sb_count. |
Definition at line 313 of file c6_sideband.c.
References c6_probe_best(), c6_probe_sunk_pin(), c6_probe_stats_t::dr_vote, c6_probe_stats_t::hs_vote, k_c6_probe_min_votes, and k_c6_sb_count.
Referenced by c6_probe_run().
| void c6_probe_sample_sideband | ( | c6_sideband_sample_t * | out | ) |
Read every Pmod1 side-band pin into one sample.
| [out] | out | Destination sample; ignored when NULL. |
Definition at line 121 of file c6_sideband.c.
References k_c6_sb_count, k_c6_sideband_pin, k_ra8_level_high, k_ra8_level_low, k_ra8_ok, c6_sideband_sample_t::level, and ra8_gpio_read().
Referenced by internal_cs_try(), internal_transfer(), internal_wait_ready(), and main().
|
nodiscard |
Claim the four Pmod1 side-band pins as no-pull digital inputs.
No internal pull is applied for sampling: the C6 drives HANDSHAKE and DATA_READY push-pull, and holding a pull-up during the run would make an unconnected pin indistinguishable from an asserted one – precisely the distinction this app exists to make. c6_probe_pull_contest engages the pull-up briefly and on purpose, then restores the pins to this state.
| k_ra8_ok | All four side-band pins are inputs. |
| k_ra8_err_gpio_conflict | A pin is already owned elsewhere. |
| k_ra8_err_gpio_invalid_port | Board pin table disagrees with the HAL. |
Definition at line 110 of file c6_sideband.c.
References k_c6_sb_count, k_c6_sideband_pin, k_ra8_ok, k_ra8_pull_none, and ra8_gpio_input_init().
Referenced by c6_probe_setup_or_halt().
| const char * c6_probe_sideband_name | ( | uint8_t | idx | ) |
Name a side-band pin for the console.
| [in] | idx | Side-band index, or k_c6_sb_count for "unresolved". |
| unresolved | idx was out of range. |
Definition at line 135 of file c6_sideband.c.
References k_c6_sb_count, and k_c6_sideband_label.
Referenced by c6_probe_print_map(), c6_probe_print_sideband(), and c6_probe_pull_contest().
|
nodiscard |
Route the Pmod1 SPI pins to SCI2 and own the chip-select as GPIO.
The three clocked signals go to their SCI function (PSEL = 00100b, which HUM Ch 20.6 "Multiplexed Pin Function Selector" maps to SCI 0/2/4/6/8), while the chip-select stays a GPIO so one assertion can span the whole 1600-byte esp-hosted frame.
| k_ra8_ok | All four pins routed or claimed. |
| k_ra8_err_gpio_conflict | A pin is already owned elsewhere. |
| k_ra8_err_gpio_invalid_port | Board pin table disagrees with the HAL. |
Definition at line 322 of file c6_xfer.c.
References k_ra8_board_pmod1_spi_cipo, k_ra8_board_pmod1_spi_copi, k_ra8_board_pmod1_spi_cs, k_ra8_board_pmod1_spi_sck, k_ra8_level_high, k_ra8_ok, k_ra8_psel_sci_async, ra8_gpio_output_init(), and ra8_pfs_route_peripheral().
Referenced by main().
| uint8_t c6_probe_sunk_pin | ( | const c6_probe_stats_t * | st, |
| uint8_t | ignore ) |
Name the one pin that lost every read of the pull-up contest.
Reads the verdict out of the evidence c6_probe_pull_contest gathered: a pin qualifies only when pull_low equals pull_samples, i.e. it read low on every single sample taken against the pull-up.
Two conditions are deliberately reported as "no answer" rather than as a winner. Unanimity is required because a pin that reads low only sometimes is being driven by something that is not an idle DATA_READY – a coupled edge, a shared ground bounce – and naming it would be a guess. Uniqueness is required because two sunk pins mean the caller's exclusion (typically the resolved HANDSHAKE) has not narrowed the field to one, and picking either by table order is the exact failure this whole mechanism exists to prevent.
| [in] | st | Statistics block filled by c6_probe_pull_contest. |
| [in] | ignore | Index to skip, or k_c6_sb_count to skip nothing. |
| k_c6_sb_count | No pin was sunk, more than one was, the contest was never run, or st was NULL. |
Definition at line 294 of file c6_sideband.c.
References k_c6_sb_count, c6_probe_stats_t::pull_low, and c6_probe_stats_t::pull_samples.
Referenced by c6_probe_resolve_map().
| bool c6_probe_sweep_mode | ( | ra8_spi_mode_t | mode, |
| uint32_t | pclka_hz, | ||
| c6_probe_stats_t * | st, | ||
| uint8_t * | hs_idx ) |
Run the transaction burst for one SPI mode.
| [in] | mode | Clock polarity / phase to open the channel with. |
| [in] | pclka_hz | SCI baud-clock source, in hertz. |
| [in,out] | st | Statistics block to accumulate into. |
| [in,out] | hs_idx | Handshake index, refined as evidence accumulates. |
| true | At least one idle or data frame decoded in this mode. |
| false | The mode produced nothing recognisable. |
Definition at line 345 of file c6_xfer.c.
References c6_probe_best(), c6_probe_put_u32(), c6_probe_puts(), c6_probe_stats_t::hs_vote, internal_one_xfer(), k_c6_frame_data, k_c6_frame_idle, k_c6_probe_gap_ms, k_c6_probe_min_votes, k_c6_probe_sck_hz, k_c6_probe_xfer_per_mode, k_c6_sb_count, k_ra8_board_pmod1_sci_channel, k_ra8_ok, ra8_delay_ms(), ra8_sci_spi_deinit(), and ra8_sci_spi_init().
Referenced by c6_probe_run().
| void c6_probe_vote | ( | const c6_sideband_sample_t * | pre, |
| const c6_sideband_sample_t * | mid, | ||
| const c6_sideband_sample_t * | post, | ||
| c6_probe_stats_t * | st ) |
Fold one transaction's three samples into the classification votes.
HANDSHAKE is the pin that is high before the transfer and low while the chip-select is asserted, because the C6 is built with CONFIG_ESP_SPI_DEASSERT_HS_ON_CS=y and clears it from its chip-select edge interrupt (gpio_disable_hs_isr_handler in the C6's peripheral-side SPI driver). That is a genuine identification: the pin moves, twice per transaction, in step with a line the probe itself drives.
The DATA_READY vote is far weaker and must not be read as its equal. It counts a pin that was high before the transfer and still low well after it, which is what the C6 does when it drains a queued frame (get_next_tx_buffer). That is a once-per-boot event: the C6 raises DATA_READY for its queued INIT event and lowers it when the first transaction takes it, after which the pin stays low and every later pre sample is already low, so the vote cannot fire again. Worse, that single transition usually lands between transactions – during the chip-select hunt, say – where nothing is sampling, so in practice the counter often stays at zero for the correctly-wired pin. The vote proves DATA_READY when it fires and says nothing at all when it does not, which is why c6_probe_resolve_map prefers the pull-up contest and keeps this counter only as a fallback.
| [in] | pre | Sample taken with the chip-select released. |
| [in] | mid | Sample taken with the chip-select asserted. |
| [in] | post | Sample taken after the chip-select was released again. |
| [in,out] | st | Statistics block to accumulate into. |
Definition at line 156 of file c6_sideband.c.
References c6_probe_stats_t::dr_vote, c6_probe_stats_t::ever_high, c6_probe_stats_t::ever_low, c6_probe_stats_t::hs_vote, k_c6_sb_count, and c6_sideband_sample_t::level.
Referenced by internal_one_xfer().
| c6_wire_kind_t c6_probe_wire_kind | ( | uint8_t | after_high, |
| uint8_t | after_low ) |
Classify one Pmod1 net from its two drive-and-release samples.
| [in] | after_high | Level read after driving the net high and releasing. |
| [in] | after_low | Level read after driving the net low and releasing. |
| k_c6_wire_floating | Held both driven levels: nothing attached. |
| k_c6_wire_low_side | Snapped low both times: pull-down or driver. |
| k_c6_wire_high_side | Snapped high both times: pull-up or driver. |
| k_c6_wire_odd | Inverted pair; not physically expected. |
Definition at line 384 of file c6_sideband.c.
References k_c6_wire_floating, k_c6_wire_high_side, k_c6_wire_low_side, and k_c6_wire_odd.
Referenced by c6_probe_wire_test().
| void c6_probe_wire_test | ( | void | ) |
Run the drive-and-release test over every Pmod1 muxed net.
Prints one line per candidate so the log states, without inference, what terminates each MCU pin that the board's Pmod1 mux might connect to J26.
Definition at line 398 of file c6_sideband.c.
References c6_probe_put_u32(), c6_probe_puts(), c6_probe_wire_kind(), internal_kick_net(), k_c6_wire_count, k_c6_wire_label, k_c6_wire_pin, k_ra8_level_high, k_ra8_level_low, and k_ra8_ok.
Referenced by main().