Digital output persistence fails silently when libbela's context FIFO activates
- Dominant language
- Rust
- Stars
- 1
- Forks
- 0
- Avg merge
- 5h 19m
- Merged PRs (30d)
- 26
Description
Measured on a Gem Stereo while verifying the digital accessors (#11): once libbela switches the application to its context-FIFO path, an output configured once and then written intermittently no longer reaches the pin. `Bela_initAudio` succeeds, audio keeps running, the LED wired to the output never changes, and no warning is printed.
`bela/examples/io_digital` configures its output pins in the first application block, then relies on Bela's documented persistence between blocks. It drives a loopback from one output to one input, toggles the output once every eight application blocks, and counts the returned edges:
| requested period | `gFifoFactor` | core frames | application frames | edges seen |
|---:|---:|---:|---:|---|
| 16, 64, 128 | 1 | same as requested | same as requested | one per write |
| 129, 160, 192, 255 | 1 | same as requested | same as requested | one per write |
| 256 | 2 | 128 | 256 | none |
| 320 | 2 | 160 | 320 | none |
Every working period had a write-to-read latency of exactly `2 * period + 1` digital frames. At 256 and 320 the LED did not blink and the loopback saw no edges.
The loopback does **not** independently prove that digital input sampling is broken: its input is driven by the output that has already failed. A separately driven input still needs to be tested in FIFO mode.
## What the boundary is
The failure begins when `gFifoFactor` becomes greater than 1.
The PRU shared-memory layout does contain two 256-word digital buffers:
```c
#define PRU_MEM_DIGITAL_BUFFER1_OFFSET 0x400 // Start pointer to DIGITAL_BUFFER1, which is 256 words.
// 256 is the maximum number of frames allowed
```
But libbela first divides a large requested period into native core blocks. In the failing Gem Stereo runs, the PRU-side `digitalFrames` is 128 at requested period 256 and 160 at requested period 320. Both fit in that buffer. The application sees 256 or 320 frames because `BelaContextFifo` joins the shorter core contexts before calling the user's `render`.
So the 256-word PRU buffer is not the direct cause. The 255/256 boundary is where this board starts running the user's callback through the FIFO.
## Already tracked upstream
BelaPlatform/Bela#604, **"core: large block sizes do not play well with remembering past digital I/O and analog out values"**, is open and describes this as a persistence-placement problem:
> "persistency" is implemented in `PRU::loop()`, while it should happen around wherever the user callback is called.
A comment on that issue reports the same visible boundary: the upstream `digital-out` example stops blinking at block size 256. The suggested workaround is to call both `digitalWrite()` and `pinMode(..., frame 0, ..., OUTPUT)` in every application block.
That matches this probe closely. It intentionally configures each pin once and writes only every eighth application block, relying on the persistence that works without the FIFO. Once the user callback moves behind `BelaContextFifo`, persistence is still applied around the shorter core callback instead of around the user callback.
No pull request or commit referencing #604 was found, and the issue remains open.
## A second source concern, not yet established as this cause
`BelaContextSplitter` also routes the digital `uint32_t*` buffer through helpers typed as `float*`. libbela is built with `-ffast-math`, so that is not an acceptable bit-preserving representation of arbitrary digital words.
This should be fixed or tested independently, but the current hardware result does not distinguish it from the already-known persistence bug. The upstream workaround provides the first discriminating test: if repeating `pinMode` and `digitalWrite` in every application block restores the 256/320 runs, #604 explains the observed failure without relying on the splitter-type hypothesis.
## What this issue is for
The long-term fix belongs in libbela #604: digital direction and output-value persistence must be applied around the user callback, including when that callback runs on `bela-audio-fifo`.
Until a fixed libbela reaches the board image, bela-rs may also need a temporary warning for the measured Gem Stereo combination:
- digital I/O is enabled;
- the resolved period is at least 256 frames;
- the installed libbela still has the affected FIFO persistence behavior.
A hard period limit or a type-level restriction is not a good permanent model. FIFO thresholds vary by board, command-line arguments can override `Settings`, and the combination should work once libbela is fixed. Refusing it would also reject audio-only applications unless they explicitly disable digital I/O, because digital is enabled by default.
## Acceptance
- [x] Add a probe mode that repeats `pin_mode` and `digital_write` in every application block, matching the workaround in BelaPlatform/Bela#604 (#106).
- [x] Re-run periods 255, 256 and 320 on the Gem Stereo. Restoration at 256/320 confirms the #604 path (#106).
- [x] Drive the input pin independently of a Bela output and test input sampling in FIFO mode (#107).
- [x] Report the measurements and probe to BelaPlatform/Bela#604 (https://github.com/BelaPlatform/Bela/issues/604#issuecomment-5248152903).
- [ ] Fix persistence around the user callback upstream and repeat the hardware runs without the workaround.
- [x] Run a separate bit-exact FIFO round-trip probe for digital words; it passed on the deployed `libbela` but does not make the `float*` splitter path language-safe (#107).
- [ ] Decide whether bela-rs needs a version-gated temporary warning.
Related: #84 concerns settings that libbela rejects or mishandles during initialisation. This issue instead concerns runtime semantics that change when the same valid configuration crosses into FIFO mode.
Contributor guide
Assessment
This issue has not been assessed yet.