Austin Schuh | 208337d | 2022-01-01 14:29:11 -0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include "hardware/claim.h" |
| 8 | #include "hardware/pio.h" |
| 9 | #include "hardware/pio_instructions.h" |
| 10 | |
| 11 | // sanity check |
| 12 | check_hw_layout(pio_hw_t, sm[0].clkdiv, PIO_SM0_CLKDIV_OFFSET); |
| 13 | check_hw_layout(pio_hw_t, sm[1].clkdiv, PIO_SM1_CLKDIV_OFFSET); |
| 14 | check_hw_layout(pio_hw_t, instr_mem[0], PIO_INSTR_MEM0_OFFSET); |
| 15 | check_hw_layout(pio_hw_t, inte0, PIO_IRQ0_INTE_OFFSET); |
| 16 | check_hw_layout(pio_hw_t, txf[1], PIO_TXF1_OFFSET); |
| 17 | check_hw_layout(pio_hw_t, rxf[3], PIO_RXF3_OFFSET); |
| 18 | check_hw_layout(pio_hw_t, ints1, PIO_IRQ1_INTS_OFFSET); |
| 19 | |
| 20 | static_assert(NUM_PIO_STATE_MACHINES * NUM_PIOS <= 8, ""); |
| 21 | static uint8_t claimed; |
| 22 | |
| 23 | void pio_sm_claim(PIO pio, uint sm) { |
| 24 | check_sm_param(sm); |
| 25 | uint which = pio_get_index(pio); |
| 26 | if (which) { |
| 27 | hw_claim_or_assert(&claimed, NUM_PIO_STATE_MACHINES + sm, "PIO 1 SM (%d - 4) already claimed"); |
| 28 | } else { |
| 29 | hw_claim_or_assert(&claimed, sm, "PIO 0 SM %d already claimed"); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | void pio_claim_sm_mask(PIO pio, uint sm_mask) { |
| 34 | for(uint i = 0; sm_mask; i++, sm_mask >>= 1u) { |
| 35 | if (sm_mask & 1u) pio_sm_claim(pio, i); |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | void pio_sm_unclaim(PIO pio, uint sm) { |
| 40 | check_sm_param(sm); |
| 41 | uint which = pio_get_index(pio); |
| 42 | hw_claim_clear(&claimed, which * NUM_PIO_STATE_MACHINES + sm); |
| 43 | } |
| 44 | |
| 45 | int pio_claim_unused_sm(PIO pio, bool required) { |
| 46 | // PIO index is 0 or 1. |
| 47 | uint which = pio_get_index(pio); |
| 48 | uint base = which * NUM_PIO_STATE_MACHINES; |
| 49 | int index = hw_claim_unused_from_range((uint8_t*)&claimed, required, base, |
| 50 | base + NUM_PIO_STATE_MACHINES - 1, "No PIO state machines are available"); |
| 51 | return index >= (int)base ? index - (int)base : -1; |
| 52 | } |
| 53 | |
| 54 | bool pio_sm_is_claimed(PIO pio, uint sm) { |
| 55 | check_sm_param(sm); |
| 56 | uint which = pio_get_index(pio); |
| 57 | return hw_is_claimed(&claimed, which * NUM_PIO_STATE_MACHINES + sm); |
| 58 | } |
| 59 | |
| 60 | static_assert(PIO_INSTRUCTION_COUNT <= 32, ""); |
| 61 | static uint32_t _used_instruction_space[2]; |
| 62 | |
| 63 | static int _pio_find_offset_for_program(PIO pio, const pio_program_t *program) { |
| 64 | assert(program->length <= PIO_INSTRUCTION_COUNT); |
| 65 | uint32_t used_mask = _used_instruction_space[pio_get_index(pio)]; |
| 66 | uint32_t program_mask = (1u << program->length) - 1; |
| 67 | if (program->origin >= 0) { |
| 68 | if (program->origin > 32 - program->length) return -1; |
| 69 | return used_mask & (program_mask << program->origin) ? -1 : program->origin; |
| 70 | } else { |
| 71 | // work down from the top always |
| 72 | for (int i = 32 - program->length; i >= 0; i--) { |
| 73 | if (!(used_mask & (program_mask << (uint) i))) { |
| 74 | return i; |
| 75 | } |
| 76 | } |
| 77 | return -1; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | bool pio_can_add_program(PIO pio, const pio_program_t *program) { |
| 82 | uint32_t save = hw_claim_lock(); |
| 83 | bool rc = -1 != _pio_find_offset_for_program(pio, program); |
| 84 | hw_claim_unlock(save); |
| 85 | return rc; |
| 86 | } |
| 87 | |
| 88 | static bool _pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { |
| 89 | valid_params_if(PIO, offset < PIO_INSTRUCTION_COUNT); |
| 90 | valid_params_if(PIO, offset + program->length <= PIO_INSTRUCTION_COUNT); |
| 91 | if (program->origin >= 0 && (uint)program->origin != offset) return false; |
| 92 | uint32_t used_mask = _used_instruction_space[pio_get_index(pio)]; |
| 93 | uint32_t program_mask = (1u << program->length) - 1; |
| 94 | return !(used_mask & (program_mask << offset)); |
| 95 | } |
| 96 | |
| 97 | bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { |
| 98 | uint32_t save = hw_claim_lock(); |
| 99 | bool rc = _pio_can_add_program_at_offset(pio, program, offset); |
| 100 | hw_claim_unlock(save); |
| 101 | return rc; |
| 102 | } |
| 103 | |
| 104 | static void _pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { |
| 105 | if (!_pio_can_add_program_at_offset(pio, program, offset)) { |
| 106 | panic("No program space"); |
| 107 | } |
| 108 | for (uint i = 0; i < program->length; ++i) { |
| 109 | uint16_t instr = program->instructions[i]; |
| 110 | pio->instr_mem[offset + i] = pio_instr_bits_jmp != _pio_major_instr_bits(instr) ? instr : instr + offset; |
| 111 | } |
| 112 | uint32_t program_mask = (1u << program->length) - 1; |
| 113 | _used_instruction_space[pio_get_index(pio)] |= program_mask << offset; |
| 114 | } |
| 115 | |
| 116 | // these assert if unable |
| 117 | uint pio_add_program(PIO pio, const pio_program_t *program) { |
| 118 | uint32_t save = hw_claim_lock(); |
| 119 | int offset = _pio_find_offset_for_program(pio, program); |
| 120 | if (offset < 0) { |
| 121 | panic("No program space"); |
| 122 | } |
| 123 | _pio_add_program_at_offset(pio, program, (uint)offset); |
| 124 | hw_claim_unlock(save); |
| 125 | return (uint)offset; |
| 126 | } |
| 127 | |
| 128 | void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) { |
| 129 | uint32_t save = hw_claim_lock(); |
| 130 | _pio_add_program_at_offset(pio, program, offset); |
| 131 | hw_claim_unlock(save); |
| 132 | } |
| 133 | |
| 134 | void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset) { |
| 135 | uint32_t program_mask = (1u << program->length) - 1; |
| 136 | program_mask <<= loaded_offset; |
| 137 | uint32_t save = hw_claim_lock(); |
| 138 | assert(program_mask == (_used_instruction_space[pio_get_index(pio)] & program_mask)); |
| 139 | _used_instruction_space[pio_get_index(pio)] &= ~program_mask; |
| 140 | hw_claim_unlock(save); |
| 141 | } |
| 142 | |
| 143 | void pio_clear_instruction_memory(PIO pio) { |
| 144 | uint32_t save = hw_claim_lock(); |
| 145 | _used_instruction_space[pio_get_index(pio)] = 0; |
| 146 | for(uint i=0;i<PIO_INSTRUCTION_COUNT;i++) { |
| 147 | pio->instr_mem[i] = pio_encode_jmp(i); |
| 148 | } |
| 149 | hw_claim_unlock(save); |
| 150 | } |
| 151 | |
| 152 | // Set the value of all PIO pins. This is done by forcibly executing |
| 153 | // instructions on a "victim" state machine, sm. Ideally you should choose one |
| 154 | // which is not currently running a program. This is intended for one-time |
| 155 | // setup of initial pin states. |
| 156 | void pio_sm_set_pins(PIO pio, uint sm, uint32_t pins) { |
| 157 | check_pio_param(pio); |
| 158 | check_sm_param(sm); |
| 159 | uint32_t pinctrl_saved = pio->sm[sm].pinctrl; |
| 160 | uint remaining = 32; |
| 161 | uint base = 0; |
| 162 | while (remaining) { |
| 163 | uint decrement = remaining > 5 ? 5 : remaining; |
| 164 | pio->sm[sm].pinctrl = |
| 165 | (decrement << PIO_SM0_PINCTRL_SET_COUNT_LSB) | |
| 166 | (base << PIO_SM0_PINCTRL_SET_BASE_LSB); |
| 167 | pio_sm_exec(pio, sm, pio_encode_set(pio_pins, pins & 0x1fu)); |
| 168 | remaining -= decrement; |
| 169 | base += decrement; |
| 170 | pins >>= 5; |
| 171 | } |
| 172 | pio->sm[sm].pinctrl = pinctrl_saved; |
| 173 | } |
| 174 | |
| 175 | void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pinvals, uint32_t pin_mask) { |
| 176 | check_pio_param(pio); |
| 177 | check_sm_param(sm); |
| 178 | uint32_t pinctrl_saved = pio->sm[sm].pinctrl; |
| 179 | while (pin_mask) { |
| 180 | uint base = (uint)__builtin_ctz(pin_mask); |
| 181 | pio->sm[sm].pinctrl = |
| 182 | (1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) | |
| 183 | (base << PIO_SM0_PINCTRL_SET_BASE_LSB); |
| 184 | pio_sm_exec(pio, sm, pio_encode_set(pio_pins, (pinvals >> base) & 0x1u)); |
| 185 | pin_mask &= pin_mask - 1; |
| 186 | } |
| 187 | pio->sm[sm].pinctrl = pinctrl_saved; |
| 188 | } |
| 189 | |
| 190 | void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pindirs, uint32_t pin_mask) { |
| 191 | check_pio_param(pio); |
| 192 | check_sm_param(sm); |
| 193 | uint32_t pinctrl_saved = pio->sm[sm].pinctrl; |
| 194 | while (pin_mask) { |
| 195 | uint base = (uint)__builtin_ctz(pin_mask); |
| 196 | pio->sm[sm].pinctrl = |
| 197 | (1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) | |
| 198 | (base << PIO_SM0_PINCTRL_SET_BASE_LSB); |
| 199 | pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, (pindirs >> base) & 0x1u)); |
| 200 | pin_mask &= pin_mask - 1; |
| 201 | } |
| 202 | pio->sm[sm].pinctrl = pinctrl_saved; |
| 203 | } |
| 204 | |
| 205 | void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin, uint count, bool is_out) { |
| 206 | check_pio_param(pio); |
| 207 | check_sm_param(sm); |
| 208 | valid_params_if(PIO, pin < 32u); |
| 209 | uint32_t pinctrl_saved = pio->sm[sm].pinctrl; |
| 210 | uint pindir_val = is_out ? 0x1f : 0; |
| 211 | while (count > 5) { |
| 212 | pio->sm[sm].pinctrl = (5u << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB); |
| 213 | pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val)); |
| 214 | count -= 5; |
| 215 | pin = (pin + 5) & 0x1f; |
| 216 | } |
| 217 | pio->sm[sm].pinctrl = (count << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB); |
| 218 | pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val)); |
| 219 | pio->sm[sm].pinctrl = pinctrl_saved; |
| 220 | } |
| 221 | |
| 222 | void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) { |
| 223 | valid_params_if(PIO, initial_pc < PIO_INSTRUCTION_COUNT); |
| 224 | // Halt the machine, set some sensible defaults |
| 225 | pio_sm_set_enabled(pio, sm, false); |
| 226 | |
| 227 | if (config) { |
| 228 | pio_sm_set_config(pio, sm, config); |
| 229 | } else { |
| 230 | pio_sm_config c = pio_get_default_sm_config(); |
| 231 | pio_sm_set_config(pio, sm, &c); |
| 232 | } |
| 233 | |
| 234 | pio_sm_clear_fifos(pio, sm); |
| 235 | |
| 236 | // Clear FIFO debug flags |
| 237 | const uint32_t fdebug_sm_mask = |
| 238 | (1u << PIO_FDEBUG_TXOVER_LSB) | |
| 239 | (1u << PIO_FDEBUG_RXUNDER_LSB) | |
| 240 | (1u << PIO_FDEBUG_TXSTALL_LSB) | |
| 241 | (1u << PIO_FDEBUG_RXSTALL_LSB); |
| 242 | pio->fdebug = fdebug_sm_mask << sm; |
| 243 | |
| 244 | // Finally, clear some internal SM state |
| 245 | pio_sm_restart(pio, sm); |
| 246 | pio_sm_clkdiv_restart(pio, sm); |
| 247 | pio_sm_exec(pio, sm, pio_encode_jmp(initial_pc)); |
| 248 | } |
| 249 | |
| 250 | void pio_sm_drain_tx_fifo(PIO pio, uint sm) { |
| 251 | uint instr = (pio->sm[sm].shiftctrl & PIO_SM0_SHIFTCTRL_AUTOPULL_BITS) ? pio_encode_out(pio_null, 32) : |
| 252 | pio_encode_pull(false, false); |
| 253 | while (!pio_sm_is_tx_fifo_empty(pio, sm)) { |
| 254 | pio_sm_exec(pio, sm, instr); |
| 255 | } |
| 256 | } |