# 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

```