QEMU ARM versatilepb平台上的SP804双定时器模块的IRQ频率不一致。

问题描述 投票:0回答:1

我在ARM平台上用QEMU写了一个裸机定时器程序。这个平台是 versatilepb. 计时器是 SP804 ARM 双定时器模块.

SP804 提供2个定时器模块。每个模块提供2个定时器。所以一共有4个定时器,分别是 Timer 0, 1, 2, 3.

定时器01共享IRQ 4.

定时器23共享IRQ 5.

我注意到,如果我启动一个 单一计时器或2个定时器 不同 定时器模块,比如02或13。中断频率很正常。

但是,如果我将2个定时器从 一样 定时器模块,比如01或23。中断频率要高很多。

而当我从同一个定时器模块启动这2个定时器时,在这2个定时器模块中的 第一 定时器的IRQ频率比我开始使用的那个定时器的IRQ频率高得多。第二 一个,而后一个似乎有一个正常的IRQ频率。

下面是我的一些IRQ处理代码。

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
}

截图:

单个定时器: (定时器有正常的频率)

enter image description here

2个不同模块的定时器13。(两个定时器都有正常频率)

enter image description here

同一模块的2个定时器23。(2号定时器先启动,频率比3号高得多,而且两个23号的整体频率也高得多) 而且两个23的整体频率都高得多)

enter image description here

ADD 1 - 6:23 PM 542020

谢谢 jcmvbkb的评论。我改用 Masked Interrupt Status 寄存器(偏移量= TMIS)来检测是哪个定时器在发出中断。

根据规范,该值是由控制寄存器中的原始中断状态与定时器中断使能位的逻辑AND。

该值是原始中断状态与控制寄存器中的定时器中断使能位的逻辑AND值 也是传递给中断输出引脚TIMINTX的相同值。

现在看来,同一定时器模块的2个定时器的中断频率是正常的。

我还在思考,为什么以前的方法与? Current Value Register 不能工作。不过看起来很自然。

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);
    }
}

下面是从同一个定时器模块启动定时器23的截图。

enter image description here

timer arm qemu versatilepb sp804
1个回答
1
投票

IRQ_handler使用 Current Value Register 来检测哪个定时器引起了中断,这看起来不对。有 Masked Interrupt Status Register 显然是为了这个目的,我建议用它来代替。此外,检查IRQ源,记录检查结果,并通过写到 Interrupt Clear Register 必须防止重入,否则单个定时器IRQ仍可能被多次计算。

我猜测,原来观察到的问题是因为定时器IRQ被其他东西触发,或者当定时器计数器为零时,IRQ_handler被其他IRQ调用。

© www.soinside.com 2019 - 2024. All rights reserved.