Hello everyone,
So, I am working with an 8051W core processor (with OTP, once again, that's a whole other discussion) and I'm pretty maxed out on code space and looking to trim it down a bit. I need to check the functionality of some of my interrupts so I've had to create code that looks like this:
void main (void) { DiagMode = 0; /* Loop for standard/hardware diagnostics mode */ if ((DiagMode == 0) || (DiagMode == 2)) { while (1) { // Insert code here }; /* Loop forever */ } /* Loop for simulation/software diagnostic mode */ else if (DiagMode == 1) { while(1) { decimator2_ISR(); // Insert code here }; /* Loop forever */ } }
So if I wanted to use the code in simulation mode, I set DiagMode = 1. When I go to burn the chip, I set it equal to 0 (or 2 if I'm doing some hardware diagnostics, I have various case statements throughout the code that run only if DiagMode = 1 or 2).
Currently, if I want to cut down the code I have, I can comment out any unnecessary code portions (anything with DiagMode = 1, etc). I was wondering if there was a way to set my code to build the code differently if it is being used in simulation mode and when I compile it to hex (or set DiagMode to 0), the portions in code with DiagMode set to 1 are not built into the hex code, saving space.
I apologize if what I'm asking for is confusing. Realistically, it probably isn't possible but I figure if anyone would know how to do something along these lines (or has a better alternative), this would be the place to go.
Let me know when you get the chance. Thanks!
why not '#if' instad of 'if'
Erik
So, I updated my code to look like this:
void main (void) { DiagMode = 0; /* Loop for standard/hardware diagnostics mode */ #if ((DiagMode == 0) || (DiagMode == 2)) { while (1) { // Insert code here }; /* Loop forever */ } /* Loop for simulation/software diagnostic mode */ #else //if (DiagMode == 1) { while(1) { decimator2_ISR(); // Insert code here }; /* Loop forever */ } #endif }
It appears that it's working as it's supposed to (my code dropped by 50 bytes, so that's good) but I did get the following warnings:
warning C322: unknown identifier warning C322: unknown identifier
It might still work but I'd feel a lot more comfortable making those warnings go away, in case I'm doing something wrong.
If I was to guesstimate the problem, I'm guessing it's referring to DiagMode twice. Is this because I need to do some sort of #declare statement or something to that effect (#declare DiagMode = 0, possibly?)? Or set it as a constant? Or do I need to include some sort of library I'm missing?
Also, I wasn't able to get a #elseif statement working. I got the following error when I tried
#elseif (DiagMode == 1)
:
warning C315: unknown #directive 'elseif'
Did I screw something else up or is there a Keil specific way to do this?
Out of curiosity, is it really easier to create your own examples from scratch rather than to simply demonstrate by correcting code that is incorrect (such as my examples)? I consider it more important that you get to understand how this works than giving you code. Giving you code will not help you next time
I don't do it because i worry about "giving code". I do it because I want to show a skeleton showing the concept without any extra noise around.
Remember that threads on this forum are read by people who find the threads using search engines at a much later time. They don't care about your specific source code. But they may care about the concept that is covered in the thread.
So it's likely that Keil is overcompensating for errors in my code. I should still use the #if statements rather than if statements, even if it seems to be working correctly, just to play it safe. Thanks for the help.
No - Keil is not overcompensating.
But compilers do optimize code. So if the compiler knows that specific code lines can never be reached, then the compiler still need to process the lines to verify that they are grammatically correct. But the compiler can decide to not generate any code. You may potentially get a warning "unreachable code".
The #if version is the only one where all information is known at compile time.
The if (expr) version depends on the contents of the expression, if the compiler will be able to deduce that the expression has a fixed value or not.
Well, I did define DiagMode using the following code:
#define DiagMode 0
So, if I understand correctly, it knows the value of DiagMode at compile time. If the code is generated based on that, does this mean I don't need to convert all my if/else statements to #if/#else statements?
It makes sense in my head that this should work but I want to do it the right way, so if this leaves it open for future errors, I'd rather make the correction now than wait until it fails down the road.
if (xxx) { code }
the code will always be included in memory and only execute if the if is valid. . .
#if (xxx) code #endif
the code will only be included in memory if xxx is valid . .
so, if xxx is not valid the second build will be smaller than the first.
Maybe not but it sounds like you and Per are saying two conflicting things. What the heck am I supposed to think?
If I understand Per, he seems to imply that if a variable is declared at compile time, it won't compile statements that don't meet that criteria. That seems to be what my code is doing, even without your #if statements.
I am now asking HIM based on what he is telling me if I need to do what YOU are suggesting. I completely understand what you are saying. Don't get me wrong. But I'm trying to understand why it appears to be working correctly with what I have, even if it contradicts what YOU are saying. I WANT to do it the correct way so I'm trying to get more information.
If you disagree with Per on something, just SAY it. But don't get annoyed with me when I try to piece bits of information together from two different people that seem to be saying different things.
Right now, my code is acting the same with my if statements as it SHOULD be acting with #if statements, which is what is leaving me conflicted. Me changing to #if statements should change the way it acts if what you are saying is correct but when I changed a few of them, the code space did not change at all, implying that either my current code is correct (without changing the #if statements BECAUSE I added the #define statement) or Keil is correcting for me and I SHOULD have the #if statements just to play it safe (which is what you seem to suggest).
The problem here is I'm having a conversation with Per about trying to understand what is going on with Keil and you're spitting out the same data over and over with no real explanation as to what Keil is doing or why I should correct my code to do it your way if it works just fine without it. I UNDERSTAND WHAT IT IS YOU'RE TRYING TO GET ME TO DO! I want to understand HOW Keil works and what it's doing and that's what Per is trying to help me understand.
If you want to help out and give me more information, that's fine. But don't judge me ("you just don't get it do you") because I'm not immediately jumping on your suggestion without fully understanding WHY it's necessary. That's the point of why you post here, correct?
If I understand Per, he seems to imply that if a variable is declared at compile time, it won't compile statements that don't meet that criteria. That seems to be what my code is doing, even without your #if statements. true IF the 'variable' (which, in this case, it is not) is used in a # statement find a 'C' book, it will have a chapter "the preprocessor"
If you disagree with Per on something, just SAY it. But don't get annoyed with me when I try to piece bits of information together from two different people that seem to be saying different things. I do not disagree with Per, however I do disagree with your interpretation of what he says. Do we have a language problem? is English your first language?
Right now, my code is acting the same with my if statements as it SHOULD be acting with #if statements, which is what is leaving me conflicted. Me changing to #if statements should change the way it acts if what you are saying is correct but when I changed a few of them, the code space did not change at all show a simple example
I want to understand HOW Keil works in this respect, just like any C compiler and what it's doing find a 'C' book, it will have a chapter "the preprocessor"
without fully understanding WHY it's necessary. find a 'C' book, it will have a chapter "the preprocessor"
read this msdn.microsoft.com/.../ew2hz0yd(v=vs.80).aspx
if I want to cut down the code I have, I can comment out any unnecessary code portions (anything with DiagMode = 1, etc). I was wondering if there was a way to set my code to build the code differently if it is being used in simulation mode
I have posted with the understanding that you want to build a code two ways a) for the processor excludng some code b) for the simulator including all code
I hope this is correct, if not please clarify
if this is correct, you NEED to use preprocessor conditionals
true IF the 'variable' (which, in this case, it is not) is used in a # statement
Maybe this is where we're not seeing eye to eye. Is #define not a preprocessor statement? If not, then that's my confusion and yes, you are correct, I need to change all my statements to #if statements.
However, my code appears to be acting as if that #define statement is a preprocessor statement. At your request, here's my example.
I have code that says
if(DiagMode==1) { //Execute Code 1 } else { //Execute Code 2 }
When I use the line:
my code size is set to code size x and it executes Code 1. When I change it to:
#define DiagMode 1
my code size now changes to code size y and executes Code 2. So for all intents and purposes, my code is acting the way I want it, changing what is compiled based on the value of DiagMode.
I then went back to my code and altered it in this way:
#if(DiagMode==1) { //Execute Code 1 } #else { //Execute Code 2 } #endif
When I use:
I now have it executing Code 1 and the size is the previously mentioned code size x (just as it was prior to me adding the # signs). When I change my DiagMode to:
It executes Code 2 and the code size changes to size y (once again, the same way it was WITHOUT the # signs).
Now from this, I conclude that the #define is acting as a preprocessor statement and that the # signs that you insist I need are absolutely necessary but I have no proof that that is the case, hence my confusion and my questions.
Now, about this . . .
Do we have a language problem? is English your first language? You can easily read my statements here and have a pretty good idea of whether or not my first language is English. Me having a different interpretation of something than you doesn't mean I don't understand the english language. Honestly, you do give a lot of helpful information but sometimes, you're just a complete ass.
Now, you say that my interpretation of what Per is saying is incorrect. Then correct me! If you understand it differently, explain what it is YOU understand from it and where my thinking is wrong. Simply saying "You're wrong because I understand it differently" is not helpful at all. And it just proves your blind arrogance.
Or rather APPEARS like blind arrogance (I'm not trying to cast judgement on you, I don't know you outside this forum. That's just how it comes off).
the 1000 pound elephant is the optimizer
Is #define not a preprocessor statement? If not, then that's my confusion and yes, you are correct, I need to change all my statements to #if statements. # define is a preprocessor statement #if is a preprocessor statement if is not a preprocessor statement regardless if whether the x in if(x) is #defined
However, my code appears to be acting as if that #define statement is a preprocessor statement. At your request, here's my example. not because #define is a preprocessor statement, see below
if(DiagMode==1) { //Execute Code 1 } else { //Execute Code 2 } When I use the line:
#define DiagMode 0 my code size is set to code size x and it executes Code 1. When I change it to:
#define DiagMode 1 my code size now changes to code size y and executes Code 2. So for all intents and purposes, my code is acting the way I want it, changing what is compiled based on the value of DiagMode
#if(DiagMode==1) { //Execute Code 1 } #else { //Execute Code 2 } #endif When I use:
#define DiagMode 0 I now have it executing Code 1 and the size is the previously mentioned code size x (just as it was prior to me adding the # signs). When I change my DiagMode to:
#define DiagMode 1 It executes Code 2 and the code size changes to size y (once again, the same way it was WITHOUT the # signs). as it should be
AAAH, got it, you have heavy optimization which will do this
if(0) { lines will be excluded }
THUS, if you reduce optimization level (heavy optimization while debugging is a ***) you need the # for the if's, if you optimize heavy, you do not
so with heavy optimization Per and I are both right, with low optimization Per is wrong and I'm right.
by the NASA philosophy "we fly what we test" I do not use heavy optimizations.
I actually wondered if that was the case and should have mentioned I had it on optimization 8 (which is very high).
Unfortunately, it's a custom ASIC with only 8k of code space and we're at 7.4k already WITH the optimizer already set to 8. Ideally, I'd like to drop it down but we don't have the space for it (and we haven't even added the I2C or SPI code to it yet).
Thanks for your help, that makes a lot more sense. In future projects when we have more available code space, I'll be sure to set the #if statements correctly. At least now I understand why it's acting in the way it is.
Oh, and please use the appropriate tags when posting code! Heh . . .
Ok - sorry for the confusion here.
If the code uses #if defined(symbol) or #if symbol == xx or #ifdef(symbol) then there is a contract between you and the compiler vendor (based on the C language standard) that the compiler/preprocessor/linker combination will completely ignore lines that are within a block that where the condition isn't fulfilled.
So you can write:
#define SKIP_DEBUG #if !defined(SKIP_DEBUG) here I can have whatever random text I want (that doesn't look like some important preprocessor directive like #else, #endif, ... and the compiler will totally ignore if this is valid C or not. It will be stripped away before the C compiler starts to figure out what code you have. #endif
But you will get errors if you write:
int skip_debug = 1; if (!skip_debug) { I want the compiler to ignore this. But the compiler will scream blue murder because this is random text within the C source code. }
Ok. So what about "correct" code, but you want to save space.
int charlie(int a,int b) { int skip_debug = 1; if (!skip_debug) { printf("I'm in debug mode now. So lots of nice extra printouts.\n"); printf("I'm in function charlie() and time of day is '%s'.\n",my_time_of_day(tmpbuf)); } return a+b; }
The compiler will process every single line as C code. Any syntax error will be caught. The compiler can see that skip_debug is always non-zero. The compiler can know that the print() statements can never be activated. The compiler _may_ decide to not generate any code for that block and compile your source as if you had:
int charlie(int a,int b) { return a+b; }
I say _may_. There is no "must". The language standard allows the compiler to figure out that the if block can never be activated, and that the expression "skip_debug" don't have any side effect.
But there are no contract between you and the language standard that guarantees the optimization. There _may_ be a contract between you and the language standard that the compiler should optimize away the code.
So try same thing agan, but we move the variable include_debug outside the function.
int skip_debug = 1; int charlie(int a,int b) { if (!skip_debug) { printf("I'm in debug mode now. So lots of nice extra printouts.\n"); printf("I'm in function charlie() and time of day is '%s'.\n",my_time_of_day(tmpbuf)); } return a+b; }
Now, the life is way harder for the compiler. include_debug is a global variable, and the above code isn't enough to prove if any other code within the full program may modify the value of skip_debug to zero.
So unless the compiler/linker have very advanced code for performing full (cross)module optimization, it must include the code even if _you_ know the the variable is always zero and the code should never be run.
So then we have the mixed case.
#define skip_debug 1; int charlie(int a,int b) { if (!skip_debug) { printf("I'm in debug mode now. So lots of nice extra printouts.\n"); printf("I'm in function charlie() and time of day is '%s'.\n",my_time_of_day(tmpbuf)); } return a+b; }
This gets processed as:
int charlie(int a,int b) { if (!1) { printf("I'm in debug mode now. So lots of nice extra printouts.\n"); printf("I'm in function charlie() and time of day is '%s'.\n",my_time_of_day(tmpbuf)); } return a+b; }
Now, it is easier again for the compiler to notice the impossible-to-reach code block. So the compiler is free to (i.e. _may_) not generate any processor instructions for the unproductive source code statements. But the compiler isn't required to save code space by throwing away the block.
So you really should use the preprocessor fully (setting the symbol that selects include/skip, and that checks this symbol) to reduce the code size. Only then will you have a valid contract guaranteeing removal of the unwanted code.
If you do not use the preprocessor, but still see the code size shrink, that will be caused by the _may_ rule. The compiler _may_ be smart enough, and the code _may_ be simple enough, that the compiler can deduce that it can throw away code. Some compilers are marvels at analyzing code. Some are rather stupid. But the marvel and the stupid compiler will honor the intentions of the #if constructs.
Unfortunately, it's a custom ASIC with only 8k of code space and we're at 7.4k already WITH the optimizer already set to 8. Ideally, I'd like to drop it down but we don't have the space for it (and we haven't even added the I2C or SPI code to it yet). OH, what fun fitting I²C and SPI (and the code that uses it) into 600 bytes. I hope your costom chip has it in hardware, NOW, beware - if you need delay routine(a) you need to do it) in assembly, that high optimization will simply drop routines that "do nothing'
Yeah, to be honest with you, I might have to look at compiling separate code for digital output code because I doubt it's going to fit (then maybe we can reduce the optimization).
Thanks for the heads up regarding delay routines, that will probably save me a lot of headaches.
I wish OUR company had the NASA attitude . . . "Well, it may be expensive, but we need it." Oh well, not much I can do about it except make the most of the situation. Brings up new challenges and I develop new skills, I guess.
some things I have a problem with with, when doing huge projects, is inactive code showing up as active on global scans and, sometimes, getting lost studying inactive code when the problem is elsewhere.
in such cases I, sometimes do the following (which seems silly in an example this small)
#ifdef TEST #define TESTCODE #endif then the following var47 = ADCinput; #ifdef TEST TESTCODE if var47 > 222 TESTCODE { TESTCODE var47 = 222; TESTCODE } #endif vr47 =/ 10;
View all questions in Keil forum