ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
c6_probe.h File Reference

Shared contract for the EK-RA8D2 <-> ESP32-C6 esp-hosted SPI probe. More...

#include <stdint.h>
#include "c6_proto.h"
#include "ra8_err.h"
#include "ra8_spi.h"
Include dependency graph for c6_probe.h:
This graph shows which files directly or indirectly include this file:

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...

Enumerations

enum  c6_probe_tunable_t : uint32_t {
  k_c6_probe_uart_baud = 115200U ,
  k_c6_probe_sck_hz = 1000000U ,
  k_c6_probe_boot_wait_ms = 1500U ,
  k_c6_probe_settle_ms = 20U ,
  k_c6_probe_gap_ms = 100U ,
  k_c6_probe_cs_hold_ms = 2U ,
  k_c6_probe_idle_ms = 500U
}
 Timing and clock budgets for the probe. More...
enum  c6_probe_budget_t : uint8_t {
  k_c6_probe_xfer_per_mode = 4U ,
  k_c6_probe_mode_count = 4U ,
  k_c6_probe_hs_poll_max = 100U ,
  k_c6_probe_hs_poll_ms = 5U ,
  k_c6_probe_dump_bytes = 32U ,
  k_c6_probe_str_max = 96U ,
  k_c6_probe_pull_samples = 8U ,
  k_c6_probe_pull_settle_ms = 5U ,
  k_c6_probe_min_votes = 2U
}
 Statically provable loop bounds (NASA Power of 10 Rule 2). More...
enum  c6_probe_fmt_t : uint8_t {
  k_c6_fmt_dec_radix = 10U ,
  k_c6_fmt_dec_digits = 10U ,
  k_c6_fmt_hex_max = 8U ,
  k_c6_fmt_hex_bits = 4U ,
  k_c6_fmt_hex_mask = 0x0FU ,
  k_c6_fmt_hex_alpha = 10U ,
  k_c6_fmt_hex_byte = 2U ,
  k_c6_fmt_hex_word = 4U
}
 Radix and field-width constants used by the console formatters. More...
enum  c6_sideband_idx_t : uint8_t {
  k_c6_sb_irq = 0U ,
  k_c6_sb_reset = 1U ,
  k_c6_sb_gpio_a = 2U ,
  k_c6_sb_gpio_b = 3U ,
  k_c6_sb_count = 4U
}
 Index into the Pmod1 side-band pin table. More...
enum  c6_wire_idx_t : uint8_t {
  k_c6_wire_p800 = 0U ,
  k_c6_wire_p801 = 1U ,
  k_c6_wire_p802 = 2U ,
  k_c6_wire_p803 = 3U ,
  k_c6_wire_p804 = 4U ,
  k_c6_wire_count = 5U
}
 Index into the Pmod1 muxed-net table. More...
enum  c6_wire_kind_t : uint8_t {
  k_c6_wire_floating = 0U ,
  k_c6_wire_low_side = 1U ,
  k_c6_wire_high_side = 2U ,
  k_c6_wire_odd = 3U
}
 What the drive-and-release test says is on the far end of a net. 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.

Detailed Description

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:

  • src/c6_console.c – bounded console formatters (no newlib printf).
  • src/c6_sideband.c – Pmod1 side-band sampling, the muxed-net wire test, the pull-up contest that identifies DATA_READY, and the chip-select hunt that identifies HANDSHAKE. None of the three needs a working data path.
  • src/c6_frame.c – the c6_proto.h half: payload-header decode, checksum and classification.
  • src/c6_xfer.c – one full 1600-byte esp-hosted transaction.

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}

Since
0.1.0

Definition in file c6_probe.h.

Enumeration Type Documentation

◆ c6_probe_budget_t

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.

Invariant
Every value is non-zero.
Example:
for (uint8_t i = 0U; i < (uint8_t)k_c6_probe_xfer_per_mode; i++) { ... }
@ k_c6_probe_xfer_per_mode
Transactions attempted per SPI mode.
Definition c6_probe.h:106
See also
c6_probe_tunable_t
Enumerator
k_c6_probe_xfer_per_mode 

Transactions attempted per SPI mode.

k_c6_probe_mode_count 

SPI modes swept.

k_c6_probe_hs_poll_max 

Handshake polls before giving up.

k_c6_probe_hs_poll_ms 

Delay between handshake polls.

k_c6_probe_dump_bytes 

Payload bytes hex-dumped per frame.

k_c6_probe_str_max 

Longest console literal accepted.

k_c6_probe_pull_samples 

Reads per pin in the pull-up contest.

k_c6_probe_pull_settle_ms 

Settle between pull-up contest reads.

k_c6_probe_min_votes 

Votes required to claim a mapping.

Definition at line 105 of file c6_probe.h.

◆ c6_probe_fmt_t

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.

Invariant
k_c6_fmt_dec_digits is wide enough for UINT32_MAX.
Example:
c6_probe_put_hex(header_byte, (uint8_t)k_c6_fmt_hex_byte);
@ k_c6_fmt_hex_byte
Nibbles printed for one byte.
Definition c6_probe.h:142
void c6_probe_put_hex(uint32_t value, uint8_t digits)
Emit a value as fixed-width lowercase hexadecimal.
Definition c6_console.c:56
See also
c6_probe_put_hex
Enumerator
k_c6_fmt_dec_radix 

Base ten.

k_c6_fmt_dec_digits 

"4294967295" is ten digits.

k_c6_fmt_hex_max 

A uint32_t is eight nibbles.

k_c6_fmt_hex_bits 

Bits per nibble.

k_c6_fmt_hex_mask 

Nibble mask.

k_c6_fmt_hex_alpha 

First nibble printed as a letter.

k_c6_fmt_hex_byte 

Nibbles printed for one byte.

k_c6_fmt_hex_word 

Nibbles printed for a 16-bit word.

Definition at line 135 of file c6_probe.h.

◆ c6_probe_tunable_t

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.

Invariant
Every value is a positive, statically known bound.
Example:
@ k_c6_probe_boot_wait_ms
Let the C6 finish booting.
Definition c6_probe.h:73
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
See also
c6_probe_budget_t
Enumerator
k_c6_probe_uart_baud 

Console baud (J-Link OB VCOM).

k_c6_probe_sck_hz 

Conservative first-light SCK rate.

k_c6_probe_boot_wait_ms 

Let the C6 finish booting.

k_c6_probe_settle_ms 

Post-transfer side-band settle.

k_c6_probe_gap_ms 

Gap between transactions.

k_c6_probe_cs_hold_ms 

Chip-select setup / hold.

k_c6_probe_idle_ms 

Background heartbeat period.

Definition at line 70 of file c6_probe.h.

◆ c6_sideband_idx_t

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.

Invariant
k_c6_sb_count equals the length of the side-band pin table.
Example:
void c6_probe_sample_sideband(c6_sideband_sample_t *out)
Read every Pmod1 side-band pin into one sample.
One simultaneous reading of every side-band pin.
Definition c6_probe.h:194
See also
c6_sideband_sample_t
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.

◆ c6_wire_idx_t

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.

Invariant
k_c6_wire_count equals the length of the muxed-net table.
Example:
c6_probe_wire_test(); // prints one line per candidate
void c6_probe_wire_test(void)
Run the drive-and-release test over every Pmod1 muxed net.
See also
c6_wire_kind_t
Enumerator
k_c6_wire_p800 

CTS2 – J26-1 only in the UART position.

k_c6_wire_p801 

COPI2 – J26-2 in SPI and UART positions.

k_c6_wire_p802 

CIPO2 – J26-3 in SPI and UART positions.

k_c6_wire_p803 

SCK2 – J26-4 only in the SPI position.

k_c6_wire_p804 

SS2 – J26-1 in SPI, J26-4 in UART.

k_c6_wire_count 

Muxed-net candidate count.

Definition at line 221 of file c6_probe.h.

◆ c6_wire_kind_t

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.

Invariant
Exactly one kind describes any sample pair.
Example:
const c6_wire_kind_t k = c6_probe_wire_kind(after_high, after_low);
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.
c6_wire_kind_t
What the drive-and-release test says is on the far end of a net.
Definition c6_probe.h:250
See also
c6_probe_wire_test
Enumerator
k_c6_wire_floating 

Held both levels: no termination.

k_c6_wire_low_side 

Snapped to 0: pull-down or driven low.

k_c6_wire_high_side 

Snapped to 1: pull-up or driven high.

k_c6_wire_odd 

Inverted result: not physically sane.

