utils icon indicating copy to clipboard operation
utils copied to clipboard

piolib: please add API to access the txstall register & other FDEBUG registers

Open jepler opened this issue 1 year ago • 4 comments

Sometimes it's necessary to wait for a txstall, not just tx fifo drain, before performing some other action. For instance, to implement transmit-only SPI with a GPIO CS pin, you need to wait for the last bit of SPI data to actually be transmitted before deasserting CS.

jepler avatar Jan 15 '25 15:01 jepler

Hi Jeff, my head is temporarily out of PIO space, but I'll consider this and #115 when it returns.

pelwell avatar Jan 16 '25 10:01 pelwell

TXSTALL appears along with other flags (RXSTALL. TXOVER and RXUNDER) in the FDEBUG register. There is currently no accessor function, so I feel free to come up with something new - ideally one that automatically selects the correct flags for the current/specified SM.

How do you feel about the following:

    uint pio_sm_get_flags(PIO pio, uint sm, uint flags, bool clear);
    uint pio_sm_wait_flags(PIO pio, uint sm, uint flags, bool clear, uint timeout_us);

flags is a bit mask of abstract flags. As you've probably guessed, clear allows any matching flags to be cleared. The return value is the sum of the flags which were set. pio_sm_wait_flags causes the return to block until any of the specified flags (is "events" a better name?) is true, or the timeout expires. pio_sm_get_flags is effectively pio_sm_wait_flag with a timeout of 0, and will probably be implemented as such. A return value of 0 indicates the timeout expired before any flags were set.

pelwell avatar Jan 17 '25 15:01 pelwell

Here's what we do in CircuitPython on rp2 around detecting completion of a transfer:

        // Clear the stall bit so we can detect when the state machine is done transmitting.
        self->pio->fdebug = stall_mask;
    }
    // Wait for the state machine to finish transmitting the data we've queued
    // up.
    if (tx) {
        while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) ||
               (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) {
            RUN_BACKGROUND_TASKS;
            if (self->user_interruptible && mp_hal_is_interrupted()) {
                break;
            }
        }
    }

the first bit would be pio_sm_get_flags(self->pio, self->state_machine, PIO_FLAG_TXSTALL, true); and the second would be pio_sm_get_flags(self->pio, self->state_machine, PIO_FLAG_TXSTALL, false). Because we need to poll for other events, we probably would't use a timeout_us variant.

We don't actually seem to use any other FDEBUG bits than TXSTALL and RXSTALL fwiw

jepler avatar Jan 17 '25 16:01 jepler

I'd already decided to include a pio_sm_clear_flags API function, however it is implemented.

pelwell avatar Jan 17 '25 16:01 pelwell