Xilinx QEMU GPIO interrupts

QEMUの割り込みはqemu_irq型の変数で表される。
(hw/core/irq.c) void qemu_set_irq(qemu_irq irq, int level)によって割り込みを発生させることができる。
割り込みを発生させたとき、qemu_irqに登録されたハンドラが呼び出される。

Xilinx QEMU ZynpmpのGPIOのハンドラは(hw/gpio/xlnx-zynqmp-gpio.c) zynqmp_gpio_in_handler。

static void zynqmp_gpio_in_handler(void *opaque, int n, int level)
{
    XlnxZynqmpGPIO *s = XLNX_ZYNQMP_GPIO(opaque);
    int offset = 0;
    int bank = gpio_get_bank_by_pin(n, &offset);
    uint32_t mask;
    uint32_t data_old = s->regs[R_GPIO_DATA_X(bank)];

    /* Accept the data only if gpio is set as input */
    mask = ~(s->regs[R_GPIO_DIRM_X(bank)]);

    if (mask & (1 << offset)) {
        DPRINT("gpio in[%d] set to %d\n", n, level);
        s->regs[R_GPIO_DATA_X(bank)] =
            deposit32(s->regs[R_GPIO_DATA_X(bank)], offset, 1, level);

        gpio_update_isr(s, bank, offset, level,
                        extract32(data_old, offset, 1));
    }
    gpio_update_irq(s);
}

引数のうち割り込み番号nでlevel(0か1)が変化後の信号値を意味する。

バンクは適当な範囲の番号の割り込みをまとめた単位。例えば、値は本物ではないが、バンク1は32番から63番、バンク2は64番から95番を管理するといった感じ。バンクごとに以下のレジスタがある。
R_GPIO_DATA_X: 割り込みピンの値。各ビット=ピン
R_GPIO_DIRM_X: ピンの入出力設定。
R_GPIO_INT_POL_X: 検出する変化の設定。ポジティブエッジなら1。
R_GPIO_INT_STAT_X: 割り込み状態。
R_GPIO_INT_MASK_X: マスク。

このハンドラでは、入力ピンについてR_GPIO_DATA_Xの値を更新し、その後gpio_update_isrを呼び出して、R_GPIO_INT_STAT_Xを更新する。
最後にgpio_update_irqを呼び出して、次の割り込みを発生させる。

static void gpio_update_isr(XlnxZynqmpGPIO *s, int bank, int pin_offset,
                            uint32_t level, uint32_t level_old)
{
    uint32_t pol = s->regs[R_GPIO_INT_POL_X(bank)] & (1 << pin_offset);

    if ((level == 0 && level_old == 1 && !pol) || /* Falling Edge */
       (level == 1 && level_old == 0 && pol)) {   /* Raising Edge */
        s->regs[R_GPIO_INT_STAT_X(bank)] =
           deposit32(s->regs[R_GPIO_INT_STAT_X(bank)], pin_offset, 1, level);
    }
}

割り込みがあったピンのR_GPIO_INT_POL_Xの値を確認して、該当する値の変化だったらR_GPIO_INT_STAT_Xに値を書き込む。

static void gpio_update_irq(XlnxZynqmpGPIO *s)
{
    bool line;
    int i;

    /* if mask high, interrupt disabled
     * if mask low, interrupt enabled
     */
    for (i = 0; i < ZYNQMP_GPIO_NUM_BANKS; i++) {
        line = !!(~s->regs[R_GPIO_INT_MASK_X(i)] &
                   s->regs[R_GPIO_INT_STAT_X(i)]);
        qemu_set_irq(s->irq, line);
    }
}

全バンクの割り込みレジスタを確認して、マスクされてない割り込みがあればqemu_set_irqを使って上位側に割り込みを発生させる。
// マスクしててもline=falseで割り込みが発生してしまうように見えるのだが、正しいのだろうか? よく分からない。


補足

ちなみにdeposit32は32ビットの元値の一部のビットフィールドだけ書き換えて返す関数であり、extract32は32ビットの元値の一部のビットフィールドだけを返す関数である。

s->irqにあるのは、gpio_init()関数でsysbus_init_irqを使って初期化されたqemu_irq変数である。
(hw/core/sysbus.c)sysbus_init_irqでは出力qemu_irqとして初期化が行われる。

void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
{
    qdev_init_gpio_out_named(DEVICE(dev), p, SYSBUS_DEVICE_GPIO_IRQ, 1);
}

gpio_realize()で行われているように、GPIOにはirq以外にgpio_outとgpio_oenという出力側qemu_irqを持っている。

この記事が気に入ったらサポートをしてみませんか?