Use AON SPE to generate PPS (Pulse Per Second) that synced to Xavier system time

Hi,

I’m trying to use the AON SPE to generate Pulse Per Second from one of the GPIO pins on Xavier, and I would like to sync an IMU with the generated PPS.

I took a look at the provided demo code. The callback function in timer-app.c prints a statement every x amount of time, but the start of the timer interrupt isn’t on an integral second. I’m wondering if I can align the timer callback with the Xavier system time so that it prints on whole seconds?

Hello, hengruiz:
The start of timer interrupt depends on when the timer setup routine calls. It seems that you want to trigger the timer interrupt according to absolute counter, right? I don’t know why sync IMU need that feature. Can you explain details about ‘IMU sync’?

br
ChenJian

Thanks for the reply.

Our IMU driver needs a Pulse Per Second signal to reset the IMU internal clock counter every second and we use the sub-second part of the IMU internal clock as the timestamp for each of the IMU message. To achieve this we would like to have the PPS generated on each of the integral seconds of the Xavier system time.

The question is that if we can generate such PPS on the SPE using Xavier system time. Are the SPE time and the Xavier Linux system time synced? Can we start the timer interrupt such that it generates a pulse at whole seconds?

Hello, hengruiz:
There are several timers in Xavier, and you can refer to Xavier TRM for details.
Maybe you can configure the timer, trigger interrupt/read from SPE, and read from CCPLEX for the same timer. That may meet your sync request.

I am still not sure why you need PPS on ‘integral seconds’. Per my understanding, for time sync purpose, you just need a unique timer to trigger/record all events, instead of using both timer and system time.

br
ChenJian

Hi jachen,

Thanks for the reply.

The reason we need the PPS for the IMU is that we could use the second part from the Xavier system time and sub-second part from the IMU internal clock as the timestamp for each of the IMU message, as the PPS will reset the internal IMU clock every second, and we only care about the sub-second part of the IMU internal clock. Eventually, the timestamp would be a corrected timestamp in Xavier system time, where all our other sensors are synced to.

To achieve this, it would be great if we could have them trigger on each of the whole seconds so that we won’t need to do any offset on the IMU time messages (from IMU internal clock), as then we would know that it got reset on each of the integral seconds on the Xavier system time.

Could you also elaborate more on how to read the same timer from both SPE and CCPLEX? Should I just read from the register listed in the TRM?

Anther followup question: Do you think we can start a timer from CCPLEX and generate trigger pulse from SPE using the same timer?

Thank you again!

Hello, hengruiz:
Current SPE FW timer is configured as count-down triggered interrupt. You can try to configure timer absolute target register (search TMRATR in Xavier TRM for details.) and maybe that can meet your requirement.

You can access TMR_SHARED (0x03010000) in R5 by updating the file: ./bootloader/t186ref/BCT/tegra194-mb1-bct-scr-cbb-mini.cfg, with following change:
scr.1867.4 = 0x39000202; # TKE_TOP_SCR_TKESCR_0
scr.1867.4 = 0x3900ffff; # TKE_TOP_SCR_TKESCR_0

It’s not recommended to

start a timer from CCPLEX and generate trigger pulse from SPE using the same timer

br
ChenJian

Hi jachen,

I tried to set a target interrupt timer with TMRATR but somehow it doesn’t work as expected. Could you help take a look at what I did and try to see if there’s anything wrong?

  1. Set the access on TMR_SHARED in the bootloader config file:
scr.1867.4 = 0x3900ffff

I left scr.1868 to scr.1880 unchanged.

  1. Wrote a modified version of timer setup in tke_tegra.c, TKE_TIMER_TMRATR_0 here is defined as 0xc as shown in TRM:
