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.

EK-TM4C129EXL: Getting a Fixed Period Short Delay

Part Number: EK-TM4C129EXL

Hi,

I need the two halves of timer A to run at the same frequency (2.5MHz @ CPU clock = 120MHz) but with a certain phase shift between them.

The code below is in theory what is needed but the execution time can vary if code AFTER it changes - I suspect this is due to pipelining etc.

TimerEnable(TIMER0_BASE, TIMER_B);

__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();
__nop();

TimerEnable(TIMER0_BASE, TIMER_A);

The __delay_cycles intrinsic, another option, carries the warning:

Note: Cycle timing is based on 0 wait states.
Results vary with additional wait states. The
implementation does not account for dynamic
prediction. Lower delay cycle counts may be
less accurate given pipeline flush behaviors.

- and behaved the same when tested.

I am aware of an instruction "ISB" that will flush the pipeline. Can you please recommend how this can be used in this instance, or any other way of getting the required delay accurately and independent of surrounding code changes? This code only needs to run once, at system startup.

Thank you,

Matthew

  • I found this which seemed to answer my question:

    CCS/EK-TM4C129EXL: EK-TM4C129EXL : delay time - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums

    So using MAP_SysCtlDelay (in ROM) will avoid the prefetch issues:

    "For better accuracy, the ROM version of this function may be used. This version will not suffer from flash- and prefect buffer-related timing variability but will still be delayed by interrupt service routines."

    I tried this and it does not fix the issue, suggesting that prefetch is not causing what I am seeing.

    I tried to ensure consistent alignment by putting the code into a function:

    __attribute__ ((aligned (4))) void set_hw_timers(void)
    {
    set_test_pin();
    TimerEnable(TIMER0_BASE, TIMER_B);
    MAP_SysCtlDelay(4);
    TimerEnable(TIMER0_BASE, TIMER_A);
    clear_test_pin();
    }

    Again this does not fix it.

    The variability seems to occur across builds, so adding/removing code elsewhere in the program affects the execution time of the code above. This difference is visible on a logic analyser. There is no optimisation in this program and no interrupts occurring when this code runs.

    Thanks,

    Matthew

  • "For better accuracy, the ROM version of this function may be used. This version will not suffer from flash- and prefect buffer-related timing variability but will still be delayed by interrupt service routines."

    Hi Matthew,

      ROM_SysCtlDelay would execute code from ROM which is single cycle. There is no piplining as in flash. Flash natively has a slower single cycle access time and therefore pipelining comes into the picture to read ahead of time before CPU reads. 

    I tried to ensure consistent alignment by putting the code into a function:

    __attribute__ ((aligned (4))) void set_hw_timers(void)
    {
    set_test_pin();
    TimerEnable(TIMER0_BASE, TIMER_B);
    MAP_SysCtlDelay(4);
    TimerEnable(TIMER0_BASE, TIMER_A);
    clear_test_pin();
    }

    Again this does not fix it.

    The variability seems to occur across builds, so adding/removing code elsewhere in the program affects the execution time of the code above. This difference is visible on a logic analyser. There is no optimisation in this program and no interrupts occurring when this code runs.

    How many cycles are you off between set_test_pin() and clear_test_pin()? I will suggest you not call set_test_pin and clear_test_pin because these two functions will subject to pipelining when fetching code from flash. Can you directly use HWREG to set and clear the pin. Perhaps for TimerEnable as well by directly using HWREG to enable Timer_B and Timer_A. Another option is to call ROM_TimerEnable instead of TimerEnable. You will need to do the same for any API calls in set_test_pin and clear_test_pin to only use the ROM version. Hopefully you will get a better control of the cycles. 

     

  • Hi Charles,

    Thank you for the advice, I will try these things and let you know how I get on.

    Regards,

    Matthew

  • Hi Charles,

    I have done some testing.

    Firstly I found that when switching off the prefetch using the following line as a test the variability went away:

    HWREG(FLASH_CONF) = FLASH_CONF_FPFOFF;

    I then changed the program as advised (below). There was still some variability until I put the function into RAM, now the cycles seem the same for all builds.

    I would not attempt to do this myself, but out of curiosity, do you ever have customers who switch off prefetch permanently? It would obviously slow down applications but would make the timing more deterministic which is important in certain cases.

    __attribute__ ((ramfunc)) void start_timer0_a_b(void)
    {
    // NOTE: Timer start needs to be offset for double edged clock to get correct phase
    // between continuous clock & SSI clock.
    #if 1
    MAP_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, GPIO_PIN_2);  // set test pin
    MAP_TimerEnable(TIMER0_BASE, TIMER_B); // DMA


    __nop();
    __nop();
    __nop();
    __nop();

    // Adjust NOPs to get desired phase shift

    MAP_TimerEnable(TIMER0_BASE, TIMER_A); // PWM
    MAP_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0);                   // clear test pin
    #endif
    }

    Regards,

    Matthew

  • There was still some variability until I put the function into RAM, now the cycles seem the same for all builds.

    Hi Matthew,

      Looks like you found a consistent solution across builds without variability. RAM access is always single cycle. Having said that, if you run the function from RAM, then I don't think you need to turn off prefetch for flash as prefetch will not affect the code execution from RAM. Is that what you are seeing?

  • Hi Charles,

    No - I didn't explain it well. I only switched off the prefetch temporarily to see if it was causing the problem, just to prove a point, then I switched it back on again and left it on.

    You are right, the code I showed, running from RAM, shows no variability with prefetch in its default state of being enabled.

    Regards,

    Matthew

  • running from RAM, shows no variability with prefetch in its default state of being enabled.

    Hi Matthew,

      Thanks for the confirmation.