Embedded C question. program memory and Ram storage

Discussion in 'Embedded Systems and Microcontrollers' started by count_volta, Feb 27, 2014.

  1. count_volta

    Thread Starter Active Member

    Feb 4, 2009
    435
    24
    Hi, I'm not sure if this belongs in the Embedded forum because this is something that is more likely to be used by people working with micro-controllers with low memory. But it is a C question so.

    I'm working with an arm micro-controller. It has a parallel RGB LCD module inside it. I want to store images. Images are big. This uC has 256kb of Ram and 2MB of flash. Let me add that this uC is a Harvard Architecture. Separate program memory and RAM.

    I was looking at example code and saw something very interesting. It appears to be a method to force data into the program memory (flash) as opposed to RAM. This is definetely useful because I have 2MB of flash, which is plenty for many small RGB888 images. Here is the example code.

    there is a header file, and inside this header file the actual image in RGB565 format is stored. Its declared like this.

    Code ( (Unknown Language)):
    1. const uint32_t RGB565_480x272[65280] =
    2. {
    3. 0x7A537A53,
    4. 0x82538253, .............};
    Then in the .C file in main() they have the following
    Code ( (Unknown Language)):
    1. uint32_t LCD_buff_addr;
    2. LCD_buff_addr=  (uint32_t)&RGB565_480x272;
    3.  
    4.  
    The header file is included in the .C file. Also its kinda interesting that LCD_buff_addr is not a pointer, even though its storing an address.

    I never saw anything like this in any C programming books. Is this even legal C? Maybe a subset of C like C99? I tested it and it works.

    I looked at the .map file and lo and behold flash memory used 0x03fc2c bytes of data, not code. This is approximately 65280*4 which is the size of the data array. So its pretty clear that the array went entirely into flash memory.

    This is a great feature, and I have been looking for something like this for a while now, but I never saw this explained anywhere on the internet or my C book. I would like a formal explanation of why this is legal and why it works.

    Thank you.
    -Gene
     
  2. count_volta

    Thread Starter Active Member

    Feb 4, 2009
    435
    24
    On second thought, this definitely belongs in the Embedded Forum. Can a moderator please move it. Thank you.
     
  3. ActivePower

    Member

    Mar 15, 2012
    155
    23
    Which processor are you using?

    While I am as clueless as you are regarding the validity of the C code posted, as far as I am aware there isn't usually a one-size-fits-all solution to the constant data storing problem (as you might probably be aware too).

    Usually moving to program memory involves tricks with assembly and some linker script manipulation as discussed in this application note from Freescale (also a useful example is the 'pgmspace' library included with avr-gcc).

    I am sorry there is nothing more useful I can contribute on this topic but I do share your interest in knowing the answer if you get one. Probably someone with more C expertise could help.
     
  4. Papabravo

    Expert

    Feb 24, 2006
    10,152
    1,794
    The const keyword allows the compiler/linker to locate constant data in program space. There may or may not be a way to read data from program space. What the compiler will not let you do is MODIFY the contents of const data in program space.

    As has already been pointed out the AVR series does allow modification of program space memory but it is not as simple as setting up a pointer and just reading and writing.
     
  5. count_volta

    Thread Starter Active Member

    Feb 4, 2009
    435
    24
    I am working with ARM Cortex M4. I am using IAR Embedded Workbench IDE. Yea I guessed that you cant modify the contents, I did define it as a constant. That is okay. Often you need to store large amounts of constant data and only read parts of it to RAM and modify those parts from RAM.

    I think the trick comes from the fact that you define the array in a header file and never declare the array in a C file. You just store the address to that array and by some weird magic dont use a pointer to store that address. Pointer casting or something. That whole subject makes my head hurt. :D
     
  6. WBahn

    Moderator

    Mar 31, 2012
    17,768
    4,804
    Code ( (Unknown Language)):
    1.  
    2. uint32_t LCD_buff_addr;
    3. LCD_buff_addr=  (uint32_t)&RGB565_480x272;
    4.  
    Assuming that you are working with a 32-bit address space, this is legal.

    What is a pointer except a 32-bit unsigned integer whose value is interpreted as being an address? The key point is that it is a 32-bit unsigned integer.

    It is probably not necessary to even cast the address to a (uint32_t), but doing so probably suppresses a compiler warning.
     
  7. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    There's nothing exceptional or strange about this code:

    Code ( (Unknown Language)):
    1. const uint32_t RGB565_480x272[65280] =
    2. {
    3. 0x7A537A53,
    4. 0x82538253, .............};
    5.  
    6.  
    7. uint32_t LCD_buff_addr;
    8. LCD_buff_addr=  (uint32_t)&RGB565_480x272;
    9.  
    RGB565_480x272 is a simple one dimensional array of unsigned integers. Exactly what size depends on the definition of uint32 but that should be a 4 byte quantity for transportably.

    LCD_buff_addr is another unsigned integer, and it is assigned the address (&) of the array's first element (which is what C always thinks you mean when you give it an array without a member). This is possible because there is a cast to finagle an address into an integer.

    It would have been interesting to see how the address value in LCD_buff_addr was used in the code: the set-up is pretty standard stuff.

    NOW... every array defined in C resided in program memory: it must start there. If you say "but I told the compiler to put it in RAM" well yes it did, but ask yourself how did it know the values to put into RAM?

    (Answer: they had to start in program memory.)

    By adding the const keyword you allow C to keep the array in program memory, just the single copy, and read values as needed.

    Lastly, for those of you who are going to try this at home do yourself a favor and DO NOT dimension the array as in the first line: the compiler will quite happily count the elements and fill this in for you, and it is a far far better counter then any human is.
     
  8. WBahn

    Moderator

    Mar 31, 2012
    17,768
    4,804
    What else could it be? My understanding is that the whole point of adding the type designators such as uint32_t was that they were supposed to be unambiguous type definitions -- namely in this case an unsigned 32-bit integer -- as opposed to "unsigned int" which only had to have a minimum size (16 bits, I believe, in this case).

     
  9. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,912
    2,176
    Yes, but they all work with proper casting (that can cause trouble if misused).
    &RGB565_480x272 , RGB565_480x272 and &RGB565_480x272[0] all equate to the same address but have two different types.
    Pointer to the entire array, pointer to first element of array, pointer to first element of array so without the cast back to a uint32_t pointer
    &RGB565_480x272+1 will be different from RGB565_480x272+1 as &RGB565_480x272 is the address to an array, +1 would skip to the next array.
     
  10. WBahn

    Moderator

    Mar 31, 2012
    17,768
    4,804
    I guess you are right. I did some digging and for a constant (or global) array &ArrayName resolves to the same value as ArrayName though it has array type as opposed to the type of the things stored in the array. But while I agree that +1 would dereference to the first memory address beyond the array, that would only "skip to the next array" if we were talking about an array of these arrays. Otherwise, "next array" would seem to be meaningless.
     
  11. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    RGB565_480x272 is unambiguous, it is the address of the zero-th element, and has a type of pointer to uint_32_t.

    &RGB565_480x272 is less clear as the & is the address of variable operator, and RGB565_480x272 is not a variable. However, I believe C will ignore your bad format and hand you back the address of the zero-th element, and has a type of pointer to uint_32_t, same as not using the &

    As the type for both is pointer to uint_32_t adding one will point to the next element, not the next array.

    Perhaps some experimentation is in order here, I'll see if I have some play time this afternoon.
     
    Last edited: Feb 28, 2014
  12. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,912
    2,176
    This just shows that arrays and pointers are not the same and can be a tripping point for everyone. Your compiler might be 'helpful' and discard the & but it might not. http://www.c-faq.com/aryptr/aryptrequiv.html
    For most non-broken compilers (discarding & here is broken IMO but understandable because it's usually what programmers want to do):
    RGB565_480x272 is a compile time data LABEL or TOKEN that contains the location of where the data for RGB565_480x272 can be found but usually needs a offset type functionally to access the data in that hunk , so &RGB565_480x272+1 would/should be the next LABEL or TOKEN of a RGB565_480x272 type hunk of data.
    http://stackoverflow.com/questions/...nverted-into-a-pointer-in-c/17506236#17506236
     
    Last edited: Feb 28, 2014
  13. WBahn

    Moderator

    Mar 31, 2012
    17,768
    4,804
    As you say, RGB565_480x272 is not a variable -- and the compiler knows this. I think the standard defines the behavior in such situations to be that if you use the address operator on a constant or global (static in general?) array it returns the address of the first element, same as just using the name, but the type of pointer is an array pointer and the size of the data pointed to is the size of the entire array.
     
  14. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,912
    2,176

    Which is why it's usually not what you really want to do because that new memory location would be treated like an index into an undeclared array of arrays in memory if we preserved the pointer to array typing. This could be useful with a set of contiguous memory mapped but separate identical format device registers but is prone to implementation errors.
     
    Last edited: Feb 28, 2014
  15. WBahn

    Moderator

    Mar 31, 2012
    17,768
    4,804
    There are still some legitimate uses. Of course, the obvious one is if you really DID an array of arrays. But a couple of others is if you are allocating memory -- the sizeof() macro will return the number of bytes in the array and not just the number of bytes for each element of the array. Also, if you do a copy by dereferencing a pointer array it copies the entire array and not just the first element.

    But I do agree that MOST of the time that someone would do this it probably isn't what they really wanted and they probably are not aware of these more subtle behaviors (as, indeed, I wasn't -- or only partly -- until looking into this last night).
     
  16. nsaspook

    AAC Fanatic!

    Aug 27, 2009
    2,912
    2,176
    The fact that you can pervert C into all these contortions is the reason why it's hated and loved as a language.;) Arrays are a 'Jedi' mind-trick in C that seems to be a two part object sometimes (an illusion of pointer with block of memory for pointer context operations) and one part objects in other operations that need raw-memory operations (it's true implementation in C).

    Dennis Ritchie
     
    Last edited: Feb 28, 2014
Loading...