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を持っている。