Definition at line 250 of file c6_probe.h.

Function Documentation

◆ c6_probe_best()

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.

Parameters
[in]votesPer-pin vote counters.
[in]ignoreIndex to skip, or k_c6_sb_count to skip nothing.
[in]min_votesSmallest vote count that may claim a mapping; must be at least one.
Returns
Winning pin index, or k_c6_sb_count when none qualifies.
Return values
k_c6_sb_countNo pin reached min_votes, the top score was tied, votes was NULL, or min_votes was zero.
Precondition
votes is non-NULL for a winner to be reported.
ignore is a valid index or k_c6_sb_count.
Postcondition
The returned index is either a strict winner or k_c6_sb_count.
votes is unmodified.
Note
Pure function; safe from any context.
Example:
const uint8_t hs = c6_probe_best(stats.hs_vote,
(uint8_t)k_c6_sb_count,
@ k_c6_sb_count
Side-band pin count.
Definition c6_probe.h:177
@ k_c6_probe_min_votes
Votes required to claim a mapping.
Definition c6_probe.h:114
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.
See also
c6_probe_resolve_map
Since
0.1.0

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().

◆ c6_probe_cs_hunt()

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.

Parameters
[in,out]stStatistics block; handshake votes are accumulated here.
Returns
Index into the muxed-net table of the pin that reached the C6.
Return values
k_c6_wire_countNo candidate provoked a side-band response.
Precondition
The console is up and the Pmod1 SPI pins are unclaimed.
st is non-NULL.
Postcondition
Every tested pin is left unclaimed.
st->hs_vote records every observed drop.
Note
Safe against contention: every candidate is an input on the C6.
Example:
const uint8_t cs = c6_probe_cs_hunt(&stats);
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...
See also
c6_probe_wire_test
Since
0.1.0

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().

◆ c6_probe_print_sideband()

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 ....

Parameters
[in]labelShort prefix, already indented by the caller.
[in]sSample to print; ignored when NULL.
Precondition
The board UART console has been initialised.
s is non-NULL for anything to be printed.
Postcondition
Exactly one console line was emitted when s is non-NULL.
Neither argument is modified.
Note
Not thread-safe.
See also
c6_probe_sample_sideband
Since
0.1.0

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().

◆ c6_probe_pull_contest()

ra8_err_t c6_probe_pull_contest ( c6_probe_stats_t * st)
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.

Parameters
[in,out]stStatistics block; pull_low and pull_samples are written.
Returns
ra8_err_t error code.
Return values
k_ra8_okEvery pin was tested and restored to a no-pull input.
k_ra8_err_null_ptrst was NULL.
k_ra8_err_gpio_conflictA side-band pin is owned elsewhere.
Precondition
c6_probe_sideband_init has claimed the side-band pins.
The chip-select is released and no transaction is in flight.
Postcondition
On success every side-band pin is a no-pull input again.
On success st->pull_samples is k_c6_probe_pull_samples.
Warning
Timing is part of the measurement, in both directions. Run it only with the chip-select released – HANDSHAKE is legitimately low while it is asserted, and would read as a sunk pin. Run it only after the transaction sweep as well: a freshly-booted C6 holds DATA_READY high for its queued INIT event until the first transaction drains it, so an early contest finds nothing sunk.
Note
Not thread-safe; boot-time diagnostic only.
Example:
if (c6_probe_pull_contest(&stats) != k_ra8_ok) { panic(); }
ra8_err_t c6_probe_pull_contest(c6_probe_stats_t *st)
Find which side-band pins an external driver is holding low.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
See also
c6_probe_sunk_pin
Since
0.1.0

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().

◆ c6_probe_put_hex()

void c6_probe_put_hex ( uint32_t value,
uint8_t digits )

Emit a value as fixed-width lowercase hexadecimal.

Parameters
[in]valueValue to print.
[in]digitsNumber of nibbles to emit (1..8).
Precondition
The board UART console has been initialised.
digits is between one and eight inclusive.
Postcondition
Exactly digits characters were queued when in range.
Nothing is queued when digits is out of range.
Note
Not thread-safe.
See also
c6_probe_put_u32
Since
0.1.0

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().

◆ c6_probe_put_u32()

void c6_probe_put_u32 ( uint32_t value)

Emit an unsigned integer in decimal.

Parameters
[in]valueValue to print; zero prints as a single 0.
Precondition
The board UART console has been initialised.
value fits in 32 bits by construction.
Postcondition
Between one and ten ASCII digits were queued.
No trailing separator was emitted.
Note
Not thread-safe.
See also
c6_probe_put_hex
Since
0.1.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().

◆ c6_probe_puts()

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.

Parameters
[in]textNUL-terminated ASCII string; ignored when NULL.
Precondition
The board UART console has been initialised.
text is NUL-terminated within k_c6_probe_str_max bytes.
Postcondition
At most k_c6_probe_str_max bytes were queued to the console.
text is unmodified.
Note
Not thread-safe; single-threaded logging only.
See also
c6_probe_put_u32
Since
0.1.0

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().

◆ c6_probe_resolve_map()

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:

  1. HANDSHAKE is the pin that wins the vote outright – more chip-select-tracking transitions than any other pin, and at least k_c6_probe_min_votes of them. The C6 drives that edge itself, so repeated transitions in step with a line the probe controls are not something noise produces.
  2. DATA_READY is the pin that loses the pull-up contest outright, excluding whichever pin took HANDSHAKE. An idle esp-hosted peripheral holds DATA_READY low, and only a real connection can hold a pin down against the pull-up.
  3. Failing that, DATA_READY falls back to the drain-transition vote under the same threshold – the only evidence available if the C6 had a frame queued and drained it during the run.

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.

Parameters
[in]stAccumulated evidence.
[out]hs_idxResolved HANDSHAKE index, or k_c6_sb_count.
[out]dr_idxResolved DATA_READY index, or k_c6_sb_count.
Precondition
All three pointers are non-NULL.
st holds the evidence of a completed run.
Postcondition
*hs_idx and *dr_idx are valid indices or k_c6_sb_count.
*hs_idx != *dr_idx unless both are k_c6_sb_count.
Note
Pure with respect to st; safe from any context.
Example:
uint8_t hs = 0U;
uint8_t dr = 0U;
c6_probe_resolve_map(&stats, &hs, &dr);
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.
See also
c6_probe_pull_contest
Since
0.1.0

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().

◆ c6_probe_sample_sideband()

void c6_probe_sample_sideband ( c6_sideband_sample_t * out)

Read every Pmod1 side-band pin into one sample.

Parameters
[out]outDestination sample; ignored when NULL.
Precondition
Every side-band pin was configured as an input.
out is non-NULL for the sample to be stored.
Postcondition
On success every out->level entry is 0 or 1.
A pin whose read fails is recorded as 0 rather than left stale.
Note
Not thread-safe.
Example:
See also
c6_probe_print_sideband
Since
0.1.0

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().

◆ c6_probe_sideband_init()

ra8_err_t c6_probe_sideband_init ( void )
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.

Returns
ra8_err_t error code.
Return values
k_ra8_okAll four side-band pins are inputs.
k_ra8_err_gpio_conflictA pin is already owned elsewhere.
k_ra8_err_gpio_invalid_portBoard pin table disagrees with the HAL.
Precondition
ra8_mstp_init has run so PFS writes land.
No other driver owns the Pmod1 side-band pins.
Postcondition
On success every side-band pin is a digital input.
No side-band pin is left driven.
Note
Not thread-safe; boot-time only.
See also
c6_probe_sample_sideband
Since
0.1.0

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().

◆ c6_probe_sideband_name()

const char * c6_probe_sideband_name ( uint8_t idx)

Name a side-band pin for the console.

Parameters
[in]idxSide-band index, or k_c6_sb_count for "unresolved".
Returns
Static label for the pin.
Return values
unresolvedidx was out of range.
Precondition
idx is a valid index or k_c6_sb_count.
The returned pointer is never freed by the caller.
Postcondition
The returned string is NUL-terminated.
No state is modified.
Note
Pure function; safe from any context.
See also
c6_probe_print_sideband
Since
0.1.0

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().

◆ c6_probe_spi_pins_init()

ra8_err_t c6_probe_spi_pins_init ( void )
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.

Returns
ra8_err_t error code.
Return values
k_ra8_okAll four pins routed or claimed.
k_ra8_err_gpio_conflictA pin is already owned elsewhere.
k_ra8_err_gpio_invalid_portBoard pin table disagrees with the HAL.
Precondition
ra8_mstp_init has run so PFS writes land.
The diagnostics released every Pmod1 pin they claimed.
Postcondition
On success the chip-select is an output driven high (deasserted).
On success SCK / CIPO / COPI carry their SCI2 function.
Note
Not thread-safe; boot-time only.
Example:
if (c6_probe_spi_pins_init() != k_ra8_ok) { panic(); }
ra8_err_t c6_probe_spi_pins_init(void)
Route the Pmod1 SPI pins to SCI2 and own the chip-select as GPIO.
Definition c6_xfer.c:322
See also
c6_probe_sweep_mode
Since
0.1.0

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().

◆ c6_probe_sunk_pin()

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.

Parameters
[in]stStatistics block filled by c6_probe_pull_contest.
[in]ignoreIndex to skip, or k_c6_sb_count to skip nothing.
Returns
Index of the uniquely sunk pin, or k_c6_sb_count.
Return values
k_c6_sb_countNo pin was sunk, more than one was, the contest was never run, or st was NULL.
Precondition
st is non-NULL for a verdict to be reported.
ignore is a valid index or k_c6_sb_count.
Postcondition
st is unmodified.
The returned index is unique, or k_c6_sb_count.
Note
Pure function; safe from any context.
Example:
const uint8_t dr = c6_probe_sunk_pin(&stats, hs_idx);
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.
See also
c6_probe_pull_contest
Since
0.1.0

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().

◆ c6_probe_sweep_mode()

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.

Parameters
[in]modeClock polarity / phase to open the channel with.
[in]pclka_hzSCI baud-clock source, in hertz.
[in,out]stStatistics block to accumulate into.
[in,out]hs_idxHandshake index, refined as evidence accumulates.
Returns
true when the C6 answered with a recognisable frame.
Return values
trueAt least one idle or data frame decoded in this mode.
falseThe mode produced nothing recognisable.
Precondition
pclka_hz is non-zero.
st and hs_idx are non-NULL.
Postcondition
The SPI channel is closed again before returning.
*hs_idx names the best handshake candidate seen so far.
Note
Not thread-safe.
Example:
const bool up = c6_probe_sweep_mode(k_ra8_spi_mode_3, pclka, &st, &hs);
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.
Definition c6_xfer.c:345
@ k_ra8_spi_mode_3
CPOL=1, CPHA=1.
Definition ra8_spi.h:65
See also
c6_probe_spi_pins_init
Since
0.1.0

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().

◆ c6_probe_vote()

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.

Parameters
[in]preSample taken with the chip-select released.
[in]midSample taken with the chip-select asserted.
[in]postSample taken after the chip-select was released again.
[in,out]stStatistics block to accumulate into.
Precondition
All four pointers are non-NULL.
The three samples come from the same transaction, in order.
Postcondition
Vote counters only ever grow.
ever_high / ever_low reflect all three samples.
Note
Not thread-safe.
Example:
c6_probe_vote(&pre, &mid, &post, &stats);
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.
See also
c6_probe_best
Since
0.1.0

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_probe_wire_kind()

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.

Parameters
[in]after_highLevel read after driving the net high and releasing.
[in]after_lowLevel read after driving the net low and releasing.
Returns
The termination classification.
Return values
k_c6_wire_floatingHeld both driven levels: nothing attached.
k_c6_wire_low_sideSnapped low both times: pull-down or driver.
k_c6_wire_high_sideSnapped high both times: pull-up or driver.
k_c6_wire_oddInverted pair; not physically expected.
Precondition
Both samples come from the same net, high test first.
Each sample is 0 or 1.
Postcondition
Exactly one classification is returned.
Neither argument is modified.
Note
Pure function; safe from any context.
Example:
const c6_wire_kind_t k = c6_probe_wire_kind(1U, 0U); // floating
See also
c6_probe_wire_test
Since
0.1.0

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().

◆ 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.

Precondition
The console is up and the Pmod1 SPI pins are unclaimed.
ra8_time_init has run so the settle delays are real.
Postcondition
Every tested pin is left unclaimed, ready for PFS routing.
Exactly k_c6_wire_count result lines were printed.
Note
Not thread-safe; boot-time diagnostic only.
Example:
See also
c6_probe_cs_hunt
Since
0.1.0

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().