Merge pull request #8223 from jepler/sm-mayeexec

rp2: Add StateMachine(may_exec=, offset=)
This commit is contained in:
Scott Shawcroft 2023-08-09 10:44:02 -07:00 committed by GitHub
commit 472e6bca4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 222 additions and 154 deletions

View File

@ -251,6 +251,22 @@ msgstr ""
msgid "%q=%q"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
msgid "%q[%u] shifts in more bits than pin count"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
msgid "%q[%u] shifts out more bits than pin count"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
msgid "%q[%u] uses extra pin"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
msgid "%q[%u] waits on input outside of count"
msgstr ""
#: ports/espressif/common-hal/espidf/__init__.c
#, c-format
msgid "%s error 0x%x"
@ -1161,26 +1177,6 @@ msgstr ""
msgid "Input/output error"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Instruction %d shifts in more bits than pin count"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Instruction %d shifts out more bits than pin count"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Instruction %d uses extra pin"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Instruction %d waits on input outside of count"
msgstr ""
#: ports/nrf/common-hal/_bleio/__init__.c
msgid "Insufficient authentication"
msgstr ""
@ -1352,38 +1348,31 @@ msgid "Mismatched swap flag"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_in_pin. Instruction %d reads pin(s)"
msgid "Missing first_in_pin. %q[%u] reads pin(s)"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_in_pin. Instruction %d shifts in from pin(s)"
msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_in_pin. Instruction %d waits based on pin"
msgid "Missing first_in_pin. %q[%u] waits based on pin"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_out_pin. Instruction %d shifts out to pin(s)"
msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_out_pin. Instruction %d writes pin(s)"
msgid "Missing first_out_pin. %q[%u] writes pin(s)"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing first_set_pin. Instruction %d sets pin(s)"
msgid "Missing first_set_pin. %q[%u] sets pin(s)"
msgstr ""
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#, c-format
msgid "Missing jmp_pin. Instruction %d jumps on pin"
msgid "Missing jmp_pin. %q[%u] jumps on pin"
msgstr ""
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c

View File