void tegra_tke_set_up_target_timer(const struct tegra_tke_id *id,                
            uint32_t clk_src_sel, uint32_t target_count,                         
            tegra_tke_timer_callback callback, void *data)                       
{                                                                                
    /* struct tegra_tke_id assumed to be first field in tegra_tke */             
    struct tegra_tke *tke_dev = (struct tegra_tke *)id;                          
    uint32_t tmrcr;                                                              
                                                                                                                
    tke_dev->callback = callback;                                                                               
    tke_dev->data = data;                                                                                       
                                                                                                                
    /* select timer clock source */                                              
    tegra_tke_timer_writel(id, clk_src_sel, TKE_TIMER_TMRCSSR_0);                                               
                                                                                                                
    /* Disable timer */                                                          
    // henryzh: set count down to max since we actually want only target         
    //          interrupt. Need to find a way to actually disable this                                          
    tmrcr = TKE_TIMER_TMRCR_0_PTV;                                               
    tegra_tke_timer_writel(id, tmrcr, TKE_TIMER_TMRCR_0);                                                       
                                                                                 
    // henryzh: set the target counter to register TKE_TIMER_TMRATR_0            
    tegra_tke_timer_writel(id, target_count, TKE_TIMER_TMRATR_0);                                               
                                                                                 
    /* Clear timer interrupts. */                                                
    tegra_tke_timer_writel(id, TKE_TIMER_TMRSR_0_INTR_CLR, TKE_TIMER_TMRSR_0);   
                                                                                 
    /* Enable timer and configure timer type */                                  
    tmrcr |= TKE_TIMER_TMRCR_0_EN;                                               
    tegra_tke_timer_writel(id, tmrcr, TKE_TIMER_TMRCR_0);                        
                                                                                 
    if (id->irq != TEGRA_TKE_NO_IRQ) {                                           
        irq_set_handler(id->irq, tegra_tke_irq, tke_dev);                        
        irq_enable(id->irq);                                                     
    }                                                                            
}
  1. I then added another timer in the timerapp.c file, to use the target interrupt setup I wrote above:
static void timer2_callback(void *data) 
{                                                                                                                                        
    static int count = 0;                                                                                                                
    uint32_t usec, osc, tsc_hi, tsc_lo;                                                                                                  
                                                                                                                                         
    usec = tegra_tke_get_usec();                                                                                                         
    osc = tegra_tke_get_osc();                                                                                                           
    tegra_tke_get_tsc(&tsc_hi, &tsc_lo);                                                                                                 
    printf_isr("Timer2 irq triggered, usec = %lu, osc = %lu "                                                                            
           "tsc_hi = %lu, tsc_lo = %lu\r\n", usec, osc, tsc_hi, tsc_lo);                                                                 
    if (++count > STOP_TIMER)                                                                                                            
        tegra_tke_stop_timer(&tegra_tke_id_timer2);                                                                                      
                                                                                                                                         
}                                                                                                                                        
                                                                                                                                         
static void timer1_callback(void* data)                                                                                                  
{                                                                                
    uint32_t usec, osc, tsc_hi, tsc_lo;                                          
                                                                                 
    usec = tegra_tke_get_usec();                                                 
    osc = tegra_tke_get_osc();                                                   
    tegra_tke_get_tsc(&tsc_hi, &tsc_lo);                                         
    printf_isr("Timer1 irq triggered, usec = %lu, osc = %lu "                    
           "tsc_hi = %lu, tsc_lo = %lu\r\n", usec, osc, tsc_hi, tsc_lo);         
}                                                                                
                                                                                 
void timer_app_init(void)                                                        
{                                                                                
    uint32_t target_count = 1300000000;                                          
    // Start timer 1                                                             
    tegra_tke_set_up_target_timer(&tegra_tke_id_timer1,                          
                    TEGRA_TKE_CLK_SRC_TSC_BIT0, target_count,                    
                    timer1_callback, 0);                                         
    // Start timer 2                                                             
    tegra_tke_set_up_timer(&tegra_tke_id_timer2, TEGRA_TKE_CLK_SRC_TSC_BIT0,     
                   true, TIMER2_PTV, timer2_callback, 0);                        
}
  1. The result is that I only have timer2 (the original count down one) working and printing in callback but never got the target interrupt to trigger. Here is an output from the serial port. I am expecting the timer1 to trigger since I set the target count of tsc_lo to 1300000000.
