unexpected M0+ bit write assemble code

Asked by bruce.li

here's the register definition:
typedef union { // __I to avoid read-modify-write on write-to-clear register
  struct {
    __IO uint8_t FC0O:1; /*!< bit: 0 Frame Counter 0 Overflow */
    __IO uint8_t FC1O:1; /*!< bit: 1 Frame Counter 1 Overflow */
    __IO uint8_t FC2O:1; /*!< bit: 2 Frame Counter 2 Overflow */
    __IO uint8_t VLCDRT:1; /*!< bit: 3 VLCD Ready Toggle */
    __IO uint8_t VLCDST:1; /*!< bit: 4 VLCD Status Toggle */
    __IO uint8_t PRST:1; /*!< bit: 5 Pump Run Status Toggle */
    __IO uint8_t :2; /*!< bit: 6.. 7 Reserved */
  } bit; /*!< Structure used for bit access */
  uint8_t reg; /*!< Type used for register access */
} SLCD_INTFLAG_Type;

And here's the disassemble code generated by compiler
void slcd_clear_fc0o_irq(void) {
  SLCD->INTFLAG.bit.FC0O = 1;
    156a: 4b09 ldr r3, [pc, #36] ; (1590 <slcd_clear_fco_irq+0x34>)
    156c: 7bd9 ldrb r1, [r3, #15]
    156e: 2201 movs r2, #1
    1570: 430a orrs r2, r1
    1572: 73da strb r2, [r3, #15]
    1574: e00a b.n 158c <slcd_clear_fco_irq+0x30>
}

What I want is just writing 1 to INTFLAG register, while compiler gives me read modify write. This behavior will clear all interrupt flag, not the one I want to clear.
My understanding is if a var is allocated in main RAM, read modify write is the expected behavior. But if var is declared as IO type, compiler should just write data and no need to read the data back?

Can you help me out there, thanks.

Question information

Language:
English Edit question
Status:
Solved
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Solved by:
bruce.li
Solved:
Last query:
Last reply:
Revision history for this message
Terry Guo (terry.guo) said :
#1

I think the generated code is totally correct. But to better understand your requirement, would you please show us your expected code sequence? Is it something like below without the first read?

    156a: 4b09 ldr r3, [pc, #36] ; (1590 <slcd_clear_fco_irq+0x34>)
    //156c: 7bd9 ldrb r1, [r3, #15]// do you mean we remove this line?
    156e: 2201 movs r2, #1
// 1570: 430a orrs r2, r1 // and remove this line?
    1572: 73da strb r2, [r3, #15]
    1574: e00a b.n 158c <slcd_clear_fco_irq+0x30>

Revision history for this message
bruce.li (lq3141) said :
#2

yes, terry. You're right.
This's what I want:
    156a: 4b09 ldr r3, [pc, #36] ; (1590 <slcd_clear_fco_irq+0x34>)
  //156c: 7bd9 ldrb r1, [r3, #15]// do you mean we remove this line?
    156e: 2201 movs r2, #1
// 1570: 430a orrs r2, r1 // and remove this line?
    1572: 73da strb r2, [r3, #15]
    1574: e00a b.n 158c <slcd_clear_fco_irq+0x30>

How can I ask compiler generate code above? thanks

Revision history for this message
Terry Guo (terry.guo) said :
#3

Then how about using below c code:

SLCD_INTFLAG_Type *SLCD;

void
slcd_clear_fc0o_irq(void)
{
  SLCD->reg = 1;
}

The corresponding code are:
slcd_clear_fc0o_irq:
        mov r2, #1
        ldr r3, .L2
        @ sp needed
        ldr r3, [r3]
        strb r2, [r3]
        bx lr

There will be no read to that memory address prior to write.

Please be noted that the smallest unit that arm processor can work on is 8-bits. It's impossible to just write 1 bit without touching the other 7 bits around. But maybe this feature is useful to you:
http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Behcjiic.html

Revision history for this message
bruce.li (lq3141) said :
#4

yes, this's our current solution: just use SLCD->reg to write-1-clear the corresponding interrupt flag bit.

But bit reference seems more natural and straightforward :)

Let me explain more detail: this's a interrupt flag register. It's write-1-clear. So we can just clear a specific bit in interrupt flag.

Do you think there exist any elegant solution?

thanks very much

Revision history for this message
bruce.li (lq3141) said :
#5

In other words, for a write-1-clear register, write 0 into a bit will not change the bit status. only the bit is writted by 1 will be cleared.

Revision history for this message
Terry Guo (terry.guo) said :
#6

In my opinion, the current code generated by gcc is elegant solution. What we are talking is a very common use case inside embedded world.

Let us get back to code in comment #2. I think below read is harmless:

156c: 7bd9 ldrb r1, [r3, #15]// do you mean we remove this line?

So may I know why you say it is undesired?

Revision history for this message
bruce.li (lq3141) said :
#7

this register is interrupt flag, and it's write-1-clear.
The intent of C code below:
 SLCD->INTFLAG.bit.FC0O = 1;
is to clear FC0O bit.
If we read this register back first, probably other bits have been set to 1 by hardware.
And according to disassemble code, gcc OR FC0O bit (our design intent) and the read back bits together.
Then write ORed value into INTFLAG register. This could clear other bits in INTFLAG register. This's not what we want.

Am I clear? thanks

Revision history for this message
Terry Guo (terry.guo) said :
#8

Thanks for your further explanation. I am pretty clear of what you are saying. Unfortunately I don't think current gcc can work like you expected. The gcc is lack of something like below:

typedef union {
  struct {
    __IO uint8_t FC0O:1;
    __IO uint8_t FC1O:1 __attribute__((always write 0 to this bit))
    __IO uint8_t FC2O:1 __attribute__((always write 0 to this bit));
    __IO uint8_t VLCDRT:1 __attribute__((always write 0 to this bit));
.................................

So user has no way to tell gcc that only the first bit FC0O should be cared and others are always zero. User also has no way to tell gcc "If we read this register back first, probably other bits have been set to 1 by hardware." just from one line c code "SLCD->INTFLAG.bit.FC0O = 1".

So to get what you want, I think you can propose this as a new feature request to gcc community. What you are asking for is not something specific to ARM. Many other targets also support bit field operations, so they can benefit from your proposal. We then can follow the result of community discussion to implement this feature for ARM.

Revision history for this message
bruce.li (lq3141) said :
#9

understood, many thanks :)