diff --git a/piolib/examples/CMakeLists.txt b/piolib/examples/CMakeLists.txt index 57a4ace..0d8ca0c 100644 --- a/piolib/examples/CMakeLists.txt +++ b/piolib/examples/CMakeLists.txt @@ -54,3 +54,38 @@ add_executable(quadenc quadrature_encoder.c) target_include_directories(quadenc PRIVATE ../include) target_link_libraries(quadenc pio) install(TARGETS quadenc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(txcount txcount.c) +target_include_directories(txcount PRIVATE ../include) +target_link_libraries(txcount pio) +install(TARGETS txcount RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(blockingtest blockingtest.c) +target_include_directories(blockingtest PRIVATE ../include) +target_link_libraries(blockingtest pio pthread) +install(TARGETS blockingtest RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(rxsource rxsource.c) +target_include_directories(rxsource PRIVATE ../include) +target_link_libraries(rxsource pio) +install(TARGETS rxsource RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(irqtest irqtest.c) +target_include_directories(irqtest PRIVATE ../include ../../pinctrl) +target_link_libraries(irqtest pio gpiolib) +install(TARGETS irqtest RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(irqhog irqhog.c) +target_include_directories(irqhog PRIVATE ../include) +target_link_libraries(irqhog pio) +install(TARGETS irqhog RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(irqtest2 irqtest2.c) +target_include_directories(irqtest2 PRIVATE ../include ../../pinctrl) +target_link_libraries(irqtest2 pio gpiolib) +install(TARGETS irqtest2 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(txsink txsink.c) +target_include_directories(txsink PRIVATE ../include) +target_link_libraries(txsink pio) +install(TARGETS txsink RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/piolib/examples/apitest.c b/piolib/examples/apitest.c index 99a21bf..a39f23c 100644 --- a/piolib/examples/apitest.c +++ b/piolib/examples/apitest.c @@ -16,11 +16,17 @@ #define DATA_WORDS 16 +static void check(const char *label, bool value) { + if (!value) + pio_panic(label); +} + int main(int argc, const char **argv) { uint32_t databuf[DATA_WORDS]; bool use_dma = true; int ret = 0; int i, j; + uint32_t temp; uint16_t dummy_program_instructions[1]; struct pio_program dummy_program = { .instructions = dummy_program_instructions, @@ -38,72 +44,62 @@ int main(int argc, const char **argv) { pio_enable_fatal_errors(pio, false); for (sm = 0; sm < pio_get_sm_count(pio); sm++) { - if (pio_sm_is_claimed(pio, sm)) - pio_panic("SM claimed (1)"); + check("SM claimed (1)", !pio_sm_is_claimed(pio, sm)); pio_sm_claim(pio, sm); - if (pio_get_error(pio)) - pio_panic("SM claim failed"); + check("SM claim failed", !pio_get_error(pio)); - if (!pio_sm_is_claimed(pio, sm)) - pio_panic("SM not claimed (1)"); + check("SM not claimed (1)", pio_sm_is_claimed(pio, sm)); pio_sm_claim(pio, sm); - if (!pio_get_error(pio)) - pio_panic("SM not claimed (2)"); + check("SM not claimed (2)", pio_get_error(pio)); pio_clear_error(pio); } for (sm = 0; sm < pio_get_sm_count(pio); sm++) { pio_sm_unclaim(pio, sm); - if (pio_sm_is_claimed(pio, sm)) - pio_panic("SM still claimed"); + check("SM still claimed", !pio_sm_is_claimed(pio, sm)); } pio_claim_sm_mask(pio, (1 << (pio_get_sm_count(pio) - 1)) - 1); - if (pio_get_error(pio)) - pio_panic("masked claim failed"); - if ((uint)pio_claim_unused_sm(pio, false) != pio_get_sm_count(pio) - 1) - pio_panic("wrong SM (expected the last)"); + check("masked claim failed", !pio_get_error(pio)); + check("wrong SM (expected the last)", + (uint)pio_claim_unused_sm(pio, false) == pio_get_sm_count(pio) - 1); for (sm = 0; sm < pio_get_sm_count(pio); sm++) { pio_sm_unclaim(pio, sm); - if (pio_sm_is_claimed(pio, sm)) - pio_panic("SM still claimed"); + check("SM still claimed", !pio_sm_is_claimed(pio, sm)); } sm = pio_claim_unused_sm(pio, true); for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { dummy_program_instructions[0] = offset + 1; - if (!pio_can_add_program(pio, &dummy_program)) - pio_panic("can't add program"); - if (pio_add_program(pio, &dummy_program) == PIO_ORIGIN_ANY) - pio_panic("failed to add program (1)"); + check("can't add program", pio_can_add_program(pio, &dummy_program)); + check("failed to add program (1)", + pio_add_program(pio, &dummy_program) != PIO_ORIGIN_ANY); + check("can't add program again", pio_can_add_program(pio, &dummy_program)); } dummy_program_instructions[0] = offset + 1; - if (pio_can_add_program(pio, &dummy_program)) - pio_panic("can add program"); - if (pio_add_program(pio, &dummy_program) != PIO_ORIGIN_INVALID) - pio_panic("added program again"); + check("can add program", !pio_can_add_program(pio, &dummy_program)); + check("added program again", + pio_add_program(pio, &dummy_program) == PIO_ORIGIN_INVALID); pio_clear_error(pio); for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { pio_remove_program(pio, &dummy_program, offset); - if (pio_get_error(pio)) - pio_panic("remove program failed"); + check("remove program failed", !pio_get_error(pio)); pio_remove_program(pio, &dummy_program, offset); - if (!pio_get_error(pio)) - pio_panic("removed program again"); + check("removed program again", pio_get_error(pio)); pio_clear_error(pio); } for (offset = 0; offset < pio_get_instruction_count(pio); offset++) { dummy_program_instructions[0] = offset + 1; - if (!pio_can_add_program_at_offset(pio, &dummy_program, offset)) - pio_panic("can't add program at offset"); + check("can't add program at offset", + pio_can_add_program_at_offset(pio, &dummy_program, offset)); pio_add_program_at_offset(pio, &dummy_program, offset); } @@ -119,79 +115,83 @@ int main(int argc, const char **argv) { pio_sm_put(pio, sm, 0); - if (pio_sm_is_tx_fifo_empty(pio, sm)) - pio_panic("TX FIFO is empty (1)"); + check("TX FIFO is empty (1)", !pio_sm_is_tx_fifo_empty(pio, sm)); pio_sm_clear_fifos(pio, sm); + pio_sm_clear_flags(pio, sm, PIO_SM_FLAG_ALL); + + check("TX FIFO is not empty (1)", pio_sm_is_tx_fifo_empty(pio, sm)); + check("TX FIFO level is not zero", pio_sm_get_tx_fifo_level(pio, sm) == 0); + check("FIFO flags are non-zero", pio_sm_get_flags(pio, sm, PIO_SM_FLAG_ALL, false) == 0); + pio_sm_get(pio, sm); + check("FIFO flags not RXUNDER", + pio_sm_get_flags(pio, sm, PIO_SM_FLAG_ALL, true) == PIO_SM_FLAG_RXUNDER); + + printf("%d: %08x\n", 0, pio_sm_get_dmactrl(pio, sm, true)); + + temp = pio_sm_get_dmactrl(pio, sm, true); + pio_sm_set_dmactrl(pio, sm, true, (temp & ~0x1f) | (pio_get_fifo_depth(pio) - 2)); - if (!pio_sm_is_tx_fifo_empty(pio, sm)) - pio_panic("TX FIFO is not empty (1)"); - if (pio_sm_get_tx_fifo_level(pio, sm) != 0) - pio_panic("TX FIFO level is not zero"); for (i = 1; i <= (int)pio_get_fifo_depth(pio); i++) { - if (pio_sm_is_tx_fifo_full(pio, sm)) - pio_panic("TX FIFO is full (1)"); + // Set the DREQ threshold to deassert when the FIFO level is i + pio_sm_set_dmactrl(pio, sm, true, (temp & ~0x1f) | (i - 1)); + printf("%d: %08x\n", i, pio_sm_get_dmactrl(pio, sm, true)); + check("TX FIFO is full (1)", !pio_sm_is_tx_fifo_full(pio, sm)); pio_sm_put(pio, sm, i); - if (pio_sm_is_tx_fifo_empty(pio, sm)) - pio_panic("TX FIFO is empty (2)"); - if ((int)pio_sm_get_tx_fifo_level(pio, sm) != i) - pio_panic("Wrong TX FIFO level"); + check("TX FIFO is empty (2)", !pio_sm_is_tx_fifo_empty(pio, sm)); + check("Wrong TX FIFO level", (int)pio_sm_get_tx_fifo_level(pio, sm) == i); + printf(" -> %08x\n", pio_sm_get_dmactrl(pio, sm, true)); } - if (!pio_sm_is_tx_fifo_full(pio, sm)) - pio_panic("TX FIFO is not full (1)"); + check("TX FIFO is not full (1)", pio_sm_is_tx_fifo_full(pio, sm)); pio_sm_drain_tx_fifo(pio, sm); - if (!pio_sm_is_tx_fifo_empty(pio, sm)) - pio_panic("TX FIFO is not empty (2)"); + check("TX FIFO is not empty (2)", pio_sm_is_tx_fifo_empty(pio, sm)); for (i = 1; i <= (int)pio_get_fifo_depth(pio); i++) pio_sm_put(pio, sm, i); - if (!pio_sm_is_tx_fifo_full(pio, sm)) - pio_panic("TX FIFO is not full (2)"); + check("TX FIFO is not full (2)", pio_sm_is_tx_fifo_full(pio, sm)); - if (!pio_sm_is_rx_fifo_empty(pio, sm)) - pio_panic("RX FIFO not empty"); + check("RX FIFO not empty", pio_sm_is_rx_fifo_empty(pio, sm)); - if ((int)pio_sm_get_rx_fifo_level(pio, sm) != 0) - pio_panic("RX FIFO level not 0"); + check("RX FIFO level not 0", (int)pio_sm_get_rx_fifo_level(pio, sm) == 0); pio_sm_set_enabled(pio, sm, true); - if (!pio_sm_is_tx_fifo_empty(pio, sm)) - pio_panic("TX FIFO is not empty (3)"); + check("TX FIFO is not empty (3)", pio_sm_is_tx_fifo_empty(pio, sm)); - if (!pio_sm_is_rx_fifo_full(pio, sm)) - pio_panic("RX FIFO is not full"); + check("RX FIFO is not full", pio_sm_is_rx_fifo_full(pio, sm)); - if (pio_sm_get_rx_fifo_level(pio, sm) != pio_get_fifo_depth(pio)) - pio_panic("RX FIFO level not the maximum"); + check("RX FIFO level not the maximum", + pio_sm_get_rx_fifo_level(pio, sm) == pio_get_fifo_depth(pio)); + temp = pio_sm_get_dmactrl(pio, sm, false); for (i = pio_get_fifo_depth(pio) - 1; i >= 0; i--) { - if (pio_sm_is_rx_fifo_empty(pio, sm)) - pio_panic("RX FIFO is empty"); - if (pio_sm_get(pio, sm) != (uint)(pio_get_fifo_depth(pio) - i)) - pio_panic("wrong RX data"); - if ((int)pio_sm_get_rx_fifo_level(pio, sm) != i) - pio_panic("wrong RX FIFO level"); + // Set the DREQ threshold to deassert when the FIFO level is i + pio_sm_set_dmactrl(pio, sm, false, (temp & ~0x1f) | (i + 1)); + printf("%d: %08x\n", i, pio_sm_get_dmactrl(pio, sm, false)); + check("RX FIFO is empty", !pio_sm_is_rx_fifo_empty(pio, sm)); + check("wrong RX data", + pio_sm_get(pio, sm) == (uint)(pio_get_fifo_depth(pio) - i)); + check("wrong RX FIFO level", (int)pio_sm_get_rx_fifo_level(pio, sm) == i); + printf(" -> %08x\n", pio_sm_get_dmactrl(pio, sm, false)); } - if (!pio_sm_is_rx_fifo_empty(pio, sm)) - pio_panic("RX FIFO is not empty"); + check("RX FIFO is not empty", pio_sm_is_rx_fifo_empty(pio, sm)); offset = pio_add_program(pio, &genseq_program); - if (!pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 0)) - pio_panic("DMA configuration didn't fail"); - if (!pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 4, NULL) || - !pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 0, databuf)) - pio_panic("DMA transfer didn't fail"); - if (pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 2)) - pio_panic("DMA configuration failed"); + check("DMA configuration didn't fail", + pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 0)); + check("DMA transfer didn't fail", + pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 4, NULL) && + pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, 0, databuf)); + check("DMA configuration failed", + !pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, 4096, 2)); pio_gpio_init(pio, gpio); pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); diff --git a/piolib/examples/blockingtest.c b/piolib/examples/blockingtest.c new file mode 100644 index 0000000..c36763f --- /dev/null +++ b/piolib/examples/blockingtest.c @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "blockingtest.pio.h" + +#define LOOP_ITERATIONS 300 +#define IRQ_ITERATIONS 20 +#define IRQ_FLAG 4 + +static PIO pio; +static int sm_loop; +static int sm_irq; +static int sm_trigger; + +static atomic_int errors; +static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER; + +static void log_line(const char *fmt, ...) { + va_list args; + pthread_mutex_lock(&log_lock); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + pthread_mutex_unlock(&log_lock); +} + +/* Pushes values into the loopback state machine as fast as it can, pausing + * occasionally so the consumer sees an empty FIFO and has to block for data. */ +static void *loop_producer_thread(void *arg) { + (void)arg; + pio_select(pio); + for (uint32_t i = 0; i < LOOP_ITERATIONS; i++) { + pio_sm_put_blocking(pio, sm_loop, i); + if ((i % 7) == 0) + usleep(rand() % 3000); + } + return NULL; +} + +/* Pulls the values back out, checking that FIFO ordering survived the trip, + * pausing occasionally so the producer sees a full FIFO and has to block. */ +static void *loop_consumer_thread(void *arg) { + (void)arg; + pio_select(pio); + for (uint32_t i = 0; i < LOOP_ITERATIONS; i++) { + uint32_t value = pio_sm_get_blocking(pio, sm_loop); + if (value != i) { + log_line("loop: expected %u, got %u\n", i, value); + atomic_fetch_add(&errors, 1); + } + if ((i % 5) == 0) + usleep(rand() % 4000); + } + return NULL; +} + +/* Sits on sm_irq, which is permanently stalled on "wait 1 irq". Each call + * only returns once irq_trigger_thread() has set the flag it is waiting on. */ +static void *irq_consumer_thread(void *arg) { + (void)arg; + pio_select(pio); + for (int i = 0; i < IRQ_ITERATIONS; i++) { + pio_sm_get_blocking(pio, sm_irq); + log_line("irq: wait %d released\n", i); + } + return NULL; +} + +/* Delays, then injects an "irq set" instruction into sm_trigger - an + * unrelated, idling state machine - using the blocking exec call. This + * proves that pio_sm_exec_wait_blocking() only returns once the injected + * instruction has actually retired, and that doing so on one state machine + * correctly wakes another that is blocked in "wait 1 irq". */ +static void *irq_trigger_thread(void *arg) { + (void)arg; + pio_select(pio); + uint set_instr = pio_encode_irq_set(false, IRQ_FLAG); + for (int i = 0; i < IRQ_ITERATIONS; i++) { + usleep(50000 + (rand() % 150000)); + pio_sm_exec_wait_blocking(pio, sm_trigger, set_instr); + log_line("irq: trigger %d fired\n", i); + } + return NULL; +} + +int main(__unused int argc, __unused const char **argv) { + pthread_t loop_producer, loop_consumer, irq_trigger, irq_consumer; + + stdio_init_all(); + srand(time(NULL)); + + pio = pio0; + + sm_loop = pio_claim_unused_sm(pio, true); + uint offset_loop = pio_add_program(pio, &blocking_loop_program); + sm_irq = pio_claim_unused_sm(pio, true); + uint offset_irq = pio_add_program(pio, &blocking_irq_program); + sm_trigger = pio_claim_unused_sm(pio, true); + uint offset_trigger = pio_add_program(pio, &blocking_idle_program); + + printf("sm_loop=%d sm_irq=%d sm_trigger=%d\n", sm_loop, sm_irq, sm_trigger); + + /* Run the loopback state machine slowly, so that put_blocking() and + * get_blocking() are genuinely forced to block on the hardware FIFOs, + * not just on each other's sleep()s. */ + pio_sm_config c = blocking_loop_program_get_default_config(offset_loop); + sm_config_set_clkdiv_int_frac(&c, 1000, 0); + pio_sm_init(pio, sm_loop, offset_loop, &c); + pio_sm_set_enabled(pio, sm_loop, true); + + c = blocking_irq_program_get_default_config(offset_irq); + pio_sm_init(pio, sm_irq, offset_irq, &c); + pio_sm_set_enabled(pio, sm_irq, true); + + c = blocking_idle_program_get_default_config(offset_trigger); + pio_sm_init(pio, sm_trigger, offset_trigger, &c); + pio_sm_set_enabled(pio, sm_trigger, true); + + pthread_create(&loop_producer, NULL, loop_producer_thread, NULL); + pthread_create(&loop_consumer, NULL, loop_consumer_thread, NULL); + pthread_create(&irq_consumer, NULL, irq_consumer_thread, NULL); + pthread_create(&irq_trigger, NULL, irq_trigger_thread, NULL); + + pthread_join(loop_producer, NULL); + pthread_join(loop_consumer, NULL); + pthread_join(irq_trigger, NULL); + pthread_join(irq_consumer, NULL); + + pio_sm_set_enabled(pio, sm_loop, false); + pio_sm_set_enabled(pio, sm_irq, false); + pio_sm_set_enabled(pio, sm_trigger, false); + + int err = atomic_load(&errors); + printf(err ? "* FAILED, %d error(s)\n" : "PASSED\n", err); + return err ? 1 : 0; +} diff --git a/piolib/examples/blockingtest.pio b/piolib/examples/blockingtest.pio new file mode 100644 index 0000000..1ca1050 --- /dev/null +++ b/piolib/examples/blockingtest.pio @@ -0,0 +1,33 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +; Loops a word from the TX FIFO straight back into the RX FIFO. Run with a +; slow clock divider so that the FIFOs genuinely fill and drain, forcing +; pio_sm_put_blocking()/pio_sm_get_blocking() callers to actually block. +.program blocking_loop +.wrap_target + pull block + mov isr, osr + push block +.wrap + +; Blocks waiting for irq flag 4 to be set (by an "irq set 4" instruction +; injected into some other, otherwise-idle, state machine via +; pio_sm_exec_wait_blocking()), then clears the flag and pushes a word to +; the RX FIFO to signal that it has woken up. +.program blocking_irq +.wrap_target + wait 1 irq 4 + irq clear 4 + push block +.wrap + +; Does nothing but spin, so that it is a valid target for one-off +; instructions injected via pio_sm_exec()/pio_sm_exec_wait_blocking(). +.program blocking_idle +.wrap_target + mov y, y +.wrap diff --git a/piolib/examples/blockingtest.pio.h b/piolib/examples/blockingtest.pio.h new file mode 100644 index 0000000..38bc4d6 --- /dev/null +++ b/piolib/examples/blockingtest.pio.h @@ -0,0 +1,110 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------------- // +// blocking_loop // +// ------------- // + +#define blocking_loop_wrap_target 0 +#define blocking_loop_wrap 2 +#define blocking_loop_pio_version 0 + +static const uint16_t blocking_loop_program_instructions[] = { + // .wrap_target + 0x80a0, // 0: pull block + 0xa0c7, // 1: mov isr, osr + 0x8020, // 2: push block + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program blocking_loop_program = { + .instructions = blocking_loop_program_instructions, + .length = 3, + .origin = -1, + .pio_version = blocking_loop_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config blocking_loop_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + blocking_loop_wrap_target, offset + blocking_loop_wrap); + return c; +} +#endif + +// ------------ // +// blocking_irq // +// ------------ // + +#define blocking_irq_wrap_target 0 +#define blocking_irq_wrap 2 +#define blocking_irq_pio_version 0 + +static const uint16_t blocking_irq_program_instructions[] = { + // .wrap_target + 0x20c4, // 0: wait 1 irq, 4 + 0xc044, // 1: irq clear 4 + 0x8020, // 2: push block + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program blocking_irq_program = { + .instructions = blocking_irq_program_instructions, + .length = 3, + .origin = -1, + .pio_version = blocking_irq_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config blocking_irq_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + blocking_irq_wrap_target, offset + blocking_irq_wrap); + return c; +} +#endif + +// ------------- // +// blocking_idle // +// ------------- // + +#define blocking_idle_wrap_target 0 +#define blocking_idle_wrap 0 +#define blocking_idle_pio_version 0 + +static const uint16_t blocking_idle_program_instructions[] = { + // .wrap_target + 0xa042, // 0: nop + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program blocking_idle_program = { + .instructions = blocking_idle_program_instructions, + .length = 1, + .origin = -1, + .pio_version = blocking_idle_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config blocking_idle_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + blocking_idle_wrap_target, offset + blocking_idle_wrap); + return c; +} +#endif + diff --git a/piolib/examples/genseq.c b/piolib/examples/genseq.c index d22b464..c9b9702 100644 --- a/piolib/examples/genseq.c +++ b/piolib/examples/genseq.c @@ -39,27 +39,28 @@ int main(int argc, const char **argv) { for (i = 0; i < DATA_WORDS; i++) { printf("Iter %d:\n", i); + if (pio_sm_get_rx_fifo_level(pio, sm) != 0) { + printf("Fifo not empty (%d)!\n", pio_sm_get_rx_fifo_level(pio, sm)); + exit(1); + } pio_sm_put_blocking(pio, sm, i); if (use_dma) { ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, (i + 1) * sizeof(databuf[0]), databuf); if (ret) break; - - for (j = i; j >= 0; j--) - { - int v = databuf[i - j]; - if (v != j) - printf(" %d: %d\n", j, v); - } } else { for (j = i; j >= 0; j--) { - int v = pio_sm_get_blocking(pio, sm); - if (v != j) - printf(" %d: %d\n", j, v); + databuf[i - j] = pio_sm_get(pio, sm); } } - sleep_ms(10); + + for (j = i; j >= 0; j--) + { + int v = databuf[i - j]; + if (v != j) + printf(" %d: %d\n", j, v); + } } if (ret) diff --git a/piolib/examples/irqhog.c b/piolib/examples/irqhog.c new file mode 100644 index 0000000..c9b4c30 --- /dev/null +++ b/piolib/examples/irqhog.c @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" + +int main(__unused int argc, __unused const char **argv) { + int pio_irq; + int ret = 0; + + stdio_init_all(); + + PIO pio = pio0; + + pio_irq = pio_get_irq_num(pio, 0); + printf("pio_irq = %d\n", pio_irq); + + while (1) + sleep_ms(1000); + + if (ret) + printf("* error %d\n", ret); + return ret; +} diff --git a/piolib/examples/irqtest.c b/piolib/examples/irqtest.c new file mode 100644 index 0000000..16c2d87 --- /dev/null +++ b/piolib/examples/irqtest.c @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include "gpiolib.h" + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "irqtest.pio.h" + +int sm; + +uint gpio = 4; + +static void irqtest_handler(void *) { + gpio_set_drive(gpio + 3, DRIVE_HIGH); + gpio_set_drive(gpio + 3, DRIVE_LOW); + printf("%d!\n", sm); +} + +int main(int argc, const char **argv) { + int ret = 0; + int pass; + int i; + + stdio_init_all(); + + ret = gpiolib_init(); + + if (ret < 0) + { + printf("Failed to initialise gpiolib - %d\n", ret); + return -1; + } + + ret = gpiolib_mmap(); + if (ret) + { + if (ret == EACCES && geteuid()) + printf("Must be root (or group 'gpio' on RPiOS)\n"); + else + printf("Failed to mmap gpiolib - %s\n", strerror(ret)); + return -1; + } + + PIO pio = pio0; + sm = pio_claim_unused_sm(pio, true); + uint offset = pio_add_program(pio, &irqtest_program); + if (argc == 2) + gpio = (uint)strtoul(argv[1], NULL, 0); + + if (sm == 1) + gpio += 4; + pio_gpio_init(pio, gpio); + pio_gpio_init(pio, gpio + 1); + pio_gpio_init(pio, gpio + 2); + gpio_set_fsel(gpio + 3, GPIO_FSEL_OUTPUT); + pio_sm_set_consecutive_pindirs(pio, sm, gpio, 3, true); + pio_sm_config c = irqtest_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, gpio); + sm_config_set_clkdiv_int_frac(&c, 65535, 0); + + for (pass = 1; pass < 2; pass++) { + int pio_irq = -1; + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + + if (pass == 1) { + pio_irq = pio_get_irq_num(pio, 0); + irq_add_shared_handler(pio_irq, irqtest_handler, PICO_DEFAULT_IRQ_PRIORITY); + pio_set_irqn_source_enabled(pio, 0, pis_interrupt0, true); + irq_set_enabled(pio_irq, true); + } + + for (i = 0; i < 10; i++) { + printf("%d: Iter %d: ", sm, i); + // sleep_ms(500); + pio_sm_put_blocking(pio, sm, i); + // sleep_ms(250); + pio_sm_put_blocking(pio, sm, 10 - i); + while (!pio_interrupt_get(pio, pio_interrupt_rel(sm, 0))) { + //sleep_ms(10); + } + while (!pio_interrupt_get(pio, pio_interrupt_rel(sm, 2))) { + //sleep_ms(10); + } + pio_interrupt_clear(pio, pio_interrupt_rel(sm, 2)); + } + + pio_sm_set_enabled(pio, sm, false); + + if (pio_irq >= 0) { + irq_set_enabled(pio_irq, false); + pio_set_irqn_source_enabled(pio, 0, pis_interrupt0, false); + } + } + + if (ret) + printf("* %d: error %d\n", sm, ret); + return ret; +} diff --git a/piolib/examples/irqtest.pio b/piolib/examples/irqtest.pio new file mode 100755 index 0000000..50f9744 --- /dev/null +++ b/piolib/examples/irqtest.pio @@ -0,0 +1,14 @@ +.program irqtest +.side_set 3 + + pull side 0x0 + mov x, isr side 0x0 + pull side 0x0 + mov y, isr side 0x0 +loop1: + jmp x--, loop1 side 0x1 + irq set 0 rel side 0x2 +loop2: + jmp y--, loop2 side 0x2 + irq wait 2 rel side 0x6 + irq clear 0 rel side 0x2 diff --git a/piolib/examples/irqtest.pio.h b/piolib/examples/irqtest.pio.h new file mode 100644 index 0000000..766d364 --- /dev/null +++ b/piolib/examples/irqtest.pio.h @@ -0,0 +1,51 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------- // +// irqtest // +// ------- // + +#define irqtest_wrap_target 0 +#define irqtest_wrap 8 +#define irqtest_pio_version 0 + +static const uint16_t irqtest_program_instructions[] = { + // .wrap_target + 0x80a0, // 0: pull block side 0 + 0xa026, // 1: mov x, isr side 0 + 0x80a0, // 2: pull block side 0 + 0xa046, // 3: mov y, isr side 0 + 0x0444, // 4: jmp x--, 4 side 1 + 0xc810, // 5: irq nowait 0 rel side 2 + 0x0886, // 6: jmp y--, 6 side 2 + 0xd832, // 7: irq wait 2 rel side 6 + 0xc850, // 8: irq clear 0 rel side 2 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program irqtest_program = { + .instructions = irqtest_program_instructions, + .length = 9, + .origin = -1, + .pio_version = irqtest_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config irqtest_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + irqtest_wrap_target, offset + irqtest_wrap); + sm_config_set_sideset(&c, 3, false, false); + return c; +} +#endif + diff --git a/piolib/examples/irqtest2.c b/piolib/examples/irqtest2.c new file mode 100644 index 0000000..784ae9f --- /dev/null +++ b/piolib/examples/irqtest2.c @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include "gpiolib.h" + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "irqtest2.pio.h" + +int sm; + +uint gpio = 4; + +static void irqtest2_handler(void *) { + gpio_set_drive(gpio + 3, DRIVE_HIGH); + sleep_us(10); + gpio_set_drive(gpio + 3, DRIVE_LOW); + printf("%d", sm); +} + +int main(int argc, const char **argv) { + int pio_irq = -1; + int ret = 0; + int i; + + stdio_init_all(); + + ret = gpiolib_init(); + + if (ret < 0) + { + printf("Failed to initialise gpiolib - %d\n", ret); + return -1; + } + + ret = gpiolib_mmap(); + if (ret) + { + if (ret == EACCES && geteuid()) + printf("Must be root (or group 'gpio' on RPiOS)\n"); + else + printf("Failed to mmap gpiolib - %s\n", strerror(ret)); + return -1; + } + + PIO pio = pio0; + sm = pio_claim_unused_sm(pio, true); + pio_irq = pio_get_irq_num(pio, 0); + uint offset = pio_add_program(pio, &irqtest2_program); + if (argc == 2) + gpio = (uint)strtoul(argv[1], NULL, 0); + else + gpio += pio_irq * 4; + + gpio_set_fsel(gpio + 2, GPIO_FSEL_INPUT); + gpio_set_fsel(gpio + 3, GPIO_FSEL_OUTPUT); + for (i = 0; i < 2; i++) { + int level = (i == 0) ? DRIVE_HIGH : DRIVE_LOW; + gpio_set_drive(gpio + 3, level); + sleep_ms(10); + if (gpio_get_level(gpio + 2) != level) { + printf("please connect GPIOs %d and %d\n", gpio + 2, gpio + 3); + exit(1); + } + } + + pio_gpio_init(pio, gpio); + pio_gpio_init(pio, gpio + 1); + pio_gpio_init(pio, gpio + 2); + pio_sm_set_consecutive_pindirs(pio, sm, gpio, 2, true); + pio_sm_set_consecutive_pindirs(pio, sm, gpio + 2, 1, false); + pio_sm_config c = irqtest2_program_get_default_config(offset); + sm_config_set_in_pins(&c, gpio + 2); + sm_config_set_sideset_pins(&c, gpio); + + pio_sm_init(pio, sm, offset, &c); + + irq_set_handler(pio_irq, irqtest2_handler, NULL); + pio_set_irqn_source_enabled(pio, 0, + pio_interrupt_rel(sm, pis_interrupt0), + true); + irq_set_enabled(pio_irq, true); + pio_sm_clear_fifos(pio, sm); + pio_sm_set_enabled(pio, sm, true); + + for (i = 0; i < 10; i++) { + printf("\n%d: Iter %d: ", sm, i); + // sleep_ms(500); + pio_sm_put_blocking(pio, sm, i); + + while (1) { + sleep_ms(50); + if (!pio_sm_is_rx_fifo_empty(pio, sm)) + break; + printf("!"); + fflush(stdout); + gpio_set_drive(gpio + 3, DRIVE_HIGH); + gpio_set_drive(gpio + 3, DRIVE_LOW); + } + pio_sm_get_blocking(pio, sm); + } + printf("\n"); + + pio_sm_set_enabled(pio, sm, false); + + if (pio_irq >= 0) { + irq_set_enabled(pio_irq, false); + pio_set_irqn_source_enabled(pio, 0, + pio_interrupt_rel(sm, pis_interrupt0), + false); + } + + if (ret) + printf("* %d: error %d\n", sm, ret); + return ret; +} diff --git a/piolib/examples/irqtest2.pio b/piolib/examples/irqtest2.pio new file mode 100755 index 0000000..ef77403 --- /dev/null +++ b/piolib/examples/irqtest2.pio @@ -0,0 +1,12 @@ +.program irqtest2 +.side_set 2 + + pull block side 0x0 + mov x, isr side 0x0 +loop: + irq set 0 rel side 0x1 + irq clear 0 rel side 0x3 + wait 1 pin 0 side 0x2 + wait 0 pin 0 side 0x0 + jmp x--, loop side 0x0 + push block side 0x0 diff --git a/piolib/examples/irqtest2.pio.h b/piolib/examples/irqtest2.pio.h new file mode 100644 index 0000000..b0a09ae --- /dev/null +++ b/piolib/examples/irqtest2.pio.h @@ -0,0 +1,50 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// -------- // +// irqtest2 // +// -------- // + +#define irqtest2_wrap_target 0 +#define irqtest2_wrap 7 +#define irqtest2_pio_version 0 + +static const uint16_t irqtest2_program_instructions[] = { + // .wrap_target + 0x80a0, // 0: pull block side 0 + 0xa026, // 1: mov x, isr side 0 + 0xc810, // 2: irq nowait 0 rel side 1 + 0xd850, // 3: irq clear 0 rel side 3 + 0x30a0, // 4: wait 1 pin, 0 side 2 + 0x2020, // 5: wait 0 pin, 0 side 0 + 0x0042, // 6: jmp x--, 2 side 0 + 0x8020, // 7: push block side 0 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program irqtest2_program = { + .instructions = irqtest2_program_instructions, + .length = 8, + .origin = -1, + .pio_version = irqtest2_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config irqtest2_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + irqtest2_wrap_target, offset + irqtest2_wrap); + sm_config_set_sideset(&c, 2, false, false); + return c; +} +#endif + diff --git a/piolib/examples/rp1sm.c b/piolib/examples/rp1sm.c index 5cbe583..d30e9ba 100644 --- a/piolib/examples/rp1sm.c +++ b/piolib/examples/rp1sm.c @@ -4,7 +4,7 @@ #include #include -#include "pio_platform.h" +#include "piolib.h" #include "rp1_pio_if.h" #define min(a,b) \ @@ -40,21 +40,22 @@ int main(int argc, const char **argv) const char *devname = "/dev/pio0"; struct rp1_access_hw_args args; uint32_t databuf[64]; - uint sm; + uint sm = ~0; int ret; int fd; - if (argc < 2) + if (argc > 2) usage(); fd = open(devname, O_RDWR, O_CLOEXEC); if (fd < 0) return -errno; - sm = (uint)strtoul(argv[1], NULL, 16); + if (argc > 1) + sm = (uint)strtoul(argv[1], NULL, 16); args.addr = 0xf0000000; - args.len = 0x20; + args.len = 20; args.data = databuf; ret = ioctl(fd, PIO_IOC_READ_HW, &args); @@ -63,45 +64,82 @@ int main(int argc, const char **argv) uint ctrl = databuf[0]; uint fstat = databuf[1]; + uint fdebug = databuf[2]; uint flevel = databuf[3]; uint flevel2 = databuf[4]; - args.addr = 0xf00000cc + sm * 0x20; - args.len = 0x20; + args.addr = 0xf0000034; + args.len = 8; args.data = databuf; ret = ioctl(fd, PIO_IOC_READ_HW, &args); if (ret < 0) return ret; - uint clkdiv = databuf[0]; - uint execctrl = databuf[1]; - uint shiftctrl = databuf[2]; - uint pc = databuf[3]; - uint instr = databuf[4]; - uint pinctrl = databuf[5]; - uint dmactrl_tx = databuf[6]; - uint dmactrl_rx = databuf[7]; - - printf("enabled: %d\n", (ctrl >> sm) & 1); - printf("clkdiv: %08x\n", clkdiv); - printf("execctrl: %08x\n", execctrl); - printf("shiftctrl: %08x\n", shiftctrl); - printf("pc: %08x\n", pc); - printf("instr: %08x\n", instr); - printf("pinctrl: %08x\n", pinctrl); - printf("dmactrl_tx: %08x\n", dmactrl_tx); - printf("dmactrl_rx: %08x\n", dmactrl_rx); - - printf("TX fifo: level %x, flags %c%c\n", - ((flevel >> (sm * 8)) & 0xf) + (((flevel2 >> (sm * 8)) & 1) << 4), - (fstat & (0x10000 << sm)) ? 'F' : 'f', - (fstat & (0x1000000 << sm)) ? 'E' : 'e'); - - printf("RX fifo: level %x, flags %c%c\n", - ((flevel >> (sm * 8 + 4)) & 0xf) + (((flevel2 >> (sm * 8 + 4)) & 1) << 4), - (fstat & (0x1 << sm)) ? 'F' : 'f', - (fstat & (0x100 << sm)) ? 'E' : 'e'); + uint irq = databuf[0]; + uint irq_force = databuf[1]; + + if (sm < MAX_PIO_SMS) { + args.addr = 0xf00000cc + sm * 0x20; + args.len = 0x20; + args.data = databuf; + + ret = ioctl(fd, PIO_IOC_READ_HW, &args); + if (ret < 0) + return ret; + + uint clkdiv = databuf[0]; + uint execctrl = databuf[1]; + uint shiftctrl = databuf[2]; + uint pc = databuf[3]; + uint instr = databuf[4]; + uint pinctrl = databuf[5]; + uint dmactrl_tx = databuf[6]; + uint dmactrl_rx = databuf[7]; + + printf("enabled: %d\n", (ctrl >> sm) & 1); + printf("clkdiv: %08x\n", clkdiv); + printf("execctrl: %08x\n", execctrl); + printf("shiftctrl: %08x\n", shiftctrl); + printf("pc: %08x\n", pc); + printf("instr: %08x\n", instr); + printf("pinctrl: %08x\n", pinctrl); + printf("dmactrl_tx: %08x\n", dmactrl_tx); + printf("dmactrl_rx: %08x\n", dmactrl_rx); + + printf("TX fifo: level %x, flags %c%c\n", + ((flevel >> (sm * 8)) & 0xf) + (((flevel2 >> (sm * 8)) & 1) << 4), + (fstat & (0x10000 << sm)) ? 'F' : 'f', + (fstat & (0x1000000 << sm)) ? 'E' : 'e'); + + printf("RX fifo: level %x, flags %c%c\n", + ((flevel >> (sm * 8 + 4)) & 0xf) + (((flevel2 >> (sm * 8 + 4)) & 1) << 4), + (fstat & (0x1 << sm)) ? 'F' : 'f', + (fstat & (0x100 << sm)) ? 'E' : 'e'); + } else { + args.addr = 0xf000014c; + args.len = 0x1c; + args.data = databuf; + + ret = ioctl(fd, PIO_IOC_READ_HW, &args); + if (ret < 0) + return ret; + + uint intr = databuf[0]; + uint inte0 = databuf[1]; + uint ints0 = databuf[3]; + uint inte1 = databuf[4]; + uint ints1 = databuf[6]; + + printf("fifodbg: %08x\n", fdebug); + printf("irq: %08x\n", irq); + printf("irq_force: %08x\n", irq_force); + printf("intr: %08x\n", intr); + printf("inte0: %08x\n", inte0); + printf("ints0: %08x\n", ints0); + printf("inte1: %08x\n", inte1); + printf("ints1: %08x\n", ints1); + } close(fd); diff --git a/piolib/examples/rxsource.c b/piolib/examples/rxsource.c new file mode 100644 index 0000000..6c8ec23 --- /dev/null +++ b/piolib/examples/rxsource.c @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "rxsource.pio.h" + +#define DATA_WORDS 16 + +int main(__unused int argc, __unused const char **argv) { + uint32_t databuf[DATA_WORDS]; + uint gpio = 2; + int ret = 0; + int i, j; + stdio_init_all(); + + PIO pio = pio0; + int sm = pio_claim_unused_sm(pio, true); + uint offset = pio_add_program(pio, &rxsource_program); + printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); + + pio_sm_config_xfer(pio, sm, PIO_DIR_FROM_SM, sizeof(databuf), 4); + + pio_gpio_init(pio, gpio); + pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); + pio_sm_config c = rxsource_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, gpio); + sm_config_set_clkdiv(&c, 1); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + + // Read ahead as much as possible + for (j = 0; j < 4; j++) { + ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, sizeof(databuf), NULL); + if (ret) + break; + } + for (i = 0; i < 1000; i++) { + ret = pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, sizeof(databuf), databuf); + if (ret) + break; + if (j < 1000) { + pio_sm_xfer_data(pio, sm, PIO_DIR_FROM_SM, sizeof(databuf), NULL); + j++; + } + } + + if (ret) + printf("* error %d\n", ret); + return ret; +} diff --git a/piolib/examples/rxsource.pio b/piolib/examples/rxsource.pio new file mode 100755 index 0000000..9624cd2 --- /dev/null +++ b/piolib/examples/rxsource.pio @@ -0,0 +1,5 @@ +.program rxsource +.side_set 1 + + push side 0x1 + nop side 0x0 diff --git a/piolib/examples/rxsource.pio.h b/piolib/examples/rxsource.pio.h new file mode 100644 index 0000000..3f55960 --- /dev/null +++ b/piolib/examples/rxsource.pio.h @@ -0,0 +1,44 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// -------- // +// rxsource // +// -------- // + +#define rxsource_wrap_target 0 +#define rxsource_wrap 1 +#define rxsource_pio_version 0 + +static const uint16_t rxsource_program_instructions[] = { + // .wrap_target + 0x9020, // 0: push block side 1 + 0xa042, // 1: nop side 0 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program rxsource_program = { + .instructions = rxsource_program_instructions, + .length = 2, + .origin = -1, + .pio_version = rxsource_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config rxsource_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + rxsource_wrap_target, offset + rxsource_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} +#endif + diff --git a/piolib/examples/txcount.c b/piolib/examples/txcount.c new file mode 100644 index 0000000..e4a3cba --- /dev/null +++ b/piolib/examples/txcount.c @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "txcount.pio.h" + +#define DATA_WORDS 100 + +int main(__unused int argc, __unused const char **argv) { + uint32_t databuf[DATA_WORDS]; + bool use_dma = true; + uint gpio = 2; + int ret = 0; + int i, j; + stdio_init_all(); + + PIO pio = pio0; + int sm = pio_claim_unused_sm(pio, true); + uint offset = pio_add_program(pio, &txcount_program); + uint32_t last_count = 0; + printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); + + pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, 256, 1); + + pio_gpio_init(pio, gpio); + pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); + pio_sm_config c = txcount_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, gpio); + sm_config_set_clkdiv(&c, 1); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + + for (i = 1; i <= DATA_WORDS; i++) { + uint32_t count; + int diff; + printf("Iter %d:\n", i); + if (use_dma) { + ret = pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, i * sizeof(databuf[0]), databuf); + if (ret) + break; + } else { + for (j = 0; j < i; j++) + { + pio_sm_put_blocking(pio, sm, databuf[i]); + } + } + + while (!pio_sm_is_rx_fifo_empty(pio, sm)) + pio_sm_get(pio, sm); + pio_sm_put_blocking(pio, sm, 0); + count = pio_sm_get_blocking(pio, sm); + diff = -(count + 1 - last_count); + if (diff != i) { + printf("%d -> %d\n", i, diff); + //return -1; + } + last_count = count; + } + + if (ret) + printf("* error %d\n", ret); + return ret; +} diff --git a/piolib/examples/txcount.pio b/piolib/examples/txcount.pio new file mode 100644 index 0000000..b9472e0 --- /dev/null +++ b/piolib/examples/txcount.pio @@ -0,0 +1,20 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; +.pio_version 0 // only requires PIO version 0 + +.program txcount +.side_set 1 + +.wrap_target + set x, 0 side 0 +loop: + pull block side 0 + jmp x--, no_jmp side 1 +no_jmp: + mov isr, x side 0 + push noblock side 0 + jmp loop side 0 +.wrap diff --git a/piolib/examples/txcount.pio.h b/piolib/examples/txcount.pio.h new file mode 100644 index 0000000..4442739 --- /dev/null +++ b/piolib/examples/txcount.pio.h @@ -0,0 +1,48 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------- // +// txcount // +// ------- // + +#define txcount_wrap_target 0 +#define txcount_wrap 5 +#define txcount_pio_version 0 + +static const uint16_t txcount_program_instructions[] = { + // .wrap_target + 0xe020, // 0: set x, 0 side 0 + 0x80a0, // 1: pull block side 0 + 0x1043, // 2: jmp x--, 3 side 1 + 0xa0c1, // 3: mov isr, x side 0 + 0x8000, // 4: push noblock side 0 + 0x0001, // 5: jmp 1 side 0 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program txcount_program = { + .instructions = txcount_program_instructions, + .length = 6, + .origin = -1, + .pio_version = txcount_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config txcount_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + txcount_wrap_target, offset + txcount_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} +#endif + diff --git a/piolib/examples/txsink.c b/piolib/examples/txsink.c new file mode 100644 index 0000000..c43b9d2 --- /dev/null +++ b/piolib/examples/txsink.c @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "hardware/pio.h" +#include "txsink.pio.h" + +#define DATA_WORDS 16 + +int main(__unused int argc, __unused const char **argv) { + uint32_t databuf[DATA_WORDS]; + uint gpio = 2; + int ret = 0; + int i; + stdio_init_all(); + + PIO pio = pio0; + int sm = pio_claim_unused_sm(pio, true); + uint offset = pio_add_program(pio, &txsink_program); + printf("Loaded program at %d, using sm %d, gpio %d\n", offset, sm, gpio); + + pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, 256, 4); + + pio_gpio_init(pio, gpio); + pio_sm_set_consecutive_pindirs(pio, sm, gpio, 1, true); + pio_sm_config c = txsink_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, gpio); + sm_config_set_clkdiv(&c, 1); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + + for (i = 0; i < 1000; i++) { + ret = pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, sizeof(databuf), databuf); + if (ret) + break; + } + + if (ret) + printf("* error %d\n", ret); + return ret; +} diff --git a/piolib/examples/txsink.pio b/piolib/examples/txsink.pio new file mode 100755 index 0000000..9f81294 --- /dev/null +++ b/piolib/examples/txsink.pio @@ -0,0 +1,5 @@ +.program txsink +.side_set 1 + + pull side 0x1 + nop side 0x0 diff --git a/piolib/examples/txsink.pio.h b/piolib/examples/txsink.pio.h new file mode 100644 index 0000000..3b0bb9b --- /dev/null +++ b/piolib/examples/txsink.pio.h @@ -0,0 +1,44 @@ +// ---------------------------------------------------------------- // +// This file is autogenerated by pioasm version 2.2.0; do not edit! // +// ---------------------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// txsink // +// ------ // + +#define txsink_wrap_target 0 +#define txsink_wrap 1 +#define txsink_pio_version 0 + +static const uint16_t txsink_program_instructions[] = { + // .wrap_target + 0x90a0, // 0: pull block side 1 + 0xa042, // 1: nop side 0 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program txsink_program = { + .instructions = txsink_program_instructions, + .length = 2, + .origin = -1, + .pio_version = txsink_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config txsink_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + txsink_wrap_target, offset + txsink_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} +#endif + diff --git a/piolib/include/hardware/gpio.h b/piolib/include/hardware/gpio.h index 7cdb46a..aa15262 100644 --- a/piolib/include/hardware/gpio.h +++ b/piolib/include/hardware/gpio.h @@ -56,6 +56,15 @@ enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_12MA = 3 ///< 12 mA nominal drive strength }; +enum pio_sm_flags { + PIO_SM_FLAG_TXSTALL = 0x0001, + PIO_SM_FLAG_RXSTALL = 0x0002, + PIO_SM_FLAG_TXOVER = 0x0004, + PIO_SM_FLAG_RXUNDER = 0x0008, + + PIO_SM_FLAG_ALL = 0x000f, +}; + static inline void check_gpio_param(__unused uint gpio) { invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); } diff --git a/piolib/include/piolib.h b/piolib/include/piolib.h index 3756aab..73b5224 100644 --- a/piolib/include/piolib.h +++ b/piolib/include/piolib.h @@ -26,8 +26,28 @@ extern "C" { #define PIO_ORIGIN_ANY ((uint)(~0)) #define PIO_ORIGIN_INVALID PIO_ORIGIN_ANY +#define MAX_PIO_SMS 4 +#define NUM_PIO_IRQS 2 + +#ifndef NDEBUG +#define _PIO_INVALID_IN_SRC 0x08u +#define _PIO_INVALID_OUT_DEST 0x10u +#define _PIO_INVALID_SET_DEST 0x20u +#define _PIO_INVALID_MOV_SRC 0x40u +#define _PIO_INVALID_MOV_DEST 0x80u +#else +#define _PIO_INVALID_IN_SRC 0u +#define _PIO_INVALID_OUT_DEST 0u +#define _PIO_INVALID_SET_DEST 0u +#define _PIO_INVALID_MOV_SRC 0u +#define _PIO_INVALID_MOV_DEST 0u +#endif + #define pio0 pio_open_helper(0) +#define irq_add_shared_handler(num, handler, order_priority) \ + irq_set_handler(num, handler, 0) + enum pio_fifo_join { PIO_FIFO_JOIN_NONE = 0, PIO_FIFO_JOIN_TX = 1, @@ -59,20 +79,6 @@ enum pio_instr_bits { pio_instr_bits_set = 0xe000, }; -#ifndef NDEBUG -#define _PIO_INVALID_IN_SRC 0x08u -#define _PIO_INVALID_OUT_DEST 0x10u -#define _PIO_INVALID_SET_DEST 0x20u -#define _PIO_INVALID_MOV_SRC 0x40u -#define _PIO_INVALID_MOV_DEST 0x80u -#else -#define _PIO_INVALID_IN_SRC 0u -#define _PIO_INVALID_OUT_DEST 0u -#define _PIO_INVALID_SET_DEST 0u -#define _PIO_INVALID_MOV_SRC 0u -#define _PIO_INVALID_MOV_DEST 0u -#endif - enum pio_src_dest { pio_pins = 0u, pio_x = 1u, @@ -103,12 +109,15 @@ typedef struct { typedef struct pio_instance *PIO; typedef const struct pio_chip PIO_CHIP_T; +typedef void (*irq_handler_t)(void *); + struct pio_chip { const char *name; const char *compatible; uint16_t instr_count; uint16_t sm_count; uint16_t fifo_depth; + uint16_t irq_count; void *hw_state; PIO (*create_instance)(PIO_CHIP_T *chip, uint index); @@ -175,13 +184,15 @@ struct pio_chip { void (*pio_sm_put)(PIO pio, uint sm, uint32_t data, bool blocking); uint32_t (*pio_sm_get)(PIO pio, uint sm, bool blocking); void (*pio_sm_set_dmactrl)(PIO pio, uint sm, bool is_tx, uint32_t ctrl); + uint32_t (*pio_sm_get_dmactrl)(PIO pio, uint sm, bool is_tx); + uint32_t (*pio_sm_get_flags)(PIO pio, uint sm, uint32_t flags, bool clear, uint32_t timeout); + void (*pio_sm_drain_tx_fifo)(PIO pio, uint sm); bool (*pio_sm_is_rx_fifo_empty)(PIO pio, uint sm); bool (*pio_sm_is_rx_fifo_full)(PIO pio, uint sm); uint (*pio_sm_get_rx_fifo_level)(PIO pio, uint sm); bool (*pio_sm_is_tx_fifo_empty)(PIO pio, uint sm); bool (*pio_sm_is_tx_fifo_full)(PIO pio, uint sm); uint (*pio_sm_get_tx_fifo_level)(PIO pio, uint sm); - void (*pio_sm_drain_tx_fifo)(PIO pio, uint sm); pio_sm_config (*pio_get_default_sm_config)(PIO pio); void (*smc_set_out_pins)(PIO pio, pio_sm_config *c, uint out_base, uint out_count); @@ -209,11 +220,22 @@ struct pio_chip { void (*gpio_set_oeover)(PIO pio, uint gpio, uint value); void (*gpio_set_input_enabled)(PIO pio, uint gpio, bool enabled); void (*gpio_set_drive_strength)(PIO pio, uint gpio, enum gpio_drive_strength drive); + + void (*set_irqn_source_mask_enabled)(PIO pio, uint irq_index, uint32_t source_mask, bool enabled); + void (*irq_set_enabled)(PIO pio, uint num, bool enabled); + bool (*irq_is_enabled)(PIO pio, uint num); + bool (*pio_interrupt_get)(PIO pio, uint pio_interrupt_num); + void (*pio_interrupt_clear)(PIO pio, uint pio_interrupt_num); + int (*pio_irq_claim)(PIO pio); + uint32_t (*pio_irq_wait)(PIO pio, uint timeout_ms); }; struct pio_instance { const PIO_CHIP_T *chip; int in_use; + uint irq_base; + uint irq_count; + int irqs[NUM_PIO_IRQS]; bool errors_are_fatal; bool error; }; @@ -228,6 +250,12 @@ int pio_get_index(PIO pio); void pio_select(PIO pio); PIO pio_get_current(void); +void irq_set_handler(uint num, irq_handler_t handler, void *context); +void irq_remove_handler(uint num, irq_handler_t handler); +irq_handler_t irq_get_handler(uint num); +void irq_set_enabled(uint num, bool enabled); +bool irq_is_enabled(uint num); + static inline void pio_error(PIO pio, const char *msg) { pio->error = true; @@ -627,6 +655,30 @@ static inline void pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctr pio->chip->pio_sm_set_dmactrl(pio, sm, is_tx, ctrl); }; +static inline uint32_t pio_sm_get_dmactrl(PIO pio, uint sm, bool is_tx) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get_dmactrl(pio, sm, is_tx); +}; + +static inline uint32_t pio_sm_wait_flags(PIO pio, uint sm, uint32_t flags, bool clear, uint32_t timeout) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get_flags(pio, sm, flags, clear, timeout); +} + +static inline uint32_t pio_sm_get_flags(PIO pio, uint sm, uint32_t flags, bool clear) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get_flags(pio, sm, flags, clear, 0); +} + +static inline void pio_sm_clear_flags(PIO pio, uint sm, uint32_t flags) +{ + check_pio_param(pio); + (void)pio->chip->pio_sm_get_flags(pio, sm, flags, true, 0); +} + static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm) { check_pio_param(pio); @@ -862,6 +914,120 @@ static inline void gpio_disable_pulls(uint gpio) { gpio_set_pulls(gpio, false, false); } +typedef enum pio_interrupt_source +{ + pis_sm0_rx_fifo_not_empty, + pis_sm1_rx_fifo_not_empty, + pis_sm2_rx_fifo_not_empty, + pis_sm3_rx_fifo_not_empty, + pis_sm0_tx_fifo_not_full, + pis_sm1_tx_fifo_not_full, + pis_sm2_tx_fifo_not_full, + pis_sm3_tx_fifo_not_full, + pis_interrupt0, + pis_interrupt1, + pis_interrupt2, + pis_interrupt3, + + pis_max +} pio_interrupt_source_t; + +uint pio_irq_map(PIO pio, uint irq_index); + +static inline int pio_get_irq_num(PIO pio, uint irqn) { + return pio->irq_base + pio_irq_map(pio, irqn); +} + +static inline void irq_set_exclusive_handler(uint num, irq_handler_t handler) +{ + irq_set_handler(num, handler, 0); +} + +static inline bool irq_has_handler(uint num) +{ + return !!irq_get_handler(num); +} + +static inline bool irq_has_shared_handler(uint num) +{ + return !!irq_get_handler(num); +} + +static inline irq_handler_t irq_get_exclusive_handler(uint num) +{ + return (void *)irq_get_handler(num); +} + +static inline void pio_set_irqn_source_mask_enabled(PIO pio, uint irq_index, + uint32_t source_mask, bool enabled) +{ + uint pirq = pio_irq_map(pio, irq_index); + pio->chip->set_irqn_source_mask_enabled(pio, pirq, source_mask, enabled); +} + +static inline void pio_set_irq0_source_enabled(PIO pio, pio_interrupt_source_t source, + bool enabled) +{ + invalid_params_if(PIO, source >= pis_max); + pio_set_irqn_source_mask_enabled(pio, 0, 1 << source, enabled); +} + +static inline void pio_set_irq1_source_enabled(PIO pio, pio_interrupt_source_t source, + bool enabled) +{ + invalid_params_if(PIO, source >= pis_max); + pio_set_irqn_source_mask_enabled(pio, 1, 1 << source, enabled); +} + +static inline void pio_set_irq0_source_mask_enabled(PIO pio, uint32_t source_mask, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(pio, 0, source_mask, enabled); +} + +static inline void pio_set_irq1_source_mask_enabled(PIO pio, uint32_t source_mask, + bool enabled) +{ + pio_set_irqn_source_mask_enabled(pio, 1, source_mask, enabled); +} + +static inline void pio_set_irqn_source_enabled(PIO pio, uint irq_index, + pio_interrupt_source_t source, + bool enabled) +{ + invalid_params_if(PIO, source >= pis_max); + pio_set_irqn_source_mask_enabled(pio, irq_index, 1 << source, enabled); +} + +static inline bool pio_interrupt_get(PIO pio, uint pio_interrupt_num) +{ + check_pio_param(pio); + invalid_params_if(PIO, pio_interrupt_num >= 8); + return pio->chip->pio_interrupt_get(pio, pio_interrupt_num); +} + +static inline void pio_interrupt_clear(PIO pio, uint pio_interrupt_num) +{ + check_pio_param(pio); + invalid_params_if(PIO, pio_interrupt_num >= 8); + pio->chip->pio_interrupt_clear(pio, pio_interrupt_num); +} + +static inline pio_interrupt_source_t pio_get_tx_fifo_not_full_interrupt_source(uint sm) +{ + invalid_params_if(PIO, sm >= MAX_PIO_SMS); + return ((pio_interrupt_source_t)(pis_sm0_tx_fifo_not_full + sm)); +} + +static inline pio_interrupt_source_t pio_get_rx_fifo_not_empty_interrupt_source(uint sm) { + invalid_params_if(PIO, sm >= MAX_PIO_SMS); + return ((pio_interrupt_source_t)(pis_sm0_rx_fifo_not_empty + sm)); +} + +static inline uint pio_interrupt_rel(uint sm, uint pio_interrupt_num) { + return (pio_interrupt_num & 0x1c) | ((pio_interrupt_num + sm) & 0x3); +} + static inline void stdio_init_all(void) { } diff --git a/piolib/include/rp1_pio_if.h b/piolib/include/rp1_pio_if.h index b92f7dc..b0dd91c 100644 --- a/piolib/include/rp1_pio_if.h +++ b/piolib/include/rp1_pio_if.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause /* - * Copyright (c) 2023-24 Raspberry Pi Ltd. + * Copyright (c) 2023-26 Raspberry Pi Ltd. * All rights reserved. */ #ifndef _RP1_PIO_IF_H @@ -12,6 +12,7 @@ #define RP1_PIO_SM_COUNT 4 #define RP1_PIO_GPIO_COUNT 28 #define RP1_GPIO_FUNC_PIO 7 +#define RP1_PIO_IRQ_COUNT 2 #define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) @@ -124,13 +125,28 @@ struct rp1_pio_sm_set_dmactrl_args { uint32_t ctrl; }; +struct rp1_pio_sm_get_dmactrl_args { + uint16_t sm; + uint8_t is_tx; + uint8_t rsvd; + uint32_t ctrl; /* OUT */ +}; + struct rp1_pio_sm_fifo_state_args { - uint16_t sm; - uint8_t tx; - uint8_t rsvd; - uint16_t level; /* OUT */ - uint8_t empty; /* OUT */ - uint8_t full; /* OUT */ + uint16_t sm; + uint8_t tx; + uint8_t rsvd; + uint16_t level; /* OUT */ + uint8_t empty; /* OUT */ + uint8_t full; /* OUT */ +}; + +struct rp1_pio_sm_get_flags_args { + uint16_t sm; + uint8_t clear; + uint8_t rsvd; + uint32_t flags; /* IN/OUT */ + uint32_t timeout; }; struct rp1_gpio_init_args { @@ -188,6 +204,38 @@ struct rp1_access_hw_args { void *data; }; +struct rp1_pio_irq_claim_args { + int irq_index; /* OUT */ +}; + +struct rp1_pio_irq_wait_args { + uint32_t timeout_ms; + uint32_t active_mask; /* OUT */ +}; + +struct rp1_pio_irq_set_enabled_args { + uint16_t irq_index; + uint8_t enabled; + uint8_t rsvd; +}; + +struct rp1_pio_set_irqn_source_mask_enabled_args { + uint16_t irq_index; + uint8_t enabled; + uint8_t rsvd; + uint32_t source_mask; +}; + +struct rp1_pio_interrupt_get_args { + uint16_t pio_interrupt_num; + uint16_t rsvd; + uint8_t active; /* OUT */ +}; + +struct rp1_pio_interrupt_clear_args { + uint16_t pio_interrupt_num; +}; + #define PIO_IOC_MAGIC 102 #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) @@ -221,8 +269,10 @@ struct rp1_access_hw_args { #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) -#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) +#define PIO_IOC_SM_FIFO_STATE _IOWR(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) #define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) +#define PIO_IOC_SM_GET_FLAGS _IOWR(PIO_IOC_MAGIC, 46, struct rp1_pio_sm_get_flags_args) +#define PIO_IOC_SM_GET_DMACTRL _IOWR(PIO_IOC_MAGIC, 47, struct rp1_pio_sm_get_dmactrl_args) #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) @@ -233,4 +283,13 @@ struct rp1_access_hw_args { #define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) #define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) +#define PIO_IOC_IRQ_CLAIM _IOWR(PIO_IOC_MAGIC, 60, struct rp1_pio_irq_claim_args) +#define PIO_IOC_IRQ_WAIT _IOWR(PIO_IOC_MAGIC, 61, struct rp1_pio_irq_wait_args) +#define PIO_IOC_IRQ_SET_ENABLED _IOW(PIO_IOC_MAGIC, 62, struct rp1_pio_irq_set_enabled_args) +#define PIO_IOC_IRQ_IS_ENABLED _IOWR(PIO_IOC_MAGIC, 63, struct rp1_pio_irq_set_enabled_args) +#define PIO_IOC_SET_IRQN_SOURCE_MASK_ENABLED \ + _IOW(PIO_IOC_MAGIC, 64, struct rp1_pio_set_irqn_source_mask_enabled_args) +#define PIO_IOC_INTERRUPT_GET _IOWR(PIO_IOC_MAGIC, 65, struct rp1_pio_interrupt_get_args) +#define PIO_IOC_INTERRUPT_CLEAR _IOW(PIO_IOC_MAGIC, 66, struct rp1_pio_interrupt_clear_args) + #endif diff --git a/piolib/pio_rp1.c b/piolib/pio_rp1.c index 1150f90..7f15ecf 100644 --- a/piolib/pio_rp1.c +++ b/piolib/pio_rp1.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -76,10 +77,15 @@ #include "hardware/regs/proc_pio.h" #include "rp1_pio_if.h" +#define NUM_SM_IRQS (PROC_PIO_IRQ_MSB - PROC_PIO_IRQ_LSB + 1) + typedef struct rp1_pio_handle { struct pio_instance base; const char *devname; int fd; + bool irq_thread_running; + pthread_mutex_t lock; + pthread_t irq_thread; } *RP1_PIO; #define smc_to_rp1(_config, _c) rp1_pio_sm_config *_c = (rp1_pio_sm_config*)_config @@ -382,7 +388,8 @@ static void rp1_pio_sm_exec(PIO pio, uint sm, uint instr, bool blocking) struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = blocking }; check_sm_param(sm); - (void)rp1_ioctl(pio, PIO_IOC_SM_EXEC, &args); + if (rp1_ioctl(pio, PIO_IOC_SM_EXEC, &args) < 0) + pio_panic("Failed to exec PIO instruction"); } static void rp1_pio_sm_clear_fifos(PIO pio, uint sm) @@ -517,7 +524,8 @@ static void rp1_pio_sm_put(PIO pio, uint sm, uint32_t data, bool blocking) struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = blocking, .data = data }; check_sm_param(sm); - (void)rp1_ioctl(pio, PIO_IOC_SM_PUT, &args); + if (rp1_ioctl(pio, PIO_IOC_SM_PUT, &args) < 0) + pio_panic("Failed to put PIO FIFO data"); } static uint32_t rp1_pio_sm_get(PIO pio, uint sm, bool blocking) @@ -525,7 +533,8 @@ static uint32_t rp1_pio_sm_get(PIO pio, uint sm, bool blocking) struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = blocking }; check_sm_param(sm); - (void)rp1_ioctl(pio, PIO_IOC_SM_GET, &args); + if (rp1_ioctl(pio, PIO_IOC_SM_GET, &args) < 0) + pio_panic("Failed to get PIO FIFO data"); return args.data; } @@ -537,6 +546,30 @@ static void rp1_pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl) (void)rp1_ioctl(pio, PIO_IOC_SM_SET_DMACTRL, &args); } +static uint32_t rp1_pio_sm_get_dmactrl(PIO pio, uint sm, bool is_tx) +{ + struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = 0 }; + + check_sm_param(sm); + + if (rp1_ioctl(pio, PIO_IOC_SM_GET_DMACTRL, &args) < 0) + pio_panic("Failed to get DMACTRL"); + + return args.ctrl; +} + +static uint32_t rp1_pio_sm_get_flags(PIO pio, uint sm, uint32_t flags, bool clear, uint32_t timeout) +{ + struct rp1_pio_sm_get_flags_args args = { .sm = sm, .flags = flags, .clear = clear, .timeout = timeout }; + + check_sm_param(sm); + + if (rp1_ioctl(pio, PIO_IOC_SM_GET_FLAGS, &args) < 0) + pio_panic("Failed to get FIFO flags"); + + return args.flags; +} + static bool rp1_pio_sm_is_rx_fifo_empty(PIO pio, uint sm) { struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; @@ -822,6 +855,88 @@ static void rp1_pio_gpio_init(PIO pio, uint pin) rp1_gpio_set_function(pio, pin, RP1_GPIO_FUNC_PIO); } +static void rp1_pio_irq_set_enabled(PIO pio, uint irq_index, bool enabled) +{ + struct rp1_pio_irq_set_enabled_args args = { .irq_index = irq_index, .enabled = enabled }; + valid_params_if(PIO, irq_index < RP1_PIO_IRQ_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_IRQ_SET_ENABLED, &args); +} + +static bool rp1_pio_irq_is_enabled(PIO pio, uint irq_index) +{ + struct rp1_pio_irq_set_enabled_args args = { .irq_index = irq_index }; + valid_params_if(PIO, irq_index < RP1_PIO_IRQ_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_IRQ_IS_ENABLED, &args); + return args.enabled; +} + +static void rp1_pio_set_irqn_source_mask_enabled(PIO pio, uint irq_index, uint32_t source_mask, bool enabled) +{ + struct rp1_pio_set_irqn_source_mask_enabled_args args = + { .irq_index = irq_index, .source_mask = source_mask, .enabled = enabled }; + valid_params_if(PIO, irq_index < RP1_PIO_IRQ_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_SET_IRQN_SOURCE_MASK_ENABLED, &args); +} + +static bool rp1_pio_interrupt_get(PIO pio, uint pio_interrupt_num) +{ + struct rp1_pio_interrupt_get_args args = { .pio_interrupt_num = pio_interrupt_num }; + invalid_params_if(PIO, pio_interrupt_num >= NUM_SM_IRQS); + (void)rp1_ioctl(pio, PIO_IOC_INTERRUPT_GET, &args); + return !!args.active; +} + +static void rp1_pio_interrupt_clear(PIO pio, uint pio_interrupt_num) +{ + struct rp1_pio_interrupt_clear_args args = { .pio_interrupt_num = pio_interrupt_num }; + invalid_params_if(PIO, pio_interrupt_num >= NUM_SM_IRQS); + (void)rp1_ioctl(pio, PIO_IOC_INTERRUPT_CLEAR, &args); +} + +static void *irq_wait_thread(void *arg) { + RP1_PIO rp = (RP1_PIO)arg; + struct rp1_pio_irq_wait_args args; + + args.timeout_ms = 0; + while (1) { + rp1_ioctl(&rp->base, PIO_IOC_IRQ_WAIT, &args); + if (!args.active_mask) + break; + printf("woke %x\n", args.active_mask); + } + + return NULL; +} + +static void rp1_pio_irq_start_thread(PIO pio) { + RP1_PIO rp = (RP1_PIO)pio; + pthread_mutex_lock(&rp->lock); + if (!rp->irq_thread_running) { + if (pthread_create(&rp->irq_thread, NULL, irq_wait_thread, rp)) + pio_panic("Failed to create irq wait thread!"); + rp->irq_thread_running = true; + } + pthread_mutex_unlock(&rp->lock); +} + +static int rp1_pio_irq_claim(PIO pio) +{ + struct rp1_pio_irq_claim_args args = { .irq_index = -1 }; + (void)rp1_ioctl(pio, PIO_IOC_IRQ_CLAIM, &args); + if (args.irq_index >= 0) + rp1_pio_irq_start_thread(pio); + return args.irq_index; +} + +static uint32_t rp1_pio_irq_wait(PIO pio, uint timeout_ms) +{ + struct rp1_pio_irq_wait_args args = { .timeout_ms = timeout_ms }; + int ret = rp1_ioctl(pio, PIO_IOC_IRQ_WAIT, &args); + if (ret < 0) + return ~0; + return args.active_mask; +} + static PIO rp1_create_instance(PIO_CHIP_T *chip, uint index) { char pathbuf[20]; @@ -839,8 +954,7 @@ static PIO rp1_create_instance(PIO_CHIP_T *chip, uint index) pio->base.chip = chip; pio->fd = -1; pio->devname = strdup(pathbuf); - - rp1_pio_clear_instruction_memory(&pio->base); + pthread_mutex_init(&pio->lock, NULL); return &pio->base; } @@ -869,6 +983,7 @@ DECLARE_PIO_CHIP(rp1) { .instr_count = RP1_PIO_INSTRUCTION_COUNT, .sm_count = RP1_PIO_SM_COUNT, .fifo_depth = 8, + .irq_count = RP1_PIO_IRQ_COUNT, .create_instance = rp1_create_instance, .open_instance = rp1_open_instance, @@ -934,6 +1049,8 @@ DECLARE_PIO_CHIP(rp1) { .pio_sm_put = rp1_pio_sm_put, .pio_sm_get = rp1_pio_sm_get, .pio_sm_set_dmactrl = rp1_pio_sm_set_dmactrl, + .pio_sm_get_dmactrl = rp1_pio_sm_get_dmactrl, + .pio_sm_get_flags = rp1_pio_sm_get_flags, .pio_sm_is_rx_fifo_empty = rp1_pio_sm_is_rx_fifo_empty, .pio_sm_is_rx_fifo_full = rp1_pio_sm_is_rx_fifo_full, .pio_sm_get_rx_fifo_level = rp1_pio_sm_get_rx_fifo_level, @@ -969,4 +1086,12 @@ DECLARE_PIO_CHIP(rp1) { .gpio_set_oeover = rp1_gpio_set_oeover, .gpio_set_input_enabled = rp1_gpio_set_input_enabled, .gpio_set_drive_strength = rp1_gpio_set_drive_strength, + + .set_irqn_source_mask_enabled = rp1_pio_set_irqn_source_mask_enabled, + .irq_set_enabled = rp1_pio_irq_set_enabled, + .irq_is_enabled = rp1_pio_irq_is_enabled, + .pio_interrupt_get = rp1_pio_interrupt_get, + .pio_interrupt_clear = rp1_pio_interrupt_clear, + .pio_irq_claim = rp1_pio_irq_claim, + .pio_irq_wait = rp1_pio_irq_wait, }; diff --git a/piolib/piolib.c b/piolib/piolib.c index 003a4ab..c0a9b33 100644 --- a/piolib/piolib.c +++ b/piolib/piolib.c @@ -18,12 +18,22 @@ #include "piolib_priv.h" #define PIO_MAX_INSTANCES 4 +#define PIO_MAX_IRQS 16 static __thread PIO __pio; +struct pio_irq_handler { + PIO pio; + irq_handler_t handler; + void *context; +}; + static PIO pio_instances[PIO_MAX_INSTANCES]; static uint num_instances; static pthread_mutex_t pio_handle_lock; +static pthread_t pio_irq_threads[PIO_MAX_INSTANCES]; +static struct pio_irq_handler irq_handlers[PIO_MAX_IRQS]; +static uint num_irqs; void pio_select(PIO pio) { @@ -48,6 +58,27 @@ int pio_get_index(PIO pio) return -1; } +static void *irq_wait_thread(void *arg) { + PIO pio = (PIO)arg; + + while (1) { + uint32_t active = pio->chip->pio_irq_wait(pio, 0); + uint i; + if (active == ~0u) + break; + for (i = 0; active && i < pio->irq_count; i++) { + uint32_t mask = (1 << i); + if (active & mask) { + struct pio_irq_handler *h = &irq_handlers[pio->irq_base + i]; + active &= ~mask; + (h->handler)(h->context); + } + } + } + + return NULL; +} + int pio_init(void) { #if LIBRARY_BUILD @@ -65,6 +96,7 @@ int pio_init(void) if (initialised) return 0; num_instances = 0; + num_irqs = 0; p = start; while (p < end) @@ -72,6 +104,17 @@ int pio_init(void) PIO_CHIP_T *chip = *p; PIO pio = chip->create_instance(chip, i); if (pio && !PIO_IS_ERR(pio)) { + int irq_count = chip->irq_count; + if (irq_count) { + pio->irq_base = num_irqs; + pio->irq_count = irq_count; + while (irq_count--) { + struct pio_irq_handler *h = &irq_handlers[num_irqs++]; + h->pio = pio; + h->handler = NULL; + pio->irqs[irq_count] = -1; + } + } pio_instances[num_instances++] = pio; i++; } else { @@ -92,6 +135,7 @@ PIO pio_open(uint idx) { PIO pio = NULL; int err; + uint i; err = pio_init(); if (err) @@ -119,6 +163,12 @@ PIO pio_open(uint idx) if (err) { pio->in_use = 0; return PIO_ERR(err); + } else if (pio->irq_count) { + for (i = 0; i < pio->irq_count; i++) + pio->irqs[i] = -1; + + if (pthread_create(&pio_irq_threads[idx], NULL, irq_wait_thread, pio)) + pio_panic("Failed to create irq wait thread!"); } pio_select(pio); @@ -175,10 +225,74 @@ void pio_panic(const char *msg) exit(1); } -void sleep_us(uint64_t us) { +void sleep_us(uint64_t us) +{ const struct timespec tv = { .tv_sec = (us / 1000000), .tv_nsec = 1000ull * (us % 1000000) }; nanosleep(&tv, NULL); } + +uint pio_irq_map(PIO pio, uint irq_index) { + int pirq; + check_pio_param(pio); + invalid_params_if(PIO, irq_index > pio->irq_count); + pirq = pio->irqs[irq_index]; + if (pirq < 0) { + pirq = pio->chip->pio_irq_claim(pio); + if (pirq >= 0) { + pio->irqs[irq_index] = pirq; + } + } + if (pirq < 0) + pio_panic("Unable to map irq"); + return (uint)pirq; +} + +static inline void check_irq_param(uint num) +{ + if (num >= num_irqs) + pio_panic("irq out of range"); +} + +void irq_set_handler(uint num, irq_handler_t handler, void *context) +{ + struct pio_irq_handler *h = &irq_handlers[num]; + check_irq_param(num); + h->handler = handler; + h->context = context; +} + +void irq_remove_handler(uint num, irq_handler_t handler) +{ + struct pio_irq_handler *h = &irq_handlers[num]; + check_irq_param(num); + if (h->handler == handler) + h->handler = NULL; +} + +irq_handler_t irq_get_handler(uint num) +{ + struct pio_irq_handler *h = &irq_handlers[num]; + check_irq_param(num); + return h->handler; +} + +void irq_set_enabled(uint num, bool enabled) +{ + struct pio_irq_handler *h = &irq_handlers[num]; + PIO pio; + check_irq_param(num); + pio = h->pio; + pio->chip->irq_set_enabled(pio, num - pio->irq_base, enabled); +} + +bool irq_is_enabled(uint num) +{ + struct pio_irq_handler *h = &irq_handlers[num]; + PIO pio; + check_irq_param(num); + pio = h->pio; + return pio->chip->irq_is_enabled(pio, num - pio->irq_base); +}