Timer2 irq triggered, usec = 22864391, osc = 877992652 tsc_hi = 0, tsc_lo = 703476123
Timer2 irq triggered, usec = 26064391, osc = 1000872652 tsc_hi = 0, tsc_lo = 803476123
Timer2 irq triggered, usec = 29264391, osc = 1123752652 tsc_hi = 0, tsc_lo = 903476123
Timer2 irq triggered, usec = 32464391, osc = 1246632652 tsc_hi = 0, tsc_lo = 1003476123
Timer2 irq triggered, usec = 35664391, osc = 1369512652 tsc_hi = 0, tsc_lo = 1103476123
Timer2 irq triggered, usec = 38864391, osc = 1492392652 tsc_hi = 0, tsc_lo = 1203476123
Timer2 irq triggered, usec = 42064391, osc = 1615272652 tsc_hi = 0, tsc_lo = 1303476123
Timer2 irq triggered, usec = 45264391, osc = 1738152652 tsc_hi = 0, tsc_lo = 1403476123
Timer2 irq triggered, usec = 48464391, osc = 1861032652 tsc_hi = 0, tsc_lo = 1503476123
Timer2 irq triggered, usec = 51664391, osc = 1983912652 tsc_hi = 0, tsc_lo = 1603476123
Timer2 irq triggered, usec = 54864391, osc = 2106792652 tsc_hi = 0, tsc_lo = 1703476123
  1. Here is an update: I actually see timer1 got triggered, but the count doesn’t seem to make sense to me as I set it to be 1300000000, but the actual count is 540346938. How are these two numbers associated?
Timer2 irq triggered, usec = 13264391, osc = 509352652 tsc_hi = 0, tsc_lo = 403476123
Timer2 irq triggered, usec = 16464391, osc = 632232652 tsc_hi = 0, tsc_lo = 503476123
Timer1 irq triggered, usec = 17644257, osc = 677539508 tsc_hi = 0, tsc_lo = 540346938  <---- Here
Timer2 irq triggered, usec = 19664391, osc = 755112652 tsc_hi = 0, tsc_lo = 603476123
Timer2 irq triggered, usec = 22864391, osc = 877992652 tsc_hi = 0, tsc_lo = 703476123
Timer2 irq triggered, usec = 26064391, osc = 1000872652 tsc_hi = 0, tsc_lo = 803476123
Timer2 irq triggered, usec = 29264391, osc = 1123752652 tsc_hi = 0, tsc_lo = 903476123
Timer2 irq triggered, usec = 32464391, osc = 1246632652 tsc_hi = 0, tsc_lo = 1003476123

Another update: Turns out this might be the count down in timer1 as I set the count down to TKE_TIMER_TMRCR_0_PTV which is 0x1ffffff = 536870911. How do I make the timer just to the target interrupt without count down?

Thank you so much for your time.

It seems that in general there is no documentation on how to enable the absolute timer target register. Wouldn’t it be expected that there is a bit that should be set to enable triggering on the absolute target instead of the count down?

Hello, hengruiz:
We are checking this internally and will update once we get progress.

thanks for patience.

br
ChenJian

Hi hengruiz,

where is the demo code? thx

Hello, garretzou
timer test code is rt-aux-cpu-demo/app/timer-app.c

br
Chenjian

Ideally, TMRATR functional is not needed and highly optional. It is meant for absolute timings, for example, if one wants to avoid even the bus cycle delays incurred during the PTV value writes between two timer interrupts. For time sync between CCPLEX and SPE, one possible solution could be as follows: Use TSC[29:0] source id 2 as a base source which is systemwide and regular PTV interrupt, have CCPLEX send message to SPE through IVC (example is given at ivc-echo-task.c) about future time where it wants interrupt at SPE side. CCPLEX can also have timer running based on the same TSC and have its interrupt triggered where it can send IVC to SPE. Only issue in that case would be the latency of interrupt in two Processors, bus cycles needed to actually write the PTV value and IVC message passing overhead (So if you program to trigger interrupt in future time X, actual interrupt may happen at X + bus cycles + interrupt latency + IVC overhead, IVC overhead can be easily accounted for while other two factors are not). If TMRATR was instead used it will be only IVC message passing overhead. I am yet to figure out why you are getting interrupt AT PTV even though you are programming ATR, but if that level of accuracy is not needed, you can still work through using PTV only like I mentioned above.

Did you have periodic bit set for target routine? If it is so, first interrupt will happen at a specific time using ATR, then you get a stream of interrupts with a period controlled by PTV. So periodic bit should be disabled while using ATR. I tried setting PTV = max and ATR to some current + future time without periodic bit and I can see it triggered at the time ATR is programmed.