itmr rollover in delay loop

Stan Sieler sieler@allegro.com
Wed, 24 Nov 1999 14:46:17 -0800 (PST)


Re:

> 
> >> Look at the loop.  What we do is basically
> >> 
> >>       cr16 = mfctl(16);
> >>       while(((cr16+loops)-mfctl(16))>0);
> >
> >You definitely don't want to do the above!
> >
> >Even ignoring the possibility of an interrupt that takes us away
> >for awhile, there's the simple possibility that cr16 might
> >roll over during your loop.
> 
> Careful use of unsigned variables fixes the problem.  The easiest way to
> explain this is by a small sample program:
...

 
Actually, unsigned integers cause the problem, as seen below.

I'd been assuming cr16 and mfctl were uint32, not int32, hence my concern
about a possible bug.  (I don't have easy access to the source code)


NOTE: with "int32 cr16" and "int32 mfctl (int crnum);" I don't
see any problems.


Assumptions:
   cr16 is a uint32  (note: better to use than "unsigned long int")
   mfctl is a uint32 function
   loops is a reasonable value 


The loop ending will be *missed* if the precise
value of CR16:    original_cr16 + loops
happens to be missed (again, using uint32).  This can easily happen if CR16 is
ticking faster than the clock (which is allowed by the
architecture) *or* can happen if an interrupt occurs shortly
before CR16 hits the final expected value.

Note that the unsigned arithmetic means that the subtraction in
the "while" control will always be > 0 except for the precise
time when cr16 = original_cr16 + loops.

Here's sample code demonstrating the problem, along with
sample output.

(the following should loop 5 times...but it loops far more!)

Startup: cr16 = 0x1e241, loops = 5, cr16 + loops = 0x1e246
  (loop, cr16 = 0x   1e242), ((cr16 + loops) - mfctl(16)) = 0x       4)
  (loop, cr16 = 0x   1e243), ((cr16 + loops) - mfctl(16)) = 0x       3)
  (loop, cr16 = 0x   1e244), ((cr16 + loops) - mfctl(16)) = 0x       2)
  (loop, cr16 = 0x   1e245), ((cr16 + loops) - mfctl(16)) = 0x       1)
   (extra tick)
  (loop, cr16 = 0x   1e247), ((cr16 + loops) - mfctl(16)) = 0xffffffff)
  (loop, cr16 = 0x   1e248), ((cr16 + loops) - mfctl(16)) = 0xfffffffe)
  (loop, cr16 = 0x   1e249), ((cr16 + loops) - mfctl(16)) = 0xfffffffd)
  (loop, cr16 = 0x   1e24a), ((cr16 + loops) - mfctl(16)) = 0xfffffffc)
  (loop, cr16 = 0x   1e24b), ((cr16 + loops) - mfctl(16)) = 0xfffffffb)
  (loop, cr16 = 0x   1e24c), ((cr16 + loops) - mfctl(16)) = 0xfffffffa)
  (loop, cr16 = 0x   1e24d), ((cr16 + loops) - mfctl(16)) = 0xfffffff9)
  (loop, cr16 = 0x   1e24e), ((cr16 + loops) - mfctl(16)) = 0xfffffff8)
  (loop, cr16 = 0x   1e24f), ((cr16 + loops) - mfctl(16)) = 0xfffffff7)
  (loop, cr16 = 0x   1e250), ((cr16 + loops) - mfctl(16)) = 0xfffffff6)
  (loop, cr16 = 0x   1e251), ((cr16 + loops) - mfctl(16)) = 0xfffffff5)
  (loop, cr16 = 0x   1e252), ((cr16 + loops) - mfctl(16)) = 0xfffffff4)
  (loop, cr16 = 0x   1e253), ((cr16 + loops) - mfctl(16)) = 0xfffffff3)
oops: we've looped too many times!

-----------------------------cut here for source code-------------------------
unsigned long cr16;
unsigned long simulated_cr16;
unsigned long loops;

/***************************************************************/
unsigned long mfctl (int crnum)

      /* ticks once per call ... sometimes twice :) */

   {

   simulated_cr16++;

         /* To demonstrate the problem, let's always make it */
         /* tick more than once at *exactly* the worst time... */

   if ((cr16 + loops) == simulated_cr16)
      {
      simulated_cr16++;
      printf ("   (extra tick)\n");
      }

   return simulated_cr16;

   } /* end mfctl proc */
/***************************************************************/

main() {

   int
      actual_loops;


   simulated_cr16 = 123456;   /* actual value irrelevant */

   cr16 = mfctl (16);         /* every call to mfctl(16) causes a simulated tick

   loops = 5;                 /* desired number of loops */
   actual_loops = 0;          /* track number we've actually done */

   printf ("Startup: cr16 = 0x%x, loops = %d, cr16 + loops = 0x%x\n",
                  cr16, loops, (cr16 + loops));

   while (((cr16 + loops) - mfctl (16)) > 0)
      {
      printf ("  (loop, cr16 = 0x%8x), ((cr16 + loops) - mfctl(16)) = 0x%8x)\n",
                  simulated_cr16,
                  ((cr16 + loops) - simulated_cr16));

      if (actual_loops++ > (loops + 10))
         {
         printf ("oops: we've looped too many times!\n");
         exit (1);
         }
      }                          /* while loop */

   printf ("\nExited normally from delay loop.  At exit:\n");
   printf ("cr16 = 0x%8x, ((cr16 + loops) - mfctl(16)) = 0x%8x)\n",
                  simulated_cr16,
                  ((cr16 + loops) - simulated_cr16));

   } /* end main proc */
/***************************************************************/
-- 
Stan Sieler                                           sieler@allegro.com
www.allegro.com/sieler/wanted/index.html          www.allegro.com/sieler