|
27 | 27 | */ |
28 | 28 |
|
29 | 29 | /*----------------------------------------------------------- |
30 | | - * Implementation of functions defined in portable.h for the RISC-V RV32 port. |
31 | | - *----------------------------------------------------------*/ |
| 30 | +* Implementation of functions defined in portable.h for the RISC-V port. |
| 31 | +*----------------------------------------------------------*/ |
32 | 32 |
|
33 | 33 | /* Scheduler includes. */ |
34 | 34 | #include "FreeRTOS.h" |
|
39 | 39 | #include "string.h" |
40 | 40 |
|
41 | 41 | #ifdef configCLINT_BASE_ADDRESS |
42 | | - #warning The configCLINT_BASE_ADDRESS constant has been deprecated. configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS are currently being derived from the (possibly 0) configCLINT_BASE_ADDRESS setting. Please update to define configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS dirctly in place of configCLINT_BASE_ADDRESS. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html |
| 42 | + #warning The configCLINT_BASE_ADDRESS constant has been deprecated. configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS are currently being derived from the (possibly 0) configCLINT_BASE_ADDRESS setting. Please update to define configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS dirctly in place of configCLINT_BASE_ADDRESS. See https: /*www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html */ |
43 | 43 | #endif |
44 | 44 |
|
45 | 45 | #ifndef configMTIME_BASE_ADDRESS |
46 | | - #warning configMTIME_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtime register then set configMTIME_BASE_ADDRESS to the mapped address. Otherwise set configMTIME_BASE_ADDRESS to 0. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html |
| 46 | + #warning configMTIME_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtime register then set configMTIME_BASE_ADDRESS to the mapped address. Otherwise set configMTIME_BASE_ADDRESS to 0. See https: /*www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html */ |
47 | 47 | #endif |
48 | 48 |
|
49 | 49 | #ifndef configMTIMECMP_BASE_ADDRESS |
50 | | - #warning configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtimecmp register then set configMTIMECMP_BASE_ADDRESS to the mapped address. Otherwise set configMTIMECMP_BASE_ADDRESS to 0. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html |
| 50 | + #warning configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtimecmp register then set configMTIMECMP_BASE_ADDRESS to the mapped address. Otherwise set configMTIMECMP_BASE_ADDRESS to 0. See https: /*www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html */ |
51 | 51 | #endif |
52 | 52 |
|
53 | 53 | /* Let the user override the pre-loading of the initial LR with the address of |
54 | | -prvTaskExitError() in case it messes up unwinding of the stack in the |
55 | | -debugger. */ |
| 54 | + * prvTaskExitError() in case it messes up unwinding of the stack in the |
| 55 | + * debugger. */ |
56 | 56 | #ifdef configTASK_RETURN_ADDRESS |
57 | | - #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS |
| 57 | + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS |
58 | 58 | #else |
59 | | - #define portTASK_RETURN_ADDRESS prvTaskExitError |
| 59 | + #define portTASK_RETURN_ADDRESS prvTaskExitError |
60 | 60 | #endif |
61 | 61 |
|
62 | 62 | /* The stack used by interrupt service routines. Set configISR_STACK_SIZE_WORDS |
63 | | -to use a statically allocated array as the interrupt stack. Alternative leave |
64 | | -configISR_STACK_SIZE_WORDS undefined and update the linker script so that a |
65 | | -linker variable names __freertos_irq_stack_top has the same value as the top |
66 | | -of the stack used by main. Using the linker script method will repurpose the |
67 | | -stack that was used by main before the scheduler was started for use as the |
68 | | -interrupt stack after the scheduler has started. */ |
| 63 | + * to use a statically allocated array as the interrupt stack. Alternative leave |
| 64 | + * configISR_STACK_SIZE_WORDS undefined and update the linker script so that a |
| 65 | + * linker variable names __freertos_irq_stack_top has the same value as the top |
| 66 | + * of the stack used by main. Using the linker script method will repurpose the |
| 67 | + * stack that was used by main before the scheduler was started for use as the |
| 68 | + * interrupt stack after the scheduler has started. */ |
69 | 69 | #ifdef configISR_STACK_SIZE_WORDS |
70 | | - static __attribute__ ((aligned(16))) StackType_t xISRStack[ configISR_STACK_SIZE_WORDS ] = { 0 }; |
71 | | - const StackType_t xISRStackTop = ( StackType_t ) &( xISRStack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] ); |
| 70 | +static __attribute__( ( aligned( 16 ) ) ) StackType_t xISRStack[ configISR_STACK_SIZE_WORDS ] = { 0 }; |
| 71 | +const StackType_t xISRStackTop = ( StackType_t ) &( xISRStack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] ); |
72 | 72 |
|
73 | | - /* Don't use 0xa5 as the stack fill bytes as that is used by the kernerl for |
74 | | - the task stacks, and so will legitimately appear in many positions within |
75 | | - the ISR stack. */ |
76 | | - #define portISR_STACK_FILL_BYTE 0xee |
| 73 | +/* Don't use 0xa5 as the stack fill bytes as that is used by the kernerl for |
| 74 | + * the task stacks, and so will legitimately appear in many positions within |
| 75 | + * the ISR stack. */ |
| 76 | + #define portISR_STACK_FILL_BYTE 0xee |
77 | 77 | #else |
78 | | - extern const uint32_t __freertos_irq_stack_top[]; |
79 | | - const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top; |
| 78 | + extern const uint32_t __freertos_irq_stack_top[]; |
| 79 | + const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top; |
80 | 80 | #endif |
81 | 81 |
|
| 82 | +/** |
| 83 | + * @brief Used to catch tasks that attempt to return from their implementing |
| 84 | + * function. |
| 85 | + */ |
| 86 | +static void prvTaskExitError( void ); |
| 87 | + |
82 | 88 | /* |
83 | 89 | * Setup the timer to generate the tick interrupts. The implementation in this |
84 | 90 | * file is weak to allow application writers to change the timer used to |
85 | 91 | * generate the tick interrupt. |
86 | 92 | */ |
87 | | -void vPortSetupTimerInterrupt( void ) __attribute__(( weak )); |
| 93 | +void vPortSetupTimerInterrupt( void ) __attribute__( ( weak ) ); |
88 | 94 |
|
89 | 95 | /*-----------------------------------------------------------*/ |
90 | 96 |
|
91 | 97 | /* Used to program the machine timer compare register. */ |
92 | 98 | uint64_t ullNextTime = 0ULL; |
93 | | -const uint64_t *pullNextTime = &ullNextTime; |
| 99 | +const uint64_t * pullNextTime = &ullNextTime; |
94 | 100 | const size_t uxTimerIncrementsForOneTick = ( size_t ) ( ( configCPU_CLOCK_HZ ) / ( configTICK_RATE_HZ ) ); /* Assumes increment won't go over 32-bits. */ |
95 | 101 | uint32_t const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS; |
96 | 102 | volatile uint64_t * pullMachineTimerCompareRegister = NULL; |
97 | 103 |
|
98 | | -/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task |
99 | | -stack checking. A problem in the ISR stack will trigger an assert, not call the |
100 | | -stack overflow hook function (because the stack overflow hook is specific to a |
101 | | -task stack, not the ISR stack). */ |
102 | | -#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 ) |
103 | | - #warning This path not tested, or even compiled yet. |
| 104 | +/* Holds the critical nesting value - deliberately non-zero at start up to |
| 105 | + * ensure interrupts are not accidentally enabled before the scheduler starts. */ |
| 106 | +size_t xCriticalNesting = ( size_t ) 0xaaaaaaaa; |
| 107 | +size_t * pxCriticalNesting = &xCriticalNesting; |
104 | 108 |
|
105 | | - static const uint8_t ucExpectedStackBytes[] = { |
106 | | - portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
107 | | - portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
108 | | - portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
109 | | - portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
110 | | - portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE }; \ |
| 109 | +/* Used to catch tasks that attempt to return from their implementing function. */ |
| 110 | +size_t xTaskReturnAddress = ( size_t ) portTASK_RETURN_ADDRESS; |
111 | 111 |
|
112 | | - #define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) ) |
113 | | -#else |
114 | | - /* Define the function away. */ |
115 | | - #define portCHECK_ISR_STACK() |
| 112 | +/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task |
| 113 | + * stack checking. A problem in the ISR stack will trigger an assert, not call |
| 114 | + * the stack overflow hook function (because the stack overflow hook is specific |
| 115 | + * to a task stack, not the ISR stack). */ |
| 116 | +#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 ) |
| 117 | + #warning This path not tested, or even compiled yet. |
| 118 | + |
| 119 | + static const uint8_t ucExpectedStackBytes[] = |
| 120 | + { |
| 121 | + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
| 122 | + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
| 123 | + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
| 124 | + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \ |
| 125 | + portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE |
| 126 | + }; \ |
| 127 | + |
| 128 | + #define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) ) |
| 129 | +#else /* if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 ) */ |
| 130 | + /* Define the function away. */ |
| 131 | + #define portCHECK_ISR_STACK() |
116 | 132 | #endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */ |
117 | 133 |
|
118 | 134 | /*-----------------------------------------------------------*/ |
119 | 135 |
|
120 | | -#if( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) |
| 136 | +static void prvTaskExitError( void ) |
| 137 | +{ |
| 138 | + volatile uint32_t ulDummy = 0UL; |
| 139 | + |
| 140 | + /* A function that implements a task must not exit or attempt to return to |
| 141 | + * its caller as there is nothing to return to. If a task wants to exit it |
| 142 | + * should instead call vTaskDelete( NULL ). Artificially force an assert() |
| 143 | + * to be triggered if configASSERT() is defined, then stop here so |
| 144 | + * application writers can catch the error. */ |
| 145 | + configASSERT( xCriticalNesting == ~0UL ); |
| 146 | + portDISABLE_INTERRUPTS(); |
| 147 | + |
| 148 | + while( ulDummy == 0 ) |
| 149 | + { |
| 150 | + /* This file calls prvTaskExitError() after the scheduler has been |
| 151 | + * started to remove a compiler warning about the function being |
| 152 | + * defined but never called. ulDummy is used purely to quieten other |
| 153 | + * warnings about code appearing after this function is called - making |
| 154 | + * ulDummy volatile makes the compiler think the function could return |
| 155 | + * and therefore not output an 'unreachable code' warning for code that |
| 156 | + * appears after it. */ |
| 157 | + } |
| 158 | +} |
| 159 | +/*-----------------------------------------------------------*/ |
| 160 | + |
| 161 | +#if ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) |
| 162 | + |
| 163 | + void vPortSetupTimerInterrupt( void ) |
| 164 | + { |
| 165 | + uint32_t ulCurrentTimeHigh, ulCurrentTimeLow; |
| 166 | + volatile uint32_t * const pulTimeHigh = ( uint32_t * ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte type so high 32-bit word is 4 bytes up. */ |
| 167 | + volatile uint32_t * const pulTimeLow = ( uint32_t * ) ( configMTIME_BASE_ADDRESS ); |
| 168 | + volatile uint32_t ulHartId; |
121 | 169 |
|
122 | | - void vPortSetupTimerInterrupt( void ) |
123 | | - { |
124 | | - uint32_t ulCurrentTimeHigh, ulCurrentTimeLow; |
125 | | - volatile uint32_t * const pulTimeHigh = ( uint32_t * ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte typer so high 32-bit word is 4 bytes up. */ |
126 | | - volatile uint32_t * const pulTimeLow = ( uint32_t * ) ( configMTIME_BASE_ADDRESS ); |
127 | | - volatile uint32_t ulHartId; |
| 170 | + __asm volatile ( "csrr %0, 0xf14" : "=r" ( ulHartId ) ); /* 0xf14 is hartid. */ |
128 | 171 |
|
129 | | - __asm volatile( "csrr %0, 0xf14" : "=r"( ulHartId ) ); /* 0xf14 is hartid. */ |
130 | | - pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) ); |
| 172 | + pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) ); |
131 | 173 |
|
132 | | - do |
133 | | - { |
134 | | - ulCurrentTimeHigh = *pulTimeHigh; |
135 | | - ulCurrentTimeLow = *pulTimeLow; |
136 | | - } while( ulCurrentTimeHigh != *pulTimeHigh ); |
| 174 | + do |
| 175 | + { |
| 176 | + ulCurrentTimeHigh = *pulTimeHigh; |
| 177 | + ulCurrentTimeLow = *pulTimeLow; |
| 178 | + } while( ulCurrentTimeHigh != *pulTimeHigh ); |
137 | 179 |
|
138 | | - ullNextTime = ( uint64_t ) ulCurrentTimeHigh; |
139 | | - ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */ |
140 | | - ullNextTime |= ( uint64_t ) ulCurrentTimeLow; |
141 | | - ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; |
142 | | - *pullMachineTimerCompareRegister = ullNextTime; |
| 180 | + ullNextTime = ( uint64_t ) ulCurrentTimeHigh; |
| 181 | + ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */ |
| 182 | + ullNextTime |= ( uint64_t ) ulCurrentTimeLow; |
| 183 | + ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; |
| 184 | + *pullMachineTimerCompareRegister = ullNextTime; |
143 | 185 |
|
144 | | - /* Prepare the time to use after the next tick interrupt. */ |
145 | | - ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; |
146 | | - } |
| 186 | + /* Prepare the time to use after the next tick interrupt. */ |
| 187 | + ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick; |
| 188 | + } |
147 | 189 |
|
148 | 190 | #endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIME_BASE_ADDRESS != 0 ) */ |
149 | 191 | /*-----------------------------------------------------------*/ |
150 | 192 |
|
151 | 193 | BaseType_t xPortStartScheduler( void ) |
152 | 194 | { |
153 | | -extern void xPortStartFirstTask( void ); |
154 | | - |
155 | | - #if( configASSERT_DEFINED == 1 ) |
156 | | - { |
157 | | - volatile uint32_t mtvec = 0; |
158 | | - |
159 | | - /* Check the least significant two bits of mtvec are 00 - indicating |
160 | | - single vector mode. */ |
161 | | - __asm volatile( "csrr %0, 0x305" : "=r"( mtvec ) ); /* 0x305 is mtvec. */ |
162 | | - configASSERT( ( mtvec & 0x03UL ) == 0 ); |
163 | | - |
164 | | - /* Check alignment of the interrupt stack - which is the same as the |
165 | | - stack that was being used by main() prior to the scheduler being |
166 | | - started. */ |
167 | | - configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 ); |
168 | | - |
169 | | - #ifdef configISR_STACK_SIZE_WORDS |
170 | | - { |
171 | | - memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) ); |
172 | | - } |
173 | | - #endif /* configISR_STACK_SIZE_WORDS */ |
174 | | - } |
175 | | - #endif /* configASSERT_DEFINED */ |
176 | | - |
177 | | - /* If there is a CLINT then it is ok to use the default implementation |
178 | | - in this file, otherwise vPortSetupTimerInterrupt() must be implemented to |
179 | | - configure whichever clock is to be used to generate the tick interrupt. */ |
180 | | - vPortSetupTimerInterrupt(); |
181 | | - |
182 | | - #if( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) ) |
183 | | - { |
184 | | - /* Enable mtime and external interrupts. 1<<7 for timer interrupt, 1<<11 |
185 | | - for external interrupt. _RB_ What happens here when mtime is not present as |
186 | | - with pulpino? */ |
187 | | - __asm volatile( "csrs 0x304, %0" :: "r"(0x880) ); /* 0x304 is mie. */ |
188 | | - } |
189 | | - #else |
190 | | - { |
191 | | - /* Enable external interrupts. */ |
192 | | - __asm volatile( "csrs 0x304, %0" :: "r"(0x800) ); /* 304 is mie. */ |
193 | | - } |
194 | | - #endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */ |
195 | | - |
196 | | - xPortStartFirstTask(); |
197 | | - |
198 | | - /* Should not get here as after calling xPortStartFirstTask() only tasks |
199 | | - should be executing. */ |
200 | | - return pdFAIL; |
| 195 | + extern void xPortStartFirstTask( void ); |
| 196 | + |
| 197 | + #if ( configASSERT_DEFINED == 1 ) |
| 198 | + { |
| 199 | + /* Check alignment of the interrupt stack - which is the same as the |
| 200 | + * stack that was being used by main() prior to the scheduler being |
| 201 | + * started. */ |
| 202 | + configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 ); |
| 203 | + |
| 204 | + #ifdef configISR_STACK_SIZE_WORDS |
| 205 | + { |
| 206 | + memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) ); |
| 207 | + } |
| 208 | + #endif /* configISR_STACK_SIZE_WORDS */ |
| 209 | + } |
| 210 | + #endif /* configASSERT_DEFINED */ |
| 211 | + |
| 212 | + /* If there is a CLINT then it is ok to use the default implementation |
| 213 | + * in this file, otherwise vPortSetupTimerInterrupt() must be implemented to |
| 214 | + * configure whichever clock is to be used to generate the tick interrupt. */ |
| 215 | + vPortSetupTimerInterrupt(); |
| 216 | + |
| 217 | + #if ( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) ) |
| 218 | + { |
| 219 | + /* Enable mtime and external interrupts. 1<<7 for timer interrupt, |
| 220 | + * 1<<11 for external interrupt. _RB_ What happens here when mtime is |
| 221 | + * not present as with pulpino? */ |
| 222 | + __asm volatile ( "csrs 0x304, %0" ::"r" ( 0x880 ) ); /* 0x304 is mie. */ |
| 223 | + } |
| 224 | + #endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */ |
| 225 | + |
| 226 | + xPortStartFirstTask(); |
| 227 | + |
| 228 | + /* Should not get here as after calling xPortStartFirstTask() only tasks |
| 229 | + * should be executing. */ |
| 230 | + return pdFAIL; |
201 | 231 | } |
202 | 232 | /*-----------------------------------------------------------*/ |
203 | 233 |
|
204 | 234 | void vPortEndScheduler( void ) |
205 | 235 | { |
206 | | - /* Not implemented. */ |
207 | | - for( ;; ); |
| 236 | + /* Not implemented. */ |
| 237 | + for( ; ; ) |
| 238 | + { |
| 239 | + } |
208 | 240 | } |
209 | | - |
210 | | - |
211 | | - |
212 | | - |
213 | | - |
| 241 | +/*-----------------------------------------------------------*/ |
0 commit comments