Fix off-by-one bug in [v]snprintf().
[bertos.git] / cpu.h
1 /*!
2  * \file
3  * <!--
4  * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2004 Giovanni Bajo
6  * This file is part of DevLib - See devlib/README 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.20  2004/11/16 20:33:32  bernie
21  *#* CPU_HARVARD: New macro.
22  *#*
23  *#* Revision 1.19  2004/10/03 20:43:54  bernie
24  *#* Fix Doxygen markup.
25  *#*
26  *#* Revision 1.18  2004/10/03 18:36:31  bernie
27  *#* IRQ_GETSTATE(): New macro; Rename IRQ macros for consistency.
28  *#*
29  *#* Revision 1.17  2004/09/06 21:48:27  bernie
30  *#* ATOMIC(): New macro.
31  *#*
32  *#* Revision 1.16  2004/08/29 21:58:33  bernie
33  *#* Rename BITS_PER_XYZ macros; Add sanity checks.
34  *#*
35  *#* Revision 1.15  2004/08/25 14:12:08  rasky
36  *#* Aggiornato il comment block dei log RCS
37  *#*
38  *#* Revision 1.14  2004/08/24 13:29:28  bernie
39  *#* Trim CVS log; Rename header guards.
40  *#*
41  *#* Revision 1.12  2004/08/14 19:37:57  rasky
42  *#* Merge da SC: macros.h, pool.h, BIT_CHANGE, nome dei processi, etc.
43  *#*
44  *#* Revision 1.11  2004/08/05 17:39:56  bernie
45  *#* Fix a Doxygen tag.
46  *#*
47  *#* Revision 1.10  2004/08/02 20:20:29  aleph
48  *#* Merge from project_ks
49  *#*
50  *#* Revision 1.9  2004/07/30 14:24:16  rasky
51  *#* Task switching con salvataggio perfetto stato di interrupt (SR)
52  *#* Kernel monitor per dump informazioni su stack dei processi
53  *#*/
54 #ifndef DEVLIB_CPU_H
55 #define DEVLIB_CPU_H
56
57 #include "compiler.h" /* for uintXX_t, PP_CAT3(), PP_STRINGIZE() */
58
59
60 // Macros for determining CPU endianness
61 #define CPU_BIG_ENDIAN    0x1234
62 #define CPU_LITTLE_ENDIAN 0x3412
63
64 // Macros to include cpu-specific version of the headers
65 #define CPU_HEADER(module)          PP_STRINGIZE(PP_CAT3(module, _, CPU_ID).h)
66
67
68 #if CPU_I196
69
70         #define DISABLE_INTS            disable_interrupt()
71         #define ENABLE_INTS             enable_interrupt()
72         #define NOP                     nop_instruction()
73
74         typedef uint16_t cpuflags_t; // FIXME
75         typedef unsigned int cpustack_t;
76
77         #define CPU_REG_BITS            16
78         #define CPU_REGS_CNT            16
79         #define CPU_STACK_GROWS_UPWARD  0
80         #define CPU_SP_ON_EMPTY_SLOT    0
81         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
82         #define CPU_HARVARD             0
83
84 #elif CPU_X86
85
86         #define NOP                     asm volatile ("nop")
87         #define DISABLE_INTS            /* nothing */
88         #define ENABLE_INTS             /* nothing */
89
90         typedef uint32_t cpuflags_t; // FIXME
91         typedef uint32_t cpustack_t;
92
93         #define CPU_REG_BITS            32
94         #define CPU_REGS_CNT            7
95         #define CPU_STACK_GROWS_UPWARD  0
96         #define CPU_SP_ON_EMPTY_SLOT    0
97         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
98         #define CPU_HARVARD             0
99
100 #elif CPU_DSP56K
101
102         #define NOP                     asm(nop)
103         #define DISABLE_INTS            do { asm(bfset #0x0200,SR); asm(nop); } while (0)
104         #define ENABLE_INTS             do { asm(bfclr #0x0200,SR); asm(nop); } while (0)
105
106         #define DISABLE_IRQSAVE(x)  \
107                 do { (void)x; asm(move SR,x); asm(bfset #0x0200,SR); } while (0)
108         #define ENABLE_IRQRESTORE(x)  \
109                 do { (void)x; asm(move x,SR); } while (0)
110
111         typedef uint16_t cpuflags_t;
112         typedef unsigned int cpustack_t;
113
114         #define CPU_REG_BITS            16
115         #define CPU_REGS_CNT            FIXME
116         #define CPU_SAVED_REGS_CNT      8
117         #define CPU_STACK_GROWS_UPWARD  1
118         #define CPU_SP_ON_EMPTY_SLOT    0
119         #define CPU_BYTE_ORDER          CPU_BIG_ENDIAN
120         #define CPU_HARVARD             1
121
122         /* Memory is word-addessed in the DSP56K */
123         #define CPU_BITS_PER_CHAR  16
124         #define SIZEOF_SHORT        1
125         #define SIZEOF_INT          1
126         #define SIZEOF_LONG         2
127         #define SIZEOF_PTR          1
128
129 #elif CPU_AVR
130
131         #define NOP           asm volatile ("nop" ::)
132         #define IRQ_DISABLE   asm volatile ("cli" ::)
133         #define IRQ_ENABLE    asm volatile ("sei" ::)
134
135         #define IRQ_SAVE_DISABLE(x) \
136         do { \
137                 __asm__ __volatile__( \
138                         "in %0,__SREG__\n\t" \
139                         "cli" \
140                         : "=r" (x) : /* no inputs */ : "cc" \
141                 ); \
142         } while (0)
143
144         #define IRQ_RESTORE(x) \
145         do { \
146                 __asm__ __volatile__( \
147                         "out __SREG__,%0" : /* no outputs */ : "r" (x) : "cc" \
148                 ); \
149         } while (0)
150
151         #define IRQ_GETSTATE() \
152         ({ \
153                 uint8_t sreg; \
154                 __asm__ __volatile__( \
155                         "in %0,__SREG__\n\t" \
156                         : "=r" (sreg)  /* no inputs & no clobbers */ \
157                 ); \
158                 (bool)(sreg & 0x80); \
159         })
160
161         /* OBSOLETE NAMES */
162         #define DISABLE_INTS IRQ_DISABLE
163         #define ENABLE_INTS  IRQ_ENABLE
164         #define DISABLE_IRQSAVE(x)   IRQ_SAVE_DISABLE(x)
165         #define ENABLE_IRQRESTORE(x) IRQ_RESTORE(x)
166
167         typedef uint8_t cpuflags_t;
168         typedef uint8_t cpustack_t;
169
170         /* Register counts include SREG too */
171         #define CPU_REG_BITS            8
172         #define CPU_REGS_CNT           33
173         #define CPU_SAVED_REGS_CNT     19
174         #define CPU_STACK_GROWS_UPWARD  0
175         #define CPU_SP_ON_EMPTY_SLOT    1
176         #define CPU_BYTE_ORDER          CPU_LITTLE_ENDIAN
177         #define CPU_HARVARD             1
178
179         /*!
180          * Initialization value for registers in stack frame.
181          * The register index is not directly corrispondent to CPU
182          * register numbers. Index 0 is the SREG register: the initial
183          * value is all 0 but the interrupt bit (bit 7).
184          */
185         #define CPU_REG_INIT_VALUE(reg) (reg == 0 ? 0x80 : 0)
186
187 #endif
188
189 /*!
190  * Execute \a CODE atomically with respect to interrupts.
191  *
192  * \see ENABLE_IRQSAVE DISABLE_IRQRESTORE
193  */
194 #define ATOMIC(CODE) \
195         do { \
196                 cpuflags_t __flags; \
197                 DISABLE_IRQSAVE(__flags); \
198                 CODE; \
199                 ENABLE_IRQRESTORE(__flags); \
200         } while (0)
201
202
203 //! Default for macro not defined in the right arch section
204 #ifndef CPU_REG_INIT_VALUE
205         #define CPU_REG_INIT_VALUE(reg)     0
206 #endif
207
208
209 #ifndef CPU_STACK_GROWS_UPWARD
210         #error CPU_STACK_GROWS_UPWARD should have been defined to either 0 or 1
211 #endif
212
213 #ifndef CPU_SP_ON_EMPTY_SLOT
214         #error CPU_SP_ON_EMPTY_SLOT should have been defined to either 0 or 1
215 #endif
216
217 /*
218  * Support stack handling peculiarities of a few CPUs.
219  *
220  * Most processors let their stack grow downward and
221  * keep SP pointing at the last pushed value.
222  */
223 #if !CPU_STACK_GROWS_UPWARD
224         #if !CPU_SP_ON_EMPTY_SLOT
225                 /* Most microprocessors (x86, m68k...) */
226                 #define CPU_PUSH_WORD(sp, data) \
227                         do { *--(sp) = (data); } while (0)
228                 #define CPU_POP_WORD(sp) \
229                         (*(sp)++)
230         #else
231                 /* AVR insanity */
232                 #define CPU_PUSH_WORD(sp, data) \
233                         do { *(sp)-- = (data); } while (0)
234                 #define CPU_POP_WORD(sp) \
235                         (*++(sp))
236         #endif
237
238 #else /* CPU_STACK_GROWS_UPWARD */
239
240         #if !CPU_SP_ON_EMPTY_SLOT
241                 /* DSP56K and other weirdos */
242                 #define CPU_PUSH_WORD(sp, data) \
243                         do { *++(sp) = (cpustack_t)(data); } while (0)
244                 #define CPU_POP_WORD(sp) \
245                         (*(sp)--)
246         #else
247                 #error I bet you cannot find a CPU like this
248         #endif
249 #endif
250
251
252 #if CPU_DSP56K
253         /* DSP56k pushes both PC and SR to the stack in the JSR instruction, but
254          * RTS discards SR while returning (it does not restore it). So we push
255          * 0 to fake the same context.
256          */
257         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
258                 do { \
259                         CPU_PUSH_WORD((sp), (func)); \
260                         CPU_PUSH_WORD((sp), 0x100); \
261                 } while (0);
262
263 #elif CPU_AVR
264         /* In AVR, the addresses are pushed into the stack as little-endian, while
265          * memory accesses are big-endian (actually, it's a 8-bit CPU, so there is
266          * no natural endianess).
267          */
268         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
269                 do { \
270                         uint16_t funcaddr = (uint16_t)(func); \
271                         CPU_PUSH_WORD((sp), funcaddr); \
272                         CPU_PUSH_WORD((sp), funcaddr>>8); \
273                 } while (0)
274
275 #else
276         #define CPU_PUSH_CALL_CONTEXT(sp, func) \
277                 CPU_PUSH_WORD((sp), (func))
278 #endif
279
280
281 /*!
282  * \name Default type sizes
283  *
284  * \def SIZEOF_CHAR SIZEOF_SHORT SIZEOF_INT SIZEOF_LONG SIZEOF_PTR
285  * \def CPU_BITS_PER_CHAR CPU_BITS_PER_SHORT CPU_BITS_PER_INT
286  * \def CPU_BITS_PER_LONG CPU_BITS_PER_PTR
287  *
288  * These defaults are reasonable for most 16/32bit machines.
289  * Some of these macros may be overridden by CPU-specific code above.
290  *
291  * ANSI C requires that the following equations be true:
292  * \code
293  *   sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
294  *   sizeof(float) <= sizeof(double)
295  *   CPU_BITS_PER_CHAR  >= 8
296  *   CPU_BITS_PER_SHORT >= 8
297  *   CPU_BITS_PER_INT   >= 16
298  *   CPU_BITS_PER_LONG  >= 32
299  * \endcode
300  * \{
301  */
302 #ifndef SIZEOF_CHAR
303 #define SIZEOF_CHAR  1
304 #endif
305
306 #ifndef SIZEOF_SHORT
307 #define SIZEOF_SHORT  2
308 #endif
309
310 #ifndef SIZEOF_INT
311 #if CPU_REG_BITS < 32
312         #define SIZEOF_INT  2
313 #else
314         #define SIZEOF_INT  4
315 #endif
316 #endif /* !SIZEOF_INT */
317
318 #ifndef SIZEOF_LONG
319 #define SIZEOF_LONG  4
320 #endif
321
322 #ifndef SIZEOF_PTR
323 #define SIZEOF_PTR   SIZEOF_INT
324 #endif
325
326 #ifndef CPU_BITS_PER_CHAR
327 #define CPU_BITS_PER_CHAR   (SIZEOF_CHAR * 8)
328 #endif
329
330 #ifndef CPU_BITS_PER_SHORT
331 #define CPU_BITS_PER_SHORT  (SIZEOF_SHORT * CPU_BITS_PER_CHAR)
332 #endif
333
334 #ifndef CPU_BITS_PER_INT
335 #define CPU_BITS_PER_INT    (SIZEOF_INT * CPU_BITS_PER_CHAR)
336 #endif
337
338 #ifndef CPU_BITS_PER_LONG
339 #define CPU_BITS_PER_LONG   (SIZEOF_LONG * CPU_BITS_PER_CHAR)
340 #endif
341
342 #ifndef CPU_BITS_PER_PTR
343 #define CPU_BITS_PER_PTR    (SIZEOF_PTR * CPU_BITS_PER_CHAR)
344 #endif
345 /*\}*/
346
347 /* Sanity checks for the above definitions */
348 STATIC_ASSERT(sizeof(char) == SIZEOF_CHAR);
349 STATIC_ASSERT(sizeof(short) == SIZEOF_SHORT);
350 STATIC_ASSERT(sizeof(long) == SIZEOF_LONG);
351 STATIC_ASSERT(sizeof(int) == SIZEOF_INT);
352
353
354 /*!
355  * \def SCHEDULER_IDLE
356  *
357  * \brief Invoked by the scheduler to stop the CPU when idle.
358  *
359  * This hook can be redefined to put the CPU in low-power mode, or to
360  * profile system load with an external strobe, or to save CPU cycles
361  * in hosted environments such as emulators.
362  */
363 #ifndef SCHEDULER_IDLE
364         #if defined(ARCH_EMUL) && (ARCH & ARCH_EMUL)
365                 /* This emulator hook should yield the CPU to the host.  */
366                 EXTERN_C_BEGIN
367                 void SchedulerIdle(void);
368                 EXTERN_C_END
369                 #define SCHEDULER_IDLE SchedulerIdle()
370         #else /* !ARCH_EMUL */
371                 #define SCHEDULER_IDLE do { /* nothing */ } while (0)
372         #endif /* !ARCH_EMUL */
373 #endif /* !SCHEDULER_IDLE */
374
375 #endif /* DEVLIB_CPU_H */