YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
task_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// Low-level task implementation for RP2350/Cortex M33
15//
16#pragma GCC diagnostic push
17#pragma GCC diagnostic ignored "-Wpmf-conversions"
18
19#include <cassert>
20#include "task_rp2350.h"
21#include "multicore_rp2350.h"
22#include "task_idle.h"
23#include "system_rp2350.h"
24#include "yahal_config.h"
25
26#include "RP2350.h"
27using namespace _PPB_;
28using namespace _SIO_;
29using namespace _TIMER0_;
30
31// Global vars
33bool multitasking_on_core[NUMBER_OF_CORES] {false};
34task_idle idle_tasks[NUMBER_OF_CORES];
35
36void task::_setup_stack(bool priv) {
37 assert(_stack_size > sizeof(Stack_Frame));
38
39 _stack_ptr = _stack_base +
40 (_stack_size - sizeof(Stack_Frame));
41
42 auto *frame = (Stack_Frame *)_stack_ptr;
43 frame->crsr.psr = 0x01000000; // Set the Thumb-Bit
44 frame->crsr.pc = (void (*)(void))(&task::_run);
45 frame->crsr.r0 = this; // Set the 'this'-pointer
46 frame->cesr.exc_return = EXC_RETURN_THREAD_PSP_NOFP;
47 frame->cesr.ctrl = CONTROL_SPSEL_PSP |
48 (priv ? CONTROL_PRIV : CONTROL_NOT_PRIV);
49}
50
51void task::_context_switch() {
52 // Trigger a PendSV interrupt
53 PPB.ICSR.PENDSVSET = 1;
54}
55
56uint64_t task::millis() {
57 uint32_t lo = TIMER0.TIMELR;
58 uint32_t hi = TIMER0.TIMEHR;
59 return (((uint64_t)hi << 32l) | lo) / timer_ticks_per_us / 1000;
60}
61
62bool task::is_irq_context() {
63 return __get_IPSR() != 0;
64}
65
66bool task::multitasking_running() {
67 return multitasking_on_core[SIO.CPUID];
68}
69
70int8_t task::get_core() {
71 return SIO.CPUID;
72}
73
74void task::start_scheduler() {
75 assert(!multitasking_running());
76 sys_call(SYS_START_SCHEDULER);
77}
78
79void task::yield() {
80 if(multitasking_running()) {
81 sys_call(SYS_YIELD);
82 }
83}
84
85void task::cpu_sleep() {
86 __WFE();
87}
88
89void task::enterCritical() {
90 NVIC_DisableIRQ(PendSV_IRQn);
91// __disable_irq();
92 __DSB();
93 __ISB();
94}
95
96void task::leaveCritical() {
97 NVIC_EnableIRQ(PendSV_IRQn);
98// __enable_irq();
99 __ISB();
100}
101
102bool task::isPrivileged() const {
103 uint32_t ctrl;
104 if (_run_ptr[_core] == this) {
105 ctrl = __get_CONTROL();
106 } else {
107 ctrl = ((Stack_Frame *)_stack_ptr)->cesr.ctrl;
108 }
109 return (ctrl & CONTROL_nPRIV_Msk) == 0;
110}
111
112bool task::isUsingFloat() const {
113 uint32_t ctrl;
114 if (_run_ptr[_core] == this) {
115 ctrl = __get_CONTROL();
116 } else {
117 ctrl = ((Stack_Frame *)_stack_ptr)->cesr.ctrl;
118 }
119 return ctrl & CONTROL_FPCA_Msk;
120}
121
123// IRQ handlers
125
126extern "C" {
127
128void __attribute__((weak)) SysTick_Handler(void) {
129 task::_tick_handler();
130}
131
132uint8_t * switch_context(uint8_t * last_sp) {
133 task::_setStackPtr(last_sp);
134#ifdef CHECK_STACK_OVERFLOW
135 assert((last_sp - task::_getStackBase()) > 10);
136#endif
137 task::_switchToNext();
138 return task::_getStackPtr();
139}
140
141void __attribute__((naked)) PendSV_Handler(void) {
142 asm volatile(
143 " mrs r0, psp @ get the current SP \n"
144 " tst lr, #0x10 @ EXC_RETURN_INT_ONLY_STACK_FRAME \n"
145 " it eq @ \n"
146 " vstmdbeq r0!, {s16-s31} @ push additional FP registers \n"
147 " mov r2, lr @ \n"
148 " mrs r3, control @ \n"
149 " stmdb r0!, {r2-r11} @ save lr, control, r4-r11 \n"
150 " bl switch_context @ \n"
151 " ldmia r0!, {r2-r11} @ get lr, control, r4-r11 \n"
152 " mov lr, r2 @ \n"
153 " msr control, r3 @ \n"
154 " isb @ arch recommendation \n"
155 " tst lr, #0x10 @ EXC_RETURN_INT_ONLY_STACK_FRAME \n"
156 " it eq @ \n"
157 " vldmiaeq r0!, {s16-s31} @ pop additional FP registers \n"
158 " msr psp, r0 @ \n"
159 " bx lr @ jump to new task \n");
160}
161
162void __attribute__((naked)) SVC_Handler(void) {
163 asm volatile(
164 " tst lr, #4 @ EXC_RETURN_PROCESS_STACK_POINTER \n"
165 " ite eq @ \n"
166 " mrseq r0, msp @ R0 (SP) will be first parameter \n"
167 " mrsne r0, psp @ of SCV_Handler_C, \n"
168 " tst lr, 0x20 @ EXC_RETURN_NORMAL_CALLEE_STACKING \n"
169 " it eq @ \n"
170 " addeq r0, #40 @ skip additional context if existing \n"
171 " mov r1, lr @ R1 (LR) is second parameter \n"
172 " bl SVC_Handler_C @ Call C part of SVC handler \n"
173 " bx r0 @ Use return value as EXC_RETURN \n"
174 );
175}
176
177uint32_t SVC_Handler_C(uint32_t * sp, uint32_t exc_return) {
178
179 // Get the PC value from the stack
180 auto * pc = (uint16_t *)sp[6];
181 // Move back one instruction (SVC call) and extract parameter
182 uint16_t svc_arg = pc[-1] & 0xff;
183
184 // uint32_t p2 = sp[2]; // R2
185 // uint32_t p3 = sp[3]; // R3
186
187 switch(svc_arg) {
189 case SYS_START_SCHEDULER:
191 {
192 // Mark this core as multitasking
193 multitasking_on_core[SIO.CPUID] = true;
194
195 // Disable the sysTick Timer
196 SysTick->CTRL = 0;
197
198 // Start the Idle task with the lowest priority (1).
199 idle_tasks[SIO.CPUID].sign_up(core_t::CURRENT_CORE, 1);
200
201 // The first task to run is the first created task
202 task::_switchToHead();
203
204 // Set scheduler priorities
205 // NVIC_SetPriority(SVCall_IRQn, 14);
206 NVIC_SetPriority(PendSV_IRQn, 15);
207 NVIC_SetPriority(SysTick_IRQn, 15); // Lowest possible value
208
209 // Set SysTick to TICK_FREQUENCY. Use the internal
210 // reference clock, not the CPU system clock.
211 PPB.SYST_RVR = (systick_ref_freq / TICK_FREQUENCY)-1;
212 PPB.SYST_CVR = 0;
213 PPB.SYST_CSR.CLKSOURCE = 0; // use external ref clock
214 PPB.SYST_CSR.TICKINT = 1;
215 PPB.SYST_CSR.ENABLE = 1;
216
217 // Return to (secure) thread mode and use PSP
218 exc_return = EXC_RETURN_THREAD_PSP_NOFP;
219
220 // Only restore registers r0-PSR, because these
221 // will be restored on return of the SVC-handler
222 __set_PSP((uint32_t)(task::_getStackPtr() +
223 sizeof(callee_saved_registers)));
224
225 // Set control register (set privileged)
226 __set_CONTROL(((Stack_Frame *)task::_getStackPtr())->cesr.ctrl);
227 __ISB();
228 break;
229 }
231 case SYS_YIELD:
233 {
234 task::_scheduler();
235 break;
236 }
238 default:
240 {
241 assert(false);
242 }
243 }
244 return exc_return;
245}
246
247} // extern "C"
248
249#pragma GCC diagnostic pop
#define SysTick
#define CONTROL_FPCA_Msk
#define CONTROL_nPRIV_Msk
#define __ISB()
Instruction Synchronization Barrier.
#define __DSB()
Data Synchronization Barrier.
#define __WFE
Wait For Event.
__STATIC_INLINE void __set_CONTROL(uint32_t control)
Set Control Register.
__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)
Set Process Stack Pointer.
__STATIC_INLINE uint32_t __get_CONTROL(void)
Enable IRQ Interrupts.
__STATIC_INLINE uint32_t __get_IPSR(void)
Get IPSR Register.
void __attribute__((noreturn))(*rom_reset_usb_boot_fn)(uint32_t
Reboot the device into BOOTSEL mode.
Definition bootrom.h:66