TNeo  v1.08
tn_arch_pic24.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeo: 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  * TNeo: copyright 2014 Dmitry Frank.
8  *
9  * TNeo 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: TNeo.
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  * PIC24/dsPIC architecture-dependent routines
42  *
43  */
44 
45 #ifndef _TN_ARCH_PIC24_H
46 #define _TN_ARCH_PIC24_H
47 
48 
49 /*******************************************************************************
50  * INCLUDED FILES
51  ******************************************************************************/
52 
53 //-- Include `tn_common_macros.h` for `_TN_STRINGIFY_MACRO()`
54 #include "../../core/tn_common_macros.h"
55 #include "../../core/tn_cfg_dispatch.h"
56 
57 //-- include macros for atomic assess to structure bit fields so that
58 // application can use it too.
59 #include "tn_arch_pic24_bfa.h"
60 
61 
62 #ifdef __cplusplus
63 extern "C" { /*}*/
64 #endif
65 
66 
67 
68 /*******************************************************************************
69  * ARCH-DEPENDENT DEFINITIONS
70  ******************************************************************************/
71 
72 #ifndef DOXYGEN_SHOULD_SKIP_THIS
73 
74 #define _TN_PIC24_INTSAVE_DATA_INVALID 0xffff
75 
76 #if TN_DEBUG
77 # define _TN_PIC24_INTSAVE_CHECK() \
78 { \
79  if (TN_INTSAVE_VAR == _TN_PIC24_INTSAVE_DATA_INVALID){ \
80  _TN_FATAL_ERROR(""); \
81  } \
82 }
83 #else
84 # define _TN_PIC24_INTSAVE_CHECK() /* nothing */
85 #endif
86 
87 /**
88  * FFS - find first set bit. Used in `_find_next_task_to_run()` function.
89  * Say, for `0xa8` it should return `3`.
90  *
91  * May be not defined: in this case, naive algorithm will be used.
92  */
93 #define _TN_FFS(x) _tn_p24_ffs_asm(x)
94 int _tn_p24_ffs_asm(int x);
95 
96 /**
97  * Used by the kernel as a signal that something really bad happened.
98  * Indicates TNeo bugs as well as illegal kernel usage
99  * (e.g. sleeping in the idle task callback)
100  *
101  * Typically, set to assembler instruction that causes debugger to halt.
102  */
103 #define _TN_FATAL_ERROR(error_msg, ...) \
104  {__asm__ volatile(".pword 0xDA4000"); __asm__ volatile ("nop");}
105 
106 
107 
108 /**
109  * \def TN_ARCH_STK_ATTR_BEFORE
110  *
111  * Compiler-specific attribute that should be placed **before** declaration of
112  * array used for stack. It is needed because there are often additional
113  * restrictions applied to alignment of stack, so, to meet them, stack arrays
114  * need to be declared with these macros.
115  *
116  * @see TN_ARCH_STK_ATTR_AFTER
117  */
118 
119 /**
120  * \def TN_ARCH_STK_ATTR_AFTER
121  *
122  * Compiler-specific attribute that should be placed **after** declaration of
123  * array used for stack. It is needed because there are often additional
124  * restrictions applied to alignment of stack, so, to meet them, stack arrays
125  * need to be declared with these macros.
126  *
127  * @see TN_ARCH_STK_ATTR_BEFORE
128  */
129 
130 #if defined (__C30__)
131 # define TN_ARCH_STK_ATTR_BEFORE
132 # define TN_ARCH_STK_ATTR_AFTER
133 #else
134 # error "Unknown compiler"
135 #endif
136 
137 /**
138  * Minimum task's stack size, in words, not in bytes; includes a space for
139  * context plus for parameters passed to task's body function.
140  */
141 #define TN_MIN_STACK_SIZE (25 \
142  + _TN_EDS_STACK_ADD \
143  + _TN_STACK_OVERFLOW_SIZE_ADD \
144  )
145 
146 /**
147  * Some devices have two registers: DSRPAG and DSWPAG instead of PSVPAG.
148  * If __HAS_EDS__ is defined, device has two these registers,
149  * so we should take this in account.
150  */
151 #ifdef __HAS_EDS__
152 # define _TN_EDS_STACK_ADD 1
153 #else
154 # define _TN_EDS_STACK_ADD 0
155 #endif
156 
157 
158 /**
159  * Width of `int` type.
160  */
161 #define TN_INT_WIDTH 16
162 
163 /**
164  * Unsigned integer type whose size is equal to the size of CPU register.
165  * Typically it's plain `unsigned int`.
166  */
167 typedef unsigned int TN_UWord;
168 
169 /**
170  * Unsigned integer type that is able to store pointers.
171  * We need it because some platforms don't define `uintptr_t`.
172  * Typically it's `unsigned int`.
173  */
174 typedef unsigned int TN_UIntPtr;
175 
176 
177 /**
178  * Maximum number of priorities available, this value usually matches
179  * `#TN_INT_WIDTH`.
180  *
181  * @see TN_PRIORITIES_CNT
182  */
183 #define TN_PRIORITIES_MAX_CNT TN_INT_WIDTH
184 
185 /**
186  * Value for infinite waiting, usually matches `ULONG_MAX`,
187  * because `#TN_TickCnt` is declared as `unsigned long`.
188  */
189 #define TN_WAIT_INFINITE (TN_TickCnt)0xFFFFFFFF
190 
191 /**
192  * Value for initializing the task's stack
193  */
194 #define TN_FILL_STACK_VAL 0xFEED
195 
196 
197 
198 /**
199  * Variable name that is used for storing interrupts state
200  * by macros TN_INTSAVE_DATA and friends
201  */
202 #define TN_INTSAVE_VAR tn_save_status_reg
203 
204 /**
205  * Declares variable that is used by macros `TN_INT_DIS_SAVE()` and
206  * `TN_INT_RESTORE()` for storing status register value.
207  *
208  * It is good idea to initially set it to some invalid value,
209  * and if TN_DEBUG is non-zero, check it in TN_INT_RESTORE().
210  * Then, we can catch bugs if someone tries to restore interrupts status
211  * without saving it first.
212  *
213  * @see `TN_INT_DIS_SAVE()`
214  * @see `TN_INT_RESTORE()`
215  */
216 #define TN_INTSAVE_DATA \
217  TN_UWord TN_INTSAVE_VAR = _TN_PIC24_INTSAVE_DATA_INVALID;
218 
219 /**
220  * The same as `#TN_INTSAVE_DATA` but for using in ISR together with
221  * `TN_INT_IDIS_SAVE()`, `TN_INT_IRESTORE()`.
222  *
223  * @see `TN_INT_IDIS_SAVE()`
224  * @see `TN_INT_IRESTORE()`
225  */
226 #define TN_INTSAVE_DATA_INT TN_INTSAVE_DATA
227 
228 /**
229  * \def TN_INT_DIS_SAVE()
230  *
231  * Disable interrupts and return previous value of status register,
232  * atomically. Similar `tn_arch_sr_save_int_dis()`, but implemented
233  * as a macro, so it is potentially faster.
234  *
235  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
236  *
237  * @see `#TN_INTSAVE_DATA`
238  * @see `tn_arch_sr_save_int_dis()`
239  */
240 
241 /**
242  * \def TN_INT_RESTORE()
243  *
244  * Restore previously saved status register.
245  * Similar to `tn_arch_sr_restore()`, but implemented as a macro,
246  * so it is potentially faster.
247  *
248  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
249  *
250  * @see `#TN_INTSAVE_DATA`
251  * @see `tn_arch_sr_save_int_dis()`
252  */
253 
254 # define TN_INT_DIS_SAVE() TN_INTSAVE_VAR = tn_arch_sr_save_int_dis()
255 # define TN_INT_RESTORE() _TN_PIC24_INTSAVE_CHECK(); \
256  tn_arch_sr_restore(TN_INTSAVE_VAR)
257 
258 /**
259  * The same as `TN_INT_DIS_SAVE()` but for using in ISR.
260  *
261  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
262  *
263  * @see `#TN_INTSAVE_DATA_INT`
264  */
265 #define TN_INT_IDIS_SAVE() TN_INT_DIS_SAVE()
266 
267 /**
268  * The same as `TN_INT_RESTORE()` but for using in ISR.
269  *
270  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
271  *
272  * @see `#TN_INTSAVE_DATA_INT`
273  */
274 #define TN_INT_IRESTORE() TN_INT_RESTORE()
275 
276 /**
277  * Returns nonzero if interrupts are disabled, zero otherwise.
278  */
279 #define TN_IS_INT_DISABLED() (_tn_arch_is_int_disabled())
280 
281 /**
282  * Pend context switch from interrupt.
283  */
284 #define _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED() \
285  _tn_context_switch_pend_if_needed()
286 
287 /**
288  * Converts size in bytes to size in `#TN_UWord`.
289  * For 32-bit platforms, we should shift it by 2 bit to the right;
290  * for 16-bit platforms, we should shift it by 1 bit to the right.
291  */
292 #define _TN_SIZE_BYTES_TO_UWORDS(size_in_bytes) ((size_in_bytes) >> 1)
293 
294 #if TN_FORCED_INLINE
295 # define _TN_INLINE inline __attribute__ ((always_inline))
296 #else
297 # define _TN_INLINE inline
298 #endif
299 
300 #define _TN_STATIC_INLINE static _TN_INLINE
301 
302 #define _TN_VOLATILE_WORKAROUND /* nothing */
303 
304 //-- internal interrupt macro stuff {{{
305 
306 #if TN_CHECK_PARAM
307 
308 /**
309  * Check whether priority is too high. On PIC24 port, we have a range of
310  * interrupt priorities (let's call this range as "system priority"), and
311  * kernel functions are allowed to call only from ISRs with priority in this
312  * range.
313  *
314  * As a result, the kernel (almost) never disables ALL interrupts: when it
315  * modifies critical data, it disables just interrupts with system priority.
316  *
317  * The "almost" is because it disables interrupts for 4-8 cycles when it modifies
318  * stack pointer and SPLIM, because they should always correspond.
319  */
320 # define _TN_SOFT_ISR_PRIORITY_CHECK() \
321  " \n" \
322  " mov #0xE0, W0 \n" \
323  " and _SR, WREG \n" \
324  " lsr W0, #5, W0 \n" \
325  " cp W0, #" _TN_STRINGIFY_MACRO(TN_P24_SYS_IPL) " \n" \
326  " bra leu, 1f \n" \
327  \
328  /* Interrupt priority is too high. Halt the debugger here. */ \
329  " .pword 0xDA4000 \n" \
330  " nop \n" \
331  "1: \n" \
332  /* Interrupt priority is ok, go on now. */ \
333 
334 #else
335 # define _TN_SOFT_ISR_PRIORITY_CHECK() /* nothing */
336 #endif
337 
338 
339 #define _TN_SOFT_ISR_PROLOGUE \
340  " \n" \
341  \
342  /* we need to use a couple of registers, so, save them. */ \
343  " push w0; \n" \
344  " push w1; \n" \
345  \
346  /* if TN_CHECK_PARAM is enabled, check if interrupt priority is */ \
347  /* too high. See macro _TN_SOFT_ISR_PRIORITY_CHECK() above for */ \
348  /* details. */ \
349  _TN_SOFT_ISR_PRIORITY_CHECK() \
350  \
351  /* before playing with SP and SPLIM, we need to disable system */ \
352  /* interrupts. NOTE that 'disi' instruction does not disable */ \
353  /* interrupts with priority 7, so, system interrupt priority should */ \
354  /* never be 7. */ \
355  \
356  " disi #8; \n" \
357  \
358  /* check if SP is already inside the interrupt stack: */ \
359  /* we check it by checking if SPLIM is set to _tn_p24_int_splim */ \
360  " mov __tn_p24_int_splim, w0; \n" \
361  " cp SPLIM; \n" \
362  " bra z, 1f; \n" \
363  \
364  /* SP is not inside the interrupt stack. We should set it. */ \
365  " \n" \
366  " mov SPLIM, w1; \n" \
367  " mov w0, SPLIM; \n" \
368  " mov w15, w0; \n" \
369  " mov __tn_p24_int_stack_low_addr, w15; \n" \
370  \
371  /* Interrupts should be re-enabled here. */ \
372  /* We just switched to interrupt stack. */ \
373  /* we need to push previous stack pointer and SPLIM there. */ \
374  " push w0; \n" /* push task's SP */ \
375  " push w1; \n" /* push task's SPLIM */ \
376  " bra 2f; \n" \
377  "1: \n" \
378  /* Interrupt stack is already active (it happens when interrupts */ \
379  /* nest) */ \
380  /* Just push SP and SPLIM so that stack contents will be compatible */ \
381  /* with the case of non-nested interrupt */ \
382  " push w15; \n" /* push SP */ \
383  " push SPLIM; \n" /* push SPLIM */ \
384  "2: \n" \
385  " \n" \
386 
387 
388 #define _TN_SOFT_ISR_CALL \
389  /* before we call user-provided ISR, we need to imitate interrupt */ \
390  /* call, i.e. store SR to the stack. It is done below, */ \
391  /* at the label 1: */ \
392  " rcall 1f; \n"
393 
394 #define _TN_SOFT_ISR_EPILOGUE \
395  /* we got here when we just returned from user-provided ISR. */ \
396  /* now, we need to restore previous SPLIM and SP, and we should */ \
397  /* disable interrupts because they could nest, and if SPLIM and SP */ \
398  /* don't correspond, system crashes. */ \
399  " disi #4; \n" \
400  " pop w1; \n" /* pop SPLIM */ \
401  " pop w0; \n" /* pop SP */ \
402  " mov w1, SPLIM; \n" \
403  " mov w0, w15; \n" \
404  \
405  /* now, interrupts should be enabled back. */ \
406  /* here we just need to restore w0 and w1 that we saved and used in */ \
407  /* _TN_SOFT_ISR_PROLOGUE */ \
408  " pop w1; \n" \
409  " pop w0; \n" \
410  \
411  /* finally, return from the ISR. */ \
412  " retfie; \n" \
413  "1: \n" \
414  /* we got here by rcall when we are about to call user-provided ISR. */ \
415  /* 'rcall' saves program counter to the stack, but we need to */ \
416  /* imitate interrupt, so, we manually save SR there. */ \
417  " mov SR, w0; \n" \
418  " mov.b w0, [w15-1]; \n" \
419  \
420  /* now, we eventually proceed to user-provided ISR. */ \
421  /* When it returns, we get above to the macro _TN_SOFT_ISR_EPILOGUE */ \
422  /* (because 'rcall' saved this address to the stack) */ \
423 
424 
425 
426 #define _tn_soft_isr_internal(_func, _psv, _shadow) \
427  void __attribute__(( \
428  __interrupt__( \
429  __preprologue__( \
430  _TN_SOFT_ISR_PROLOGUE \
431  _TN_SOFT_ISR_CALL \
432  _TN_SOFT_ISR_EPILOGUE \
433  ) \
434  ), \
435  _psv, \
436  _shadow \
437  )) \
438  _func(void)
439 
440 
441 // }}}
442 
443 #define _TN_ARCH_STACK_PT_TYPE _TN_ARCH_STACK_PT_TYPE__EMPTY
444 #define _TN_ARCH_STACK_DIR _TN_ARCH_STACK_DIR__ASC
445 
446 #endif //-- DOXYGEN_SHOULD_SKIP_THIS
447 
448 
449 
450 
451 
452 
453 
454 
455 
456 
457 /**
458  * ISR wrapper macro for software context saving.
459  *
460  * Usage looks like the following:
461  *
462  * \code{.c}
463  * tn_p24_soft_isr(_T1Interrupt, auto_psv)
464  * {
465  * //-- clear interrupt flag
466  * IFS0bits.T1IF = 0;
467  *
468  * //-- do something useful
469  * }
470  * \endcode
471  *
472  * Which should be used for system interrupts, instead of standard way:
473  *
474  * \code{.c}
475  * void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void)
476  * \endcode
477  *
478  * Where `_T1Interrupt` is the usual PIC24/dsPIC ISR name,
479  * and `auto_psv` (or `no_auto_psv`) is the usual attribute argument for
480  * interrupt.
481  *
482  *
483  */
484 #define tn_p24_soft_isr(_func, _psv) _tn_soft_isr_internal(_func, _psv, )
485 
486 
487 
488 
489 #ifdef __cplusplus
490 } /* extern "C" */
491 #endif
492 
493 #endif // _TN_ARCH_PIC24_H
494 
unsigned int TN_UIntPtr
Unsigned integer type that is able to store pointers.
Atomic bit-field access macros for PIC24/dsPIC.
unsigned int TN_UWord
Unsigned integer type whose size is equal to the size of CPU register.