TNeoKernel  v1.02
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tn_arch_pic32.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeoKernel: real-time kernel initially based on TNKernel
4  *
5  * TNKernel: copyright © 2004, 2013 Yuri Tiomkin.
6  * PIC32-specific routines: copyright © 2013, 2014 Anders Montonen.
7  * TNeoKernel: copyright © 2014 Dmitry Frank.
8  *
9  * TNeoKernel was born as a thorough review and re-implementation of
10  * TNKernel. The new kernel has well-formed code, inherited bugs are fixed
11  * as well as new features being added, and it is tested carefully with
12  * unit-tests.
13  *
14  * API is changed somewhat, so it's not 100% compatible with TNKernel,
15  * hence the new name: TNeoKernel.
16  *
17  * Permission to use, copy, modify, and distribute this software in source
18  * and binary forms and its documentation for any purpose and without fee
19  * is hereby granted, provided that the above copyright notice appear
20  * in all copies and that both that copyright notice and this permission
21  * notice appear in supporting documentation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY FRANK OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 /**
38  *
39  * \file
40  *
41  * PIC32 architecture-dependent routines
42  *
43  */
44 
45 #ifndef _TN_ARCH_PIC32_H
46 #define _TN_ARCH_PIC32_H
47 
48 //-- this include is needed to get build-time configuration
49 // (TN_DEBUG is used)
50 #include "../../core/tn_common.h"
51 
52 
53 #ifdef __cplusplus
54 extern "C" { /*}*/
55 #endif
56 
57 #ifndef DOXYGEN_SHOULD_SKIP_THIS
58 
59 #define _TN_PIC32_INTSAVE_DATA_INVALID 0xffffffff
60 
61 #if TN_DEBUG
62 # define _TN_PIC32_INTSAVE_CHECK() \
63 { \
64  if (tn_save_status_reg == _TN_PIC32_INTSAVE_DATA_INVALID){ \
65  _TN_FATAL_ERROR(""); \
66  } \
67 }
68 #else
69 # define _TN_PIC32_INTSAVE_CHECK() /* nothing */
70 #endif
71 
72 /**
73  * FFS - find first set bit. Used in `_find_next_task_to_run()` function.
74  *
75  * May be not defined: in this case, naive algorithm will be used.
76  */
77 #define _TN_FFS(x) (32 - __builtin_clz((x) & (0 - (x))))
78 
79 /**
80  * Used by the kernel as a signal that something really bad happened.
81  * Indicates TNeoKernel bugs as well as illegal kernel usage
82  * (e.g. sleeping in the idle task callback)
83  *
84  * Typically, set to assembler instruction that causes debugger to halt.
85  */
86 #define _TN_FATAL_ERROR(error_msg, ...) \
87  {__asm__ volatile(" sdbbp 0"); __asm__ volatile ("nop");}
88 
89 
90 
91 /**
92  * \def TN_ARCH_STK_ATTR_BEFORE
93  *
94  * Compiler-specific attribute that should be placed **before** declaration of
95  * array used for stack. It is needed because there are often additional
96  * restrictions applied to alignment of stack, so, to meet them, stack arrays
97  * need to be declared with these macros.
98  *
99  * @see TN_ARCH_STK_ATTR_AFTER
100  */
101 
102 /**
103  * \def TN_ARCH_STK_ATTR_AFTER
104  *
105  * Compiler-specific attribute that should be placed **after** declaration of
106  * array used for stack. It is needed because there are often additional
107  * restrictions applied to alignment of stack, so, to meet them, stack arrays
108  * need to be declared with these macros.
109  *
110  * @see TN_ARCH_STK_ATTR_BEFORE
111  */
112 
113 #if defined (__XC32)
114 # define TN_ARCH_STK_ATTR_BEFORE
115 # define TN_ARCH_STK_ATTR_AFTER __attribute__((aligned(0x8)))
116 #else
117 # error "Unknown compiler"
118 #endif
119 
120 /**
121  * Minimum task's stack size, in words, not in bytes; includes a space for
122  * context plus for parameters passed to task's body function.
123  */
124 #define TN_MIN_STACK_SIZE 36
125 
126 /**
127  * Width of `int` type.
128  */
129 #define TN_INT_WIDTH 32
130 
131 /**
132  * Unsigned integer type whose size is equal to the size of CPU register.
133  * Typically it's plain `unsigned int`.
134  */
135 typedef unsigned int TN_UWord;
136 
137 
138 /**
139  * Number of priorities available, this value usually matches `#TN_INT_WIDTH`.
140  * For compatibility with all platforms, it's recommended to use only values
141  * from 1 to 14, inclusive.
142  */
143 #define TN_PRIORITIES_CNT TN_INT_WIDTH
144 
145 /**
146  * Value for infinite waiting, usually matches `UINT_MAX`
147  */
148 #define TN_WAIT_INFINITE 0xFFFFFFFF
149 
150 /**
151  * Value for initializing the task's stack
152  */
153 #define TN_FILL_STACK_VAL 0xFEEDFACE
154 
155 
156 
157 
158 /**
159  * Declares variable that is used by macros `TN_INT_DIS_SAVE()` and
160  * `TN_INT_RESTORE()` for storing status register value.
161  *
162  * It is good idea to initially set it to some invalid value,
163  * and if TN_DEBUG is non-zero, check it in TN_INT_RESTORE().
164  * Then, we can catch bugs if someone tries to restore interrupts status
165  * without saving it first.
166  *
167  * @see `TN_INT_DIS_SAVE()`
168  * @see `TN_INT_RESTORE()`
169  */
170 #define TN_INTSAVE_DATA \
171  int tn_save_status_reg = _TN_PIC32_INTSAVE_DATA_INVALID;
172 
173 /**
174  * The same as `#TN_INTSAVE_DATA` but for using in ISR together with
175  * `TN_INT_IDIS_SAVE()`, `TN_INT_IRESTORE()`.
176  *
177  * @see `TN_INT_IDIS_SAVE()`
178  * @see `TN_INT_IRESTORE()`
179  */
180 #define TN_INTSAVE_DATA_INT TN_INTSAVE_DATA
181 
182 /**
183  * \def TN_INT_DIS_SAVE()
184  *
185  * Disable interrupts and return previous value of status register,
186  * atomically. Similar `tn_arch_sr_save_int_dis()`, but implemented
187  * as a macro, so it is potentially faster.
188  *
189  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
190  *
191  * @see `#TN_INTSAVE_DATA`
192  * @see `tn_arch_sr_save_int_dis()`
193  */
194 
195 /**
196  * \def TN_INT_RESTORE()
197  *
198  * Restore previously saved status register.
199  * Similar to `tn_arch_sr_restore()`, but implemented as a macro,
200  * so it is potentially faster.
201  *
202  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
203  *
204  * @see `#TN_INTSAVE_DATA`
205  * @see `tn_arch_sr_save_int_dis()`
206  */
207 
208 #ifdef __mips16
209 # define TN_INT_DIS_SAVE() tn_save_status_reg = tn_arch_sr_save_int_dis()
210 # define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); \
211  tn_arch_sr_restore(tn_save_status_reg)
212 #else
213 # define TN_INT_DIS_SAVE() __asm__ __volatile__( \
214  "di %0; ehb" \
215  : "=d" (tn_save_status_reg) \
216  )
217 # define TN_INT_RESTORE() _TN_PIC32_INTSAVE_CHECK(); \
218  __builtin_mtc0(12, 0, tn_save_status_reg)
219 #endif
220 
221 /**
222  * The same as `TN_INT_DIS_SAVE()` but for using in ISR.
223  *
224  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
225  *
226  * @see `#TN_INTSAVE_DATA_INT`
227  */
228 #define TN_INT_IDIS_SAVE() TN_INT_DIS_SAVE()
229 
230 /**
231  * The same as `TN_INT_RESTORE()` but for using in ISR.
232  *
233  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
234  *
235  * @see `#TN_INTSAVE_DATA_INT`
236  */
237 #define TN_INT_IRESTORE() TN_INT_RESTORE()
238 
239 /**
240  * Returns nonzero if interrupts are disabled, zero otherwise.
241  */
242 #define TN_IS_INT_DISABLED() ((__builtin_mfc0(12, 0) & 1) == 0)
243 
244 
245 #endif //-- DOXYGEN_SHOULD_SKIP_THIS
246 
247 
248 
249 
250 
251 
252 
253 
254 
255 
256 // ---------------------------------------------------------------------------
257 
258 /**
259  * Interrupt handler wrapper macro for software context saving.
260  *
261  * Usage looks like the following:
262  *
263  * tn_soft_isr(_TIMER_1_VECTOR)
264  * {
265  * INTClearFlag(INT_T1);
266  *
267  * //-- do something useful
268  * }
269  *
270  * Note that you should not use `__ISR(_TIMER_1_VECTOR)` macro for that.
271  *
272  * @param vec interrupt vector number, such as `_TIMER_1_VECTOR`, etc.
273  */
274 #define tn_soft_isr(vec) \
275 __attribute__((__noinline__)) void _func##vec(void); \
276 void __attribute__((naked, nomips16)) \
277  __attribute__((vector(vec))) \
278  _isr##vec(void) \
279 { \
280  asm volatile(".set push"); \
281  asm volatile(".set mips32r2"); \
282  asm volatile(".set nomips16"); \
283  asm volatile(".set noreorder"); \
284  asm volatile(".set noat"); \
285  \
286  asm volatile("rdpgpr $sp, $sp"); \
287  \
288  /* Increase interrupt nesting count */ \
289  asm volatile("lui $k0, %hi(tn_int_nest_count)"); \
290  asm volatile("lw $k1, %lo(tn_int_nest_count)($k0)"); \
291  asm volatile("addiu $k1, $k1, 1"); \
292  asm volatile("sw $k1, %lo(tn_int_nest_count)($k0)"); \
293  asm volatile("ori $k0, $zero, 1"); \
294  asm volatile("bne $k1, $k0, 1f"); \
295  \
296  /* Swap stack pointers if nesting count is one */ \
297  asm volatile("lui $k0, %hi(tn_user_sp)"); \
298  asm volatile("sw $sp, %lo(tn_user_sp)($k0)"); \
299  asm volatile("lui $k0, %hi(tn_int_sp)"); \
300  asm volatile("lw $sp, %lo(tn_int_sp)($k0)"); \
301  \
302  asm volatile("1:"); \
303  /* Save context on stack */ \
304  asm volatile("addiu $sp, $sp, -92"); \
305  asm volatile("mfc0 $k1, $14"); /* c0_epc*/ \
306  asm volatile("mfc0 $k0, $12, 2"); /* c0_srsctl*/ \
307  asm volatile("sw $k1, 84($sp)"); \
308  asm volatile("sw $k0, 80($sp)"); \
309  asm volatile("mfc0 $k1, $12"); /* c0_status*/ \
310  asm volatile("sw $k1, 88($sp)"); \
311  \
312  /* Enable nested interrupts */ \
313  asm volatile("mfc0 $k0, $13"); /* c0_cause*/ \
314  asm volatile("ins $k1, $zero, 1, 15"); \
315  asm volatile("ext $k0, $k0, 10, 6"); \
316  asm volatile("ins $k1, $k0, 10, 6"); \
317  asm volatile("mtc0 $k1, $12"); /* c0_status*/ \
318  \
319  /* Save caller-save registers on stack */ \
320  asm volatile("sw $ra, 76($sp)"); \
321  asm volatile("sw $t9, 72($sp)"); \
322  asm volatile("sw $t8, 68($sp)"); \
323  asm volatile("sw $t7, 64($sp)"); \
324  asm volatile("sw $t6, 60($sp)"); \
325  asm volatile("sw $t5, 56($sp)"); \
326  asm volatile("sw $t4, 52($sp)"); \
327  asm volatile("sw $t3, 48($sp)"); \
328  asm volatile("sw $t2, 44($sp)"); \
329  asm volatile("sw $t1, 40($sp)"); \
330  asm volatile("sw $t0, 36($sp)"); \
331  asm volatile("sw $a3, 32($sp)"); \
332  asm volatile("sw $a2, 28($sp)"); \
333  asm volatile("sw $a1, 24($sp)"); \
334  asm volatile("sw $a0, 20($sp)"); \
335  asm volatile("sw $v1, 16($sp)"); \
336  asm volatile("sw $v0, 12($sp)"); \
337  asm volatile("sw $at, 8($sp)"); \
338  asm volatile("mfhi $v0"); \
339  asm volatile("mflo $v1"); \
340  asm volatile("sw $v0, 4($sp)"); \
341  \
342  /* Call ISR */ \
343  asm volatile("la $t0, _func"#vec); \
344  asm volatile("jalr $t0"); \
345  asm volatile("sw $v1, 0($sp)"); \
346  \
347  /* Pend context switch if needed */ \
348  asm volatile("lw $t0, tn_curr_run_task"); \
349  asm volatile("lw $t1, tn_next_task_to_run"); \
350  asm volatile("lw $t0, 0($t0)"); \
351  asm volatile("lw $t1, 0($t1)"); \
352  asm volatile("lui $t2, %hi(IFS0SET)"); \
353  asm volatile("beq $t0, $t1, 1f"); \
354  asm volatile("ori $t1, $zero, 2"); \
355  asm volatile("sw $t1, %lo(IFS0SET)($t2)"); \
356  \
357  asm volatile("1:"); \
358  /* Restore registers */ \
359  asm volatile("lw $v1, 0($sp)"); \
360  asm volatile("lw $v0, 4($sp)"); \
361  asm volatile("mtlo $v1"); \
362  asm volatile("mthi $v0"); \
363  asm volatile("lw $at, 8($sp)"); \
364  asm volatile("lw $v0, 12($sp)"); \
365  asm volatile("lw $v1, 16($sp)"); \
366  asm volatile("lw $a0, 20($sp)"); \
367  asm volatile("lw $a1, 24($sp)"); \
368  asm volatile("lw $a2, 28($sp)"); \
369  asm volatile("lw $a3, 32($sp)"); \
370  asm volatile("lw $t0, 36($sp)"); \
371  asm volatile("lw $t1, 40($sp)"); \
372  asm volatile("lw $t2, 44($sp)"); \
373  asm volatile("lw $t3, 48($sp)"); \
374  asm volatile("lw $t4, 52($sp)"); \
375  asm volatile("lw $t5, 56($sp)"); \
376  asm volatile("lw $t6, 60($sp)"); \
377  asm volatile("lw $t7, 64($sp)"); \
378  asm volatile("lw $t8, 68($sp)"); \
379  asm volatile("lw $t9, 72($sp)"); \
380  asm volatile("lw $ra, 76($sp)"); \
381  \
382  asm volatile("di"); \
383  asm volatile("ehb"); \
384  \
385  /* Restore context */ \
386  asm volatile("lw $k0, 84($sp)"); \
387  asm volatile("mtc0 $k0, $14"); /* c0_epc */ \
388  asm volatile("lw $k0, 80($sp)"); \
389  asm volatile("mtc0 $k0, $12, 2"); /* c0_srsctl */ \
390  asm volatile("addiu $sp, $sp, 92"); \
391  \
392  /* Decrease interrupt nesting count */ \
393  asm volatile("lui $k0, %hi(tn_int_nest_count)"); \
394  asm volatile("lw $k1, %lo(tn_int_nest_count)($k0)"); \
395  asm volatile("addiu $k1, $k1, -1"); \
396  asm volatile("sw $k1, %lo(tn_int_nest_count)($k0)"); \
397  asm volatile("bne $k1, $zero, 1f"); \
398  asm volatile("lw $k1, -4($sp)"); \
399  \
400  /* Swap stack pointers if nesting count is zero */ \
401  asm volatile("lui $k0, %hi(tn_int_sp)"); \
402  asm volatile("sw $sp, %lo(tn_int_sp)($k0)"); \
403  asm volatile("lui $k0, %hi(tn_user_sp)"); \
404  asm volatile("lw $sp, %lo(tn_user_sp)($k0)"); \
405  \
406  asm volatile("1:"); \
407  asm volatile("wrpgpr $sp, $sp"); \
408  asm volatile("mtc0 $k1, $12"); /* c0_status */ \
409  asm volatile("eret"); \
410  \
411  asm volatile(".set pop"); \
412  \
413 } __attribute((__noinline__)) void _func##vec(void)
414 
415 
416 
417 
418 /**
419  * Interrupt handler wrapper macro for shadow register context saving.
420  *
421  * Usage looks like the following:
422  *
423  * tn_srs_isr(_INT_UART_1_VECTOR)
424  * {
425  * INTClearFlag(INT_U1);
426  *
427  * //-- do something useful
428  * }
429  *
430  * Note that you should not use `__ISR(_INT_UART_1_VECTOR)` macro for that.
431  *
432  * @param vec interrupt vector number, such as `_TIMER_1_VECTOR`, etc.
433  */
434 #define tn_srs_isr(vec) \
435 __attribute__((__noinline__)) void _func##vec(void); \
436 void __attribute__((naked, nomips16)) \
437  __attribute__((vector(vec))) \
438  _isr##vec(void) \
439 { \
440  asm volatile(".set push"); \
441  asm volatile(".set mips32r2"); \
442  asm volatile(".set nomips16"); \
443  asm volatile(".set noreorder"); \
444  asm volatile(".set noat"); \
445  \
446  asm volatile("rdpgpr $sp, $sp"); \
447  \
448  /* Increase interrupt nesting count */ \
449  asm volatile("lui $k0, %hi(tn_int_nest_count)"); \
450  asm volatile("lw $k1, %lo(tn_int_nest_count)($k0)"); \
451  asm volatile("addiu $k1, $k1, 1"); \
452  asm volatile("sw $k1, %lo(tn_int_nest_count)($k0)"); \
453  asm volatile("ori $k0, $zero, 1"); \
454  asm volatile("bne $k1, $k0, 1f"); \
455  \
456  /* Swap stack pointers if nesting count is one */ \
457  asm volatile("lui $k0, %hi(tn_user_sp)"); \
458  asm volatile("sw $sp, %lo(tn_user_sp)($k0)"); \
459  asm volatile("lui $k0, %hi(tn_int_sp)"); \
460  asm volatile("lw $sp, %lo(tn_int_sp)($k0)"); \
461  \
462  asm volatile("1:"); \
463  /* Save context on stack */ \
464  asm volatile("addiu $sp, $sp, -20"); \
465  asm volatile("mfc0 $k1, $14"); /* c0_epc */ \
466  asm volatile("mfc0 $k0, $12, 2"); /* c0_srsctl */ \
467  asm volatile("sw $k1, 12($sp)"); \
468  asm volatile("sw $k0, 8($sp)"); \
469  asm volatile("mfc0 $k1, $12"); /* c0_status */ \
470  asm volatile("sw $k1, 16($sp)"); \
471  \
472  /* Enable nested interrupts */ \
473  asm volatile("mfc0 $k0, $13"); /* c0_cause */ \
474  asm volatile("ins $k1, $zero, 1, 15"); \
475  asm volatile("ext $k0, $k0, 10, 6"); \
476  asm volatile("ins $k1, $k0, 10, 6"); \
477  asm volatile("mtc0 $k1, $12"); /* c0_status */ \
478  \
479  /* Save caller-save registers on stack */ \
480  asm volatile("mfhi $v0"); \
481  asm volatile("mflo $v1"); \
482  asm volatile("sw $v0, 4($sp)"); \
483  \
484  /* Call ISR */ \
485  asm volatile("la $t0, _func"#vec); \
486  asm volatile("jalr $t0"); \
487  asm volatile("sw $v1, 0($sp)"); \
488  \
489  /* Pend context switch if needed */ \
490  asm volatile("lw $t0, tn_curr_run_task"); \
491  asm volatile("lw $t1, tn_next_task_to_run"); \
492  asm volatile("lw $t0, 0($t0)"); \
493  asm volatile("lw $t1, 0($t1)"); \
494  asm volatile("lui $t2, %hi(IFS0SET)"); \
495  asm volatile("beq $t0, $t1, 1f"); \
496  asm volatile("ori $t1, $zero, 2"); \
497  asm volatile("sw $t1, %lo(IFS0SET)($t2)"); \
498  \
499  asm volatile("1:"); \
500  /* Restore registers */ \
501  asm volatile("lw $v1, 0($sp)"); \
502  asm volatile("lw $v0, 4($sp)"); \
503  asm volatile("mtlo $v1"); \
504  asm volatile("mthi $v0"); \
505  \
506  asm volatile("di"); \
507  asm volatile("ehb"); \
508  \
509  /* Restore context */ \
510  asm volatile("lw $k0, 12($sp)"); \
511  asm volatile("mtc0 $k0, $14"); /* c0_epc */ \
512  asm volatile("lw $k0, 8($sp)"); \
513  asm volatile("mtc0 $k0, $12, 2"); /* c0_srsctl */ \
514  asm volatile("addiu $sp, $sp, 20"); \
515  \
516  /* Decrease interrupt nesting count */ \
517  asm volatile("lui $k0, %hi(tn_int_nest_count)"); \
518  asm volatile("lw $k1, %lo(tn_int_nest_count)($k0)"); \
519  asm volatile("addiu $k1, $k1, -1"); \
520  asm volatile("sw $k1, %lo(tn_int_nest_count)($k0)"); \
521  asm volatile("bne $k1, $zero, 1f"); \
522  asm volatile("lw $k1, -4($sp)"); \
523  \
524  /* Swap stack pointers if nesting count is zero */ \
525  asm volatile("lui $k0, %hi(tn_int_sp)"); \
526  asm volatile("sw $sp, %lo(tn_int_sp)($k0)"); \
527  asm volatile("lui $k0, %hi(tn_user_sp)"); \
528  asm volatile("lw $sp, %lo(tn_user_sp)($k0)"); \
529  \
530  asm volatile("1:"); \
531  asm volatile("wrpgpr $sp, $sp"); \
532  asm volatile("mtc0 $k1, $12"); /* c0_status */ \
533  asm volatile("eret"); \
534  \
535  asm volatile(".set pop"); \
536  \
537 } __attribute((__noinline__)) void _func##vec(void)
538 
539 
540 #ifdef __cplusplus
541 } /* extern "C" */
542 #endif
543 
544 #endif // _TN_ARCH_PIC32_H
545 
unsigned int TN_UWord
Unsigned integer type whose size is equal to the size of CPU register.