IRQ 连接:两种机制
QEMU 中有两种 IRQ 连接机制,分别用于不同场景:
sysbus_connect_irq qdev_connect_gpio_out
设备 ─────────────────────── PLIC ─────────────────────── CPU
n = 中断源编号 irq = IRQ_S_EXT/M_EXT
外部设备 ──────────────────── GPIO ────────────────────── LED
qdev_connect_gpio_out qdev_connect_gpio_in机制 1: sysbus_init_irq + sysbus_connect_irq¶
通过 SysBus 总线连接,用于设备 → 中断控制器:
// 设备端 realize:
sysbus_init_irq(sbd, &s->irq); // 注册 IRQ 输出(索引 0)
// Board 端:
sysbus_connect_irq(sbd, 0, // 将设备的 IRQ[0]
qdev_get_gpio_in(plic, 10)); // 连接到 PLIC 的 GPIO_IN[10]特点:通过 SysBus 总线,按索引号匹配,一对一的设备→PLIC 连接。
机制 2: qdev_init_gpio_in/out + qdev_connect_gpio_out¶
不经过任何总线,板级直连,用于 PLIC → CPU、外设 ↔ 外设:
// PLIC → CPU(实现板级中断通路):
qdev_connect_gpio_out(plic_dev, hart_idx,
qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
// ↑ ↑
// PLIC 的 GPIO_OUT[hart] CPU 的 GPIO_IN[IRQ_S_EXT=9]
// 外设 → 外设(如按键→GPIO):
qdev_connect_gpio_out(key_dev, 0,
qdev_get_gpio_in(gpio_dev, 7));特点:无总线介入,名称/索引匹配,多用于芯片内部引脚对接。
Example. gpio
IRQ 路径:
GPIO.sysbus_irq[0] ──sysbus_connect_irq──→ PLIC.qdev_gpio_in[2]
│
qdev_connect_gpio_out
│
▼
CPU.qdev_gpio_in[IRQ_S_EXT=9]
│
riscv_cpu_set_irq
│
env->mip.SEIP = 1 → trap
数据路径(对比):
GPIO.output[0] ──qdev_connect_gpio_out──→ LED.qdev_gpio_in[0]Summay
从芯片规格到 QEMU 代码的映射:
───────────────────────────
1. 芯片引脚
├── MMIO 寄存器 → memory_region_init_io + sysbus_init_mmio
├── IRQ 输出 → sysbus_init_irq + sysbus_connect_irq → PLIC
└── GPIO 引脚 → qdev_init_gpio_in/out → 板级连线
2. 内部寄存器
→ C 结构体字段(uint32_t 映射每一位)
3. 状态转换
→ read/write 回调(每次 MMIO 访问触发)
4. 中断触发条件
→ g233_xxx_update_irq() → qemu_set_irq(s->irq, level)
5. 时间建模
→ 延迟追赶: last_update_ns + elapsed → 当前值
6. 总线互联(仅 SPI)
→ realize 中创建 SSI Bus
→ Board 中挂载从设备
→ GPIO 引脚连接 CS