From 25a80ccd0fb451b0399e5bcc31d9bc8e3c43bee0 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 24 Sep 2024 13:50:25 -0700 Subject: [PATCH] add pio background read --- frozen/Adafruit_CircuitPython_LED_Animation | 2 +- frozen/Adafruit_CircuitPython_Requests | 2 +- frozen/Adafruit_CircuitPython_SD | 2 +- ports/raspberrypi/audio_dma.c | 20 +- .../bindings/rp2pio/StateMachine.c | 169 ++++++++++- .../bindings/rp2pio/StateMachine.h | 16 +- .../common-hal/rp2pio/StateMachine.c | 278 +++++++++++++----- .../common-hal/rp2pio/StateMachine.h | 12 +- 8 files changed, 414 insertions(+), 87 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_LED_Animation b/frozen/Adafruit_CircuitPython_LED_Animation index 576a094602b09..251bcd13cf14d 160000 --- a/frozen/Adafruit_CircuitPython_LED_Animation +++ b/frozen/Adafruit_CircuitPython_LED_Animation @@ -1 +1 @@ -Subproject commit 576a094602b099a1f211f126f68ff3cb07d32a05 +Subproject commit 251bcd13cf14da68bd176d6d2d97b844fac5cdd5 diff --git a/frozen/Adafruit_CircuitPython_Requests b/frozen/Adafruit_CircuitPython_Requests index 6c56de79fb49e..4b55319f0c7a2 160000 --- a/frozen/Adafruit_CircuitPython_Requests +++ b/frozen/Adafruit_CircuitPython_Requests @@ -1 +1 @@ -Subproject commit 6c56de79fb49ef16164148b68a63336d82b69f7a +Subproject commit 4b55319f0c7a24b91ca307a2cca49a4502c9752d diff --git a/frozen/Adafruit_CircuitPython_SD b/frozen/Adafruit_CircuitPython_SD index 8688597c753b7..bb8f0d84cdaa6 160000 --- a/frozen/Adafruit_CircuitPython_SD +++ b/frozen/Adafruit_CircuitPython_SD @@ -1 +1 @@ -Subproject commit 8688597c753b76beb8a834daef592294ffba216c +Subproject commit bb8f0d84cdaa6cc8fc8b1a1d83a2650d6f0a9ebd diff --git a/ports/raspberrypi/audio_dma.c b/ports/raspberrypi/audio_dma.c index c837bc116d48d..6255f5884da6f 100644 --- a/ports/raspberrypi/audio_dma.c +++ b/ports/raspberrypi/audio_dma.c @@ -480,7 +480,25 @@ void __not_in_flash_func(isr_dma_0)(void) { } if (MP_STATE_PORT(background_pio)[i] != NULL) { rp2pio_statemachine_obj_t *pio = MP_STATE_PORT(background_pio)[i]; - rp2pio_statemachine_dma_complete(pio, i); + rp2pio_statemachine_dma_complete_write(pio, i); + } + } +} + +void isr_dma_1(void) { + for (size_t i = 0; i < NUM_DMA_CHANNELS; i++) { + uint32_t mask = 1 << i; + if ((dma_hw->intr & mask) == 0) { + continue; + } + // acknowledge interrupt early. Doing so late means that you could lose an + // interrupt if the buffer is very small and the DMA operation + // completed by the time callback_add() / dma_complete() returned. This + // affected PIO continuous write more than audio. + dma_hw->ints1 = mask; + if (MP_STATE_PORT(background_pio)[i] != NULL) { + rp2pio_statemachine_obj_t *pio = MP_STATE_PORT(background_pio)[i]; + rp2pio_statemachine_dma_complete_read(pio, i); } } } diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index 806674ee4c3dd..6a9f08dff9521 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -535,7 +535,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_write_obj, 2, rp2pio_statemachine //| """ //| ... -static void fill_buf_info(sm_buf_info *info, mp_obj_t obj, size_t *stride_in_bytes) { +static void fill_buf_info_write(sm_buf_info *info, mp_obj_t obj, size_t *stride_in_bytes) { if (obj != mp_const_none) { info->obj = obj; mp_get_buffer_raise(obj, &info->info, MP_BUFFER_READ); @@ -567,8 +567,8 @@ static mp_obj_t rp2pio_statemachine_background_write(size_t n_args, const mp_obj sm_buf_info once_info; sm_buf_info loop_info; size_t stride_in_bytes = 0; - fill_buf_info(&once_info, args[ARG_once].u_obj, &stride_in_bytes); - fill_buf_info(&loop_info, args[ARG_loop].u_obj, &stride_in_bytes); + fill_buf_info_write(&once_info, args[ARG_once].u_obj, &stride_in_bytes); + fill_buf_info_write(&loop_info, args[ARG_loop].u_obj, &stride_in_bytes); if (!stride_in_bytes) { return mp_const_none; } @@ -602,6 +602,7 @@ static mp_obj_t rp2pio_statemachine_obj_stop_background_write(mp_obj_t self_in) } MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_stop_background_write_obj, rp2pio_statemachine_obj_stop_background_write); + //| writing: bool //| """Returns True if a background write is in progress""" static mp_obj_t rp2pio_statemachine_obj_get_writing(mp_obj_t self_in) { @@ -618,23 +619,166 @@ const mp_obj_property_t rp2pio_statemachine_writing_obj = { }; + //| pending: int +//| pending_write: int //| """Returns the number of pending buffers for background writing. //| //| If the number is 0, then a `StateMachine.background_write` call will not block.""" -static mp_obj_t rp2pio_statemachine_obj_get_pending(mp_obj_t self_in) { +static mp_obj_t rp2pio_statemachine_obj_get_pending_write(mp_obj_t self_in) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_rp2pio_statemachine_get_pending_write(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_pending_write_obj, rp2pio_statemachine_obj_get_pending_write); + +const mp_obj_property_t rp2pio_statemachine_pending_write_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&rp2pio_statemachine_get_pending_write_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + + +// ================================================================================================================================= + +//| def background_read( +//| self, +//| once: Optional[WriteableBuffer] = None, +//| *, +//| loop: Optional[WriteableBuffer] = None, +//| swap: bool = False, +//| ) -> None: +//| """Read data from the RX fifo in the background, with optional looping. +//| +//| First, if any previous ``once`` or ``loop`` buffer has not been started, this function blocks until they have been started. +//| This means that any ``once`` or ``loop`` buffer will be read at least once. +//| Then the ``once`` and/or ``loop`` buffers are queued. and the function returns. +//| The ``once`` buffer (if specified) will be read just once. +//| Finally, the ``loop`` buffer (if specified) will continue being read indefinitely. +//| +//| Reads from the FIFO will match the input buffer's element size. For example, bytearray elements +//| will perform 8 bit reads from the PIO FIFO. The RP2040's memory bus will duplicate the value into +//| the other byte positions. So, pulling more data in the PIO assembly will read the duplicated values. +//| +//| To perform 16 or 32 bits reads from the FIFO use an `array.array` with a type code of the desired +//| size, or use `memoryview.cast` to change the interpretation of an +//| existing buffer. To receive just part of a larger buffer, slice a `memoryview` +//| of it. +//| +//| Most use cases will probably only use one of ``once`` or ``loop``. +//| +//| Having neither ``once`` nor ``loop`` terminates an existing +//| background looping read after exactly a whole loop. This is in contrast to +//| `stop_background_read`, which interrupts an ongoing DMA operation. +//| +//| :param ~Optional[circuitpython_typing.WriteableBuffer] once: Data to be read once +//| :param ~Optional[circuitpython_typing.WriteableBuffer] loop: Data to be read repeatedly +//| :param bool swap: For 2- and 4-byte elements, swap (reverse) the byte order +//| """ +//| ... + +static void fill_buf_info_read(sm_buf_info *info, mp_obj_t obj, size_t *stride_in_bytes) { + if (obj != mp_const_none) { + info->obj = obj; + mp_get_buffer_raise(obj, &info->info, MP_BUFFER_WRITE); + size_t stride = mp_binary_get_size('@', info->info.typecode, NULL); + if (stride > 4) { + mp_raise_ValueError(MP_ERROR_TEXT("Buffer elements must be 4 bytes long or less")); + } + if (*stride_in_bytes && stride != *stride_in_bytes) { + mp_raise_ValueError(MP_ERROR_TEXT("Mismatched data size")); + } + *stride_in_bytes = stride; + } else { + memset(info, 0, sizeof(*info)); + } +} + +static mp_obj_t rp2pio_statemachine_background_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_once, ARG_loop, ARG_swap }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_once, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_loop, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_swap, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + sm_buf_info once_read_info; + sm_buf_info loop_read_info; + size_t stride_in_bytes = 0; + fill_buf_info_read(&once_read_info, args[ARG_once].u_obj, &stride_in_bytes); + fill_buf_info_read(&loop_read_info, args[ARG_loop].u_obj, &stride_in_bytes); + if (!stride_in_bytes) { + return mp_const_none; + } + + bool ok = common_hal_rp2pio_statemachine_background_read(self, &once_read_info, &loop_read_info, stride_in_bytes, args[ARG_swap].u_bool); + + if (mp_hal_is_interrupted()) { + return mp_const_none; + } + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(rp2pio_statemachine_background_read_obj, 1, rp2pio_statemachine_background_read); + +//| def stop_background_read(self) -> None: +//| """Immediately stop a background read, if one is in progress. Any +//| DMA in progress is halted, but items already in the RX FIFO are not +//| affected.""" +static mp_obj_t rp2pio_statemachine_obj_stop_background_read(mp_obj_t self_in) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + bool ok = common_hal_rp2pio_statemachine_stop_background_read(self); + if (mp_hal_is_interrupted()) { + return mp_const_none; + } + if (!ok) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_stop_background_read_obj, rp2pio_statemachine_obj_stop_background_read); + +//| reading: bool +//| """Returns True if a background read is in progress""" +static mp_obj_t rp2pio_statemachine_obj_get_reading(mp_obj_t self_in) { + rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_rp2pio_statemachine_get_reading(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_reading_obj, rp2pio_statemachine_obj_get_reading); + +const mp_obj_property_t rp2pio_statemachine_reading_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&rp2pio_statemachine_get_reading_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| pending_read: int +//| """Returns the number of pending buffers for background reading. +//| +//| If the number is 0, then a `StateMachine.background_read` call will not block.""" +static mp_obj_t rp2pio_statemachine_obj_get_pending_read(mp_obj_t self_in) { rp2pio_statemachine_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(common_hal_rp2pio_statemachine_get_pending(self)); + return mp_obj_new_int(common_hal_rp2pio_statemachine_get_pending_read(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_pending_obj, rp2pio_statemachine_obj_get_pending); +MP_DEFINE_CONST_FUN_OBJ_1(rp2pio_statemachine_get_pending_read_obj, rp2pio_statemachine_obj_get_pending_read); -const mp_obj_property_t rp2pio_statemachine_pending_obj = { +const mp_obj_property_t rp2pio_statemachine_pending_read_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&rp2pio_statemachine_get_pending_obj, + .proxy = {(mp_obj_t)&rp2pio_statemachine_get_pending_read_obj, MP_ROM_NONE, MP_ROM_NONE}, }; + +// ================================================================================================================================= + //| def readinto( //| self, //| buffer: WriteableBuffer, @@ -952,10 +1096,17 @@ static const mp_rom_map_elem_t rp2pio_statemachine_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&rp2pio_statemachine_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&rp2pio_statemachine_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&rp2pio_statemachine_write_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_background_write), MP_ROM_PTR(&rp2pio_statemachine_background_write_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_background_write), MP_ROM_PTR(&rp2pio_statemachine_stop_background_write_obj) }, { MP_ROM_QSTR(MP_QSTR_writing), MP_ROM_PTR(&rp2pio_statemachine_writing_obj) }, - { MP_ROM_QSTR(MP_QSTR_pending), MP_ROM_PTR(&rp2pio_statemachine_pending_obj) }, + { MP_ROM_QSTR(MP_QSTR_pending), MP_ROM_PTR(&rp2pio_statemachine_pending_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_pending_write), MP_ROM_PTR(&rp2pio_statemachine_pending_write_obj) }, + + { MP_ROM_QSTR(MP_QSTR_background_read), MP_ROM_PTR(&rp2pio_statemachine_background_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_background_read), MP_ROM_PTR(&rp2pio_statemachine_stop_background_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_reading), MP_ROM_PTR(&rp2pio_statemachine_reading_obj) }, + { MP_ROM_QSTR(MP_QSTR_pending_read), MP_ROM_PTR(&rp2pio_statemachine_pending_read_obj) }, { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&rp2pio_statemachine_frequency_obj) }, { MP_ROM_QSTR(MP_QSTR_rxstall), MP_ROM_PTR(&rp2pio_statemachine_rxstall_obj) }, diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index 41cceb3063574..2d52b4cfb2ee8 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -33,7 +33,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool wait_for_txstall, bool auto_push, uint8_t push_threshold, bool in_shift_right, bool user_interruptible, - int wrap_taget, int wrap, + int wrap_target, int wrap, int offset, int fifo_type, int mov_status_type, @@ -50,10 +50,19 @@ void common_hal_rp2pio_statemachine_run(rp2pio_statemachine_obj_t *self, const u // Lengths are in bytes. bool common_hal_rp2pio_statemachine_write(rp2pio_statemachine_obj_t *self, const uint8_t *data, size_t len, uint8_t stride_in_bytes, bool swap); -bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once_obj, const sm_buf_info *loop_obj, uint8_t stride_in_bytes, bool swap); + +bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once, const sm_buf_info *loop, uint8_t stride_in_bytes, bool swap); +bool common_hal_rp2pio_statemachine_background_read(rp2pio_statemachine_obj_t *self, const sm_buf_info *once_read, const sm_buf_info *loop_read, uint8_t stride_in_bytes, bool swap); + bool common_hal_rp2pio_statemachine_stop_background_write(rp2pio_statemachine_obj_t *self); -mp_int_t common_hal_rp2pio_statemachine_get_pending(rp2pio_statemachine_obj_t *self); +bool common_hal_rp2pio_statemachine_stop_background_read(rp2pio_statemachine_obj_t *self); + +mp_int_t common_hal_rp2pio_statemachine_get_pending_write(rp2pio_statemachine_obj_t *self); +mp_int_t common_hal_rp2pio_statemachine_get_pending_read(rp2pio_statemachine_obj_t *self); + bool common_hal_rp2pio_statemachine_get_writing(rp2pio_statemachine_obj_t *self); +bool common_hal_rp2pio_statemachine_get_reading(rp2pio_statemachine_obj_t *self); + bool common_hal_rp2pio_statemachine_readinto(rp2pio_statemachine_obj_t *self, uint8_t *data, size_t len, uint8_t stride_in_bytes, bool swap); bool common_hal_rp2pio_statemachine_write_readinto(rp2pio_statemachine_obj_t *self, const uint8_t *data_out, size_t out_len, uint8_t out_stride_in_bytes, @@ -65,6 +74,7 @@ void common_hal_rp2pio_statemachine_set_frequency(rp2pio_statemachine_obj_t *sel bool common_hal_rp2pio_statemachine_get_rxstall(rp2pio_statemachine_obj_t *self); void common_hal_rp2pio_statemachine_clear_rxfifo(rp2pio_statemachine_obj_t *self); + bool common_hal_rp2pio_statemachine_get_txstall(rp2pio_statemachine_obj_t *self); void common_hal_rp2pio_statemachine_clear_txstall(rp2pio_statemachine_obj_t *self); size_t common_hal_rp2pio_statemachine_get_in_waiting(rp2pio_statemachine_obj_t *self); diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index bffc9400b34c9..0bb9e14539edd 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -37,12 +37,19 @@ static bool _never_reset[NUM_PIOS][NUM_PIO_STATE_MACHINES]; static uint32_t _current_pins[NUM_PIOS]; static uint32_t _current_sm_pins[NUM_PIOS][NUM_PIO_STATE_MACHINES]; -static int8_t _sm_dma_plus_one[NUM_PIOS][NUM_PIO_STATE_MACHINES]; -#define SM_DMA_ALLOCATED(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] != 0) -#define SM_DMA_GET_CHANNEL(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] - 1) -#define SM_DMA_CLEAR_CHANNEL(pio_index, sm) (_sm_dma_plus_one[(pio_index)][(sm)] = 0) -#define SM_DMA_SET_CHANNEL(pio_index, sm, channel) (_sm_dma_plus_one[(pio_index)][(sm)] = (channel) + 1) +static int8_t _sm_dma_plus_one_write[NUM_PIOS][NUM_PIO_STATE_MACHINES]; +static int8_t _sm_dma_plus_one_read[NUM_PIOS][NUM_PIO_STATE_MACHINES]; + +#define SM_DMA_ALLOCATED_WRITE(pio_index, sm) (_sm_dma_plus_one_write[(pio_index)][(sm)] != 0) +#define SM_DMA_GET_CHANNEL_WRITE(pio_index, sm) (_sm_dma_plus_one_write[(pio_index)][(sm)] - 1) +#define SM_DMA_CLEAR_CHANNEL_WRITE(pio_index, sm) (_sm_dma_plus_one_write[(pio_index)][(sm)] = 0) +#define SM_DMA_SET_CHANNEL_WRITE(pio_index, sm, channel) (_sm_dma_plus_one_write[(pio_index)][(sm)] = (channel) + 1) + +#define SM_DMA_ALLOCATED_READ(pio_index, sm) (_sm_dma_plus_one_read[(pio_index)][(sm)] != 0) +#define SM_DMA_GET_CHANNEL_READ(pio_index, sm) (_sm_dma_plus_one_read[(pio_index)][(sm)] - 1) +#define SM_DMA_CLEAR_CHANNEL_READ(pio_index, sm) (_sm_dma_plus_one_read[(pio_index)][(sm)] = 0) +#define SM_DMA_SET_CHANNEL_READ(pio_index, sm, channel) (_sm_dma_plus_one_read[(pio_index)][(sm)] = (channel) + 1) static PIO pio_instances[NUM_PIOS] = { pio0, @@ -68,24 +75,40 @@ static void rp2pio_statemachine_set_pull(uint32_t pull_pin_up, uint32_t pull_pin } } -static void rp2pio_statemachine_clear_dma(int pio_index, int sm) { - if (SM_DMA_ALLOCATED(pio_index, sm)) { - int channel = SM_DMA_GET_CHANNEL(pio_index, sm); - uint32_t channel_mask = 1u << channel; - dma_hw->inte0 &= ~channel_mask; +static void rp2pio_statemachine_clear_dma_write(int pio_index, int sm) { + if (SM_DMA_ALLOCATED_WRITE(pio_index, sm)) { + int channel_write = SM_DMA_GET_CHANNEL_WRITE(pio_index, sm); + uint32_t channel_mask_write = 1u << channel_write; + dma_hw->inte0 &= ~channel_mask_write; if (!dma_hw->inte0) { irq_set_mask_enabled(1 << DMA_IRQ_0, false); } - MP_STATE_PORT(background_pio)[channel] = NULL; - dma_channel_abort(channel); - dma_channel_unclaim(channel); + MP_STATE_PORT(background_pio)[channel_write] = NULL; + dma_channel_abort(channel_write); + dma_channel_unclaim(channel_write); } - SM_DMA_CLEAR_CHANNEL(pio_index, sm); + SM_DMA_CLEAR_CHANNEL_WRITE(pio_index, sm); +} + +static void rp2pio_statemachine_clear_dma_read(int pio_index, int sm) { + if (SM_DMA_ALLOCATED_READ(pio_index, sm)) { + int channel_read = SM_DMA_GET_CHANNEL_READ(pio_index, sm); + uint32_t channel_mask_read = 1u << channel_read; + dma_hw->inte0 &= ~channel_mask_read; + if (!dma_hw->inte0) { + irq_set_mask_enabled(1 << DMA_IRQ_0, false); + } + MP_STATE_PORT(background_pio)[channel_read] = NULL; + dma_channel_abort(channel_read); + dma_channel_unclaim(channel_read); + } + SM_DMA_CLEAR_CHANNEL_READ(pio_index, sm); } static void _reset_statemachine(PIO pio, uint8_t sm, bool leave_pins) { uint8_t pio_index = pio_get_index(pio); - rp2pio_statemachine_clear_dma(pio_index, sm); + rp2pio_statemachine_clear_dma_write(pio_index, sm); + rp2pio_statemachine_clear_dma_read(pio_index, sm); uint32_t program_id = _current_program_id[pio_index][sm]; if (program_id == 0) { return; @@ -420,7 +443,8 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, self->sm_config = c; // no DMA allocated - SM_DMA_CLEAR_CHANNEL(pio_index, state_machine); + SM_DMA_CLEAR_CHANNEL_READ(pio_index, state_machine); + SM_DMA_CLEAR_CHANNEL_WRITE(pio_index, state_machine); pio_sm_init(self->pio, self->state_machine, program_offset, &c); common_hal_rp2pio_statemachine_run(self, init, init_len); @@ -1023,16 +1047,16 @@ uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self) { return _current_program_offset[pio_index][sm]; } -bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once, const sm_buf_info *loop, uint8_t stride_in_bytes, bool swap) { +bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t *self, const sm_buf_info *once_write, const sm_buf_info *loop_write, uint8_t stride_in_bytes, bool swap) { uint8_t pio_index = pio_get_index(self->pio); uint8_t sm = self->state_machine; - int pending_buffers = (once->info.len != 0) + (loop->info.len != 0); - if (!once->info.len) { - once = loop; + int pending_buffers_write = (once_write->info.len != 0) + (loop_write->info.len != 0); + if (!once_write->info.len) { + once_write = loop_write; } - if (SM_DMA_ALLOCATED(pio_index, sm)) { + if (SM_DMA_ALLOCATED_WRITE(pio_index, sm)) { if (stride_in_bytes != self->background_stride_in_bytes) { mp_raise_ValueError(MP_ERROR_TEXT("Mismatched data size")); } @@ -1040,7 +1064,7 @@ bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t * mp_raise_ValueError(MP_ERROR_TEXT("Mismatched swap flag")); } - while (self->pending_buffers) { + while (self->pending_buffers_write) { RUN_BACKGROUND_TASKS; if (self->user_interruptible && mp_hal_is_interrupted()) { return false; @@ -1048,13 +1072,13 @@ bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t * } common_hal_mcu_disable_interrupts(); - self->once = *once; - self->loop = *loop; - self->pending_buffers = pending_buffers; + self->once_write = *once_write; + self->loop_write = *loop_write; + self->pending_buffers_write = pending_buffers_write; - if (self->dma_completed && self->once.info.len) { - rp2pio_statemachine_dma_complete(self, SM_DMA_GET_CHANNEL(pio_index, sm)); - self->dma_completed = false; + if (self->dma_completed_write && self->once_write.info.len) { + rp2pio_statemachine_dma_complete_write(self, SM_DMA_GET_CHANNEL_WRITE(pio_index, sm)); + self->dma_completed_write = false; } common_hal_mcu_enable_interrupts(); @@ -1062,82 +1086,202 @@ bool common_hal_rp2pio_statemachine_background_write(rp2pio_statemachine_obj_t * return true; } - int channel = dma_claim_unused_channel(false); - if (channel == -1) { + int channel_write = dma_claim_unused_channel(false); + if (channel_write == -1) { return false; } - SM_DMA_SET_CHANNEL(pio_index, sm, channel); + SM_DMA_SET_CHANNEL_WRITE(pio_index, sm, channel_write); volatile uint8_t *tx_destination = (volatile uint8_t *)&self->pio->txf[self->state_machine]; self->tx_dreq = pio_get_dreq(self->pio, self->state_machine, true); - dma_channel_config c; + dma_channel_config c_write; - self->current = *once; - self->once = *loop; - self->loop = *loop; - self->pending_buffers = pending_buffers; - self->dma_completed = false; + self->current_write = *once_write; + self->once_write = *loop_write; + self->loop_write = *loop_write; + self->pending_buffers_write = pending_buffers_write; + self->dma_completed_write = false; self->background_stride_in_bytes = stride_in_bytes; self->byteswap = swap; - c = dma_channel_get_default_config(channel); - channel_config_set_transfer_data_size(&c, _stride_to_dma_size(stride_in_bytes)); - channel_config_set_dreq(&c, self->tx_dreq); - channel_config_set_read_increment(&c, true); - channel_config_set_write_increment(&c, false); - channel_config_set_bswap(&c, swap); - dma_channel_configure(channel, &c, + c_write = dma_channel_get_default_config(channel_write); + channel_config_set_transfer_data_size(&c_write, _stride_to_dma_size(stride_in_bytes)); + channel_config_set_dreq(&c_write, self->tx_dreq); + channel_config_set_read_increment(&c_write, true); + channel_config_set_write_increment(&c_write, false); + channel_config_set_bswap(&c_write, swap); + dma_channel_configure(channel_write, &c_write, tx_destination, - once->info.buf, - once->info.len / stride_in_bytes, + once_write->info.buf, + once_write->info.len / stride_in_bytes, false); common_hal_mcu_disable_interrupts(); - MP_STATE_PORT(background_pio)[channel] = self; - dma_hw->inte0 |= 1u << channel; + MP_STATE_PORT(background_pio)[channel_write] = self; + dma_hw->inte0 |= 1u << channel_write; irq_set_mask_enabled(1 << DMA_IRQ_0, true); - dma_start_channel_mask(1u << channel); + dma_start_channel_mask(1u << channel_write); common_hal_mcu_enable_interrupts(); return true; } -void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel) { - self->current = self->once; - self->once = self->loop; +void rp2pio_statemachine_dma_complete_write(rp2pio_statemachine_obj_t *self, int channel_write) { + self->current_write = self->once_write; + self->once_write = self->loop_write; - if (self->current.info.buf) { - if (self->pending_buffers > 0) { - self->pending_buffers--; + if (self->current_write.info.buf) { + if (self->pending_buffers_write > 0) { + self->pending_buffers_write--; } - dma_channel_set_read_addr(channel, self->current.info.buf, false); - dma_channel_set_trans_count(channel, self->current.info.len / self->background_stride_in_bytes, true); + dma_channel_set_read_addr(channel_write, self->current_write.info.buf, false); + dma_channel_set_trans_count(channel_write, self->current_write.info.len / self->background_stride_in_bytes, true); } else { - self->dma_completed = true; - self->pending_buffers = 0; // should be a no-op + self->dma_completed_write = true; + self->pending_buffers_write = 0; // should be a no-op } } bool common_hal_rp2pio_statemachine_stop_background_write(rp2pio_statemachine_obj_t *self) { uint8_t pio_index = pio_get_index(self->pio); uint8_t sm = self->state_machine; - rp2pio_statemachine_clear_dma(pio_index, sm); - memset(&self->current, 0, sizeof(self->current)); - memset(&self->once, 0, sizeof(self->once)); - memset(&self->loop, 0, sizeof(self->loop)); - self->pending_buffers = 0; + rp2pio_statemachine_clear_dma_write(pio_index, sm); + memset(&self->current_write, 0, sizeof(self->current_write)); + memset(&self->once_write, 0, sizeof(self->once_write)); + memset(&self->loop_write, 0, sizeof(self->loop_write)); + self->pending_buffers_write = 0; return true; } bool common_hal_rp2pio_statemachine_get_writing(rp2pio_statemachine_obj_t *self) { - return !self->dma_completed; + return !self->dma_completed_write; +} + +int common_hal_rp2pio_statemachine_get_pending_write(rp2pio_statemachine_obj_t *self) { + return self->pending_buffers_write; +} + +// ================================================================================= + +bool common_hal_rp2pio_statemachine_background_read(rp2pio_statemachine_obj_t *self, + const sm_buf_info *once_read, const sm_buf_info *loop_read, uint8_t stride_in_bytes, bool swap) { + + uint8_t pio_index = pio_get_index(self->pio); + uint8_t sm = self->state_machine; + + int pending_buffers_read = (once_read->info.len != 0) + (loop_read->info.len != 0); + if (!once_read->info.len) { + once_read = loop_read; + } + + if (SM_DMA_ALLOCATED_READ(pio_index, sm)) { + if (stride_in_bytes != self->background_stride_in_bytes) { + mp_raise_ValueError(MP_ERROR_TEXT("Mismatched data size")); + } + if (swap != self->byteswap) { + mp_raise_ValueError(MP_ERROR_TEXT("Mismatched swap flag")); + } + + while (self->pending_buffers_read) { + RUN_BACKGROUND_TASKS; + if (self->user_interruptible && mp_hal_is_interrupted()) { + return false; + } + } + + common_hal_mcu_disable_interrupts(); + self->once_read = *once_read; + self->loop_read = *loop_read; + self->pending_buffers_read = pending_buffers_read; + + if (self->dma_completed_read && self->once_read.info.len) { + rp2pio_statemachine_dma_complete_read(self, SM_DMA_GET_CHANNEL_READ(pio_index, sm)); + self->dma_completed_read = false; + } + + common_hal_mcu_enable_interrupts(); + + return true; + } + + int channel_read = dma_claim_unused_channel(false); + if (channel_read == -1) { + return false; + } + SM_DMA_SET_CHANNEL_READ(pio_index, sm, channel_read); + + // set up receive DMA + + volatile uint8_t *rx_source = (volatile uint8_t *)&self->pio->rxf[self->state_machine]; + + self->rx_dreq = pio_get_dreq(self->pio, self->state_machine, false); + + dma_channel_config c_read; + + self->current_read = *once_read; + self->once_read = *loop_read; + self->loop_read = *loop_read; + self->pending_buffers_read = pending_buffers_read; + self->dma_completed_read = false; + + c_read = dma_channel_get_default_config(channel_read); + channel_config_set_transfer_data_size(&c_read, _stride_to_dma_size(stride_in_bytes)); + channel_config_set_dreq(&c_read, self->rx_dreq); + channel_config_set_read_increment(&c_read, false); + channel_config_set_write_increment(&c_read, true); + channel_config_set_bswap(&c_read, swap); + dma_channel_configure(channel_read, &c_read, + once_read->info.buf, + rx_source, + once_read->info.len / stride_in_bytes, + false); + + common_hal_mcu_disable_interrupts(); + MP_STATE_PORT(background_pio)[channel_read] = self; + dma_hw->inte1 |= 1u << channel_read; + irq_set_mask_enabled(1 << DMA_IRQ_1, true); + dma_start_channel_mask((1u << channel_read)); + common_hal_mcu_enable_interrupts(); + + return true; +} + +void rp2pio_statemachine_dma_complete_read(rp2pio_statemachine_obj_t *self, int channel_read) { + self->current_read = self->once_read; + self->once_read = self->loop_read; + + if (self->current_read.info.buf) { + if (self->pending_buffers_read > 0) { + self->pending_buffers_read--; + } + dma_channel_set_write_addr(channel_read, self->current_read.info.buf, false); + dma_channel_set_trans_count(channel_read, self->current_read.info.len / self->background_stride_in_bytes, true); + } else { + self->dma_completed_read = true; + self->pending_buffers_read = 0; // should be a no-op + } +} + +bool common_hal_rp2pio_statemachine_stop_background_read(rp2pio_statemachine_obj_t *self) { + uint8_t pio_index = pio_get_index(self->pio); + uint8_t sm = self->state_machine; + rp2pio_statemachine_clear_dma_read(pio_index, sm); + memset(&self->current_read, 0, sizeof(self->current_read)); + memset(&self->once_read, 0, sizeof(self->once_read)); + memset(&self->loop_read, 0, sizeof(self->loop_read)); + self->pending_buffers_read = 0; + return true; +} + +bool common_hal_rp2pio_statemachine_get_reading(rp2pio_statemachine_obj_t *self) { + return !self->dma_completed_read; } -int common_hal_rp2pio_statemachine_get_pending(rp2pio_statemachine_obj_t *self) { - return self->pending_buffers; +int common_hal_rp2pio_statemachine_get_pending_read(rp2pio_statemachine_obj_t *self) { + return self->pending_buffers_read; } int common_hal_rp2pio_statemachine_get_offset(rp2pio_statemachine_obj_t *self) { diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h index 16946e4f7281a..9181f59601fd3 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h @@ -47,10 +47,13 @@ typedef struct { uint8_t fifo_depth; // Either 4 if FIFOs are not joined, or 8 if they are. // dma-related items - volatile int pending_buffers; - sm_buf_info current, once, loop; + volatile int pending_buffers_write; + volatile int pending_buffers_read; + sm_buf_info current_write, once_write, loop_write; + sm_buf_info current_read, once_read, loop_read; int background_stride_in_bytes; - bool dma_completed, byteswap; + bool dma_completed_write, byteswap; + bool dma_completed_read; #if PICO_PIO_VERSION > 0 memorymap_addressrange_obj_t rxfifo_obj; #endif @@ -85,7 +88,8 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self); void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins); -void rp2pio_statemachine_dma_complete(rp2pio_statemachine_obj_t *self, int channel); +void rp2pio_statemachine_dma_complete_write(rp2pio_statemachine_obj_t *self, int channel); +void rp2pio_statemachine_dma_complete_read(rp2pio_statemachine_obj_t *self, int channel); void rp2pio_statemachine_reset_ok(PIO pio, int sm); void rp2pio_statemachine_never_reset(PIO pio, int sm);