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.

AM335x DMTimer Enable/Disable is very slow (~60 us)

Other Parts Discussed in Thread: AM3359

Hello,

I have a board with the AM3359 processor, running at 600 MHz..

I am using CCS v5.4, SYS/BIOS 6.35.1.29, and the Sitara SYS/BIOS Industrial SDK v1.1.0.1.

I am using the DMTimer peripheral for my application.

More specifically, I am using DMTimer 3 in one-shot mode, and I start the timer roughly every 500us.

While using the RTA Execution Graph, I noticed that the ISR for the DMTimer takes an unusually long time. After commenting out "DMTimerDisable(SOC_DMTIMER_3_REGs)" I noticed that the execution time improved by 60us, indicating that disabling the DMTimer was taking 60us. The CPU Load of my application also fell from 55% to 45% when I commented out that one line of code. "DMTimerEnable(SOC_DMTIMER_3_REGs)" also takes 60us. Clearly, something is very wrong. A 600 MHz processor should not take 60us to write to a register.

I had some difficuly getting the SYS/BIOS HAL API and the StarterWare from the SYS/BIOS Industrial SDK to work well together when configuring the DMTimer peripheral. My previous difficulties with the configuration are discribed in this post: http://e2e.ti.com/support/arm/sitara_arm/f/791/t/194331.aspx

 I was finally able to get the timer configured, and it "works", but apparently there may still be some issues, as indicated by the extremely long time it takes to enable/disable the DMTimer.

I would appreciate any information that might help to diagnose why toggling the DMTimer peripheral is so sluggish.

Thanks.

  • Here is my ISR for DMTimer3:

     

    void ADC_Timer_ISR(void)    // DMTimer3 Interrupt Number = 69 (TINT3)
    {

     /* Stop the DMTimer */
     DMTimerDisable(SOC_DMTIMER_3_REGS);

     /* Disable the DMTimer interrupts */
     DMTimerIntDisable(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_EN_FLAG);

     /* Clear the status of the interrupt flags */
     DMTimerIntStatusClear(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_IT_FLAG);

        /* Load the counter with the reload count value */
        DMTimerCounterSet(SOC_DMTIMER_3_REGS, ADC_TIMER_RLD_COUNT);

        // Start ADC Timer Task
        Semaphore_post(SEM_ADC_TIMER); // Post semaphore for ADC Timer Task

     /* Enable the DMTimer interrupts */
        DMTimerIntEnable(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_EN_FLAG);

    }

     

    If I simply comment out "DMTimerDisable(SOC_DMTIMER_3_REGS)", I am able to shave 60us off the execution time of the ISR:

    void ADC_Timer_ISR(void)    // DMTimer3 Interrupt Number = 69 (TINT3)
    {

     /* Stop the DMTimer */
     //DMTimerDisable(SOC_DMTIMER_3_REGS);

     /* Disable the DMTimer interrupts */
     DMTimerIntDisable(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_EN_FLAG);

     /* Clear the status of the interrupt flags */
     DMTimerIntStatusClear(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_IT_FLAG);

        /* Load the counter with the reload count value */
        DMTimerCounterSet(SOC_DMTIMER_3_REGS, ADC_TIMER_RLD_COUNT);

        // Start ADC Timer Task
        Semaphore_post(SEM_ADC_TIMER); // Post semaphore for ADC Timer Task

     /* Enable the DMTimer interrupts */
        DMTimerIntEnable(SOC_DMTIMER_3_REGS, DMTIMER_INT_OVF_EN_FLAG);

    }

     

     

  • Here is a screen capture showing the result of that small code change.

     

    BEFORE commenting out "DMTimerDisable(SOC_DMTIMER_3_REGS)": ~123us execution time

     

    AFTER commenting out "DMTimerDisable(SOC_DMTIMER_3_REGS)":  ~63us execution time

     

    123us - 63us = 60us difference

  • Here are the functions I use to configure the timer.

     

    void Enable_Timers_32KHz()
    {
     WR_MEM_32(CM_PER_TIMER2_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER2_CLK, 0x2);

     WR_MEM_32(CM_PER_TIMER3_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER3_CLK, 0x2);

     WR_MEM_32(CM_PER_TIMER4_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER4_CLK, 0x2);

     WR_MEM_32(CM_PER_TIMER5_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER5_CLK, 0x2);

     WR_MEM_32(CM_PER_TIMER6_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER6_CLK, 0x2);

     WR_MEM_32(CM_PER_TIMER7_CLKCTRL, 0x2);
     WR_MEM_32(CLKSEL_TIMER7_CLK, 0x2);

            //printf("Timers 2-7 enabled for 32KHz.\n");
    }

     

    void DMTimer3ModuleClkConfig(void)
    {
        HWREG(SOC_CM_PER_REGS + CM_PER_L3S_CLKSTCTRL) =
                                 CM_PER_L3S_CLKSTCTRL_CLKTRCTRL_SW_WKUP;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L3S_CLKSTCTRL) &
         CM_PER_L3S_CLKSTCTRL_CLKTRCTRL) != CM_PER_L3S_CLKSTCTRL_CLKTRCTRL_SW_WKUP);

        HWREG(SOC_CM_PER_REGS + CM_PER_L3_CLKSTCTRL) =
                                 CM_PER_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L3_CLKSTCTRL) &
         CM_PER_L3_CLKSTCTRL_CLKTRCTRL) != CM_PER_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP);

        HWREG(SOC_CM_PER_REGS + CM_PER_L3_INSTR_CLKCTRL) =
                                 CM_PER_L3_INSTR_CLKCTRL_MODULEMODE_ENABLE;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L3_INSTR_CLKCTRL) &
                                   CM_PER_L3_INSTR_CLKCTRL_MODULEMODE) !=
                                       CM_PER_L3_INSTR_CLKCTRL_MODULEMODE_ENABLE);

        HWREG(SOC_CM_PER_REGS + CM_PER_L3_CLKCTRL) =
                                 CM_PER_L3_CLKCTRL_MODULEMODE_ENABLE;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L3_CLKCTRL) &
            CM_PER_L3_CLKCTRL_MODULEMODE) != CM_PER_L3_CLKCTRL_MODULEMODE_ENABLE);

        HWREG(SOC_CM_PER_REGS + CM_PER_OCPWP_L3_CLKSTCTRL) =
                                 CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_OCPWP_L3_CLKSTCTRL) &
                                  CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL) !=
                                    CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP);

        HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKSTCTRL) =
                                 CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL_SW_WKUP;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKSTCTRL) &
                                 CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL) !=
                                   CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL_SW_WKUP);

        HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKCTRL) =
                                 CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKCTRL) &
          CM_PER_L4LS_CLKCTRL_MODULEMODE) != CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE);

        /* Select the clock source for the Timer3 instance. */ 
        HWREG(SOC_CM_DPLL_REGS + CM_DPLL_CLKSEL_TIMER3_CLK) &=
              ~(CM_DPLL_CLKSEL_TIMER3_CLK_CLKSEL);

        HWREG(SOC_CM_DPLL_REGS + CM_DPLL_CLKSEL_TIMER3_CLK) |=
              CM_DPLL_CLKSEL_TIMER3_CLK_CLKSEL_SEL3;

        while((HWREG(SOC_CM_DPLL_REGS + CM_DPLL_CLKSEL_TIMER3_CLK) &
               CM_DPLL_CLKSEL_TIMER3_CLK_CLKSEL) !=
               CM_DPLL_CLKSEL_TIMER3_CLK_CLKSEL_SEL3);

        HWREG(SOC_CM_PER_REGS + CM_PER_TIMER3_CLKCTRL) |=
                                 CM_PER_TIMER3_CLKCTRL_MODULEMODE_ENABLE;

        while((HWREG(SOC_CM_PER_REGS + CM_PER_TIMER3_CLKCTRL) &
           CM_PER_TIMER3_CLKCTRL_MODULEMODE) != CM_PER_TIMER3_CLKCTRL_MODULEMODE_ENABLE);

        while((HWREG(SOC_CM_PER_REGS + CM_PER_TIMER3_CLKCTRL) &
           CM_PER_TIMER3_CLKCTRL_IDLEST) != CM_PER_TIMER3_CLKCTRL_IDLEST_FUNC);

        while(!(HWREG(SOC_CM_PER_REGS + CM_PER_L3S_CLKSTCTRL) &
                CM_PER_L3S_CLKSTCTRL_CLKACTIVITY_L3S_GCLK));

        while(!(HWREG(SOC_CM_PER_REGS + CM_PER_L3_CLKSTCTRL) &
                CM_PER_L3_CLKSTCTRL_CLKACTIVITY_L3_GCLK));

        while(!(HWREG(SOC_CM_PER_REGS + CM_PER_OCPWP_L3_CLKSTCTRL) &
               (CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L3_GCLK |
                CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L4_GCLK)));

        while(!(HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKSTCTRL) &
               (CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_L4LS_GCLK |
                CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_TIMER3_GCLK)));

    }


    void Enable_ADC_Timer_Clock()
    {

     /* This function will enable clocks for the DMTimer3 instance */
     DMTimer3ModuleClkConfig();

    }


    void Create_ADC_Timer_DMTimer()
    {
        //Timer_Handle ADC_Timer;
        Timer_Params ADC_Timer_Params;
        //Hwi_Params ADC_Timer_Hwi_Params;
        Error_Block eb;

        Error_init(&eb);
        Timer_Params_init(&ADC_Timer_Params);
        //Hwi_Params_init(&ADC_Timer_Hwi_Params);

        //ADC_Timer_Params.hwiParams = &ADC_Timer_Hwi_Params;
        ADC_Timer_Params.period = 500;
        ADC_Timer_Params.periodType = Timer_PeriodType_MICROSECS;
        ADC_Timer_Params.arg = 0;
        ADC_Timer_Params.startMode = Timer_StartMode_USER;
        ADC_Timer_Params.runMode = Timer_RunMode_ONESHOT;
        Timer_Handle ADC_Timer = Timer_create(1, (Timer_FuncPtr)ADC_Timer_ISR, &ADC_Timer_Params, &eb);

    }


    void Create_ADC_Timer_HWI()
    {
     Hwi_Handle OLD_ADC_Timer_Hwi;
     Hwi_Handle ADC_Timer_Hwi;
     Hwi_Params ADC_Timer_Hwi_Params;
        Error_Block eb;

        Error_init(&eb);
        Hwi_Params_init(&ADC_Timer_Hwi_Params);

        // Delete HWI previously created by Create_ADC_Timer_DMTimer()
        OLD_ADC_Timer_Hwi = Hwi_getHandle(SYS_INT_TINT3); // Returns Hwi_Handle associated with SYS_INT_TINT3
        if (OLD_ADC_Timer_Hwi != NULL)
        {
         Hwi_delete(&OLD_ADC_Timer_Hwi); // Finalize and free this previously allocated instance object, setting the referenced handle to NULL
        }

        // Create HWI for TINT3 for DMTimer3 for ADC Timer
        ADC_Timer_Hwi = Hwi_create(SYS_INT_TINT3, (Hwi_FuncPtr)ADC_Timer_ISR, &ADC_Timer_Hwi_Params, &eb);
        if (ADC_Timer_Hwi == NULL) {
          System_abort("Hwi create failed");
        }

    }


    void ADC_Timer_Config(void)
    {

        /* Configure the DMTimer for One-shot and no-compare mode */
        DMTimerModeConfigure(SOC_DMTIMER_3_REGS, DMTIMER_ONESHOT_NOCMP_ENABLE);

        /* Load the counter with the initial count value */
        DMTimerCounterSet(SOC_DMTIMER_3_REGS, ADC_TIMER_INITIAL_COUNT);

        /* Load the load register with the reload count value */
        DMTimerReloadSet(SOC_DMTIMER_3_REGS, ADC_TIMER_RLD_COUNT);

        /* Stop/Reset the DMTimer */
        DMTimerDisable(SOC_DMTIMER_3_REGS);

    }

     

  • I figured out what is wrong. I have been using a 32 kHz (32768 Hz) clock for my timers. The period for this clock is ~30 us. Apparently it takes a couple of clock cycles to start or stop the timer. Therefore, it took 2 x ~30 us = ~60 us. That explains why starting or stopping the timer took so long.

    I solved this problem by switching the timer clock sources to 24 MHz (CLK_M_OSC). This much faster clock has a period of a few 10s of nanoseconds, so the clock cycles or "ticks" are much faster. Therefore, the couple of clock cycles it takes to start or stop the timer are in the 100s of nanoseconds. This is a dramatic improvement over 60 us!

    So if you want to stop/start the timer quickly, you must use a high-frequency clock (i.e., CLK_M_OSC) for that timer.