1.引言-板卡环境搭建

1.1 qemu 环境搭建

主要参考文章:

  1. 基于qemu-riscv从0开始构建嵌入式linux系统ch2. 添加qemu仿真板——Quard-Star板 — 主页 (quard-star-tutorial.readthedocs.io)
  2. 1.新建quard-star开发板 | TimerのBlog (yanglianoo.github.io)
  3. 【RISC-V】risc-v架构学习笔记(架构初学)_riscv mtvec-CSDN博客
  4. ‘virt’ 通用虚拟平台 (virt) — QEMU 文档

写这个 blog 主要记录一下学习过程,再次感谢!!

硬件架构图:

硬件架构

本机环境

Ubuntu22.04 + qemu8.0.0

qemu 环境配置:

新建 build.sh 文件,用于qemu的编译,

1
2
3
4
5
6
7
cd qemu-8.0.0
if [ ! -d "$SHELL_FOLDER/output/qemu" ]; then
./configure --prefix=$SHELL_FOLDER/output/qemu --target-list=riscv64-softmmu --enable-gtk --enable-virtfs --disable-gio
fi
make -j16
make install
cd ..
  • 注意:这种的环境变量并没有添加到终端中,终端并不能直接执行shell命令,需要去到qemu/bin下面才能执行,所以需要将路径添加到zshrc
1
export PATH=$PATH:/home/liangzhou/桌面/riscv code/output/qemu/bin

image-20240326112910836

  • 然后在终端输入:qemu-system-riscv64 --version

1.2 新建板卡

需要增加两个文件以及并修改四个文件

  1. /qemu8.0.0/hw/riscv 下 增加quard_star.c
  2. /qemu8.0.0/include/hw/riscv 下 增加quard_star.h
  3. 修改 qemu-8.0.0/configs/devices/riscv64-softmmu/default.mak CONFIG QUARD STAR=y 启动板卡配置
  4. 修改 qemu-8.0.0/configs/devices/riscv32-softmmu/default.mak 同上
  5. 修改 qemu-8.0.0/hw/riscv/meson.build — 作用:添加 源文件以及依赖
    1. image-20240326115307621
  6. 修改 qemu-8.0.0/hw/riscv/Kconfig — 作用 用于 QEMU 中 RISC-V 硬件模拟的配置
    1. image-20240326115434463

1.2.1 quard_star.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef HW_RISCV_QUARD_STAR__H   // 预处理指令放置头文件被重复包含
#define HW_RISCV_QUARD_STAR__H

#include "hw/riscv/riscv_hart.h"
#include "hw/sysbus.h"
#include "qom/object.h"
#include "hw/block/flash.h"

#define QUARD_STAR_CPUS_MAX 8 // 定义quard_star 最大CPU数 = 8 --- 上面硬件架构图
#define QUARD_STAR_SOCKETS_MAX 8 // 定义最大插槽数 也为 8

#define TYPE_RISCV_QUARD_STAR_MACHINE MACHINE_TYPE_NAME("quard-star") // 定义了机器名称
typedef struct QuardStarState QuardStarState; // 结构体前置声明 用于解决两个结构体互相依赖的问题

DECLARE_INSTANCE_CHECKER(QuardStarState, RISCV_VIRT_MACHINE,
TYPE_RISCV_QUARD_STAR_MACHINE) // 实例化一个检查函数

struct QuardStarState {
/*< private >*/
MachineState parent;

/*< public >*/
RISCVHartArrayState soc[QUARD_STAR_SOCKETS_MAX];
};
// 枚举定义 定义了 资源 MROM等
enum {
QUARD_STAR_MROM,
QUARD_STAR_SRAM,
QUARD_STAR_UART0,
QUARD_STAR_DRAM,
};

enum {
QUARD_STAR_UART0_IRQ = 10, //定义了串口中断号为10
};
#endif

1.2.2 quard_star.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/error-report.h"
#include "qemu/guest-random.h"
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/sysbus.h"
#include "hw/qdev-properties.h"
#include "hw/char/serial.h"
#include "target/riscv/cpu.h"

