v2n-gd32-bridge-loopback
Jumpered Tier-B loopback validation of the GD32 supervisor bridge.
Where the sibling v2n-gd32-bridge-functional drives each bridge surface against a known-exact software answer — and reads ADC pads that float on the bench — this tier closes the loop in copper: three physical jumpers on the E1M-X V2 carrier route a bridge output back into a bridge input, so the analog and timer signal paths get validated end to end on real silicon.
Source: examples/v2n/v2n-gd32-bridge-loopback/.
:::warning Bench-only, and jumper-only
This is a maintainer bench tool in example form. Like the functional tier it exercises the gd32g553 chip driver directly — the documented exception to the portable-API rule for dedicated bridge demos.
On an unwired board every value assertion fails. That is expected. The suite is only meaningful on a board jumpered per the table below. :::
It runs the three tests once, publishes a verdict block the V2N DAP reads over SWD (there is no console on this SoM), then idles forever. Every stimulus is parked at 0 on the way out of its test.
Bench wiring (E1M-X V2 carrier)
| Jumper | From | To | Signal path |
|---|---|---|---|
| A | Raw DAC0 net (E1M-X pin A19) | P7.1 (CK_ANA) | Direct 1:1 analog loopback → raw passthrough to E1M-X pin A17 = ANA_S0 = bridge ADC channel 0. |
| B | J26.14 (CK_PWM1) | J18.7 (ENC1_X) | PWM bridge ch1 → encoder index 1 X input. Bidirectional level translation on both sides (transparent to the signal). Y floats with a firmware pull-up = static HIGH. |
| C | J26.10 (CK_PWM2) | J26.8 (CK_PWM3) | PWM bridge ch2 output → PWM bridge ch3 rebound as input capture. Both pins ride the same bidirectional level translator, so no contention. |
Safety — read before plugging anything in
-
The carrier's buffered DAC output path (
J15.2) is inoperable on this carrier revision (carrier erratum, fixed next rev). The rawDAC0net is the only usable DAC0 source for this validation path on configured bench carriers.The loopback is same-rail 1.8 V → 1.8 V and physically cannot overdrive the ADC pad.
DAC_MAX_SAFE_MV(1500 mV) in the source is a linearity bound — stay off the rail-clip region — not an electrical cap. If a future carrier rev restores the bufferedJ15.2path, its ×2 gain makes everything above ~850 mV an over-rail hazard. Re-derive the bound before rewiring. -
No physical rotary encoder may be plugged into
J18during the qenc test. Jumper B drivesENC1_Xfrom the PWM bridge; an external encoder would contend the line.
The three tests
1. t_dac_adc_loopback (Jumper A)
For each setpoint in {150, 450, 900, 1350} mV: command DAC0, settle 3 ms, then read ADC channel 0 — a burst of 4 independent samples. The assertion takes the first; the burst exists so a noisy connection is visible in the forensics.
The expected reading equals the command (direct 1:1 wiring, both converters on the same 1.8 V VREF). Tolerance is ±(25 mV + 2 % of expected) — offset/INL of the converter pair plus scale error. Tighter than a buffered path, because no external gain resistors remain in the loop. The DAC is parked at 0 on every exit path, including failures.
2. t_pwm_capture_loopback (Jumper C)
Drive PWM ch2 at 200 Hz, 50 % duty (5 ms period, 2.5 ms high), rebind ch3 as a both-edges input-capture source, settle 10 ms, then read in a tight poll loop (up to 80 reads, no inter-read delay) that treats ALP_ERR_NOSUPPORT as the documented "no fresh edge yet, poll again" sentinel. Asserts pulse width in [2400000, 2600000] ns — 2.5 ms ± 100 µs.
Two choices make this robust on a shared-timer loopback (ch2 and ch3 both ride TIMER0):
- 50 % duty — the both-edges machine measures the delta between adjacent edges. At 50 % the high and low times are equal (period/2 = 2.5 ms), so the pulse width is the same regardless of which edge armed the capture. No phase ambiguity.
- Slow rate + tight polling — at 200 Hz the edges are 2.5 ms apart, far wider than one bridge transaction (~150 µs), so the host catches three consecutive edges. At the old 1 kHz with a 5 ms retry ladder the three samples were non-consecutive edges and the delta was meaningless — the bug that made this read 0 before bridge firmware v0.2.7.
The period is deliberately not asserted. Stimulus and capture share TIMER0, so the same-edge "period" delta is exactly one counter wrap and reads ~0 — a documented degeneracy, not a fault. The raw period_ns and pulse_width_ns are still recorded for forensics. Firmware v0.2.7 takes all edge deltas modulo the counter period, so the wrap underflow no longer poisons the pulse-width reading.
3. t_pwm_qenc_stimulus (Jumper B)
Reset encoder 1, drive ENC1_X with a 1 kHz 50 % square from PWM ch1, wait 100 ms, read pos1, wait 10 ms, read pos2, park ch1. Asserts all statuses OK and |pos1| ≤ 8 and |pos2| ≤ 8.
In X4 quadrature decode with Y held static HIGH, a lone toggling X cannot accumulate net position — each X edge with an unchanging Y is an ambiguous transition the decoder treats as ±1 dither about the origin. The bound is deliberately loose for this first silicon pass: it exists to catch the failure mode this loopback guards against — a genuinely floating ENC input free-ran to thousands of counts. The raw pos1 / pos2 are recorded so the bound can be tightened from silicon truth.
Verdict block
A static volatile uint32_t loopback_results[32] the bench reads over SWD.
| Slot | Meaning |
|---|---|
[0] | Magic 0xB10CBAC4 — sanity-check the symbol + image before trusting the rest |
[1] | State: 0 = init, 1 = running, 2 = done (idle forever after), 0xDEAD = SPI never opened |
[2] | Pass count |
[3] | Fail count |
[4..11] | Per-record code (cursor order: 4× DAC setpoints, then capture, then qenc). 0 = PASS, 0x7E = transport OK but value assertion failed, anything else = the failing alp_status_t (two's complement) |
[12..15] | The four raw DAC → ADC readings (mV), in {150, 450, 900, 1350} setpoint order |
[16] | Raw capture period_ns — forensics, not asserted (shared-timer wrap degeneracy) |
[17] | Raw capture pulse_width_ns |
[18] / [19] | Raw qenc pos1 / pos2 (cast to u32 from int32_t) |
[20..31] | Reserved (0) |
Check the magic in [0] before trusting [2] / [3] and the raw forensics slots.
board.yaml
Same SoM/board declarations as the sibling functional example by design — one PCB, variant-populated V2N family.
som:
sku: E1M-V2M101
preset: e1m-x-evk
cores:
a55_cluster:
os: "off"
m33_sm:
app: ./src
peripherals:
- spi
- i2c
chips:
- gd32g553
diagnostics:
log_level: info
Status
build_only: true on native_sim/native/64. It builds clean everywhere as a CI artifact, but only produces meaningful results on an E1M-X V2 carrier wired per the table above. Every result on this page is a jumpered-bench signal.
See also
v2n-gd32-bridge-functional— the value-asserting tier without jumpersv2n-gd32-bridge-hil-soak— full-opcode forever soakv2n-gd32-bridge-ping— minimal liveness probe- GD32 bridge protocol — wire spec
- Chip catalogue
- Examples overview