5

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 ?

Adam Flis
  • 51
  • 4
  • This code is ill-formed; pointer to volatile object cannot be passed to a function expecting pointer to non-volatile. If you don't see an error message then check your compiler switches. (And if you use a cast the error message may be suppressed but the behaviour is still undefined). I would suggest fixing this problem by having the function take `uint64_t` (no pointer), and also using a hex literal instead of binary literal as recommended by dasblinkenlight; then seeing if the problem persists. If it does then update the question to include a [MCVE](https://stackoverflow.com/help/mcve) – M.M Apr 29 '18 at 23:14
  • Could you tell which version of avr-gcc you have? (`avr-gcc --version`) I tried to report the problem on the GCC bug tracker, but they threw it back by that my version is old, and I really don't want to sink half a day in setting up one for which they might care (I develop on Linux where binaries apart from those provided by the distro are not that easy to acquire and set up). – Jubatian May 17 '18 at 07:08
  • Of course, my version is 4.8.1 – Adam Flis May 19 '18 at 10:14
  • @AdamFlis I have the behavior posted on GCC Bugzilla, and someone did a test with avr-gcc 8.1.0 on it finding that it is present even there ( https://www.avrfreaks.net/forum/gcc-bugs-could-someone-test-recent-version ). No response from the GCC team as of now. – Jubatian May 31 '18 at 09:05
  • Any response from GCC team or any solution to this problem found? – Adam Flis Jun 19 '18 at 10:57

3 Answers3

1

Just use the proper suffixes.

L for int32

LL for int64

UL for uint32

ULL for uint64

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Sorry, should've looked at the submission time before editing. Hope you don't mind! – a3f Apr 29 '18 at 22:04
  • Those are not the correct suffixes for the types listed. `L` is `long`, etc. Also it's not clear whether that has anything to do with the actual problem – M.M Apr 29 '18 at 23:07
  • M.M downvoter. This is for 8 bit uC. And the answer shows what suffixes to use for the particular integer. Non on the 64bit system only on the 8bit. M answer was edited by someone who added `_t` – 0___________ Apr 30 '18 at 00:01
1

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.

Jubatian
  • 2,171
  • 16
  • 22
  • It really looks like compiler bug, tahnk you a lot for checking it in assembly code. I get around by declare mask 0xFF800000UL as a globar variable. – Adam Flis May 19 '18 at 10:17
  • @AdamFlis It is now confirmed, this is a real compiler bug, I updated the answer accordingly. – Jubatian Jul 19 '18 at 05:54
0

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);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523