rp2/rp2_pio: Track use of PIO resources and free them on soft reset.

Prior to this commit, on Pico W (where the CYW43 driver is enabled) the PIO
instruction memory was not released on soft reset, so using PIO after a
soft reset would eventually (after a few soft resets) lead to ENOMEM when
allocating a PIO program.

This commit fixes that by tracking the use of PIO memory by this module and
freeing it on soft reset.

Similarly, use of the state machines themselves are tracked and released on
soft reset.

Fixes issue #9003.

Signed-off-by: Damien George <damien@micropython.org>
pull/10652/head
Damien George 2023-02-03 18:04:56 +11:00
rodzic 9ea64a36ac
commit 0afe60b876
1 zmienionych plików z 85 dodań i 17 usunięć

Wyświetl plik

@ -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.