| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #ifndef _HARDWARE_PIO_INSTRUCTIONS_H_ |
| #define _HARDWARE_PIO_INSTRUCTIONS_H_ |
| |
| #include "pico.h" |
| |
| /** \brief PIO instruction encoding |
| * \defgroup pio_instructions pio_instructions |
| * \ingroup hardware_pio |
| * |
| * Functions for generating PIO instruction encodings programmatically. In debug builds |
| *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function |
| * parameters. |
| * |
| * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" |
| */ |
| |
| // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions |
| #ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS |
| #define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| enum pio_instr_bits { |
| pio_instr_bits_jmp = 0x0000, |
| pio_instr_bits_wait = 0x2000, |
| pio_instr_bits_in = 0x4000, |
| pio_instr_bits_out = 0x6000, |
| pio_instr_bits_push = 0x8000, |
| pio_instr_bits_pull = 0x8080, |
| pio_instr_bits_mov = 0xa000, |
| pio_instr_bits_irq = 0xc000, |
| 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 |
| |
| /*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions |
| * \ingroup pio_instructions |
| * |
| * \note Not all values are suitable for all functions. Validity is only checked in debug mode when |
| * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 |
| */ |
| enum pio_src_dest { |
| pio_pins = 0u, |
| pio_x = 1u, |
| pio_y = 2u, |
| pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, |
| pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, |
| pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, |
| pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, |
| pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, |
| pio_isr = 6u | _PIO_INVALID_SET_DEST, |
| pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, |
| pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, |
| }; |
| |
| static inline uint _pio_major_instr_bits(uint instr) { |
| return instr & 0xe000u; |
| } |
| |
| static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { |
| valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); |
| #if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) |
| uint32_t major = _pio_major_instr_bits(instr_bits); |
| if (major == pio_instr_bits_in || major == pio_instr_bits_out) { |
| assert(arg2 && arg2 <= 32); |
| } else { |
| assert(arg2 <= 31); |
| } |
| #endif |
| return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); |
| } |
| |
| static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { |
| return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); |
| } |
| |
| /*! \brief Encode just the delay slot bits of an instruction |
| * \ingroup pio_instructions |
| * |
| * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay |
| * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when |
| * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt |
| * as they share the same bits within the instruction encoding. |
| * |
| * \param cycles the number of cycles 0-31 (or less if side set is being used) |
| * \return the delay slot bits to be ORed with an instruction encoding |
| */ |
| static inline uint pio_encode_delay(uint cycles) { |
| // note that the maximum cycles will be smaller if sideset_bit_count > 0 |
| valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); |
| return cycles << 8u; |
| } |
| |
| /*! \brief Encode just the side set bits of an instruction (in non optional side set mode) |
| * \ingroup pio_instructions |
| * |
| * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits |
| * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when |
| * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits |
| * within the instruction encoding. |
| * |
| * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm |
| * \param value the value to sideset on the pins |
| * \return the side set bits to be ORed with an instruction encoding |
| */ |
| static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { |
| valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); |
| valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); |
| return value << (13u - sideset_bit_count); |
| } |
| |
| /*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) |
| * \ingroup pio_instructions |
| * |
| * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits |
| * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when |
| * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits |
| * within the instruction encoding. |
| * |
| * \param sideset_bit_count number of side set bits as would be specified via `.sideset <n> opt` in pioasm |
| * \param value the value to sideset on the pins |
| * \return the side set bits to be ORed with an instruction encoding |
| */ |
| static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { |
| valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); |
| valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); |
| return 0x1000u | value << (12u - sideset_bit_count); |
| } |
| |
| /*! \brief Encode an unconditional JMP instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if scratch X zero instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP !X <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_not_x(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP X-- <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_x_dec(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if scratch Y zero instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP !Y <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_not_y(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP Y-- <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_y_dec(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP X!=Y <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_x_ne_y(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if input pin high instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP PIN <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_pin(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); |
| } |
| |
| /*! \brief Encode a conditional JMP if output shift register not empty instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `JMP !OSRE <addr>` |
| * |
| * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_jmp_not_osre(uint addr) { |
| return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); |
| } |
| |
| static inline uint _pio_encode_irq(bool relative, uint irq) { |
| valid_params_if(PIO_INSTRUCTIONS, irq <= 7); |
| return (relative ? 0x10u : 0x0u) | irq; |
| } |
| |
| /*! \brief Encode a WAIT for GPIO pin instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `WAIT <polarity> GPIO <gpio>` |
| * |
| * \param polarity true for `WAIT 1`, false for `WAIT 0` |
| * \param gpio The real GPIO number 0-31 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { |
| return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); |
| } |
| |
| /*! \brief Encode a WAIT for pin instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `WAIT <polarity> PIN <pin>` |
| * |
| * \param polarity true for `WAIT 1`, false for `WAIT 0` |
| * \param pin The pin number 0-31 relative to the executing SM's input pin mapping |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_wait_pin(bool polarity, uint pin) { |
| return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); |
| } |
| |
| /*! \brief Encode a WAIT for IRQ instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `WAIT <polarity> IRQ <irq> <relative>` |
| * |
| * \param polarity true for `WAIT 1`, false for `WAIT 0` |
| * \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ <irq>` |
| * \param irq the irq number 0-7 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { |
| valid_params_if(PIO_INSTRUCTIONS, irq <= 7); |
| return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); |
| } |
| |
| /*! \brief Encode an IN instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `IN <src>, <count>` |
| * |
| * \param src The source to take data from |
| * \param count The number of bits 1-32 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_in(enum pio_src_dest src, uint count) { |
| valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); |
| } |
| |
| /*! \brief Encode an OUT instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `OUT <src>, <count>` |
| * |
| * \param dest The destination to write data to |
| * \param count The number of bits 1-32 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { |
| valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); |
| } |
| |
| /*! \brief Encode a PUSH instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `PUSH <if_full>, <block>` |
| * |
| * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` |
| * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_push(bool if_full, bool block) { |
| return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); |
| } |
| |
| /*! \brief Encode a PULL instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `PULL <if_empty>, <block>` |
| * |
| * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` |
| * \param block true for `PULL ... BLOCK`, false for `PULL ...` |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_pull(bool if_empty, bool block) { |
| return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); |
| } |
| |
| /*! \brief Encode a MOV instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `MOV <dest>, <src>` |
| * |
| * \param dest The destination to write data to |
| * \param src The source to take data from |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { |
| valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); |
| valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); |
| } |
| |
| /*! \brief Encode a MOV instruction with bit invert |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `MOV <dest>, ~<src>` |
| * |
| * \param dest The destination to write inverted data to |
| * \param src The source to take data from |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { |
| valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); |
| valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); |
| } |
| |
| /*! \brief Encode a MOV instruction with bit reverse |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `MOV <dest>, ::<src>` |
| * |
| * \param dest The destination to write bit reversed data to |
| * \param src The source to take data from |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { |
| valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); |
| valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); |
| } |
| |
| /*! \brief Encode a IRQ SET instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `IRQ SET <irq> <relative>` |
| * |
| * \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET <irq>` |
| * \param irq the irq number 0-7 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_irq_set(bool relative, uint irq) { |
| return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); |
| } |
| |
| /*! \brief Encode a IRQ WAIT instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `IRQ WAIT <irq> <relative>` |
| * |
| * \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ WAIT <irq>` |
| * \param irq the irq number 0-7 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_irq_wait(bool relative, uint irq) { |
| return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); |
| } |
| |
| /*! \brief Encode a IRQ CLEAR instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `IRQ CLEAR <irq> <relative>` |
| * |
| * \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ CLEAR <irq>` |
| * \param irq the irq number 0-7 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_irq_clear(bool relative, uint irq) { |
| return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); |
| } |
| |
| /*! \brief Encode a SET instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `SET <dest>, <value>` |
| * |
| * \param dest The destination to apply the value to |
| * \param value The value 0-31 |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { |
| valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); |
| return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); |
| } |
| |
| /*! \brief Encode a NOP instruction |
| * \ingroup pio_instructions |
| * |
| * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` |
| * |
| * \return The instruction encoding with 0 delay and no side set value |
| * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt |
| */ |
| static inline uint pio_encode_nop(void) { |
| return pio_encode_mov(pio_y, pio_y); |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |