Hi,
I'm wondering if I am doing something wrong here. I am trying to store an array for a large 256 byte look-up table.
I have a pretty small 8051 MCU of 8 KB flash memory (IROM), 256 bytes internal RAM (IRAM) and 256 bytes on-chip external RAM (XRAM).
Prior to adding this look-up table, my current build from Keil reports:
"Program Size: data=54.1 xdata=0 code=1984"
I take that to mean I am using 54 bytes of IRAM, 0 bytes of XRAM, and 1984 bytes of IROM?
Here is the variable declaration I am using:
code unsigned char CRC8LookupTable[256];
When I define the CRC8LookupTable and populate values, I get the Segment Too Large error.
If you use "code" doesn't that store the values in ROM, which I should have plenty of space for?
Is there something else I need to add to that variable declaration?
Thanks -Tim
This is what I am trying to save to the code area:
One thing I notice is that I can put in say 16 of those values at a time, but if I just fill the whole it drops the "DATA: Segment Too Large Error".
Is the issue that I need to parse in the array values due to RAM limits?
CRC8LookupTable[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 };
So, I see what is happening, but I don't know why...
When I define the array values in the .c, even though I declared the array as the code type, the array values are still being saved to the internal ram (data)...
What gives? Am I doing something wrong?
This particular MCU vendor, I've had some issues with their chips (seriously...). Am I doing something wrong here with the Keil syntax?
here is what I had that was not working:
crc.h
unsigned char code CRC8LookupTable[256];
crc.c
CRC8LookupTable[256] = ...
But your definition there does not match the declaration - the type & 'code' qualifier are missing!
Surely, it should have been:
extern unsigned char code CRC8LookupTable[256];
unsigned char code CRC8LookupTable[256] = ...
@Andrew
The behavior is really weird. I'm not a wizard at C or compiler mechanics.
If you do not declare and define the array like this.
unsigned char code CRC8LookupTable[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 };
You can declare like this in the header:
But unless you define & declare the array in the source, it doesn't place the array in the flash memory... It is almost like the header declaration has no bearing on getting the array into the flash.
To me, I kind of expect that you would declare the array with the code statement, and then define it in the source code like any other variable. But, definitely not. I'm sure there is a compiler reason for this.
It seems like the compiler ignores the declaration in the header if it has code in the declaration.
Adding the declaration to the header, maybe it helps the compiler? But seems like the compiler expects the declaration and definition in the source.
Just for sake of completeness, Andrew is right about the header, it is not ignored. I know because I just spent 15 minutes trying to massage the declaration and definition correctly.
To call the table from other files here is the final declaration and definitions I needed to use.
//This holds the 256 possible byte combinations for the CRC8 polynomial. extern unsigned char code CRC8LookupTable[256];
//This stores the lookup table in the program flash memory through the "code" unsigned char code CRC8LookupTable[256] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 };
Bizarre, but it works and I can call it from other source files.
Bizarre, but it works and I can call it from other source files. what is bizarre about things working when they are done right?
I do not know if, for some reason, your original mess would be supposed to work, but a compiler that fails because of code in a header file is, in my opinion, bug free
@Erik -- If you were building a compiler, why would you treat the case where you put code and require that to be declared and defined in two places?
When are other variables used like that? I'm sure there is a reason from a compiler perspective, but from an end user perspective when you shift around fundamental behaviors like that, it seems a bizarre.
Have you ever read the definition of bizarre? It doesn't imply standard of "rightness" or "wrongness" -- it is just unusual, as in atypical usage of a variable in C...
When you say things are "right" with Keil's compiler -- it's pretty relative. I'm not getting metaphysical on a compiler. We aren't just a Keil C51 shop in my business.
PIC's compiler at least provides an error that you need to declare and initialize at the same time if you use const. In that sense, as being more clear does make it their compiler "better"? Maybe Keil could add a similar warning to their compiler, and make it easier to use. Doesn't seem like a crack-pot idea. Easier products for end-users, more end users who make more money using the tool... seems pretty straightforward.
Geez man - I don't get your snark. Do you work for Keil?
A compiler is a relative construct -- likewise conforming C standards. Whatever -- just a tool for a job. I don't have any religious zeal to software languages and standards. I try to write good code that is portable and re-usable. But I am all moving bits around for money. I'm surely not putting in a CRC routine, on a beautiful spring, just because.
I literally don't care how Keil wants to implement things. Seriously. Their product, their compiler -- whatever. They can do it anyway they want. I would wish maybe they illustrated this point a bit better in their documentation, and maybe called it out with a more direct warning in their compiler.
End of the day -- we are building a cheap device using an 8051 and we plan to move thousand of units a month using these 8051s and save a bundle of money versus a PIC based implementation on each unit. If I have to fiddle with some Keil C51 semantics, no biggie.
Forum post is here for the next user, to save them some time. Mission accomplished. I'm out. I won't respond to anymore.
Way to be a nice neighbor, man. Remind me NOT TO EVER ASK YOU FOR PAID CONSULTING. You never know who is on the other end of the forum. Maybe we need some really tight assembly for one of these low-end MCUs, we pay $130/hr for good assembly all day long...
I don't see how that is in any way bizarre?
That is the standard way that you would declare & define a variable in 'C'.
The Keil-specific 'code' qualifier is applied in exactly the same way as the standard 'volatile' and 'const' qualifiers.
c-faq.com/.../decldef.html
Qualifiers. Good word to learn, didn't know that. I told you I am not a C wizard.
Okay -- so, for volatile & extern and other qualifiers. You don't re-use them when you define the variables in the source, right?
I only use the qualifier for volatile in the declaration, right? (add in an extern if you want too!)
volatile unsigned char SomeData;
Now we go to the source, no more qualifier required since it is in the declaration in the header.
unsigned char SomeData = 1;
Great we got variable we can use safely with interrupts. But volatile qualifier required in the definition? How is that not bizarre, how qualifiers are used differently between these cases?
I'm sure their is a reason for the compilation process, but I'm talking about end user experience here of someone typing in text.
Then you have the example of CODE as a qualifier. It has to be used as a qualifier in both the declaration and definition unlike volatile?
For my knowledge, does Keil C51 treat the XDATA qualifier the same way?
I'm sure one day I'll need to tap into XDATA. I was looking at the memory model stuff and everything else while diving into this yesterday.
CONCUR WITH MR ERIK
No. Quite the opposite: you do "re-use" them!
The declaration must match the definition in both type and qualifiers
If you're sharing the declaration with other modules (and why else would you put it in a header file?) you add 'extern'
some.h
extern volatile unsigned char SomeData ;
some.c
volatile unsigned char SomeData = 1;
Only a definition can contain an initialisation.
Are you sure on volatile? I'm looking at some code from another programmer who built some PIC routines, and I see they use volatile on both the definition and declaration.
Are you sure that the compiler cares? If you add volatile to the definition or not when you initialize the variable, the compiler doesn't utter a warning / error either way. I'm sure there is some way (memory map or something) to see if the variable is being optimized or not.
But for extern, you clearly need it in the declaration....
I actually appreciate the help Andrew! This like a master class in qualifiers...!
I have no skin in the game -- if the compiler wants volatile in both declaration and definition. It can have them!
TECHNICALLY YOU DONT NEED THE EXTERN IN THE DECLARATION IN A HEADER (IN C) BUT IT IS COMMON TO SEE IT THERE.
STOP SHOUTING!
The 'extern' is only optional for function declarations (ie, prototypes):
www.youtube.com/watch
HA? You guys know this one?
Good stuff here today.
NO. VARIABLES TOO. CHECK YOUR DOCUMENTS MORE THOROUGHLY. OR TRY IT YOURSELF.
DID U CHECK? NOW I AM FINISHED PARTYING AND I CAN REPLY MORE. IF U MISS THE EXTERN IT IS A TENTATIVE DECLARATION. IN C YOU CAN HAVE MANY OF THEM. GO CHECK IT OUT AND LEARN SOMETHING.
Dude -- I have never even heard of tentative declarations, this might be getting closer to finally nailing this down:
en.cppreference.com/.../extern
I'll have to read through this. Sheesh, they need to make an order of operations diagram for this kind of thing with the compiler. Seriously.
Thumbs up!
I'll have to read through this. Sheesh, they need to make an order of operations diagram for this kind of thing with the compiler. Seriously. It's crap like this you get when people are more interested in showing how smart they think they are that in writing maintainable code.
@Eric
This is entirely my point! The way CODE as a specifier/qualifier is treated is probably/maybe not consistent with the Bible of C. Who cares... not me.
So when I call the syntax for CODE bizarre, maybe you shouldn't be so dismissive cause that C Language has got a whole-lotta side-loops and exceptions all over the place. Lot of hands have touched it over the years.
Honestly, as someone without a stake in the Bible of C -- I have been using tentative declarations (I didn't even know what they were called!) and they've been working. But that CODE qualifier doesn't work like that. Fine... that's why I asked.
In my mind, why would you want to double type declarations between headers and source code? Now if you change a qualifier, you have to update it in two places. Plus the extra time double typing everything?
I really don't care if you consider that wise or stupid either. No need for a debate. It's like a debate over how many spaces are in indent -- who cares...?
ALSO -- there is a very clearly typed implementation for the next guy to figure out from the post. Karma on me, to pay something back to the forum.
End goal: the MCU is moving bits, and the code is modular and reusable and compiles on whatever target I need.
Guess what -- we don't write everything in my business in C either, it's just one of many computer languages we use! All of them have warts, wrinkles, and oddities.
You mean a tentative definition.
A tentative definition is a declaration that may or may not act as a definition.
YOU'VE NOW FINALLY READ ABOUT THEM MR NEIL? SEE EXTERN IS ***NOT*** NEEDED. WELL DONE.
You are correct, I mistyped all that. Tenative definitions -- that are in my header declarations (there is a mouthful).
What a language C is. I have a brother who was a decent web programmer, and now is a very good securities lawyer. Frigging exact same thing, very technical language with exacting logic. Except securities contracts are written in English vs. C.
I miss the good old days of PHP & Python -- now that is an easy language compared to C.
I appreciate the help. Seriously.
I TREAT C AS A TOOL. I THINK I KNOW ENOUGH TO GET THE JOB DONE BUT SURE OF COURSE SOME PEOPLE WILL ARGUE I DON'T KNOW ENOUGH.
IT IS GOOD TO KEEP GOOD MANUALS TO HAND TO CHECK THINGS RARELY USED OR EASILY FORGOTTEN.
ALWAYS GOOD TO LEARN FROM OTHERS AND ACKNOWLEDGE ***GOOD*** ADVICE.
C IS NO MORE DIFFICULT THAN MANY OTHER LANGUAGES. JUST LIKE OTHERS, PRACTICE IS IMPORTANT.
OK, let's look at it
you have, in a module blah = ralph[i];
ralph is defined extern in an included header
the compiler, working on this module has no informationhus about 'ralph' except what is in that header thus it will (correctly) assume ralph is in DATA and create code accordingly. so if you want the compiler to use MOVC to access ralph, you must let the compiler working on that module know ralph is in code by including the qualifier on the definition in the header.
Just noticed this thread and was going to reply, but then saw what Erik says. He explains the requirement well. All that is meeded is a reaonable understanding of what a compiler does and what it is capable of.
Also, if you have this understanding for one environment, you will have a better chance of understanding for another.
View all questions in Keil forum