CPU_SAVED_REGS_CNT: Declare for x86/x86_64.
[bertos.git] / cfg / cpu.h
1 /*!
2  * \file
3  * <!--
4  * Copyright 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2004 Giovanni Bajo
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \brief CPU-specific definitions
10  *
11  * \version $Id$
12  *
13  * \author Giovanni Bajo <rasky@develer.com>
14  * \author Bernardo Innocenti <bernie@develer.com>
15  * \author Stefano Fedrigo <aleph@develer.com>
16  */
17
18 /*#*
19  *#* $Log$
20  *#* Revision 1.10  2006/02/24 01:17:30  bernie
21  *#* CPU_SAVED_REGS_CNT: Declare for x86/x86_64.
22  *#*
23  *#* Revision 1.9  2006/02/23 09:08:43  bernie
24  *#* Add note for a frequently reported non-bug.
25  *#*
26  *#* Revision 1.8  2006/02/10 12:37:45  bernie
27  *#* Add support for ARM on IAR.
28  *#*
29  *#* Revision 1.7  2005/11/27 03:04:38  bernie
30  *#* Add POSIX emulation for IRQ_* macros; Add Qt support.
31  *#*
32  *#* Revision 1.6  2005/07/19 07:26:49  bernie
33  *#* Add missing #endif.
34  *#*
35  *#* Revision 1.5  2005/06/27 21:24:17  bernie
36  *#* CPU_CSOURCE(): New macro.
37  *#*
38  *#* Revision 1.4  2005/06/14 06:15:10  bernie
39  *#* Add X86_64 support.
40  *#*
41  *#* Revision 1.3  2005/04/12 04:06:17  bernie
42  *#* Catch missing CPU earlier.
43  *#*
44  *#* Revision 1.2  2005/04/11 19:10:27  bernie
45  *#* Include top-level headers from cfg/ subdir.
46  *#*
47  *#* Revision 1.1  2005/04/11 19:04:13  bernie
48  *#* Move top-level headers to cfg/ subdir.
49  *#*
50  *#* Revision 1.30  2005/03/15 00:20:09  bernie
51  *#* BREAKPOINT, IRQ_RUNNING(), IRQ_GETSTATE(): New DSP56K macros.
52  *#*
53  *#* Revision 1.29  2005/02/16 20:33:24  bernie
54  *#* Preliminary PPC support.
55  *#*
56  *#* Revision 1.28  2004/12/31 17:39:41  bernie
57  *#* Fix documentation.
58  *#*
59  *#* Revision 1.27  2004/12/31 17:02:47  bernie
60  *#* IRQ_SAVE_DISABLE(), IRQ_RESTORE(): Add null stubs for x86.
61  *#*
62  *#* Revision 1.26  2004/12/13 12:08:12  bernie
63  *#* DISABLE_IRQSAVE, ENABLE_IRQRESTORE, DISABLE_INTS, ENABLE_INTS: Remove obsolete macros.
64  *#*
65  *#* Revision 1.25  2004/12/08 08:31:02  bernie
66  *#* CPU_HARVARD: Define to 1 for AVR and DSP56K.
67  *#*/
68 #ifndef DEVLIB_CPU_H
69 #define DEVLIB_CPU_H
70
71 #include <cfg/compiler.h> /* for uintXX_t */
72
73
74 /*!
75  * \name Macros for determining CPU endianness.
76  * \{
77  */
78 #define CPU_BIG_ENDIAN    0x1234
79 #define CPU_LITTLE_ENDIAN 0x3412 /* Look twice, pal. This is not a bug. */
80 /*\}*/
81
82 /*! Macro to include cpu-specific versions of the headers. */
83 #define CPU_HEADER(module)          PP_STRINGIZE(PP_CAT3(module, _, CPU_ID).h)
84
85 /*! Macro to include cpu-specific versions of implementation files. */
86 #define CPU_CSOURCE(module)         PP_STRINGIZE(PP_CAT3(module, _, CPU_ID).c)
87
88
89 #if CPU_I196
90
91         #define NOP                     nop_instruction()
92         #define IRQ_DISABLE             disable_interrupt()
93         #define IRQ_ENABLE              enable_interrupt()
94
95         typedef uint16_t cpuflags_t; // FIXME
96         typedef unsigned int cpustack_t;
97
98         #define CPU_REG_BITS            16
99         #define CPU_REGS_CNT            16
100         #define CPU_STACK_GROWS_UPWARD  0
101         #define CPU_SP_ON_EMPTY_SLOT    0
102         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
103         #define CPU_HARVARD             0
104
105 #elif CPU_X86
106
107         #define NOP                     asm volatile ("nop")
108
109         /* Get IRQ_* definitions from the hosting environment. */
110         #include <cfg/os.h>
111         #if OS_EMBEDDED
112                 #define IRQ_DISABLE             FIXME
113                 #define IRQ_ENABLE              FIXME
114                 #define IRQ_SAVE_DISABLE(x)     FIXME
115                 #define IRQ_RESTORE(x)          FIXME
116                 typedef uint32_t cpuflags_t; // FIXME
117         #endif /* OS_EMBEDDED */
118
119
120         #define CPU_REGS_CNT            7
121         #define CPU_SAVED_REGS_CNT      7
122         #define CPU_STACK_GROWS_UPWARD  0
123         #define CPU_SP_ON_EMPTY_SLOT    0
124         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
125         #define CPU_HARVARD             0
126
127         #if CPU_X86_64
128                 typedef uint64_t cpustack_t;
129                 #define CPU_REG_BITS    64
130
131                 #ifdef __WIN64__
132                         /* WIN64 is an IL32-P64 weirdo. */
133                         #define SIZEOF_LONG  4
134                 #endif
135         #else
136                 typedef uint32_t cpustack_t;
137                 #define CPU_REG_BITS    32
138         #endif
139
140 #elif CPU_ARM
141
142         #ifdef __IAR_SYSTEMS_ICC__
143
144                 #include <inarm.h>
145
146                 #define NOP         __no_operation()
147                 #define IRQ_DISABLE __disable_interrupt()
148                 #define IRQ_ENABLE  __enable_interrupt()
149
150                 #define IRQ_SAVE_DISABLE(x) \
151                 do { \
152                         (x) = __get_CPSR(); \
153                         __disable_interrupt(); \
154                 } while (0)
155
156                 #define IRQ_RESTORE(x) \
157                 do { \
158                         __set_CPSR(x); \
159                 } while (0)
160
161                 #define IRQ_GETSTATE() \
162                         ((bool)(__get_CPSR() & 0xb0))
163
164         #else /* __IAR_SYSTEMS_ICC__ */
165
166                 #warning "IRQ_ macros need testing!"
167
168                 #define NOP         asm volatile ("mov r0,r0" ::)
169
170                 #define IRQ_DISABLE \
171                 do { \
172                         asm volatile ( \
173                                 "mrs r0, cpsr\n\t" \
174                                 "orr r0, r0, #0xb0\n\t" \
175                                 "msr cpsr, r0" \
176                                 :: \
177                         ); \
178                 } while (0)
179
180                 #define IRQ_ENABLE \
181                 do { \
182                         asm volatile ( \
183                                 "mrs r0, cpsr\n\t" \
184                                 "bic r0, r0, #0xb0\n\t" \
185                                 "msr cpsr, r0" \
186                                 :: \
187                         ); \
188                 } while (0)
189
190                 #define IRQ_SAVE_DISABLE(x) \
191                 do { \
192                         asm volatile ( \
193                                 "mrs r0, cpsr\n\t" \
194                                 "mov %0, r0\n\t" \
195                                 "orr r0, r0, #0xb0\n\t" \
196                                 "msr cpsr, r0" \
197                                 : "=r" (x) \
198                                 : /* no inputs */ \
199                                 : "r0" \
200                         ); \
201                 } while (0)
202
203                 #define IRQ_RESTORE(x) \
204                 do { \
205                         asm volatile ( \
206                                 "mov r0, %0\n\t" \
207                                 "msr cpsr, r0" \
208                                 : /* no outputs */ \
209                                 : "r" (x) \
210                                 : "r0" \
211                         ); \
212                 } while (0)
213
214                 #define IRQ_GETSTATE() \
215                 ({ \
216                         uint32_t sreg; \
217                         asm volatile ( \
218                                 "mrs r0, cpsr\n\t" \
219                                 "mov %0, r0" \
220                                 : "=r" (sreg)
221                                 : /* no inputs */
222                                 : "r0" \
223                         ); \
224                         (bool)(sreg & 0xb0); \
225                 })
226
227         #endif /* __IAR_SYSTEMS_ICC_ */
228
229         typedef uint32_t cpuflags_t;
230         typedef uint32_t cpustack_t;
231
232         /* Register counts include SREG too */
233         #define CPU_REG_BITS           32
234         #define CPU_REGS_CNT           16
235         #define CPU_SAVED_REGS_CNT     FIXME
236         #define CPU_STACK_GROWS_UPWARD 0  //FIXME
237         #define CPU_SP_ON_EMPTY_SLOT   0  //FIXME
238         #define CPU_BYTE_ORDER         (__BIG_ENDIAN__ ? CPU_BIG_ENDIAN : CPU_LITTLE_ENDIAN)
239         #define CPU_HARVARD            0
240
241 #elif CPU_PPC
242         #define NOP                 asm volatile ("nop" ::)
243
244         #define IRQ_DISABLE         FIXME
245         #define IRQ_ENABLE          FIXME
246         #define IRQ_SAVE_DISABLE(x) FIXME
247         #define IRQ_RESTORE(x)      FIXME
248         #define IRQ_GETSTATE()      FIXME
249
250         typedef uint32_t cpuflags_t; // FIXME
251         typedef uint32_t cpustack_t; // FIXME
252
253         /* Register counts include SREG too */
254         #define CPU_REG_BITS           (CPU_PPC32 ? 32 : 64)
255         #define CPU_REGS_CNT           FIXME
256         #define CPU_SAVED_REGS_CNT     FIXME
257         #define CPU_STACK_GROWS_UPWARD 0  //FIXME
258         #define CPU_SP_ON_EMPTY_SLOT   0  //FIXME
259         #define CPU_BYTE_ORDER         (__BIG_ENDIAN__ ? CPU_BIG_ENDIAN : CPU_LITTLE_ENDIAN)
260         #define CPU_HARVARD            0
261
262 #elif CPU_DSP56K
263
264         #define NOP                     asm(nop)
265         #define BREAKPOINT              asm(debug)
266         #define IRQ_DISABLE             do { asm(bfset #0x0200,SR); asm(nop); } while (0)
267         #define IRQ_ENABLE              do { asm(bfclr #0x0200,SR); asm(nop); } while (0)
268
269         #define IRQ_SAVE_DISABLE(x)  \
270                 do { (void)x; asm(move SR,x); asm(bfset #0x0200,SR); } while (0)
271         #define IRQ_RESTORE(x)  \
272                 do { (void)x; asm(move x,SR); } while (0)
273
274         static inline bool irq_running(void)
275         {
276                 extern void *user_sp;
277                 return !!user_sp;
278         }
279         #define IRQ_RUNNING() irq_running()
280
281         static inline bool irq_getstate(void)
282         {
283                 uint16_t x;
284                 asm(move SR,x);
285                 return !(x & 0x0200);
286         }
287         #define IRQ_GETSTATE() irq_getstate()
288
289         typedef uint16_t cpuflags_t;
290         typedef unsigned int cpustack_t;
291
292         #define CPU_REG_BITS            16
293         #define CPU_REGS_CNT            FIXME
294         #define CPU_SAVED_REGS_CNT      8
295         #define CPU_STACK_GROWS_UPWARD  1
296         #define CPU_SP_ON_EMPTY_SLOT    0
297         #define CPU_BYTE_ORDER          CPU_BIG_ENDIAN
298         #define CPU_HARVARD             1
299
300         /* Memory is word-addessed in the DSP56K */
301         #define CPU_BITS_PER_CHAR  16
302         #define SIZEOF_SHORT        1
303         #define SIZEOF_INT          1
304         #define SIZEOF_LONG         2
305         #define SIZEOF_PTR          1
306
307 #elif CPU_AVR
308
309         #define NOP           asm volatile ("nop" ::)
310         #define IRQ_DISABLE   asm volatile ("cli" ::)
311         #define IRQ_ENABLE    asm volatile ("sei" ::)
312
313         #define IRQ_SAVE_DISABLE(x) \
314         do { \
315                 __asm__ __volatile__( \
316                         "in %0,__SREG__\n\t" \
317                         "cli" \
318                         : "=r" (x) : /* no inputs */ : "cc" \
319                 ); \
320         } while (0)
321
322         #define IRQ_RESTORE(x) \
323         do { \
324                 __asm__ __volatile__( \
325                         "out __SREG__,%0" : /* no outputs */ : "r" (x) : "cc" \
326                 ); \
327         } while (0)
328
329         #define IRQ_GETSTATE() \
330         ({ \
331                 uint8_t sreg; \
332                 __asm__ __volatile__( \
333                         "in %0,__SREG__\n\t" \
334                         : "=r" (sreg)  /* no inputs & no clobbers */ \
335                 ); \
336                 (bool)(sreg & 0x80); \
337         })
338
339         typedef uint8_t cpuflags_t;
340         typedef uint8_t cpustack_t;
341
342         /* Register counts include SREG too */
343         #define CPU_REG_BITS            8
344         #define CPU_REGS_CNT           33
345         #define CPU_SAVED_REGS_CNT     19
346         #define CPU_STACK_GROWS_UPWARD  0
347         #define CPU_SP_ON_EMPTY_SLOT    1
348         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
349         #define CPU_HARVARD             1
350
351         /*!
352          * Initialization value for registers in stack frame.
353          * The register index is not directly corrispondent to CPU
354          * register numbers. Index 0 is the SREG register: the initial
355          * value is all 0 but the interrupt bit (bit 7).
356          */
357         #define CPU_REG_INIT_VALUE(reg) (reg == 0 ? 0x80 : 0)
358
359 #else
360         #error No CPU_... defined.
361 #endif
362
363 /*!
364  * Execute \a CODE atomically with respect to interrupts.
365  *
366  * \see IRQ_SAVE_DISABLE IRQ_RESTORE
367  */
368 #define ATOMIC(CODE) \
369         do { \
370                 cpuflags_t __flags; \
371                 IRQ_SAVE_DISABLE(__flags); \
372                 CODE; \
373                 IRQ_RESTORE(__flags); \
374         } while (0)
375
376
377 //! Default for macro not defined in the right arch section
378 #ifndef CPU_REG_INIT_VALUE
379         #define CPU_REG_INIT_VALUE(reg)     0
380 #endif
381
382
383 #ifndef CPU_STACK_GROWS_UPWARD
384         #error CPU_STACK_GROWS_UPWARD should have been defined to either 0 or 1
385 #endif
386
387 #ifndef CPU_SP_ON_EMPTY_SLOT
388         #error CPU_SP_ON_EMPTY_SLOT should have been defined to either 0 or 1
389 #endif
390
391 /*
392  * Support stack handling peculiarities of a few CPUs.
393  *
394  * Most processors let their stack grow downward and
395  * keep SP pointing at the last pushed value.
396  */
397 #if !CPU_STACK_GROWS_UPWARD
398         #if !CPU_SP_ON_EMPTY_SLOT
399                 /* Most microprocessors (x86, m68k...) */
400                 #define CPU_PUSH_WORD(sp, data) \
401                         do { *--(sp) = (data); } while (0)
402                 #define CPU_POP_WORD(sp) \
403                         (*(sp)++)
404         #else
405                 /* AVR insanity */
406                 #define CPU_PUSH_WORD(sp, data) \
407                         do { *(sp)-- = (data); } while (0)
408                 #define CPU_POP_WORD(sp) \
409                         (*++(sp))
410         #endif
411
412 #else /* CPU_STACK_GROWS_UPWARD */
413
414         #if !CPU_SP_ON_EMPTY_SLOT
415                 /* DSP56K and other weirdos */
416                 #define CPU_PUSH_WORD(sp, data) \
417                         do { *++(sp) = (cpustack_t)(data); } while (0)
418                 #define CPU_POP_WORD(sp) \
419                         (*(sp)--)
420         #else
421                 #error I bet you cannot find a CPU like this
422         #endif
423 #endif
424
425
426 #if CPU_DSP56K
427         /*
428          * DSP56k pushes both PC and SR to the stack in the JSR instruction, but
429          * RTS discards SR while returning (it does not restore it). So we push
430          * 0 to fake the same context.
431          */
432         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
433                 do { \
434                         CPU_PUSH_WORD((sp), (func)); \
435                         CPU_PUSH_WORD((sp), 0x100); \
436                 } while (0);
437
438 #elif CPU_AVR
439         /*
440          * In AVR, the addresses are pushed into the stack as little-endian, while
441          * memory accesses are big-endian (actually, it's a 8-bit CPU, so there is
442          * no natural endianess).
443          */
444         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
445                 do { \
446                         uint16_t funcaddr = (uint16_t)(func); \
447                         CPU_PUSH_WORD((sp), funcaddr); \
448                         CPU_PUSH_WORD((sp), funcaddr>>8); \
449                 } while (0)
450
451 #else
452         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
453                 CPU_PUSH_WORD((sp), (cpustack_t)(func))
454 #endif
455
456
457 /*!
458  * \name Default type sizes.
459  *
460  * These defaults are reasonable for most 16/32bit machines.
461  * Some of these macros may be overridden by CPU-specific code above.
462  *
463  * ANSI C requires that the following equations be true:
464  * \code
465  *   sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
466  *   sizeof(float) <= sizeof(double)
467  *   CPU_BITS_PER_CHAR  >= 8
468  *   CPU_BITS_PER_SHORT >= 8
469  *   CPU_BITS_PER_INT   >= 16
470  *   CPU_BITS_PER_LONG  >= 32
471  * \endcode
472  * \{
473  */
474 #ifndef SIZEOF_CHAR
475 #define SIZEOF_CHAR  1
476 #endif
477
478 #ifndef SIZEOF_SHORT
479 #define SIZEOF_SHORT  2
480 #endif
481
482 #ifndef SIZEOF_INT
483 #if CPU_REG_BITS < 32
484         #define SIZEOF_INT  2
485 #else
486         #define SIZEOF_INT  4
487 #endif
488 #endif /* !SIZEOF_INT */
489
490 #ifndef SIZEOF_LONG
491 #if CPU_REG_BITS > 32
492         #define SIZEOF_LONG  8
493 #else
494         #define SIZEOF_LONG  4
495 #endif
496 #endif
497
498 #ifndef SIZEOF_PTR
499 #if CPU_REG_BITS < 32
500         #define SIZEOF_PTR   2
501 #elif CPU_REG_BITS == 32
502         #define SIZEOF_PTR   4
503 #else /* CPU_REG_BITS > 32 */
504         #define SIZEOF_PTR   8
505 #endif
506 #endif
507
508 #ifndef CPU_BITS_PER_CHAR
509 #define CPU_BITS_PER_CHAR   (SIZEOF_CHAR * 8)
510 #endif
511
512 #ifndef CPU_BITS_PER_SHORT
513 #define CPU_BITS_PER_SHORT  (SIZEOF_SHORT * CPU_BITS_PER_CHAR)
514 #endif
515
516 #ifndef CPU_BITS_PER_INT
517 #define CPU_BITS_PER_INT    (SIZEOF_INT * CPU_BITS_PER_CHAR)
518 #endif
519
520 #ifndef CPU_BITS_PER_LONG
521 #define CPU_BITS_PER_LONG   (SIZEOF_LONG * CPU_BITS_PER_CHAR)
522 #endif
523
524 #ifndef CPU_BITS_PER_PTR
525 #define CPU_BITS_PER_PTR    (SIZEOF_PTR * CPU_BITS_PER_CHAR)
526 #endif
527
528 #ifndef BREAKPOINT
529 #define BREAKPOINT /* nop */
530 #endif
531
532 /*\}*/
533
534 /* Sanity checks for the above definitions */
535 STATIC_ASSERT(sizeof(char) == SIZEOF_CHAR);
536 STATIC_ASSERT(sizeof(short) == SIZEOF_SHORT);
537 STATIC_ASSERT(sizeof(long) == SIZEOF_LONG);
538 STATIC_ASSERT(sizeof(int) == SIZEOF_INT);
539 STATIC_ASSERT(sizeof(void *) == SIZEOF_PTR);
540 STATIC_ASSERT(sizeof(int8_t) * CPU_BITS_PER_CHAR == 8);
541 STATIC_ASSERT(sizeof(uint8_t) * CPU_BITS_PER_CHAR == 8);
542 STATIC_ASSERT(sizeof(int16_t) * CPU_BITS_PER_CHAR == 16);
543 STATIC_ASSERT(sizeof(uint16_t) * CPU_BITS_PER_CHAR == 16);
544 STATIC_ASSERT(sizeof(int32_t) * CPU_BITS_PER_CHAR == 32);
545 STATIC_ASSERT(sizeof(uint32_t) * CPU_BITS_PER_CHAR == 32);
546 #ifdef __HAS_INT64_T__
547 STATIC_ASSERT(sizeof(int64_t) * CPU_BITS_PER_CHAR == 64);
548 STATIC_ASSERT(sizeof(uint64_t) * CPU_BITS_PER_CHAR == 64);
549 #endif
550
551 /*!
552  * \def CPU_IDLE
553  *
554  * \brief Invoked by the scheduler to stop the CPU when idle.
555  *
556  * This hook can be redefined to put the CPU in low-power mode, or to
557  * profile system load with an external strobe, or to save CPU cycles
558  * in hosted environments such as emulators.
559  */
560 #ifndef CPU_IDLE
561         #if defined(ARCH_EMUL) && (ARCH & ARCH_EMUL)
562                 /* This emulator hook should yield the CPU to the host.  */
563                 EXTERN_C_BEGIN
564                 void SchedulerIdle(void);
565                 EXTERN_C_END
566                 #define CPU_IDLE SchedulerIdle()
567         #else /* !ARCH_EMUL */
568                 #define CPU_IDLE do { /* nothing */ } while (0)
569         #endif /* !ARCH_EMUL */
570 #endif /* !CPU_IDLE */
571
572 /* OBSOLETE */
573 #define SCHEDULER_IDLE CPU_IDLE
574
575 #endif /* DEVLIB_CPU_H */