@ -62,6 +62,7 @@
//| program: ReadableBuffer,
//| frequency: int,
//| *,
//| may_exec: Optional[ReadableBuffer] = None,
//| init: Optional[ReadableBuffer] = None,
//| first_out_pin: Optional[microcontroller.Pin] = None,
//| out_pin_count: int = 1,
@ -93,6 +94,7 @@
//| user_interruptible: bool = True,
//| wrap_target: int = 0,
//| wrap: int = -1,
//| offset: int = -1,
//| ) -> None:
//| """Construct a StateMachine object on the given pins with the given program.
//|
@ -100,6 +102,10 @@
//| :param int frequency: the target clock frequency of the state machine. Actual may be less. Use 0 for system clock speed.
//| :param ReadableBuffer init: a program to run once at start up. This is run after program
//| is started so instructions may be intermingled
//| :param ReadableBuffer may_exec: Instructions that may be executed via `StateMachine.run` calls.
//| Some elements of the `StateMachine`'s configuration are inferred from the instructions used;
//| for instance, if there is no ``in`` or ``push`` instruction, then the `StateMachine` is configured without a receive FIFO.
//| In this case, passing a ``may_exec`` program containing an ``in`` instruction such as ``in x``, a receive FIFO will be configured.
//| :param ~microcontroller.Pin first_out_pin: the first pin to use with the OUT instruction
//| :param int out_pin_count: the count of consecutive pins to use with OUT starting at first_out_pin
//| :param int initial_out_pin_state: the initial output value for out pins starting at first_out_pin
@ -146,13 +152,16 @@
//| :param int wrap: The instruction after which to wrap to the ``wrap``
//| instruction. As a special case, -1 (the default) indicates the
//| last instruction of the program.
//| :param int offset: A specific offset in the state machine's program memory where the program must be loaded.
//| The default value, -1, allows the program to be loaded at any offset.
//| This is appropriate for most programs.
//| """
//| ...
STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
rp2pio_statemachine_obj_t *self = m_new_obj(rp2pio_statemachine_obj_t);
self->base.type = &rp2pio_statemachine_type;
enum { ARG_program, ARG_frequency, ARG_init,
enum { ARG_program, ARG_frequency, ARG_init, ARG_may_exec,
ARG_first_out_pin, ARG_out_pin_count, ARG_initial_out_pin_state, ARG_initial_out_pin_direction,
ARG_first_in_pin, ARG_in_pin_count,
ARG_pull_in_pin_up, ARG_pull_in_pin_down,
@ -166,11 +175,13 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
ARG_auto_push, ARG_push_threshold, ARG_in_shift_right,
ARG_user_interruptible,
ARG_wrap_target,
ARG_wrap,};
ARG_wrap,
ARG_offset,};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_program, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_frequency, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_init, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_may_exec, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_first_out_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_out_pin_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
@ -209,6 +220,7 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
{ MP_QSTR_wrap_target, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_wrap, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -220,6 +232,10 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
init_bufinfo.len = 0;
mp_get_buffer(args[ARG_init].u_obj, &init_bufinfo, MP_BUFFER_READ);
mp_buffer_info_t may_exec_bufinfo;
may_exec_bufinfo.len = 0;
mp_get_buffer(args[ARG_may_exec].u_obj, &may_exec_bufinfo, MP_BUFFER_READ);
// We don't validate pin in use here because we may be ok sharing them within a PIO.
const mcu_pin_obj_t *first_out_pin =
validate_obj_is_pin_or_none(args[ARG_first_out_pin].u_obj, MP_QSTR_first_out_pin);
@ -264,6 +280,7 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
bufinfo.buf, bufinfo.len / 2,
args[ARG_frequency].u_int,
init_bufinfo.buf, init_bufinfo.len / 2,
may_exec_bufinfo.buf, may_exec_bufinfo.len / 2,
first_out_pin, out_pin_count, args[ARG_initial_out_pin_state].u_int, args[ARG_initial_out_pin_direction].u_int,
first_in_pin, in_pin_count, args[ARG_pull_in_pin_up].u_int, args[ARG_pull_in_pin_down].u_int,
first_set_pin, set_pin_count, args[ARG_initial_set_pin_state].u_int, args[ARG_initial_set_pin_direction].u_int,
@ -276,7 +293,7 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n
args[ARG_wait_for_txstall].u_bool,
args[ARG_auto_push].u_bool, push_threshold, args[ARG_in_shift_right].u_bool,
args[ARG_user_interruptible].u_bool,
wrap_target, wrap);
wrap_target, wrap, args[ARG_offset].u_int);
return MP_OBJ_FROM_PTR(self);
}

View File

@ -41,6 +41,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
const uint16_t *program, size_t program_len,
size_t frequency,
const uint16_t *init, size_t init_len,
const uint16_t *may_exec, size_t may_exec_len,
const mcu_pin_obj_t *first_out_pin, uint8_t out_pin_count, uint32_t initial_out_pin_state, uint32_t initial_out_pin_direction,
const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count, uint32_t pull_pin_up, uint32_t pull_pin_down,
const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count, uint32_t initial_set_pin_state, uint32_t initial_set_pin_direction,
@ -53,7 +54,8 @@ 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_taget, int wrap,
int offset);
void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self);
bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self);

View File

@ -121,7 +121,8 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
&self->state_machine,
program, program_len,
44100 * 32 * 6, // Clock at 44.1 khz to warm the DAC up.
NULL, 0,
NULL, 0, // init
NULL, 0, // may_exec
data, 1, 0, 0xffffffff, // out pin
NULL, 0, // in pins
0, 0, // in pulls
@ -135,7 +136,8 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
false, // Wait for txstall
false, 32, false, // in settings
false, // Not user-interruptible.
0, -1); // wrap settings
0, -1, // wrap settings
PIO_ANY_OFFSET);
self->playing = false;
audio_dma_init(&self->dma);

View File

