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