#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/quard_star.h"
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/riscv_aplic.h"

#include "chardev/char.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/tpm.h"

// 结构体数组中添加硬件地址 和映射的长度地址 前一个是基址 后一个是长度
static const MemMapEntry quard_star_memmap[] = {
[QUARD_STAR_MROM] = { 0x0, 0x8000 },
[QUARD_STAR_SRAM] = { 0x8000, 0x8000 },
[QUARD_STAR_UART0] = { 0x10000000, 0x100 },
[QUARD_STAR_DRAM] = { 0x80000000, 0x80 },
};
/* 创建CPU */
static void quard_star_cpu_create(MachineState *machine)
{
int i, base_hartid, hart_count;
char *soc_name;
QuardStarState *s = RISCV_VIRT_MACHINE(machine);

if (QUARD_STAR_SOCKETS_MAX < riscv_socket_count(machine)) {
error_report("number of sockets/nodes should be less than %d",
QUARD_STAR_SOCKETS_MAX);
exit(1);
}

for (i = 0; i < riscv_socket_count(machine); i++) {
if (!riscv_socket_check_hartids(machine, i)) {
error_report("discontinuous hartids in socket%d", i);
exit(1);
}

base_hartid = riscv_socket_first_hartid(machine, i);
if (base_hartid < 0) {
error_report("can't find hartid base for socket%d", i);
exit(1);
}

hart_count = riscv_socket_hart_count(machine, i);
if (hart_count < 0) {
error_report("can't find hart count for socket%d", i);
exit(1);
}

soc_name = g_strdup_printf("soc%d", i);
object_initialize_child(OBJECT(machine), soc_name, &s->soc[i],
TYPE_RISCV_HART_ARRAY);
g_free(soc_name);
object_property_set_str(OBJECT(&s->soc[i]), "cpu-type",
machine->cpu_type, &error_abort);
object_property_set_int(OBJECT(&s->soc[i]), "hartid-base",
base_hartid, &error_abort);
object_property_set_int(OBJECT(&s->soc[i]), "num-harts",
hart_count, &error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
}
}

/* 创建内存 */
static void quard_star_memory_create(MachineState *machine)
{
QuardStarState *s = RISCV_VIRT_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
//分配三片存储空间 dram sram mrom
MemoryRegion *dram_mem = g_new(MemoryRegion, 1); //DRAM
MemoryRegion *sram_mem = g_new(MemoryRegion, 1); //SRAM
MemoryRegion *mask_rom = g_new(MemoryRegion, 1); //MROM


memory_region_init_ram(dram_mem, NULL, "riscv_quard_star_board.dram",
quard_star_memmap[QUARD_STAR_DRAM].size, &error_fatal);
memory_region_add_subregion(system_memory,
quard_star_memmap[QUARD_STAR_DRAM].base, dram_mem);

memory_region_init_ram(sram_mem, NULL, "riscv_quard_star_board.sram",
quard_star_memmap[QUARD_STAR_SRAM].size, &error_fatal);
memory_region_add_subregion(system_memory,
quard_star_memmap[QUARD_STAR_SRAM].base, sram_mem);

memory_region_init_rom(mask_rom, NULL, "riscv_quard_star_board.mrom",
quard_star_memmap[QUARD_STAR_MROM].size, &error_fatal);
memory_region_add_subregion(system_memory,
quard_star_memmap[QUARD_STAR_MROM].base, mask_rom);

riscv_setup_rom_reset_vec(machine, &s->soc[0],
quard_star_memmap[QUARD_STAR_MROM].base,
quard_star_memmap[QUARD_STAR_MROM].base,
quard_star_memmap[QUARD_STAR_MROM].size,
0x0, 0x0);
}

/* quard-star 初始化各种硬件 资源 cpu 内存等 后面会扩展 can flash i2c等等 */
static void quard_star_mach ine_init(MachineState *machine)
{
// 创建CPU
quard_star_cpu_create(machine);
// 创建主存
quard_star_memory_create(machine);
}

static void quard_star_machine_instance_init(Object *obj)
{

}

/* 创建machine */
static void quard_star_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);

