diff --git a/articles/20221120-riscv-kvm-int-impl-1.md b/articles/20221120-riscv-kvm-int-impl-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..aeef44c63a74a83b5e2ccc5498ffb4d7c7526f57
--- /dev/null
+++ b/articles/20221120-riscv-kvm-int-impl-1.md
@@ -0,0 +1,664 @@
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [tounix spaces]
+> Author: XiakaiPan <13212017962@163.com>
+> Date: 20221201
+> Revisor: Walimis
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [RISC-V 虚拟化技术调研与分析](https://gitee.com/tinylab/riscv-linux/issues/I5E4VB)
+> Sponsor: PLCT Lab, ISCAS
+
+# RISC-V 中断处理的实现(一)
+
+## 前言
+
+本文就 Spike 和 QEMU 中与 RISC-V Trap 处理相关的实现细节进行了较为深入的挖掘,包括 PLIC 和 CLINT 等在模拟器中的实现。从模拟器的角度来看,当前它们对于虚拟化扩展的支持,是在其内部中断处理机制的基础上,添加了虚拟化所需的 CSR 以及对应的 Trap 判断与触发逻辑。
+
+## 软件版本
+
+| Software | Version |
+|------------|------------------------------------------|
+| [QEMU][5] | 7.1.0 |
+| [Spike][6] | 70f8aa01b803f4dbc0461fd7c986c1ca76d4b1d9 |
+
+## Spike 实现分析
+
+### Trap 统一编码与命名习惯
+
+RISC-V Trap 包含中断和异常,其编码在 [自动生成][1] 的编码文件中分别以如下名称开头:
+
+- IRQ:Interrupt ReQuest,用于表示中断编码
+- CAUSE:cause 是 RISC-V 中用于保存 trap 的具体编码的 CSR,包括 `mcause`, `scause`, `vscause` 等,此处专门用于表示异常编码
+
+### Trap 处理函数调用
+
+#### 中断处理
+
+##### 中断定义
+
+```cpp
+// riscv/encoding.h: line 266
+#define IRQ_U_SOFT 0
+#define IRQ_S_SOFT 1
+#define IRQ_VS_SOFT 2
+#define IRQ_M_SOFT 3
+#define IRQ_U_TIMER 4
+#define IRQ_S_TIMER 5
+#define IRQ_VS_TIMER 6
+#define IRQ_M_TIMER 7
+#define IRQ_U_EXT 8
+#define IRQ_S_EXT 9
+#define IRQ_VS_EXT 10
+#define IRQ_M_EXT 11
+#define IRQ_S_GEXT 12
+#define IRQ_COP 12
+#define IRQ_LCOF 13
+```
+
+Spike 定义了 `sim_t` 类作为最外层的模拟管理器,其包含了一个 `processor` vector,Spike 模拟 CPU 的执行即是 `sim` 的各个 `processor` 调用 `step` 函数执行 fetch, decode, execute 等操作。trap 处理操作即在 `step` 函数中实现。
+
+具体来说,trap 处理包含了两个步骤:
+
+##### 判断当前是否需要进行 Interrupt 处理
+
+当且仅当 `mie` 和 `mip` 两个 CSR 的值,进行逻辑与不为 0 时,trap 才会被处理:
+
+```cpp
+void take_pending_interrupt() { take_interrupt(state.mip->read() & state.mie->read()); }
+```
+
+##### 确定 Interrupt 的内容
+
+`void take_interrupt(reg_t mask);`:如果 `mask` 为 0 则不执行 trap 处理,否则将判断 trap 具体类型,抛出一个 trap(`trap_t`)
+
+```cpp
+void processor_t::take_interrupt(reg_t pending_interrupts)
+{
+ // 不执行中断处理
+ // Do nothing if no pending interrupts
+ if (!pending_interrupts) {
+ return;
+ }
+
+ // 依照 M, HS, VS 的顺序确定具体要处理哪一个特权级的 trap
+ // M-ints have higher priority over HS-ints and VS-ints
+ const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE);
+ const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
+ reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled;
+ if (enabled_interrupts == 0) {
+ // HS-ints have higher priority over VS-ints
+ const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read();
+ const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE);
+ const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie);
+ enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled;
+ if (state.v && enabled_interrupts == 0) {
+ // VS-ints have least priority and can only be taken with virt enabled
+ const reg_t deleg_to_vs = state.hideleg->read();
+ const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
+ enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled;
+ }
+ }
+
+ // 按照 MEI, MSI, MTI; SEI, SSI, STI (HS > VS) 的优先级确定 mcause/scause 最高位之外的内容
+ if (!state.debug_mode && enabled_interrupts) {
+ // nonstandard interrupts have highest priority
+ if (enabled_interrupts >> (IRQ_M_EXT + 1))
+ enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1);
+ // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI
+ else if (enabled_interrupts & MIP_MEIP)
+ enabled_interrupts = MIP_MEIP;
+ else if (enabled_interrupts & MIP_MSIP)
+ enabled_interrupts = MIP_MSIP;
+ else if (enabled_interrupts & MIP_MTIP)
+ enabled_interrupts = MIP_MTIP;
+ else if (enabled_interrupts & MIP_SEIP)
+ enabled_interrupts = MIP_SEIP;
+ else if (enabled_interrupts & MIP_SSIP)
+ enabled_interrupts = MIP_SSIP;
+ else if (enabled_interrupts & MIP_STIP)
+ enabled_interrupts = MIP_STIP;
+ else if (enabled_interrupts & MIP_LCOFIP)
+ enabled_interrupts = MIP_LCOFIP;
+ else if (enabled_interrupts & MIP_VSEIP)
+ enabled_interrupts = MIP_VSEIP;
+ else if (enabled_interrupts & MIP_VSSIP)
+ enabled_interrupts = MIP_VSSIP;
+ else if (enabled_interrupts & MIP_VSTIP)
+ enabled_interrupts = MIP_VSTIP;
+ else
+ abort();
+
+ // 抛出异常编码(最高位为 1)
+ throw trap_t(((reg_t)1 << (isa->get_max_xlen() - 1)) | ctz(enabled_interrupts));
+ }
+}
+```
+
+#### 异常处理
+
+##### 异常定义
+
+如上节所述,异常的指令集编码即宏定义命名由生成的 `encoding.h` 指定,之后在 `trap.h` 通过宏定义为每一类异常定义一个 `trap_t` 的派生类。
+
+RISC-V 指令集规定的所有异常编码及其命名如下代码块所示:
+
+```cpp
+// riscv/encoding.h: line 3147
+#define CAUSE_MISALIGNED_FETCH 0x0
+#define CAUSE_FETCH_ACCESS 0x1
+#define CAUSE_ILLEGAL_INSTRUCTION 0x2
+#define CAUSE_BREAKPOINT 0x3
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_LOAD_ACCESS 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_STORE_ACCESS 0x7
+#define CAUSE_USER_ECALL 0x8
+#define CAUSE_SUPERVISOR_ECALL 0x9
+#define CAUSE_VIRTUAL_SUPERVISOR_ECALL 0xa
+#define CAUSE_MACHINE_ECALL 0xb
+#define CAUSE_FETCH_PAGE_FAULT 0xc
+#define CAUSE_LOAD_PAGE_FAULT 0xd
+#define CAUSE_STORE_PAGE_FAULT 0xf
+#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14
+#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15
+#define CAUSE_VIRTUAL_INSTRUCTION 0x16
+#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17
+```
+
+将 exception cause 编码与特定类绑定是通过如下代码实现的,共有三类 exception 类型的 trap:
+
+- `MEM_TRAP`:访存相关的异常,如地址对齐、page-fault
+- `TRAP`:系统调用指令 `ecall` 和 中断指令 `ebreak`
+- `INSN_TRAP`:指令相关异常,如非法指令、虚拟指令特权级问题
+
+```cpp
+// riscv/trap.h: line 91
+#define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \
+ public: \
+ trap_##x(bool gva, reg_t tval, reg_t tval2, reg_t tinst) : mem_trap_t(n, gva, tval, tval2, tinst) {} \
+ const char* name() { return "trap_"#x; } \
+};
+#define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \
+ public: \
+ trap_##x() : trap_t(n) {} \
+ const char* name() { return "trap_"#x; } \
+};
+#define DECLARE_INST_TRAP(n, x) class trap_##x : public insn_trap_t { \
+ public: \
+ trap_##x(reg_t tval) : insn_trap_t(n, /* gva */false, tval) {} \
+ const char* name() { return "trap_"#x; } \
+};
+// ...
+
+// riscv/trap.h: line 103
+DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned)
+DECLARE_TRAP(CAUSE_USER_ECALL, user_ecall)
+DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction)
+// ...
+```
+
+综上,所有 trap(exception,interrupt)在 Spike 代码中的表示方式如下图所示:
+
+```mermaid
+flowchart BT
+
+subgraph interrupt
+int[interrupt]
+end
+
+subgraph riscv/trap.h: abstract trap
+direction LR
+t[trap_t]---int
+it[insn_trap_t]-->t
+mt[mem_trap_t]-->t
+end
+
+subgraph riscv/trap.h: all exceptions
+
+tmf[trap_instruction_address_misaligned]
+tii[trap_illegal_instruction]
+tec[trap_user_ecall]
+oe[other exceptions, ...]
+
+tmf-->mt
+tii-->it
+tec-->t
+
+oe-.->it
+oe-.->mt
+oe-.->t
+end
+
+subgraph riscv/encoding.h
+mf[CAUSE_MISALIGNED_FETCH]
+ii[CAUSE_ILLEGAL_INSTRUCTION]
+ec[CAUSE_USER_ECALL]
+oc[other causes, ...]
+end
+
+mf-->tmf
+ii-->tii
+ec-->tec
+oc-->oe
+```
+
+([下载由 Mermaid 生成的 PNG 图片][007])
+
+##### 异常抛出
+
+整个模拟器在执行过程中,通过 `try, catch` 来捕获并处理每个处理器执行中的异常,这些异常是在各个组件以及指令中写定的。例如访存相关的异常都是在 MMU 的实现中规定的:
+
+```cpp
+// riscv/mmu.h: line 364
+// ITLB lookup
+inline tlb_entry_t translate_insn_addr(reg_t addr) {
+ // ...
+ return fetch_slow_path(addr);
+}
+// riscv/mmu.cc: line 80
+tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
+{
+ // ...
+ if (!mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp))
+ throw trap_instruction_access_fault(proc->state.v, vaddr, 0, 0);
+ result = {(char*)&fetch_temp - vaddr, paddr - vaddr};
+ // ...
+ return result;
+}
+```
+
+单个处理器的执行主题循环如下所示,异常和中断均是在其中的 `try, catch` 中捕获并处理的:
+
+```cpp
+// riscv/execute.cc: line 219
+// fetch/decode/execute loop
+void processor_t::step(size_t n)
+{
+ // ...
+
+ while (n > 0) {
+ // ...
+
+ try
+ {
+ take_pending_interrupt();
+
+ // 仿真的循环主体:抛出执行过程中的异常,之后进入 catch 中的处理语句
+ // Main simulation loop, slow/fast path: throw exceptions if they occur during execution
+ // ...
+ }
+ catch(trap_t& t)
+ {
+ take_trap(t, pc);
+ // ...
+ }
+ // Other catch statements, instructions counting.
+ // ...
+ }
+}
+
+```
+
+#### Trap 处理
+
+整个 trap 的处理过程如下图所示:
+
+虚线表示函数对应的实现,细实线表示函数调用关系,粗实线表示参数的传递关系。
+
+```mermaid
+flowchart TB
+
+subgraph sim.h
+ss[sim_t]
+end
+
+subgraph execute.c
+ps[step]
+
+subgraph try_catch
+try-->
+ttpi[take_pending_interrupt]
+
+try-->mem[mmu]==throw mem_trap_t==>c
+
+try-->o[others]==throw insn_trap_t==>c
+end
+
+ps-->try_catch
+c[catch take_trap]
+end
+
+subgraph processor.cc
+s[sim_t::procs->step]
+tpi[take_pending_interrupt]
+ti[take_interrupt]
+tt[take_trap]
+end
+
+subgraph mmu
+li[load_insn]
+ai[access_icache]
+end
+
+mem-->li
+mem-->ai
+
+ss-->s-.->ps
+ttpi-.->tpi-->ti
+c-.->tt
+
+ttpi==throw trap_t==>c
+ti==interrupt==>tt
+```
+
+([下载由 Mermaid 生成的 PNG 图片][008])
+
+由图可知,最终的 trap 处理是通过调用 `riscv/process.cc` 的 `take_trap` 函数实现的,其定义及功能分析参见 [此文][2] 的模拟器实现中的 Spike 小节。简单来说,它完成了如下任务:
+
+- 确定该 trap 的类型(异常或者中断)以及它将在哪个特权级被处理(默认 M,但可以通过 medeleg/mideleg 和 hedeleg/hideleg 委托给 HS 或 VS)
+- 写入 CSR(`cause`, `epc`, `tval`)以记录 trap 内容,保存当前执行环境用于处理完成之后的恢复
+- 修改 `status` 寄存器及当前特权级,进入 trap 处理程序运行环境
+
+### CLINT
+
+CLINT(Core-Local INTerrupt)是 RISC-V 核间局部中断控制器,用于管理软件中断和计时器中断的注入和解除。
+
+在 Spike 中,CLINT 是 `bus` 上的设备之一 `device`,CPU 对 CLINT 和 `mems` 等的访问通过 `mmio.store/load` 来实现。所有的 mmio 访存均是通过调用 `bus.load/store` 完成的,例如 mmu_t 和 sim_t 的 load, store 操作:
+
+```mermaid
+flowchart
+
+subgraph riscv/abstract_device.h
+ad[abstract_device_t]
+end
+
+subgraph riscv/devices.h
+direction TB
+cl[clint_t]-->ad
+rom[rom_device_t]-->ad
+mem[mem_t]-->ad
+plic[plic_t]-->ad
+ns[ns16550_t]-->ad
+plg[mmio_plugin_device_t]-->ad
+
+subgraph bus_t
+d[devices:map]
+access[load/store]
+end
+
+cl-.-d
+rom-.-d
+mem-.-d
+plic-.-d
+ns-.-d
+plg-.-d
+
+bus_t-->ad
+end
+
+subgraph riscv/sim.cc:sim_t
+ldst[mmio_load/store]-->access
+end
+
+subgraph riscv/mmu.cc:mmu_t
+direction LR
+mldst[mmio_load/store]-->ldst
+spi[store_slow_path_intrapage]-->mldst
+ssp[store_slow_path]-->spi
+store[store, ...]-->ssp
+end
+```
+
+([下载由 Mermaid 生成的 PNG 图片][009])
+
+而 CPU 通过 `bus` 实现的访存,是先通过地址所在的范围确定对应的具体设备,进而调用该设备的访存函数来完成的:
+
+```cpp
+bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes)
+{
+ // Find the device with the base address closest to but
+ // less than addr (price-is-right search)
+ auto it = devices.upper_bound(addr);
+ if (devices.empty() || it == devices.begin()) {
+ // Either the bus is empty, or there weren't
+ // any items with a base address <= addr
+ return false;
+ }
+ // Found at least one item with base address <= addr
+ // The iterator points to the device after this, so
+ // go back by one item.
+ it--;
+ return it->second->load(addr - it->first, len, bytes);
+}
+
+bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes)
+{
+ auto it = devices.upper_bound(addr);
+ if (devices.empty() || it == devices.begin()) {
+ return false;
+ }
+ it--;
+ return it->second->store(addr - it->first, len, bytes);
+}
+```
+
+对于 CLINT 来说,其所担负的软件中断和计时器中断的职能,就是通过 `clint_t` 的 `load/store` 方法配合对应 CSR 的访问来实现的,参考 [此文][3]:
+
+```cpp
+// riscv/clint.cc: line 48
+bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes)
+{
+ // 若地址在 MSIP 内,表示当前中断为软件中断,将 byte 内的内容写入 addr 作为偏移量所指示的位置,即写入软件中断
+ if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) {
+ std::vector msip(procs.size());
+ std::vector mask(procs.size(), 0);
+ memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len);
+ memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len);
+ for (size_t i = 0; i < procs.size(); ++i) {
+ if (!(mask[i] & 0xFF)) continue;
+ procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, 0);
+ if (!!(msip[i] & 1))
+ procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP);
+ }
+ } else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) {
+ // 设置计时器中断:向 mtimecmp 寄存器写入特定值,待到 mtime 达到该值时产生一个中断
+ memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len);
+ } else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) {
+ // 设置 mtime 的值
+ memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len);
+ } else {
+ return false;
+ }
+ increment(0);
+ return true;
+}
+```
+
+### PLIC/Interrupt Controller/NS16550
+
+PLIC(Platform-Level Interrupt Controller)是 RISC-V 外部中断控制器,而 NS16550 则是一个 [UART(Universal Asynchronous Receiver/Transmitter)软核][4],用于 CPU 和外设之间的通信。CLINT,PLIC 以及 NS16550 都要挂载在设备树(fdt, flatten device tree)上。NS16550 在创建时会以 PLIC 指针为中断控制器,用来向 CPU 发送外部中断。
+
+与 CLINT 类似,PLIC 等设备在创建之后也都几乎没有用到除计时之外的功能。
+
+## QEMU
+
+### Trap 相关术语及定义
+
+中断统一以 IRQ 作为前缀命名:
+
+```cpp
+// target/riscv/cpu_bits.h: line 609
+/* Interrupt causes */
+#define IRQ_U_SOFT 0
+#define IRQ_S_SOFT 1
+#define IRQ_VS_SOFT 2
+#define IRQ_M_SOFT 3
+#define IRQ_U_TIMER 4
+#define IRQ_S_TIMER 5
+#define IRQ_VS_TIMER 6
+#define IRQ_M_TIMER 7
+#define IRQ_U_EXT 8
+#define IRQ_S_EXT 9
+#define IRQ_VS_EXT 10
+#define IRQ_M_EXT 11
+#define IRQ_S_GEXT 12
+#define IRQ_LOCAL_MAX 16
+#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1)
+```
+
+异常定义为枚举变量:
+
+```cpp
+// target/riscv/cpu_bits.h: line 581
+/* Exception causes */
+typedef enum RISCVException {
+ RISCV_EXCP_NONE = -1, /* sentinel value */
+ RISCV_EXCP_INST_ADDR_MIS = 0x0,
+ RISCV_EXCP_INST_ACCESS_FAULT = 0x1,
+ RISCV_EXCP_ILLEGAL_INST = 0x2,
+ RISCV_EXCP_BREAKPOINT = 0x3,
+ RISCV_EXCP_LOAD_ADDR_MIS = 0x4,
+ RISCV_EXCP_LOAD_ACCESS_FAULT = 0x5,
+ RISCV_EXCP_STORE_AMO_ADDR_MIS = 0x6,
+ RISCV_EXCP_STORE_AMO_ACCESS_FAULT = 0x7,
+ RISCV_EXCP_U_ECALL = 0x8,
+ RISCV_EXCP_S_ECALL = 0x9,
+ RISCV_EXCP_VS_ECALL = 0xa,
+ RISCV_EXCP_M_ECALL = 0xb,
+ RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */
+ RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */
+ RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */
+ RISCV_EXCP_SEMIHOST = 0x10,
+ RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14,
+ RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15,
+ RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16,
+ RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17,
+} RISCVException;
+```
+
+所有的 CSR 操作函数其返回值均为 `RISCVException`,例如:
+
+```cpp
+// target/riscv/csr.c: line 1468
+static RISCVException read_mtvec(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->mtvec;
+ return RISCV_EXCP_NONE;
+}
+// target/riscv/csr.c: line 832
+static const target_ulong vs_delegable_excps = DELEGABLE_EXCPS &
+ ~((1ULL << (RISCV_EXCP_S_ECALL)) |
+ (1ULL << (RISCV_EXCP_VS_ECALL)) |
+ (1ULL << (RISCV_EXCP_M_ECALL)) |
+ (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) |
+ (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) |
+ (1ULL << (RISCV_EXCP_VIRT_INSTRUCTION_FAULT)) |
+ (1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT)));
+```
+
+### Trap 处理
+
+QEMU 内部通过 `DEFINE_TYPES` 注册 RISC-V CPU 的相关信息,其中就包括中断处理相关的操作。内部中断(Software,Timer)通过将中断处理函数 `riscv_cpu_do_interrupt` 注册到 `tcg_ops` 里面来实现,外部中断则通过 CPU 初始化函数中的 GPIO 设备配置来完成。
+
+整体结构如下图所示:
+
+```mermaid
+flowchart TB
+
+subgraph cpu.c
+
+define[DEFINE_TYPES: riscv_cpu_type_infos]-->
+ti[riscv_cpu_type_infos]-->ci[.class_init = riscv_cpu_class_init]-->tcg[riscv_tcg_ops]
+
+ti-->ii[.instance_init = riscv_cpu_init]
+end
+
+subgraph cpu.c
+ii-->igpioi[qdev_init_gpio_in]-->sirq[riscv_cpu_set_irq]
+end
+
+subgraph kvm.c
+ksirq[kvm_riscv_set_irq]
+end
+
+ioctl[kvm_vcpu_ioctl: KVM_INTERRUPT]
+
+sirq-->ksirq-->ioctl
+
+subgraph cpu_helper.c
+eint[riscv_cpu_exec_interrupt]
+dint[riscv_cpu_do_interrupt]
+end
+
+tcg-->eint-->dint
+tcg-->dint
+```
+
+([下载由 Mermaid 生成的 PNG 图片][010])
+
+用于依据 `type_infos` 进行初始化的函数如下:
+
+```cpp
+// include/qom/object.h: line 849
+/**
+ * DEFINE_TYPES:
+ * @type_array: The array containing #TypeInfo structures to register
+ *
+ * @type_array should be static constant that exists for the life time
+ * that the type is registered.
+ */
+#define DEFINE_TYPES(type_array) \
+static void do_qemu_init_ ## type_array(void) \
+{ \
+ type_register_static_array(type_array, ARRAY_SIZE(type_array)); \
+} \
+type_init(do_qemu_init_ ## type_array)
+
+// include/qemu/module.h: line 56
+#define type_init(function) module_init(function, MODULE_INIT_QOM)
+// include/qemu/module.h: line 34
+/* This should not be used directly. Use block_init etc. instead. */
+#define module_init(function, type) \
+static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
+{ \
+ register_module_init(function, type); \
+}
+#endif
+```
+
+```cpp
+// util/module.c: line 69
+void register_module_init(void (*fn)(void), module_init_type type)
+{
+ ModuleEntry *e;
+ ModuleTypeList *l;
+
+ e = g_malloc0(sizeof(*e));
+ e->init = fn;
+ e->type = type;
+
+ l = find_type(type);
+
+ QTAILQ_INSERT_TAIL(l, e, node);
+}
+```
+
+## 总结
+
+本文对两个模拟器中 RISC-V 的 Trap 定义和处理过程进行了较为详细地调研,为实际上手进行软硬件实现提供了一定的参考。
+
+## 参考资料
+
+- [RISC-V CSR 编码生成器][1]
+- [RISC-V 架构 H 扩展中的 Trap 处理][2]
+- [哪吒 D1 开发板 RISC-V CLINT 编程实践][3]
+
+[1]: https://github.com/riscv/riscv-opcodes
+[2]: 20220905-riscv-kvm-virt-trap.md#spike
+[3]: https://cloud.tencent.com/developer/article/1851557
+[4]: https://www.latticesemi.com/zh-CN/Products/DesignSoftwareAndIP/IntellectualProperty/IPCore/DCDCores/D16550
+[5]: https://www.qemu.org/
+[6]: https://github.com/riscv-software-src/riscv-isa-sim
+[007]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png
+[008]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png
+[009]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png
+[010]: images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png
diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..75fb405deb0c175d6923e5fb4339b2a4bf480a54
Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-1.png differ
diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..da018e9babb0340e4bbf1dac896d4e0154e70514
Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-2.png differ
diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png
new file mode 100644
index 0000000000000000000000000000000000000000..28b67d0de8805e581b57d706726c746843b2d031
Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-3.png differ
diff --git a/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..c78785fd12cbdd4dc735c0bb149dba81e6386602
Binary files /dev/null and b/articles/images/riscv-riscv_kvm_int_impl_1/mermaid-riscv-kvm-int-impl-1-4.png differ