]> git.sur5r.net Git - freertos/blob - FreeRTOS/Source/portable/GCC/RISC-V/portASM.S
Prepare the RISC-V port layer for addition of 64-bit port.
[freertos] / FreeRTOS / Source / portable / GCC / RISC-V / portASM.S
1 /*\r
2  * FreeRTOS Kernel V10.2.0\r
3  * Copyright (C) 2019 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 #if __riscv_xlen == 64\r
29         #error Not implemented yet - change lw to ld, and sw to sd.\r
30         #define portWORD_SIZE 8\r
31         #define store_x sd\r
32         #define load_x ld\r
33 #elif __riscv_xlen == 32\r
34         #define portWORD_SIZE 4\r
35         #define store_x sw\r
36         #define load_x lw\r
37 #else\r
38         #error Assembler did not define __riscv_xlen\r
39 #endif\r
40 \r
41 /*\r
42  * The FreeRTOS kernel's RISC-V port is split between the the code that is\r
43  * common across all currently supported RISC-V chips (implementations of the\r
44  * RISC-V ISA), and code which tailors the port to a specific RISC-V chip:\r
45  *\r
46  * + The code that is common to all RISC-V chips is implemented in\r
47  *   FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S.  There is only one\r
48  *   portASM.S file because the same file is used no matter which RISC-V chip is\r
49  *   in use.\r
50  *\r
51  * + The code that tailors the kernel's RISC-V port to a specific RISC-V\r
52  *   chip is implemented in freertos_risc_v_chip_specific_extensions.h.  There\r
53  *   is one freertos_risc_v_chip_specific_extensions.h that can be used with any\r
54  *   RISC-V chip that both includes a standard CLINT and does not add to the\r
55  *   base set of RISC-V registers.  There are additional\r
56  *   freertos_risc_v_chip_specific_extensions.h files for RISC-V implementations\r
57  *   that do not include a standard CLINT or do add to the base set of RISC-V\r
58  *   registers.\r
59  *\r
60  * CARE MUST BE TAKEN TO INCLDUE THE CORRECT\r
61  * freertos_risc_v_chip_specific_extensions.h HEADER FILE FOR THE CHIP\r
62  * IN USE.  To include the correct freertos_risc_v_chip_specific_extensions.h\r
63  * header file ensure the path to the correct header file is in the assembler's\r
64  * include path.\r
65  *\r
66  * This freertos_risc_v_chip_specific_extensions.h is for use on RISC-V chips\r
67  * that include a standard CLINT and do not add to the base set of RISC-V\r
68  * registers.\r
69  *\r
70  */\r
71 #include "freertos_risc_v_chip_specific_extensions.h"\r
72 \r
73 /* Check the freertos_risc_v_chip_specific_extensions.h and/or command line\r
74 definitions. */\r
75 #ifndef portasmHAS_CLINT\r
76         #error freertos_risc_v_chip_specific_extensions.h must define portasmHAS_CLINT to either 1 (CLINT present) or 0 (clint not present).\r
77 #endif\r
78 \r
79 #ifndef portasmHANDLE_INTERRUPT\r
80         #error portasmHANDLE_INTERRUPT must be defined to the function to be called to handle external/peripheral interrupts.  portasmHANDLE_INTERRUPT can be defined on the assmbler command line or in the appropriate freertos_risc_v_chip_specific_extensions.h header file.\r
81 #endif\r
82 \r
83 /* Only the standard core registers are stored by default.  Any additional\r
84 registers must be saved by the portasmSAVE_ADDITIONAL_REGISTERS and\r
85 portasmRESTORE_ADDITIONAL_REGISTERS macros - which can be defined in a chip\r
86 specific version of freertos_risc_v_chip_specific_extensions.h.  See the notes\r
87 at the top of this file. */\r
88 #define portCONTEXT_SIZE ( 30 * portWORD_SIZE )\r
89 \r
90 .global xPortStartFirstTask\r
91 .global freertos_risc_v_trap_handler\r
92 .global pxPortInitialiseStack\r
93 .extern pxCurrentTCB\r
94 .extern ulPortTrapHandler\r
95 .extern vTaskSwitchContext\r
96 .extern Timer_IRQHandler\r
97 .extern pullMachineTimerCompareRegister\r
98 .extern pullNextTime\r
99 .extern ulTimerIncrementsForOneTick\r
100 .extern xISRStackTop\r
101 \r
102 /*-----------------------------------------------------------*/\r
103 \r
104 .align 8\r
105 .func\r
106 freertos_risc_v_trap_handler:\r
107         addi sp, sp, -portCONTEXT_SIZE\r
108         store_x x1, 1 * portWORD_SIZE( sp )\r
109         store_x x5, 2 * portWORD_SIZE( sp )\r
110         store_x x6, 3 * portWORD_SIZE( sp )\r
111         store_x x7, 4 * portWORD_SIZE( sp )\r
112         store_x x8, 5 * portWORD_SIZE( sp )\r
113         store_x x9, 6 * portWORD_SIZE( sp )\r
114         store_x x10, 7 * portWORD_SIZE( sp )\r
115         store_x x11, 8 * portWORD_SIZE( sp )\r
116         store_x x12, 9 * portWORD_SIZE( sp )\r
117         store_x x13, 10 * portWORD_SIZE( sp )\r
118         store_x x14, 11 * portWORD_SIZE( sp )\r
119         store_x x15, 12 * portWORD_SIZE( sp )\r
120         store_x x16, 13 * portWORD_SIZE( sp )\r
121         store_x x17, 14 * portWORD_SIZE( sp )\r
122         store_x x18, 15 * portWORD_SIZE( sp )\r
123         store_x x19, 16 * portWORD_SIZE( sp )\r
124         store_x x20, 17 * portWORD_SIZE( sp )\r
125         store_x x21, 18 * portWORD_SIZE( sp )\r
126         store_x x22, 19 * portWORD_SIZE( sp )\r
127         store_x x23, 20 * portWORD_SIZE( sp )\r
128         store_x x24, 21 * portWORD_SIZE( sp )\r
129         store_x x25, 22 * portWORD_SIZE( sp )\r
130         store_x x26, 23 * portWORD_SIZE( sp )\r
131         store_x x27, 24 * portWORD_SIZE( sp )\r
132         store_x x28, 25 * portWORD_SIZE( sp )\r
133         store_x x29, 26 * portWORD_SIZE( sp )\r
134         store_x x30, 27 * portWORD_SIZE( sp )\r
135         store_x x31, 28 * portWORD_SIZE( sp )\r
136 \r
137         csrr t0, mstatus                                        /* Required for MPIE bit. */\r
138         store_x t0, 29 * portWORD_SIZE( sp )\r
139 \r
140         portasmSAVE_ADDITIONAL_REGISTERS        /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */\r
141 \r
142         load_x  t0, pxCurrentTCB                        /* Load pxCurrentTCB. */\r
143         store_x  sp, 0( t0 )                            /* Write sp to first TCB member. */\r
144 \r
145         csrr a0, mcause\r
146         csrr a1, mepc\r
147 \r
148 test_if_asynchronous:\r
149         srli a2, a0, 0x1f                                       /* MSB of mcause is 1 if handing an asynchronous interrupt - shift to LSB to clear other bits. */\r
150         beq a2, x0, handle_synchronous          /* Branch past interrupt handing if not asynchronous. */\r
151         store_x a1, 0( sp )                                     /* Asynch so save unmodified exception return address. */\r
152 \r
153 handle_asynchronous:\r
154 \r
155 #if( portasmHAS_CLINT != 0 )\r
156 \r
157         test_if_mtimer:                                         /* If there is a CLINT then the mtimer is used to generate the tick interrupt. */\r
158 \r
159                 addi t0, x0, 1\r
160 \r
161                 #if( __riscv_xlen == 32 )\r
162                         slli t0, t0, 31                         /* LSB is already set, shift into MSB. */\r
163                         addi t1, t0, 7                          /* 0x80000007 == machine timer interrupt. */\r
164                         bne a0, t1, test_if_external_interrupt\r
165 \r
166                         lw t0, pullMachineTimerCompareRegister  /* Load address of compare register into t0. */\r
167                         lw t1, pullNextTime                     /* Load the address of ullNextTime into t1. */\r
168                         lw t2, 0(t1)                            /* Load the low word of ullNextTime into t2. */\r
169                         lw t3, 4(t1)                            /* Load the high word of ullNextTime into t3. */\r
170                         sw t2, 0(t0)                            /* Store low word of ullNextTime into compare register. */\r
171                         sw t3, 4(t0)                            /* Store high word of ullNextTime into compare register. */\r
172                         lw t0, ulTimerIncrementsForOneTick      /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */\r
173                         add t4, t0, t2                          /* Add the low word of ullNextTime to the timer increments for one tick (assumes timer increment for one tick fits in 32-bits. */\r
174                         sltu t5, t4, t2                         /* See if the sum of low words overflowed (what about the zero case?). */\r
175                         add t6, t3, t5                          /* Add overflow to high word of ullNextTime. */\r
176                         sw t4, 0(t1)                            /* Store new low word of ullNextTime. */\r
177                         sw t6, 4(t1)                            /* Store new high word of ullNextTime. */\r
178                 #endif /* __riscv_xlen == 32 */\r
179 \r
180                 load_x sp, xISRStackTop                 /* Switch to ISR stack before function call. */\r
181                 jal xTaskIncrementTick\r
182                 beqz a0, processed_source               /* Don't switch context if incrementing tick didn't unblock a task. */\r
183                 jal vTaskSwitchContext\r
184                 j processed_source\r
185 \r
186         test_if_external_interrupt:                     /* If there is a CLINT and the mtimer interrupt is not pending then check to see if an external interrupt is pending. */\r
187                 addi t1, t1, 4                                  /* 0x80000007 + 4 = 0x8000000b == Machine external interrupt. */\r
188                 bne a0, t1, as_yet_unhandled    /* Something as yet unhandled. */\r
189 \r
190 #endif /* portasmHAS_CLINT */\r
191 \r
192         load_x sp, xISRStackTop                         /* Switch to ISR stack before function call. */\r
193         jal portasmHANDLE_INTERRUPT                     /* Jump to the interrupt handler if there is no CLINT or if there is a CLINT and it has been determined that an external interrupt is pending. */\r
194         j processed_source\r
195 \r
196 handle_synchronous:\r
197         addi a1, a1, 4                                          /* Synchronous so updated exception return address to the instruction after the instruction that generated the exeption. */\r
198         store_x a1, 0( sp )                                     /* Save updated exception return address. */\r
199 \r
200 test_if_environment_call:\r
201         li t0, 11                                                       /* 11 == environment call. */\r
202         bne a0, t0, is_exception                        /* Not an M environment call, so some other exception. */\r
203         load_x sp, xISRStackTop                         /* Switch to ISR stack before function call. */\r
204         jal vTaskSwitchContext\r
205         j processed_source\r
206 \r
207 is_exception:\r
208         ebreak\r
209         j is_exception\r
210 \r
211 as_yet_unhandled:\r
212         ebreak\r
213         j as_yet_unhandled\r
214 \r
215 processed_source:\r
216         load_x  sp, pxCurrentTCB                        /* Load pxCurrentTCB. */\r
217         load_x  sp, 0( sp )                                     /* Read sp from first TCB member. */\r
218 \r
219         /* Load mret with the address of the next instruction in the task to run next. */\r
220         load_x t0, 0( sp )\r
221         csrw mepc, t0\r
222 \r
223         portasmRESTORE_ADDITIONAL_REGISTERS     /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */\r
224 \r
225         /* Load mstatus with the interrupt enable bits used by the task. */\r
226         load_x  t0, 29 * portWORD_SIZE( sp )\r
227         csrw mstatus, t0                                                /* Required for MPIE bit. */\r
228 \r
229         load_x  x1, 1 * portWORD_SIZE( sp )\r
230         load_x  x5, 2 * portWORD_SIZE( sp )             /* t0 */\r
231         load_x  x6, 3 * portWORD_SIZE( sp )             /* t1 */\r
232         load_x  x7, 4 * portWORD_SIZE( sp )             /* t2 */\r
233         load_x  x8, 5 * portWORD_SIZE( sp )             /* s0/fp */\r
234         load_x  x9, 6 * portWORD_SIZE( sp )             /* s1 */\r
235         load_x  x10, 7 * portWORD_SIZE( sp )    /* a0 */\r
236         load_x  x11, 8 * portWORD_SIZE( sp )    /* a1 */\r
237         load_x  x12, 9 * portWORD_SIZE( sp )    /* a2 */\r
238         load_x  x13, 10 * portWORD_SIZE( sp )   /* a3 */\r
239         load_x  x14, 11 * portWORD_SIZE( sp )   /* a4 */\r
240         load_x  x15, 12 * portWORD_SIZE( sp )   /* a5 */\r
241         load_x  x16, 13 * portWORD_SIZE( sp )   /* a6 */\r
242         load_x  x17, 14 * portWORD_SIZE( sp )   /* a7 */\r
243         load_x  x18, 15 * portWORD_SIZE( sp )   /* s2 */\r
244         load_x  x19, 16 * portWORD_SIZE( sp )   /* s3 */\r
245         load_x  x20, 17 * portWORD_SIZE( sp )   /* s4 */\r
246         load_x  x21, 18 * portWORD_SIZE( sp )   /* s5 */\r
247         load_x  x22, 19 * portWORD_SIZE( sp )   /* s6 */\r
248         load_x  x23, 20 * portWORD_SIZE( sp )   /* s7 */\r
249         load_x  x24, 21 * portWORD_SIZE( sp )   /* s8 */\r
250         load_x  x25, 22 * portWORD_SIZE( sp )   /* s9 */\r
251         load_x  x26, 23 * portWORD_SIZE( sp )   /* s10 */\r
252         load_x  x27, 24 * portWORD_SIZE( sp )   /* s11 */\r
253         load_x  x28, 25 * portWORD_SIZE( sp )   /* t3 */\r
254         load_x  x29, 26 * portWORD_SIZE( sp )   /* t4 */\r
255         load_x  x30, 27 * portWORD_SIZE( sp )   /* t5 */\r
256         load_x  x31, 28 * portWORD_SIZE( sp )   /* t6 */\r
257         addi sp, sp, portCONTEXT_SIZE\r
258 \r
259         mret\r
260         .endfunc\r
261 /*-----------------------------------------------------------*/\r
262 \r
263 .align 8\r
264 .func\r
265 xPortStartFirstTask:\r
266 \r
267 #if( portasmHAS_CLINT != 0 )\r
268         /* If there is a clint then interrupts can branch directly to the FreeRTOS\r
269         trap handler.  Otherwise the interrupt controller will need to be configured\r
270         outside of this file. */\r
271         la t0, freertos_risc_v_trap_handler\r
272         csrw mtvec, t0\r
273 #endif /* portasmHAS_CLILNT */\r
274 \r
275         load_x  sp, pxCurrentTCB                        /* Load pxCurrentTCB. */\r
276         load_x  sp, 0( sp )                                     /* Read sp from first TCB member. */\r
277 \r
278         load_x  x1, 0( sp ) /* Note for starting the scheduler the exception return address is used as the function return address. */\r
279 \r
280         portasmRESTORE_ADDITIONAL_REGISTERS     /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */\r
281 \r
282         load_x  t0, 29 * portWORD_SIZE( sp )    /* mstatus */\r
283         csrrw  x0, mstatus, t0                                  /* Interrupts enabled from here! */\r
284 \r
285         load_x  x5, 2 * portWORD_SIZE( sp )             /* t0 */\r
286         load_x  x6, 3 * portWORD_SIZE( sp )             /* t1 */\r
287         load_x  x7, 4 * portWORD_SIZE( sp )             /* t2 */\r
288         load_x  x8, 5 * portWORD_SIZE( sp )             /* s0/fp */\r
289         load_x  x9, 6 * portWORD_SIZE( sp )             /* s1 */\r
290         load_x  x10, 7 * portWORD_SIZE( sp )    /* a0 */\r
291         load_x  x11, 8 * portWORD_SIZE( sp )    /* a1 */\r
292         load_x  x12, 9 * portWORD_SIZE( sp )    /* a2 */\r
293         load_x  x13, 10 * portWORD_SIZE( sp )   /* a3 */\r
294         load_x  x14, 11 * portWORD_SIZE( sp )   /* a4 */\r
295         load_x  x15, 12 * portWORD_SIZE( sp )   /* a5 */\r
296         load_x  x16, 13 * portWORD_SIZE( sp )   /* a6 */\r
297         load_x  x17, 14 * portWORD_SIZE( sp )   /* a7 */\r
298         load_x  x18, 15 * portWORD_SIZE( sp )   /* s2 */\r
299         load_x  x19, 16 * portWORD_SIZE( sp )   /* s3 */\r
300         load_x  x20, 17 * portWORD_SIZE( sp )   /* s4 */\r
301         load_x  x21, 18 * portWORD_SIZE( sp )   /* s5 */\r
302         load_x  x22, 19 * portWORD_SIZE( sp )   /* s6 */\r
303         load_x  x23, 20 * portWORD_SIZE( sp )   /* s7 */\r
304         load_x  x24, 21 * portWORD_SIZE( sp )   /* s8 */\r
305         load_x  x25, 22 * portWORD_SIZE( sp )   /* s9 */\r
306         load_x  x26, 23 * portWORD_SIZE( sp )   /* s10 */\r
307         load_x  x27, 24 * portWORD_SIZE( sp )   /* s11 */\r
308         load_x  x28, 25 * portWORD_SIZE( sp )   /* t3 */\r
309         load_x  x29, 26 * portWORD_SIZE( sp )   /* t4 */\r
310         load_x  x30, 27 * portWORD_SIZE( sp )   /* t5 */\r
311         load_x  x31, 28 * portWORD_SIZE( sp )   /* t6 */\r
312         addi    sp, sp, portCONTEXT_SIZE\r
313         ret\r
314         .endfunc\r
315 /*-----------------------------------------------------------*/\r
316 \r
317 /*\r
318  * Unlike other ports pxPortInitialiseStack() is written in assembly code as it\r
319  * needs access to the portasmADDITIONAL_CONTEXT_SIZE constant.  The prototype\r
320  * for the function is as per the other ports:\r
321  * StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters );\r
322  *\r
323  * As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in\r
324  * a1, and pvParameters in a2.  The new top of stack is passed out in a0.\r
325  *\r
326  * RISC-V maps registers to ABI names as follows (X1 to X31 integer registers\r
327  * for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed).\r
328  *\r
329  * Register             ABI Name        Description                                             Saver\r
330  * x0                   zero            Hard-wired zero                                 -\r
331  * x1                   ra                      Return address                                  Caller\r
332  * x2                   sp                      Stack pointer                                   Callee\r
333  * x3                   gp                      Global pointer                                  -\r
334  * x4                   tp                      Thread pointer                                  -\r
335  * x5-7                 t0-2            Temporaries                                             Caller\r
336  * x8                   s0/fp           Saved register/Frame pointer    Callee\r
337  * x9                   s1                      Saved register                                  Callee\r
338  * x10-11               a0-1            Function Arguments/return values Caller\r
339  * x12-17               a2-7            Function arguments                              Caller\r
340  * x18-27               s2-11           Saved registers                                 Callee\r
341  * x28-31               t3-6            Temporaries                                             Caller\r
342  *\r
343  * The RISC-V context is saved t FreeRTOS tasks in the following stack frame,\r
344  * where the global and thread pointers are currently assumed to be constant so\r
345  * are not saved:\r
346  *\r
347  * mstatus\r
348  * x31\r
349  * x30\r
350  * x29\r
351  * x28\r
352  * x27\r
353  * x26\r
354  * x25\r
355  * x24\r
356  * x23\r
357  * x22\r
358  * x21\r
359  * x20\r
360  * x19\r
361  * x18\r
362  * x17\r
363  * x16\r
364  * x15\r
365  * x14\r
366  * x13\r
367  * x12\r
368  * x11\r
369  * pvParameters\r
370  * x9\r
371  * x8\r
372  * x7\r
373  * x6\r
374  * x5\r
375  * portTASK_RETURN_ADDRESS\r
376  * [chip specific registers go here]\r
377  * pxCode\r
378  */\r
379 .align 8\r
380 .func\r
381 pxPortInitialiseStack:\r
382 \r
383         csrr t0, mstatus                                        /* Obtain current mstatus value. */\r
384         addi t1, x0, 0x188                                      /* Generate the value 0x1880, which are the MPIE and MPP bits to set in mstatus. */\r
385         slli t1, t1, 4\r
386         or t0, t0, t1                                           /* Set MPIE and MPP bits in mstatus value. */\r
387 \r
388         addi a0, a0, -portWORD_SIZE\r
389         store_x t0, 0(a0)                                       /* mstatus onto the stack. */\r
390         addi a0, a0, -(22 * portWORD_SIZE)      /* Space for registers x11-x31. */\r
391         store_x a2, 0(a0)                                       /* Task parameters (pvParameters parameter) goes into register X10/a0 on the stack. */\r
392         addi a0, a0, -(6 * portWORD_SIZE)       /* Space for registers x5-x9. */\r
393         store_x x0, 0(a0)                                       /* Return address onto the stack, could be portTASK_RETURN_ADDRESS */\r
394         addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE /* The number of chip specific additional registers. */\r
395 chip_specific_stack_frame:                              /* First add any chip specific registers to the stack frame being created. */\r
396         beq t0, x0, 1f                                          /* No more chip specific registers to save. */\r
397         addi a0, a0, -portWORD_SIZE                     /* Make space for chip specific register. */\r
398         store_x x0, 0(a0)                                       /* Give the chip specific register an initial value of zero. */\r
399         addi t0, t0, -1                                         /* Decrement the count of chip specific registers remaining. */\r
400         j chip_specific_stack_frame                     /* Until no more chip specific registers. */\r
401 1:\r
402         addi a0, a0, -portWORD_SIZE\r
403         store_x a1, 0(a0)                                       /* mret value (pxCode parameter) onto the stack. */\r
404         ret\r
405         .endfunc\r
406 /*-----------------------------------------------------------*/\r