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