diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 87deaf7e5f..3ece26bd41 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -68,6 +68,11 @@ typedef struct _rp2_state_machine_irq_obj_t { STATIC const rp2_state_machine_obj_t rp2_state_machine_obj[8]; STATIC uint8_t rp2_state_machine_initial_pc[8]; +// These masks keep track of PIO instruction memory used by this module. +STATIC uint32_t rp2_pio_instruction_memory_usage_mask[2]; + +STATIC const rp2_state_machine_obj_t *rp2_state_machine_get_object(mp_int_t sm_id); +STATIC void rp2_state_machine_reset_all(void); STATIC mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); STATIC void pio_irq0(PIO pio) { @@ -101,16 +106,42 @@ STATIC void pio1_irq0(void) { pio_irq0(pio1); } -void rp2_pio_init(void) { - // Reset all PIO instruction memory. - #if MICROPY_PY_NETWORK_CYW43 - // TODO: cannot reset PIO memory when CYW43 driver is enabled and active - // because it uses a PIO for the bus interface. - #else - pio_clear_instruction_memory(pio0); - pio_clear_instruction_memory(pio1); - #endif +// Calls pio_add_program() and keeps track of used instruction memory. +STATIC uint rp2_pio_add_managed_program(PIO pio, struct pio_program *pio_program) { + uint offset = pio_add_program(pio, pio_program); + uint32_t mask = ((1 << pio_program->length) - 1) << offset; + rp2_pio_instruction_memory_usage_mask[pio_get_index(pio)] |= mask; + return offset; +} +// Calls pio_remove_program() and keeps track of used instruction memory. +STATIC void rp2_pio_remove_managed_program(PIO pio, struct pio_program *pio_program, uint offset) { + pio_remove_program(pio, pio_program, offset); + uint32_t mask = ((1 << pio_program->length) - 1) << offset; + rp2_pio_instruction_memory_usage_mask[pio_get_index(pio)] &= ~mask; +} + +// Calls pio_remove_program() for all programs registered with rp2_pio_add_managed_program(), +// that weren't already removed via rp2_pio_remove_managed_program(). +STATIC void rp2_pio_remove_all_managed_programs(PIO pio) { + uint32_t mask = rp2_pio_instruction_memory_usage_mask[pio_get_index(pio)]; + for (size_t i = 0; i < 32; ++i) { + if (mask & (1 << i)) { + size_t j; + for (j = i + 1; j < 32; ++j) { + if (!(mask & (1 << j))) { + break; + } + } + struct pio_program pio_program = { NULL, j - i, -1 }; + pio_remove_program(pio, &pio_program, i); + i = j; // i indexes an unused bit, or is 32 + } + } + rp2_pio_instruction_memory_usage_mask[pio_get_index(pio)] = 0; +} + +void rp2_pio_init(void) { // Set up interrupts. memset(MP_STATE_PORT(rp2_pio_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_pio_irq_obj))); memset(MP_STATE_PORT(rp2_state_machine_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_state_machine_irq_obj))); @@ -123,6 +154,14 @@ void rp2_pio_deinit(void) { irq_set_mask_enabled((1u << PIO0_IRQ_0) | (1u << PIO0_IRQ_1), false); irq_remove_handler(PIO0_IRQ_0, pio0_irq0); irq_remove_handler(PIO1_IRQ_0, pio1_irq0); + + rp2_state_machine_reset_all(); + + // Reset all PIO instruction memory allocated by this module. + // Note: other subsystems (eg the CYW43 driver) may use the PIO, + // and their PIO programs should remain intact. + rp2_pio_remove_all_managed_programs(pio0); + rp2_pio_remove_all_managed_programs(pio1); } /******************************************************************************/ @@ -238,7 +277,7 @@ STATIC mp_obj_t rp2_pio_add_program(mp_obj_t self_in, mp_obj_t prog_in) { if (!pio_can_add_program(self->pio, &pio_program)) { mp_raise_OSError(MP_ENOMEM); } - uint offset = pio_add_program(self->pio, &pio_program); + uint offset = rp2_pio_add_managed_program(self->pio, &pio_program); // Store the program offset in the program object. prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)] = MP_OBJ_NEW_SMALL_INT(offset); @@ -272,7 +311,7 @@ STATIC mp_obj_t rp2_pio_remove_program(size_t n_args, const mp_obj_t *args) { // Remove the program from the instruction memory. struct pio_program pio_program = { NULL, length, -1 }; - pio_remove_program(self->pio, &pio_program, offset); + rp2_pio_remove_managed_program(self->pio, &pio_program, offset); return mp_const_none; } @@ -285,11 +324,11 @@ STATIC mp_obj_t rp2_pio_state_machine(size_t n_args, const mp_obj_t *pos_args, m // Get and verify the state machine id. mp_int_t sm_id = mp_obj_get_int(pos_args[1]); if (!(0 <= sm_id && sm_id < 4)) { - mp_raise_ValueError("invalide state machine"); + mp_raise_ValueError("invalid StateMachine"); } // Return the correct StateMachine object. - const rp2_state_machine_obj_t *sm = &rp2_state_machine_obj[(self->pio == pio0 ? 0 : 4) + sm_id]; + const rp2_state_machine_obj_t *sm = rp2_state_machine_get_object((self->pio == pio0 ? 0 : 4) + sm_id); if (n_args > 2 || kw_args->used > 0) { // Configuration arguments given so init this StateMachine. @@ -414,6 +453,9 @@ STATIC const mp_irq_methods_t rp2_pio_irq_methods = { /******************************************************************************/ // StateMachine object +// This mask keeps track of state machines claimed by this module. +STATIC uint32_t rp2_state_machine_claimed_mask; + STATIC const mp_irq_methods_t rp2_state_machine_irq_methods; STATIC const rp2_state_machine_obj_t rp2_state_machine_obj[] = { @@ -427,6 +469,35 @@ STATIC const rp2_state_machine_obj_t rp2_state_machine_obj[] = { { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 3, 7 }, }; +STATIC const rp2_state_machine_obj_t *rp2_state_machine_get_object(mp_int_t sm_id) { + if (!(0 <= sm_id && sm_id < MP_ARRAY_SIZE(rp2_state_machine_obj))) { + mp_raise_ValueError("invalid StateMachine"); + } + + const rp2_state_machine_obj_t *sm_obj = &rp2_state_machine_obj[sm_id]; + + if (!(rp2_state_machine_claimed_mask & (1 << sm_id))) { + if (pio_sm_is_claimed(sm_obj->pio, sm_obj->sm)) { + mp_raise_ValueError("StateMachine claimed by external resource"); + } + pio_sm_claim(sm_obj->pio, sm_obj->sm); + rp2_state_machine_claimed_mask |= 1 << sm_id; + } + + return sm_obj; +} + +STATIC void rp2_state_machine_reset_all(void) { + for (size_t i = 0; i < MP_ARRAY_SIZE(rp2_state_machine_obj); ++i) { + if (rp2_state_machine_claimed_mask & (1 << i)) { + const rp2_state_machine_obj_t *sm_obj = &rp2_state_machine_obj[i]; + pio_sm_unclaim(sm_obj->pio, sm_obj->sm); + pio_sm_set_enabled(sm_obj->pio, sm_obj->sm, false); + } + } + rp2_state_machine_claimed_mask = 0; +} + STATIC void rp2_state_machine_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "StateMachine(%u)", self->id); @@ -573,10 +644,7 @@ STATIC mp_obj_t rp2_state_machine_make_new(const mp_obj_type_t *type, size_t n_a // Get the StateMachine object. mp_int_t sm_id = mp_obj_get_int(args[0]); - if (!(0 <= sm_id && sm_id < MP_ARRAY_SIZE(rp2_state_machine_obj))) { - mp_raise_ValueError("invalid StateMachine"); - } - const rp2_state_machine_obj_t *self = &rp2_state_machine_obj[sm_id]; + const rp2_state_machine_obj_t *self = rp2_state_machine_get_object(sm_id); if (n_args > 1 || n_kw > 0) { // Configuration arguments given so init this StateMachine.