当前成果:
1git clone -b nuclei_gd32vf103 https://gitee.com/gevico/qemu.git
下面记录关键 patch 解决的问题:
[patch] 修复 dts 编译警告问题
1commit c48d9d247e6bf30153b8e5e91dab4f63ac847a80
2Author: zevorn <zevorn@yeah.net>
3Date: Tue Jul 23 00:38:22 2024 +0800
4
5 pc-bios: Fix the compilation warning when dtb was generated
6
7 It is mainly related to the memory and opb nodes in bamboo.dts.
这个 patch 主要解决了编译过程中遇到的 dts 编译警告,后续主线应该会修复该问题。
[patch] 添加 nuclei n205 核心
1commit 8b0a11489cb4c7d796e545fb4bb59a0ada6ed09d
2Author: zevorn <zevorn@yeah.net>
3Date: Mon Jul 22 22:57:21 2024 +0800
4
5 target/riscv: Add nuclei_n205 core for RISCV architecture
初始化 N205 核心的时候,用的 DEFINE_VENDOR_CPU(),可扩展厂商自定义指令集。
1diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
2index 3670cfe6d9..7351cb4b0b 100644
3--- a/target/riscv/cpu-qom.h
4+++ b/target/riscv/cpu-qom.h
5@@ -50,6 +50,7 @@
6 #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906")
7 #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1")
8 #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host")
9+#define TYPE_RISCV_CPU_NUCLEI_N205 RISCV_CPU_TYPE_NAME("nuclei-n205")
[patch] 添加 gd32vf103 Machine
1commit aa5224ecb03957825982f52579dae0dc02a93c98
2Author: zevorn <zevorn@yeah.net>
3Date: Mon Jul 22 22:58:01 2024 +0800
4
5 hw/riscv: Add gd32vf103 mcu for RISCV architecture
没有实现具体外设,只增加了一些 mr 的初始化:
1static void nuclei_board_init(MachineState *machine)
2+{
3+ const struct MemmapEntry *memmap = gd32vf103_memmap;
4+ NucleiGDState *s = g_new0(NucleiGDState, 1);
5+ MemoryRegion *system_memory = get_system_memory();
6+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
7+ s->soc.sram = *main_mem;
8+ int i;
9+
10+ /* TODO: Add qtest support */
11+ /* Initialize SOC */
12+ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_NUCLEI_GD32VF103_SOC);
13+ qdev_realize(DEVICE(&s->soc), NULL, &error_abort);
14+
15+ memory_region_init_ram(&s->soc.main_flash, NULL, "riscv.nuclei.main_flash",
16+ memmap[GD32VF103_MAINFLASH].size, &error_fatal);
17+ memory_region_add_subregion(system_memory,
18+ memmap[GD32VF103_MAINFLASH].base, &s->soc.main_flash);
19+
20+ memory_region_init_ram(&s->soc.boot_loader, NULL, "riscv.nuclei.boot_loader",
21+ memmap[GD32VF103_BL].size, &error_fatal);
22+ memory_region_add_subregion(system_memory,
23+ memmap[GD32VF103_BL].base, &s->soc.boot_loader);
24+
25+ memory_region_init_ram(&s->soc.ob, NULL, "riscv.nuclei.ob",
26+ memmap[GD32VF103_OB].size, &error_fatal);
27+ memory_region_add_subregion(system_memory,
28+ memmap[GD32VF103_OB].base, &s->soc.ob);
29+
30+ memory_region_init_ram(&s->soc.sram, NULL, "riscv.nuclei.sram",
31+ memmap[GD32VF103_SRAM].size, &error_fatal);
32+ memory_region_add_subregion(system_memory,
33+ memmap[GD32VF103_SRAM].base, &s->soc.sram);
34+
35+ /* reset vector */
36+ uint32_t reset_vec[8] = {
37+ 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
38+ 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
39+ 0xf1402573, /* csrr a0, mhartid */
40+#if defined(TARGET_RISCV32)
41+ 0x0182a283, /* lw t0, 24(t0) */
42+#elif defined(TARGET_RISCV64)
43+ 0x0182b283, /* ld t0, 24(t0) */
44+#endif
45+ 0x00028067, /* jr t0 */
46+ 0x00000000,
47+ memmap[GD32VF103_MAINFLASH].base, /* start: .dword */
48+ 0x00000000,
49+ /* dtb: */
50+ };
51+
52+ /* copy in the reset vector in little_endian byte order */
53+ for (i = 0; i < sizeof(reset_vec) >> 2; i++)
54+ {
55+ reset_vec[i] = cpu_to_le32(reset_vec[i]);
56+ }
57+ rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
58+ memmap[GD32VF103_MFOL].base + 0x1000, &address_space_memory);
59+
60+ /* boot rom */
61+ if (machine->kernel_filename)
62+ {
63+ riscv_load_kernel(machine, &s->soc.cpus,
64+ memmap[GD32VF103_MAINFLASH].base,
65+ false, NULL);
66+ }
67+}
[patch] 增加 eclic 外设,完善中断控制流程
1commit 72a1f9a351d5b3adf2dd8dbcbb1505e9f3824ec6
2Author: zevorn <zevorn@yeah.net>
3Date: Thu Jul 25 00:05:12 2024 +0800
4
5 hw/riscv: Add the intc device to gdv32vf103 cpu
6
7commit ac3c55d0e19cb03bb31bd8f580ec24f18eb18590
8Author: zevorn <zevorn@yeah.net>
9Date: Thu Jul 25 00:05:02 2024 +0800
10
11 hw/intc: Add the gdv32vf103_eclic device
这部分还没有仔细研究,直接移植的 PLCT 原本的代码,但是做了 patch 整理,代码更加清晰。
[patch] 增加 nuclei-n205 CSR 扩充的寄存器的支持
1commit 7df1ce25dd9ea24fe1c4e9a4bc0ed41d933495e2
2Author: zevorn <zevorn@yeah.net>
3Date: Sun Jul 28 17:04:20 2024 +0800
4
5 hw/riscv: Add the rcu device to gdv32vf103 cpu
6 target/riscv: Add CSR register support for nuclei N205
这个 patch 有几个关键修改。
需要将 n205 的指令集版本设置为 latest,不然不支持 CSR_MCOUNTINHIBIT 寄存器,或者运行时有警告,另外需要将 cpu->cfg.ext_zifencei 和 cpu->cfg.ext_zicsr 设置为 true,不然有些 CSR 寄存器也不支持,会导致 startup 阶段访问某些 CSR 寄存器时意外陷入异常:
1diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
2index 326576fe20..1d9d36ddb2 100644
3--- a/target/riscv/cpu.c
4+++ b/target/riscv/cpu.c
5@@ -684,13 +684,15 @@ static void rv32imacu_nuclei_cpu_init(Object *obj)
6 #endif
7
8 riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVC | RVU);
9- env->priv_ver = PRIV_VERSION_1_10_0;
10+ env->priv_ver = PRIV_VERSION_LATEST; // support CSR_MCOUNTINHIBIT
11
12 /* inherited from parent obj via riscv_cpu_init() */
13 qdev_prop_set_bit(DEVICE(obj), "mmu", false);
14 #ifndef CONFIG_USER_ONLY
15 env->resetvec = DEFAULT_RSTVEC;
16 #endif
17+ cpu->cfg.ext_zifencei = true;
18+ cpu->cfg.ext_zicsr = true;
19 cpu->cfg.pmp = true;
20 }
[patch] 增加 gd32vf103 startup 阶段需要的几个外设
1commit b37aac1ef3641e4aff84c9d95f48ff1cba8b2b74 (HEAD -> nuclei_gd32vf103)
2Author: zevorn <zevorn@yeah.net>
3Date: Sun Jul 28 17:16:46 2024 +0800
4
5 hw/riscv: Add the gpio device to gdv32vf103 cpu
6
7commit 5969602dcb461db5cba548ba83c29069be097dd8
8Author: zevorn <zevorn@yeah.net>
9Date: Sun Jul 28 17:16:39 2024 +0800
10
11 hw/gpio: Add the nuclei_gpio device
12
13commit 60d9091f591599635af9ef49625962c25e50c2dc
14Author: zevorn <zevorn@yeah.net>
15Date: Sun Jul 28 17:04:20 2024 +0800
16
17 hw/riscv: Add the rcu device to gdv32vf103 cpu
18
19commit e0af6cbaf0307ebd727ae5bdf471920733557f08
20Author: zevorn <zevorn@yeah.net>
21Date: Sun Jul 28 17:04:07 2024 +0800
22
23 hw/timer: Add the nuclei_rcu device
这里使用 riscv-gdb 远程 remote 到 qemu 以后,观察异常点的函数调用栈,可以追溯是哪个外设没实现,导致的访问寄存器时触发了异常,然后将对应的外设移植过来即可。
比较简单,这里不赘述移植过程,只说一下调试的流程,命令如下:
open 一个终端,启动 qemu :
1cd qemu
2qemu-system-riscv32 -M gd32vf103v_rvstar -cpu nuclei-n205 -icount shift=0 -nodefaults -nographic -serial stdio -kernel nuclei-sdk/application/baremetal/helloworld/helloworld.elf -gdb tcp::1234 -S
open 一个新的终端,启动 gdb 来调试客户程序:
1riscv-nuclei-linux-gnu-gdb ../nuclei-sdk/application/baremetal/helloworld/helloworld.elf
2(gdb) target remote localhost:1234
3(gdb) run
4(gdb) ctrl + c
5(gdb) bt
6...
这里可以根据函数调用栈,来定位具体异常对应的外设,或者其他指令。
Ps:对于来自客户程序的异常行为,即使是 qemu 这边没有实现对应的指令或者外设,亦或者没有初始化一些内存区域,也是按照 Guest Machine 的异常来处理,这样非常方便通过客户程序的指令流或者函数调用流,来定位出错的地方。