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?
the preprocessor, which handles the #... thingies do NOT look at char, int etc.
thus if you have #ifdef RALPH
RALPH must be a #define
the ussual way to do this is in xxx.h to have something like //#define BUILD1 //uncomment this line for build1
So you're saying I would just need to change all my:
if(DiagMode==1)
statements to
#if(BUILD1)
Is that correct? I just want to make sure before I change my whole program.
Thanks, Erik!
Note that #if BUILD1 tests if the preprocessor symbol BUILD1 is nonzero. #if defined(BUILD1) (or the older #ifdef BUILD1) just tests if it is defined.
#define BUILD1 // just defines this symbol #define BUILD1 1 // also gives it a value
if you give the define a value, you can also do
#define BUILD_TYPE 2 #if BUILD_TYPE==0 ... #elif BUILD_TYPE==1 ... #elif BUILD_TYPE==2 ... #else #error "Unsupported build type." #endif
Well, found the issue with the errors with
#define DiagMode 0
in my VARS.H file. Turns out, I just need to comment out:
unsigned char idata DiagMode = 0; extern unsigned char idata DiagMode;
Now I don't have to change anything (which is convenient). Thanks!
Well remember that #define does symbol replacement.
So your
unsigned char idata DiagMode = 0;
will look like
unsigned char idata 0 = 0;
and the compiler is not impressed by that.
if(DiagMode==1) statements to
#if(BUILD1) Is that correct? I just want to make sure before I change my whole program.
of course not, Names in an example are just that
Ok, now I'm confused. What is it you guys are proposing me to do at this point?
Originally, I had:
VARS.H
#ifndef _D4_VARS_H #define _D4_VARS_H extern unsigned char idata DiagMode;
vars.c
#include "VARS.H" unsigned char idata DiagMode = 0;
main.c
#include "VARS.H" 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 */ } }
I have now changed it to:
#ifndef _D4_VARS_H #define _D4_VARS_H //extern unsigned char idata DiagMode; #define DiagMode 0
#include "VARS.H" //unsigned char idata DiagMode = 0;
This appears to work as I can change the Diagmode = 0 to 1 and it gives me a different code size, which I assume means it's working (maybe I'm wrong). Am I doing this right or are you suggesting I need to do it a bit differently?
I understand that you guys generally want to give guidance without giving sample code but at this point, I think you're losing me with what it is you're suggesting. Can you give me a quick code example for what you think I should be doing because, if I'm doing something incorrectly right now, I really don't understand what it is you are suggesting.
Look at my example.
I have a question for you. So, if you look at my last post, you can see what I did and it seemed to work without updating any of my if statements to #if statements. Is this because Keil fixed the issue for me with the compiler (which probably means I am still doing it incorrectly, regardless of how Keil fixes it for me) or is what I did actually correct?
I found an area that had an if/else diagmode statement and changed them to #if and #else and noticed no change in code size (ie the code I had was executing correctly with my fixes) so it appears to be doing what I wanted it to do correctly.
I just want to know if my procedure is incorrect and, for best practices or when using other compilers (or even changing optimization levels, possibly), I need to implement it differently or if my coding is correct.
Let me know if my wording is confusing and I can try and explain it again. Thanks!
you are mixing precompiler and compiler directive
THIS IS AN EXAMPLE VARIABLE NAMES ARE FICTIONAL
common.h
//#define OMIT1 //uncomment if certain code is to be omitted //#define OMIT2 //uncomment if certain other code is to be omitted
whatever.c
#include common.h code #ifndef OMIT1 code that is to be excluded if omit 1 #endif code #ifndef OMIT2 code that is to be excluded if omit 2 #endif code #ifndef OMIT1 more code that is to be excluded if omit 1 #endif code
Ok, I understand.
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)? Could save a few posts.
Oh, and for the record, I don't want to come off as ungrateful because I do appreciate the help I'm getting.
And so you know, I used "BUILD1" for reference purpose, not because I thought BUILD1 was the required variable (I understand you weren't being literal with that name). So that part wasn't lost on me, in case I gave you the impression I didn't know how variables worked . . . :p
The compiler is smart. If it see C code that is unreachable, it can remove it.
So:
int charlie=1 if (charlie == 1) { ... } else { ... }
the compiler can throw away the code inside the else statement.
If the code instead looks like:
void my_function(int charlie) { if (charlie == 1) { ... } else { ... } }
Then the compiler can't throw away any code.
But when using #ifdef x or #if defined(x) or #if x == y then the preprocessor will filter away non-matching code and the compiler don't need to worry about what can be resolved at compile time and what needs to be resolved at runtime.
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.
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.
That makes sense. Sounds like, whether my code needs it or not using the optimization I'm using, it's in my best interest to add the #if statements to ensure I'm not relying on "MAY" conditions to occur.
At least now I understand what's happening and why. Thanks, guys!
View all questions in Keil forum