TNeo  v1.07
 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 
49 /*******************************************************************************
50  * INCLUDED FILES
51  ******************************************************************************/
52 
53 //-- Include `tn_common_macros.h` for `_TN_STRINGIZE_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_save_status_reg == _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 /**
200  * Declares variable that is used by macros `TN_INT_DIS_SAVE()` and
201  * `TN_INT_RESTORE()` for storing status register value.
202  *
203  * It is good idea to initially set it to some invalid value,
204  * and if TN_DEBUG is non-zero, check it in TN_INT_RESTORE().
205  * Then, we can catch bugs if someone tries to restore interrupts status
206  * without saving it first.
207  *
208  * @see `TN_INT_DIS_SAVE()`
209  * @see `TN_INT_RESTORE()`
210  */
211 #define TN_INTSAVE_DATA \
212  int tn_save_status_reg = _TN_PIC24_INTSAVE_DATA_INVALID;
213 
214 /**
215  * The same as `#TN_INTSAVE_DATA` but for using in ISR together with
216  * `TN_INT_IDIS_SAVE()`, `TN_INT_IRESTORE()`.
217  *
218  * @see `TN_INT_IDIS_SAVE()`
219  * @see `TN_INT_IRESTORE()`
220  */
221 #define TN_INTSAVE_DATA_INT TN_INTSAVE_DATA
222 
223 /**
224  * \def TN_INT_DIS_SAVE()
225  *
226  * Disable interrupts and return previous value of status register,
227  * atomically. Similar `tn_arch_sr_save_int_dis()`, but implemented
228  * as a macro, so it is potentially faster.
229  *
230  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
231  *
232  * @see `#TN_INTSAVE_DATA`
233  * @see `tn_arch_sr_save_int_dis()`
234  */
235 
236 /**
237  * \def TN_INT_RESTORE()
238  *
239  * Restore previously saved status register.
240  * Similar to `tn_arch_sr_restore()`, but implemented as a macro,
241  * so it is potentially faster.
242  *
243  * Uses `#TN_INTSAVE_DATA` as a temporary storage.
244  *
245  * @see `#TN_INTSAVE_DATA`
246  * @see `tn_arch_sr_save_int_dis()`
247  */
248 
249 # define TN_INT_DIS_SAVE() tn_save_status_reg = tn_arch_sr_save_int_dis()
250 # define TN_INT_RESTORE() _TN_PIC24_INTSAVE_CHECK(); \
251  tn_arch_sr_restore(tn_save_status_reg)
252 
253 /**
254  * The same as `TN_INT_DIS_SAVE()` but for using in ISR.
255  *
256  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
257  *
258  * @see `#TN_INTSAVE_DATA_INT`
259  */
260 #define TN_INT_IDIS_SAVE() TN_INT_DIS_SAVE()
261 
262 /**
263  * The same as `TN_INT_RESTORE()` but for using in ISR.
264  *
265  * Uses `#TN_INTSAVE_DATA_INT` as a temporary storage.
266  *
267  * @see `#TN_INTSAVE_DATA_INT`
268  */
269 #define TN_INT_IRESTORE() TN_INT_RESTORE()
270 
271 /**
272  * Returns nonzero if interrupts are disabled, zero otherwise.
273  */
274 #define TN_IS_INT_DISABLED() (_tn_arch_is_int_disabled())
275 
276 /**
277  * Pend context switch from interrupt.
278  */
279 #define _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED() \
280  _tn_context_switch_pend_if_needed()
281 
282 /**
283  * Converts size in bytes to size in `#TN_UWord`.
284  * For 32-bit platforms, we should shift it by 2 bit to the right;
285  * for 16-bit platforms, we should shift it by 1 bit to the right.
286  */
287 #define _TN_SIZE_BYTES_TO_UWORDS(size_in_bytes) ((size_in_bytes) >> 1)
288 
289 #define _TN_INLINE inline
290 
291 #define _TN_VOLATILE_WORKAROUND /* nothing */
292 
293 //-- internal interrupt macro stuff {{{
294 
295 #if TN_CHECK_PARAM
296 
297 /**
298  * Check whether priority is too high. On PIC24 port, we have a range of
299  * interrupt priorities (let's call this range as "system priority"), and
300  * kernel functions are allowed to call only from ISRs with priority in this
301  * range.
302  *
303  * As a result, the kernel (almost) never disables ALL interrupts: when it
304  * modifies critical data, it disables just interrupts with system priority.
305  *
306  * The "almost" is because it disables interrupts for 4-8 cycles when it modifies
307  * stack pointer and SPLIM, because they should always correspond.
308  */
309 # define _TN_SOFT_ISR_PRIORITY_CHECK() \
310  " \n" \
311  " mov #0xE0, W0 \n" \
312  " and _SR, WREG \n" \
313  " lsr W0, #5, W0 \n" \
314  " cp W0, #" _TN_STRINGIZE_MACRO(TN_P24_SYS_IPL) " \n" \
315  " bra leu, 1f \n" \
316  \
317  /* Interrupt priority is too high. Halt the debugger here. */ \
318  " .pword 0xDA4000 \n" \
319  " nop \n" \
320  "1: \n" \
321  /* Interrupt priority is ok, go on now. */ \
322 
323 #else
324 # define _TN_SOFT_ISR_PRIORITY_CHECK() /* nothing */
325 #endif
326 
327 
328 #define _TN_SOFT_ISR_PROLOGUE \
329  " \n" \
330  \
331  /* we need to use a couple of registers, so, save them. */ \
332  " push w0; \n" \
333  " push w1; \n" \
334  \
335  /* if TN_CHECK_PARAM is enabled, check if interrupt priority is */ \
336  /* too high. See macro _TN_SOFT_ISR_PRIORITY_CHECK() above for */ \
337  /* details. */ \
338  _TN_SOFT_ISR_PRIORITY_CHECK() \
339  \
340  /* before playing with SP and SPLIM, we need to disable system */ \
341  /* interrupts. NOTE that 'disi' instruction does not disable */ \
342  /* interrupts with priority 7, so, system interrupt priority should */ \
343  /* never be 7. */ \
344  \
345  " disi #8; \n" \
346  \
347  /* check if SP is already inside the interrupt stack: */ \
348  /* we check it by checking if SPLIM is set to _tn_p24_int_splim */ \
349  " mov __tn_p24_int_splim, w0; \n" \
350  " cp SPLIM; \n" \
351  " bra z, 1f; \n" \
352  \
353  /* SP is not inside the interrupt stack. We should set it. */ \
354  " \n" \
355  " mov SPLIM, w1; \n" \
356  " mov w0, SPLIM; \n" \
357  " mov w15, w0; \n" \
358  " mov __tn_p24_int_stack_low_addr, w15; \n" \
359  \
360  /* Interrupts should be re-enabled here. */ \
361  /* We just switched to interrupt stack. */ \
362  /* we need to push previous stack pointer and SPLIM there. */ \
363  " push w0; \n" /* push task's SP */ \
364  " push w1; \n" /* push task's SPLIM */ \
365  " bra 2f; \n" \
366  "1: \n" \
367  /* Interrupt stack is already active (it happens when interrupts */ \
368  /* nest) */ \
369  /* Just push SP and SPLIM so that stack contents will be compatible */ \
370  /* with the case of non-nested interrupt */ \
371  " push w15; \n" /* push SP */ \
372  " push SPLIM; \n" /* push SPLIM */ \
373  "2: \n" \
374  " \n" \
375 
376 
377 #define _TN_SOFT_ISR_CALL \
378  /* before we call user-provided ISR, we need to imitate interrupt */ \
379  /* call, i.e. store SR to the stack. It is done below, */ \
380  /* at the label 1: */ \
381  " rcall 1f; \n"
382 
383 #define _TN_SOFT_ISR_EPILOGUE \
384  /* we got here when we just returned from user-provided ISR. */ \
385  /* now, we need to restore previous SPLIM and SP, and we should */ \
386  /* disable interrupts because they could nest, and if SPLIM and SP */ \
387  /* don't correspond, system crashes. */ \
388  " disi #4; \n" \
389  " pop w1; \n" /* pop SPLIM */ \
390  " pop w0; \n" /* pop SP */ \
391  " mov w1, SPLIM; \n" \
392  " mov w0, w15; \n" \
393  \
394  /* now, interrupts should be enabled back. */ \
395  /* here we just need to restore w0 and w1 that we saved and used in */ \
396  /* _TN_SOFT_ISR_PROLOGUE */ \
397  " pop w1; \n" \
398  " pop w0; \n" \
399  \
400  /* finally, return from the ISR. */ \
401  " retfie; \n" \
402  "1: \n" \
403  /* we got here by rcall when we are about to call user-provided ISR. */ \
404  /* 'rcall' saves program counter to the stack, but we need to */ \
405  /* imitate interrupt, so, we manually save SR there. */ \
406  " mov SR, w0; \n" \
407  " mov.b w0, [w15-1]; \n" \
408  \
409  /* now, we eventually proceed to user-provided ISR. */ \
410  /* When it returns, we get above to the macro _TN_SOFT_ISR_EPILOGUE */ \
411  /* (because 'rcall' saved this address to the stack) */ \
412 
413 
414 
415 #define _tn_soft_isr_internal(_func, _psv, _shadow) \
416  void __attribute__(( \
417  __interrupt__( \
418  __preprologue__( \
419  _TN_SOFT_ISR_PROLOGUE \
420  _TN_SOFT_ISR_CALL \
421  _TN_SOFT_ISR_EPILOGUE \
422  ) \
423  ), \
424  _psv, \
425  _shadow \
426  )) \
427  _func(void)
428 
429 
430 // }}}
431 
432 #define _TN_ARCH_STACK_PT_TYPE _TN_ARCH_STACK_PT_TYPE__EMPTY
433 #define _TN_ARCH_STACK_DIR _TN_ARCH_STACK_DIR__ASC
434 
435 #endif //-- DOXYGEN_SHOULD_SKIP_THIS
436 
437 
438 
439 
440 
441 
442 
443 
444 
445 
446 /**
447  * ISR wrapper macro for software context saving.
448  *
449  * Usage looks like the following:
450  *
451  * \code{.c}
452  * tn_p24_soft_isr(_T1Interrupt, auto_psv)
453  * {
454  * //-- clear interrupt flag
455  * IFS0bits.T1IF = 0;
456  *
457  * //-- do something useful
458  * }
459  * \endcode
460  *
461  * Which should be used for system interrupts, instead of standard way:
462  *
463  * \code{.c}
464  * void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void)
465  * \endcode
466  *
467  * Where `_T1Interrupt` is the usual PIC24/dsPIC ISR name,
468  * and `auto_psv` (or `no_auto_psv`) is the usual attribute argument for
469  * interrupt.
470  *
471  *
472  */
473 #define tn_p24_soft_isr(_func, _psv) _tn_soft_isr_internal(_func, _psv, )
474 
475 
476 
477 
478 #ifdef __cplusplus
479 } /* extern "C" */
480 #endif
481 
482 #endif // _TN_ARCH_PIC24_H
483 
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.