Search code examples
ceclipsegccarm

GCC Block Read-Modify-Write for special Variable


currently i am working on a project with an ARM M4F on arm-gcc and eclipse.

I have declared bitfields within structs to access the IO-registers. Please see as example:

struct abc{
   volatile U32 a:1;
   volatile U32 b:1;
   volatile U32 c:30;
}

Now, some of these structs are mappend onto peripherals, which use a set of "Set and Clear" registers.

You write 1U to the Set-Register to set the specific bit and 1U to the Clear-Register to clear the specific bit.

BUT If a bit is set, it is also reflected as set in the Clear-Register for reads.

So abc_set.a = 1U; results in abc_clear.a == 1U

In my code if have written: abc_clear.a = 1U;

This results in arm-gcc generating:

ldr.w   r0, [r1, #128]  ; 0x80
orr.w   r0, r0, #536870912      ; 0x20000000
str.w   r0, [r1, #128]  ; 0x80

The compiler enforces a Read-Modify-Write approach.

BUT If other bits are reflected as "1U" in the abc_clear registers, they get also written as 1U and are subsequently cleared as well.

My question is

How can i enforce gcc to use a "Write-Only" sheme for specific registers?

I am looking for something like:

struct abc{
    #pragma "Only-Write" / or attribute((only-write))
    volatile U32 a:1;
    #pragma "R-M-W allowed"
    volatile U32 b:1;
    volatile U32 c:30;
}

Solution

  • I have declared bitfields within structs to access the IO-registers.

    You can't do this with bitfields. Basically, bitfields are not good for this purpose. uP do not have instructions to write or read a few (less than 8) bits only *. You wasted your time

    You need to do this traditional way

    
    #define REG_X_A_POS (0)
    #define REG_X_A (1<<REG_X_A_POS)
    #define REG_X_B_POS (1)
    #define REG_X_B (1<<REG_X_B_POS)
    
    #define REG_X   (*(volatile uint32_t *)0x44006655)
    

    And usage:

    REGX = REG_X_A
    

    And the resulting code:

    foo:
            ldr     r3, .L2
            movs    r2, #1
            str     r2, [r3, #1616]
    .L2:
            .word   1140875264
    

    When you need RMW you just do it yourself:

        REG_X |= REG_X_A;
    

    and the compiler will generate the correct code

            ldr     r2, .L3
            ldr     r3, [r2, #1616]
            orr     r3, r3, #1
            str     r3, [r2, #1616]
    .L3:
            .word   1140875264
    

    Another way is to use a temporary variable (not a very convenient way).

    struct abc tempReg = abc_set;
    tmpReg.a = 1;
    abc_set = tempReg
    

    * You can do this using (M4) bitbang areas, but the compiler does not know anything about it.