??? 03/16/05 19:15 Modified: 03/16/05 19:19 Read: times Msg Score: +2 +2 Good Answer/Helpful |
#89794 - More thoughts on C Responding to: ???'s previous message |
Ian Bell said:
Many others have have given excellent posts on the limitations of C in general and some on the specific problems on an 8 bit micro. I would just like to add that most C compilers pass parameters to functions on the stack and also use the stack for variables that are local to a function. Most 8051 C compilers do not do this because of the very small size of stack available on the 8051. Many use the registers to pass parameters but this causes problems as soon as you nest subroutines. Some compilers even look at the call sequence of your code and try to minimise the number of registers used by reusing ones that they reckon will not overlap in execution. One might argue that the small hardware stack of the 8051 is one of its greatest architectural limitations. As you point out, the Keil compiler (and more than likely most other) do quite a bit of acrobatics to deal with this, both in the form of fixed memory location parameter passing and overlaying of local variables in functions that can be shown not to be in execution simultaneusly (i.e., by static call tree construction). While this generally works out, as you mentioned, it poses a reentrancy problem, which among other things, pretty much rules out straightforward recursive functions, makes multithreaded applications difficult, etc. In addition, static call tree construction requires programmer assistance in some cases, such as in certain situations when function pointers are used. This is both tedious and error-prone. Keil Application Note 129 gives a nice discussion of this. Of course, the appropriateness of recursive functions and multithreaded applications on an 8051 system would make for a good thread all in itself; I am just pointing out a few limitations of this scheme. The folks at Keil, always trying to make our lives more convenient, offer us what they call a "reentrant stack". With a bit of startup code and the "reentrant" keyword following the argument list in a function's header, a function magically begins accepting arguments and storing local variables on something resembling a stack, allowing it to become reentrant, much like on a PC. However, this is done using a software stack (i.e., one without the hardware assistance of a push and pop facility); a notable performance penalty is the result. This can, from time to time be a convenenient feature, but knowing when it is appropriate is the real key. Once again, a good PC C programmer will not immediately be a great 8051 C programmer the first day he/she picks up an 8051 C compiler. Incidentally, there is a nice discussion of reentrant functions in the Keil C51/CX51 User's Guide. All this means is that once the code has been optimised, if you make a small change to the call sequence, the code timing could change a lot. One way to minimise this is to pass as few parameters as possible and use global variables wherever possible. This will certainly reduce code size and speed up execution but goes against the software gurus ideas of code modularity and managing the scope of variables. I agree entirely. But tightly coupled code on an 8051 is no less of a nightmare than tightly coupled code on a PC. Once I have written an embedded DS12887 driver, for example, I want to be able to easily use it in multiple projects. If I used too many globals, this might not be an easy task. It is important to balance the need for performance on and embedded platform with generally accepted software engineering practices. Of course, in assembler none of this is a problem.
Ian Hmm. If by "in assembler none of this is a problem" you mean that everything is fundamentally globally visible and there is no question about what is and is not on the stack, then I agree. As Craig suggested earlier, even though this is generally considered acceptable in assembly language, that does not necessarily make it good software engineering practice. Assembly language does not promote writing loosely coupled, reusable code that exhibits good modularity, data abstraction, information hiding, and other software engineering principles. It does not preclude it, but it is a lot easier to do this in C or another higher level language. --Sasha Jevtic |