??? 01/20/12 12:27 Read: times |
#185585 - Don't simplify to believe it's about fastest speed Responding to: ???'s previous message |
But it isn't just a question of speed.
With C code that does not map well to the processor, you can get code that is twice as large - or even worse than that. An 8051 chip has hardly any stack, so a traditional 8051 compiler tries to figure out if a function may be called recursively or not. All to allow the compiler to convert local - auto - variables into shared global variables. So manufacturer examples for how to make use of a peripherial chip are good as reference. But if not written for the processor architecture you are using, you may have to consider rewrites of the reference implementation to end up with a good result. A manufacturer of a 93c66 would not assume that you must use a 8051 or a Z80 or a PIC or a PPC. And for most general-purpose processor architectures, it doesn't matter much because C is a "nice" language with a limited amount of assumptions about the target platform. But it's common practice to use "int" for all variables in C, unless there is a very specific reason to go for char, float/double, or a long data type. int on the 8051 is very costly. Think about the meaning of multiplying a 16-bit integer with a 16-bit integer on the 8051. Think about the complexities of having a 16-bit array offset. Some sample programs you find out there may even use function pointers, since function pointers are a very elegant way to implement customizable algorithms. On an 8051, they are evilishly problematic. Reliable is normally the same as simple, i.e. easy to see and understand. But simple code need not work well. It is a huge misunderstanding to believe that knowledge about mappings between C and assembler is just limited to speed. As already covered a huge number of times on this and other forums, a very tiny part of all embedded source code lines are time-critical. But say you write a battery-operated device, that sleeps the processor between each event. A bad C-to-assembler mapping that makes the loop take twice as much time can result in the battery-operated device still be 100 times faster than needed. But suddenly consume 50% more battery power. So the device isn't battery-operated? Wrong selection of C primitives can result in a program that is twice as large. Might require a different processor because the original processor hasn't enough code or data space. Standard C has pointers. But a 8051 need to know if the pointer is to CODE or to some other memory region, since it uses different instructions depending on memory address space. So sample source code containing a void* will suddenly be very problematic if directly used on a 8051 chip. It doesn't matter that the code may look simple in C. It doesn't matter that the code may be "reliable" when run on an ARM or PPC. Next thing is that "simple" C code may not do what you think. What is: function delay_ms(unsigned ms) { int i,j; for (i = 0; i < ms_delay; i++) { for (j = 0; j < ITERATIONS_PER_MS; j++) { ; } } }? What happens when you change the compiler version? Or compiler optimization switch? Or if one of the variables are in XRAM instead of DATA or registers? One thing with programs accessing external hardware is that timing is normally extremely important. There are minimum timings for almost all events. In some situation, the minimum timing is so short that the C program can't fail. But not always. And in some situations there are also maximum timings. Another interesting thing. Some 8-bit processors have memory-mapped 16-bit registers. Maybe for setting baudrate. Or a timer counter. An 8-bit processor doesn't have a defined endianess. It's the compiler who decides if a 16-bit integer is stored least-significant byte first or most-significant byte first. But reading/writing that 16-bit mempry-mapped device from C using a 16-bit access requires that the compiler is using same byte order as the peripherial register. Or you are forced to instead manually do two 8-bit accesses. And sometimes it isn't even enough if compiler and chip has same definition of byte order - the hw may have special logic that requires one 8-bit access to happen before the other because there is a 16-bit latch involved. You say "you simply tweak if required". But you simply can not really tweak and end up with a reliable solution unless you: 1) Know why you need to tweak. 2) Know why a specific change will result in the required tweak. 3) Know how to validate that the tweak does solve the problem and doesn't just seem like it solves it. But this normally means that you 1) Need to be able to understand enough of the C reference code. Or maybe the assembler reference code - most probably for different processor and different clock frequency or maybe even different processor architecture. 2) Need to be able to understand what happens when same code - with or without tweaks or other adjustments - are used on the real processor. And bullet 2 above are often only fulfilled if you are able to understand the assembler generated by the processor. And have a decent grasp about mappings C -> assembler. Just so you know what is a trivial change to the C code that will not suddenly make the critical code change behaviour from working to non-working. So anyone who thinks mappings C/assembler is off-topic has to take one or two steps back and think again because they are busy fooling themselves. It's very much an important concern when writing hw-interfacing code, to know exactly what happens and why. "Optimal" is seldom an important factor. But "correct" is. And "correct" requires "knowledge", since it takes knowledge to prove correctness. |