This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Inconsistent delay with SysCtlDelay() compared to ROM_SysCtlDelay()

I've been experimenting with various LM4F120 peripherals.  While doing this I was not being careful about consistently using ROM_* calls.  I created a simple delay function using SysCtlDelay() but while testing a timer utility I found a problem with my timing results.  After a lot of wasted time I finally discovered that for me the SysCtlDelay() was not producing an accurate delay.   I eventually tried changing the call to ROM_SysCtlDelay() and then began getting the results I expected.  

I'm not sure if this is a known problem or not but here's the code I used to test it:

My delay function:

void delayMS(int ms) {
    //ROM_SysCtlDelay( (ROM_SysCtlClockGet()/(3*1000))*ms ) ;  // more accurate
    SysCtlDelay( (SysCtlClockGet()/(3*1000))*ms ) ;  // less accurate
}

....

            a = getTimeStamp() ;   // returns double derived from Hibernate RTC
            onLED(BLUE) ;    // simply turns on BLUE LED
            delayMS(15) ;     //  calls for 15ms delay
            offLED(BLUE) ;  //  turns off BLUE LED
            b = getTimeStamp() ;  // gets another measure of time
            diff = b - a ;   //   estimates delay time (w/in a few hundred uS
            doubleToStringL("diff=", diff, true) ; // label, double, true=finish w \n

....  Note: above code executed in 2 second loop

I measured the delay two ways.  I used a logic analyzer to monitor BLUE LED pin on Stellaris Launchpad and by comparing RTC time before and after delay.

Both logic analyzer and timestamp differences were within 10uS of each other so I believe I was measuring relatively accurately.

With ROM_SysCtlDelay(), I got the delay I expected within a few hundred uS as measure by logic analyzer and timestamp difference.

With SysCtlDelay() I did not get the delays I expected.  Here's a sample

Delay amount     Measured
        in MS               delay

  2000                       2222.2

  1500                       1666.6

  1000                       1111.1

   900                         999.9

   800                         888.8

   700                         777.77

   500                         555.5

   400                         444.4

   300                         333.3

   200                          400.3

   100                         200.0

    50                           100.0

    25                           50.0

   15                           30.3

    5                            10.0

   1                              2.0

The patterns are suspicious to say the least.

In any case, I'm moving on now that I'm getting better results with ROM_SysCtlDelay().  It would be nice to get some feedback if anything is known about this problem.

Thanks

Target LM4F120, CCS 5.2, host Ubuntu 12.4, shoe size 11.5

  • It probably means that the person who wrote the ROM version is a better programmer that wrote the the code version.

  • Actually not. The person who wrote the ROM version is the same person who wrote the version in the DriverLib library because they are generated from identical source :-) The actual answer here is likely to do with the fact that ROM is always accessed in a single cycle whereas flash may have different numbers of wait states depending upon the system clock frequency in use and the part you are running the code on. To further complicate matters, different parts have different instruction prefetching capabilities. Adding this all up, you will find that the nominal 3 cycle loop inside SysCtlDelay() may not take clock 3 cycles to run when you run it from flash.

    We should update the function documentation to make this clear but, if you really want 3 cycle delay loops, you should use ROM_SysCtlDelay() instead (as you found out). Note that even this will be affected by interrupt latencies so the accuracy will be determined by the longest ISR in your system.

  • Suspect poster greenja's response was bit in jest - usually he's "spot-on."

    ROM's single cycle access is a key insight - thank you Dave for that.

    We should note that the linearity of this delay degrades substantially when short delays (less than ~250uS) are sought. 

    As illustration - here are averaged results (3 back to back runs) from an LX4F w/SysClock @50MHz:

    Delay Var    Scope Measured Duration (uS)

    5000            300
    2500           160
    1666           100

     300              20
     200             14
     100             09
      50              05.5
      20              03.9
      10              03.2

    Lesson here may be that ARM's abundance of Timers may be better choice when short, precise delays are required.  

    For completeness - test data here based upon call to ROM_ SysCtlDelay() with values as shown and "toggle" of GPIO surrounding the delay call...  BTW - IAR has powerful Cycle-Counter - if sufficient interest - test can be repeated w/the elimination of errors introduced by bit toggle intrusion into the above measurements...

     

  • Yes, bad joke...my "timing" was a little off you could say.

    My apologizes to the programmer and his family.

  • I'm sure he'll recover. I think he's crying in a corner somewhere just now. I'll got and tell him he is forgiven. :-)

  • [plz don't interpret the tone of this response as being harsh or argumentative,  I'm just in a hurry :-)]

    I'm happy that I have a workaround, better yet better solution.  But I was hoping I'd hear a follow-up discussion more consistent with the obvious--SysCtlDelay() for all practical purposes is not working.   I have to trust and I believe that the developers who developed the peripheral library code, knew what they were doing and performed some testing.  My gutt feeling is that linking difference between ROM_ vs compile time isn't the problem.

    We can "argue" that choosing a small enough delay relative to the cycle time will result in significant lost of accuracy.  But, a 1sec+ delay should not result in 20%+ error.  Also, the patterns seen in my results are suspicious, e.g. requested delays <= 200ms result in 50% error, otherwise longer delays result in delays that have repeating digits.  Believe me I spent a lot of time verifying that these were that actual results and not a mistake in my debugging.  I hope this statement doesn't come back to bite me :-{

    cb_mobile, are you saying you've verified my results, or at least that there's a problem?

    FYI, my system clock is set using:

        ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                SYSCTL_XTAL_16MHZ);

    which is 80Mhz. 

    Please let me know what else I can say that might explain this.  Again, I'm able to move on but it seems that if this is reproducible it should be explained.

    thanks

  • I could argue that SysCtlDelay is working perfectly according to its documentation which states merely that it provides a means of generating a constant length delay which is toolchain independent. It doesn't actually make any claims about the accuracy of said delay, however :-) That aside, there is one glaring error in the documentation. The line "The loop takes 3 cycles/loop," apart from being badly worded, is completely incorrect for our latest parts. This was written in the old days when the CPU speed topped out somewhere below the flash speed and all flash accesses occurred in a single clock cycle. As the CPU speed increased however, this was not possible so flash wait states and prefetch buffers came into play. With these, it makes it extremely difficult to accurately predict exactly how many cycles it will take for a given piece of code to execute in a real-world environment (i.e. one with interrupts going off and messing up the prefetch buffer at unpredictable times).

    If you need accurate timing, the real solution is to use a timer which will run for an exact number of system clocks regardless of interrupt activity then fire its own interrupt.

    If you can handle an approximate time delay and don't want to have to muck with interrupts, use ROM_SysCtlDelay() where the single cycle rule still applies and you don't have to worry about interrupts messing up the prefetch buffer. Note, however, that interrupts will still mess up the timing of ROM_SysCtlDelay() because every cycle spent processing an interrupt is a cycle that the delay loop isn't running.

    If you really don't care about accuracy and just want to delay some period at least as long as some value, use SysCtlDelay().

    I reckon I should update the function documentation to make it very clear that the presence of interrupts and flash wait states will cause the delay accuracy to vary enormously and to suggest people use ROM_SysCtlDelay instead of SysCtlDelay. Seem reasonable?

  • FAR OUT.    I wish I had known about this bug (or poorly documented feature if you are a TI employee;)  after wasting days getting inconsistent timing results with an 80MHz clock on my LM4F.    

    I can confirm that by switching to the ROM version my timing results are sufficiently accurate.  They were 100 % out !! I was getting 2ms delay rather than 1ms !!!  That is not 20% out as claimed above.  Now it is much improved.  At least no more than a few % out (if that), but without a putting a CRO on it I can't narrow it down any better.

    No doubt this problem will resurface when I migrate to the Tiva devices that support a 120MHz clock.    If it does then I would encourage TI to make this function (in both flash and ROM) work for all clock speeds and give us a non interrupt method of timing for simplicity's sake in your latest Tiva range !   Please ?

  • Peter,

      If you need accurate timing, SysCtlDelay() is definitely not the right thing to use on any Tiva part. It's a quick-and-dirty delay and was never intended to provide accurate delay timing. As I mentioned, the best approach for timing short delays accurately would be to use a hardware timer and trigger the end of your delay with the timer interrupt. Remember, too, that any software loop-based timing method (which is what ROM_SysCtlDelay() is) will suffer from different levels of inaccuracy depending upon the application's interrupt environment even if it is executing in a known number of cycles and doesn't have to worry about flash wait states and prefetch buffer misses. For accuracy in a polling loop you would need to turn all interrupts off and, obviously, that is generally a pretty bad idea.

      Something that does this could probably be added to our collection of utils but, given the wide variety of different delays people may want to time, I'm not sure how universally helpful it would be, though. We already ship timer examples which should provide good cut/paste candidates for people needing to do this so perhaps you could use one of these in the meantime?

  • Dave Wilson said:

     As I mentioned, the best approach for timing short delays accurately would be to use a hardware timer and trigger the end of your delay with the timer interrupt.

    Or use a timer in free running mode and "simply " look for time match.  Depending on other requirements, latentcies and the time it takes to do a proper comparison (You cannot do a simple equality check and you have to be aware of wrap-around) this can sometimes be superior to an interrupt based delay. On most architectures the performance will be comparable to an interrupt based delay.

    It retains the advantage of a simple polling timer delay without needing interrupts, particularly the simplicity.  It still works in the presence of other interrupts, which as you pointed out a loop counting delay does not.  It is accurate and with decent sized timers it has a large dynamic range.

    If you have a spare timer I can see no excuse for using delay loop for timing except when you are setting up a delay shorter than the minimal overhead to load and read the timer (or the jitter needs to be similarly small).  And if you are in the situation where you need to use instruction execution to time delays you really need to be using assembly.

     

    Robert

  • Robert,

      Indeed. I should have been clearer in my previous post since I was thinking about polling the timer's raw interrupt status. You could, of course, use the actual interrupt but, as you note, this creates additional complexity that may not be merited in many situations.

      Use of the interrupt may be a good idea in applications where power consumption is important and you are waiting significant periods of time. Rather than polling the interrupt status, setting a flag in the timer interrupt handler then having the delay function check this in a loop which also calls CPUwfi() (to put the CPU to sleep until the next interrupt) would likely be a better approach.

  • Hi Dave and Robert,

    Thanks for your replies.    I have used interrupts, SysCtrlDelay and polling a timer/counter value for controlling loop rates and delay times.  I liked the simplicity of SysCtrlDelay and was discouraged to learn how inaccurate it was.  At the very least this inaccuracy needs to be documented in the Programming Guide !

    Robert, as you mentioned with the polling method "You cannot do a simple equality check ".  Yes - I found it was necessary  to perform an inequality check.    i.e. poll the timer value until it falls below a threshold - because when performing an equality check you could miss the exact value if it is changing in fewer clock cycles than the number of clk cycles it takes to actually perform the equality check and then loop back around.

    rgds

    Peter

  • Peter,

      I've completely rewritten the documentation for SysCtlDelay(). You'll find the new version, based on discussion in this thread, in the TivaWare 2.1 release which will be hitting the streets in a few weeks.

  • Thanks Dave.

    rgds

    Peter