Initial Release

This commit is contained in:
graham sanderson
2021-01-20 10:44:27 -06:00
commit 26653ea81e
404 changed files with 135614 additions and 0 deletions

View File

@ -0,0 +1,4 @@
pico_simple_hardware_target(pio)
# additional libraries
target_link_libraries(hardware_pio INTERFACE hardware_gpio hardware_claim)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
/*
* 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"
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=hardware_pio
#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
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,
};
inline static uint _pio_major_instr_bits(uint instr) {
return instr & 0xe000u;
}
inline static 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);
}
inline static 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);
}
inline static uint pio_encode_delay(uint cycles) {
valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f);
return cycles << 8u;
}
inline static 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 <= (0x1fu >> sideset_bit_count));
return value << (13u - sideset_bit_count);
}
inline static uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) {
valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 2 && sideset_bit_count <= 5);
valid_params_if(PIO_INSTRUCTIONS, value <= (0x1fu >> sideset_bit_count));
return 0x1000u | value << (12u - sideset_bit_count);
}
inline static uint pio_encode_jmp(uint addr) {
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr);
}
inline static uint _pio_encode_irq(bool relative, uint irq) {
valid_params_if(PIO_INSTRUCTIONS, irq <= 7);
return (relative ? 0x10u : 0x0u) | irq;
}
inline static uint pio_encode_wait_gpio(bool polarity, uint pin) {
return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), pin);
}
inline static uint pio_encode_wait_pin(bool polarity, uint pin) {
return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin);
}
inline static 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));
}
inline static uint pio_encode_in(enum pio_src_dest src, uint value) {
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC));
return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, value);
}
inline static uint pio_encode_out(enum pio_src_dest dest, uint value) {
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST));
return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, value);
}
inline static 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);
}
inline static 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);
}
inline static 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);
}
inline static 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));
}
inline static 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));
}
inline static 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));
}
inline static 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));
}
inline static 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);
}
inline static uint pio_encode_nop() {
return pio_encode_mov(pio_y, pio_y);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,240 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "hardware/claim.h"
#include "hardware/pio.h"
#include "hardware/pio_instructions.h"
// sanity check
check_hw_layout(pio_hw_t, sm[0].clkdiv, PIO_SM0_CLKDIV_OFFSET);
check_hw_layout(pio_hw_t, sm[1].clkdiv, PIO_SM1_CLKDIV_OFFSET);
check_hw_layout(pio_hw_t, instr_mem[0], PIO_INSTR_MEM0_OFFSET);
check_hw_layout(pio_hw_t, inte0, PIO_IRQ0_INTE_OFFSET);
check_hw_layout(pio_hw_t, txf[1], PIO_TXF1_OFFSET);
check_hw_layout(pio_hw_t, rxf[3], PIO_RXF3_OFFSET);
check_hw_layout(pio_hw_t, ints1, PIO_IRQ1_INTS_OFFSET);
static_assert(NUM_PIO_STATE_MACHINES * NUM_PIOS <= 8, "");
static uint8_t claimed;
void pio_sm_claim(PIO pio, uint sm) {
check_sm_param(sm);
uint which = pio_get_index(pio);
if (which) {
hw_claim_or_assert(&claimed, NUM_PIO_STATE_MACHINES + sm, "PIO 1 SM %d already claimed");
} else {
hw_claim_or_assert(&claimed, sm, "PIO 0 SM %d already claimed");
}
}
void pio_claim_sm_mask(PIO pio, uint sm_mask) {
for(uint i = 0; sm_mask; i++, sm_mask >>= 1u) {
if (sm_mask & 1u) pio_sm_claim(pio, i);
}
}
void pio_sm_unclaim(PIO pio, uint sm) {
check_sm_param(sm);
uint which = pio_get_index(pio);
hw_claim_clear(&claimed, which * NUM_PIO_STATE_MACHINES + sm);
}
int pio_claim_unused_sm(PIO pio, bool required) {
uint which = pio_get_index(pio);
uint base = which * NUM_PIO_STATE_MACHINES;
int index = hw_claim_unused_from_range((uint8_t*)&claimed, required, base,
base + NUM_PIO_STATE_MACHINES - 1, "No PIO state machines are available");
return index >= base ? index - base : -1;
}
void pio_load_program(PIO pio, const uint16_t *prog, uint8_t prog_len, uint8_t load_offset) {
// instructions are only 16 bits, but instruction memory locations are spaced 32 bits apart
// Adjust the addresses of any jump instructions to respect load offset
assert(load_offset + prog_len <= PIO_INSTRUCTION_COUNT);
}
static_assert(PIO_INSTRUCTION_COUNT <= 32, "");
static uint32_t _used_instruction_space[2];
static int _pio_find_offset_for_program(PIO pio, const pio_program_t *program) {
assert(program->length < PIO_INSTRUCTION_COUNT);
uint32_t used_mask = _used_instruction_space[pio_get_index(pio)];
uint32_t program_mask = (1u << program->length) - 1;
if (program->origin >= 0) {
if (program->origin > 32 - program->length) return -1;
return used_mask & (program_mask << program->origin) ? -1 : program->origin;
} else {
// work down from the top always
for (int i = 32 - program->length; i >= 0; i--) {
if (!(used_mask & (program_mask << (uint) i))) {
return i;
}
}
return -1;
}
}
bool pio_can_add_program(PIO pio, const pio_program_t *program) {
uint32_t save = hw_claim_lock();
bool rc = -1 != _pio_find_offset_for_program(pio, program);
hw_claim_unlock(save);
return rc;
}
static bool _pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
assert(offset < PIO_INSTRUCTION_COUNT);
assert(offset + program->length <= PIO_INSTRUCTION_COUNT);
if (program->origin >= 0 && program->origin != offset) return false;
uint32_t used_mask = _used_instruction_space[pio_get_index(pio)];
uint32_t program_mask = (1u << program->length) - 1;
return !(used_mask & (program_mask << offset));
}
bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
uint32_t save = hw_claim_lock();
bool rc = _pio_can_add_program_at_offset(pio, program, offset);
hw_claim_unlock(save);
return rc;
}
static void _pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
if (!_pio_can_add_program_at_offset(pio, program, offset)) {
panic("No program space");
}
for (uint i = 0; i < program->length; ++i) {
uint16_t instr = program->instructions[i];
pio->instr_mem[offset + i] = pio_instr_bits_jmp != _pio_major_instr_bits(instr) ? instr : instr + offset;
}
uint32_t program_mask = (1u << program->length) - 1;
_used_instruction_space[pio_get_index(pio)] |= program_mask << offset;
}
// these assert if unable
uint pio_add_program(PIO pio, const pio_program_t *program) {
uint32_t save = hw_claim_lock();
int offset = _pio_find_offset_for_program(pio, program);
if (offset < 0) {
panic("No program space");
}
_pio_add_program_at_offset(pio, program, offset);
hw_claim_unlock(save);
return offset;
}
void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset) {
uint32_t program_mask = (1u << program->length) - 1;
program_mask <<= loaded_offset;
uint32_t save = hw_claim_lock();
assert(program_mask == (_used_instruction_space[pio_get_index(pio)] & program_mask));
_used_instruction_space[pio_get_index(pio)] &= ~program_mask;
hw_claim_unlock(save);
}
void pio_clear_instruction_memory(PIO pio) {
uint32_t save = hw_claim_lock();
_used_instruction_space[pio_get_index(pio)] = 0;
for(uint i=0;i<PIO_INSTRUCTION_COUNT;i++) {
pio->instr_mem[i] = pio_encode_jmp(i);
}
hw_claim_unlock(save);
}
// Set the value of all PIO pins. This is done by forcibly executing
// instructions on a "victim" state machine, sm. Ideally you should choose one
// which is not currently running a program. This is intended for one-time
// setup of initial pin states.
void pio_sm_set_pins(PIO pio, uint sm, uint32_t pins) {
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
uint remaining = 32;
uint base = 0;
while (remaining) {
uint decrement = remaining > 5 ? 5 : remaining;
pio->sm[sm].pinctrl =
(decrement << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
pio_sm_exec(pio, sm, pio_encode_set(pio_pins, pins & 0x1fu));
remaining -= decrement;
base += decrement;
pins >>= 5;
}
pio->sm[sm].pinctrl = pinctrl_saved;
}
void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pinvals, uint32_t pin_mask) {
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
while (pin_mask) {
uint base = __builtin_ctz(pin_mask);
pio->sm[sm].pinctrl =
(1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
pio_sm_exec(pio, sm, pio_encode_set(pio_pins, (pinvals >> base) & 0x1u));
pin_mask &= pin_mask - 1;
}
pio->sm[sm].pinctrl = pinctrl_saved;
}
void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pindirs, uint32_t pin_mask) {
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
while (pin_mask) {
uint base = __builtin_ctz(pin_mask);
pio->sm[sm].pinctrl =
(1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, (pindirs >> base) & 0x1u));
pin_mask &= pin_mask - 1;
}
pio->sm[sm].pinctrl = pinctrl_saved;
}
void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin, uint count, bool is_out) {
assert(pin < 32u);
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
uint pindir_val = is_out ? 0x1f : 0;
while (count > 5) {
pio->sm[sm].pinctrl = (5u << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB);
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val));
count -= 5;
pin = (pin + 5) & 0x1f;
}
pio->sm[sm].pinctrl = (count << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB);
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val));
pio->sm[sm].pinctrl = pinctrl_saved;
}
void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) {
// Halt the machine, set some sensible defaults
pio_sm_set_enabled(pio, sm, false);
if (config) {
pio_sm_set_config(pio, sm, config);
} else {
pio_sm_config c = pio_get_default_sm_config();
pio_sm_set_config(pio, sm, &c);
}
pio_sm_clear_fifos(pio, sm);
// Clear FIFO debug flags
const uint32_t fdebug_sm_mask =
(1u << PIO_FDEBUG_TXOVER_LSB) |
(1u << PIO_FDEBUG_RXUNDER_LSB) |
(1u << PIO_FDEBUG_TXSTALL_LSB) |
(1u << PIO_FDEBUG_RXSTALL_LSB);
pio->fdebug = fdebug_sm_mask << sm;
// Finally, clear some internal SM state
pio_sm_restart(pio, sm);
pio_sm_clkdiv_restart(pio, sm);
pio_sm_exec(pio, sm, pio_encode_jmp(initial_pc));
}
void pio_sm_drain_tx_fifo(PIO pio, uint sm) {
uint instr = (pio->sm[sm].shiftctrl & PIO_SM0_SHIFTCTRL_AUTOPULL_BITS) ? pio_encode_out(pio_null, 32) :
pio_encode_pull(false, false);
while (!pio_sm_is_tx_fifo_empty(pio, sm)) {
pio_sm_exec(pio, sm, instr);
}
}