I want a fast and simple delay routine:
#pragma OT(8,SPEED) // Switch to speed optimization for this routine void my_delay(BYTE count) { register BYTE i; for (i=count; i; i--); }
Is the best so far and becomes:
B02:0xB1F1 EF MOV A,R7 B02:0xB1F2 6003 JZ B02:B1F7 B02:0xB1F4 1F DEC R7 B02:0xB1F5 80FA SJMP I2C_delay(B02:B1F1) B02:0xB1F7 22 RET
but I was hoping there was a way to get C51 to do just a DJNZ instead of 4 instructions for each loop.
Is there a magic coding style or optimization level that will generate the DJNZ?
This is not a religious war.
Actually, in the usual figurative meaning of that phrase, it is.
Your side lost it decades ago, but every new generation of programmers refuses to admit it.
PLEASE STOP the discussion of the reasons not to use instruction delay loops! Please just answer the question!
You either ask questions, or you get to choose answers all to your liking. You won't have it both ways.
But in this case, I don't care about accuracy of the timing!
I just want the top end to be a little faster by using a DJNZ instead of 4 instructions.
You're contradicting yourself. If you really didn't care about the accuracy, it would make no difference whatsoever whether the granularity is one instruction or four.
That is very disturbing.
That is even more disturbing.
Eric,
I'd still suggest you consider writing your delay in assembler. Any seasoned low level programmer knows when the use of a software delay is sensible and would code it accordingly, so any statement by a person on whether it is right or wrong of it is just showing their ignorance.
Even if a zealous bigot responder here has a way of causing the compiler to use the DJNZ, it's really not sensible to rely on it. Any number of things could cause the compiler to change it's mind so your code could simply break at a future point in time. Write it in assembler, and you know it is safe from these compiler decisions.
Thank you for the hint!
The way you get a DJNZ delay loop for the 8051 using Keil 9.53.0 C51 compiler is:
#pragma OT(8,SIZE) // Switch to speed optimization for this routine void I2C_delay(BYTE count) { // register BYTE i; // for (i=count; i; --i); // this is a 4 instruction loop - MOV/JZ/DEC/SJMP which should simply be a DJNZ but I can't get Keil to generate it! // while(count) --count; // this results in the exact same 4 instruction loop. while(--count); // this results in a DJNZ! }
Any other C compiler, version or target MCU may or may not result in the desired result.
Any seasoned low level programmer knows when the use of a software delay is sensible and would code it accordingly, so any statement by a person on whether it is right or wrong of it is just showing their ignorance. I see no argument in the above against software delays. I see plenty against doing it in C.
the real point here is trying to force the compiler to do it's job in a certain way. To that my point is "if you can not live with what the compiler does use assembler". The sad fact is that many waste huge amounts of time on these issues when, in reality, they do not matter one iota. We are not working with the steam driven 8 MHz 12 clocker any more.
"We are not working with the steam driven 8 MHz 12 clocker any more."
Steam-driven or not. The problem is that an instruction-counting loop written in C will always be at the mercy of whatever optimization the compiler may feel in a mood to supply.
It isn't even enough with inline assembler, since the ARM optimizer will run the optimization on all the output from a C/C++ file without caring which instructions was from the C/C++ compiler and which instructions that was inlined.
And for the C51, it's the linker that can jump in and play havoc based on changes in other locations of the project.
And that's why a timing loop in a high-level language really should poll some hardware to make the instruction count or actual instructions used more-or-less irrelevant for the loop.
And also why a super-short "min-time" delay created using nop() intrinsics can work well - since he compiler is expected to understand that the nop() intrinsic must never be optimized away.
The nice thing here, is that people don't even need to understand assembler to do a software loop in assembler. It's enough to write it in C. Then copy the produced assembler instructions and move to a separate assembler file. Suddenly, they are well protected from changed optimization levels of the compiler.
I don't see software loops as religion. But as a great way to get hurt. Anyone remember the old Turbo Pascal programs that suddenly stopped working when the machines became so fast that the auto-calibrating software loop did run out of calibrating range? So even auto-calibrating sw loops can fail in spectacular ways.
Steam-driven or not. The problem is that an instruction-counting loop written in C will always be at the mercy of whatever optimization the compiler may feel in a mood to supply. Per, I diverted from "just delays" to the fact than ever so many get lost in "optimizing the compiler". Looking at the OPs issue, 1) he don't care about precision 2) he want the compiler to "do the optimal" and (s)he has now wasted days on something that does not matter. The issue is that if it is not super critical, WHO THE #&!! CARES how optimal the compiler generated code is. This is, by no means, not the first post we have seen "why does the compiler not generate code as efficient as i (believe) I can do in assembler. Since the 1 and 2 clockers appeared (1995?) I have, without ever running the optimizer beyond level 2, had only ONE case where I had to do assembler for a function.
Erik
PS "without ever running the optimizer" I do not believe in releasing code that is not debuggable. I have had an experience where someone needed help "my code fails, but runs when I debug (no optimize)" he was developing with more memory than the production model (a valid case if you produce millions of units, his market was about 100) BTW, the problem WAS a timing loop.
Hi Eric, this is the answer:
6: void my_delay(unsigned char count) 7: { 8: do{}while(--count); C:0x0016 DFFE DJNZ R7,my_delay(C:0016) 9: } C:0x0018 22 RET