简体   繁体   中英

uint64_t variable with & operations

I have a function which is searching for series of nine "1" in 64-bits variable(RFID tag number) and if found, moves them to the MSB. I have a huge problem with understanding why it does not work properly.

This is my variable

uint64_t volatile RFID_data2= 0b1111111110000000000000000000000000000000000000000000000000000000;

and i send it as pointer to function

test_flag= header_align(&RFID_data2);


uint8_t header_align(uint64_t *data){

uint64_t bit_mask=(uint64_t) 
0b1111111110000000000000000000000000000000000000000000000000000000;


if((*data&bit_mask)==bit_mask){
PORTA ^= (1<<PORTA2);
return 1;
}

This condition is never fulfilled, but if i changed the conition to this :

if((*data==bit_mask){
PORTA ^= (1<<PORTA2);
return 1;
    }

it appears to work good.

What is more i write another condition - which is working.

if((*data&bit_mask)>(bit_mask-1) && (*data&bit_mask)<(bit_mask+1 )){
    PORTA ^= (1<<PORTA2);
    return 1

As i can see it is a problem with AND '&' operation. In addition there are not any problems when i change RFID_data to 32 bits variable. I am working with Attiny441 and GCC compiler, Atmel Studio Is there any way to make it works on 64 bits?

I changed function to take uint64t (non-pointer) but the problem still persists. I also tried to create global varaible, and remove volatile modifer but it still does not working properly. Using a macro UINT64_C does not help also. It looks like :

uint64_t RFID_data;// global var

int main(void)
{
  RFID_data=(0xFF80000000000000);// write into global var
 uint64_t RFID_data2=(0xFF80000000000000);
   while(1)
   {
    test_flag=header_align(RFID_data2);// send local variable
   }
}


 uint8_t header_align(uint64_t data){
  uint64_t bit_mask = UINT64_C(0xFF80000000000000);


    if((data&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);//nothink
        return 1;
    }

I also tried to check if-condtion by global var:

     if((RFID_data&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);///nothink
        return 1;
    }

In both ways it does not returning 1, neither changing PORTA2 state.

It works only when i create a new local variable in header_allgin, like this:

 uint8_t header_align(uint64_t data){
  uint64_t RFID_data3 = UINT64_C(0xFF80000000000000);
  uint64_t bit_mask = UINT64_C(0xFF80000000000000);


    if((RFID_data3&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);// here i can see signal
        return 1;
    }}

Is it way to make it work by global variable or byargument of function ?

Just use the proper suffixes.

L for int32

LL for int64

UL for uint32

ULL for uint64

Seems like you caught a real compiler bug here!

GCC version:

avr-gcc (GCC) 4.8.1

(with -O1 or -O2 optimizations enabled, at -O0 the problem doesn't seem to be present)

Reduced test case:

#include <stdint.h>


uint8_t volatile tmp;


__attribute__((noinline)) void test_64(uint64_t d64)
{
 if ((d64 & 0xFF800000UL) == 0xFF800000UL){
  tmp ++;
 }
}

__attribute__((noinline)) void test_32(uint32_t d32)
{
 if ((d32 & 0xFF800000UL) == 0xFF800000UL){
  tmp ++;
 }
}


int main(void)
{
 test_64(0);
 test_32(0);

 while(1);
}

Assembler output for the critical part:

00000228 <test_64>:
 228:   08 95           ret

0000022a <test_32>:
 22a:   66 27           eor r22, r22
 22c:   77 27           eor r23, r23
 22e:   80 78           andi    r24, 0x80   ; 128
 230:   61 15           cp  r22, r1
 232:   71 05           cpc r23, r1
 234:   80 48           sbci    r24, 0x80   ; 128
 236:   9f 4f           sbci    r25, 0xFF   ; 255
 238:   09 f0           breq    .+2         ; 0x23c <test_32+0x12>
 23a:   08 95           ret
 23c:   80 91 00 20     lds r24, 0x2000
 240:   8f 5f           subi    r24, 0xFF   ; 255
 242:   80 93 00 20     sts 0x2000, r24
 246:   08 95           ret

00000248 <main>:
 248:   20 e0           ldi r18, 0x00   ; 0
 24a:   30 e0           ldi r19, 0x00   ; 0
 24c:   40 e0           ldi r20, 0x00   ; 0
 24e:   50 e0           ldi r21, 0x00   ; 0
 250:   60 e0           ldi r22, 0x00   ; 0
 252:   70 e0           ldi r23, 0x00   ; 0
 254:   80 e0           ldi r24, 0x00   ; 0
 256:   90 e0           ldi r25, 0x00   ; 0
 258:   0e 94 14 01     call    0x228   ; 0x228 <test_64>
 25c:   60 e0           ldi r22, 0x00   ; 0
 25e:   70 e0           ldi r23, 0x00   ; 0
 260:   cb 01           movw    r24, r22
 262:   0e 94 15 01     call    0x22a   ; 0x22a <test_32>
 266:   ff cf           rjmp    .-2         ; 0x266 <main+0x1e>

Observation

For 32 bits the correct code is generated. For 64 bits, no comparison is performed at all, the code compiles as if the result of the if was always false. A native GCC compiles both functions correctly.

You should probably avoid using 64 bits variables in your code.

The bug is now confirmed on the GCC bugtracker, you may follow it here:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85805

So at the time of writing all sufficiently modern avr-gcc versions are affected until this gets fixed.

Casting a constant to uint64_t is not a correct way of making uint64_t literal. You should use UINT64_C macro instead:

uint64_t bit_mask = UINT64_C(0b1111111110000000000000000000000000000000000000000000000000000000);

or if you prefer hex

uint64_t bit_mask = UINT64_C(0xFF80000000000000);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM