c - Casting troubles when using bit-banding macros with a pre-cast address on Cortex-M3 -
tl;dr:
- why isn't
(unsigned long)(0x400253fc)equivalent(unsigned long)((*((volatile unsigned long *)0x400253fc)))? - how can make macro works former work latter?
background information
environment
i'm working arm cortex-m3 processor, lm3s6965 ti, stellarisware (free download, export controlled) definitions. i'm using gcc version 4.6.1 (sourcery codebench lite 2011.09-69). stellaris provides definitions 5,000 registers , memory addresses in "inc/lm3s6965.h", , don't want redo of those. however, seem incompatible macro want write.
bit banding
on arm cortex-m3, portion of memory aliased 1 32-bit word per bit of peripheral , ram memory space. setting memory @ address 0x42000000 0x00000001 set first bit of memory @ address 0x40000000 1, not affect rest of word. change bit 2, change word @ 0x42000004 1. that's neat feature, , extremely useful. according arm technical reference manual, algorithm compute address is:
bit_word_offset = (byte_offset x 32) + (bit_number × 4) bit_word_addr = bit_band_base + bit_word_offset where:
bit_word_offsetposition of target bit in bit-band memory region.bit_word_addraddress of word in alias memory region maps targeted bit.bit_band_basestarting address of alias region.byte_offsetnumber of byte in bit-band region contains targeted bit.bit_numberbit position, 0 7, of targeted bit
implementation of bit banding
the "inc/hw_types.h" file includes following macro implements algorithm. clear, implements word-based model accepts 4-byte-aligned words , 0-31-bit offsets, resulting address equivalent:
#define hwregbitb(x, b) \ hwregb(((unsigned long)(x) & 0xf0000000) | 0x02000000 | \ (((unsigned long)(x) & 0x000fffff) << 5) | ((b) << 2)) this algorithm takes base either in sram @ 0x20000000 or peripheral memory space @ 0x40000000) , ors 0x02000000, adding bit band base offset. then, multiples offset base 32 (equivalent five-position left shift) , adds bit number.
the referenced hwreg performs requisite cast writing given location in memory:
#define hwreg(x) \ (*((volatile unsigned long *)(x))) this works quite nicely assignments like
hwregbitw(0x400253fc, 0) = 1; where 0x400253fc magic number memory-mapped peripheral , want set bit 0 of peripheral 1. above code computes (at compile-time, of course) bit offset , sets word 1.
what doesn't work
unfortunately, aforememntioned definitions in "inc/lm3s6965.h" perform cast done hwreg. want avoid magic numbers , instead use provided definitions
#define gpio_portf_data_r (*((volatile unsigned long *)0x400253fc)) an attempt paste hwregbitw causes macro no longer work, cast interferes:
hwregbitw(gpio_portf_data_r, 0) = 1; the preprocessor generates following mess (indentation added):
(*((volatile unsigned long *) ((((unsigned long)((*((volatile unsigned long *)0x400253fc)))) & 0xf0000000) | 0x02000000 | ((((unsigned long)((*((volatile unsigned long *)0x400253fc)))) & 0x000fffff) << 5) | ((0) << 2)) )) = 1; note 2 instances of
(((unsigned long)((*((volatile unsigned long *)0x400253fc))))) i believe these casts causing process fail. following result of preprocessing hwregbitw(0x400253fc, 0) = 1; work, supporting assertion:
(*((volatile unsigned long *) ((((unsigned long)(0x400253fc)) & 0xf0000000) | 0x02000000 | ((((unsigned long)(0x400253fc)) & 0x000fffff) << 5) | ((0) << 2)) )) = 1; the (type) cast operator has right-to-left precedence, last cast should apply , unsigned long used bitwise arithmetic (which should work correctly). there's nothing implicit anywhere, no float pointer conversions, no precision/range changes...the left-most cast should nullify casts right.
my question (finally...)
- why isn't
(unsigned long)(0x400253fc)equivalent(unsigned long)((*((volatile unsigned long *)0x400253fc)))? - how can make existing
hwregbitwmacro work? or, how can macro written same task not fail when given argument pre-existing cast?
1- why isn't (unsigned long)(0x400253fc) equivalent (unsigned long)((*((volatile unsigned long *)0x400253fc)))?
the former integer literal , value 0x400253fcul while latter unsigned long value stored in (memory or gpio) address 0x400253fc
2- how can make existing hwregbitw macro work? or, how can macro written same task not fail when given argument pre-existing cast?
use hwregbitw(&gpio_portf_data_r, 0) = 1; instead.
Comments
Post a Comment