fix PIO gpio compatibility checks, docs and simplify/optimize implementation (#2989)
Co-authored-by: Andrew Scheller <lurch@durge.org>
Co-authored-by: will-v-pi <108662275+will-v-pi@users.noreply.github.com>
diff --git a/src/common/pico_base_headers/include/pico/assert.h b/src/common/pico_base_headers/include/pico/assert.h
index cc33779..a9af542 100644
--- a/src/common/pico_base_headers/include/pico/assert.h
+++ b/src/common/pico_base_headers/include/pico/assert.h
@@ -34,7 +34,7 @@
#define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));})
#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);})
#define hard_assert_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) hard_assert(!(test));})
-#define invalid_params_if_and_return(x, test, rc) ({/*if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test)); */ if (test) return rc; })
+#define invalid_params_if_and_return(x, test, rc) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test)); if (test) return rc; })
#ifdef NDEBUG
extern void hard_assertion_failure(void);
diff --git a/src/rp2_common/hardware_pio/include/hardware/pio.h b/src/rp2_common/hardware_pio/include/hardware/pio.h
index 9d3d6ba..a67a014 100644
--- a/src/rp2_common/hardware_pio/include/hardware/pio.h
+++ b/src/rp2_common/hardware_pio/include/hardware/pio.h
@@ -837,8 +837,8 @@
// configs that use pins 32-47
uint32_t gpio_over_32 = (config->pinhi >> 1) & used; // checks if bit 1 of any field is 1
uint gpio_base = pio_get_gpio_base(pio);
- invalid_params_if_and_return(PIO, gpio_under_16 && gpio_base, PICO_ERROR_BAD_ALIGNMENT);
- invalid_params_if_and_return(PIO, gpio_over_32 && !gpio_base, PICO_ERROR_BAD_ALIGNMENT);
+ invalid_params_if_and_return(HARDWARE_PIO, gpio_under_16 && gpio_base, PICO_ERROR_BAD_ALIGNMENT);
+ invalid_params_if_and_return(HARDWARE_PIO, gpio_over_32 && !gpio_base, PICO_ERROR_BAD_ALIGNMENT);
// flip the top bit of any used (execctrl/pinctrl) values to turn:
// bit6(32) + 0-15 -> base(16) + 16-31
// bit6(0) + 16-31 -> base(16) + 0-15
@@ -2027,13 +2027,13 @@
* \ingroup hardware_pio
*
* \param program PIO program to add
- * \param pio Returns the PIO hardware instance or NULL if no PIO is available
- * \param sm Returns the index of the PIO state machine that was claimed
- * \param offset Returns the instruction memory offset of the start of the program
+ * \param pio_out Returns the PIO hardware instance or NULL if no PIO is available
+ * \param sm_out Returns the index of the PIO state machine that was claimed
+ * \param offset_out Returns the instruction memory offset of the start of the program
* \return true on success, false otherwise
* \see pio_remove_program_and_unclaim_sm
*/
-bool pio_claim_free_sm_and_add_program(const pio_program_t *program, PIO *pio, uint *sm, uint *offset);
+bool pio_claim_free_sm_and_add_program(const pio_program_t *program, PIO *pio_out, uint *sm_out, uint *offset_out);
/*! \brief Finds a PIO and statemachine and adds a program into PIO memory
* \ingroup hardware_pio
@@ -2046,18 +2046,22 @@
* and optionally will set the GPIO base (see \ref pio_set_gpio_base) of an unused PIO instance if necessary
*
* \param program PIO program to add
- * \param pio Returns the PIO hardware instance or NULL if no PIO is available
- * \param sm Returns the index of the PIO state machine that was claimed
- * \param offset Returns the instruction memory offset of the start of the program
+ * \param pio_out Returns the PIO hardware instance or NULL if no PIO is available
+ * \param sm_out Returns the index of the PIO state machine that was claimed
+ * \param offset_out Returns the instruction memory offset of the start of the program
* \param gpio_start the lowest GPIO number required (0-47 on RP2350B, 0-31 otherwise)
* \param gpio_count the count of consecutive GPIOs required
* \param set_gpio_base if there is no free SM on a PIO instance with the right GPIO base, and there IS an unused PIO
- * instance, then that PIO will be reconfigured so that this method can succeed
+ * instance, then that PIO will be reconfigured so that this method can succeed. Note
+ * this parameter is ignored when PICO_PIO_USE_GPIO_BASE=0; i.e. by default on anything other than RP2350B
*
* \return true on success, false otherwise
* \see pio_remove_program_and_unclaim_sm
+ *
+ * \note on RP2040 or RP2350A (strictly when PICO_PIO_USE_GPIO_BASE == 0), gpio_start + gpio_count must be <= 32
+ * for success, and the set_gpio_base parameter is ignored
*/
-bool pio_claim_free_sm_and_add_program_for_gpio_range(const pio_program_t *program, PIO *pio, uint *sm, uint *offset, uint gpio_start, uint gpio_count, bool set_gpio_base);
+bool pio_claim_free_sm_and_add_program_for_gpio_range(const pio_program_t *program, PIO *pio_out, uint *sm_out, uint *offset_out, uint gpio_start, uint gpio_count, bool set_gpio_base);
/*! \brief Removes a program from PIO memory and unclaims the state machine
* \ingroup hardware_pio
diff --git a/src/rp2_common/hardware_pio/pio.c b/src/rp2_common/hardware_pio/pio.c
index 95d607d..976d7e5 100644
--- a/src/rp2_common/hardware_pio/pio.c
+++ b/src/rp2_common/hardware_pio/pio.c
@@ -51,7 +51,7 @@
uint base = which * NUM_PIO_STATE_MACHINES;
int index = hw_claim_unused_from_range((uint8_t*)&claimed[0], required, base,
base + NUM_PIO_STATE_MACHINES - 1, "No PIO state machines are available");
- return index >= (int)base ? index - (int)base : -1;
+ return index >= (int)base ? index - (int)base : PICO_ERROR_GENERIC;
}
bool pio_sm_is_claimed(PIO pio, uint sm) {
@@ -81,18 +81,15 @@
}
}
+#if PICO_PIO_USE_GPIO_BASE
static int pio_set_gpio_base_unsafe(PIO pio, uint gpio_base) {
invalid_params_if_and_return(HARDWARE_PIO, gpio_base != 0 && (!PICO_PIO_VERSION || gpio_base != 16), PICO_ERROR_BAD_ALIGNMENT);
-#if PICO_PIO_USE_GPIO_BASE
uint32_t used_mask = _used_instruction_space[pio_get_index(pio)];
invalid_params_if_and_return(HARDWARE_PIO, used_mask, PICO_ERROR_INVALID_STATE);
pio->gpiobase = gpio_base;
-#else
- ((void)pio);
- ((void)gpio_base);
-#endif
return PICO_OK;
}
+#endif
int pio_set_gpio_base(PIO pio, uint gpio_base) {
int rc = PICO_OK;
@@ -107,22 +104,19 @@
return rc;
}
+#if PICO_PIO_VERSION > 0 || PICO_PIO_USE_GPIO_BASE
static bool is_gpio_compatible(PIO pio, uint32_t used_gpio_ranges) {
-#if PICO_PIO_USE_GPIO_BASE
bool gpio_base = pio_get_gpio_base(pio);
return !((gpio_base && (used_gpio_ranges & 1)) ||
(!gpio_base && (used_gpio_ranges & 4)));
-#else
- ((void)pio);
- ((void)used_gpio_ranges);
- return true;
-#endif
}
+#endif
static bool is_program_gpio_compatible(PIO pio, const pio_program_t *program) {
#if PICO_PIO_VERSION > 0
return is_gpio_compatible(pio, program->used_gpio_ranges);
#else
+ // there are no stored gpio_ranges, so we assume we're good
((void)pio);
((void)program);
return true;
@@ -178,7 +172,6 @@
return (int)offset;
}
-// these assert if unable
int pio_add_program(PIO pio, const pio_program_t *program) {
uint32_t save = hw_claim_lock();
int offset = find_offset_for_program(pio, program);
@@ -411,36 +404,48 @@
}
}
-bool pio_claim_free_sm_and_add_program(const pio_program_t *program, PIO *pio, uint *sm, uint *offset) {
- return pio_claim_free_sm_and_add_program_for_gpio_range(program, pio, sm, offset, 0, 0, false);
+bool pio_claim_free_sm_and_add_program(const pio_program_t *program, PIO *pio_out, uint *sm_out, uint *offset_out) {
+ int pio_num = NUM_PIOS;
+ while (pio_num--) {
+ PIO pio = pio_get_instance((uint)pio_num);
+ int sm_or_error = pio_claim_unused_sm(pio, false);
+ if (sm_or_error >= 0) {
+ int offset_or_error = pio_add_program(pio, program);
+ if (offset_or_error >= 0) {
+ *pio_out = pio;
+ *sm_out = sm_or_error;
+ *offset_out = offset_or_error;
+ return true;
+ }
+ pio_sm_unclaim(pio, sm_or_error);
+ }
+ }
+ return false;
}
-bool pio_claim_free_sm_and_add_program_for_gpio_range(const pio_program_t *program, PIO *pio, uint *sm, uint *offset, uint gpio_start, uint gpio_count, bool set_gpio_base) {
- invalid_params_if(HARDWARE_PIO, (gpio_start + gpio_count) > NUM_BANK0_GPIOS);
-
+bool pio_claim_free_sm_and_add_program_for_gpio_range(const pio_program_t *program, PIO *pio_out, uint *sm_out, uint *offset_out, uint gpio_start, uint gpio_count, bool set_gpio_base) {
+ invalid_params_if_and_return(HARDWARE_PIO, (gpio_start + gpio_count) > MAX(32, NUM_BANK0_GPIOS), false);
#if !PICO_PIO_USE_GPIO_BASE
- // short-circuit some logic when not using GPIO_BASE
- set_gpio_base = 0;
- gpio_count = 0;
-#endif
+ invalid_params_if(HARDWARE_PIO, (gpio_start + gpio_count) > 32);
+ (void)set_gpio_base;
+ return pio_claim_free_sm_and_add_program(program, pio_out, sm_out, offset_out);
+#else
+ invalid_params_if_and_return(HARDWARE_PIO, !gpio_count, false); // 0 gpio_count breaks logic below, so return false
- // note if gpio_count == 0, we don't care about GPIOs so use a zero mask for what we require
- // if gpio_count > 0, then we just set used mask for the ends, since that is all that is checked at the moment
- uint32_t required_gpio_ranges;
- if (gpio_count) required_gpio_ranges = (1u << (gpio_start >> 4)) | (1u << ((gpio_start + gpio_count - 1) >> 4));
- else required_gpio_ranges = 0;
+ // we just set used mask for the ends, since that is all that is checked at the moment
+ uint32_t required_gpio_ranges = (1u << (gpio_start >> 4)) | (1u << ((gpio_start + gpio_count - 1) >> 4));
int passes = set_gpio_base ? 2 : 1;
for(int pass = 0; pass < passes; pass++) {
int pio_num = NUM_PIOS;
while (pio_num--) {
- *pio = pio_get_instance((uint)pio_num);
+ PIO pio = pio_get_instance((uint)pio_num);
// We need to claim an SM on the PIO
int8_t sm_index[NUM_PIO_STATE_MACHINES];
// on second pass, if there is one, we try and claim all the state machines so that we can change the GPIO base
int num_claimed;
for(num_claimed = 0; num_claimed < (pass ? (int)NUM_PIO_STATE_MACHINES : 1) ; num_claimed++) {
- sm_index[num_claimed] = (int8_t)pio_claim_unused_sm(*pio, false);
+ sm_index[num_claimed] = (int8_t)pio_claim_unused_sm(pio, false);
if (sm_index[num_claimed] < 0) break;
}
// rc = 0 if we claimed all the required state machines for the pass, <0 otherwise
@@ -448,29 +453,30 @@
if (rc >= 0) {
uint32_t save = hw_claim_lock();
if (pass) {
- pio_set_gpio_base_unsafe(*pio, required_gpio_ranges & 4 ? 16 : 0);
+ pio_set_gpio_base_unsafe(pio, required_gpio_ranges & 4 ? 16 : 0);
}
- rc = is_gpio_compatible(*pio, required_gpio_ranges) ? 0 : -1;
- if (rc >= 0) rc = find_offset_for_program(*pio, program);
- if (rc >= 0) rc = add_program_at_offset(*pio, program, (uint)rc);
+ rc = is_gpio_compatible(pio, required_gpio_ranges) ? 0 : -1;
+ if (rc >= 0) rc = find_offset_for_program(pio, program);
+ if (rc >= 0) rc = add_program_at_offset(pio, program, (uint)rc);
if (rc >= 0) {
- *sm = (uint) sm_index[0];
- *offset = (uint) rc;
+ *pio_out = pio;
+ *sm_out = (uint) sm_index[0];
+ *offset_out = (uint) rc;
}
hw_claim_unlock(save);
}
// always un-claim all SMs other than the one we need (array index 0),
// or all of them if we had an error
for (int i = (rc >= 0); i < num_claimed; i++) {
- pio_sm_unclaim(*pio, (uint) sm_index[i]);
+ pio_sm_unclaim(pio, (uint) sm_index[i]);
}
if (rc >= 0) {
return true;
}
}
}
- *pio = NULL;
return false;
+#endif
}
void pio_remove_program_and_unclaim_sm(const pio_program_t *program, PIO pio, uint sm, uint offset) {