1 /*
2     FreeRTOS V8.2.3 - Copyright (C) 2015 Real Time Engineers Ltd.
3     All rights reserved
4 
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
6 
7     This file is part of the FreeRTOS distribution and was contributed
8     to the project by Technolution B.V. (www.technolution.nl,
9     freertos-riscv@technolution.eu) under the terms of the FreeRTOS
10     contributors license.
11 
12     FreeRTOS is free software; you can redistribute it and/or modify it under
13     the terms of the GNU General Public License (version 2) as published by the
14     Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.
15 
16     ***************************************************************************
17     >>!   NOTE: The modification to the GPL is included to allow you to     !<<
18     >>!   distribute a combined work that includes FreeRTOS without being   !<<
19     >>!   obliged to provide the source code for proprietary components     !<<
20     >>!   outside of the FreeRTOS kernel.                                   !<<
21     ***************************************************************************
22 
23     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
24     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25     FOR A PARTICULAR PURPOSE.  Full license text is available on the following
26     link: http://www.freertos.org/a00114.html
27 
28     ***************************************************************************
29      *                                                                       *
30      *    FreeRTOS provides completely free yet professionally developed,    *
31      *    robust, strictly quality controlled, supported, and cross          *
32      *    platform software that is more than just the market leader, it     *
33      *    is the industry's de facto standard.                               *
34      *                                                                       *
35      *    Help yourself get started quickly while simultaneously helping     *
36      *    to support the FreeRTOS project by purchasing a FreeRTOS           *
37      *    tutorial book, reference manual, or both:                          *
38      *    http://www.FreeRTOS.org/Documentation                              *
39      *                                                                       *
40     ***************************************************************************
41 
42     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
43     the FAQ page "My application does not run, what could be wrong?".  Have you
44     defined configASSERT()?
45 
46     http://www.FreeRTOS.org/support - In return for receiving this top quality
47     embedded software for free we request you assist our global community by
48     participating in the support forum.
49 
50     http://www.FreeRTOS.org/training - Investing in training allows your team to
51     be as productive as possible as early as possible.  Now you can receive
52     FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
53     Ltd, and the world's leading authority on the world's leading RTOS.
54 
55     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
56     including FreeRTOS+Trace - an indispensable productivity tool, a DOS
57     compatible FAT file system, and our tiny thread aware UDP/IP stack.
58 
59     http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
60     Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
61 
62     http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
63     Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
64     licenses offer ticketed support, indemnification and commercial middleware.
65 
66     http://www.SafeRTOS.com - High Integrity Systems also provide a safety
67     engineered and independently SIL3 certified version for use in safety and
68     mission critical applications that require provable dependability.
69 
70     1 tab == 4 spaces!
71 */
72 
73 /*-----------------------------------------------------------------------
74  * Implementation of functions defined in portable.h for the RISC-V port.
75  *----------------------------------------------------------------------*/
76 
77 #include "sdkconfig.h"
78 #include <string.h>
79 #include "soc/soc_caps.h"
80 #include "soc/periph_defs.h"
81 #include "soc/system_reg.h"
82 #include "hal/systimer_hal.h"
83 #include "hal/systimer_ll.h"
84 #include "riscv/rvruntime-frames.h"
85 #include "riscv/riscv_interrupts.h"
86 #include "riscv/interrupt.h"
87 #include "esp_private/crosscore_int.h"
88 #include "esp_attr.h"
89 #include "esp_system.h"
90 #include "esp_intr_alloc.h"
91 #include "esp_debug_helpers.h"
92 #include "esp_log.h"
93 #include "FreeRTOS.h"       /* This pulls in portmacro.h */
94 #include "task.h"
95 #include "portmacro.h"
96 #include "port_systick.h"
97 
98 
99 
100 /* ---------------------------------------------------- Variables ------------------------------------------------------
101  *
102  * ------------------------------------------------------------------------------------------------------------------ */
103 
104 static const char *TAG = "cpu_start"; // [refactor-todo]: might be appropriate to change in the future, but
105 
106 /**
107  * @brief A variable is used to keep track of the critical section nesting.
108  * @note This variable has to be stored as part of the task context and must be initialized to a non zero value
109  *       to ensure interrupts don't inadvertently become unmasked before the scheduler starts.
110  *       As it is stored as part of the task context it will automatically be set to 0 when the first task is started.
111  */
112 static UBaseType_t uxCriticalNesting = 0;
113 static UBaseType_t uxSavedInterruptState = 0;
114 BaseType_t uxSchedulerRunning = 0;
115 UBaseType_t uxInterruptNesting = 0;
116 BaseType_t xPortSwitchFlag = 0;
117 __attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE];
118 StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK)));
119 
120 
121 
122 /* ------------------------------------------------ FreeRTOS Portable --------------------------------------------------
123  * - Provides implementation for functions required by FreeRTOS
124  * - Declared in portable.h
125  * ------------------------------------------------------------------------------------------------------------------ */
126 
127 // ----------------- Scheduler Start/End -------------------
128 
129 extern void esprv_intc_int_set_threshold(int); // FIXME, this function is in ROM only
xPortStartScheduler(void)130 BaseType_t xPortStartScheduler(void)
131 {
132     uxInterruptNesting = 0;
133     uxCriticalNesting = 0;
134     uxSchedulerRunning = 0;
135 
136     /* Setup the hardware to generate the tick. */
137     vPortSetupTimer();
138 
139     esprv_intc_int_set_threshold(1); /* set global INTC masking level */
140     riscv_global_interrupts_enable();
141 
142     vPortYield();
143 
144     /*Should not get here*/
145     return pdFALSE;
146 }
147 
vPortEndScheduler(void)148 void vPortEndScheduler(void)
149 {
150     /* very unlikely this function will be called, so just trap here */
151     abort();
152 }
153 
154 // ------------------------ Stack --------------------------
155 
prvTaskExitError(void)156 static void prvTaskExitError(void)
157 {
158     /* A function that implements a task must not exit or attempt to return to
159     its caller as there is nothing to return to.  If a task wants to exit it
160     should instead call vTaskDelete( NULL ).
161 
162     Artificially force an assert() to be triggered if configASSERT() is
163     defined, then stop here so application writers can catch the error. */
164     configASSERT(uxCriticalNesting == ~0UL);
165     portDISABLE_INTERRUPTS();
166     abort();
167 }
168 
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)169 StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
170 {
171     extern uint32_t __global_pointer$;
172     uint8_t *task_thread_local_start;
173     uint8_t *threadptr;
174     extern char _thread_local_start, _thread_local_end, _flash_rodata_start;
175 
176     /* Byte pointer, so that subsequent calculations don't depend on sizeof(StackType_t). */
177     uint8_t *sp = (uint8_t *) pxTopOfStack;
178 
179     /* Set up TLS area.
180      * The following diagram illustrates the layout of link-time and run-time
181      * TLS sections.
182      *
183      *          +-------------+
184      *          |Section:     |      Linker symbols:
185      *          |.flash.rodata|      ---------------
186      *       0x0+-------------+ <-- _flash_rodata_start
187      *        ^ |             |
188      *        | | Other data  |
189      *        | |     ...     |
190      *        | +-------------+ <-- _thread_local_start
191      *        | |.tbss        | ^
192      *        v |             | |
193      *    0xNNNN|int example; | | (thread_local_size)
194      *          |.tdata       | v
195      *          +-------------+ <-- _thread_local_end
196      *          | Other data  |
197      *          |     ...     |
198      *          |             |
199      *          +-------------+
200      *
201      *                                Local variables of
202      *                              pxPortInitialiseStack
203      *                             -----------------------
204      *          +-------------+ <-- pxTopOfStack
205      *          |.tdata (*)   |  ^
206      *        ^ |int example; |  |(thread_local_size
207      *        | |             |  |
208      *        | |.tbss (*)    |  v
209      *        | +-------------+ <-- task_thread_local_start
210      * 0xNNNN | |             |  ^
211      *        | |             |  |
212      *        | |             |  |_thread_local_start - _rodata_start
213      *        | |             |  |
214      *        | |             |  v
215      *        v +-------------+ <-- threadptr
216      *
217      *   (*) The stack grows downward!
218      */
219 
220     uint32_t thread_local_sz = (uint32_t) (&_thread_local_end - &_thread_local_start);
221     thread_local_sz = ALIGNUP(0x10, thread_local_sz);
222     sp -= thread_local_sz;
223     task_thread_local_start = sp;
224     memcpy(task_thread_local_start, &_thread_local_start, thread_local_sz);
225     threadptr = task_thread_local_start - (&_thread_local_start - &_flash_rodata_start);
226 
227     /* Simulate the stack frame as it would be created by a context switch interrupt. */
228     sp -= RV_STK_FRMSZ;
229     RvExcFrame *frame = (RvExcFrame *)sp;
230     memset(frame, 0, sizeof(*frame));
231     frame->ra = (UBaseType_t)prvTaskExitError;
232     frame->mepc = (UBaseType_t)pxCode;
233     frame->a0 = (UBaseType_t)pvParameters;
234     frame->gp = (UBaseType_t)&__global_pointer$;
235     frame->tp = (UBaseType_t)threadptr;
236 
237     //TODO: IDF-2393
238     return (StackType_t *)frame;
239 }
240 
241 
242 
243 /* ---------------------------------------------- Port Implementations -------------------------------------------------
244  *
245  * ------------------------------------------------------------------------------------------------------------------ */
246 
247 // --------------------- Interrupts ------------------------
248 
xPortInIsrContext(void)249 BaseType_t xPortInIsrContext(void)
250 {
251     return uxInterruptNesting;
252 }
253 
xPortInterruptedFromISRContext(void)254 BaseType_t IRAM_ATTR xPortInterruptedFromISRContext(void)
255 {
256     /* For single core, this can be the same as xPortInIsrContext() because reading it is atomic */
257     return uxInterruptNesting;
258 }
259 
260 // ---------------------- Spinlocks ------------------------
261 
262 
263 
264 // ------------------ Critical Sections --------------------
265 
vPortEnterCritical(void)266 void vPortEnterCritical(void)
267 {
268     BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR();
269     uxCriticalNesting++;
270 
271     if (uxCriticalNesting == 1) {
272         uxSavedInterruptState = state;
273     }
274 }
275 
vPortExitCritical(void)276 void vPortExitCritical(void)
277 {
278     if (uxCriticalNesting > 0) {
279         uxCriticalNesting--;
280         if (uxCriticalNesting == 0) {
281             portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptState);
282         }
283     }
284 }
285 
286 // ---------------------- Yielding -------------------------
287 
vPortSetInterruptMask(void)288 int vPortSetInterruptMask(void)
289 {
290     int ret;
291     unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
292     ret = REG_READ(INTERRUPT_CORE0_CPU_INT_THRESH_REG);
293     REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, RVHAL_EXCM_LEVEL);
294     RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
295     /**
296      * In theory, this function should not return immediately as there is a
297      * delay between the moment we mask the interrupt threshold register and
298      * the moment a potential lower-priority interrupt is triggered (as said
299      * above), it should have a delay of 2 machine cycles/instructions.
300      *
301      * However, in practice, this function has an epilogue of one instruction,
302      * thus the instruction masking the interrupt threshold register is
303      * followed by two instructions: `ret` and `csrrs` (RV_SET_CSR).
304      * That's why we don't need any additional nop instructions here.
305      */
306     return ret;
307 }
308 
vPortClearInterruptMask(int mask)309 void vPortClearInterruptMask(int mask)
310 {
311     REG_WRITE(INTERRUPT_CORE0_CPU_INT_THRESH_REG, mask);
312     /**
313      * The delay between the moment we unmask the interrupt threshold register
314      * and the moment the potential requested interrupt is triggered is not
315      * null: up to three machine cycles/instructions can be executed.
316      *
317      * When compilation size optimization is enabled, this function and its
318      * callers returning void will have NO epilogue, thus the instruction
319      * following these calls will be executed.
320      *
321      * If the requested interrupt is a context switch to a higher priority
322      * task then the one currently running, we MUST NOT execute any instruction
323      * before the interrupt effectively happens.
324      * In order to prevent this, force this routine to have a 3-instruction
325      * delay before exiting.
326      */
327     asm volatile ( "nop" );
328     asm volatile ( "nop" );
329     asm volatile ( "nop" );
330 }
331 
vPortYield(void)332 void vPortYield(void)
333 {
334     if (uxInterruptNesting) {
335         vPortYieldFromISR();
336     } else {
337 
338         esp_crosscore_int_send_yield(0);
339         /* There are 3-4 instructions of latency between triggering the software
340            interrupt and the CPU interrupt happening. Make sure it happened before
341            we return, otherwise vTaskDelay() may return and execute 1-2
342            instructions before the delay actually happens.
343 
344            (We could use the WFI instruction here, but there is a chance that
345            the interrupt will happen while evaluating the other two conditions
346            for an instant yield, and if that happens then the WFI would be
347            waiting for the next interrupt to occur...)
348         */
349         while (uxSchedulerRunning && uxCriticalNesting == 0 && REG_READ(SYSTEM_CPU_INTR_FROM_CPU_0_REG) != 0) {}
350     }
351 }
352 
vPortYieldFromISR(void)353 void vPortYieldFromISR( void )
354 {
355     traceISR_EXIT_TO_SCHEDULER();
356     uxSchedulerRunning = 1;
357     xPortSwitchFlag = 1;
358 }
359 
vPortYieldOtherCore(BaseType_t coreid)360 void vPortYieldOtherCore(BaseType_t coreid)
361 {
362     esp_crosscore_int_send_yield(coreid);
363 }
364 
365 // ------------------- Hook Functions ----------------------
366 
vApplicationStackOverflowHook(TaskHandle_t xTask,char * pcTaskName)367 void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
368 {
369 #define ERR_STR1 "***ERROR*** A stack overflow in task "
370 #define ERR_STR2 " has been detected."
371     const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2};
372 
373     char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = {0};
374 
375     char *dest = buf;
376     for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
377         dest = strcat(dest, str[i]);
378     }
379     esp_system_abort(buf);
380 }
381 
382 // ----------------------- System --------------------------
383 
xPortGetTickRateHz(void)384 uint32_t xPortGetTickRateHz(void)
385 {
386     return (uint32_t)configTICK_RATE_HZ;
387 }
388 
389 #define STACK_WATCH_AREA_SIZE 32
390 #define STACK_WATCH_POINT_NUMBER (SOC_CPU_WATCHPOINTS_NUM - 1)
391 
vPortSetStackWatchpoint(void * pxStackStart)392 void vPortSetStackWatchpoint(void *pxStackStart)
393 {
394     uint32_t addr = (uint32_t)pxStackStart;
395     addr = (addr + (STACK_WATCH_AREA_SIZE - 1)) & (~(STACK_WATCH_AREA_SIZE - 1));
396     esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_WATCHPOINT_STORE);
397 }
398 
399 
400 
401 /* ---------------------------------------------- Misc Implementations -------------------------------------------------
402  *
403  * ------------------------------------------------------------------------------------------------------------------ */
404 
405 // --------------------- App Start-up ----------------------
406 
407 /* [refactor-todo]: See if we can include this through a header */
408 extern void esp_startup_start_app_common(void);
409 
esp_startup_start_app(void)410 void esp_startup_start_app(void)
411 {
412     esp_startup_start_app_common();
413 
414     ESP_LOGI(TAG, "Starting scheduler.");
415     vTaskStartScheduler();
416 }
417