Skip to content

Accessing RX FIFO in PUTGET mode triggers DREQ #2350

Open
@KewlKris

Description

@KewlKris

I've noticed a strange PIO behavior which I can't find documented anywhere. I've created a small project to isolate and reproduce this issue.

I'm using RX FIFO in PUTGET mode to give the state machine extra scratch registers. The RP2350 data sheet describes this use case, and the .fifo putget pioasm directive makes it easy to configure this. I'm able to use the mov OSR, RXFIFO[y] and mov RXFIFO[y], ISR instructions to read and write from RX FIFO as expected.

The problem occurs when I then use the PIO DREQ to pace word transfers from DMA to TX FIFO. From testing, I've found that mov OSR, RXFIFO[y] triggers DREQ_PIO0_TX0 and mov RXFIFO[y], ISR triggers DREQ_PIO0_RX0. This means that every time a word is loaded from RX FIFO into OSR, the DMA overwrites data in TX FIFO which never makes it to the state machine.

To test this, I've written a small program which records how long a DMA transfer takes to complete. If DREQ is being called extra times, then the DMA transfer will finish sooner than expected (which is how I noticed this behavior). Further below, I've included the C program which does the configuration and performs the test.

test.pio (normal transfer):

.program test
.pio_version 1
.fifo putget

.wrap_target
    pull block ; First DREQ

    nop ; Placeholder instruction

    nop [31] ; Delay
.wrap

Output: DMA transfer took 475 ms

test.pio (data loss):

.program test
.pio_version 1
.fifo putget

.wrap_target
    pull block ; First DREQ

    mov OSR, RXFIFO[0] ; Second, unexpected DREQ

    nop [31] ; Delay
.wrap

Output: DMA transfer took 237 ms

dma_test.c:

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#include "hardware/powman.h"

#include "test.pio.h"

static uint32_t SOME_VALUE = 0xdeadbeef;

int main() {
    stdio_init_all();

    // Configure DMA
    int dma_channel = dma_claim_unused_channel(true);
    dma_channel_config dma_config = dma_channel_get_default_config(dma_channel);
    channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32);
    channel_config_set_read_increment(&dma_config, false);
    channel_config_set_write_increment(&dma_config, false);
    channel_config_set_dreq(&dma_config, DREQ_PIO0_TX0);
    dma_channel_configure(
        dma_channel,
        &dma_config,
        &pio0_hw->txf[0], // Write address
        &SOME_VALUE, // Read address
        (2 << 20), // Transfer count (a bunch so it can be timed in milliseconds)
        false
    );

    // Configure PIO
    const uint state_machine = 0;
    uint prgm_offset = pio_add_program(pio0, &test_program);
    pio_sm_config pio_config = test_program_get_default_config(prgm_offset);
    pio_sm_init(pio0, state_machine, prgm_offset, &pio_config);

    // Start recording duration
    powman_timer_set_1khz_tick_source_xosc();
    powman_timer_set_ms(0);
    powman_timer_start();

    // Start DMA and PIO
    dma_channel_start(dma_channel);
    pio_sm_set_enabled(pio0, state_machine, true);

    // Wait for DMA to complete transfer
    dma_channel_wait_for_finish_blocking(dma_channel);

    // Print results
    uint64_t end_time = powman_timer_get_ms();
    printf("DMA transfer took %lld ms\n", end_time);
}

The PIO program with the mov instruction completes the transfer in half the time as the original. I believe this shows that there must be two DMA transfers in each iteration of the loop. I don't believe this effect is intentional because any read from the RX FIFO "registers" causes the DMA to throw away data. I spent more than a week re-reading data sheets because I assumed I must have been missing something, but I cannot for the life of me find any information which corroborates what I'm observing.

I think I can avoid this issue by either triggering DMA with an IRQ or padding the data buffer with empty slots so the important indices line up with pull block instructions, but both of these workarounds have drawbacks.

Is anyone able to reproduce this issue? Is this somehow an intended/expected behavior?

Please forgive me if there's an obvious answer, I'm new to RP2350 but eager to learn :)

Misc info

  • Board: Pico 2 W
  • Project files generated using VS Code Pico Extension
  • Using UART for stdio
  • Compiling on MacOS
  • Using pico-sdk v2.1.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions