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.

DMTimer accuracy deviation

Genius 5820 points
Other Parts Discussed in Thread: AM3359

I'm using some DMtimer-code to create regularly interrupts. The code is taken out of DMTimer-example for BeagleBone and is running with a frequency of 2 MHz and highest interrupt priority:

#define TIMER_INITIAL_COUNT (0xFFFFFFFFu-12)
#define TIMER_RLD_COUNT     (0xFFFFFFFFu-12)

IntPrioritySet(SYS_INT_TINT2, 0, AINTC_HOSTINT_ROUTE_IRQ);

Beside of this USB (out of USBDevSerial-example), Ethernet (out of enetLwip-example) and SD-card/FatFS is used, there the interrupts are running with lower priority in order to not to influence the DMtimer-interrupt:

// ethernet IRQs
IntPrioritySet(SYS_INT_3PGSWTXINT0, 1, AINTC_HOSTINT_ROUTE_IRQ);
IntPrioritySet(SYS_INT_3PGSWRXINT0, 1, AINTC_HOSTINT_ROUTE_IRQ);
...
//EDMA IRQs for SD-Card
IntPrioritySet(EDMA_COMPLTN_INT_NUM, 3, AINTC_HOSTINT_ROUTE_IRQ);
IntPrioritySet(EDMA_ERROR_INT_NUM, 3, AINTC_HOSTINT_ROUTE_IRQ);
//MMCSD-IRQs
IntPrioritySet(SYS_INT_MMCSD0INT, 3, AINTC_HOSTINT_ROUTE_IRQ);
IntPrioritySet(SYS_INT_MMCSD1INT, 3, AINTC_HOSTINT_ROUTE_IRQ);
...
//USB
IntPrioritySet(SYS_INT_USB0, 2, AINTC_HOSTINT_ROUTE_IRQ);

All of them run with a higher priority value, means a lower priority than the DMTimer interrupt.

Nevertheless I notice a deviation of timer accuracy, it is about factor 1,68 too slow.

Any ideas what could cause this? Are there any other interrupts I should care about? Or what else could slow down the DMtimer?

  • OK, I did some experiments and found out the following:

    - the timer interrupt is not only slowed down but there is also a big jitter on it

    - this slow down and jitter is mainly caused by some calls of GPIOMultiplePinsRead() within my main loop, it seems reading GPIO states from hardware registers (from outside of any ISR!) influences the timer/timer interrupt!

    - the lower priority IRQs have an influence too, they cause a smaller jitter, it seems the high-priority interrupt from DMTimer2 waits or drops when CPU is already running in a other ISR

    So my questions:

    How can I avoid that reading of GPIOs influences the DMTimer?

    Is there a possibility to let an DMTimer-IRQ interrupt a lower priority ISR?

    Thanks :-)

  • Meanwhile things become a bit more clear. Reading a GPIO DATAIN register harms the timer, writing data to some output GPIOs does not have any influence.

    The problem can be reproduced easily with the DMTimer-code shown in http://e2e.ti.com/support/embedded/starterware/f/790/t/324059.aspx , when there reading of a GPIO input is added to the main loop (after configuring it properly of course) the DMTimer no longer works stable but comes with a big jitter.

    So after I could not find any GPIO parameters in TRM that would explain this ... what could cause this behaviour?

  • It's not surprising that you're seeing the jitter.  You're using the timer at the extreme of its capability.

    You don't mention how you're measuring the timer's performance but I would assume you are toggling an I/O pin.  If so, adding the GPIO to you main loop means there's a very good chance that the bus from the CPU to the GPIO is busy when the interrupt occurs.  You won't be able to update the interrupt GPIO until that transaction finishes.

    I don't know what you need a 2Mhz timer for but you might want to consider moving that service to a PRU.

  • @James: the situation is quite different. The IO pins are toggeled within the 2 MHz-ISR which shoud - that's my understanding at least - run stable in every case.

    It's celar for me I can't get a stable frequency when the GPIO pin is toggeled within my main loop, therefore it is not done there but in ISR.

    In applications main loop I'm reading a GPIO pin only which causes the DMTimer to become instable (writing some other GPIO outputs does not have this influence).

  • So...nobody (from TI) has an idea why reading DATAIN of GPIO lets the DMTimer interrupt become inaccurate that much?

  • Just to make the problem more clear: the code shown below can be used to reproduce it. In DMTimerIsr a GPIO output is toggeled to check timer accuracy via oscilloscope and an other output is toggeled just to blink an LED.

    The whole thing works well and 100% accurate until the line

    GPIOPinRead(SOC_GPIO_2_REGS,BOOT_SWITCH_BIT);

    is added in main loop (not in ISR!). This thrashes the DMTimer and causes a huge jitter. Any idea why accessing DATAIN has this bad influence on DMTimer?

    #include "soc_AM335x.h"
    #include "beaglebone.h"
    #include "interrupt.h"
    #include "dmtimer.h"
    #include "error.h"
    #include "gpio_v2.h"
    #include "cache.h"
    #include "mmu.h"
    
    #define GPIO_INSTANCE_ADDRESS           (SOC_GPIO_1_REGS)
    #define GPIO_INSTANCE_PIN_NUMBER        (23)
    
    #define TIMER_INITIAL_COUNT             (0xFFFFFFFFu-12)
    #define TIMER_RLD_COUNT                 (0xFFFFFFFFu-12)
    
    static void DMTimerAintcConfigure(void);
    static void DMTimerSetUp(void);
    static void DMTimerIsr(void);
    
    static volatile unsigned int cntValue = 10;
    static volatile unsigned int flagIsr = 0;
    
    int cnt=0;
    
    #include "hw_control_AM335x.h"
    #include "soc_AM335x.h"
    #include "hw_cm_wkup.h"
    #include "beaglebone.h"
    #include "hw_cm_per.h"
    #include "hw_types.h"
    
    #define START_ADDR_DDR                     (0x80000000)
    #define START_ADDR_DEV                     (0x44000000)
    #define START_ADDR_OCMC                    (0x40300000)
    #define NUM_SECTIONS_DDR                   (512)
    #define NUM_SECTIONS_DEV                   (960)
    #define NUM_SECTIONS_OCMC                  (1)
    
    #ifdef __TMS470__
    #pragma DATA_ALIGN(pageTable, 16384);
    static volatile unsigned int pageTable[4*1024];
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment=16384
    static volatile unsigned int pageTable[4*1024];
    #else
    static volatile unsigned int pageTable[4*1024] __attribute__((aligned(16*1024)));
    #endif
    
    void MMUConfigAndEnable(void)
    {
        REGION regionDdr = {
                            MMU_PGTYPE_SECTION, START_ADDR_DDR, NUM_SECTIONS_DDR,
                            MMU_MEMTYPE_NORMAL_NON_SHAREABLE(MMU_CACHE_WT_NOWA,
                                                             MMU_CACHE_WB_WA),
                            MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                            (unsigned int*)pageTable
                           };
        REGION regionOcmc = {
                             MMU_PGTYPE_SECTION, START_ADDR_OCMC, NUM_SECTIONS_OCMC,
                             MMU_MEMTYPE_NORMAL_NON_SHAREABLE(MMU_CACHE_WT_NOWA,
                                                              MMU_CACHE_WB_WA),
                             MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                             (unsigned int*)pageTable
                            };
        REGION regionDev = {
                            MMU_PGTYPE_SECTION, START_ADDR_DEV, NUM_SECTIONS_DEV,
                            MMU_MEMTYPE_DEVICE_SHAREABLE,
                            MMU_REGION_NON_SECURE,
                            MMU_AP_PRV_RW_USR_RW  | MMU_SECTION_EXEC_NEVER,
                            (unsigned int*)pageTable
                           };
    
        MMUInit((unsigned int*)pageTable);
        MMUMemRegionMap(&regionDdr);
        MMUMemRegionMap(&regionOcmc);
        MMUMemRegionMap(&regionDev);
        MMUEnable((unsigned int*)pageTable);
    }
    
    #define BOOT_SWITCH_IN  (8)
    #define BOOT_SWITCH_BIT (1<<BOOT_SWITCH_IN)
    
    int main(void)
    {
       MMUConfigAndEnable();
       CacheEnable(CACHE_ALL);
    
       GPIO0ModuleClkConfig();
       GPIO1ModuleClkConfig();
       GPIO2ModuleClkConfig();
    
       GPIO1Pin23PinMuxSetup();
       HWREG(SOC_CONTROL_REGS + CONTROL_CONF_GPMC_AD(9)) = CONTROL_CONF_MUXMODE(7);
       HWREG(SOC_CONTROL_REGS + CONTROL_CONF_LCD_DATA(2)) = CONTROL_CONF_MUXMODE(7);
    
       GPIODirModeSet(SOC_GPIO_2_REGS,BOOT_SWITCH_IN,GPIO_DIR_INPUT);
    
       GPIOModuleEnable(SOC_GPIO_0_REGS);
       GPIOModuleEnable(SOC_GPIO_1_REGS);
       GPIOModuleEnable(SOC_GPIO_2_REGS);
    
       GPIOModuleReset(SOC_GPIO_0_REGS);
       GPIOModuleReset(SOC_GPIO_1_REGS);
       GPIOModuleReset(SOC_GPIO_2_REGS);
    
       GPIODirModeSet(SOC_GPIO_0_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_DIR_OUTPUT);
       GPIODirModeSet(SOC_GPIO_1_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_DIR_OUTPUT);
       GPIODirModeSet(SOC_GPIO_2_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_DIR_OUTPUT);
    
       DMTimer2ModuleClkConfig();
       IntMasterIRQEnable();
       DMTimerAintcConfigure();
       DMTimerSetUp();
       DMTimerIntEnable(SOC_DMTIMER_2_REGS, DMTIMER_INT_OVF_EN_FLAG);
       DMTimerEnable(SOC_DMTIMER_2_REGS);  
       while(1)
       {
           // the following line reads DATAIN register of GPIO2 -> this operation stalls the DMTimer
           GPIOPinRead(SOC_GPIO_2_REGS,BOOT_SWITCH_BIT);
       }
    }
    
    static void DMTimerAintcConfigure(void)
    {
        IntAINTCInit();
        IntRegister(SYS_INT_TINT2, DMTimerIsr);
        IntPrioritySet(SYS_INT_TINT2, 0, AINTC_HOSTINT_ROUTE_IRQ);
        IntSystemEnable(SYS_INT_TINT2);
    }
    
    static void DMTimerSetUp(void)
    {
        DMTimerPreScalerClkDisable(SOC_DMTIMER_2_REGS);
        DMTimerReset(SOC_DMTIMER_2_REGS);
        DMTimerCounterSet(SOC_DMTIMER_2_REGS, TIMER_INITIAL_COUNT);
        DMTimerReloadSet(SOC_DMTIMER_2_REGS, TIMER_RLD_COUNT);
        DMTimerModeConfigure(SOC_DMTIMER_2_REGS, DMTIMER_AUTORLD_NOCMP_ENABLE);
        DMTimerPostedModeConfig(SOC_DMTIMER_2_REGS,DMTIMER_POSTED);
    }
    
    static void DMTimerIsr(void)
    {
        DMTimerIntStatusClear(SOC_DMTIMER_2_REGS, DMTIMER_INT_OVF_IT_FLAG);
    
        if (cnt % 2==0) GPIOPinWrite(SOC_GPIO_0_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_PIN_HIGH);
        else GPIOPinWrite(SOC_GPIO_0_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_PIN_LOW);
    
        if (cnt==0) GPIOPinWrite(SOC_GPIO_1_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_PIN_HIGH);
        else if (cnt==200000) GPIOPinWrite(SOC_GPIO_1_REGS,GPIO_INSTANCE_PIN_NUMBER,GPIO_PIN_LOW);
        else if (cnt==400000) cnt=-1;
        cnt++;
    }
    

  • Just an other update: not only DMTimer is affected, it seems all interrupts are influenced. When I read GPIOs DATAIN register within a lwIP-application more often, receiving of data is slowed down dramatically. Currently I'd guess that happens because the interrupts used by lwIP-implementation are influenced by GPIOs somehow.

    Just to mention: my BeagleBone Black still comes with a AM3359 CPU.

    So every idea and suggestion is welcome!

  • Have you found a solution for this in the meantime?

    Regards,
    Daniel

  • Hi Daniel,

    unfortunately there is no real solution for this, also TI could not help. There are two reasons for the DMTimer working that bad:

    • reading of GPIO DATAIN register stalls the whole system including high priority interrupts; as workaround I set up GPIO interrupts that fire on every input state change so that reading DATAIN register of GPIO happens only when input state really has changed. The effect itself seems to be caused by the CPU design, whenever DATAIN is read hardware has to go through several buses and wait for snychronisation on each.
    • speed limit of 2 MHz is still an unsolved issue, here I definitely have to blame TI: whenever I have some really strong arguments why it should work, their support takes cover and does not respond any more. So what I noticed: independent on how short your code in ISR is so that it can be executed in a time that is MUCH shorter thant the timer-period, the ISR code influences the timer and slows it down. Only way to have an interrupt running faster than 2 MHz is to have no ISR and to toggle an GPO pin automatically (or in other words, to have an useless timer). In my optinion this is either a heavy bug in AM335x or there is something wrong in DMTimer-code, means some more is required than what is shown in example code

    Hans