Embedded C question. program memory and Ram storage

Thread Starter

count_volta

Joined Feb 4, 2009
435
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.

Rich (BB code):
const uint32_t RGB565_480x272[65280] =
{
0x7A537A53,
0x82538253, .............};
Then in the .C file in main() they have the following
Rich (BB code):
uint32_t LCD_buff_addr; 
LCD_buff_addr=  (uint32_t)&RGB565_480x272;
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
 

ActivePower

Joined Mar 15, 2012
155
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.
 

Papabravo

Joined Feb 24, 2006
21,159
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.
 

Thread Starter

count_volta

Joined Feb 4, 2009
435
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
 

WBahn

Joined Mar 31, 2012
29,978
Rich (BB code):
uint32_t LCD_buff_addr; 
LCD_buff_addr=  (uint32_t)&RGB565_480x272;
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.
 

ErnieM

Joined Apr 24, 2011
8,377
There's nothing exceptional or strange about this code:

Rich (BB code):
const uint32_t RGB565_480x272[65280] =
{
0x7A537A53,
0x82538253, .............};


uint32_t LCD_buff_addr; 
LCD_buff_addr=  (uint32_t)&RGB565_480x272;
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.
 

WBahn

Joined Mar 31, 2012
29,978
There's nothing exceptional or strange about this code:

Rich (BB code):
const uint32_t RGB565_480x272[65280] =
{
0x7A537A53,
0x82538253, .............};


uint32_t LCD_buff_addr; 
LCD_buff_addr=  (uint32_t)&RGB565_480x272;
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.
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).

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.

Actually, I think there IS an error here (that I overlooked the first time, too).

The name of an array is a pointer to the array. But when you add the address operator to that you are now asking for the address where the pointer is stored.

I think it should be either

LCD_buff_addr= (uint32_t) RGB565_480x272;

or

LCD_buff_addr= (uint32_t) &RGB565_480x272[0];
 

nsaspook

Joined Aug 27, 2009
13,081
I think it should be either

LCD_buff_addr= (uint32_t) RGB565_480x272;

or

LCD_buff_addr= (uint32_t) &RGB565_480x272[0];
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.
 

WBahn

Joined Mar 31, 2012
29,978
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.
 

ErnieM

Joined Apr 24, 2011
8,377
&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.
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:

nsaspook

Joined Aug 27, 2009
13,081
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.
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
The exceptions are when the array is the operand of a sizeof or & operator, or is a string literal initializer for a character array.
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:

WBahn

Joined Mar 31, 2012
29,978
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.
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.
 

nsaspook

Joined Aug 27, 2009
13,081
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.

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:

WBahn

Joined Mar 31, 2012
29,978
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.
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).
 

nsaspook

Joined Aug 27, 2009
13,081
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).

C is quirky, flawed, and an enormous success.
Dennis Ritchie
 
Last edited:
Top