EmbeddedRelated.com
Forums

Accurate delay routine in assembly for ARM7, CortexM3 and M0

Started by Alexan_e June 7, 2012
On Sat, Jun 9, 2012 at 5:35 PM, Alexan_e wrote:
> I'm not asking for a time critical delay but I wouldn't want to get
> random delays either that is why I asked for a delay that is completely
> written in assembly so we know exactly the cpu clocks it will take to
> execute , then depending on the delay needed this routine will be
> executed multiple times.
> I have used that type of delay in AVR for years, why is it so hart for
> Cortex/ARM?

for exactly the same reasons that you're moving into the realm of bigger,
more complex microcontrollers in the first place!

thing with an assembly delay is that:
* it will run at different speeds depending if it's run from flash or sram
due to the different fetch times. there's also pipelining to take into
account, and I don't know what sort of cache these chips have..
* it may take too long if you use DMA since the DMA controller may be
jumping in and using the bus
* it will take too long if you use interrupts, and you really should be
using interrupts for at least some things like systick

so, do you start to see why an assembler busy loop is not the accurate
delay you're looking for on this platform?

If you want an accurate delay, you /must/ use a timer or similar mechanism
because a timer's count rate isn't affected by all the other stuff going on
At this point I must ask, is your target purpose something that's better
suited to simply handing over to the DMA engine?

An Engineer's Guide to the LPC2100 Series

What I wrote was by no means meant to say that you didn't try to help
but unfortunately my compiler does not work with what you have suggested
that works in gcc.

I did put the code you posted in a delay.s file and got the following
errors.
(I hope that this is what I was supposed to do with this content.)

delay.s
--------------
.text
.thumb_func
.code 16
.syntax unified
.globl delay_loop
delay_loop:
subs r0, r0, #1
bne delay_loop
bx lr
--------------

assembling delay.s...
delay.s(1): error: A1167E: Invalid line start
delay.s(2): error: A1167E: Invalid line start
delay.s(3): error: A1167E: Invalid line start
delay.s(4): error: A1158E: Illegal line start, should be blank
delay.s(5): error: A1163E: Unknown opcode delay_loop , expecting opcode
or Macro
delay.s(6): error: A1167E: Invalid line start
delay.s(7): error: A1163E: Unknown opcode r0, , expecting opcode or Macro
delay.s(8): error: A1163E: Unknown opcode delay_loop , expecting opcode
or Macro
delay.s(9): error: A1163E: Unknown opcode lr , expecting opcode or Macro
delay.s(10): warning: A1313W: Missing END directive at end of file

If I add spaces in front of each line then the errors are

assembling delay.s...
delay.s(1): error: A1137E: Unexpected characters at end of line
delay.s(2): error: A1137E: Unexpected characters at end of line
delay.s(3): error: A1137E: Unexpected characters at end of line
delay.s(4): error: A1137E: Unexpected characters at end of line
delay.s(5): error: A1137E: Unexpected characters at end of line
delay.s(6): error: A1163E: Unknown opcode delay_loop: , expecting opcode
or Macro
delay.s(7): error: A1105E: Area directive missing
delay.s(7): warning: A1088W: Faking declaration of area AREA |$$$$$$$|
delay.s(10): warning: A1313W: Missing END directive at end of file

The only part I understand is the End part.

I may have to use a timer too at the end since no one seems to have a
solution that can be applied in my compiler.

Alex
In uVision add this to an assembly file



Then in a header file



then just call Delay(xxxx); from the C code.

Regards

Phil.
Thank you Phil.

I copy pasted the code in a file named my_delay.s and I got a few errors (basically because I didn't use spaces and an additional
END that was needed) so for anyone who is unrelated to assembly like I am do the following

Create a file like my_delay.s
Add the following content:



Note that you need a space in front or each line except from >>delay4 PROC<<
Then add the my_delay.s file to the project and add extern void delay4(int loops) in your code as Phil said and call the delay
using delay4(x) where x the number of loops (or change the names if you wish)

A quick test in the software simulator for LPC1313 (CortexM3) shows that each loop consumes 4 clocks and there is an overhead of 5
clocks so

delay4(1) consumes (1*4)+5 clocks
delay4(2) consumes (2*4)+5 clocks
delay4(10) consumes (10*4)+5 clocks
......

I have tested the following in the software simulator
delay4(1) consumes 9 clocks
delay4(2) consumes 13 clocks
delay4(1000) consumes 4005 clocks
delay4(10000) consumes 40005 clocks
delay4(100000) consumes 400006 clocks , from this point an additional clock is added but the difference is meaningless since
it's one total and not per loop
delay4(1000000) consumes 4000006 clocks

As everyone in this thread have already pointed you can't be sure that this delay will be accurate as it it shown in the examples
above but I still think it is a far better alternative that a volatile variable loop.
I have not yet tested the delay in ARM7TDMI or cortex M0

Alex
Just a minor correction to this, the branch should be to delay4, not DELAY
as DELAY is a section name, delay4 is the label exported as the function
address.

Other functions could get put in the same section in other files which could
break it if using the section name rather than the label.



In uVision all lines not preceded by a space are assumed to start with a
label

Also to use this from C++ you need to declare the function as extern "C" in
the C++ code or it will not be recognized by the linker, this tells the
compiler not to decorate the function name.

Regards

Phil.