mc->desc = "RISC-V Quard Star board";
mc->init = quard_star_machine_init; // 初始化板卡资源 以及 最大支持的smp核心数
mc->max_cpus = QUARD_STAR_CPUS_MAX;
mc->default_cpu_type = TYPE_RISCV_CPU_BASE;
mc->pci_allow_0_address = true;
mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids;
mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props;
mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id;
mc->numa_mem_supported = true;
}
/* 注册 quard-star 定义了 */
static const TypeInfo quard_star_machine_typeinfo = {
.name = MACHINE_TYPE_NAME("quard-star"),
.parent = TYPE_MACHINE,
.class_init = quard_star_machine_class_init,
.instance_init = quard_star_machine_instance_init,
.instance_size = sizeof(QuardStarState),
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
},
};

static void quard_star_machine_init_register_types(void)
{
type_register_static(&quard_star_machine_typeinfo);
}
type_init(quard_star_machine_init_register_types)
// 类的注册

注意: 格式是 基址, 长度

MenMapEntry 结构体定义如下:

1
2
3
4
typedef struct MemMapEntry {
hwaddr base; //基址
hwaddr size; //长度
} MemMapEntry;
1
2
3
4
5
6
static const MemMapEntry quard_star_memmap[] = {
[QUARD_STAR_MROM] = { 0x0, 0x8000 },
[QUARD_STAR_SRAM] = { 0x8000, 0x8000 },
[QUARD_STAR_UART0] = { 0x10000000, 0x100 },
[QUARD_STAR_DRAM] = { 0x80000000, 0x80 },
};
  1. MRON(maskrom) 用于cpu启动时固定执行其内部的代码。
  2. SRAM 为早期启动代码时数据存放的空间。

riscv_setup_rom_reset_vec 在boot.c中 函数的各个参数的定义如下:

1
2
3
4
5
void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts,
hwaddr start_addr,
hwaddr rom_base,
hwaddr rom_size,
uint64_t kernel_entry, uint64_t fdt_load_addr)
1
2
3
4
5
riscv_setup_rom_reset_vec(machine, &s->soc[0], 
quard_star_memmap[QUARD_STAR_MROM].base,
quard_star_memmap[QUARD_STAR_MROM].base,
quard_star_memmap[QUARD_STAR_MROM].size,
0x0, 0x0);
  1. 将cpu hart id 值加载到 a0寄存器
  2. 设备树文件基地址加载到a1寄存器 然后跳转到下级运行代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 创建machine */
static void quard_star_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);

mc->desc = "RISC-V Quard Star board";
mc->init = quard_star_machine_init; // 初始化板卡资源 以及 最大支持的smp核心数
mc->max_cpus = QUARD_STAR_CPUS_MAX;
mc->default_cpu_type = TYPE_RISCV_CPU_BASE;
mc->pci_allow_0_address = true;
mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids;
mc->cpu_index_to_instance_props = riscv_numa_cpu_index_to_props;
mc->get_default_cpu_node_id = riscv_numa_get_default_cpu_node_id;
mc->numa_mem_supported = true;
}
  1. 初始化machine,将machine中的各个字段进行更新

1.2.3 文件执行

run.sh文件

1
2
3
4
5
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
$SHELL_FOLDER/output/qemu/bin/qemu-system-riscv64 \
-M quard-star \
-m 1G \
-smp 8 \
  1. $SHELL_FOLDER/output/qemu/bin/qemu-system-riscv64: 使用完整路径指定qemu-system-riscv64的位置,这是QEMU中用于启动RISC-V 64位架构虚拟机的程序。路径依赖于第一行中设置的SHELL_FOLDER变量
  2. -M quard-star: 指定虚拟机使用的机器类型为“quard-star”,即我们之前定制的RISC-V机器模型。
  3. -m 1G: 为虚拟机分配1GB的内存。
  4. -smp 8: 配置虚拟机使用的处理器核心数为8,即虚拟机将模拟一个8核心的处理器。

在qemu中输入 info qtree

image-20240602213630396