]> git.sur5r.net Git - freertos/blob - Source/portable/GCC/ARM_CM3/port.c
Ready for V5.2.0 release.
[freertos] / Source / portable / GCC / ARM_CM3 / port.c
1 /*\r
2         FreeRTOS.org V5.2.0 - Copyright (C) 2003-2009 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify it \r
7         under the terms of the GNU General Public License (version 2) as published\r
8         by the Free Software Foundation and modified by the FreeRTOS exception.\r
9 \r
10         FreeRTOS.org is distributed in the hope that it will be useful, but WITHOUT\r
11         ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \r
12         FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for \r
13         more details.\r
14 \r
15         You should have received a copy of the GNU General Public License along \r
16         with FreeRTOS.org; if not, write to the Free Software Foundation, Inc., 59 \r
17         Temple Place, Suite 330, Boston, MA  02111-1307  USA.\r
18 \r
19         A special exception to the GPL is included to allow you to distribute a \r
20         combined work that includes FreeRTOS.org without being obliged to provide\r
21         the source code for any proprietary components.  See the licensing section\r
22         of http://www.FreeRTOS.org for full details.\r
23 \r
24 \r
25         ***************************************************************************\r
26         *                                                                         *\r
27         * Get the FreeRTOS eBook!  See http://www.FreeRTOS.org/Documentation      *\r
28         *                                                                         *\r
29         * This is a concise, step by step, 'hands on' guide that describes both   *\r
30         * general multitasking concepts and FreeRTOS specifics. It presents and   *\r
31         * explains numerous examples that are written using the FreeRTOS API.     *\r
32         * Full source code for all the examples is provided in an accompanying    *\r
33         * .zip file.                                                              *\r
34         *                                                                         *\r
35         ***************************************************************************\r
36 \r
37         1 tab == 4 spaces!\r
38 \r
39         Please ensure to read the configuration and relevant port sections of the\r
40         online documentation.\r
41 \r
42         http://www.FreeRTOS.org - Documentation, latest information, license and\r
43         contact details.\r
44 \r
45         http://www.SafeRTOS.com - A version that is certified for use in safety\r
46         critical systems.\r
47 \r
48         http://www.OpenRTOS.com - Commercial support, development, porting,\r
49         licensing and training services.\r
50 */\r
51 \r
52 /*-----------------------------------------------------------\r
53  * Implementation of functions defined in portable.h for the ARM CM3 port.\r
54  *----------------------------------------------------------*/\r
55 \r
56 /* Scheduler includes. */\r
57 #include "FreeRTOS.h"\r
58 #include "task.h"\r
59 \r
60 /* For backward compatibility, ensure configKERNEL_INTERRUPT_PRIORITY is\r
61 defined.  The value should also ensure backward compatibility.\r
62 FreeRTOS.org versions prior to V4.4.0 did not include this definition. */\r
63 #ifndef configKERNEL_INTERRUPT_PRIORITY\r
64         #define configKERNEL_INTERRUPT_PRIORITY 255\r
65 #endif\r
66 \r
67 /* Constants required to manipulate the NVIC. */\r
68 #define portNVIC_SYSTICK_CTRL           ( ( volatile unsigned portLONG *) 0xe000e010 )\r
69 #define portNVIC_SYSTICK_LOAD           ( ( volatile unsigned portLONG *) 0xe000e014 )\r
70 #define portNVIC_INT_CTRL                       ( ( volatile unsigned portLONG *) 0xe000ed04 )\r
71 #define portNVIC_SYSPRI2                        ( ( volatile unsigned portLONG *) 0xe000ed20 )\r
72 #define portNVIC_SYSTICK_CLK            0x00000004\r
73 #define portNVIC_SYSTICK_INT            0x00000002\r
74 #define portNVIC_SYSTICK_ENABLE         0x00000001\r
75 #define portNVIC_PENDSVSET                      0x10000000\r
76 #define portNVIC_PENDSV_PRI                     ( ( ( unsigned portLONG ) configKERNEL_INTERRUPT_PRIORITY ) << 16 )\r
77 #define portNVIC_SYSTICK_PRI            ( ( ( unsigned portLONG ) configKERNEL_INTERRUPT_PRIORITY ) << 24 )\r
78 \r
79 /* Constants required to set up the initial stack. */\r
80 #define portINITIAL_XPSR                        ( 0x01000000 )\r
81 \r
82 /* The priority used by the kernel is assigned to a variable to make access\r
83 from inline assembler easier. */\r
84 const unsigned portLONG ulKernelPriority = configKERNEL_INTERRUPT_PRIORITY;\r
85 \r
86 /* Each task maintains its own interrupt status in the critical nesting\r
87 variable. */\r
88 static unsigned portBASE_TYPE uxCriticalNesting = 0xaaaaaaaa;\r
89 \r
90 /*\r
91  * Setup the timer to generate the tick interrupts.\r
92  */\r
93 static void prvSetupTimerInterrupt( void );\r
94 \r
95 /*\r
96  * Exception handlers.\r
97  */\r
98 void xPortPendSVHandler( void ) __attribute__ (( naked ));\r
99 void xPortSysTickHandler( void );\r
100 void vPortSVCHandler( void ) __attribute__ (( naked ));\r
101 \r
102 /*\r
103  * Start first task is a separate function so it can be tested in isolation.\r
104  */\r
105 void vPortStartFirstTask( void ) __attribute__ (( naked ));\r
106 \r
107 /*-----------------------------------------------------------*/\r
108 \r
109 /*\r
110  * See header file for description.\r
111  */\r
112 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )\r
113 {\r
114         /* Simulate the stack frame as it would be created by a context switch\r
115         interrupt. */\r
116         *pxTopOfStack = portINITIAL_XPSR;       /* xPSR */\r
117         pxTopOfStack--;\r
118         *pxTopOfStack = ( portSTACK_TYPE ) pxCode;      /* PC */\r
119         pxTopOfStack--;\r
120         *pxTopOfStack = 0;      /* LR */\r
121         pxTopOfStack -= 5;      /* R12, R3, R2 and R1. */\r
122         *pxTopOfStack = ( portSTACK_TYPE ) pvParameters;        /* R0 */\r
123         pxTopOfStack -= 8;      /* R11, R10, R9, R8, R7, R6, R5 and R4. */\r
124 \r
125         return pxTopOfStack;\r
126 }\r
127 /*-----------------------------------------------------------*/\r
128 \r
129 void vPortSVCHandler( void )\r
130 {\r
131         asm volatile (\r
132                                         "       ldr     r3, pxCurrentTCBConst2          \n" /* Restore the context. */\r
133                                         "       ldr r1, [r3]                                    \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */\r
134                                         "       ldr r0, [r1]                                    \n" /* The first item in pxCurrentTCB is the task top of stack. */\r
135                                         "       ldmia r0!, {r4-r11}                             \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */\r
136                                         "       msr psp, r0                                             \n" /* Restore the task stack pointer. */\r
137                                         "       mov r0, #0                                              \n"\r
138                                         "       msr     basepri, r0                                     \n"\r
139                                         "       orr r14, #0xd                                   \n"\r
140                                         "       bx r14                                                  \n"\r
141                                         "                                                                       \n"\r
142                                         "       .align 2                                                \n"\r
143                                         "pxCurrentTCBConst2: .word pxCurrentTCB                         \n"\r
144                                 );\r
145 }\r
146 /*-----------------------------------------------------------*/\r
147 \r
148 void vPortStartFirstTask( void )\r
149 {\r
150         asm volatile(\r
151                                         " ldr r0, =0xE000ED08   \n" /* Use the NVIC offset register to locate the stack. */\r
152                                         " ldr r0, [r0]                  \n"\r
153                                         " ldr r0, [r0]                  \n"\r
154                                         " msr msp, r0                   \n" /* Set the msp back to the start of the stack. */\r
155                                         " svc 0                                 \n" /* System call to start first task. */\r
156                                 );\r
157 }\r
158 /*-----------------------------------------------------------*/\r
159 \r
160 /*\r
161  * See header file for description.\r
162  */\r
163 portBASE_TYPE xPortStartScheduler( void )\r
164 {\r
165         /* Make PendSV, CallSV and SysTick the same priroity as the kernel. */\r
166         *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;\r
167         *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;\r
168 \r
169         /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
170         here already. */\r
171         prvSetupTimerInterrupt();\r
172 \r
173         /* Initialise the critical nesting count ready for the first task. */\r
174         uxCriticalNesting = 0;\r
175 \r
176         /* Start the first task. */\r
177         vPortStartFirstTask();\r
178 \r
179         /* Should not get here! */\r
180         return 0;\r
181 }\r
182 /*-----------------------------------------------------------*/\r
183 \r
184 void vPortEndScheduler( void )\r
185 {\r
186         /* It is unlikely that the CM3 port will require this function as there\r
187         is nothing to return to.  */\r
188 }\r
189 /*-----------------------------------------------------------*/\r
190 \r
191 void vPortYieldFromISR( void )\r
192 {\r
193         /* Set a PendSV to request a context switch. */\r
194         *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;\r
195 }\r
196 /*-----------------------------------------------------------*/\r
197 \r
198 void vPortEnterCritical( void )\r
199 {\r
200         portDISABLE_INTERRUPTS();\r
201         uxCriticalNesting++;\r
202 }\r
203 /*-----------------------------------------------------------*/\r
204 \r
205 void vPortExitCritical( void )\r
206 {\r
207         uxCriticalNesting--;\r
208         if( uxCriticalNesting == 0 )\r
209         {\r
210                 portENABLE_INTERRUPTS();\r
211         }\r
212 }\r
213 /*-----------------------------------------------------------*/\r
214 \r
215 void xPortPendSVHandler( void )\r
216 {\r
217         /* This is a naked function. */\r
218 \r
219         __asm volatile\r
220         (\r
221         "       mrs r0, psp                                                     \n"\r
222         "                                                                               \n"\r
223         "       ldr     r3, pxCurrentTCBConst                   \n" /* Get the location of the current TCB. */\r
224         "       ldr     r2, [r3]                                                \n"\r
225         "                                                                               \n"\r
226         "       stmdb r0!, {r4-r11}                                     \n" /* Save the remaining registers. */\r
227         "       str r0, [r2]                                            \n" /* Save the new top of stack into the first member of the TCB. */\r
228         "                                                                               \n"\r
229         "       stmdb sp!, {r3, r14}                            \n"\r
230         "       mov r0, %0                                                      \n"\r
231         "       msr basepri, r0                                         \n"\r
232         "       bl vTaskSwitchContext                           \n"\r
233         "       mov r0, #0                                                      \n"\r
234         "       msr basepri, r0                                         \n"                     \r
235         "       ldmia sp!, {r3, r14}                            \n"\r
236         "                                                                               \n"     /* Restore the context, including the critical nesting count. */\r
237         "       ldr r1, [r3]                                            \n"\r
238         "       ldr r0, [r1]                                            \n" /* The first item in pxCurrentTCB is the task top of stack. */\r
239         "       ldmia r0!, {r4-r11}                                     \n" /* Pop the registers. */\r
240         "       msr psp, r0                                                     \n"\r
241         "       bx r14                                                          \n"\r
242         "                                                                               \n"\r
243         "       .align 2                                                        \n"\r
244         "pxCurrentTCBConst: .word pxCurrentTCB  \n"\r
245         ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)\r
246         );\r
247 }\r
248 /*-----------------------------------------------------------*/\r
249 \r
250 void xPortSysTickHandler( void )\r
251 {\r
252 unsigned portLONG ulDummy;\r
253 \r
254         /* If using preemption, also force a context switch. */\r
255         #if configUSE_PREEMPTION == 1\r
256                 *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;\r
257         #endif\r
258 \r
259         ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();\r
260         {\r
261                 vTaskIncrementTick();\r
262         }\r
263         portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );\r
264 }\r
265 /*-----------------------------------------------------------*/\r
266 \r
267 /*\r
268  * Setup the systick timer to generate the tick interrupts at the required\r
269  * frequency.\r
270  */\r
271 void prvSetupTimerInterrupt( void )\r
272 {\r
273         /* Configure SysTick to interrupt at the requested rate. */\r
274         *(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;\r
275         *(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE;\r
276 }\r
277 /*-----------------------------------------------------------*/\r
278 \r