Page 1 of 1
Delays
Posted: Wed Sep 05, 2018 12:29 pm
by steve64
How much precise is the Timers.Delay() in terms of the generated wait interval
when using a timer initialized with Timers.uSec as unit?
Also, is there a way (by other means) to generate a specific delay below 1 us ?
I can guess something like a FOR loop with EMIT passing a NOP instruction...
(clearly would be CPU clock dependent but could be acceptable for this use case).
Re: Delays
Posted: Wed Sep 05, 2018 2:23 pm
by cfbsoftware
The following code counts the number of 1000 uSec delays that occur during 1 second of time as recorded by the real-time clock. It returns exactly 1000:
Code: Select all
Clock.Init();
secs := 0;
Clock.SetHMS(0, 0, 0);
count := 0;
REPEAT secs := Clock.Seconds() UNTIL secs = 1;
WHILE secs # 2 DO
Timers.Delay(usecTimer, 1000);
INC(count);
secs := Clock.Seconds();
END;
Out.Int(count, 0); Out.Ln();
If the delay is reduced to 100 uSec the result is 9945 not 10000 because the overheads of executing the loop start to become a more significant factor. If you want to get a more accurate measurement you could use a 1 uSec delay to toggle a GPIO pin and look at the resulting pulse on a scope.
Yes, you could use an empty loop to get much shorter delays that are dependent on your clock speed. e.g. the following code in a leaf procedure executes 3 * x + 1 instructions.
Code: Select all
i := 0; REPEAT INC(i) UNTIL i = x;
I thought that equated to (3 * x + 1 nanosecs) for a 100MHz clock. However, when I ran the following at 216 MHz on a Cortex-M7 it executed in exactly 2000000 uSecs not 3000000 uSecs as I expected. Maybe I've misunderstood something?
Code: Select all
PROCEDURE* Delay(n: INTEGER);
VAR
i: INTEGER;
BEGIN
i := 0; REPEAT INC(i) UNTIL i = n
END Delay;
PROCEDURE Run;
VAR
i, elapsed: INTEGER;
BEGIN
Timers.Start(usecTimer);
Delay(216000000);
elapsed := Timers.Elapsed(usecTimer);
Out.Int(elapsed, 0); Out.Ln();
END Run;
Re: Delays
Posted: Wed Sep 05, 2018 3:54 pm
by steve64
The Cortex M7 used in the STM32 MCU has a six-stage dual-issue pipeline.
So I guess that that loop is executed faster than expected for such reason.
Thanks for all the info on timing.
Re: Delays
Posted: Thu Sep 06, 2018 8:16 am
by steve64
There's a strange behavior in my environment (Cortex M7).
Let try the following test. It takes 5 seconds whatever the CPU clock.
I also performed a HW reset before each run with different clock.
Maybe I'm missing something...
MODULE SpeedTest;
IMPORT Main, LinkOptions, Out, Clock;
VAR
i, x, secs: INTEGER;
BEGIN
Out.String("CPU Frequency: "); Out.Int(LinkOptions.Fosc, 0); Out.Ln;
Clock.Init();
Out.String("measuring..."); Out.Ln;
secs := 0;
x := 100000000;
Clock.SetHMS(0, 0, 0);
i := 0; REPEAT INC(i) UNTIL i = x;
secs := Clock.Seconds();
Out.String("elapsed (s): "); Out.Int(secs, 0); Out.Ln;
END SpeedTest.
Re: Delays
Posted: Sat Sep 08, 2018 6:00 am
by cfbsoftware
(Sorry - I accidentally overwrote your previous reply)
You can use the Crystal Freq (Hz) parameter if you want to target boards with different clock frequencies. You could then use the value to set all of the appropriate clock parameters in Main.mod. You would also need to put checks in your code to report if an non-applicable frequency has been chosen. As both of the Cortex-M7 boards currently supported have the same clock frequency (8 MHz) none of that additional complexity was necessary. Consequently If you use the default code supplied with Astrobe with the currently supported boards it doesn't matter if you change the value on the configuration form because it is ignored.
According to the STM documentation you should be able to confirm that the CPU is running at the max speed (216 MHz) by monitoring the oscillator outputs MCO1 or MCO2. For details refer to Chapter 5 Reset and clock control (RCC) in the RM0410 Reference Manual for the STM32F76xxx and STM32F77xxx, and the UM1974 User manual for the STM32 Nucleo-144 boards. Unfortunately I have had no success with that - I'm probably just missing one of the many parameters that you need to take care of. The next best thing I could do was measure a waveform on GPIO pin PF13 which was generated using 7 instructions with the following minimal code:
Code: Select all
PROCEDURE* Run;
CONST
GPIOFBase = 040021400H;
GPIO_BSRR = 018H;
VAR
on, off: SET;
BEGIN
on := {13};
off := {29};
WHILE TRUE DO
SYSTEM.PUT(GPIOFBase + GPIO_BSRR, on);
SYSTEM.PUT(GPIOFBase + GPIO_BSRR, off);
END
END Run;
That resulted in a frequency of 36 MHz which is higher than I would have expected. I would have thought that you would need only six instructions to get that rate so the pipeline you mentioned must be having an effect.
Re: Delays
Posted: Sat Sep 08, 2018 7:40 am
by cfbsoftware
cfbsoftware wrote:Unfortunately I have had no success with that - I'm probably just missing one of the many parameters that you need to take care of.
Duh! I wasn't missing a parameter I just had Pin PC9 mode configured as Output Mode rather than an Alternate Function Mode.
The MCO2 frequency on Pin PC9 measures as 43.2 MHz. That is
exactly what it should be for a 216 MHz clock as the relevant RCC_CFGR values are set in Main.mod as follows:
Bits 31:30 MCO2 = 0 (Clock source = SYSCLK)
Bits 27:29 MC02PRE = 111 (MCO2 prescalar = division by 5)
If you want to try it yourself, this is the code you need:
Code: Select all
GPIO.Map(PORTC, 9, MCO2);
ConfigurePin(MCO2, GPIO.Mode_AF, GPIO.AF0);
Re: Delays
Posted: Mon Sep 10, 2018 9:57 am
by steve64
Thanks a lot, great help from you as usual.