]> git.sur5r.net Git - freertos/blob - FreeRTOS/Source/portable/IAR/LPC2000/port.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS / Source / portable / IAR / LPC2000 / port.c
1 /*\r
2  * FreeRTOS Kernel V10.0.0\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /*-----------------------------------------------------------\r
30  * Implementation of functions defined in portable.h for the Philips ARM7 port.\r
31  *----------------------------------------------------------*/\r
32 \r
33 /*\r
34         Changes from V3.2.2\r
35 \r
36         + Bug fix - The prescale value for the timer setup is now written to T0PR\r
37           instead of T0PC.  This bug would have had no effect unless a prescale\r
38           value was actually used.\r
39 */\r
40 \r
41 /* Standard includes. */\r
42 #include <stdlib.h>\r
43 #include <intrinsics.h>\r
44 \r
45 /* Scheduler includes. */\r
46 #include "FreeRTOS.h"\r
47 #include "task.h"\r
48 \r
49 /* Constants required to setup the tick ISR. */\r
50 #define portENABLE_TIMER                        ( ( uint8_t ) 0x01 )\r
51 #define portPRESCALE_VALUE                      0x00\r
52 #define portINTERRUPT_ON_MATCH          ( ( uint32_t ) 0x01 )\r
53 #define portRESET_COUNT_ON_MATCH        ( ( uint32_t ) 0x02 )\r
54 \r
55 /* Constants required to setup the initial stack. */\r
56 #define portINITIAL_SPSR                                ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, interrupts enabled. */\r
57 #define portTHUMB_MODE_BIT                              ( ( StackType_t ) 0x20 )\r
58 #define portINSTRUCTION_SIZE                    ( ( StackType_t ) 4 )\r
59 \r
60 /* Constants required to setup the PIT. */\r
61 #define portPIT_CLOCK_DIVISOR                   ( ( uint32_t ) 16 )\r
62 #define portPIT_COUNTER_VALUE                   ( ( ( configCPU_CLOCK_HZ / portPIT_CLOCK_DIVISOR ) / 1000UL ) * portTICK_PERIOD_MS )\r
63 \r
64 /* Constants required to handle interrupts. */\r
65 #define portTIMER_MATCH_ISR_BIT         ( ( uint8_t ) 0x01 )\r
66 #define portCLEAR_VIC_INTERRUPT         ( ( uint32_t ) 0 )\r
67 \r
68 /* Constants required to handle critical sections. */\r
69 #define portNO_CRITICAL_NESTING                 ( ( uint32_t ) 0 )\r
70 \r
71 \r
72 #define portINT_LEVEL_SENSITIVE  0\r
73 #define portPIT_ENABLE          ( ( uint16_t ) 0x1 << 24 )\r
74 #define portPIT_INT_ENABLE      ( ( uint16_t ) 0x1 << 25 )\r
75 \r
76 /* Constants required to setup the VIC for the tick ISR. */\r
77 #define portTIMER_VIC_CHANNEL           ( ( uint32_t ) 0x0004 )\r
78 #define portTIMER_VIC_CHANNEL_BIT       ( ( uint32_t ) 0x0010 )\r
79 #define portTIMER_VIC_ENABLE            ( ( uint32_t ) 0x0020 )\r
80 \r
81 /*-----------------------------------------------------------*/\r
82 \r
83 /* Setup the PIT to generate the tick interrupts. */\r
84 static void prvSetupTimerInterrupt( void );\r
85 \r
86 /* ulCriticalNesting will get set to zero when the first task starts.  It\r
87 cannot be initialised to 0 as this will cause interrupts to be enabled\r
88 during the kernel initialisation process. */\r
89 uint32_t ulCriticalNesting = ( uint32_t ) 9999;\r
90 \r
91 /*-----------------------------------------------------------*/\r
92 \r
93 /*\r
94  * Initialise the stack of a task to look exactly as if a call to\r
95  * portSAVE_CONTEXT had been called.\r
96  *\r
97  * See header file for description.\r
98  */\r
99 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )\r
100 {\r
101 StackType_t *pxOriginalTOS;\r
102 \r
103         pxOriginalTOS = pxTopOfStack;\r
104 \r
105         /* Setup the initial stack of the task.  The stack is set exactly as\r
106         expected by the portRESTORE_CONTEXT() macro. */\r
107 \r
108         /* First on the stack is the return address - which in this case is the\r
109         start of the task.  The offset is added to make the return address appear\r
110         as it would within an IRQ ISR. */\r
111         *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE;          \r
112         pxTopOfStack--;\r
113 \r
114         *pxTopOfStack = ( StackType_t ) 0xaaaaaaaa;     /* R14 */\r
115         pxTopOfStack--; \r
116         *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */\r
117         pxTopOfStack--;\r
118         *pxTopOfStack = ( StackType_t ) 0x12121212;     /* R12 */\r
119         pxTopOfStack--; \r
120         *pxTopOfStack = ( StackType_t ) 0x11111111;     /* R11 */\r
121         pxTopOfStack--; \r
122         *pxTopOfStack = ( StackType_t ) 0x10101010;     /* R10 */\r
123         pxTopOfStack--; \r
124         *pxTopOfStack = ( StackType_t ) 0x09090909;     /* R9 */\r
125         pxTopOfStack--; \r
126         *pxTopOfStack = ( StackType_t ) 0x08080808;     /* R8 */\r
127         pxTopOfStack--; \r
128         *pxTopOfStack = ( StackType_t ) 0x07070707;     /* R7 */\r
129         pxTopOfStack--; \r
130         *pxTopOfStack = ( StackType_t ) 0x06060606;     /* R6 */\r
131         pxTopOfStack--; \r
132         *pxTopOfStack = ( StackType_t ) 0x05050505;     /* R5 */\r
133         pxTopOfStack--; \r
134         *pxTopOfStack = ( StackType_t ) 0x04040404;     /* R4 */\r
135         pxTopOfStack--; \r
136         *pxTopOfStack = ( StackType_t ) 0x03030303;     /* R3 */\r
137         pxTopOfStack--; \r
138         *pxTopOfStack = ( StackType_t ) 0x02020202;     /* R2 */\r
139         pxTopOfStack--; \r
140         *pxTopOfStack = ( StackType_t ) 0x01010101;     /* R1 */\r
141         pxTopOfStack--; \r
142 \r
143         /* When the task starts is will expect to find the function parameter in\r
144         R0. */\r
145         *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */\r
146         pxTopOfStack--;\r
147 \r
148         /* The status register is set for system mode, with interrupts enabled. */\r
149         *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;\r
150         \r
151         if( ( ( uint32_t ) pxCode & 0x01UL ) != 0x00UL )\r
152         {\r
153                 /* We want the task to start in thumb mode. */\r
154                 *pxTopOfStack |= portTHUMB_MODE_BIT;\r
155         }\r
156         \r
157         pxTopOfStack--;\r
158 \r
159         /* Interrupt flags cannot always be stored on the stack and will\r
160         instead be stored in a variable, which is then saved as part of the\r
161         tasks context. */\r
162         *pxTopOfStack = portNO_CRITICAL_NESTING;\r
163 \r
164         return pxTopOfStack;    \r
165 }\r
166 /*-----------------------------------------------------------*/\r
167 \r
168 BaseType_t xPortStartScheduler( void )\r
169 {\r
170 extern void vPortStartFirstTask( void );\r
171 \r
172         /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
173         here already. */\r
174         prvSetupTimerInterrupt();\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 ARM port will require this function as there\r
187         is nothing to return to.  */\r
188 }\r
189 /*-----------------------------------------------------------*/\r
190 \r
191 #if configUSE_PREEMPTION == 0\r
192 \r
193         /* The cooperative scheduler requires a normal IRQ service routine to\r
194         simply increment the system tick. */\r
195         static __arm __irq void vPortNonPreemptiveTick( void );\r
196         static __arm __irq void vPortNonPreemptiveTick( void )\r
197         {\r
198                 /* Increment the tick count - which may wake some tasks but as the\r
199                 preemptive scheduler is not being used any woken task is not given\r
200                 processor time no matter what its priority. */\r
201                 xTaskIncrementTick();\r
202                 \r
203                 /* Ready for the next interrupt. */\r
204                 T0IR = portTIMER_MATCH_ISR_BIT;\r
205                 VICVectAddr = portCLEAR_VIC_INTERRUPT;\r
206         }\r
207 \r
208 #else\r
209 \r
210         /* This function is called from an asm wrapper, so does not require the __irq\r
211         keyword. */\r
212         void vPortPreemptiveTick( void );\r
213         void vPortPreemptiveTick( void )\r
214         {\r
215                 /* Increment the tick counter. */\r
216                 if( xTaskIncrementTick() != pdFALSE )\r
217                 {       \r
218                         /* The new tick value might unblock a task.  Ensure the highest task that\r
219                         is ready to execute is the task that will execute when the tick ISR\r
220                         exits. */\r
221                         vTaskSwitchContext();\r
222                 }\r
223         \r
224                 /* Ready for the next interrupt. */\r
225                 T0IR = portTIMER_MATCH_ISR_BIT;\r
226                 VICVectAddr = portCLEAR_VIC_INTERRUPT;\r
227         }\r
228 \r
229 #endif\r
230 \r
231 /*-----------------------------------------------------------*/\r
232 \r
233 static void prvSetupTimerInterrupt( void )\r
234 {\r
235 uint32_t ulCompareMatch;\r
236 \r
237         /* A 1ms tick does not require the use of the timer prescale.  This is\r
238         defaulted to zero but can be used if necessary. */\r
239         T0PR = portPRESCALE_VALUE;\r
240 \r
241         /* Calculate the match value required for our wanted tick rate. */\r
242         ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ;\r
243 \r
244         /* Protect against divide by zero.  Using an if() statement still results\r
245         in a warning - hence the #if. */\r
246         #if portPRESCALE_VALUE != 0\r
247         {\r
248                 ulCompareMatch /= ( portPRESCALE_VALUE + 1 );\r
249         }\r
250         #endif\r
251 \r
252         T0MR0 = ulCompareMatch;\r
253 \r
254         /* Generate tick with timer 0 compare match. */\r
255         T0MCR = portRESET_COUNT_ON_MATCH | portINTERRUPT_ON_MATCH;\r
256 \r
257         /* Setup the VIC for the timer. */\r
258         VICIntSelect &= ~( portTIMER_VIC_CHANNEL_BIT );\r
259         VICIntEnable |= portTIMER_VIC_CHANNEL_BIT;\r
260         \r
261         /* The ISR installed depends on whether the preemptive or cooperative\r
262         scheduler is being used. */\r
263         #if configUSE_PREEMPTION == 1\r
264         {       \r
265                 extern void ( vPortPreemptiveTickEntry )( void );\r
266 \r
267                 VICVectAddr0 = ( uint32_t ) vPortPreemptiveTickEntry;\r
268         }\r
269         #else\r
270         {\r
271                 extern void ( vNonPreemptiveTick )( void );\r
272 \r
273                 VICVectAddr0 = ( int32_t ) vPortNonPreemptiveTick;\r
274         }\r
275         #endif\r
276 \r
277         VICVectCntl0 = portTIMER_VIC_CHANNEL | portTIMER_VIC_ENABLE;\r
278 \r
279         /* Start the timer - interrupts are disabled when this function is called\r
280         so it is okay to do this here. */\r
281         T0TCR = portENABLE_TIMER;\r
282 }\r
283 /*-----------------------------------------------------------*/\r
284 \r
285 void vPortEnterCritical( void )\r
286 {\r
287         /* Disable interrupts first! */\r
288         __disable_interrupt();\r
289 \r
290         /* Now interrupts are disabled ulCriticalNesting can be accessed\r
291         directly.  Increment ulCriticalNesting to keep a count of how many times\r
292         portENTER_CRITICAL() has been called. */\r
293         ulCriticalNesting++;\r
294 }\r
295 /*-----------------------------------------------------------*/\r
296 \r
297 void vPortExitCritical( void )\r
298 {\r
299         if( ulCriticalNesting > portNO_CRITICAL_NESTING )\r
300         {\r
301                 /* Decrement the nesting count as we are leaving a critical section. */\r
302                 ulCriticalNesting--;\r
303 \r
304                 /* If the nesting level has reached zero then interrupts should be\r
305                 re-enabled. */\r
306                 if( ulCriticalNesting == portNO_CRITICAL_NESTING )\r
307                 {\r
308                         __enable_interrupt();\r
309                 }\r
310         }\r
311 }\r
312 /*-----------------------------------------------------------*/\r
313 \r
314 \r
315 \r
316 \r
317 \r
318 \r