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