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