@ -66,6 +66,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self,
pdmin, MP_ARRAY_SIZE(pdmin),
sample_rate * 32 * 2, // Frequency based on sample rate
NULL, 0,
NULL, 0, // may_exec
NULL, 1, 0, 0xffffffff, // out pin
data_pin, 1, // in pins
0, 0, // in pulls
@ -79,8 +80,8 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self,
false, // Wait for txstall
false, 32, true, // in settings
false, // Not user-interruptible.
0, -1); // wrap settings
0, -1, // wrap settings
PIO_ANY_OFFSET);
uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine);
if (actual_frequency < MIN_MIC_CLOCK) {
mp_raise_ValueError(translate("sampling rate out of range"));

View File

@ -102,6 +102,7 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle
imagecapture_code, MP_ARRAY_SIZE(imagecapture_code),
common_hal_mcu_processor_get_frequency(), // full speed (4 instructions per loop -> max pclk 30MHz @ 120MHz)
0, 0, // init
NULL, 0, // may_exec
NULL, 0, 0, 0, // out pins
pin_from_number(data_pins[0]), data_count, // in pins
0, 0, // in pulls
@ -119,7 +120,8 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle
false, // wait for txstall
true, 32, true, // in settings
false, // Not user-interruptible.
2, 5); // wrap settings
2, 5, // wrap settings
PIO_ANY_OFFSET);
}
void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) {

View File

@ -79,7 +79,9 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
false, // claim pins
false, // Not user-interruptible.
false, // No sideset enable
0, -1); // wrap
0, -1, // wrap
PIO_ANY_OFFSET // offset
);
if (!ok) {
// Do nothing. Maybe bitbang?
return;

View File

@ -98,6 +98,7 @@ void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbu
parallel_program, MP_ARRAY_SIZE(parallel_program),
frequency * 2, // frequency multiplied by 2 as 2 PIO instructions
NULL, 0, // init
NULL, 0, // may_exec
data0, 8, 0, 255, // first out pin, # out pins
NULL, 0, 0, 0, // first in pin, # in pins
NULL, 0, 0, 0, // first set pin
@ -110,7 +111,8 @@ void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbu
false, // wait for TX stall
false, 32, true, // RX setting we don't use
false, // Not user-interruptible.
0, -1); // wrap settings
0, -1, // wrap settings
PIO_ANY_OFFSET);
common_hal_rp2pio_statemachine_never_reset(&self->state_machine);
}

View File

@ -61,6 +61,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self,
pulsein_program, MP_ARRAY_SIZE(pulsein_program),
1000000, // frequency
NULL, 0, // init, init_len
NULL, 0, // may_exec
NULL, 0, 0, 0, // first out pin, # out pins, initial_out_pin_state
pin, 1, 0, 0, // first in pin, # in pins
NULL, 0, 0, 0, // first set pin
@ -73,7 +74,8 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self,
false, // wait for TX stall
true, 32, true, // RX auto pull every 32 bits. shift left to output msb first
false, // Not user-interruptible.
0, -1); // wrap settings
0, -1, // wrap settings
PIO_ANY_OFFSET);
common_hal_pulseio_pulsein_pause(self);

View File

@ -81,6 +81,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
encoder, MP_ARRAY_SIZE(encoder),
1000000,
encoder_init, MP_ARRAY_SIZE(encoder_init), // init
NULL, 0, // may_exec
NULL, 0, 0, 0, // out pin
pins[0], 2, // in pins
3, 0, // in pulls
@ -94,7 +95,8 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
false, // Wait for txstall
false, 32, false, // in settings
false, // Not user-interruptible.
0, MP_ARRAY_SIZE(encoder) - 1 // wrap settings
0, MP_ARRAY_SIZE(encoder) - 1, // wrap settings
PIO_ANY_OFFSET
);
// We're guaranteed by the init code that some output will be available promptly

View File

