Reply by Phil Young June 9, 20122012-06-09
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.

An Engineer's Guide to the LPC2100 Series

Reply by Alexan_e June 9, 20122012-06-09
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
Reply by Phil Young June 9, 20122012-06-09
In uVision add this to an assembly file



Then in a header file



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

Regards

Phil.
Reply by Alexan_e June 9, 20122012-06-09
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
Reply by Triffid Hunter June 9, 20122012-06-09
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?
Reply by Paul Curtis June 9, 20122012-06-09
> Paul suggested that If everything else fails I should write it using
> UNIFIED ASSEMBLY LANGUAGE.
> I searched in google, I think I have to write an .s file but I have no
> idea what to write inside the file in order to be able to call it as a
> function from C, I have no experience in assembly.

I gave you, written out, the complete code to plug into gas. I mean, what more can I do? I also gave C-level versions of the loop which you can run. The fact you have some problem with Keil is just that there is no standardised assembly language writing mechanism in C, and mnemonics in assemblers will usually be compatible across implementations, but the periphery of directives will not. Before UAL it was pretty much a nightmare writing code in the assembly language parts of the CrossWorks library. It's still not much fun, but it's manageable.

> The first place I'm going to use this delay is in a HD44780 display,
> there is noting critical about it as long as the minimum delay is
> achieved but that doesn't mean that every time I ask for 5us I should
> get 10us.
> I'm also going to use it in other external peripheral device libraries.

You know, I dedicate a single timer to ticking at the core frequency--that way I know how long something takes and minimal delays where burning a few cycles isn't a problem are taken care of.

-- Paul.
Reply by Alexan_e June 9, 20122012-06-09
My original request was an accurate delay because that is what the delay
routine I found was supposed to be but I couldn't use it.
Basically I was asking for someone experienced in assembly (and probably
a uvision user) to tell me what I should change in the given routine to
make it compile in the ARM compiler for a Cortex mcu but instead this
started a whole conversation about how wrong this type of delay is.

Many of you said that an accurate delay is not possible because the loop
may be interrupted, I agree so I have rephrased what I requested to a
minimum delay when uninterrupted although I think that this goes without
saying in this type of delay.

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?

The routine should also be portable in M0,M3 and ATM7TDMI but if this is
not possible then I can use a define to choose from two delay routines
depending on the core but so far I have none for Cortex.
The problem is still that I can't use any assembly instruction in Cortex
since I get the error I described ( Inline assembler not permitted) so
does anyone have a suggestion of how I can use assembly?

Paul suggested that If everything else fails I should write it using
UNIFIED ASSEMBLY LANGUAGE.
I searched in google, I think I have to write an .s file but I have no
idea what to write inside the file in order to be able to call it as a
function from C, I have no experience in assembly.

The first place I'm going to use this delay is in a HD44780 display,
there is noting critical about it as long as the minimum delay is
achieved but that doesn't mean that every time I ask for 5us I should
get 10us.
I'm also going to use it in other external peripheral device libraries.

I don't have any specific tolerance requirement , if 1% is possible then
that is what I want or maybe a higher or lower accuracy but I don't want
to use a volatile variable loop in C for this delay since I think it
will give a far worce accuracy than a predictable assembly loop .

Alex
Reply by Felipe de Andrade Neves Lavratti June 8, 20122012-06-08
Someone has mentioned that if one build libraries than resources could
become scarce.

There are rules that must be followed in order to modules making makes
sense.

I do only build system in software modules, being experienced on that I can
give a few advices

1) Modules should be platform independent.
2) In case a module need to access a peripheral, its preferable to abstract
that access using a callback or lowerlvl calls for two resons:
1st: It truly makes a module platform independent.
2nd: By being platform independent, performing unit test on a PC
environment gives the debugging process a great efficiency, which means,
high system quality.
ps: lowlvl call / callback is, for example, if you need to read a byte
from a SD Card, than you call a function that you expect to give you that
byte. On unit test, this function will read from a pseudo SDCARD on the
test ram, if on a target platform, there will be a real function that will
do read the sd card. Can you see the great power achieved by platform
independence?
3) In case that you need a delay/uart routine, do call backs, call a
function that you expect to write on char to a logging port, and build a
module's private delay routine that do callback to a function that return a
system tick counter.

In the end, if you have one thousands modules using delays, there will be
only one counter/timer being spent.

At.
Reply by cfbsoftware1 June 8, 20122012-06-08
--- In l..., Alexan_e wrote:

> As I have explained in a previous post what I meant by accurate to have a min delay accuracy, for example delay_us(5) should give
> a delay of 5us if not interrupted or more if for any reason it is interrupted but it shouldn't give 4.5us in any case.
>

OK - we are are getting closer to knowing what you really want but your requirements could still be misinterpreted.

e.g. are you only interested in a minimum delay or do you also require that it shouldn't give 5.5us if uninterrupted either?

Is the maximum acceptable margin of error always 0.5us or does it depend on the magnitude of the delay i.e.

a) do you mean you want a timer that is accurate to +/- 0.5us

b) do you mean you want a timer that is accurate to +/- 10%

c) do you mean something else?

Regards,
Chris

Chris Burrows
Astrobe v4.2: Oberon for Cortex-M3
http://www.astrobe.com
Reply by Mario Ivancic June 8, 20122012-06-08
On 8.6.2012 15:21, Alexan_e wrote:
> That is why I'm asking for an assembly routine which will have a very specific predictable duration, I can easily use a C loop but
> it will be unpredictable.
>

When I need delay in some xy lib I usually make some void(*)(int)
pointer that will be initialized in run-time to point to some delay
function. That way my xy lib is independent of delay lib.
In case that I need specifically sw loop delay (for whatever reason) I
would make function like this:



Of course, loops_per_ms needs calibration of some sort, in run-time
using hw timer or in compile time, using come constant.
This is IMHO the best way to make sw delay because C is easier to
maintain than ASM and calibration provides accuracy (probably better
than counting cycles manually in ASM code)