1 /*
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28 
29 #ifndef PORTCONTEXT_H
30 #define PORTCONTEXT_H
31 
32 #ifndef configENABLE_FPU
33     #define configENABLE_FPU 0
34 #endif
35 
36 #ifndef configENABLE_VPU
37     #define configENABLE_VPU 0
38 #endif
39 
40 #if __riscv_xlen == 64
41     #define portWORD_SIZE    8
42     #define store_x          sd
43     #define load_x           ld
44 #elif __riscv_xlen == 32
45     #define store_x          sw
46     #define load_x           lw
47     #define portWORD_SIZE    4
48 #else
49     #error Assembler did not define __riscv_xlen
50 #endif
51 
52 #include "freertos_risc_v_chip_specific_extensions.h"
53 
54 /* Only the standard core registers are stored by default.  Any additional
55  * registers must be saved by the portasmSAVE_ADDITIONAL_REGISTERS and
56  * portasmRESTORE_ADDITIONAL_REGISTERS macros - which can be defined in a chip
57  * specific version of freertos_risc_v_chip_specific_extensions.h.  See the
58  * notes at the top of portASM.S file. */
59 #ifdef __riscv_32e
60     #define portCONTEXT_SIZE               ( 15 * portWORD_SIZE )
61     #define portCRITICAL_NESTING_OFFSET    14
62 #else
63     #define portCONTEXT_SIZE               ( 31 * portWORD_SIZE )
64     #define portCRITICAL_NESTING_OFFSET    30
65 #endif
66 
67 #if ( configENABLE_FPU == 1 )
68     /* Bit [14:13] in the mstatus encode the status of FPU state which is one of
69      * the following values:
70      * 1. Value: 0, Meaning: Off.
71      * 2. Value: 1, Meaning: Initial.
72      * 3. Value: 2, Meaning: Clean.
73      * 4. Value: 3, Meaning: Dirty.
74      */
75     #define MSTATUS_FS_MASK                 0x6000
76     #define MSTATUS_FS_INITIAL              0x2000
77     #define MSTATUS_FS_CLEAN                0x4000
78     #define MSTATUS_FS_DIRTY                0x6000
79     #define MSTATUS_FS_OFFSET               13
80 
81     #ifdef __riscv_fdiv
82         #if __riscv_flen == 32
83             #define load_f                  flw
84             #define store_f                 fsw
85         #elif __riscv_flen == 64
86             #define load_f                  fld
87             #define store_f                 fsd
88         #else
89             #error Assembler did not define __riscv_flen
90         #endif
91 
92         #define portFPU_REG_SIZE                ( __riscv_flen / 8 )
93         #define portFPU_REG_COUNT               33 /* 32 Floating point registers plus one CSR. */
94         #define portFPU_REG_OFFSET( regIndex )  ( ( 2 * portWORD_SIZE ) + ( regIndex * portFPU_REG_SIZE ) )
95         #define portFPU_CONTEXT_SIZE            ( portFPU_REG_SIZE * portFPU_REG_COUNT )
96     #else
97         #error configENABLE_FPU must not be set to 1 if the hardware does not have FPU
98     #endif
99 #endif
100 
101 #if ( configENABLE_VPU == 1 )
102     /* Bit [10:9] in the mstatus encode the status of VPU state which is one of
103      * the following values:
104      * 1. Value: 0, Meaning: Off.
105      * 2. Value: 1, Meaning: Initial.
106      * 3. Value: 2, Meaning: Clean.
107      * 4. Value: 3, Meaning: Dirty.
108      */
109     #define MSTATUS_VS_MASK                 0x600
110     #define MSTATUS_VS_INITIAL              0x200
111     #define MSTATUS_VS_CLEAN                0x400
112     #define MSTATUS_VS_DIRTY                0x600
113     #define MSTATUS_VS_OFFSET               9
114 
115     #ifndef __riscv_vector
116         #error configENABLE_VPU must not be set to 1 if the hardware does not have VPU
117     #endif
118 #endif
119 /*-----------------------------------------------------------*/
120 
121 .extern pxCurrentTCB
122 .extern xISRStackTop
123 .extern xCriticalNesting
124 .extern pxCriticalNesting
125 /*-----------------------------------------------------------*/
126 
127     .macro portcontexSAVE_FPU_CONTEXT
128 addi sp, sp, -( portFPU_CONTEXT_SIZE )
129 /* Store the FPU registers. */
130 store_f f0,  portFPU_REG_OFFSET( 0  )( sp )
131 store_f f1,  portFPU_REG_OFFSET( 1  )( sp )
132 store_f f2,  portFPU_REG_OFFSET( 2  )( sp )
133 store_f f3,  portFPU_REG_OFFSET( 3  )( sp )
134 store_f f4,  portFPU_REG_OFFSET( 4  )( sp )
135 store_f f5,  portFPU_REG_OFFSET( 5  )( sp )
136 store_f f6,  portFPU_REG_OFFSET( 6  )( sp )
137 store_f f7,  portFPU_REG_OFFSET( 7  )( sp )
138 store_f f8,  portFPU_REG_OFFSET( 8  )( sp )
139 store_f f9,  portFPU_REG_OFFSET( 9  )( sp )
140 store_f f10, portFPU_REG_OFFSET( 10 )( sp )
141 store_f f11, portFPU_REG_OFFSET( 11 )( sp )
142 store_f f12, portFPU_REG_OFFSET( 12 )( sp )
143 store_f f13, portFPU_REG_OFFSET( 13 )( sp )
144 store_f f14, portFPU_REG_OFFSET( 14 )( sp )
145 store_f f15, portFPU_REG_OFFSET( 15 )( sp )
146 store_f f16, portFPU_REG_OFFSET( 16 )( sp )
147 store_f f17, portFPU_REG_OFFSET( 17 )( sp )
148 store_f f18, portFPU_REG_OFFSET( 18 )( sp )
149 store_f f19, portFPU_REG_OFFSET( 19 )( sp )
150 store_f f20, portFPU_REG_OFFSET( 20 )( sp )
151 store_f f21, portFPU_REG_OFFSET( 21 )( sp )
152 store_f f22, portFPU_REG_OFFSET( 22 )( sp )
153 store_f f23, portFPU_REG_OFFSET( 23 )( sp )
154 store_f f24, portFPU_REG_OFFSET( 24 )( sp )
155 store_f f25, portFPU_REG_OFFSET( 25 )( sp )
156 store_f f26, portFPU_REG_OFFSET( 26 )( sp )
157 store_f f27, portFPU_REG_OFFSET( 27 )( sp )
158 store_f f28, portFPU_REG_OFFSET( 28 )( sp )
159 store_f f29, portFPU_REG_OFFSET( 29 )( sp )
160 store_f f30, portFPU_REG_OFFSET( 30 )( sp )
161 store_f f31, portFPU_REG_OFFSET( 31 )( sp )
162 csrr t0, fcsr
163 store_x t0,  portFPU_REG_OFFSET( 32 )( sp )
164     .endm
165 /*-----------------------------------------------------------*/
166 
167     .macro portcontextRESTORE_FPU_CONTEXT
168 /* Restore the FPU registers. */
169 load_f f0,  portFPU_REG_OFFSET( 0  )( sp )
170 load_f f1,  portFPU_REG_OFFSET( 1  )( sp )
171 load_f f2,  portFPU_REG_OFFSET( 2  )( sp )
172 load_f f3,  portFPU_REG_OFFSET( 3  )( sp )
173 load_f f4,  portFPU_REG_OFFSET( 4  )( sp )
174 load_f f5,  portFPU_REG_OFFSET( 5  )( sp )
175 load_f f6,  portFPU_REG_OFFSET( 6  )( sp )
176 load_f f7,  portFPU_REG_OFFSET( 7  )( sp )
177 load_f f8,  portFPU_REG_OFFSET( 8  )( sp )
178 load_f f9,  portFPU_REG_OFFSET( 9  )( sp )
179 load_f f10, portFPU_REG_OFFSET( 10 )( sp )
180 load_f f11, portFPU_REG_OFFSET( 11 )( sp )
181 load_f f12, portFPU_REG_OFFSET( 12 )( sp )
182 load_f f13, portFPU_REG_OFFSET( 13 )( sp )
183 load_f f14, portFPU_REG_OFFSET( 14 )( sp )
184 load_f f15, portFPU_REG_OFFSET( 15 )( sp )
185 load_f f16, portFPU_REG_OFFSET( 16 )( sp )
186 load_f f17, portFPU_REG_OFFSET( 17 )( sp )
187 load_f f18, portFPU_REG_OFFSET( 18 )( sp )
188 load_f f19, portFPU_REG_OFFSET( 19 )( sp )
189 load_f f20, portFPU_REG_OFFSET( 20 )( sp )
190 load_f f21, portFPU_REG_OFFSET( 21 )( sp )
191 load_f f22, portFPU_REG_OFFSET( 22 )( sp )
192 load_f f23, portFPU_REG_OFFSET( 23 )( sp )
193 load_f f24, portFPU_REG_OFFSET( 24 )( sp )
194 load_f f25, portFPU_REG_OFFSET( 25 )( sp )
195 load_f f26, portFPU_REG_OFFSET( 26 )( sp )
196 load_f f27, portFPU_REG_OFFSET( 27 )( sp )
197 load_f f28, portFPU_REG_OFFSET( 28 )( sp )
198 load_f f29, portFPU_REG_OFFSET( 29 )( sp )
199 load_f f30, portFPU_REG_OFFSET( 30 )( sp )
200 load_f f31, portFPU_REG_OFFSET( 31 )( sp )
201 load_x t0,  portFPU_REG_OFFSET( 32 )( sp )
202 csrw fcsr, t0
203 addi sp, sp, ( portFPU_CONTEXT_SIZE )
204     .endm
205 /*-----------------------------------------------------------*/
206 
207     .macro portcontexSAVE_VPU_CONTEXT
208 /* Un-reserve the space reserved for mstatus and epc. */
209 add sp, sp, ( 2 * portWORD_SIZE )
210 
211 csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
212 slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
213 neg  t0, t0
214 
215 /* Store the vector registers in group of 8. */
216 add     sp, sp, t0
217 vs8r.v  v0, (sp)    /* Store v0-v7. */
218 add     sp, sp, t0
219 vs8r.v  v8, (sp)    /* Store v8-v15. */
220 add     sp, sp, t0
221 vs8r.v  v16, (sp)   /* Store v16-v23. */
222 add     sp, sp, t0
223 vs8r.v  v24, (sp)   /* Store v24-v31. */
224 
225 /* Store the VPU CSRs. */
226 addi    sp, sp, -( 4 * portWORD_SIZE )
227 csrr    t0, vstart
228 store_x t0, 0 * portWORD_SIZE( sp )
229 csrr    t0, vcsr
230 store_x t0, 1 * portWORD_SIZE( sp )
231 csrr    t0, vl
232 store_x t0, 2 * portWORD_SIZE( sp )
233 csrr    t0, vtype
234 store_x t0, 3 * portWORD_SIZE( sp )
235 
236 /* Re-reserve the space for mstatus and epc. */
237 add sp, sp, -( 2 * portWORD_SIZE )
238     .endm
239 /*-----------------------------------------------------------*/
240 
241     .macro portcontextRESTORE_VPU_CONTEXT
242 /* Un-reserve the space reserved for mstatus and epc. */
243 add sp, sp, ( 2 * portWORD_SIZE )
244 
245 /* Restore the VPU CSRs. */
246 load_x  t0, 0  * portWORD_SIZE( sp )
247 csrw    vstart, t0
248 load_x  t0, 1 * portWORD_SIZE( sp )
249 csrw    vcsr, t0
250 load_x  t0, 2 * portWORD_SIZE( sp )
251 load_x  t1, 3 * portWORD_SIZE( sp )
252 vsetvl  x0, t0, t1 /* vlen and vtype can only be updated by using vset*vl* instructions. */
253 addi    sp, sp, ( 4 * portWORD_SIZE )
254 
255 csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
256 slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
257 
258 /* Restore the vector registers. */
259 vl8r.v  v24, (sp)
260 add     sp, sp, t0
261 vl8r.v  v16, (sp)
262 add     sp, sp, t0
263 vl8r.v  v8, (sp)
264 add     sp, sp, t0
265 vl8r.v  v0, (sp)
266 add     sp, sp, t0
267 
268 /* Re-reserve the space for mstatus and epc. */
269 add sp, sp, -( 2 * portWORD_SIZE )
270     .endm
271 /*-----------------------------------------------------------*/
272 
273    .macro portcontextSAVE_CONTEXT_INTERNAL
274 addi sp, sp, -portCONTEXT_SIZE
275 store_x x1,  2  * portWORD_SIZE( sp )
276 store_x x5,  3  * portWORD_SIZE( sp )
277 store_x x6,  4  * portWORD_SIZE( sp )
278 store_x x7,  5  * portWORD_SIZE( sp )
279 store_x x8,  6  * portWORD_SIZE( sp )
280 store_x x9,  7  * portWORD_SIZE( sp )
281 store_x x10, 8  * portWORD_SIZE( sp )
282 store_x x11, 9  * portWORD_SIZE( sp )
283 store_x x12, 10 * portWORD_SIZE( sp )
284 store_x x13, 11 * portWORD_SIZE( sp )
285 store_x x14, 12 * portWORD_SIZE( sp )
286 store_x x15, 13 * portWORD_SIZE( sp )
287 #ifndef __riscv_32e
288     store_x x16, 14 * portWORD_SIZE( sp )
289     store_x x17, 15 * portWORD_SIZE( sp )
290     store_x x18, 16 * portWORD_SIZE( sp )
291     store_x x19, 17 * portWORD_SIZE( sp )
292     store_x x20, 18 * portWORD_SIZE( sp )
293     store_x x21, 19 * portWORD_SIZE( sp )
294     store_x x22, 20 * portWORD_SIZE( sp )
295     store_x x23, 21 * portWORD_SIZE( sp )
296     store_x x24, 22 * portWORD_SIZE( sp )
297     store_x x25, 23 * portWORD_SIZE( sp )
298     store_x x26, 24 * portWORD_SIZE( sp )
299     store_x x27, 25 * portWORD_SIZE( sp )
300     store_x x28, 26 * portWORD_SIZE( sp )
301     store_x x29, 27 * portWORD_SIZE( sp )
302     store_x x30, 28 * portWORD_SIZE( sp )
303     store_x x31, 29 * portWORD_SIZE( sp )
304 #endif /* ifndef __riscv_32e */
305 
306 load_x t0, xCriticalNesting                                   /* Load the value of xCriticalNesting into t0. */
307 store_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Store the critical nesting value to the stack. */
308 
309 #if( configENABLE_FPU == 1 )
310     csrr t0, mstatus
311     srl t1, t0, MSTATUS_FS_OFFSET
312     andi t1, t1, 3
313     addi t2, x0, 3
314     bne t1, t2, 1f /* If FPU status is not dirty, do not save FPU registers. */
315 
316     portcontexSAVE_FPU_CONTEXT
317 1:
318 #endif
319 
320 #if( configENABLE_VPU == 1 )
321     csrr t0, mstatus
322     srl t1, t0, MSTATUS_VS_OFFSET
323     andi t1, t1, 3
324     addi t2, x0, 3
325     bne t1, t2, 2f /* If VPU status is not dirty, do not save FPU registers. */
326 
327     portcontexSAVE_VPU_CONTEXT
328 2:
329 #endif
330 
331 portasmSAVE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */
332 
333 csrr t0, mstatus
334 store_x t0, 1 * portWORD_SIZE( sp )
335 
336 #if( configENABLE_FPU == 1 )
337     /* Mark the FPU as clean, if it was dirty and we saved FPU registers. */
338     srl t1, t0, MSTATUS_FS_OFFSET
339     andi t1, t1, 3
340     addi t2, x0, 3
341     bne t1, t2, 3f
342 
343     li t1, ~MSTATUS_FS_MASK
344     and t0, t0, t1
345     li t1, MSTATUS_FS_CLEAN
346     or t0, t0, t1
347     csrw mstatus, t0
348 3:
349 #endif
350 
351 #if( configENABLE_VPU == 1 )
352     /* Mark the VPU as clean, if it was dirty and we saved VPU registers. */
353     srl t1, t0, MSTATUS_VS_OFFSET
354     andi t1, t1, 3
355     addi t2, x0, 3
356     bne t1, t2, 4f
357 
358     li t1, ~MSTATUS_VS_MASK
359     and t0, t0, t1
360     li t1, MSTATUS_VS_CLEAN
361     or t0, t0, t1
362     csrw mstatus, t0
363 4:
364 #endif
365 
366 load_x t0, pxCurrentTCB          /* Load pxCurrentTCB. */
367 store_x sp, 0 ( t0 )             /* Write sp to first TCB member. */
368 
369    .endm
370 /*-----------------------------------------------------------*/
371 
372    .macro portcontextSAVE_EXCEPTION_CONTEXT
373 portcontextSAVE_CONTEXT_INTERNAL
374 csrr a0, mcause
375 csrr a1, mepc
376 addi a1, a1, 4          /* Synchronous so update exception return address to the instruction after the instruction that generated the exception. */
377 store_x a1, 0 ( sp )    /* Save updated exception return address. */
378 load_x sp, xISRStackTop /* Switch to ISR stack. */
379    .endm
380 /*-----------------------------------------------------------*/
381 
382    .macro portcontextSAVE_INTERRUPT_CONTEXT
383 portcontextSAVE_CONTEXT_INTERNAL
384 csrr a0, mcause
385 csrr a1, mepc
386 store_x a1, 0 ( sp )    /* Asynchronous interrupt so save unmodified exception return address. */
387 load_x sp, xISRStackTop /* Switch to ISR stack. */
388    .endm
389 /*-----------------------------------------------------------*/
390 
391    .macro portcontextRESTORE_CONTEXT
392 load_x t1, pxCurrentTCB /* Load pxCurrentTCB. */
393 load_x sp, 0 ( t1 )     /* Read sp from first TCB member. */
394 
395 /* Load mepc with the address of the instruction in the task to run next. */
396 load_x t0, 0 ( sp )
397 csrw mepc, t0
398 
399 /* Restore mstatus register. */
400 load_x t0, 1 * portWORD_SIZE( sp )
401 csrw mstatus, t0
402 
403 /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */
404 portasmRESTORE_ADDITIONAL_REGISTERS
405 
406 #if( configENABLE_VPU == 1 )
407     csrr t0, mstatus
408     srl t1, t0, MSTATUS_VS_OFFSET
409     andi t1, t1, 3
410     addi t2, x0, 3
411     bne t1, t2, 5f /* If VPU status is not dirty, do not restore VPU registers. */
412 
413     portcontextRESTORE_VPU_CONTEXT
414 5:
415 #endif /* ifdef portasmSTORE_VPU_CONTEXT */
416 
417 #if( configENABLE_FPU == 1 )
418     csrr t0, mstatus
419     srl t1, t0, MSTATUS_FS_OFFSET
420     andi t1, t1, 3
421     addi t2, x0, 3
422     bne t1, t2, 6f /* If FPU status is not dirty, do not restore FPU registers. */
423 
424     portcontextRESTORE_FPU_CONTEXT
425 6:
426 #endif /* ifdef portasmSTORE_FPU_CONTEXT */
427 
428 load_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Obtain xCriticalNesting value for this task from task's stack. */
429 load_x t1, pxCriticalNesting                                 /* Load the address of xCriticalNesting into t1. */
430 store_x t0, 0 ( t1 )                                         /* Restore the critical nesting value for this task. */
431 
432 load_x x1,  2  * portWORD_SIZE( sp )
433 load_x x5,  3  * portWORD_SIZE( sp )
434 load_x x6,  4  * portWORD_SIZE( sp )
435 load_x x7,  5  * portWORD_SIZE( sp )
436 load_x x8,  6  * portWORD_SIZE( sp )
437 load_x x9,  7  * portWORD_SIZE( sp )
438 load_x x10, 8  * portWORD_SIZE( sp )
439 load_x x11, 9  * portWORD_SIZE( sp )
440 load_x x12, 10 * portWORD_SIZE( sp )
441 load_x x13, 11 * portWORD_SIZE( sp )
442 load_x x14, 12 * portWORD_SIZE( sp )
443 load_x x15, 13 * portWORD_SIZE( sp )
444 #ifndef __riscv_32e
445     load_x x16, 14 * portWORD_SIZE( sp )
446     load_x x17, 15 * portWORD_SIZE( sp )
447     load_x x18, 16 * portWORD_SIZE( sp )
448     load_x x19, 17 * portWORD_SIZE( sp )
449     load_x x20, 18 * portWORD_SIZE( sp )
450     load_x x21, 19 * portWORD_SIZE( sp )
451     load_x x22, 20 * portWORD_SIZE( sp )
452     load_x x23, 21 * portWORD_SIZE( sp )
453     load_x x24, 22 * portWORD_SIZE( sp )
454     load_x x25, 23 * portWORD_SIZE( sp )
455     load_x x26, 24 * portWORD_SIZE( sp )
456     load_x x27, 25 * portWORD_SIZE( sp )
457     load_x x28, 26 * portWORD_SIZE( sp )
458     load_x x29, 27 * portWORD_SIZE( sp )
459     load_x x30, 28 * portWORD_SIZE( sp )
460     load_x x31, 29 * portWORD_SIZE( sp )
461 #endif /* ifndef __riscv_32e */
462 addi sp, sp, portCONTEXT_SIZE
463 
464 mret
465    .endm
466 /*-----------------------------------------------------------*/
467 
468 #endif /* PORTCONTEXT_H */
469