@ -178,6 +178,21 @@ STATIC uint32_t _check_pins_free(const mcu_pin_obj_t *first_pin, uint8_t pin_cou
return pins_we_use;
}
static bool can_add_program(PIO pio, const pio_program_t *program, int offset) {
if (offset == -1) {
return pio_can_add_program(pio, program);
}
return pio_can_add_program_at_offset(pio, program, offset);
}
static uint add_program(PIO pio, const pio_program_t *program, int offset) {
if (offset == -1) {
return pio_add_program(pio, program);
} else {
pio_add_program_at_offset(pio, program, offset);
return offset;
}
}
bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
const uint16_t *program, size_t program_len,
@ -197,7 +212,8 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
bool claim_pins,
bool user_interruptible,
bool sideset_enable,
int wrap_target, int wrap
int wrap_target, int wrap,
int offset
) {
// Create a program id that isn't the pointer so we can store it without storing the original object.
uint32_t program_id = ~((uint32_t)program);
@ -215,14 +231,15 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
uint8_t free_count = 0;
for (size_t j = 0; j < NUM_PIO_STATE_MACHINES; j++) {
if (_current_program_id[i][j] == program_id &&
_current_program_len[i][j] == program_len) {
_current_program_len[i][j] == program_len &&
(offset == -1 || offset == _current_program_offset[i][j])) {
program_offset = _current_program_offset[i][j];
}
if (!pio_sm_is_claimed(pio, j)) {
free_count++;
}
}
if (free_count > 0 && (program_offset < 32 || pio_can_add_program(pio, &program_struct))) {
if (free_count > 0 && (program_offset < 32 || can_add_program(pio, &program_struct, offset))) {
pio_index = i;
if (program_offset < 32) {
break;
@ -254,7 +271,7 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
self->pio = pio_instances[pio_index];
self->state_machine = state_machine;
if (program_offset == 32) {
program_offset = pio_add_program(self->pio, &program_struct);
program_offset = add_program(self->pio, &program_struct, offset);
}
self->offset = program_offset;
_current_program_id[pio_index][state_machine] = program_id;
@ -387,10 +404,119 @@ static uint32_t mask_and_rotate(const mcu_pin_obj_t *first_pin, uint32_t bit_cou
return value << shift | value >> (32 - shift);
}
typedef struct {
struct {
uint32_t pins_we_use, in_pin_count, out_pin_count;
bool has_jmp_pin, auto_push, auto_pull, has_in_pin, has_out_pin, has_set_pin;
} inputs;
struct {
bool tx_fifo, rx_fifo, in_loaded, out_loaded, in_used, out_used;
} outputs;
} introspect_t;
static void consider_instruction(introspect_t *state, uint16_t full_instruction, qstr what_program, size_t i) {
uint16_t instruction = full_instruction & 0xe000;
if (instruction == 0x8000) {
if ((full_instruction & 0xe080) == pio_instr_bits_push) {
state->outputs.rx_fifo = true;
state->outputs.in_loaded = true;
} else { // pull otherwise.
state->outputs.tx_fifo = true;
state->outputs.out_loaded = true;
}
}
if (instruction == pio_instr_bits_jmp) {
uint16_t condition = (full_instruction & 0x00e0) >> 5;
if ((condition == 0x6) && !state->inputs.has_jmp_pin) {
mp_raise_ValueError_varg(translate("Missing jmp_pin. %q[%u] jumps on pin"), what_program, i);
}
}
if (instruction == pio_instr_bits_wait) {
uint16_t wait_source = (full_instruction & 0x0060) >> 5;
uint16_t wait_index = full_instruction & 0x001f;
if (wait_source == 0 && (state->inputs.pins_we_use & (1 << wait_index)) == 0) { // GPIO
mp_raise_ValueError_varg(translate("%q[%u] uses extra pin"), what_program, i);
}
if (wait_source == 1) { // Input pin
if (!state->inputs.has_in_pin) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. %q[%u] waits based on pin"), what_program, i);
}
if (wait_index >= state->inputs.in_pin_count) {
mp_raise_ValueError_varg(translate("%q[%u] waits on input outside of count"), what_program, i);
}
}
}
if (instruction == pio_instr_bits_in) {
uint16_t source = (full_instruction & 0x00e0) >> 5;
uint16_t bit_count = full_instruction & 0x001f;
if (source == 0) {
if (!state->inputs.has_in_pin) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. %q[%u] shifts in from pin(s)"), what_program, i);
}
if (bit_count > state->inputs.in_pin_count) {
mp_raise_ValueError_varg(translate("%q[%u] shifts in more bits than pin count"), what_program, i);
}
}
if (state->inputs.auto_push) {
state->outputs.in_loaded = true;
state->outputs.rx_fifo = true;
}
state->outputs.in_used = true;
}
if (instruction == pio_instr_bits_out) {
uint16_t bit_count = full_instruction & 0x001f;
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if (destination == 0x0 || destination == 0x4) {
if (!state->inputs.has_out_pin) {
mp_raise_ValueError_varg(translate("Missing first_out_pin. %q[%u] shifts out to pin(s)"), what_program, i);
}
if (bit_count > state->inputs.out_pin_count) {
mp_raise_ValueError_varg(translate("%q[%u] shifts out more bits than pin count"), what_program, i);
}
}
if (state->inputs.auto_pull) {
state->outputs.out_loaded = true;
state->outputs.tx_fifo = true;
}
state->outputs.out_used = true;
}
if (instruction == pio_instr_bits_set) {
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if ((destination == 0x00 || destination == 0x4) && !state->inputs.has_set_pin) {
mp_raise_ValueError_varg(translate("Missing first_set_pin. %q[%u] sets pin(s)"), what_program, i);
}
}
if (instruction == pio_instr_bits_mov) {
uint16_t source = full_instruction & 0x0007;
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if (destination == 0x0 && !state->inputs.has_out_pin) {
mp_raise_ValueError_varg(translate("Missing first_out_pin. %q[%u] writes pin(s)"), what_program, i);
}
if (source == 0x0 && !state->inputs.has_in_pin) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. %q[%u] reads pin(s)"), what_program, i);
}
if (destination == 0x6) {
state->outputs.in_loaded = true;
} else if (destination == 0x7) {
state->outputs.out_loaded = true;
}
}
}
static void consider_program(introspect_t *state, const uint16_t *program, size_t program_len, qstr what_program) {
for (size_t i = 0; i < program_len; i++) {
consider_instruction(state, program[i], what_program, i);
}
}
void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
const uint16_t *program, size_t program_len,
size_t frequency,
const uint16_t *init, size_t init_len,
const uint16_t *may_exec, size_t may_exec_len,
const mcu_pin_obj_t *first_out_pin, uint8_t out_pin_count, uint32_t initial_out_pin_state, uint32_t initial_out_pin_direction,
const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count,
uint32_t pull_pin_up, uint32_t pull_pin_down,
@ -404,7 +530,8 @@ 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_target, int wrap) {
int wrap_target, int wrap,
int offset) {
// First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false.
uint32_t pins_we_use = wait_gpio_mask;
@ -415,109 +542,27 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
pins_we_use |= _check_pins_free(jmp_pin, 1, exclusive_pin_use);
// Look through the program to see what we reference and make sure it was provided.
bool tx_fifo = false;
bool rx_fifo = false;
bool in_loaded = false; // can be loaded in other ways besides the fifo
bool out_loaded = false;
bool in_used = false;
bool out_used = false;
for (size_t i = 0; i < program_len; i++) {
uint16_t full_instruction = program[i];
uint16_t instruction = full_instruction & 0xe000;
if (instruction == 0x8000) {
if ((full_instruction & 0xe080) == pio_instr_bits_push) {
rx_fifo = true;
in_loaded = true;
} else { // pull otherwise.
tx_fifo = true;
out_loaded = true;
}
introspect_t state = {
.inputs = {
.pins_we_use = pins_we_use,
.has_jmp_pin = jmp_pin != NULL,
.has_in_pin = first_in_pin != NULL,
.has_out_pin = first_out_pin != NULL,
.has_set_pin = first_set_pin != NULL,
.in_pin_count = in_pin_count,
.out_pin_count = out_pin_count,
.auto_pull = auto_pull,
.auto_push = auto_push,
}
if (instruction == pio_instr_bits_jmp) {
uint16_t condition = (full_instruction & 0x00e0) >> 5;
if ((condition == 0x6) && (jmp_pin == NULL)) {
mp_raise_ValueError_varg(translate("Missing jmp_pin. Instruction %d jumps on pin"), i);
}
}
if (instruction == pio_instr_bits_wait) {
uint16_t wait_source = (full_instruction & 0x0060) >> 5;
uint16_t wait_index = full_instruction & 0x001f;
if (wait_source == 0 && (pins_we_use & (1 << wait_index)) == 0) { // GPIO
mp_raise_ValueError_varg(translate("Instruction %d uses extra pin"), i);
}
if (wait_source == 1) { // Input pin
if (first_in_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d waits based on pin"), i);
}
if (wait_index >= in_pin_count) {
mp_raise_ValueError_varg(translate("Instruction %d waits on input outside of count"), i);
}
}
}
if (instruction == pio_instr_bits_in) {
uint16_t source = (full_instruction & 0x00e0) >> 5;
uint16_t bit_count = full_instruction & 0x001f;
if (source == 0) {
if (first_in_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d shifts in from pin(s)"), i);
}
if (bit_count > in_pin_count) {
mp_raise_ValueError_varg(translate("Instruction %d shifts in more bits than pin count"), i);
}
}
if (auto_push) {
in_loaded = true;
rx_fifo = true;
}
in_used = true;
}
if (instruction == pio_instr_bits_out) {
uint16_t bit_count = full_instruction & 0x001f;
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if (destination == 0x0 || destination == 0x4) {
if (first_out_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_out_pin. Instruction %d shifts out to pin(s)"), i);
}
if (bit_count > out_pin_count) {
mp_raise_ValueError_varg(translate("Instruction %d shifts out more bits than pin count"), i);
}
}
if (auto_pull) {
out_loaded = true;
tx_fifo = true;
}
out_used = true;
}
if (instruction == pio_instr_bits_set) {
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if ((destination == 0x00 || destination == 0x4) && first_set_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_set_pin. Instruction %d sets pin(s)"), i);
}
}
if (instruction == pio_instr_bits_mov) {
uint16_t source = full_instruction & 0x0007;
uint16_t destination = (full_instruction & 0x00e0) >> 5;
// Check for pins or pindirs destination.
if (destination == 0x0 && first_out_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_out_pin. Instruction %d writes pin(s)"), i);
}
if (source == 0x0 && first_in_pin == NULL) {
mp_raise_ValueError_varg(translate("Missing first_in_pin. Instruction %d reads pin(s)"), i);
}
if (destination == 0x6) {
in_loaded = true;
} else if (destination == 0x7) {
out_loaded = true;
}
}
}
};
consider_program(&state, program, program_len, MP_QSTR_program);
consider_program(&state, init, init_len, MP_QSTR_init);
consider_program(&state, may_exec, may_exec_len, MP_QSTR_may_exec);
if (!in_loaded && in_used) {
if (!state.outputs.in_loaded && state.outputs.in_used) {
mp_raise_ValueError_varg(translate("Program does IN without loading ISR"));
}
if (!out_loaded && out_used) {
if (!state.outputs.out_loaded && state.outputs.out_used) {
mp_raise_ValueError_varg(translate("Program does OUT without loading OSR"));
}
@ -570,14 +615,14 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
first_sideset_pin, sideset_pin_count,
initial_pin_state, initial_pin_direction,
jmp_pin,
pins_we_use, tx_fifo, rx_fifo,
pins_we_use, state.outputs.tx_fifo, state.outputs.rx_fifo,
auto_pull, pull_threshold, out_shift_right,
wait_for_txstall,
auto_push, push_threshold, in_shift_right,
true /* claim pins */,
user_interruptible,
sideset_enable,
wrap_target, wrap);
wrap_target, wrap, offset);
if (!ok) {
mp_raise_RuntimeError(translate("All state machines in use"));
}

View File

@ -32,6 +32,8 @@
#include "common-hal/microcontroller/Pin.h"
#include "src/rp2_common/hardware_pio/include/hardware/pio.h"
enum { PIO_ANY_OFFSET = -1 };
typedef struct sm_buf_info {
mp_obj_t obj;
mp_buffer_info_t info;
@ -88,7 +90,7 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self,
bool claim_pins,
bool interruptible,
bool sideset_enable,
int wrap_target, int wrap);
int wrap_target, int wrap, int offset);
uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self);