YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
task_rp2040.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 RP2040/Cortex M0+
15//
16#pragma GCC diagnostic push
17#pragma GCC diagnostic ignored "-Wpmf-conversions"
18
19#include <cassert>
20#include "task_rp2040.h"
21#include "multicore_rp2040.h"
22#include "task_idle.h"
23#include "system_rp2040.h"
24#include "yahal_config.h"
25
26#include "RP2040.h"
27using namespace _PPB_;
28using namespace _SIO_;
29using namespace _TIMER_;
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;
47 frame->cesr.ctrl = priv ? 0x02 : 0x03;
48}
49
50void task::_context_switch() {
51 // Trigger a PendSV interrupt
52 PPB.ICSR.PENDSVSET = 1;
53}
54
55uint64_t task::millis() {
56 uint32_t lo = TIMER.TIMELR;
57 uint32_t hi = TIMER.TIMEHR;
58 return (((uint64_t)hi << 32l) | lo) / _ticks_per_millis;
59}
60
61bool task::is_irq_context() {
62 return __get_IPSR() != 0;
63}
64
65bool task::multitasking_running() {
66 return multitasking_on_core[SIO.CPUID];
67}
68
69int8_t task::get_core() {
70 return SIO.CPUID;
71}
72
73void task::start_scheduler() {
74 assert(!multitasking_running());
75 sys_call(SYS_START_SCHEDULER);
76}
77
78void task::yield() {
79 if(multitasking_running() && !is_irq_context()) {
80 sys_call(SYS_YIELD);
81 }
82}
83
84void task::cpu_sleep() {
85 __WFE();
86}
87
88void task::enterCritical() {
89 NVIC_DisableIRQ(PendSV_IRQn);
90// __disable_irq();
91}
92
93void task::leaveCritical() {
94 NVIC_EnableIRQ(PendSV_IRQn);
95// __enable_irq();
96}
97
98bool task::isPrivileged() const {
99 uint32_t ctrl;
100 if (_run_ptr[_core] == this) {
101 ctrl = __get_CONTROL();
102 } else {
103 ctrl = ((Stack_Frame *)_stack_ptr)->cesr.ctrl;
104 }
105 return (ctrl & CONTROL_nPRIV_Msk) == 0;
106}
107
108bool task::isUsingFloat() const {
109 uint32_t ctrl;
110 if (_run_ptr[_core] == this) {
111 ctrl = __get_CONTROL();
112 return ctrl & 0x04;
113 } else {
114 ctrl = ((Stack_Frame *)_stack_ptr)->cesr.ctrl;
115 return ctrl & 0x10;
116 }
117}
118
120// IRQ handlers
122
123extern "C" {
124
125void __attribute__((weak)) SysTick_Handler(void) {
126 task::_tick_handler();
127}
128
129uint8_t * switch_context(uint8_t * last_sp) {
130 task::_setStackPtr(last_sp);
131#ifdef CHECK_STACK_OVERFLOW
132 assert((last_sp - task::_getStackBase()) > 10);
133#endif
134 task::_switchToNext();
135 return task::_getStackPtr();
136}
137
138void __attribute__((naked)) PendSV_Handler(void) {
139 asm volatile(
140 " .syntax unified @ \n"
141 " mrs r0, psp @ \n"
142 " mov r2, lr @ \n"
143 " mrs r3, control @ \n"
144 " subs r0, r0, #40 @ \n"
145 " stmia r0!,{r2-r7} @ \n"
146 " mov r4, r8 @ \n"
147 " mov r5, r9 @ \n"
148 " mov r6, r10 @ \n"
149 " mov r7, r11 @ \n"
150 " stmia r0!,{r4-r7} @ \n"
151 " subs r0, r0, #40 @ \n"
152 " bl switch_context @ \n"
153 " adds r0, r0, #24 @ \n"
154 " ldmia r0!, {r4-r7} @ \n"
155 " mov r8, r4 @ \n"
156 " mov r9, r5 @ \n"
157 " mov r10, r6 @ \n"
158 " mov r11, r7 @ \n"
159 " msr psp, r0 @ \n"
160 " subs r0, r0, #40 @ \n"
161 " ldmia r0!, {r2-r7} @ \n"
162 " mov lr, r2 @ \n"
163 " msr control, r3 @ \n"
164 " bx lr @ \n");
165}
166
167void __attribute__((naked)) SVC_Handler(void) {
168 asm volatile(
169 " movs r0, #4 @ EXC_RETURN_PROCESS_STACK_POINTER \n"
170 " mov r1, lr @ \n"
171 " tst r0, r1 @ Check which stack to use \n"
172 " beq svc1 @ \n"
173 " mrs r0, psp @ Use PSP \n"
174 " b svc2 @ \n"
175 "svc1: mrs r0, msp @ Use MSP \n"
176 "svc2: mov r1, lr @ R1 (LR) is second parameter \n"
177 " bl SVC_Handler_C @ Call C part of SVC_Handler \n"
178 " bx r0 @ Use return value as EXC_RETURN \n"
179 );
180}
181
182uint32_t SVC_Handler_C(uint32_t * sp, uint32_t exc_return) {
183
184 // Get the PC value from the stack
185 auto * pc = (uint16_t *)sp[6];
186 // Move back one instruction (SVC call) and extract parameter
187 uint16_t svc_arg = pc[-1] & 0xff;
188
189 // uint32_t p2 = sp[2]; // R2
190 // uint32_t p3 = sp[3]; // R3
191
192 switch(svc_arg) {
194 case SYS_START_SCHEDULER:
196 {
197 // Mark this core as multitasking
198 multitasking_on_core[SIO.CPUID] = true;
199
200 // Disable the sysTick Timer
201 SysTick->CTRL = 0;
202
203 // Start the Idle task with the lowest priority (1).
204 idle_tasks[SIO.CPUID].sign_up(core_t::CURRENT_CORE, 1);
205
206 // The first task to run is the first created task
207 task::_switchToHead();
208
209 // Set scheduler priorities
210 // NVIC_SetPriority(SVCall_IRQn, 2);
211 NVIC_SetPriority(PendSV_IRQn, 3);
212 NVIC_SetPriority(SysTick_IRQn, 3); // Lowest possible value
213
214 // set SysTick to TICK_FREQUENCY and priority to
215 // second-highest level (highest is used for
216 // application IRQs like timers and GPIO)
217 PPB.SYST_RVR = (CLK_SYS / TICK_FREQUENCY)-1;
218 PPB.SYST_CVR = 0;
219 PPB.SYST_CSR.CLKSOURCE = 1; // use CLK_SYS
220 PPB.SYST_CSR.TICKINT = 1;
221 PPB.SYST_CSR.ENABLE = 1;
222
223 // Return to thread mode and use PSP
224 exc_return = EXC_RETURN_THREAD_PSP;
225
226 // Only restore registers r0-PSR, because these
227 // will be restored on return of the SVC-handler
228 __set_PSP((uint32_t)(task::_getStackPtr() +
229 sizeof(callee_saved_registers)));
230
231 // Set control register (set privileged)
232 __set_CONTROL(((Stack_Frame *)task::_getStackPtr())->cesr.ctrl);
233 __ISB();
234 break;
235 }
237 case SYS_YIELD:
239 {
240 task::_scheduler();
241 break;
242 }
244 default:
246 {
247 assert(false);
248 }
249 }
250 return exc_return;
251}
252
253} // extern "C"
254
255#pragma GCC diagnostic pop
CMSIS-Core(M) Device Peripheral Access Layer Header File for Device RP2040.
#define SysTick
#define CONTROL_nPRIV_Msk
#define __ISB()
Instruction 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