I wrote a bare-metal timer program on an ARM platform with QEMU. The platform is versatilepb
. The timer is SP804 ARM Dual-Timer Module.
The SP804
provides 2 timer modules. Each module provides 2 timers. So there are totally 4 timers, namely Timer 0, 1, 2, 3
.
Timer 0/1 share the IRQ 4.
Timer 2/3 share the IRQ 5.
I noticed that if I start a single timer, or 2 timers from different timer modules, say, 0/2 or 1/3. The interrupt frequency is quite normal.
But If I start the 2 timers from the same timer module, say 0/1 or 2/3. The interrupt frequency is much higher.
And when I start the 2 timers from the same timer module, the first timer I started got much more IRQ frequency than the second one, while the latter one seems to have a normal IRQ frequency.
Below is some of my IRQ handler code:
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
// VIC status BITs: timer0,1=4, timer2,3=5
if (vicstatus & (1<<4))
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TVALUE) == 0) // timer 0
timer_handler(0);
if (*(timer[1].base + TVALUE) == 0) // timer 1
timer_handler(1);
}
if (vicstatus & (1<<5))
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TVALUE) == 0) // timer 2
timer_handler(2);
if (*(timer[3].base + TVALUE) == 0) // timer 3
timer_handler(3);
}
}
void timer_handler(u32 n)
{
TIMER *t = &timer[n];
t->tick++; // Assume 20 ticks per second. Need to calculate it for more accuracy.
if (t->tick == 20)
{
t->tick = 0;
t->ss++;
if (t->ss == 60)
{
t->ss = 0;
t->mm++;
if (t->mm == 60)
{
t->mm = 0;
t->hh++; // no 24 hour roll around
}
}
t->clock[7] = '0' + (t->ss % 10);
t->clock[6] = '0' + (t->ss / 10);
t->clock[4] = '0' + (t->mm % 10);
t->clock[3] = '0' + (t->mm / 10);
t->clock[1] = '0' + (t->hh % 10);
t->clock[0] = '0' + (t->hh / 10);
kprintf("Timer [%d]: %s\n", n, (u8 *)&t->clock[0]);
}
timer_clearInterrupt(n); // clear timer interrupt
}
Screenshot:
Single timer: (the timer has normal frequency)
2 timers 1/3 from different module: (both timers have normal frequency)
2 timers 2/3 from same module: (timer 2 started first and it has much higher frequency than 3. And the overall frequency of both 2/3 are much higher)
ADD 1 - 6:23 PM 5/4/2020
Thanks to jcmvbkb's comment. I change to use the Masked Interrupt Status
register (offset = TMIS
) to detect which timer is issuing the interrupt.
According to the spec:
This value is the logical AND of the raw interrupt status with the Timer Interrupt Enable bit from the control register, and is the same value which is passed to the interrupt output pin, TIMINTX.
The interrupt frequency of the 2 timers from the same timer module appears normal now.
I am still thinking about why the previous approach with the Current Value Register
cannot work. It seems so natural though.
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
//UART 0
if (vicstatus & UART0_IRQ_VIC_BIT)
{
uart_handler(&uart[0]);
}
//UART 1
if (vicstatus & UART1_IRQ_VIC_BIT)
{
uart_handler(&uart[1]);
}
// VIC status BITs: timer0,1=4, uart0=13, uart1=14
if (vicstatus & TIMER01_IRQ_VIC_BIT)
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TMIS) == 1) // timer 0 <===== HERE changed to use TMIS
timer_handler(0);
if (*(timer[1].base + TMIS) == 1) // timer 1 <===== HERE changed to use TMIS
timer_handler(1);
}
if (vicstatus & TIMER23_IRQ_VIC_BIT)
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TMIS) == 1) // timer 2 <===== HERE changed to use TMIS
timer_handler(2);
if (*(timer[3].base + TMIS) == 1) // timer 3 <===== HERE changed to use TMIS
timer_handler(3);
}
}
Below is screenshot for starting timer 2/3 from the same timer module:
Masked Interrupt Status Register
now. If you post your comment as an answer, I will be honored to mark it as the answer. I am still wondering why theCurrent Value Register
doesn't work. It seems so natural though... – smwikipedia