/* * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for more * details. */ /** * @ingroup cpu_rpx0xx * @{ * * @file * @brief Internal PIO interface * * @author Fabian Hüßler */ #ifndef PIO_PIO_H #define PIO_PIO_H #include "periph_conf.h" #include "periph/gpio.h" #include "periph/pio.h" #ifdef __cplusplus extern "C" { #endif #if defined(CPU_MODEL_RP2040) || defined(DOXYGEN) /** * @brief Number of state machines per PIO */ #define PIO_SM_NUMOF 4 /** * @brief Maximum number of instructions per PIO */ #define PIO_INSTR_NUMOF 32 /** * @brief Number of interrupt flags per PIO */ #define PIO_IRQ_NUMOF 8 #endif /** * @brief Type to represent the width of an instruction */ typedef uint16_t pio_instr_t; /** * @brief To be used to set the state of a PIO pin in @ref pio_gpio_init_t::gpio_state to high * * @param[in] pin GPIO pin relative to @ref pio_gpio_init_t::gpio_base starting with 0 */ #define PIO_GPIO_INIT_HIGH(pin) (((gpio_t)(1)) << (pin)) /** * @brief To be used to set the state of a PIO pin in @ref pio_gpio_init_t::gpio_state to low * * @param[in] pin GPIO pin relative to @ref pio_gpio_init_t::gpio_base starting with 0 */ #define PIO_GPIO_INIT_LOW(pin) ((gpio_t)0) /** * @brief To be used to set the direction of a PIO pin in @ref pio_gpio_init_t::gpio_state as output * * @param[in] pin GPIO pin relative to @ref pio_gpio_init_t::gpio_base starting with 0 */ #define PIO_GPIO_INIT_OUT(pin) (((gpio_t)(1)) << (pin)) /** * @brief To be used to set the direction of a PIO pin in @ref pio_gpio_init_t::gpio_state as input * * @param[in] pin GPIO pin relative to @ref pio_gpio_init_t::gpio_base starting with 0 */ #define PIO_GPIO_INIT_IN(pin) ((gpio_t)0) /** * @brief Type used to configure PIO gpios pins */ typedef struct { gpio_pad_ctrl_t pad; /**< Pads bank GPIO control register configuration */ gpio_io_ctrl_t io; /**< IO bank GPIO control register */ gpio_t gpio_state; /**< GPIO states applied to pins, where the LSBit is the base */ gpio_t gpio_direction; /**< GPIO directions applied to pins, where the LSBit is the base */ gpio_t gpio_base; /**< GPIO base */ unsigned gpio_count; /**< Number of GPIOs starting at base */ } pio_gpio_init_t; /** * @name PIO instruction set * @{ */ /** * @brief JMP opcode */ #define PIO_INST_JMP (0u << 13) /** * @brief JMP opcode mask */ #define PIO_INST_JMP_MASK (7u << 13) /** * @brief JMP condition shift position */ #define PIO_INST_JMP_CONDITION_SHIFT 5 /** * @brief JMP condition mask */ #define PIO_INST_JMP_CONDITION_MASK (7u << PIO_INST_JMP_CONDITION_SHIFT) /** * @brief JMP address mask */ #define PIO_INST_JMP_ADDRESS_MASK (31u) /** * @brief JMP conditions */ typedef enum { PIO_INST_JMP_COND_NONE = 0, /**< Unconditionally */ PIO_INST_JMP_COND_X_ZERO = 1, /**< If scratch X is 0 */ PIO_INST_JMP_COND_X_DEC = 2, /**< If scratch X-- is not 0 */ PIO_INST_JMP_COND_Y_ZERO = 3, /**< If scratch Y is 0 */ PIO_INST_JMP_COND_Y_DEC = 4, /**< If scratch Y-- is not 0 */ PIO_INST_JMP_COND_NOT_X_EQ_Y = 5, /**< If scratch X != scratch Y */ PIO_INST_JMP_COND_PIN = 6, /**< If jump pin is high */ PIO_INST_JMP_COND_NOT_OSR_EMPTY = 7 /**< If OSR is not empty */ } pio_inst_jmp_cond_t; /** * @brief WAIT opcode */ #define PIO_INST_WAIT (1u << 13) /** * @brief WAIT opcode mask */ #define PIO_INST_WAIT_MASK (7u << 13) /** * @brief WAIT polarity shift position */ #define PIO_INST_WAIT_POL_SHIFT 7 /** * @brief WAIT polarity mask */ #define PIO_INST_WAIT_POL_MASK (1u << PIO_INST_WAIT_POL_SHIFT) /** * @brief WAIT source shift positions */ #define PIO_INST_WAIT_SOURCE_SHIFT 5 /** * @brief WAIT source mask */ #define PIO_INST_WAIT_SOURCE_MASK (3u << PIO_INST_WAIT_SOURCE_SHIFT) /** * @brief WAIT index mask */ #define PIO_INST_WAIT_INDEX_MASK (31u) /** * @brief WAIT polarities */ typedef enum { PIO_INST_WAIT_POL_LOW = 0, /**< Wait for 0 */ PIO_INST_WAIT_POL_HIGH = 1, /**< Wait for 1 */ } pio_inst_wait_pol_t; /** * @brief WAIT sources */ typedef enum { PIO_INST_WAIT_SRC_GPIO = 0, /**< Wait on GPIO */ PIO_INST_WAIT_SRC_PIN = 1, /**< Wait on input pin */ PIO_INST_WAIT_SRC_IRQ = 2 /**< Wait on interrupt */ } pio_inst_wait_src_t; /** * @brief IN opcode */ #define PIO_INST_IN (2u << 13) /** * @brief IN opcode mask */ #define PIO_INST_IN_MASK (7u << 13) /** * @brief IN source shift position */ #define PIO_INST_IN_SOURCE_SHIFT 5 /** * @brief IN source mask */ #define PIO_INST_IN_SOURCE_MASK (7u << PIO_INST_IN_SOURCE_SHIFT) /** * @brief Number of bits to be shifted into the input shift register */ #define PIO_INST_IN_BIT_COUNT_MASK (31u) /** * @brief IN sources */ typedef enum { PIO_INST_IN_SRC_PINS = 0, /**< From input pins */ PIO_INST_IN_SRC_X = 1, /**< From scratch X */ PIO_INST_IN_SRC_Y = 2, /**< From scratch Y */ PIO_INST_IN_SRC_NULL = 3, /**< Fill with zeros */ PIO_INST_IN_SRC_ISR = 6, /**< From input shift register */ PIO_INST_IN_SRC_OSR = 7 /**< From output shift register */ } pio_inst_in_src_t; /** * @brief OUT opcode */ #define PIO_INST_OUT (3u << 13) /** * @brief OUT opcode mask */ #define PIO_INST_OUT_MASK (7u << 13) /** * @brief OUT destination shift position */ #define PIO_INST_OUT_DESTINATION_SHIFT 5 /** * @brief OUT destination mask */ #define PIO_INST_OUT_DESTINATION_MASK (7u << PIO_INST_OUT_DESTINATION_SHIFT) /** * @brief Number of bits to be shifted out of the output shift register */ #define PIO_INST_OUT_BIT_COUNT_MASK (31u) /** * @brief OUT destinations */ typedef enum { PIO_INST_OUT_DST_PINS = 0, /**< To output pins */ PIO_INST_OUT_DST_X = 1, /**< To scratch X */ PIO_INST_OUT_DST_Y = 2, /**< To scratch Y */ PIO_INST_OUT_DST_NULL = 3, /**< Discard data */ PIO_INST_OUT_DST_PINDIRS = 4, /**< To output pin directions */ PIO_INST_OUT_DST_PC = 5, /**< To program counter */ PIO_INST_OUT_DST_ISR = 6, /**< To input shift register */ PIO_INST_OUT_DST_EXEC = 7 /**< Execute as instruction */ } pio_inst_out_dst_t; /** * @brief PUSH opcode */ #define PIO_INST_PUSH (4u << 13) /** * @brief PUSH opcode mask */ #define PIO_INST_PUSH_MASK ((7u << 13) | (1u << 7) | 31u) /** * @brief PUSH if input shift register is full shift position */ #define PIO_INST_PUSH_IF_FULL_SHIFT 6 /** * @brief PUSH if input shift register is full mask */ #define PIO_INST_PUSH_IF_FULL_MASK (1u << PIO_INST_PUSH_IF_FULL_SHIFT) /** * @brief PUSH if RX FIFO is not full shift position */ #define PIO_INST_PUSH_BLOCK_SHIFT 5 /** * @brief PUSH if RX FIFO is not full mask */ #define PIO_INST_PUSH_BLOCK_MASK (1u << PIO_INST_PUSH_BLOCK_SHIFT) /** * @brief PULL opcode */ #define PIO_INST_PULL ((4u << 13) | (1u << 7)) /** * @brief PULL opcode mask */ #define PIO_INST_PULL_MASK ((7u << 13) | (1u << 7) | 31u) /** * @brief PULL if output shift register is empty flag shift position */ #define PIO_INST_PULL_IF_EMPTY_SHIFT 6 /** * @brief PULL if output shift register is empty mask */ #define PIO_INST_PULL_IF_EMPTY_MASK (1u << PIO_INST_PULL_IF_EMPTY_SHIFT) /** * @brief PULL if TX FIFO is not empty flag shift position */ #define PIO_INST_PULL_BLOCK_SHIFT 5 /** * @brief PULL if TX FIFO is not empty mask */ #define PIO_INST_PULL_BLOCK_MASK (1u << PIO_INST_PULL_BLOCK_SHIFT) /** * @brief MOV opcode */ #define PIO_INST_MOV (5u << 13) /** * @brief MOV opcode mask */ #define PIO_INST_MOV_MASK (7u << 13) /** * @brief MOV destination shift position */ #define PIO_INST_MOV_DESTINATION_SHIFT 5 /** * @brief MOV destination mask */ #define PIO_INST_MOV_DESTINATION_MASK (7u << PIO_INST_MOV_DESTINATION_SHIFT) /** * @brief MOV operation shift position */ #define PIO_INST_MOV_OP_SHIFT 3 /** * @brief MOV operation mask */ #define PIO_INST_MOV_OP_MASK (3u << PIO_INST_MOV_OP_SHIFT) /** * @brief MOV source shift position */ #define PIO_INST_MOV_SOURCE_SHIFT 0 /** * @brief MOV source mask */ #define PIO_INST_MOV_SOURCE_MASK (7u) /** * @brief MOV destinations */ typedef enum { PIO_INST_MOV_DST_PINS = 0, /**< To output pins */ PIO_INST_MOV_DST_X = 1, /**< To scratch X */ PIO_INST_MOV_DST_Y = 2, /**< To scratch Y */ PIO_INST_MOV_DST_EXEC = 4, /**< Execute data as instruction */ PIO_INST_MOV_DST_PC = 5, /**< To program counter */ PIO_INST_MOV_DST_ISR = 6, /**< To input shift register */ PIO_INST_MOV_DST_OSR = 7 /**< To output shift register */ } pio_inst_mov_dst_t; /** * @brief MOV operation */ typedef enum { PIO_INST_MOV_OP_NONE = 0, /**< No operation */ PIO_INST_MOV_OP_INVERT = 1, /**< Bitwise complement */ PIO_INST_MOV_OP_REVERSE = 2 /**< Bitwise reverse */ } pio_inst_mov_op_t; /** * @brief MOV source */ typedef enum { PIO_INST_MOV_SRC_PINS = 0, /**< From input pins */ PIO_INST_MOV_SRC_X = 1, /**< From scratch X */ PIO_INST_MOV_SRC_Y = 2, /**< From scratch Y */ PIO_INST_MOV_SRC_NULL = 3, /**< Fill with zeros */ PIO_INST_MOV_SRC_STATUS = 5, /**< From status */ PIO_INST_MOV_SRC_ISR = 6, /**< From input shift register */ PIO_INST_MOV_SRC_OSR = 7 /**< From output shift register */ } pio_inst_mov_src_t; /** * @brief IRQ opcode */ #define PIO_INST_IRQ (6u << 13) /** * @brief IRQ opcode mask */ #define PIO_INST_IRQ_MASK ((7u << 13) | (1u << 7)) /** * @brief IRQ clear flag shift position */ #define PIO_INST_IRQ_CLR_SHIFT 6 /** * @brief IRQ clear flag mask */ #define PIO_INST_IRQ_CLR_MASK (1u << PIO_INST_IRQ_CLR_SHIFT) /** * @brief IRQ wait until cleared flag shift position */ #define PIO_INST_IRQ_WAIT_SHIFT 5 /** * @brief IRQ wait until cleared flag mask */ #define PIO_INST_IRQ_WAIT_MASK (1u << PIO_INST_IRQ_WAIT_SHIFT) /** * @brief IRQ index mask */ #define PIO_INST_IRQ_INDEX_MASK (31u) /** * @brief SET opcode */ #define PIO_INST_SET (7u << 13) /** * @brief SET opcode mask */ #define PIO_INST_SET_MASK (7u << 13) /** * @brief SET destination shift position */ #define PIO_INST_SET_DESTINATION_SHIFT 5 /** * @brief SET destination mask */ #define PIO_INST_SET_DESTINATION_MASK (7u << PIO_INST_SET_DESTINATION_SHIFT) /** * @brief SET data mask */ #define PIO_INST_SET_DATA_MASK (31u) /** * @brief SET destinations */ typedef enum { PIO_INST_SET_DST_PINS = 0, /**< To set pins */ PIO_INST_SET_DST_X = 1, /**< To scratch X */ PIO_INST_SET_DST_Y = 2, /**< To scratch Y */ PIO_INST_SET_DST_PINDIRS = 4 /**< To set pin directions */ } pio_inst_set_dst_t; /** * @brief Construct a JMP instruction * * @param[in] condition Jump if this condition is true * @param[in] address Address in instruction memory to jump to * * @return JMP instruction */ static inline pio_instr_t pio_inst_jmp(pio_inst_jmp_cond_t condition, unsigned address) { return PIO_INST_JMP | (((unsigned)condition) << PIO_INST_JMP_CONDITION_SHIFT) | address; } /** * @brief Construct a WAIT instruction * * @param[in] polarity Polarity * @param[in] source Which source to wait on * @param[in] relative IRQ index is relative to SM index * @param[in] index Which GPIO/PIN/IRQ to wait on * * @return WAIT instruction */ static inline pio_instr_t pio_inst_wait(pio_inst_wait_pol_t polarity, pio_inst_wait_src_t source, bool relative, unsigned index) { return PIO_INST_WAIT | ((unsigned)polarity << PIO_INST_WAIT_POL_SHIFT) | ((unsigned)source << PIO_INST_WAIT_SOURCE_SHIFT) | ((!!relative) << 4) | index; } /** * @brief Construct an IN instruction * * @param[in] source From where to shift * @param[in] bit_count Number of bits to shift * * @return IN instruction */ static inline pio_instr_t pio_inst_in(pio_inst_in_src_t source, unsigned bit_count) { return PIO_INST_IN | ((unsigned)source << PIO_INST_IN_SOURCE_SHIFT) | (bit_count % 32); } /** * @brief Construct an OUT instruction * * @param[in] destination To where to shift * @param[in] bit_count Number of bits to shift * * @return OUT instruction */ static inline pio_instr_t pio_inst_out(pio_inst_out_dst_t destination, unsigned bit_count) { return PIO_INST_OUT | ((unsigned)destination << PIO_INST_OUT_DESTINATION_SHIFT) | (bit_count % 32); } /** * @brief Construct a PUSH instruction * * @param[in] if_full Only push if shift threshold is reached * @param[in] block Stall if RX FIFO is full * * @return PUSH instruction */ static inline pio_instr_t pio_inst_push(bool if_full, bool block) { return PIO_INST_PUSH | ((!!if_full) << PIO_INST_PUSH_IF_FULL_SHIFT) | ((!!block) << PIO_INST_PUSH_BLOCK_SHIFT); } /** * @brief Construct a PULL instruction * * @param[in] if_empty Only pull if shift threshold is reached * @param[in] block Block if TX FIFO is empty * * @return PULL instruction */ static inline pio_instr_t pio_inst_pull(bool if_empty, bool block) { return PIO_INST_PULL | ((!!if_empty) << PIO_INST_PULL_IF_EMPTY_SHIFT) | ((!!block) << PIO_INST_PULL_BLOCK_SHIFT); } /** * @brief Construct a MOV instruction * * @param[in] destination Where to move to * @param[in] operation Operation to perform on move * @param[in] source Where to move from * * @return MOV instruction */ static inline pio_instr_t pio_inst_mov(pio_inst_mov_dst_t destination, pio_inst_mov_op_t operation, pio_inst_mov_src_t source) { return PIO_INST_MOV | ((unsigned)destination << PIO_INST_MOV_DESTINATION_SHIFT) | ((unsigned)operation << PIO_INST_MOV_OP_SHIFT) | ((unsigned)source << PIO_INST_MOV_SOURCE_SHIFT); } /** * @brief Construct an IRQ instruction * * @param[in] clear Clear IRQ instead to raise it * @param[in] wait Wait until IRQ is cleared if raised * @param[in] relative IRQ index is relative to SM number * @param[in] index IRQ index * * @return IRQ instruction */ static inline pio_instr_t pio_inst_irq(bool clear, bool wait, bool relative, unsigned index) { return PIO_INST_IRQ | (!!clear << PIO_INST_IRQ_CLR_SHIFT) | (!!wait << PIO_INST_IRQ_WAIT_SHIFT) | ((!!relative << 4) | index); } /** * @brief Construct a SET instruction * * @param[in] destination Set destination * @param[in] data Data to set * * @return SET instruction */ static inline pio_instr_t pio_inst_set(pio_inst_set_dst_t destination, unsigned data) { return PIO_INST_SET | ((unsigned)destination << PIO_INST_SET_DESTINATION_SHIFT) | data; } /** * @brief Encode the delay/sideset instruction field * * @param[in] sideset Sideset value to write to sideset pins * @param[in] sideset_count Number of sideset pins * @param[in] sideset_opt If sideset is optional * @param[in] delay Delay cycles * * @return Delay/sideset encoding that can be added to an instruction */ static inline pio_instr_t pio_inst_delay_sideset(unsigned sideset, unsigned sideset_count, bool sideset_opt, unsigned delay) { return ((!!sideset_opt) << 12) | (sideset << (13 - (!!sideset_opt + sideset_count))) | (delay << 8); } /** @} */ /** * @brief Internal state machine registers */ typedef struct pio_sm_ctrl_regs { volatile uint32_t clkdiv; /**< SMx_CLKDIV */ volatile uint32_t execctrl; /**< SMx_EXECCTRL*/ volatile uint32_t shiftctrl; /**< SMx_SHIFTCTRL */ const volatile uint32_t addr; /**< SMx_ADDR */ volatile uint32_t instr; /**< SMx_INSTR */ volatile uint32_t pinctrl; /**< SMx_PINCTRL */ } pio_sm_ctrl_regs_t; /** * @brief State machine configuration registers */ #define PIO_SM_CTRL_BASE(dev) ((pio_sm_ctrl_regs_t *)(&((dev)->SM0_CLKDIV))) /** * @brief Maximum clock divider */ #define PIO_SM_CLKDIV_MAX 65536 /** * @brief Convert state machine index to bitmask */ #define PIO_SM_MASK(sm) (1u << (sm)) /** * @brief Convert IRQ index to bitmask */ #define PIO_IRQ_MASK(irq) (1u << (irq)) /** * @brief Convert absolute IRQ index to relative IRQ index * * A relative state machine interrupt instruction (see datasheet 3.4.9 IRQ) * raises a certain flag in the IRQ register. The following table shows the * values of the IRQ register for all combinations of interrupts and state * machines. This allows that a program running on different state machines * to raise a different IRQ flag. There are 8 PIO IRQ flags but only the * lower 4 IRQ flags are cached by NVIC. * * @verbatim * +--------+---------------------------+---------------------------+---------------------------+---------------------------+ * | | SM 0 | SM 1 | SM 2 | SM 3 | * | | IRQ indice | irq register | IRQ indice | irq register | IRQ indice | IRQ register | IRQ indice | IRQ register | * +--------+---------------------------+---------------------------+---------------------------+---------------------------+ * | IRQ 0 | 0 | 0b00000001 | 1 | 0b00000010 | 2 | 0b00000100 | 3 | 0b00001000 | * | IRQ 1 | 1 | 0b00000010 | 2 | 0b00000100 | 3 | 0b00001000 | 0 | 0b00000001 | * | IRQ 2 | 2 | 0b00000100 | 3 | 0b00001000 | 0 | 0b00000001 | 1 | 0b00000010 | * | IRQ 3 | 3 | 0b00001000 | 0 | 0b00000001 | 1 | 0b00000010 | 2 | 0b00000100 | * +--------+------------+--------------+------------+--------------+------------+--------------+------------+--------------| * | IRQ 4 | 4 | 0b00010000 | 5 | 0b00100000 | 6 | 0b01000000 | 7 | 0b10000000 | * | IRQ 5 | 5 | 0b00100000 | 6 | 0b01000000 | 7 | 0b10000000 | 4 | 0b00010000 | * | IRQ 6 | 6 | 0b01000000 | 7 | 0b10000000 | 4 | 0b00010000 | 5 | 0b00100000 | * | IRQ 7 | 7 | 0b10000000 | 4 | 0b00010000 | 5 | 0b00100000 | 6 | 0b01000000 | * +--------+------------+--------------+------------+--------------+------------+--------------+------------+--------------+ * @endverbatim * * @param[in] irq_abs Absolute PIO IRQ index [0-7] * @param[in] sm PIO state machine index * * @return Relative PIO IRQ index */ static inline unsigned pio_irq_rel_index(unsigned irq_abs, unsigned sm) { assert(irq_abs < PIO_IRQ_NUMOF); assert(sm < PIO_SM_NUMOF); return (irq_abs & 0b100u) | ((irq_abs + sm) & 0b011u); } /** * @brief Get state machine index from relative IRQ index * * rel = pio_irq_rel_index(abs, sm) <=> pio_irq_rel_sm(abs, rel) = sm * * @param[in] irq_abs Absolute PIO IRQ index [0-7] * @param[in] irq_rel Relative PIO IRQ index [0-7] * * @return PIO state machine index */ static inline unsigned pio_irq_rel_sm(unsigned irq_abs, unsigned irq_rel) { assert(irq_abs < PIO_IRQ_NUMOF); assert(irq_rel < PIO_IRQ_NUMOF); return (irq_abs & 0b100u) | ((irq_rel - irq_abs) & 0b011u); } /** * @brief Convert relative IRQ index to bitmask */ #define PIO_IRQ_REL_MASK(irq, sm) PIO_IRQ_MASK(pio_irq_rel_index(irq, sm)) /** * @brief PIO IRQ flags */ typedef enum { /* lower 4 hardware IRQs which are issued with 'irq <0-3> [rel]' and are routed to NVIC */ /* This does not encode the state machine index of which state machine fired! */ PIO_IRQ_SM_3 = PIO0_INTR_SM3_Msk, /**< Any state machine issued irq 3 */ PIO_IRQ_SM_2 = PIO0_INTR_SM2_Msk, /**< Any state machine issued irq 2 */ PIO_IRQ_SM_1 = PIO0_INTR_SM1_Msk, /**< Any state machine issued irq 1 */ PIO_IRQ_SM_0 = PIO0_INTR_SM0_Msk, /**< any state machine issued irq 0 */ /* FIFO interrupts per state machine */ PIO_IRQ_TXNFULL_SM3 = PIO0_INTR_SM3_TXNFULL_Msk, /**< SM3 TX FIFO is not full */ PIO_IRQ_TXNFULL_SM2 = PIO0_INTR_SM2_TXNFULL_Msk, /**< SM2 TX FIFO is not full */ PIO_IRQ_TXNFULL_SM1 = PIO0_INTR_SM1_TXNFULL_Msk, /**< SM1 TX FIFO is not full */ PIO_IRQ_TXNFULL_SM0 = PIO0_INTR_SM0_TXNFULL_Msk, /**< SM0 TX FIFO is not full */ PIO_IRQ_RXNEMPTY_SM3 = PIO0_INTR_SM3_RXNEMPTY_Msk, /**< SM3 RX FIFO is not empty */ PIO_IRQ_RXNEMPTY_SM2 = PIO0_INTR_SM2_RXNEMPTY_Msk, /**< SM2 RX FIFO is not empty */ PIO_IRQ_RXNEMPTY_SM1 = PIO0_INTR_SM1_RXNEMPTY_Msk, /**< SM1 RX FIFO is not empty */ PIO_IRQ_RXNEMPTY_SM0 = PIO0_INTR_SM0_RXNEMPTY_Msk, /**< SM0 RX FIFO is not empty */ /* all routed state machine interrupts */ PIO_IRQ_ALL_SM = (PIO_IRQ_SM_3 | PIO_IRQ_SM_2 | PIO_IRQ_SM_1 | PIO_IRQ_SM_0), /**< All SM interrupts */ /* all interrupts */ PIO_IRQ_ALL = (PIO_IRQ_ALL_SM | PIO_IRQ_TXNFULL_SM3 | PIO_IRQ_TXNFULL_SM2 | PIO_IRQ_TXNFULL_SM1 | PIO_IRQ_TXNFULL_SM0 | PIO_IRQ_RXNEMPTY_SM3 | PIO_IRQ_RXNEMPTY_SM2 | PIO_IRQ_RXNEMPTY_SM1 | PIO_IRQ_RXNEMPTY_SM0), /**< All flags above */ } pio_irq_source_t; /** * @brief PIO interrupt lines */ typedef enum { PIO_IRQ_LINE_0 = 0, /**< IRQ line 0 */ PIO_IRQ_LINE_1, /**< IRQ line 1 */ PIO_IRQ_LINE_NUMOF, /**< Number of IRQ lines */ } pio_irq_line_t; /** * @brief PIO state machine flags */ typedef enum { PIO_SM0 = PIO_SM_MASK(0), /**< Mask bit of SM 0 */ PIO_SM1 = PIO_SM_MASK(1), /**< Mask bit of SM 1 */ PIO_SM2 = PIO_SM_MASK(2), /**< Mask bit of SM 2 */ PIO_SM3 = PIO_SM_MASK(3), /**< Mask bit of SM 3 */ PIO_SM_ALL = (PIO_SM0 | PIO_SM1 | PIO_SM2 | PIO_SM3) /**< All flags above */ } pio_sm_mask_t; /** * @brief PIO interrupt callbacks for FIFO interrupts */ typedef struct pio_isr_vec { /** * @brief Called when Tx FIFO is not full * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void (*tx_ready)(pio_t pio, pio_sm_t sm); /** * @brief Called when Rx FIFO is not empty * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void (*rx_ready)(pio_t pio, pio_sm_t sm); } pio_isr_vec_t; /** * @brief PIO state machine interrupt callbacks for state machine interrupts */ typedef struct pio_isr_sm_vec { /** * @brief Called when any SM issues an interrupt [0-3] * * @param[in] pio PIO index * @param[in] irq IRQ index [0-3] */ void (*sm)(pio_t pio, unsigned irq); } pio_isr_sm_vec_t; /** * @brief PIO program configuration */ typedef struct pio_program_conf { unsigned pc_start; /**< Initial program counter */ unsigned wrap_bottom; /**< Instruction index after which the PC wraps around */ unsigned wrap_top; /**< Instruction index the PC wraps to */ unsigned sideset_count; /**< Number of bits used for sideset */ bool sideset_optional; /**< Whether the sideset is optional */ bool sideset_pindirs; /**< Whether the sideset effects pin directions */ } pio_program_conf_t; /** * @brief PIO clock configuration */ typedef struct pio_sm_clkdiv { uint16_t div; /**< Integer divider */ uint8_t frac_100; /**< Fractional divider, two digits after comma */ } pio_sm_clkdiv_t; /** * @brief Execute a single instruction * * This function does not wait until the instruction has completed. * The instruction is written to SMx INSTR register and is immediately * executed, interrupting other execution. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] inst Instruction to be executed * * @return Success: 0 * Failure: < 0 (state machine stalls on a previous instruction) */ int pio_sm_exec(pio_t pio, pio_sm_t sm, pio_instr_t inst); /** * @brief Execute a single instruction * * This function blocks until the instruction has completed. * The instruction is written to SMx INSTR register and is immediately * executed, interrupting other execution. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] inst Instruction to be executed */ void pio_sm_exec_block(pio_t pio, pio_sm_t sm, pio_instr_t inst); /** * @brief Write program instructions to their allocated location * * @param[in] pio PIO index * @param[in,out] prog PIO program to write * @param[in] instr Program instructions * * @return Success: 0 * Failure: != 0 */ int pio_write_program(pio_t pio, pio_program_t *prog, const pio_instr_t *instr); /** * @brief Apply the default state machine configuration * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_reset(pio_t pio, pio_sm_t sm); /** * @brief Restart a state machine * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_restart(pio_t pio, pio_sm_t sm); /** * @brief Set ISR callbacks for FIFO interrupts per state machine * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] vec FIFO ISR callback vector */ void pio_set_isr_vec(pio_t pio, pio_sm_t sm, const pio_isr_vec_t *vec); /** * @brief Set ISR callbacks for state machine interrupts * * @param[in] pio PIO index * @param[in] irq PIO IRQ index [0-3] or use @ref pio_irq_rel_index * @param[in] vec State machine ISR callback vector */ void pio_set_isr_sm_vec(pio_t pio, unsigned irq, const pio_isr_sm_vec_t *vec); /** * @brief Configure which pins are writeable by an 'out pins' instruction * * The pins start at @p pin_base and are @p pin_count many. * The pin sequence wraps around after the maximum pin. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_base First pin of a pin sequence effected by an out instruction * @param[in] pin_count Number of out pins */ void pio_sm_set_out_pins(pio_t pio, pio_sm_t sm, gpio_t pin_base, unsigned pin_count); /** * @brief Configure the state machine input pin mapping * * The pin sequence wraps around after the maximum pin. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_base First pin of a pin sequence effected by an in instruction */ void pio_sm_set_in_pins(pio_t pio, pio_sm_t sm, gpio_t pin_base); /** * @brief Configure which pins are effected by a 'set pins' instructions. * * The pins start at @p pin_base and are @p pin_count many. * The pin sequence wraps around after the maximum pin. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_base First pin of a pin sequence effected by a set instruction * @param[in] pin_count Number of set pins */ void pio_sm_set_set_pins(pio_t pio, pio_sm_t sm, gpio_t pin_base, unsigned pin_count); /** * @brief Set the first pin of a sequence of sideset count pins which * are effected by sideset instructions * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_base First pin of a pin sequence effected by a sideset instruction */ void pio_sm_set_sideset_pins(pio_t pio, pio_sm_t sm, gpio_t pin_base); /** * @brief Configure how many pins are sideset pins and whether a sideset * is optional or required for every instruction * * Each sideset pin occupies one bit of the delay field of an instruction. * If sidesets are optional, one more bit is used. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_count Number of pins effected by a sideset instruction * @param[in] enable Whether the sideset is optional */ void pio_sm_set_sideset_count(pio_t pio, pio_sm_t sm, unsigned pin_count, bool enable); /** * @brief Configure whether a sideset effects pins or pin directions * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pindir Whether the sideset effects pin directions */ void pio_sm_set_sideset_target(pio_t pio, pio_sm_t sm, bool pindir); /** * @brief Create a clock divider struct * * @warning If the resulting clock divisor @ref CLOCK_CORECLOCK / @p f_hz * would be greater than @ref PIO_SM_CLKDIV_MAX, an assert is fired. * * @param[in] f_hz Desired PIO frequency * * @return Clock divider struct */ pio_sm_clkdiv_t pio_sm_clkdiv(uint32_t f_hz); /** * @brief Apply the clock configuration to a PIO * * There is an integer and a fractional portion of the clock divider. * The fractional parameter is in 1/100. * That means 90 means .90 and 9 means .09. * The PIOs are running from the system clock. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] clk Clock configuration */ void pio_sm_set_clkdiv(pio_t pio, pio_sm_t sm, pio_sm_clkdiv_t clk); /** * @brief Restart the clock divider of several state machines. * * This is useful if several state machines must be synchronized * and to resync after the clock divider has been changed on-the-fly. * * @param[in] pio PIO index * @param[in] sm_mask PIO state machine mask @ref pio_sm_mask_t */ void pio_sm_clkdiv_restart(pio_t pio, unsigned sm_mask); /** * @brief Set program instruction wrap boundaries. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] prog_loc Program location * @param[in] top wrap from * @param[in] bottom wrap to */ void pio_sm_set_wrap(pio_t pio, pio_sm_t sm, unsigned prog_loc, uint8_t top, uint8_t bottom); /** * @brief Configure the pin on which to branch on a 'jmp pins' instruction. * * The jump is executed if the pin is high. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin Branch pin */ void pio_sm_set_jmp_pin(pio_t pio, pio_sm_t sm, gpio_t pin); /** * @brief Configure the shift in behaviour of a state machine * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] right Whether to shift right instead of left * @param[in] autopush Whether to automatically push on reached threshold * @param[in] threshold In shift threshold */ void pio_sm_set_in_shift(pio_t pio, pio_sm_t sm, bool right, bool autopush, unsigned threshold); /** * @brief Configure the shift out behaviour of a state machine * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] right Whether to shift right instead of left * @param[in] autopull Whether to automatically pull on reached threshold * @param[in] threshold Out shift threshold */ void pio_sm_set_out_shift(pio_t pio, pio_sm_t sm, bool right, bool autopull, unsigned threshold); /** * @brief Join the TX FIFO to the RX FIFO * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_set_fifo_join_rx(pio_t pio, pio_sm_t sm); /** * @brief Join the RX FIFO to the TX FIFO * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_set_fifo_join_tx(pio_t pio, pio_sm_t sm); /** * @brief No joined FIFO * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_reset_fifos(pio_t pio, pio_sm_t sm); /** * @brief Drop all pending words in the tx and rx FIFO * * @param[in] pio PIO index * @param[in] sm PIO state machine index */ void pio_sm_clear_fifos(pio_t pio, pio_sm_t sm); /** * @brief Enable PIO_IRQ interrupts * * @param[in] pio PIO index * @param[in] irq PIO IRQ line to use * @param[in] irq_mask IRQ flags to enable */ void pio_irq_enable(pio_t pio, pio_irq_line_t irq, pio_irq_source_t irq_mask); /** * @brief Disable PIO_IRQ interrupts * * @param[in] pio PIO index * @param[in] irq PIO IRQ line to use * @param[in] irq_mask IRQ flags to disable */ void pio_irq_disable(pio_t pio, pio_irq_line_t irq, pio_irq_source_t irq_mask); /** * @brief Send one word to a state machine * * This function does not block but returns a negative integer if the * FIFO is fill. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] word Word to transmit * * @return Success: 0 * Failure: != 0 */ int pio_sm_transmit_word(pio_t pio, pio_sm_t sm, uint32_t word); /** * @brief Send one word to a state machine * * This function blocks until one word has been sent. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] word Word to transmit */ void pio_sm_transmit_word_block(pio_t pio, pio_sm_t sm, uint32_t word); /** * @brief Send @p count words to a state machine * * This function blocks until @p count words have been sent. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] words Words to transmit * @param[in] count Number of words */ void pio_sm_transmit_words_block(pio_t pio, pio_sm_t sm, const uint32_t *words, unsigned count); /** * @brief Receive a word from a state machine * * This function does not block but returns a negative integer if there is * no data. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[out] word Destination to store the received word * * @return Success: 0 * Failure: != 0 */ int pio_sm_receive_word(pio_t pio, pio_sm_t sm, uint32_t *word); /** * @brief Receive one word from a state machine * * This function blocks until one word has been received. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[out] word Destination to store the received word */ void pio_sm_receive_word_block(pio_t pio, pio_sm_t sm, uint32_t *word); /** * @brief Receive words from a state machine. * * This function blocks until @p count words have been received. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[out] words Buffer to store received words * @param[in] count Number of words */ void pio_sm_receive_words_block(pio_t pio, pio_sm_t sm, uint32_t *words, unsigned count); /** * @brief Read interrupt flags * * @param[in] pio PIO index * * @return IRQ flags */ uint32_t pio_irq_get(pio_t pio); /** * @brief Clear interrupt flags * * @param[in] pio PIO index * @param[in] irq_flags Interrupt flags to clear */ void pio_irq_clear(pio_t pio, unsigned irq_flags); /** * @brief Apply common program configuration. * * This function should be called before any specific program * parameters are set because the state machine is reset first. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] prog Program * @param[in] conf Program configuration * * @return Success: 0 * Failure: != 0 */ int pio_sm_init_common(pio_t pio, pio_sm_t sm, const pio_program_t *prog, const pio_program_conf_t *conf); /** * @brief Apply pin directions in @p values for which the corresponding bit in @p mask is set. * * 1 means output, 0 means input. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] values Pin directions out/in to be set * @param[in] mask Mask of which pins should be modified */ void pio_sm_set_pindirs_with_mask(pio_t pio, pio_sm_t sm, gpio_t values, gpio_t mask); /** * @brief Apply pin values in @p values for which the corresponding bit in @p mask is set. * * 1 means high, 0 means low. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] values Pin values on/off to be set * @param[in] mask Mask of which pins should be modified */ void pio_sm_set_pins_with_mask(pio_t pio, pio_sm_t sm, gpio_t values, gpio_t mask); /** * @brief Set pins affected by 'set pins' and 'set pindirs' instructions * and initialize the pins as PIO pins and a state according to @p pin_init. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_init GPIO pin initialization values */ void pio_sm_set_set_pins_init(pio_t pio, pio_sm_t sm, const pio_gpio_init_t *pin_init); /** * @brief Set output pins affected by 'out pins', 'out pindirs' and 'mov pins' instructions * and initialize the pins as PIO pins and a state according to @p pin_init. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_init GPIO pin initialization values */ void pio_sm_set_out_pins_init(pio_t pio, pio_sm_t sm, const pio_gpio_init_t *pin_init); /** * @brief Set pins affected by sideset instructions * and initialize the pins as PIO pins and a state according to @p pin_init. * * @param[in] pio PIO index * @param[in] sm PIO state machine index * @param[in] pin_init GPIO pin initialization values */ void pio_sm_set_sideset_pins_init(pio_t pio, pio_sm_t sm, const pio_gpio_init_t *pin_init); /** * @brief Print status information about the FIFOs and programs * * @param[in] pio PIO index */ void pio_print_status(pio_t pio); /** * @brief Print debug information about the current state of this PIO * * @param[in] pio PIO index */ void pio_print_debug(pio_t pio); /** * @brief Clear TX stall debug flag * * @param[in] pio PIO index * @param[in] sm_mask SM mask */ static inline void pio_sm_clear_debug_txstall(pio_t pio, unsigned sm_mask) { io_reg_atomic_set(&pio_config[pio].dev->FDEBUG, ((sm_mask & PIO_SM_ALL) << PIO0_FDEBUG_TXSTALL_Pos)); } /** * @brief Clear TX overflow debug flag * * @param[in] pio PIO index * @param[in] sm_mask SM mask */ static inline void pio_sm_clear_debug_txover(pio_t pio, unsigned sm_mask) { io_reg_atomic_set(&pio_config[pio].dev->FDEBUG, ((sm_mask & PIO_SM_ALL) << PIO0_FDEBUG_TXOVER_Pos)); } /** * @brief Clear RX underflow debug flag * * @param[in] pio PIO index * @param[in] sm_mask SM mask */ static inline void pio_sm_clear_debug_rxunder(pio_t pio, unsigned sm_mask) { io_reg_atomic_set(&pio_config[pio].dev->FDEBUG, ((sm_mask & PIO_SM_ALL) << PIO0_FDEBUG_RXUNDER_Pos)); } /** * @brief Clear RX stall debug flag * * @param[in] pio PIO index * @param[in] sm_mask SM mask */ static inline void pio_sm_clear_debug_rxstall(pio_t pio, unsigned sm_mask) { io_reg_atomic_set(&pio_config[pio].dev->FDEBUG, ((sm_mask & PIO_SM_ALL) << PIO0_FDEBUG_RXSTALL_Pos)); } /** * @brief Check if TX FIFO is empty * * @param[in] pio PIO index * @param[in] sm PIO state machine index * * @return If TX FIFO is empty */ static inline bool pio_sm_tx_fifo_empty(pio_t pio, pio_sm_t sm) { return !!(pio_config[pio].dev->FSTAT & ((1u << sm) << PIO0_FSTAT_TXEMPTY_Pos)); } /** * @brief Check if TX FIFO is full * * @param[in] pio PIO index * @param[in] sm PIO state machine index * * @return If TX FIFO is full */ static inline bool pio_sm_tx_fifo_full(pio_t pio, pio_sm_t sm) { return !!(pio_config[pio].dev->FSTAT & ((1u << sm) << PIO0_FSTAT_TXFULL_Pos)); } /** * @brief Check if RX FIFO is empty * * @param[in] pio PIO index * @param[in] sm PIO state machine index * * @return If RX FIFO is empty */ static inline bool pio_sm_rx_fifo_empty(pio_t pio, pio_sm_t sm) { return !!(pio_config[pio].dev->FSTAT & ((1u << sm) << PIO0_FSTAT_RXEMPTY_Pos)); } /** * @brief Check if RX FIFO is full * * @param[in] pio PIO index * @param[in] sm PIO state machine index * * @return If RX FIFO is full */ static inline bool pio_sm_rx_fifo_full(pio_t pio, pio_sm_t sm) { return !!((pio_config[pio].dev)->FSTAT & ((1u << sm) << PIO0_FSTAT_RXFULL_Pos)); } #ifdef __cplusplus } #endif #endif /* PIO_PIO_H */ /** @} */