Skip to content

Commit 3334207

Browse files
Bogdan PricopBrian Baltz
Bogdan Pricop
authored and
Brian Baltz
committed
Time related Arduino core functions: Re-write
* Change Timer0 configuration according to Alexandre D'Alton suggestion: use it as a free-run timer which delivers interrupts every 0xFFFFFFFF clock ticks and its ISR increments a global variable; in this way a 64-bit virtual RTC is created. The downside is the delay() function cannot take advantage of ARc's power management features anymore. * Change delay() implementation to make use of 64-bit time-stamp. * Change delayMicroseconds to use Timer0 counter value and unsigned arithmetic's features to handle the overflow case. The credit goes to Dan O'Dononvan. * Add function to get an atomic 64-bit time-stamp. * Change millis() and micros() implementation to use the 64-bit time-stamp. Signed-off-by: Bogdan Pricop <[email protected]>
1 parent 7405797 commit 3334207

File tree

4 files changed

+91
-116
lines changed

4 files changed

+91
-116
lines changed

Diff for: cores/arduino/wiring.c

+34-74
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2020
#include "Arduino.h"
2121

2222
#include "wiring.h"
23-
#include "arcv2_timer0.h"
2423
#include "data_type.h"
2524
#include "conf.h"
2625
#include "interrupt.h"
@@ -29,94 +28,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2928

3029
#define FREQ_MHZ ((ARCV2_TIMER0_CLOCK_FREQ)/1000000)
3130

32-
void delay(uint32_t msec)
33-
{
34-
uint32_t no_of_irqs = timer0_overflows + msec;
35-
uint32_t microseconds = arcv2_timer0_count_get();
36-
37-
while(timer0_overflows < no_of_irqs){
38-
yield();
39-
/* Go to sleep and wait to be awaken by Timer0 (or an external)
40-
* interrupt. */
41-
__asm__ volatile ("sleep");
42-
}
43-
/* For the last fraction of millisecond don't go to sleep - you'll wake up
44-
* too late - just spin */
45-
while ((arcv2_timer0_count_get() < microseconds) &&
46-
(timer0_overflows == no_of_irqs));
47-
}
48-
49-
50-
uint32_t millis(void)
51-
{
52-
return timer0_overflows;
53-
}
54-
5531

56-
uint32_t micros(void)
32+
static uint64_t getTimeStampClks(void)
5733
{
58-
__asm__ __volatile__ (
34+
__asm__ volatile (
5935
/* Disable interrupts - we don't want to be disturbed */
6036
"clri r2 \n\t"
61-
/* Load in r3 value of timer0_overflows */
62-
"ld r3, %0 \n\t"
63-
/* Use only the least-significant 22 bits of timer0_overflows */
64-
"and r3, r3, 0x3FFFFF \n\t"
37+
/* Load in r1 value of timer0_overflows */
38+
"ld r1, %0 \n\t"
6539
/* Read COUNT0 register */
6640
"lr r0, [0x21] \n\t"
67-
/* Read CONTORL0 register */
68-
"lr r1, [0x22] \n\t"
69-
/* If CONTROL0.IP is set COUNT0 reached LIMIT0 => r0 value might not be
41+
/* Read CONTROL0 register */
42+
"lr r3, [0x22] \n\t"
43+
/* If CONTROL0.IP is set COUNT0 reached LIMIT0 => r1 value might not be
7044
* accurate => read COUNT0 again */
71-
"bbit0.nt r1, 3, continue \n\t"
45+
"bbit0.nt r3, 3, end \n\t"
7246
/* Read COUNT0 again*/
7347
"lr r0, [0x21] \n\t"
7448
/* Timer0 overflowed => timer0_overflows++ */
75-
"add r3, r3, 1 \n\t"
49+
"add r1, r1, 1 \n\t"
7650
/***/
77-
"continue: \n\t"
78-
/* Compute microseconds time-stamp */
79-
/* Transform milliseconds in microseconds */
80-
"mpy r3, r3, 1000 \n\t"
81-
/* Transform ticks in microseconds */
82-
"div r0, r0, 32 \n\t"
83-
/* Store the final result in R0 register.
84-
* The function returns the result in R0 register. */
85-
"add r0, r0, r3 \n\t"
51+
"end: \n\t"
8652
"seti r2 \n\t"
87-
: /* Output parameters and their constraints */
88-
: "m"(timer0_overflows) /* Input parameters and their constraints */
89-
: "r0", "r1", "r2", "r3" /* Killed registers */
90-
);
53+
: /* Output parameters and their constraints */
54+
: "m"(timer0_overflows) /* Input parameters and their constraints */
55+
: "r0", "r1", "r2", "r3" /* Killed registers */
56+
);
9157
}
9258

93-
void delayMicroseconds(uint32_t usec)
59+
60+
void delay(uint32_t msec)
9461
{
95-
/* Function parameter is stored in R0 register */
96-
__asm__ __volatile__ (
97-
"mpy.f r0, r0, 32 \n\t" /* delay_ticks = delay_usec * 32 */
98-
"bz.d _end \n\t" /* if usec == 0 goto end */
99-
/* Subtract the function call overhead and the time spent by the previous
100-
* instructions of the function.
101-
* T function_call = 4 clks
102-
* T mpy + T bz = 3 + 3 = 6 clks
103-
* T exit_function + loop_precision ~ 5 clks
104-
* The above described timings were computed using a scope and infinite
105-
* loops in the code, meaning the instructions were most likely executed
106-
* from ICACHE.
107-
* */
108-
"sub r0, r0, 15 \n\t"
109-
/* Minimum value of r0 = 32 => the above subtraction cannot overflow */
110-
"_repeat: \n\t"
111-
/* T sub.f = 1 clk; T bnc.nd = 3 clks */
112-
"sub.f r0, r0, 4 \n\t"
113-
/* Repeat above subtraction until carry flag is set */
114-
"bnc.nd _repeat \n\t"
115-
"_end: \n\t"
116-
: /* output parameters of ASM instructions */
117-
: /* input parameters */
118-
: "r0" /* registers killed by above ASM instructions. */
119-
);
62+
uint64_t initial_timestamp = getTimeStampClks();
63+
uint64_t delay_clks = msec * FREQ_MHZ * 1000;
64+
65+
while (getTimeStampClks() - initial_timestamp < delay_clks) {
66+
yield();
67+
}
12068
}
12169

12270

71+
uint64_t millis(void)
72+
{
73+
uint64_t timestamp = getTimeStampClks();
74+
return (uint64_t)(timestamp / (FREQ_MHZ * 1000));
75+
}
76+
77+
uint64_t micros(void)
78+
{
79+
uint64_t timestamp = getTimeStampClks();
80+
/* Divide by FREQ_MHZ and return */
81+
return (timestamp >> 5);
82+
}

Diff for: cores/arduino/wiring.h

+43-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
Copyright (c) 2011 Arduino. All right reserved.
3-
Copyright (c) 2013 by Paul Stoffregen <[email protected]> (delayMicroseconds)
3+
Copyright (c) 2015 Intel Corporation. All right reserved (delayMicroseconds).
44
55
This library is free software; you can redistribute it and/or
66
modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,8 @@ extern "C" {
2626

2727
#include <stdint.h>
2828
#include "wiring_constants.h"
29+
#include "arcv2_timer0.h"
30+
2931

3032
/**
3133
*
@@ -34,50 +36,68 @@ extern void initVariant( void ) ;
3436
extern void init( void ) ;
3537

3638
/**
37-
* \brief Returns the number of milliseconds since the Arduino board began running the current program.
39+
* \brief Returns the number of milliseconds since the Intel EDU board began
40+
* running the current program.
3841
*
39-
* This number will overflow (go back to zero), after approximately 50 days.
42+
* This number will practically never overflow (go back to zero).
4043
*
41-
* \return Number of milliseconds since the program started (uint32_t)
44+
* \return Number of milliseconds since the program started (uint64_t).
4245
*/
43-
extern uint32_t millis( void ) ;
46+
extern uint64_t millis( void ) ;
4447

4548
/**
46-
* \brief Returns the number of microseconds since the Arduino board began running the current program.
49+
* \brief Returns the number of microseconds since the Intel EDU board began
50+
* running the current program.
4751
*
48-
* This number will overflow (go back to zero), after approximately 70 minutes. On 16 MHz Arduino boards
49-
* (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is
50-
* always a multiple of four). On 8 MHz Arduino boards (e.g. the LilyPad), this function has a resolution
51-
* of eight microseconds.
52+
* This number will practically never overflow (go back to zero).
53+
* It will overflow after more than 18000 years.
54+
* This function has a resolution of 2 microseconds.
5255
*
53-
* \note There are 1,000 microseconds in a millisecond and 1,000,000 microseconds in a second.
56+
* \note There are 1,000 microseconds in a millisecond and 1,000,000
57+
* microseconds in a second.
5458
*/
55-
extern uint32_t micros( void ) ;
59+
extern uint64_t micros( void ) ;
5660

5761
/**
58-
* \brief Pauses the program for the amount of time (in miliseconds) specified as parameter.
59-
* (There are 1000 milliseconds in a second.)
62+
* \brief Pauses the program for the amount of time (in milliseconds) specified
63+
* as parameter.
64+
*
65+
* This function relies on Timer0 interrupts, therefore it shouldn't be called
66+
* from a context with interrupts disabled.
67+
* It doesn't use CPU's power management features.
6068
*
6169
* \param dwMs the number of milliseconds to pause (uint32_t)
70+
*
71+
* \note There are 1000 milliseconds in a second.
6272
*/
6373
extern void delay( uint32_t dwMs ) ;
6474

6575

6676
/**
6777
* \brief Pauses the program for the amount of time (in microseconds) specified
6878
* as parameter.
69-
* Theoretically it works pretty accurate in the range 1 microsecond and up.
70-
* The precision should be +- 0.15 microseconds.
71-
* The above statements are based on measurements done when the function was
72-
* most likely executed from cache.
73-
* The accuracy for values <= 4 microseconds is impacted by cache miss, Timer
74-
* or external interrupts.
75-
* It doesn't disable the interrupts.
79+
*
80+
* The precision is +- 0.5 microsecond.
81+
* It doesn't disable the interrupts and it doesn't rely on interrupts,
82+
* meaning this function works reliable even if the interrupts are disabled.
7683
* It doesn't use CPU's power management features.
7784
*
78-
* \param usec the number of microseconds to pause (uint32_t)
85+
* \param dwUs the number of microseconds to pause (uint32_t)
86+
* Accepted range: from 1 microsecond up to 0x07FFFFFF microseconds.
87+
* If dwUs > above specifies threshold, the delay overflows.
88+
* E.g. If dwUs = 0x08000000 (0x07FFFFFF + 1), it actually means dwUs = 0
7989
*/
80-
void delayMicroseconds(uint32_t dwMs);
90+
static inline __attribute__ ((always_inline))
91+
void delayMicroseconds(uint32_t dwUs)
92+
{
93+
if (0 == dwUs) return;
94+
uint32_t init_count = arcv2_timer0_count_get();
95+
/* Multiply microseconds with FREQ_MHZ to transform them in clocks */
96+
uint32_t clocks = dwUs << 5;
97+
98+
while (arcv2_timer0_count_get() - init_count < clocks);
99+
}
100+
81101

82102
#ifdef __cplusplus
83103
}

Diff for: system/libarc32_edu/common/arcv2_timer0.h

+4-7
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,9 @@ extern "C" {
4242
#endif
4343

4444

45-
/* Maxim number of Timer0 overflows used to compute micros()
46-
* It overflows at 70 minutes = 70*60 sec = 70*60*1000 millis = 4200000 */
47-
#define MAX_OVERFLOWS_US 4200000UL
48-
4945
/* Increments every Timer0 overflow.
50-
* Timer0 is configured to overflow and fire an IRQ every 1 millisecond
46+
* Timer0 is configured as a free-run timer; it overflows every 0xFFFFFFFF
47+
* clocks.
5148
*/
5249
extern uint32_t volatile timer0_overflows;
5350

@@ -56,8 +53,8 @@ extern uint32_t volatile timer0_overflows;
5653
*
5754
* timer0_driver_init - initialize and enable the system clock
5855
*
59-
* This routine is used to program the ARCv2 timer to deliver interrupts at the
60-
* 1 millisecond rate specified via the ONE_MILLISECOND macro.
56+
* This routine is used to program the ARCv2 Timer as a free-run timer.
57+
* It delivers interrupts every 0xFFFFFFFF clocks.
6158
*
6259
* RETURNS: N/A
6360
*/

Diff for: system/libarc32_edu/drivers/arcv2_timer0.c

+10-12
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ The ARCv2 processor timer provides a 32-bit incrementing, wrap-to-zero counter.
4545
#include "conf.h"
4646
#include "interrupt.h"
4747

48-
#define ONE_MILLISECOND ARCV2_TIMER0_CLOCK_FREQ/1000
49-
50-
51-
/* globals */
5248

5349
uint32_t volatile timer0_overflows = 0x00;
5450

@@ -83,18 +79,19 @@ void arcv2_timer0_enable(uint32_t count)
8379

8480
/*******************************************************************************
8581
*
86-
* _arcv2_timer0_int_handler - system clock periodic tick handler
82+
* _arcv2_timer0_int_handler - Timer0 ISR
8783
*
88-
* This routine handles the system clock periodic tick interrupt.
89-
* It increments number of milliseconds since sketch begun.
84+
* This routine handles the overflows of 32-bit free-run Timer0.
85+
* In this way a virtually 64-bit RTC is created:
86+
* timer0_overflows = high double word of virtual RTC.
87+
* COUNT0 of Tiemr0 = low double word of virtual RTC.
9088
*
9189
* RETURNS: N/A
9290
*
9391
* \NOMANUAL
9492
*/
9593
void _arcv2_timer0_int_handler(void)
9694
{
97-
9895
/* clear the interrupt (by writing 0 to IP bit of the control register) */
9996
aux_reg_write(ARC_V2_TMR0_CONTROL,
10097
ARC_V2_TMR_CTRL_NH | ARC_V2_TMR_CTRL_IE);
@@ -106,17 +103,18 @@ void _arcv2_timer0_int_handler(void)
106103
*
107104
* timer0_driver_init - initialize and enable the system clock
108105
*
109-
* This routine is used to program the ARCv2 timer to deliver interrupts at the
110-
* 1 millisecond rate specified via the ONE_MILLISECOND macro.
106+
* This routine is used to the ARCv2 Timer as a free-run timer.
107+
* It delivers interrupts evry 0xFFFFFFFF clocks.
111108
*
112109
* RETURNS: N/A
113110
*/
114111
void timer0_driver_init(void)
115112
{
116113
/* connect specified routine/parameter to the timer 0 interrupt vector */
117114
interrupt_connect(ARCV2_IRQ_TIMER0, _arcv2_timer0_int_handler, 0);
118-
/* configure timer to overflow and fire an IRQ every 1 ms */
119-
arcv2_timer0_enable(ONE_MILLISECOND);
115+
/* Enable Timer0 as a free-run timer. */
116+
arcv2_timer0_enable(0xFFFFFFFF);
117+
120118
/* Everything has been configured. It is now safe to enable the interrupt */
121119
interrupt_enable(ARCV2_IRQ_TIMER0);
122120
}

0 commit comments

Comments
 (0)