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