This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

us_delay() improvement


This is something I've had floating about for a while...

Index: ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/common/current/ChangeLog,v
retrieving revision 1.92
diff -u -5 -r1.92 ChangeLog
--- ChangeLog	6 May 2003 21:00:20 -0000	1.92
+++ ChangeLog	19 Jun 2003 18:25:39 -0000
@@ -1,5 +1,10 @@
+2003-06-19  Nick Garnett  <nickg@balti.calivar.com>
+
+	* src/hal_if.c (delay_us): Reorganized to cope with high frequency
+	timers by eliminating a source of arithmetic overflow. 
+
 2003-05-06  Mark Salter  <msalter@redhat.com>
 
 	* src/hal_stub.c (handle_exception_exit): Call sys_profile_reset from
 	here. Setup to return through return_from_stub() when appropriate.
 	(return_from_stub): New function to call CYGACC_CALL_IF_MONITOR_RETURN
Index: src/hal_if.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/common/current/src/hal_if.c,v
retrieving revision 1.22
diff -u -5 -r1.22 hal_if.c
--- src/hal_if.c	3 Mar 2003 17:09:56 -0000	1.22
+++ src/hal_if.c	19 Jun 2003 18:25:52 -0000
@@ -8,10 +8,11 @@
 //####ECOSGPLCOPYRIGHTBEGIN####
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
 // Copyright (C) 2002 Gary Thomas
+// Copyright (C) 2003 Nick Garnett <nickg@calivar.com>
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
 // Software Foundation; either version 2 or (at your option) any later version.
 //
@@ -32,12 +33,12 @@
 // in accordance with section (3) of the GNU General Public License.
 //
 // This exception does not invalidate any other reasons why a work based on
 // this file might be covered by the GNU General Public License.
 //
-// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
-// at http://sources.redhat.com/ecos/ecos-license/
+// Alternative licenses for eCos may be arranged by contacting the copyright
+// holders.
 // -------------------------------------------
 //####ECOSGPLCOPYRIGHTEND####
 //=============================================================================
 //#####DESCRIPTIONBEGIN####
 //
@@ -158,37 +159,70 @@
 delay_us(cyg_int32 usecs)
 {
     CYGARC_HAL_SAVE_GP();
 #ifdef CYGPKG_KERNEL
     {
-        cyg_int32 start, elapsed;
-        cyg_int32 usec_ticks, slice;
-
-        // How many ticks total we should wait for.
-        usec_ticks = usecs*CYGNUM_KERNEL_COUNTERS_RTC_PERIOD;
-        usec_ticks /= CYGNUM_HAL_RTC_NUMERATOR/CYGNUM_HAL_RTC_DENOMINATOR/1000;
-
+        cyg_int32 start, elapsed, elapsed_usec;
+        cyg_int32 slice;
+        cyg_int32 usec_per_period = CYGNUM_HAL_RTC_NUMERATOR/CYGNUM_HAL_RTC_DENOMINATOR/1000;
+        cyg_int32 ticks_per_usec = CYGNUM_KERNEL_COUNTERS_RTC_PERIOD/usec_per_period;
+        
         do {
             // Spin in slices of 1/2 the RTC period. Allows interrupts
-            // time to run without messing up the algorithm. If we spun
-            // for 1 period (or more) of the RTC, there'd be also problems
-            // figuring out when the timer wrapped.  We may lose a tick or
-            // two for each cycle but it shouldn't matter much.
-            slice = usec_ticks % (CYGNUM_KERNEL_COUNTERS_RTC_PERIOD / 2);
+            // time to run without messing up the algorithm. If we
+            // spun for 1 period (or more) of the RTC, there would also
+            // be problems figuring out when the timer wrapped.  We
+            // may lose a tick or two for each cycle but it shouldn't
+            // matter much.
+
+            // The tests against CYGNUM_KERNEL_COUNTERS_RTC_PERIOD
+            // check for a value that would cause a 32 bit signed
+            // multiply to overflow. But this also implies that just
+            // multiplying by ticks_per_usec will yield a good
+            // approximation.  Otherwise we need to do the full
+            // multiply+divide to get sufficient accuracy. Note that
+            // this test is actually constant, so the compiler will
+            // eliminate it and only compile the branch that is
+            // selected.
+            
+            if( usecs > usec_per_period/2 )
+                slice = CYGNUM_KERNEL_COUNTERS_RTC_PERIOD/2;
+            else if( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD >= 0x7FFFFFFF/usec_per_period )
+                slice = usecs * ticks_per_usec;
+            else
+            {
+                slice = usecs*CYGNUM_KERNEL_COUNTERS_RTC_PERIOD;
+                slice /= usec_per_period;
+            }
     
             HAL_CLOCK_READ(&start);
             do {
                 HAL_CLOCK_READ(&elapsed);
                 elapsed = (elapsed - start); // counts up!
                 if (elapsed < 0)
                     elapsed += CYGNUM_KERNEL_COUNTERS_RTC_PERIOD;
             } while (elapsed < slice);
             
-            // Adjust by elapsed, not slice, since an interrupt may have
-            // been stalling us for some time.
-            usec_ticks -= elapsed;
-        } while (usec_ticks > 0);
+            // Adjust by elapsed, not slice, since an interrupt may
+            // have been stalling us for some time.
+
+            if( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD >= 0x7FFFFFFF/usec_per_period )
+                elapsed_usec = elapsed / ticks_per_usec;
+            else
+            {
+                elapsed_usec = elapsed * usec_per_period;
+                elapsed_usec = elapsed_usec / CYGNUM_KERNEL_COUNTERS_RTC_PERIOD;
+            }
+
+            // It is possible for elapsed_usec to end up zero in some
+            // circumstances and we could end up looping indefinitely.
+            // Avoid that by ensuring that we always decrement usec by
+            // at least 1 each time.
+            
+            usecs -= elapsed_usec ? elapsed_usec : 1;
+            
+        } while (usecs > 0);
     }
 #else // CYGPKG_KERNEL
 #ifdef HAL_DELAY_US
     // Use a HAL feature if defined
     HAL_DELAY_US(usecs);


-- 
Nick Garnett                    eCos Kernel Architect
http://www.ecoscentric.com/     The eCos and RedBoot experts


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]