We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I am working on project with AT8052 with keil While doing C coding i have created many functions at certain situation multiple nesting of functions occur which may be avoided with some effort. Since 8051 stack is inside the internal RAM itself and i am not using any external data memory, should i avoid multiple nesting of function as it may occupy more RAM area. Regards Nitin
yes exactly calling function from within function likewise nesting. NITIN
Every time you go one level deeper in function calls 2 bytes are used on the stack. In some cases other added loads of the DATA/IDATA section may occur, but these concerns should not allow anyone to drop a well structured approach to writing code. A means of reducing DATA/IDATA use without killing a good program structure is to break up a deep nesting into two e.g. blah() stores a result in a global ralph snd is followed by a billy() that process ralph. Now I have to ask all the "real C" people that believe that the '51 is to be treated as a PC to shut up, there is absolutely nothing wrong with global variables, they reduce nesting and speed up processing Erik
"there is absolutely nothing wrong with global variables, they reduce nesting and speed up processing" I see a red rag...! ;-) Of course, all generalisations are bad! Globals are a potential maintenance nightmare, but do have the potential to reduce nesting and speed up processing. Because C51 does not put locals on the stack, there is no overhead in using local variables within functions; in fact, it has the positive advantage of allowing Overlaying. Globals only have advantage in passing data between functions.
Globals only have advantage in passing data between functions. Absolutely, I thought I stated that. However I do not agree with the 'only'. Some globals, left over from the assembly days, are inherintly dangerous such as
unsigned char temp // to be used as a temporary in several functions.
unsigned char MainState // occasionally globally search that no writes exist outside 'main'
Of course, you could always just define a function: U8 GetMainState(void); This would have the advantage of avoiding the need to "occasionally search" for writes to your state variable, and instead make it impossible to write code that wrote the variable from outside main. You don't have to search for bugs you can't create in the first place. It's generally more reliable to design so that you can't have a particular problem that to try to inspect and test to be sure that a problem didn't occur. The function costs less than you might think. A direct reference to the variable would probably be something like: MOV A,direct costing two bytes. ACALL _GetMainState also costs two bytes. So the per-use size cost is actualy the same. The function call will, however, cost more instruction cycles; 2 for the ACALL, 1 for the move, 2 for the RET. It's not likely that checking a state variable is the key source of delay in your program -- but if this factor really is crucial in your particular program, one answer is of course to inline the function. Keil, oddly enough, doesn't have an inline keyword, but in this case we can always fall back on the not-so-good old fashioned way: #define GetMainState() (mainState) With the macro, you're still unlikely to write over the variable since it doesn't look like a variable, and it costs exactly the same as a direct access, since it is one. The time assumptions presume that you keep this variable in internal RAM. If the variable is in external RAM, and thus requires a 3-byte DPTR setup for every access, a function would actually produce smaller code than a global variable. A variable larger than one byte also changes the tradeoff. An access function can also prove useful in debugging, as well. That address gives you a nice spot to set breakpoints to catch reads of your variable. With direct accesses scattered throughout the code, you need an ICE with a fancy trace capability to catch addresses. (And if the variable is internal data, you won't see accesses on the address bus at all.) Or you might put in some log code (printf, a buffer with timestamps) for debugging, in which case you add the code in one place. You might even flip back and forth during development between an actual function and direct access. The function call (macro or otherwise) allows you to keep the same syntax for access throughout the rest of the code, no matter what the implementation, so you don't have it edit every access for every change. The development benefits of an access function may well outweigh the tiny advantage in speed of direct access. This is just a special case of the reason high level languages exist at all. You can almost always tweak the assembler to be a little more efficient, but the extra cost in development time usually doesn't provide a good return on investment. There's an old proverb in software engineering. "Any process works on a small project." 8051 apps are typically small enough that bad habits won't hurt you too much (if at all), but that doesn't necessarily make them good habits. As with any generalization, of course, there's always a specific example to disprove "the rule".
Drew, I never said always use global variables, I said there is nothing wrong with global variables, that is hardly a generalization. unsigned char MainState // occasionally globally search that no writes exist outside 'main' the comment was not intended as a practice, more as an illustration. If you use a good variable naming convention there is no need to constantly examine. The development benefits of an access function may well outweigh the tiny advantage in speed of direct access as you stated using an 'access macro' which I wholehardedly support you hide the fact that the variable is global. I love anything that limits the ability to make mistakes as long as it does not interfere with the most effective code. Many moons ago I created this project that ran a $250.000 peripheral at full speed using a minicomputer that was mandated as the system controller (why is another story). After release to the field this was turned over to sustaining engineering and after a few days, I started getting calls "you can't do that" "that is illegal" etc. After 6 months they released an upgrade and every customer screamed bloody murder since their $250.000 machine now only ran at 1/3 speed and the word from sustaining engineering was "but the code is now good". Do you really think the customers were impressed? So global variables are OK, but not always the best local variables are OK, but not always the best, 'transfer variables' and a few other cases should be examined. Erik Ps I am still in the 'the faster you run, the better it is' business.
Erik, I did not intend that post as criticism, but rather an examination of some of the issues surrounding such choices. as long as it does not interfere with the most effective code "Effective" is measured in multiple dimensions, which is more or less the point of my earlier post. In my opinion, the best engineering tradeoff isn't necessarily the minimum on the code size or speed axis, but that's certainly a major factor that has to be taken into account for each case. And limited environments such as a typical 8051 project place even more of a premium on performance. I belong to the "less is more" school myself, particular since my current project is pushing 64,000 bytes of object code ;) It even has a global variable not all that different from the hypothetical mainState (though I might have talked myself into wrapping it with an access function with my own post!)
Drew, I think we agree that coding is not pantyhose (one size does not fit all) and while some may lean one way and some may lean another way the truth is that in all cases "you shall not" should be replaced by "you shall only if good". My reaction on global variables is strongly flovored by exposure to some programmers that would rather screw up a program structure than use a global variable. I have also seen programmers coming from assembly using global variables where a local would be the better choice. So let's agree that the right choice is not what some "guru" stated a a "rule" but what is best for functionality and mainatainability (the ability to maintain it - not killing the code to protect it aginst you). Where functionality should take priority over mainatainability will always be a judgement call and I know that you can prioritize functionality without losing more than a few percent of maintainability if you keep thinking maintainability while coding functionality. Erik