YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
pio_rp2040.cpp
1/*
2 * pio_rp2040.cpp
3 *
4 * Created on: 25.12.2022
5 * Author: andreas
6 */
7
8#include <cassert>
9#include "pio_rp2040.h"
10#include "system_rp2040.h"
11
12using namespace _PIO0_;
13using namespace _PIO1_;
14
15// The two PIO instances
16pio_rp2040 pio_rp2040::pio0(PIO0, 0);
17pio_rp2040 pio_rp2040::pio1(PIO1, 1);
18
19function<void()> pio_rp2040::_handler_pio0[12] = { nullptr };
20function<void()> pio_rp2040::_handler_pio1[12] = { nullptr };
21
22void SM_regs::init() {
23 // Initialize SM registers with reset values;
24 SM_CLKDIV = 0;
25 SM_CLKDIV.INT = 1;
26 SM_EXECCTRL = 0;
27 SM_EXECCTRL.WRAP_TOP = 0x1f;
28 SM_SHIFTCTRL = 0;
29 SM_SHIFTCTRL.OUT_SHIFTDIR = 1;
30 SM_SHIFTCTRL.IN_SHIFTDIR = 1;
31 SM_PINCTRL = 0;
32 SM_PINCTRL.SET_COUNT = 5;
33}
34
35void SM::setRegister(out_dest_t reg, uint32_t val,
36 uint8_t offset, uint8_t size) {
37 // Store current configuration
38 bool en = isEnabled();
39 disable();
40 uint8_t out_base = regs.SM_PINCTRL.OUT_BASE;
41 uint8_t out_count = regs.SM_PINCTRL.OUT_COUNT;
42 // Configure bits to write
43 regs.SM_PINCTRL.OUT_BASE = offset;
44 regs.SM_PINCTRL.OUT_COUNT = size;
45 // Empty the TX Fifo
46 while(pio.FLEVEL & (0xf << (sm_index << 3))) {
47 execute( op_PULL(0, 0) );
48 }
49 // Write value to TX Fifo first.
50 // Then pull it and write it to destination.
51 writeTxFifo(val);
52 execute( op_PULL(0, 0) );
53 execute( op_OUT (0, reg, 0) );
54 // Restore original state
55 regs.SM_PINCTRL.OUT_BASE = out_base;
56 regs.SM_PINCTRL.OUT_COUNT = out_count;
57 if (en) enable();
58}
59
60void SM::setClock(uint32_t hz) {
61 regs.SM_CLKDIV.INT = CLK_SYS / hz;
62 uint64_t frac = ((CLK_SYS % hz) << 8);
63 frac /= hz;
64 regs.SM_CLKDIV.FRAC = frac;
65}
66
67void SM::attachIrq(function<void()> handler) {
68 if (pio_index)
69 pio_rp2040::_handler_pio1[sm_index + 8] = handler;
70 else
71 pio_rp2040::_handler_pio0[sm_index + 8] = handler;
72}
73
74void SM::enableIrq() {
75 pio_set.IRQ0_INTE = 1 << (sm_index + 8);
76}
77
78void SM::disableIrq() {
79 pio_clr.IRQ0_INTE = 1 << (sm_index + 8);
80}
81
82void SM::attachTXNFULLIrq(function<void()> handler) {
83 if (pio_index)
84 pio_rp2040::_handler_pio1[sm_index + 4] = handler;
85 else
86 pio_rp2040::_handler_pio0[sm_index + 4] = handler;
87}
88
89void SM::enableTXNFULLIrq() {
90 pio_set.IRQ0_INTE = 1 << (sm_index + 4);
91}
92
93void SM::disableTXNFULLIrq() {
94 pio_clr.IRQ0_INTE = 1 << (sm_index + 4);
95}
96
97void SM::attachRXNEMPTYIrq(function<void()> handler) {
98 if (pio_index)
99 pio_rp2040::_handler_pio1[sm_index + 0] = handler;
100 else
101 pio_rp2040::_handler_pio0[sm_index + 0] = handler;
102}
103
104void SM::enableRXNEMPTYIrq() {
105 pio_set.IRQ0_INTE = 1 << (sm_index);
106}
107
108void SM::disableRXNEMPTYIrq() {
109 pio_clr.IRQ0_INTE = 1 << (sm_index);
110}
111
112pio_rp2040::pio_rp2040(PIO0_t & pio, uint8_t pio_index)
113: _pio(pio),
114 _pio_xor(*(PIO0_t *)((uint8_t *)&pio + 0x1000)),
115 _pio_set(*(PIO0_t *)((uint8_t *)&pio + 0x2000)),
116 _pio_clr(*(PIO0_t *)((uint8_t *)&pio + 0x3000)),
117 _pio_index(pio_index),
118 _next_free_addr(0) {
119 // Set base address of SM register banks
120 _sm_regbanks = (SM_regs *)&pio.SM0_CLKDIV;
121 // Disable and restart all state machines
122 _pio.CTRL.SM_ENABLE = 0x0;
123 _pio.CTRL.SM_RESTART = 0xf;
124 // Erase instruction memory
125 for(int i=0; i < 32; ++i) {
126 _pio.INSTR_MEM[i] = 0;
127 }
128 // Initially all state machines are not in use
129 for(int i=0; i < 4; ++i) {
130 _in_use[i] = false;
131 }
132 // Enable PIO interrupts in NVIC
133 NVIC_EnableIRQ(PIO0_IRQ_0_IRQn);
134 NVIC_EnableIRQ(PIO1_IRQ_0_IRQn);
135}
136
137pio_rp2040::~pio_rp2040() {
138 // Disable all state machines
139 _pio.CTRL.SM_ENABLE = 0x0;
140}
141
142SM* pio_rp2040::loadProgram(const pio_program & prgm) {
143 // Find a free state machine
144 uint8_t sm_index=0;
145 for(sm_index=0; sm_index < 4; ++sm_index) {
146 if (!_in_use[sm_index]) break;
147 }
148 assert(sm_index < 4);
149 // Set 'used' marker
150 _in_use[sm_index] = true;
151 // Check program origin
152 uint8_t load_at_addr = 0;
153 if (prgm.origin == -1) {
154 // Reloctable program
155 load_at_addr = _next_free_addr;
156 } else {
157 // Absolute origin
158 assert(prgm.origin >= _next_free_addr);
159 load_at_addr = prgm.origin;
160 }
161 // Check program size
162 assert((load_at_addr + prgm.length) <= 32);
163 // Load the PIO program
164 for(int i=0; i < prgm.length; i++) {
165 uint16_t inst = prgm.instructions[i];
166 // Relocate JMP instruction if needed
167 if (is_JMP(inst) && (prgm.origin == -1)) {
168 inst += load_at_addr;
169 }
170 _pio.INSTR_MEM[i + load_at_addr] = inst;
171 }
172 // Generate a state machine and initialize it
173 SM* sm = new SM(_pio, _pio_set, _pio_clr, _pio_index, sm_index,
174 load_at_addr, _sm_regbanks[sm_index]);
175 // Set the initial PC
176 sm->setRegister(out_dest_t::PC, load_at_addr);
177 // Update the next free addr
178 _next_free_addr += load_at_addr + prgm.length;
179 // Return the state machine in charge
180 return sm;
181}
182
183extern "C" {
184 void PIO0_IRQ_0_Handler(void) {
185 uint32_t status = PIO0.IRQ0_INTS;
186 while (uint8_t pos = __builtin_ffs(status)) {
187 status ^= 1 << --pos;
188 if (pos > 7) {
189 PIO0.IRQ = 1 << (pos - 8);
190 }
191 if (pio_rp2040::_handler_pio0[pos])
192 pio_rp2040::_handler_pio0[pos]();
193 }
194 }
195
196 void PIO1_IRQ_0_Handler(void) {
197 uint32_t status = PIO1.IRQ0_INTS;
198 while (uint8_t pos = __builtin_ffs(status)) {
199 status ^= 1 << --pos;
200 if (pos > 7) {
201 PIO1.IRQ = 1 << (pos - 8);
202 }
203 if (pio_rp2040::_handler_pio1[pos])
204 pio_rp2040::_handler_pio1[pos]();
205 }
206 }
207}