HWA
Bare metal programming with style
hwa_2.h
Go to the documentation of this file.
1 
2 /* This file is part of the HWA project.
3  * Copyright (c) 2012,2015 Christophe Duparquet.
4  * All rights reserved. Read LICENSE.TXT for details.
5  */
6 
13 /* Interrupts
14  */
15 #define _hw_enirq(o,v,n,m,f,...) _hw_write(n,m,1) HW_EOL(__VA_ARGS__)
16 #define _hwa_enirq(o,v,n,m,f,...) _hwa_write(n,m,1) HW_EOL(__VA_ARGS__)
17 
18 #define _hw_dsirq(o,v,n,m,f,...) _hw_write(n,m,0) HW_EOL(__VA_ARGS__)
19 #define _hwa_dsirq(o,v,n,m,f,...) _hwa_write(n,m,0) HW_EOL(__VA_ARGS__)
20 
21 #define _hw_isenirq(o,v,n,m,f,...) _hw_read(n,m) HW_EOL(__VA_ARGS__)
22 
23 #define _hw_rdirq(o,v,n,m,f,...) _hw_read(n,f) HW_EOL(__VA_ARGS__)
24 
25 #define _hw_clirq(o,v,n,m,f,...) _hw_write(n,f,1) HW_EOL(__VA_ARGS__) /* Write 1 to clear */
26 #define _hwa_clirq(o,v,n,m,f,...) _hwa_write(n,f,1) HW_EOL(__VA_ARGS__) /* Write 1 to clear */
27 
28 #define _hw_enirqs(o,a,...) hw_asm("sei") HW_EOL(__VA_ARGS__)
29 #define _hw_dsirqs(o,a,...) hw_asm("cli") HW_EOL(__VA_ARGS__)
30 
31 
32 
48 #define hw_wait_irq , _hw_wait_irq
49 #define _hw_wait_irq(...) do{ /* _hw_write(core0,se,1); */ hw_asm("sleep"); }while(0)
50 
51 
52 #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
53 # define HW_ISR_ATTRIBUTES __attribute__((signal, used, externally_visible))
54 #else /* GCC < 4.1 */
55 # define HW_ISR_ATTRIBUTES __attribute__((signal, used))
56 #endif
57 
58 #define _hw_israttr_atomic ,
59 #define _hw_israttr_non_interruptible ,
60 #define _hw_israttr_interruptible , __attribute__((interrupt))
61 #define _hw_israttr_naked , __attribute__((naked))
62 
63 
64 /* Single event ISR
65  */
66 #define _HW_ISR_(...) _HW_ISR__(__VA_ARGS__)
67 #define _HW_ISR__(v,...) \
68  HW_EXTERN_C void __vector_##v(void) HW_ISR_ATTRIBUTES __VA_ARGS__ ; \
69  void __vector_##v(void)
70 
71 
72 /* Alias
73  */
74 #define _HW_ISR_ALIAS(v1,v2) _HW_ISR_ALIAS_(v1,v2)
75 #define _HW_ISR_ALIAS_(v1,v2) \
76  HW_EXTERN_C void __vector_##v1(void) __attribute__((signal,used,externally_visible)) \
77  __attribute__((alias(HW_Q(__vector_##v2)))); \
78  void __vector_##v1(void)
79 
80 
81 /* Void ISR
82  */
83 #define _HW_VISR_(v) \
84  HW_EXTERN_C void __vector_##v(void) __attribute__((naked)) ; \
85  void __vector_##v(void) { hw_asm("reti"); }
86 
87 
98 #define hw_waste_cycles(n) __builtin_avr_delay_cycles(n)
99 
100 
101 #include "../../hwa/hwa_2.h"
102 
103 
104 /* This is a generic method that can be implemented by all peripheral
105  * classes. An object supports power management if it has a logical register
106  * named `prr`.
107  */
108 #define hw_power , _hw_power
109 #define hwa_power , _hwa_power
110 
111 #define _hw_power(c,o,a,v,g,...) HW_B(_hwx_pwr1_,g)(_hw,o,v,g)
112 #define _hwa_power(c,o,a,v,g,...) HW_B(_hwx_pwr1_,g)(_hwa,o,v,g)
113 #define _hwx_pwr1_0(h,o,v,g) HW_E(HW_EM_G(g))
114 #define _hwx_pwr1_1(h,o,v,g) HW_B(_hwx_pwr2_,_hw_state_v)(h,o,v)
115 #define _hwx_pwr2_0(h,o,v) HW_E(HW_EM_ST(v))
116 #define _hwx_pwr2_1(h,o,v) HW_B(_hwx_pwr3_,HW_G2(_hw_isa_reg, hw_##o##_##prr))(h,o,v)
117 #define _hwx_pwr3_0(h,o,v) HW_E(HW_EM_FO(h##_power,o))
118 #define _hwx_pwr3_1(h,o,v) h##_write(o,prr,HW_A1(_hw_state_##v)==0)
119 
120 
128 #define HW_ATOMIC(...) \
129  do{ \
130  uint8_t s = _hw_read(core0,sreg); \
131  hw_disable, interrupts(); \
132  { __VA_ARGS__ } \
133  _hw_write(core0,sreg,s) ; \
134  }while(0)
135 
136 
146 #define HW_MEM_EEPROM __attribute__((section(".eeprom")))
147 
148 
162 HW_INLINE void _hw_write_r8 ( intptr_t ra, uint8_t rwm, uint8_t rfm, uint8_t mask, uint8_t value )
163 {
164 #if defined HWA_CHECK_ACCESS
165  if ( ra == ~0 )
166  HWA_E(HW_EM_X("_hw_write_r8: invalid access"));
167 #endif
168 
169 #if !defined HWA_NO_CHECK_USEFUL
170  if ( mask == 0 )
171  HWA_E(HW_EM_X("_hw_write_r8: no bit to be changed?"));
172 #endif
173 
174 #if !defined HWA_NO_CHECK_LIMITS
175  // if ( (value & mask) != value ) {
176  if ( value & (~mask) ) {
177  /* hw_asm("wdr"); */
178  /* *(volatile uint8_t*)0 = value ; */
179  /* *(volatile uint8_t*)0 = mask ; */
180  /* *(volatile uint8_t*)0 = value & mask ; */
181  /* hw_asm("wdr"); */
182  HWA_E(HW_EM_X("_hw_write_r8: value overflows mask"));
183  }
184 #endif
185 
186  /* Verify that we do not try to set non-writeable bits
187  */
188  if ( (value & mask & rwm) != (value & mask) )
189  HWA_E(HW_EM_X("_hw_write_r8: bits not writeable."));
190 
191  volatile uint8_t *p = (volatile uint8_t *)ra ;
192 
193  if ( ra < 0x40 &&
194  (mask==0x01 || mask==0x02 || mask==0x04 || mask==0x08 ||
195  mask==0x10 || mask==0x20 || mask==0x40 || mask==0x80) ) {
196  /*
197  * Just 1 bit to be written at C address < 0x40 (ASM address < 0x20): use
198  * sbi/cbi
199  */
200  if ( value )
201  *p |= mask ; /* sbi */
202  else
203  *p &= ~mask ; /* cbi */
204  }
205  else {
206  /*
207  * Mask of bits to be read
208  * = bits that are writeable and not to be modified and not flags
209  */
210  uint8_t rm = rwm & ~mask & ~rfm ;
211 
212  if ( rm == 0 )
213  /*
214  * Nothing to be read, just write the new value
215  */
216  *p = value ;
217  else {
218  /*
219  * Read-modify-write
220  */
221  uint8_t sm = mask & rwm & value ; /* what has to be set */
222  uint8_t cm = mask & rwm & (~value) ; /* what has to be cleared */
223  *p = (*p & ~cm) | sm ;
224  }
225  }
226 }
227 
228 
229 /*
230  * @ingroup private
231  * Write one 16-bit hardware register
232  *
233  * Write `value` through `mask` bits of the hardware register at address `ra`.
234  * Trying to write `1`s into non-writeable bits triggers an error.
235  *
236  * @param ra address of register.
237  * @param rwm writeable bits mask of the register.
238  * @param rfm flag bits mask of the register.
239  * @param mask mask of bits concerned.
240  * @param value value to write.
241  */
242 HW_INLINE void _hw_write_r16 ( intptr_t ra, uint16_t rwm, uint16_t rfm, uint16_t mask, uint16_t value )
243 {
244 #if defined HWA_CHECK_ACCESS
245  if ( ra == ~0 )
246  HWA_E(HW_EM_X("_hw_write_r16: invalid access"));
247 #endif
248 
249 #if !defined HWA_NO_CHECK_USEFUL
250  if ( mask == 0 )
251  HWA_E(HW_EM_X("_hw_write_r16: no bit to be changed?"));
252 #endif
253 
254 #if !defined HWA_NO_CHECK_LIMITS
255  if ( value & (~mask) ) {
256  HWA_E(HW_EM_X("_hw_write_r16: value overflows mask"));
257  }
258 #endif
259 
260  /* Verify that we do not try to set non-writeable bits
261  */
262  if ( (value & mask & rwm) != (value & mask) )
263  HWA_E(HW_EM_X("_hw_write_r16: bits not writeable."));
264 
265  volatile uint16_t *p = (volatile uint16_t *)ra ;
266 
267  if ( ra < 0x40 &&
268  (mask==0x0001 || mask==0x0002 || mask==0x0004 || mask==0x0008 ||
269  mask==0x0010 || mask==0x0020 || mask==0x0040 || mask==0x0080 ||
270  mask==0x0100 || mask==0x0200 || mask==0x0400 || mask==0x0800 ||
271  mask==0x1000 || mask==0x2000 || mask==0x4000 || mask==0x8000) ) {
272  /*
273  * Just 1 bit to be written at C address < 0x40 (ASM address < 0x20): use
274  * sbi/cbi
275  */
276  if ( value )
277  *p |= mask ; /* sbi */
278  else
279  *p &= ~mask ; /* cbi */
280  }
281  else {
282  /*
283  * Mask of bits to be read
284  * = bits that are writeable and not to be modified and not flags
285  */
286  uint16_t rm = rwm & ~mask & ~rfm ;
287 
288  if ( rm == 0 )
289  /*
290  * Nothing to be read, just write the new value
291  */
292  *p = value ;
293  else {
294  /*
295  * Read-modify-write
296  */
297  uint16_t sm = mask & rwm & value ; /* what has to be set */
298  uint16_t cm = mask & rwm & (~value) ; /* what has to be cleared */
299  *p = (*p & ~cm) | sm ;
300  }
301  }
302 }
303 
304 
305 /*
306  * @ingroup private
307  * Commit an 8-bit HWA register to hardware
308  * @hideinitializer
309  *
310  * The code is meant to produce the best machine code among (verified on Atmel
311  * AVRs):
312  *
313  * - bit-set or bit-clear: 1 instruction
314  * - load-immediate / store: 2 instructions
315  * - load / modify / store: 3 instructions or more
316  */
317 HW_INLINE void _hwa_commit__r8 ( hwa_r8_t *r, uint8_t rwm, uint8_t rfm, _Bool commit )
318 {
319  if ( !commit ) {
320  r->ovalue = (r->ovalue & r->omask & ~r->mmask) | (r->mvalue & r->mmask) ;
321  r->omask |= r->mmask ;
322  r->mmask = 0 ;
323  return ;
324  }
325 
326  volatile uint8_t *p = (volatile uint8_t *)r->a ;
327 
328  /* Mask of bits to be written:
329  * = bits that are writeable (rwm)
330  * AND that has been written (mmask)
331  * AND that really need to be modified (ovalue != mvalue)
332  */
333  uint8_t wm = rwm & r->mmask & ((r->ovalue ^ r->mvalue) | ~r->omask);
334 
335  if ( wm ) {
336  if ( (uintptr_t)p < 0x40 &&
337  (wm==0x01 || wm==0x02 || wm==0x04 || wm==0x08 ||
338  wm==0x10 || wm==0x20 || wm==0x40 || wm==0x80 ) ) {
339  /*
340  * Just 1 bit to be modified at C address < 0x40 (ASM address < 0x20): use
341  * instruction CBI or SBI
342  */
343  if ( wm & r->mvalue )
344  *p |= wm ; /* sbi */
345  else
346  *p &= ~wm ; /* cbi */
347  r->ovalue = (r->ovalue & ~wm) | (r->mvalue & wm) ;
348  r->omask |= wm ;
349  r->mmask = 0 ;
350  return ;
351  }
352 
353  /* Mask of bits to be read
354  * = bits that are not to be modified (mmask)
355  * AND not flags (rfm)
356  * AND that are not known (omask)
357  * AND that are writeable (rwm)
358  */
359  uint8_t rm = ~r->mmask & ~rfm & ~r->omask & rwm ;
360 
361  /* Read only if needed
362  */
363  if ( rm )
364  r->ovalue = *p ;
365 
366  /* Compute new value
367  */
368  r->ovalue = ((r->ovalue & ~wm) | (r->mvalue & wm)) & ~rfm ;
369 
370  /* Write new value
371  */
372  *p = r->ovalue | (rfm & r->mmask & r->mvalue) ;
373  }
374 
375  r->omask |= r->mmask ;
376  r->mmask = 0 ;
377 }
378 
379 
380 HW_INLINE void _hwa_commit__r16 ( hwa_r16_t *r, uint16_t rwm, uint16_t rfm, _Bool commit )
381 {
382  if ( !commit ) {
383  r->ovalue = (r->ovalue & r->omask & ~r->mmask) | (r->mvalue & r->mmask) ;
384  r->omask |= r->mmask ;
385  r->mmask = 0 ;
386  return ;
387  }
388 
389  volatile uint16_t *p = (volatile uint16_t *)r->a ;
390 
391  uint16_t wm = rwm & r->mmask & ((r->ovalue ^ r->mvalue) | ~r->omask);
392 
393  if ( wm ) {
394  /*
395  * Do not try sbi/cbi for 16-bit access as since 8-bit AVRs do not have
396  * 16-bit configuration registers, it is unlikely that it would be useful.
397  */
398  uint16_t rm = ~r->mmask & ~rfm & ~r->omask & rwm ;
399 
400  if ( rm )
401  r->ovalue = *p ;
402 
403  r->ovalue = ((r->ovalue & ~wm) | (r->mvalue & wm)) & ~rfm ;
404 
405  *p = r->ovalue | (rfm & r->mmask & r->mvalue) ;
406  }
407 
408  r->omask |= r->mmask ;
409  r->mmask = 0 ;
410 }
411 
412 
413 
414 /*
415  * Read from one hardware register.
416  *
417  * @param ra address of register.
418  * @param rbn number of consecutive bits conderned.
419  * @param rbp position of the least significant bit conderned in the register.
420  * @return the value of the rbn consecutive bits at position rbp in the register.
421  */
422 #define _hw_read__r8(ra,rbn,rbp) (((*(volatile uint8_t*)(ra))>>(rbp))&((1U<<(rbn))-1))
423 
424 
425 #define hw_read__r16 , _hw_read_r16
426 //#define _hw_read_r16(o,a,wm,fm,...) _hw_read__r16(a,16,0)
427 #define _hw_read_r16(o,a,wm,fm,...) *(volatile uint16_t*)a
428 
429 
430 HW_INLINE uint16_t _hw_read__r16 ( intptr_t ra, uint8_t rbn, uint8_t rbp )
431 {
432  uint16_t m = (1UL<<rbn)-1 ;
433  volatile uint16_t *p = (volatile uint16_t *)ra ;
434  return ((*p)>>rbp) & m ;
435 }
436 
437 
438 #define _hw_atomic_read__r8 _hw_read__r8
439 
440 /*
441  * @addtogroup atmelavr8
442  * Atomic read 16-bit value
443  *
444  * Interrupts are re-enabled as soon as possible.
445  *
446  * @note Interrupts are enabled after reading even if they were not before.
447  */
448 HW_INLINE uint16_t _hw___atomic_read__r16 ( intptr_t ra )
449 {
450  uint16_t r;
451 
452  hw_asm("cli" "\n\t"
453  "lds %A[r], %[a]" "\n\t"
454  "sei" "\n\t"
455  "lds %B[r], %[a]+1" "\n\t"
456  : [r] "=&r" (r)
457  : [a] "p" (ra)
458  : "memory"
459  );
460  return r;
461 }
462 /* FIXME: if the I bit is set after writing SREG with its prior value, is the
463  * execution of the next opcode guaranteed as if the SEI instruction was used?
464  */
465 
466 
467 #define _hw_atomic_read__m11(oo,o,r,rc,ra,rwm,rfm,bn,bp,...) _hw_atomic_read_##rc(ra,bn,bp)
468 
469 
470 HW_INLINE uint16_t _hw_atomic_read__r16 ( intptr_t ra, uint8_t rbn, uint8_t rbp )
471 {
472  uint16_t v ;
473  uint16_t m = ((1UL<<rbn)-1)<<rbp ;
474 
475 #if 0
476  volatile uint8_t *pl = (volatile uint8_t *)ra+0 ;
477  volatile uint8_t *ph = (volatile uint8_t *)ra+1 ;
478 
479  if ( (m & 0xFF) && (m >> 8) ) {
480  uint8_t s = _hw_read(core0,sreg);
481  hw_disable, interrupts();
482  uint8_t lb = *pl ;
483  _hw_write(core0,sreg,s);
484  uint8_t hb = *ph ;
485  v = (hb << 8) | lb ;
486  }
487  else if ( m & 0xFF )
488  v = *pl ;
489  else
490  v = (*ph)<<8 ;
491 #else
492  if ( (m&0xFF) == 0 )
493  v = (*(volatile uint8_t *)ra+1)<<8 ;
494  else if ( (m>>8) == 0 )
495  v = *(volatile uint8_t *)ra;
496  else
497  v = _hw___atomic_read__r16( ra );
498 #endif
499 
500  return (v>>rbp) & m ;
501 }
502 
503 
504 #if 0
505 /*
506  * From http://www.avrfreaks.net/forum/atomic-readwrite-uint16t-variable?skey=atomic%20write
507  */
508 #define atomic_read_word(__addr) \
509 (__extension__({ \
510  uint16_t __result; \
511  __asm__ __volatile__ ( \
512  "cli \n\t" \
513  "lds %A[res], %[addr] \n\t" \
514  "sei \n\t" \
515  "lds %B[res], %[addr] + 1 \n\t" \
516  : [res] "=&r" (__result) \
517  : [addr] "p" (&(__addr)) \
518  : "memory" \
519  ); \
520  __result; \
521 }))
522 
523 #define atomic_write_word_restore(__addr, __data) \
524 (__extension__({ \
525  uint8_t __tmp; \
526  __asm__ __volatile__ ( \
527  "in %[tmp], __SREG__ \n\t" \
528  "cli \n\t" \
529  "sts %[addr], %A[data] \n\t" \
530  "out __SREG__, %[tmp] \n\t" \
531  "sts %[addr] + 1, %B[data] \n\t" \
532  : [tmp] "=&r" (__tmp) \
533  : [data] "r" (__data) \
534  , [addr] "p" (&(__addr)) \
535  : "memory" \
536  ); \
537 }))
538 #endif
_hw_write
#define _hw_write(o, r, v)
Write one register of an object.
Definition: hwa_2.h:168
HWA_E
#define HWA_E(...)
Trigger an error after code optimization.
Definition: hwa_2.h:17
hw_asm
#define hw_asm(...)
Insert inline assembler code.
Definition: hwa_2.h:37
_hw_read
#define _hw_read(o, r)
Read one register of an object.
Definition: hwa_2.h:140
_hw_write_r8
HW_INLINE void _hw_write_r8(intptr_t ra, uint8_t rwm, uint8_t rfm, uint8_t mask, uint8_t value)
Definition: hwa_2.h:162