Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. volatile issue - repost

volatile issue - repost

Scheduled Pinned Locked Moved C / C++ / MFC
helphardwareperformance
21 Posts 6 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    Vaclav_
    wrote on last edited by
    #1

    I am reposting this in an attempt to focus on the issue. THIS IS NOT A BUMP! ( I know better ) I am well aware what volatile keyword does. In my application it prevents accidental access to memory AKA RPi hardware. The task is to map the hardware to memory. It works just fine resulting in "base" pointer to memory map. Now I need to add specific offset to the "base" and have the "result" to be of volatile type. The included snippet of test code does that with ONE exception - no matter what combination of code I use I cannot achieve VALID volatile result. It only works when no volatile keyword is used , none at all. I did try casts and dynamic_cast. I have optimizer turned off and obviously doing something wrong applying pointers. This code test snippet is located in main(), no other code is involved. It gives pointer to "base" ( value is irrelevant) and adds "int" offset and produces new "result" pointer.

        int \*base;
    int offset = 1;
    int \*result;
    result = base + offset;
    

    Any help will be greatly appreciated. Cheers Vaclav

    L C 2 Replies Last reply
    0
    • V Vaclav_

      I am reposting this in an attempt to focus on the issue. THIS IS NOT A BUMP! ( I know better ) I am well aware what volatile keyword does. In my application it prevents accidental access to memory AKA RPi hardware. The task is to map the hardware to memory. It works just fine resulting in "base" pointer to memory map. Now I need to add specific offset to the "base" and have the "result" to be of volatile type. The included snippet of test code does that with ONE exception - no matter what combination of code I use I cannot achieve VALID volatile result. It only works when no volatile keyword is used , none at all. I did try casts and dynamic_cast. I have optimizer turned off and obviously doing something wrong applying pointers. This code test snippet is located in main(), no other code is involved. It gives pointer to "base" ( value is irrelevant) and adds "int" offset and produces new "result" pointer.

          int \*base;
      int offset = 1;
      int \*result;
      result = base + offset;
      

      Any help will be greatly appreciated. Cheers Vaclav

      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #2

      You have not declared result as volatile, and it is not clear what you are actually trying to achieve. How do you determine that the variable is not volatile?

      C L 2 Replies Last reply
      0
      • V Vaclav_

        I am reposting this in an attempt to focus on the issue. THIS IS NOT A BUMP! ( I know better ) I am well aware what volatile keyword does. In my application it prevents accidental access to memory AKA RPi hardware. The task is to map the hardware to memory. It works just fine resulting in "base" pointer to memory map. Now I need to add specific offset to the "base" and have the "result" to be of volatile type. The included snippet of test code does that with ONE exception - no matter what combination of code I use I cannot achieve VALID volatile result. It only works when no volatile keyword is used , none at all. I did try casts and dynamic_cast. I have optimizer turned off and obviously doing something wrong applying pointers. This code test snippet is located in main(), no other code is involved. It gives pointer to "base" ( value is irrelevant) and adds "int" offset and produces new "result" pointer.

            int \*base;
        int offset = 1;
        int \*result;
        result = base + offset;
        

        Any help will be greatly appreciated. Cheers Vaclav

        C Offline
        C Offline
        CPallini
        wrote on last edited by
        #3

        Richard is right; what you expect as a 'valid volatile result'? How do you check it?

        V 1 Reply Last reply
        0
        • L Lost User

          You have not declared result as volatile, and it is not clear what you are actually trying to achieve. How do you determine that the variable is not volatile?

          C Offline
          C Offline
          CPallini
          wrote on last edited by
          #4

          Indeed. :thumbsup:

          1 Reply Last reply
          0
          • C CPallini

            Richard is right; what you expect as a 'valid volatile result'? How do you check it?

            V Offline
            V Offline
            Vaclav_
            wrote on last edited by
            #5
                cout << "base    " << base << '\\n';
            cout << "offset  " << offset << '\\n';
            cout << "result  " << result << '\\n';
            

            Please read the OP again. Here is a repeat. Base gets set to a pointer, simple , right? I add an "offset" to it, still pretty straight. I expect the result pointer to be base plus offset. It is AS LONG AS NONE of the variables is typed with "volatile" keyword.

            L 2 Replies Last reply
            0
            • V Vaclav_
                  cout << "base    " << base << '\\n';
              cout << "offset  " << offset << '\\n';
              cout << "result  " << result << '\\n';
              

              Please read the OP again. Here is a repeat. Base gets set to a pointer, simple , right? I add an "offset" to it, still pretty straight. I expect the result pointer to be base plus offset. It is AS LONG AS NONE of the variables is typed with "volatile" keyword.

              L Offline
              L Offline
              leon de boer
              wrote on last edited by
              #6

              The problem is called C point arithmetic You have a pointer to an integer so if you add one to it then it will add either 4 or 8 bytes depending the size of int on your C compiler. The only way you can add one physical byte is have a uint8_t* pointer

              In vino veritas

              1 Reply Last reply
              0
              • V Vaclav_
                    cout << "base    " << base << '\\n';
                cout << "offset  " << offset << '\\n';
                cout << "result  " << result << '\\n';
                

                Please read the OP again. Here is a repeat. Base gets set to a pointer, simple , right? I add an "offset" to it, still pretty straight. I expect the result pointer to be base plus offset. It is AS LONG AS NONE of the variables is typed with "volatile" keyword.

                L Offline
                L Offline
                leon de boer
                wrote on last edited by
                #7

                Your current code as posted will print some random value because you haven't set the variable base to a start value. Specifically it will be what junk is the memory address it gets assigned, basic rules of C. Could you please try this code

                #include
                int main (void){
                int *base = 0;
                int offset = 1;
                volatile int *result;
                result = base + offset;
                printf("Volatile result pointer = %p\n", result);
                if ((int)result != sizeof(int)) printf("compiler is broken\n");
                }

                It should return 2, 4 or 8 for the result depending on the size of int on your compiler As you are doing pointer addition the offset is defined as the sizeof(int) by C standards. If you see the message "compiler is broken" there are 10 different gcc compilers for the Pi use another one or change version of your current. GCC being a public domain support compiler does get bugs in versions from time to time. As a last comment the above possible multiple answers is specifically why you shouldn't use int for hardware registers. The registers on the Pi have to be accessed as 32bit, so the only safe way to do it is uint32_t The following code will give an answer of 4 on EVERY C COMPILER, it is safe and portable and even works on the Pi3 64bit compilers My advice is DO IT THIS WAY.

                #include
                #include
                int main (void){
                uint32_t *base = 0;
                uint32_t offset = 1;
                volatile uint32_t *result;
                result = base + offset;
                printf("Volatile result pointer = %p\n", result);
                }

                In vino veritas

                V 1 Reply Last reply
                0
                • L leon de boer

                  Your current code as posted will print some random value because you haven't set the variable base to a start value. Specifically it will be what junk is the memory address it gets assigned, basic rules of C. Could you please try this code

                  #include
                  int main (void){
                  int *base = 0;
                  int offset = 1;
                  volatile int *result;
                  result = base + offset;
                  printf("Volatile result pointer = %p\n", result);
                  if ((int)result != sizeof(int)) printf("compiler is broken\n");
                  }

                  It should return 2, 4 or 8 for the result depending on the size of int on your compiler As you are doing pointer addition the offset is defined as the sizeof(int) by C standards. If you see the message "compiler is broken" there are 10 different gcc compilers for the Pi use another one or change version of your current. GCC being a public domain support compiler does get bugs in versions from time to time. As a last comment the above possible multiple answers is specifically why you shouldn't use int for hardware registers. The registers on the Pi have to be accessed as 32bit, so the only safe way to do it is uint32_t The following code will give an answer of 4 on EVERY C COMPILER, it is safe and portable and even works on the Pi3 64bit compilers My advice is DO IT THIS WAY.

                  #include
                  #include
                  int main (void){
                  uint32_t *base = 0;
                  uint32_t offset = 1;
                  volatile uint32_t *result;
                  result = base + offset;
                  printf("Volatile result pointer = %p\n", result);
                  }

                  In vino veritas

                  V Offline
                  V Offline
                  Vaclav_
                  wrote on last edited by
                  #8

                  OK, it's late but here is the latest. This code with result declared as volatile PRINTS the correct value of "result". However "cout" outputs value of 1 - that is ONE. After removing "volatile" from declaration of result - cout will output 4. I'll let you gentlemen decide where is my problem , it the meantime I am initializing the pointer to zero and will be checking for absence of zero and hoping computer is adding the values correctly. BTW the correct additions of both values was never my concern , hence I did not care about the uninitialized value of "base" and was using "int" to test. .

                  /*
                  uint32_t *base = 0;
                  uint32_t offset = 1;
                  volatile uint32_t *result;
                  result = base + offset;
                  printf("Volatile result pointer = %p\n", result);

                  */

                     uint32\_t \*base = 0;
                     uint32\_t  offset = 1;
                     volatile uint32\_t  \*result;
                     result = base + offset;
                  
                     cout << "base    " << base << '\\n';
                     cout << "offset  " << offset << '\\n';
                  

                  THIS LINE IS AN UNRESOLVED ISSUE
                  OUTPUT IS CORRECT WHEN NO VOLATILE KEYWORD IS USED
                  result = 1 WHEN VOLATILE IS USED

                     cout << "result  " << result << '\\n';
                  

                  this always prints correct value

                     printf("Volatile result pointer = %p\\n", result);
                  

                  PS ill try "bad compiler" in the morning.

                  L 1 Reply Last reply
                  0
                  • V Vaclav_

                    OK, it's late but here is the latest. This code with result declared as volatile PRINTS the correct value of "result". However "cout" outputs value of 1 - that is ONE. After removing "volatile" from declaration of result - cout will output 4. I'll let you gentlemen decide where is my problem , it the meantime I am initializing the pointer to zero and will be checking for absence of zero and hoping computer is adding the values correctly. BTW the correct additions of both values was never my concern , hence I did not care about the uninitialized value of "base" and was using "int" to test. .

                    /*
                    uint32_t *base = 0;
                    uint32_t offset = 1;
                    volatile uint32_t *result;
                    result = base + offset;
                    printf("Volatile result pointer = %p\n", result);

                    */

                       uint32\_t \*base = 0;
                       uint32\_t  offset = 1;
                       volatile uint32\_t  \*result;
                       result = base + offset;
                    
                       cout << "base    " << base << '\\n';
                       cout << "offset  " << offset << '\\n';
                    

                    THIS LINE IS AN UNRESOLVED ISSUE
                    OUTPUT IS CORRECT WHEN NO VOLATILE KEYWORD IS USED
                    result = 1 WHEN VOLATILE IS USED

                       cout << "result  " << result << '\\n';
                    

                    this always prints correct value

                       printf("Volatile result pointer = %p\\n", result);
                    

                    PS ill try "bad compiler" in the morning.

                    L Offline
                    L Offline
                    leon de boer
                    wrote on last edited by
                    #9

                    That ostream object is a pile of overloaded functions on the operator << std::basic_ostream::operator<< - cppreference.com[^] NOTE FROM ABOVE .. I underlined the statement parts:

                    Quote:

                    There are no overload for pointers to non-static member, pointers to volatile, or function pointers (other than the ones with signatures accepted by the (10-12) overloads). Attempting to output such objects invokes implicit conversion to bool, and, for any non-null pointer value, the value 1 is printed

                    I avoid the thing like the plague because you have to know it's behaviour .. so yes a non zero volatile pointer will always print as 1 .. it tells you that. So the good news is there is nothing wrong with your compiler it is following the c++ standard. If you don't like it take it up with the c++ standard comittee. Ostream has other strange (but documented) filter behaviour. For example with char* pointers it has a special overload expecting it to be a pointer to a null terminated string

                    cout << (char*)0x41 << endl; // This will crash and burn .. it expects a null terminated string from a char*
                    cout << (void*)0x41 << endl; // This will print fine

                    You could try putting a void* typecast so it goes thru the void* pointer overload ... like so should work

                    cout << "result " << (void*)result << '\n';

                    If you are intending to use ostream you will need to learn it's ins and outs and quirky behaviour. Anyhow it's not a big issue and rolls around ostream behaviour .. so don't make it more than what it is. Your pointers are clearly adding properly you were just having a display issue with ostream :-)

                    In vino veritas

                    V 1 Reply Last reply
                    0
                    • L Lost User

                      You have not declared result as volatile, and it is not clear what you are actually trying to achieve. How do you determine that the variable is not volatile?

                      L Offline
                      L Offline
                      leon de boer
                      wrote on last edited by
                      #10

                      It's all cleared up .. ostream has a behaviour with volatile pointers

                      Quote:

                      There are no overload for pointers to non-static member, pointers to volatile, or function pointers (other than the ones with signatures accepted by the (10-12) overloads). Attempting to output such objects invokes implicit conversion to bool, and, for any non-null pointer value, the value 1 is printed

                      It prints 1 because the volatile pointer he makes is non zero and that is what the ostream standard says it should do. I must admit I hadn't run across it because I rarely use the ostream to output because of quirks like this.

                      In vino veritas

                      L C 2 Replies Last reply
                      0
                      • L leon de boer

                        It's all cleared up .. ostream has a behaviour with volatile pointers

                        Quote:

                        There are no overload for pointers to non-static member, pointers to volatile, or function pointers (other than the ones with signatures accepted by the (10-12) overloads). Attempting to output such objects invokes implicit conversion to bool, and, for any non-null pointer value, the value 1 is printed

                        It prints 1 because the volatile pointer he makes is non zero and that is what the ostream standard says it should do. I must admit I hadn't run across it because I rarely use the ostream to output because of quirks like this.

                        In vino veritas

                        L Offline
                        L Offline
                        Lost User
                        wrote on last edited by
                        #11

                        I must be missing something but I cannot see any reference to ostream in OP's question.

                        1 Reply Last reply
                        0
                        • L leon de boer

                          It's all cleared up .. ostream has a behaviour with volatile pointers

                          Quote:

                          There are no overload for pointers to non-static member, pointers to volatile, or function pointers (other than the ones with signatures accepted by the (10-12) overloads). Attempting to output such objects invokes implicit conversion to bool, and, for any non-null pointer value, the value 1 is printed

                          It prints 1 because the volatile pointer he makes is non zero and that is what the ostream standard says it should do. I must admit I hadn't run across it because I rarely use the ostream to output because of quirks like this.

                          In vino veritas

                          C Offline
                          C Offline
                          CPallini
                          wrote on last edited by
                          #12

                          Wow, nice to know.

                          1 Reply Last reply
                          0
                          • L leon de boer

                            That ostream object is a pile of overloaded functions on the operator << std::basic_ostream::operator<< - cppreference.com[^] NOTE FROM ABOVE .. I underlined the statement parts:

                            Quote:

                            There are no overload for pointers to non-static member, pointers to volatile, or function pointers (other than the ones with signatures accepted by the (10-12) overloads). Attempting to output such objects invokes implicit conversion to bool, and, for any non-null pointer value, the value 1 is printed

                            I avoid the thing like the plague because you have to know it's behaviour .. so yes a non zero volatile pointer will always print as 1 .. it tells you that. So the good news is there is nothing wrong with your compiler it is following the c++ standard. If you don't like it take it up with the c++ standard comittee. Ostream has other strange (but documented) filter behaviour. For example with char* pointers it has a special overload expecting it to be a pointer to a null terminated string

                            cout << (char*)0x41 << endl; // This will crash and burn .. it expects a null terminated string from a char*
                            cout << (void*)0x41 << endl; // This will print fine

                            You could try putting a void* typecast so it goes thru the void* pointer overload ... like so should work

                            cout << "result " << (void*)result << '\n';

                            If you are intending to use ostream you will need to learn it's ins and outs and quirky behaviour. Anyhow it's not a big issue and rolls around ostream behaviour .. so don't make it more than what it is. Your pointers are clearly adding properly you were just having a display issue with ostream :-)

                            In vino veritas

                            V Offline
                            V Offline
                            Vaclav_
                            wrote on last edited by
                            #13

                            Leon and everybody, thanks for all your help. Now I can sleep better. One more "thing" - if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1. So - is the BCM2835 memory "only" 8 bits wide? Cheers and thanks again. Vaclav Somebody is trying to tell me something - I just got very close hit by lightning and lost power so I have to start over. This "byte" reference make no sense to me. If uint32 is 4 bytes wide - why did they name it "32"? uint32 IS 32 bits variable and if adding 1 uint32 variable to the original pointer comes up with ADDRESS offset of 4 that means there are 4 8 bits wide memory locations AKA memory is 8 bits wide. I know I am not the best communicator - but I never did question pointer arithmetic. I do not get why some people always volunteer to "teach" and then complain about it. Ever since I join this forum I appreciated straight talk, now it looks very similar to forum ,which should remain nameless, - everybody is a teacher and fails to read the posts. Of course when I express my feelings I get flamed. I better quit - the storm is getting too close.

                            C L P L 4 Replies Last reply
                            0
                            • V Vaclav_

                              Leon and everybody, thanks for all your help. Now I can sleep better. One more "thing" - if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1. So - is the BCM2835 memory "only" 8 bits wide? Cheers and thanks again. Vaclav Somebody is trying to tell me something - I just got very close hit by lightning and lost power so I have to start over. This "byte" reference make no sense to me. If uint32 is 4 bytes wide - why did they name it "32"? uint32 IS 32 bits variable and if adding 1 uint32 variable to the original pointer comes up with ADDRESS offset of 4 that means there are 4 8 bits wide memory locations AKA memory is 8 bits wide. I know I am not the best communicator - but I never did question pointer arithmetic. I do not get why some people always volunteer to "teach" and then complain about it. Ever since I join this forum I appreciated straight talk, now it looks very similar to forum ,which should remain nameless, - everybody is a teacher and fails to read the posts. Of course when I express my feelings I get flamed. I better quit - the storm is getting too close.

                              C Offline
                              C Offline
                              CPallini
                              wrote on last edited by
                              #14

                              Quote:

                              So - is the BCM2835 memory "only" 8 bits wide?

                              What do you mean with that?

                              1 Reply Last reply
                              0
                              • V Vaclav_

                                Leon and everybody, thanks for all your help. Now I can sleep better. One more "thing" - if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1. So - is the BCM2835 memory "only" 8 bits wide? Cheers and thanks again. Vaclav Somebody is trying to tell me something - I just got very close hit by lightning and lost power so I have to start over. This "byte" reference make no sense to me. If uint32 is 4 bytes wide - why did they name it "32"? uint32 IS 32 bits variable and if adding 1 uint32 variable to the original pointer comes up with ADDRESS offset of 4 that means there are 4 8 bits wide memory locations AKA memory is 8 bits wide. I know I am not the best communicator - but I never did question pointer arithmetic. I do not get why some people always volunteer to "teach" and then complain about it. Ever since I join this forum I appreciated straight talk, now it looks very similar to forum ,which should remain nameless, - everybody is a teacher and fails to read the posts. Of course when I express my feelings I get flamed. I better quit - the storm is getting too close.

                                L Offline
                                L Offline
                                Lost User
                                wrote on last edited by
                                #15

                                The uint32_t type (the clue is in the name) is 32 bits wide, or 4 bytes. The uint8_t is 1 byte. When you add 1 to a pointer it will increment it by the size of the object(s) pointed at. So a pointer to a uint32_t will increment by 4 bytes when adding 1, in order to point to the next variable of that type. Pointer arithmetic in C and C++ has always done this.

                                1 Reply Last reply
                                0
                                • V Vaclav_

                                  Leon and everybody, thanks for all your help. Now I can sleep better. One more "thing" - if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1. So - is the BCM2835 memory "only" 8 bits wide? Cheers and thanks again. Vaclav Somebody is trying to tell me something - I just got very close hit by lightning and lost power so I have to start over. This "byte" reference make no sense to me. If uint32 is 4 bytes wide - why did they name it "32"? uint32 IS 32 bits variable and if adding 1 uint32 variable to the original pointer comes up with ADDRESS offset of 4 that means there are 4 8 bits wide memory locations AKA memory is 8 bits wide. I know I am not the best communicator - but I never did question pointer arithmetic. I do not get why some people always volunteer to "teach" and then complain about it. Ever since I join this forum I appreciated straight talk, now it looks very similar to forum ,which should remain nameless, - everybody is a teacher and fails to read the posts. Of course when I express my feelings I get flamed. I better quit - the storm is getting too close.

                                  P Offline
                                  P Offline
                                  Peter_in_2780
                                  wrote on last edited by
                                  #16

                                  Vaclav_ wrote:

                                  if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1.

                                  Pointer arithmetic in C.

                                  Software rusts. Simon Stephenson, ca 1994. So does this signature. me, 2012

                                  1 Reply Last reply
                                  0
                                  • V Vaclav_

                                    Leon and everybody, thanks for all your help. Now I can sleep better. One more "thing" - if I use uint32_t adding 1 results in pointer being incremented by 4. If I use uint8_t same process increments the pointer by 1. So - is the BCM2835 memory "only" 8 bits wide? Cheers and thanks again. Vaclav Somebody is trying to tell me something - I just got very close hit by lightning and lost power so I have to start over. This "byte" reference make no sense to me. If uint32 is 4 bytes wide - why did they name it "32"? uint32 IS 32 bits variable and if adding 1 uint32 variable to the original pointer comes up with ADDRESS offset of 4 that means there are 4 8 bits wide memory locations AKA memory is 8 bits wide. I know I am not the best communicator - but I never did question pointer arithmetic. I do not get why some people always volunteer to "teach" and then complain about it. Ever since I join this forum I appreciated straight talk, now it looks very similar to forum ,which should remain nameless, - everybody is a teacher and fails to read the posts. Of course when I express my feelings I get flamed. I better quit - the storm is getting too close.

                                    L Offline
                                    L Offline
                                    leon de boer
                                    wrote on last edited by
                                    #17

                                    Quote:

                                    If uint32 is 4 bytes wide - why did they name it "32"?

                                    4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t :-) 8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes. Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard. So we can basically say uint32_t is always 4 bytes, uint64_t is always 8 bytes on any C/C++ compiler Yes correct it adds by 4 and as EVERYTHING on the PI 1,2 or 3 is aligned to 4 bytes they are an ARM RISC processor and unaligned access is less than desirable. It is actually illegal and not predictable on many earlier ARM CPU's. On your Pi3 which is 4 x Cortexa53 cores unaligned access is generally configured to raise an error exception but can handle it if required. Now here is the big but, there is a MASSIVE penalty for unaligned access not just from the CPU but the caches don't work properly. The bottom line however is every hardware register is aligned to 4 bytes (that is it's divisible by 4) and can only be written with 4 bytes at once (even in 64 bit mode). So the behaviour of uint32_t pointer addition is desirable and ideal. There is one further thing we generally do to make it compatible to for 64bit code for the Pi3 which is we declare the pointers and/or structs representing hardware as align 4 with a GCC attribute. A typical sample will look like this

                                    /*--------------------------------------------------------------------------}
                                    { RASPBERRY PI SYSTEM TIMER HARDWARE REGISTERS - BCM2835 Manual Section 12 }
                                    {--------------------------------------------------------------------------*/
                                    struct __attribute__((__packed__, aligned(4))) SystemTimerRegisters {
                                    uint32_t ControlStatus; // 0x00
                                    uint32_t TimerLo; // 0x04
                                    uint32_t TimerHi; // 0x08
                                    uint32_t Compare0; // 0x0C
                                    uint32_t Compare1; // 0x10
                                    uint32_t Compare2; // 0x14
                                    uint32_t Compare3; // 0x18
                                    };

                                    The reason is that the cortexa53 in AARCH64 mode natively runs everything in 8 byte alignment which it calls a word. It does however have the ability to do half word (4 byte writes) and the aligned(4) is the way to tell it to use half word access. The packed attribute is because without that the struct will have uint32_t spaced every 8 bytes (so 4 bytes uint32_t then 4 byte gap) A volatile pointer to a hardware register is simi

                                    V S 2 Replies Last reply
                                    0
                                    • L leon de boer

                                      Quote:

                                      If uint32 is 4 bytes wide - why did they name it "32"?

                                      4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t :-) 8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes. Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard. So we can basically say uint32_t is always 4 bytes, uint64_t is always 8 bytes on any C/C++ compiler Yes correct it adds by 4 and as EVERYTHING on the PI 1,2 or 3 is aligned to 4 bytes they are an ARM RISC processor and unaligned access is less than desirable. It is actually illegal and not predictable on many earlier ARM CPU's. On your Pi3 which is 4 x Cortexa53 cores unaligned access is generally configured to raise an error exception but can handle it if required. Now here is the big but, there is a MASSIVE penalty for unaligned access not just from the CPU but the caches don't work properly. The bottom line however is every hardware register is aligned to 4 bytes (that is it's divisible by 4) and can only be written with 4 bytes at once (even in 64 bit mode). So the behaviour of uint32_t pointer addition is desirable and ideal. There is one further thing we generally do to make it compatible to for 64bit code for the Pi3 which is we declare the pointers and/or structs representing hardware as align 4 with a GCC attribute. A typical sample will look like this

                                      /*--------------------------------------------------------------------------}
                                      { RASPBERRY PI SYSTEM TIMER HARDWARE REGISTERS - BCM2835 Manual Section 12 }
                                      {--------------------------------------------------------------------------*/
                                      struct __attribute__((__packed__, aligned(4))) SystemTimerRegisters {
                                      uint32_t ControlStatus; // 0x00
                                      uint32_t TimerLo; // 0x04
                                      uint32_t TimerHi; // 0x08
                                      uint32_t Compare0; // 0x0C
                                      uint32_t Compare1; // 0x10
                                      uint32_t Compare2; // 0x14
                                      uint32_t Compare3; // 0x18
                                      };

                                      The reason is that the cortexa53 in AARCH64 mode natively runs everything in 8 byte alignment which it calls a word. It does however have the ability to do half word (4 byte writes) and the aligned(4) is the way to tell it to use half word access. The packed attribute is because without that the struct will have uint32_t spaced every 8 bytes (so 4 bytes uint32_t then 4 byte gap) A volatile pointer to a hardware register is simi

                                      V Offline
                                      V Offline
                                      Vaclav_
                                      wrote on last edited by
                                      #18

                                      Thanks Leon it always makes more sense after your posts. And as far as my project goes I got too confident and ended up with a mess , as always. Luckily I had a backup so no lost. If you recall I had some issues with memory mapping. Ill have to prove this to myself , but it seems that when the memory map uses /dev/gpiomem I cannot map other devices such as SPI. Makes sense when you pointed out that each "/dev" has its own file. I think it is just the way the BCM library tries to be "universal" and does everything in one file. I actually only need SPI,I2C and GPIO. And I have a class which handless the GPIO nicely. Cheers Vaclav

                                      1 Reply Last reply
                                      0
                                      • L leon de boer

                                        Quote:

                                        If uint32 is 4 bytes wide - why did they name it "32"?

                                        4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t :-) 8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes. Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard. So we can basically say uint32_t is always 4 bytes, uint64_t is always 8 bytes on any C/C++ compiler Yes correct it adds by 4 and as EVERYTHING on the PI 1,2 or 3 is aligned to 4 bytes they are an ARM RISC processor and unaligned access is less than desirable. It is actually illegal and not predictable on many earlier ARM CPU's. On your Pi3 which is 4 x Cortexa53 cores unaligned access is generally configured to raise an error exception but can handle it if required. Now here is the big but, there is a MASSIVE penalty for unaligned access not just from the CPU but the caches don't work properly. The bottom line however is every hardware register is aligned to 4 bytes (that is it's divisible by 4) and can only be written with 4 bytes at once (even in 64 bit mode). So the behaviour of uint32_t pointer addition is desirable and ideal. There is one further thing we generally do to make it compatible to for 64bit code for the Pi3 which is we declare the pointers and/or structs representing hardware as align 4 with a GCC attribute. A typical sample will look like this

                                        /*--------------------------------------------------------------------------}
                                        { RASPBERRY PI SYSTEM TIMER HARDWARE REGISTERS - BCM2835 Manual Section 12 }
                                        {--------------------------------------------------------------------------*/
                                        struct __attribute__((__packed__, aligned(4))) SystemTimerRegisters {
                                        uint32_t ControlStatus; // 0x00
                                        uint32_t TimerLo; // 0x04
                                        uint32_t TimerHi; // 0x08
                                        uint32_t Compare0; // 0x0C
                                        uint32_t Compare1; // 0x10
                                        uint32_t Compare2; // 0x14
                                        uint32_t Compare3; // 0x18
                                        };

                                        The reason is that the cortexa53 in AARCH64 mode natively runs everything in 8 byte alignment which it calls a word. It does however have the ability to do half word (4 byte writes) and the aligned(4) is the way to tell it to use half word access. The packed attribute is because without that the struct will have uint32_t spaced every 8 bytes (so 4 bytes uint32_t then 4 byte gap) A volatile pointer to a hardware register is simi

                                        S Offline
                                        S Offline
                                        supercat9
                                        wrote on last edited by
                                        #19

                                        leon de boer wrote:

                                        4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t :) 8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes. Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard.

                                        The size of "uint32_t" is fixed at four bytes on compilers which implement uint8_t. A conforming compiler where "char" is 16 bits (e.g. because it targets a platform where memory can only be written in 16-bit chunks) and either "int" or "long" is a 32-bit two's-complement integer without padding bits must, however, define type `uint16_t` with a size of 1 and `uint32_t` with a size of two.

                                        L 1 Reply Last reply
                                        0
                                        • S supercat9

                                          leon de boer wrote:

                                          4 bytes each of 8 bits = 4 x 8 = 32 bits .. its unsigned and a type .... so uint32_t :) 8 bytes of 8 bits = 8 x 8 = 64 bits .... so uint64_t also means 8 bytes. Unlike int, long etc the sizes in bits and bytes are fixed in stone on every C compiler that meets the standard.

                                          The size of "uint32_t" is fixed at four bytes on compilers which implement uint8_t. A conforming compiler where "char" is 16 bits (e.g. because it targets a platform where memory can only be written in 16-bit chunks) and either "int" or "long" is a 32-bit two's-complement integer without padding bits must, however, define type `uint16_t` with a size of 1 and `uint32_t` with a size of two.

                                          L Offline
                                          L Offline
                                          leon de boer
                                          wrote on last edited by
                                          #20

                                          What you have said is misleading you are just talking about a compiler that use the clause that says

                                          Quote:

                                          Various implementations of C and C++ reserve 8, 9, 16, 32, or 36 bits for the storage of a byte

                                          So a byte in your case is defined as 16 bits and that is why the uint8_t type doesn't exist for you. So yes a uint32_t in your case would be 2 bytes (because your byte is 16 bits) and all you have proven is what we have long argued to the standards committee that C has butchered the word byte it should have been called register. I mean all you have is a 16 bit register that can't break to 8 bits that is creating the problem. I have always wanted to see a C compiler with 9 bit bytes because I think that would make for some real fun :-) So lets reword it uint32_t is always 4 x uint8_t assuming both are implemented because we have to avoid using the word byte because it's been butchered in C.

                                          In vino veritas

                                          S 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups