1 /**************************** sys_time.c ********************************/
2 /* Copyright 2003/12/28 Aeolus Development */
3 /* All rights reserved. */
5 /* Redistribution and use in source and binary forms, with or without */
6 /* modification, are permitted provided that the following conditions */
8 /* 1. Redistributions of source code must retain the above copyright */
9 /* notice, this list of conditions and the following disclaimer. */
10 /* 2. Redistributions in binary form must reproduce the above copyright */
11 /* notice, this list of conditions and the following disclaimer in the*/
12 /* documentation and/or other materials provided with the */
14 /* 3. The name of the Aeolus Development or its contributors may not be */
15 /* used to endorse or promote products derived from this software */
16 /* without specific prior written permission. */
18 /* THIS SOFTWARE IS PROVIDED BY THE AEOULUS DEVELOPMENT "AS IS" AND ANY */
19 /* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */
20 /* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
21 /* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AEOLUS DEVELOPMENT BE */
22 /* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */
23 /* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */
24 /* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR */
25 /* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,*/
26 /* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE */
27 /* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
28 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30 /* Time routines. Polled implementations of routines to provide timed */
31 /* waits and elapsed time. */
32 /* Could be refined yet. */
33 /************************************************************************/
35 * TLIB revision history:
36 * 1 sys_time.c 30-Dec-2003,10:34:12,`RADSETT' First archival version.
37 * 2 sys_time.c 17-Jan-2004,16:07:18,`RADSETT' Move max_microsecond storage and
38 * init out of timing function
39 * into initialization.
40 * 3 sys_time.c 29-Jan-2004,10:29:08,`RADSETT' Update StartClock minimum count
41 * measurement to use a better comparison
42 * Add section to StartClock to measure the overhead of a WaitUs call.
43 * Change WaitUs to avoid attempting measurements with counts below
45 * Change WaitUs to reduce the requested wait time to compensate for call
47 * Change UsToCounts to replace /CLOCK_SPEED operation with shift and add
48 * operations so that it takes constant time. Add ifdef so it can be
50 * back to divide if CLOCK_SPEED is changed.
51 * TLIB revision history ends.
57 #include "errno_lpc.h"
59 /**** Local Macros ****/
61 /* If TIME_TO_COUNTS_USE_DIV is defined then the routines will */
62 /* use a normal divide operation. Otherwise the division will */
63 /* be replaced with shift and add operations. This is */
64 /* marginally faster. More important it does the operation in */
65 /* the same time no matter what the value used. */
66 /* Note: If CLOCK_SPEED is changed then either */
67 /* TIME_TO_COUNTS_USE_DIV must be defined OR UsToCounts and */
68 /* divx must be changed to reflect the new value. */
69 /* #define TIME_TO_COUNTS_USE_DIV */
70 #define CLOCK_SPEED (10000000) /* Nominal speed for internal */
73 /* Used for converting between counts and uS for time function.*/
74 /* An unsigned long long so overflows are not possible during */
75 /* the conversion. Note relationship to CLOCK_SPEED. */
76 #define COUNTS_PER_US (10uLL)
78 #define COUNTER_RESET ((unsigned char)2) /* Hold counter in reset. */
79 #define COUNTER_ENABLE ((unsigned char)1) /* Let counter run. */
81 /* Used to control and detect matches. Use R3 since it has no */
82 /* matching external output on T0. */
83 #define MATCH_T0_R3_NOTHING (~0xE00u)/* Do nothing when match */
84 /* register 3 matches T0. */
85 #define EXTERN_MATCH_T0_R3 (~0xC08u)/* Set External output low (if */
86 /* connected), do nothing on */
88 #define EXTERN_MATCH_T0_R3_SET (0x800) /* Set external output high */
89 /* (if connected) on match. */
91 /*** Local Variables ***/
93 /* Scales between desired nominal clock frequency and what we */
94 /* were actually able to achieve. This ends up being the */
95 /* actual clock rate in Hz. */
96 static unsigned long timing_scale_factor
;
98 /* Provides high order timing bits so internal timer can */
99 /* record a running time longer than ~.12 Hrs. In fact this */
100 /* allows a representation of > 50,000 years clearly (I hope) */
101 /* unreasonably long. */
102 static unsigned long high_counts
= 0uL;
104 /* Maximum number of microseconds that a single timing */
105 /* operation can handle. Larger values have to be split into */
106 /* multiple operations. */
107 static unsigned int max_microsec
= 0;
109 /* Minimum number of counts a core timing operation takes. */
110 static unsigned int min_count
= 0;
112 /* Minimum number of counts a WaitUs call takes. */
113 static unsigned int count_overhead
= 0;
115 /**** Internal Prototypes ****/
117 /********************* counts_to_us *************************************/
118 /* counts_to_us -- converts from internal units in counts to uS. */
119 static unsigned int counts_to_us( unsigned int counts
);
121 /********************* divx *********************************************/
122 /* Divide by 78125. Constant time operation. */
123 static unsigned long long divx(unsigned long long x
);
125 /********************* accumulate_time **********************************/
126 /* accumulate_time -- Check for rollover of HW timer and increments */
127 /* the high order count if it is detected. Must be called at at least */
128 /* twice the rollover frequency of the HW timer to ensure proper */
129 /* detection of rollover. That works out to about once every 3.5 */
130 /* minutes at nominal clock rate. */
131 static void accumulate_time( void);
133 /********************* get_full_counts **********************************/
134 /* get_full_counts -- Concatenate the stored high counts and the HW */
135 /* timer low counts to get a large number containing the full count. */
136 static unsigned long long get_full_counts( void);
138 /**** Implementation ****/
140 /********************* StartClock ***************************************/
141 /* StartClock -- Starts up the clock used for internal timing. */
142 /* Attempts to match the desired clock speed (CLOCK_SPEED) and */
143 /* initializes timing_scale_factor to a compensating scale. Returns 0 */
144 /* if successful, otherwise error code will be retained in errno. */
145 /* struct _reent *r -- re-entrancy structure, used by newlib to */
146 /* support multiple threads of operation. */
147 /* Note: Should be called only after all clocks have been set up. */
148 /* Otherwise time scale will not be correct. */
149 /* Returns 0 if successful. */
151 struct _reent
*r
) /* Reentrancy structure. */
153 unsigned long rate
, divider
;
154 unsigned long lclmax_microsec
= 0uL;
155 unsigned int start_count
, end_count
;
156 unsigned int test_us
;
158 rate
= VPBRate(); /* Get the clock rate of the */
159 /* peripheral bus. */
160 if( rate
== 0) { /* Internal error, complain and exit. */
161 r
->_errno
= ELPC_INTERNAL
;
165 /* Set clock to reset. This will keep it at zero. */
166 T0TCR
= COUNTER_RESET
;
168 /* Calculate a divider that will cause the internal clock to */
169 /* approximate the target clock speed. This should give a */
170 /* clock counting rate between the desired rate and twice the */
172 divider
= rate
/ CLOCK_SPEED
;
173 if( divider
== 0) { /* Sanity check on the divider. If we have a */
174 divider
= 1uL; /* pclk slower than our desired rate we won't */
175 } /* be able to reach the desired rate so just */
176 /* run as fast as we can. */
177 T0PR
= divider
- 1; /* Set prescaler to give us our target rate. */
179 /* Since the actual clock rate we have and our target rate may */
180 /* not be the same, calculate a corrective factor for routines */
181 /* that make use of it. This value will be divided by */
182 /* CLOCK_SPEED when dealing with time and so should produce a */
183 /* result precise to 1 part in CLOCK_SPEED. */
184 timing_scale_factor
= rate
/ divider
;
186 /* Set to do nothing on match, and set output to 0. */
187 T0MCR
= (unsigned short)(T0MCR
& MATCH_T0_R3_NOTHING
);
188 T0EMR
= (unsigned short)(T0EMR
& EXTERN_MATCH_T0_R3
);
190 T0TCR
= COUNTER_ENABLE
; /* Enable clocks. */
192 if( max_microsec
== 0) { /* Find saturation point. */
193 lclmax_microsec
= counts_to_us( UINT_MAX
);
194 max_microsec
= lclmax_microsec
;
197 /* Find the minimum number of counts it takes to set up and */
198 /* detect a time period. This simulates the normal timer */
199 /* operation. The purpose is to avoid any possible missed time */
200 /* periods by shortcutting the time wait process if the time */
201 /* would have passed by the time we had performed the operation */
203 /* We deliberately set out to slightly overestimate the time */
204 /* involved to avoid the danger of underestimating. This */
205 /* overestimation occurs in three ways: */
206 /* 1 - Operations that would normally occur after a match */
207 /* would be detected are included in the count. */
208 /* 2 - One count is added so that any fractional counts will */
209 /* be compensated for. */
210 /* 3 - The operations that do the counting are invariably */
212 /* Note that we are at the tender mercies of the compiler not */
213 /* to generate significantly different code for this and the */
214 /* code used for actual timing. */
215 start_count
= T0TC
; /* Starting time. */
217 /* This main loop is copied from WaitUs and modified in a */
218 /* fashion to try and preserve all the operations in a fashion */
219 /* that the compiler will not optimize this passage differently */
220 /* than the 'real' version. Comments from the original version */
221 /* are kept with notes added to indicate what operations are */
224 /* First perform a comparison of a local variable against */
225 /* global variable. Note: this comparison assumes that */
226 /* high_counts is still zero. If StartClock is run more than */
227 /* once between resets this code may fail to operate correctly. */
228 if( lclmax_microsec
> high_counts
) {
230 /* Add a local variable to current time counter and store in */
231 /* match register. */
232 T0MR3
= T0TC
+ start_count
; /* Set match to current time + delay */
235 /* Exact match for 'real' operation. */
236 /* Set operation on match to set to 1. */
237 T0EMR
= (unsigned short)(T0EMR
| EXTERN_MATCH_T0_R3_SET
);
239 /* Compare external register to local. This does not match */
240 /* the original operation which masks the external register and */
241 /* compares it to zero. The hope is that the operations will */
242 /* be close enough together that any differences (particularly */
243 /* short counts) will be masked by the over-estimation */
244 /* precautions. Since the difference between the two is only */
245 /* an instruction or two this appears to be a reasonable */
247 while( T0TC
== start_count
) { /* Wait for match to set output*/
250 /* Exact match for 'real' operation. */
251 /* Set external match 0 output to 0 and operation on */
252 /* match to do nothing. */
253 /* Set external match 0 output to 0 and operation on */
254 /* match to do nothing so we don't trigger next wait */
255 /* early by accident. */
256 T0EMR
= (unsigned short)(T0EMR
& EXTERN_MATCH_T0_R3
);
258 /* End of the copied main loop. */
260 end_count
= T0TC
; /* Ending time. */
262 /* Calculate the counts needed to perform a timing operation. */
263 /* Add a count for safety. */
264 min_count
= (end_count
- start_count
) + 1;
266 /* Now find overhead for a wait call. This will be used to */
267 /* compensate for that overhead to increase the accuracy at */
268 /* short waits. We do this by waiting for a short period and */
269 /* measuring how long it actually takes. */
271 /* First figure out how long to wait. Want as short as */
272 /* possible so we wait for the minimum useable period which */
273 /* will be the minimum detectable plus 1 microsecond */
274 test_us
= counts_to_us(min_count
) + 1u;
276 start_count
= T0TC
; /* Starting time. */
278 end_count
= T0TC
; /* Ending time. */
280 /* Total time spent. Add 1/2 of a microsecond for rounding. */
281 count_overhead
= (end_count
- start_count
) - UsToCounts( test_us
);
286 /********************* counts_to_us *************************************/
287 /* counts_to_us -- converts from internal units in counts to uS. */
288 /* unsigned int counts -- # of counts to be converted. */
289 /* Returns number of microseconds corresponding to counts. Truncates */
290 /* result to microsecond resolution. */
291 static unsigned int counts_to_us( unsigned int counts
)
295 /* Convert the counts to microseconds taking the actual clock */
296 /* rate into account. Note that as long as */
297 /* CLOCK_SPEED/(timing_scale_factor * COUNTS_PER_US) is less */
298 /* than 1 this cannot overflow a long. With nominal CLOCK_SPEED*/
299 /* at 10000000 this will be true as long as the actual clock */
300 /* speed is > 1000000. */
301 us
= (unsigned int)((counts
* (unsigned long long)CLOCK_SPEED
)/
302 (timing_scale_factor
* COUNTS_PER_US
));
307 #ifdef TIME_TO_COUNTS_USE_DIV
309 /********************* UsToCounts ***************************************/
310 /* UsToCounts -- converts to internal units in counts from uS. Other */
311 /* Modules use this counter for a timebase so this needs to be */
312 /* available to them. */
313 /* unsigned int us -- microseconds to convert to counts. */
314 /* Returns number of counts corresponding to us. Saturates on */
315 /* overflow so for large time periods it is possible to get a result */
316 /* lower than requested. */
317 unsigned int UsToCounts( unsigned int us
)
319 unsigned long long counts
;
321 /* Convert the nanoseconds to counts taking the actual clock */
322 /* rate into account. Note: NS_PER_COUNT * CLOCK_SPEED should */
323 /* always be 1,000,000,000. The expanded form just shows the */
324 /* intent more clearly. */
325 counts
= (COUNTS_PER_US
* us
* timing_scale_factor
)/(CLOCK_SPEED
);
327 if( counts
> ULONG_MAX
) {
330 return (unsigned int)counts
;
335 /********************* divx *********************************************/
336 /* Divide by 78125. Constant time operation. */
337 static unsigned long long divx(unsigned long long x
)
339 unsigned long long res
;
341 /* None of these operations overflow only since before this */
342 /* routine is called the calling routine uses a shift operation */
343 /* to perform a divide by 128 to provide enough headroom to */
344 /* allow this to succeed w/o overflow. */
346 res
= (73*x
- res
)>> 7;
347 res
= (94*x
+ res
) >> 8;
348 res
= (121*x
+ res
) >> 7;
349 res
= (106*x
+ res
) >> 9;
350 res
= (101*x
+ res
) >> 7;
351 res
= (95*x
+ res
) >> 8;
352 res
= (107*x
+ res
) >> 23;
356 /********************* UsToCounts ***************************************/
357 /* UsToCounts -- converts to internal units in counts from uS. Other */
358 /* Modules use this counter for a timebase so this needs to be */
359 /* available to them. Replaces division operation with an equivalent */
360 /* series of multiplies, shifts and adds. */
361 /* unsigned int us -- microseconds to convert to counts. */
362 /* Returns number of counts corresponding to us. Saturates on */
363 /* overflow so for large time periods it is possible to get a result */
364 /* lower than requested. */
365 unsigned int UsToCounts( unsigned int us
)
367 unsigned long long tmp
;
369 tmp
= COUNTS_PER_US
* us
* timing_scale_factor
;
370 tmp
>>= 7; /* First divide by 128. Doing this first */
371 /* ensures 7 bits of overhead for later divide */
372 /* by 5 operations. */
376 if( tmp
> ULONG_MAX
) {
379 return (unsigned int)tmp
;
382 #endif /* TIME_TO_COUNTS_USE_DIV */
384 /********************* accumulate_time **********************************/
385 /* accumulate_time -- Check for rollover of HW timer and increments */
386 /* the high order count if it is detected. Must be called at at least */
387 /* twice the rollover frequency of the HW timer to ensure proper */
388 /* detection of rollover. That works out to about once every 3.5 */
389 /* minutes at nominal clock rate. */
390 static void accumulate_time( void)
392 static unsigned int last_time
= 0;
393 unsigned long current_time
;
395 /* A simple polled rollover check. This should work as long as*/
396 /* this routine is called at least once every time the counter */
397 /* has incremented 1/2 of its maximum count. */
398 current_time
= T0TC
; /* Get the current count */
399 if(current_time
< last_time
) { /* If we have rolled over, */
400 high_counts
++; /* increment the high order */
402 last_time
= current_time
; /* Record last read value for */
403 /* the next check. */
406 /********************* get_full_counts **********************************/
407 /* get_full_counts -- Concatenate the stored high counts and the HW */
408 /* timer low counts to get a large number containing the full count. */
409 /* Returns number of counts since start. */
410 static unsigned long long get_full_counts( void)
413 unsigned long long full_count
;
416 /* Read in the clock counts. It is necessary to perform a */
417 /* rollover check so we don't end up with the low order value */
418 /* that of after the rollover and the high order count from */
419 /* before the rollover. If that were to happen our time would */
420 /* not increase in a monotonic fashion. */
422 accumulate_time(); /* Check for rollover of HW clock. */
423 low
= T0TC
; /* Read current count. */
425 /* Form low order and high order counts into a single value. */
426 full_count
= ((unsigned long long)high_counts
<< 32) | low
;
428 } while( low
> T0TC
); /* Check for rollover and redo if one */
430 return full_count
; /* All done. */
433 /********************* WaitUs *******************************************/
434 /* WaitUs -- Wait for 'wait_time' us */
435 /* unsigned long wait_time -- microseconds to convert to counts. */
436 /* Will break wait into multiple waits if needed to avoid saturation. */
437 void WaitUs( unsigned int wait_time
)
441 accumulate_time(); /* Check for rollover of HW clock. */
443 /* Convert from requested wait in uS to counts used by the HW */
444 /* counter. If necessary break into multiple waits. */
445 while( wait_time
> 0) {
446 if( wait_time
> max_microsec
) {
447 counts
= UsToCounts( max_microsec
);
448 wait_time
-= max_microsec
;
451 /* Note that we always execute the else portion of this test */
452 /* once for every call to WaitUs. */
454 counts
= UsToCounts( wait_time
);
456 /* Compensate for overhead. Subtract out overhead for */
457 /* waits that are long enough. For shorter periods */
458 /* simply minimize the wait. */
459 if( counts
> count_overhead
) {
460 counts
-= count_overhead
;
468 /* If counts is lower than min_count zero then the wait */
469 /* interval is too small and we should just return. The */
470 /* smallest count we can deal with is the count that occurs */
471 /* between reading the HW clock and setting the match register. */
472 /* The value of this minimum will depend on the timer rate and */
473 /* the CPU execution speed. */
474 if( counts
> min_count
) {
476 T0MR3
= T0TC
+ counts
; /* Set match to current time + delay */
479 /* Set operation on match to set to 1. */
480 T0EMR
= (unsigned short)(T0EMR
| EXTERN_MATCH_T0_R3_SET
);
482 while( (T0EMR
& 0x8) == 0) { /* Wait for match to set output*/
485 /* Set external match 0 output to 0 and operation on */
486 /* match to do nothing. */
487 /* Set external match 0 output to 0 and operation on */
488 /* match to do nothing so we don't trigger next wait */
489 /* early by accident. */
490 T0EMR
= (unsigned short)(T0EMR
& EXTERN_MATCH_T0_R3
);
493 accumulate_time(); /* Check for rollover of HW clock. */
497 /********************* GetUs ********************************************/
498 /* GetUs -- Get the current time in uS. */
499 unsigned long long GetUs( void)
501 unsigned long long counts
, us
;
503 counts
= get_full_counts(); /* Get the total passed time sice */
505 /* Convert low order of timer to nS. */
506 us
= counts_to_us( (unsigned int)(counts
& UINT_MAX
));
508 counts
>>= 32; /* Now get the high order count. */
510 /* Given a 64 bit number uS resolution provides a range */
511 /* exceeding 500,000 years. I think we'll skip overflow */
513 us
+= counts_to_us(UINT_MAX
) * counts
;
517 /********************* MinimumAchievableWait ****************************/
518 /* MinimumAchievableWait -- Get the shortest wait we can do. */
519 unsigned int MinimumAchievableWait(void)
521 return counts_to_us( count_overhead
+ min_count
+ (UsToCounts( 1u)/2));