在WIN从零开始在QMUE上添加一块自己的开发板(二)

文章目录

  • 一、前言
    • 往期回顾
  • 二、CPU虚拟化
    • (一)相关源码
    • (二)举个例子
    • (三)测试
  • 三、内存虚拟化
    • (一)相关源码
    • (二)举个例子
    • 测试
  • 参考资料

一、前言

笔者这篇博客作为平时学习时的笔记记录,如有不对还望指正,本博客大量借鉴资料,笔者只是拾人牙慧的小屁孩。
QEMU是一种通用的开源计算机仿真器和虚拟器。而QUME内置支持了一些开发板,我们可以基于这些内置的板子来做操作系统等软件的配置,但是实际市面上很多板子QUME中是没有提供支持的,这需要我们根据QUME的源码自定义一些开发板,然后再重新编译。

往期回顾

在WIN从零开始在QMUE上添加一块自己的开发板(一)

二、CPU虚拟化

(一)相关源码

QEMU中RISC-V CPU的支持
QOM的TYPE定义
target/riscv/cpu.h:

#define TYPE_RISCV_CPU "riscv-cpu"#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64")
#define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex")
#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31")
#define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34")
#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51")
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")

TYPE用于hash表的key(GHashTable.key),而hash表的值也就是ObjectClassObject

Class
RISCVCPUClass继承于CPUClass,然后继承于DeviceClass
CPUClass中有很多接口:

struct CPUClass {DeviceClass parent_class;ObjectClass *(*class_by_name)();void (*parse_features)();int reset_dump_flags;bool (*has_work)();bool (*virtio_is_big_endian)();int (*memory_rw_debug)();void (*dump_state)();GuestPanicInformation* (*get_crash_info)();void (*dump_statistics)();int64_t (*get_arch_id)();bool (*get_paging_enabled)();void (*get_memory_mapping)();void (*set_pc)();hwaddr (*get_phys_page_debug)();hwaddr (*get_phys_page_attrs_debug)();int (*asidx_from_attrs)();int (*gdb_read_register)();int (*gdb_write_register)();int (*write_elf64_note)();int (*write_elf64_qemunote)();int (*write_elf32_note)();int (*write_elf32_qemunote)();const VMStateDescription *vmsd;const char *gdb_core_xml_file;gchar * (*gdb_arch_name)();const char * (*gdb_get_dynamic_xml)();void (*disas_set_info)();const char *deprecation_note;int gdb_num_core_regs;bool gdb_stop_before_watchpoint;struct AccelCPUClass *accel_cpu;struct TCGCPUOps *tcg_ops;
};

我们着重看一下struct TCGCPUOps

struct TCGCPUOps {void (*initialize)(void);void (*synchronize_from_tb)(CPUState *cpu, const TranslationBlock *tb);void (*cpu_exec_enter)(CPUState *cpu);void (*cpu_exec_exit)(CPUState *cpu);bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);void (*do_interrupt)(CPUState *cpu);bool (*tlb_fill)(CPUState *cpu, vaddr address, int size,MMUAccessType access_type, int mmu_idx,bool probe, uintptr_t retaddr);void (*debug_excp_handler)(CPUState *cpu);
#ifdef NEED_CPU_H
#ifdef CONFIG_SOFTMMUvoid (*do_transaction_failed)(CPUState *cpu, hwaddr physaddr, vaddr addr,unsigned size, MMUAccessType access_type,int mmu_idx, MemTxAttrs attrs,MemTxResult response, uintptr_t retaddr);void (*do_unaligned_access)(CPUState *cpu, vaddr addr,MMUAccessType access_type,int mmu_idx, uintptr_t retaddr);vaddr (*adjust_watchpoint_address)(CPUState *cpu, vaddr addr, int len);bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp);bool (*io_recompile_replay_branch)(CPUState *cpu,const TranslationBlock *tb);
#endif /* CONFIG_SOFTMMU */
#endif /* NEED_CPU_H */
};

可以看见里面有很多CPU运行时的接口。
在不同架构中,我们进行相应的实现,对于RISCVCPU,已经有相应的实现了:

static struct TCGCPUOps riscv_tcg_ops = {.initialize = riscv_translate_init,.synchronize_from_tb = riscv_cpu_synchronize_from_tb,.cpu_exec_interrupt = riscv_cpu_exec_interrupt,.tlb_fill = riscv_cpu_tlb_fill,
#ifndef CONFIG_USER_ONLY.do_interrupt = riscv_cpu_do_interrupt,.do_transaction_failed = riscv_cpu_do_transaction_failed,.do_unaligned_access = riscv_cpu_do_unaligned_access,
#endif /* !CONFIG_USER_ONLY */
};

Object
对于CPU中寄存器的定义都在Object中:

struct CPURISCVState {target_ulong gpr[32];uint64_t fpr[32]; /* assume both F and D extensions *//* vector coprocessor state. */uint64_t vreg[32 * RV_VLEN_MAX / 64] QEMU_ALIGNED(16);//vector regtarget_ulong pc;target_ulong misa;uint32_t features;/* Hypervisor CSRs *//* Virtual CSRs *//* HS Backup CSRs *//* temporary htif regs *//* physical memory protection *//* machine specific rdtime callback *//* True if in debugger mode. */bool debugger;float_status fp_status;/* Fields from here on are preserved across CPU reset. */QEMUTimer *timer; /* Internal timer */
};

实例化

	struct RISCVCPU {/*< private >*/CPUState parent_obj;/*< public >*/CPUNegativeOffsetState neg;CPURISCVState env;char *dyn_csr_xml;/* Configuration Settings */struct {……} cfg;
};

RISCV CPU TypeInfo注册:

.instance_init = riscv_cpu_init,
.class_init = riscv_cpu_class_init,

特殊的CPU使用特殊的函数进行实例

.instance_init = rvxx_sifive_e_cpu_init,
.class_init = riscv_cpu_class_init,

在特殊的函数中,将会针对不同CPU的特性进行个性化实例:

static void rvxx_sifive_e_cpu_init(Object *obj)
{CPURISCVState *env = &RISCV_CPU(obj)->env;set_misa(env, RVXLEN | RVI | RVM | RVA | RVC | RVU);set_priv_version(env, PRIV_VERSION_1_10_0);set_resetvec(env, 0x1004);qdev_prop_set_bit(DEVICE(obj), "mmu", false);
}

(二)举个例子

我们为之前创建的开发板增加CPU。
我们去target\riscv\cpu-qom.h,添加一个我们自己的CPU:

...
#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)#define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
#define TYPE_RISCV_CPU_MAX              RISCV_CPU_TYPE_NAME("max")
#define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
#define TYPE_RISCV_CPU_BASE64           RISCV_CPU_TYPE_NAME("rv64")
#define TYPE_RISCV_CPU_BASE128          RISCV_CPU_TYPE_NAME("x-rv128")
#define TYPE_RISCV_CPU_IBEX             RISCV_CPU_TYPE_NAME("lowrisc-ibex")
#define TYPE_RISCV_CPU_SHAKTI_C         RISCV_CPU_TYPE_NAME("shakti-c")
#define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e31")
#define TYPE_RISCV_CPU_SIFIVE_E34       RISCV_CPU_TYPE_NAME("sifive-e34")
#define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e51")
#define TYPE_RISCV_CPU_SIFIVE_U34       RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54       RISCV_CPU_TYPE_NAME("sifive-u54")
#define TYPE_RISCV_CPU_THEAD_C906       RISCV_CPU_TYPE_NAME("thead-c906")
#define TYPE_RISCV_CPU_VEYRON_V1        RISCV_CPU_TYPE_NAME("veyron-v1")
#define TYPE_RISCV_CPU_HOST             RISCV_CPU_TYPE_NAME("host")
/* 添加自己的CPU */
#define TYPE_RISCV_CPU_NUCLEI_N600       RISCV_CPU_TYPE_NAME("nuclei-n600")

并在./target/riscv/cpu.c中,添加其初始化函数:

#if defined(TARGET_RISCV32)
/* 自己的CPU */
static void rv32_nuclei_n_cpu_init(Object *obj)
{CPURISCVState *env = &RISCV_CPU(obj)->env;RISCVCPU *cpu = RISCV_CPU(obj);riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVF | RVD | RVU);env->priv_ver=  PRIV_VERSION_1_10_0;#ifndef CONFIG_USER_ONLYset_satp_mode_max_supported(cpu, VM_1_10_MBARE);
#endif/* inherited from parent obj via riscv_cpu_init() */cpu->cfg.ext_zifencei = true;cpu->cfg.ext_zicsr = true;cpu->cfg.pmp = true;
}
#endif

并在riscv_cpu_type_infos中添加DEFINE

static const TypeInfo riscv_cpu_type_infos[] = {{.name = TYPE_RISCV_CPU,.parent = TYPE_CPU,.instance_size = sizeof(RISCVCPU),.instance_align = __alignof(RISCVCPU),.instance_init = riscv_cpu_init,.instance_post_init = riscv_cpu_post_init,.abstract = true,.class_size = sizeof(RISCVCPUClass),.class_init = riscv_cpu_class_init,},{.name = TYPE_RISCV_DYNAMIC_CPU,.parent = TYPE_RISCV_CPU,.abstract = true,},DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_ANY,      riscv_any_cpu_init),DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX,      riscv_max_cpu_init),
#if defined(TARGET_RISCV32)DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE32,   rv32_base_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_IBEX,             rv32_ibex_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31,       rv32_sifive_e_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34,       rv32_imafcu_nommu_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34,       rv32_sifive_u_cpu_init),/* 自己的CPU */DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N600,      rv32_nuclei_n_cpu_init),#elif defined(TARGET_RISCV64)DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE64,   rv64_base_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51,       rv64_sifive_e_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54,       rv64_sifive_u_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_SHAKTI_C,         rv64_sifive_u_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_THEAD_C906,       rv64_thead_c906_cpu_init),DEFINE_CPU(TYPE_RISCV_CPU_VEYRON_V1,        rv64_veyron_v1_cpu_init),DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128,  rv128_base_cpu_init),
#endif
};

接着我们回到我们的开发板文件,
这里讲个小技巧,也就是从include\hw\riscv\virt.h(官方虚拟开发板的例程)看相关的引用,从而确定相关头文件的所在位置和相关代码的实现。
在我们的nuclei_n.h中(请注意,为了方便理解,这里对往期变量及函数的名字进行了更改):
引入头文件

#include "hw/riscv/riscv_hart.h"

并在SOC中添加CPU

/* CPU 定义 */
#define NUCLEI_N_CPU TYPE_RISCV_CPU_NUCLEI_N600typedef struct NucLeiNSoCState
{/*< private >*/SysBusDevice parent_obj;/*< public >*/RISCVHartArrayState cpus;
} NucLeiNSoCState;

以及一些TYPE

#define NUCLEI_N_CPU 						TYPE_RISCV_CPU_NUCLEI_N600

之后我们在Machine的Class里面增加最小CPU个数和默认CPU的TYPE

static void nuclei_machine_class_init(ObjectClass *oc, void *data)
{qemu_log(">>nuclei_machine_class_init \n");MachineClass *mc = MACHINE_CLASS(oc);mc->desc = "Nuclei MCU 200T FPGA Evaluation Kit";mc->init = nuclei_mcu_machine_init;mc->max_cpus = 1;mc->default_cpu_type = NUCLEI_N_CPU;
}

我们在SOC实例初始化函数中,对CPU初始化:

static void nuclei_n_soc_instance_init(Object *obj)
{qemu_log(">>nuclei_n_soc_instance_init \n");NucLeiNSoCState *s = NUCLEI_N_SOC(obj);object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);  //初始化CPU
}

并在SOC实现中(nuclei_n_soc_realize)进行CPU的实现:

static void nuclei_n_soc_realize(DeviceState *dev, Error **errp)
{qemu_log(">>nuclei_n_soc_realize \n");MachineState *ms = MACHINE(qdev_get_machine());NucLeiNSoCState *s = NUCLEI_N_SOC(dev);object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, &error_abort);      object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort);sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);  //CPU实例化
}

附上完整:

nuclei_n.h:

#include "hw/sysbus.h"
#include "hw/riscv/riscv_hart.h"#define TYPE_NUCLEI_N_SOC "riscv.nuclei.n.soc"
#define NUCLEI_N_SOC(obj) \OBJECT_CHECK(NucLeiNSoCState, (obj), TYPE_NUCLEI_N_SOC)/* CPU 定义 */
#define NUCLEI_N_CPU TYPE_RISCV_CPU_NUCLEI_N600typedef struct NucLeiNSoCState
{/*< private >*/SysBusDevice parent_obj;/*< public >*/RISCVHartArrayState cpus;
} NucLeiNSoCState;/* Machine state定义 */
#define TYPE_NUCLEI_MCU_FPGA_MACHINE MACHINE_TYPE_NAME("mcu_200t")
#define MCU_FPGA_MACHINE(obj) \OBJECT_CHECK(NucLeiNState, (obj), TYPE_NUCLEI_MCU_FPGA_MACHINE)typedef struct NucLeiNState
{/*< private >*/SysBusDevice parent_obj;/*< public >*/NucLeiNSoCState soc;
} NucLeiNState;

nuclei_n.c:

#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/riscv/nuclei_n.h"
#include "hw/boards.h"static void nuclei_n_soc_instance_init(Object *obj)
{qemu_log(">>nuclei_n_soc_instance_init \n");NucLeiNSoCState *s = NUCLEI_N_SOC(obj);object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);  //初始化CPU
}static void nuclei_n_soc_realize(DeviceState *dev, Error **errp)
{qemu_log(">>nuclei_n_soc_realize \n");MachineState *ms = MACHINE(qdev_get_machine());NucLeiNSoCState *s = NUCLEI_N_SOC(dev);object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, &error_abort);      object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort);sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);            //CPU实例化
}
static void nuclei_n_soc_class_init(ObjectClass *oc, void *data)
{qemu_log(">>nuclei_n_soc_class_init \n");DeviceClass *dc = DEVICE_CLASS(oc);dc->realize = nuclei_n_soc_realize;dc->user_creatable = false;
}static const TypeInfo nuclei_n_soc_type_info = {.name = TYPE_NUCLEI_N_SOC,.parent = TYPE_DEVICE,.instance_size = sizeof(NucLeiNSoCState),.instance_init = nuclei_n_soc_instance_init,.class_init = nuclei_n_soc_class_init,
};
static void nuclei_n_soc_register_types(void)
{type_register_static(&nuclei_n_soc_type_info);
}
type_init(nuclei_n_soc_register_types)static void nuclei_mcu_machine_init(MachineState *machine)
{NucLeiNState *s = MCU_FPGA_MACHINE(machine);qemu_log(">>nuclei_mcu_machine_init \n");/* Initialize SOC */object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_NUCLEI_N_SOC);qdev_realize(DEVICE(&s->soc), NULL, &error_abort);
}
static void nuclei_machine_instance_init(Object *obj)
{qemu_log(">>nuclei_machine_instance_init \n");
}
static void nuclei_machine_class_init(ObjectClass *oc, void *data)
{qemu_log(">>nuclei_machine_class_init \n");MachineClass *mc = MACHINE_CLASS(oc);mc->desc = "Nuclei MCU 200T FPGA Evaluation Kit";mc->init = nuclei_mcu_machine_init;mc->max_cpus = 1;mc->default_cpu_type = NUCLEI_N_CPU;
}static const TypeInfo nuclei_machine_typeinfo = {.name = TYPE_NUCLEI_MCU_FPGA_MACHINE,.parent = TYPE_MACHINE,.class_init = nuclei_machine_class_init,.instance_init = nuclei_machine_instance_init,.instance_size = sizeof(NucLeiNState),
};
static void nuclei_machine_init_register_types(void)
{type_register_static(&nuclei_machine_typeinfo);
}
type_init(nuclei_machine_init_register_types)

不用忘记在./target/riscv/cpu.c去定义CPU哦。

(三)测试

执行run.sh:

SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
$SHELL_FOLDER/output/qemu/qemu-system-riscv32.exe \
-M mcu_200t

得到以下效果:
CPU虚拟化

三、内存虚拟化

(一)相关源码

有了之前的开发经验,我们这次直接看相关API:

Types of regionsinitialize
RAMmemory_region_init_ram()
MMIOmemory_region_init_io()
ROMmemory_region_init_rom()
ROM_evicememory_region_init_rom_device()
IOMMU regionmemory_region_init_iommu()
containermemory_region_init()
aliasmemory_region_init_alias()
reservation regionmemory_region_init_io()

其次是添加硬件的地址和映射的地址长度的结构体:
(注意这里的长度不能为0,不然会报错)

typedef struct MemMapEntry {hwaddr base; //基址hwaddr size; //长度
} MemMapEntry;

然后是关于ROM的指令初始化:

/* reset vector */
uint32_t reset_vec[8] = {0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */0x02028593, /* addi a1, t0, %pcrel_lo(1b) */0xf1402573, /* csrr a0, mhartid */#if defined(TARGET_RISCV32)0x0182a283, /* lw t0, 24(t0) */#elif defined(TARGET_RISCV64)0x0182b283, /* ld t0, 24(t0) */#endif0x00028067, /* jr t0 */0x00000000,start_addr, /* start: .dword DRAM_BASE */0x00000000,
};
/* copy in the reset vector in little_endian byte order */
for (i = 0; i < sizeof(reset_vec) >> 2; i++)
{reset_vec[i] = cpu_to_le32(reset_vec[i]);
}
rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),memmap[NUCLEI_N_ROM].base, &address_space_memory);
/* boot rom */
if (machine->kernel_filename)
{riscv_load_kernel(machine>kernel_filename, start_addr, NULL);
}

这种是手动执行指令初始化,当然,因为我们使用的RISCV架构,直接使用riscv_setup_rom_reset_vec进行指令初始化也是可以的。

这里我们再讲一下内存模拟的一个步骤:

  1. 执行初始化函数,例如ROM的就是memory_region_init_rom
  2. 分配/挂载,memory_region_add_subregion,当然还会用上系统根节点获取get_system_memory
  3. ROM设置(指令初始化)或者加载kernel

(二)举个例子

根据SOC指定地址编写MemMapEntry
MemMapEntry

enum
{NUCLEI_N_DEBUG,NUCLEI_N_ROM,NUCLEI_N_TIMER,NUCLEI_N_ECLIC,NUCLEI_N_GPIO,NUCLEI_N_UART0,NUCLEI_N_QSPI0,NUCLEI_N_PWM0,NUCLEI_N_UART1,NUCLEI_N_QSPI1,NUCLEI_N_PWM1,NUCLEI_N_QSPI2,NUCLEI_N_PWM2,NUCLEI_N_XIP,NUCLEI_N_DRAM,NUCLEI_N_ILM,NUCLEI_N_DLM
};static MemMapEntry nuclei_n_memmap[] = {[NUCLEI_N_DEBUG] 	= 	{0x0, 0x1000},[NUCLEI_N_ROM] 		= 	{0x1000, 0x1000},[NUCLEI_N_TIMER] 	= 	{0x2000000, 0x1000},[NUCLEI_N_ECLIC] 	= 	{0xc000000, 0x10000},[NUCLEI_N_GPIO] 	= 	{0x10012000, 0x1000},[NUCLEI_N_UART0] 	= 	{0x10013000, 0x1000},[NUCLEI_N_QSPI0] 	= 	{0x10014000, 0x1000},[NUCLEI_N_PWM0] 	= 	{0x10015000, 0x1000},[NUCLEI_N_UART1] 	= 	{0x10023000, 0x1000},[NUCLEI_N_QSPI1] 	= 	{0x10024000, 0x1000},[NUCLEI_N_PWM1] 	= 	{0x10025000, 0x1000},[NUCLEI_N_QSPI2] 	= 	{0x10034000, 0x1000},[NUCLEI_N_PWM2] 	= 	{0x10035000, 0x1000},[NUCLEI_N_XIP] 		= 	{0x20000000, 0x10000000},[NUCLEI_N_DRAM] 	= 	{0xa0000000, 0x0},[NUCLEI_N_ILM] 		= 	{0x80000000, 0x20000},[NUCLEI_N_DLM] 		= 	rub{0x90000000, 0x20000},
};

之后我们初始化ROM地址:

/* Internal ROM */memory_region_init_rom(&s->internal_rom, OBJECT(obj), "riscv.nuclei.n.irom", memmap[NUCLEI_N_ROM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_ROM].base, &s->internal_rom);

这里我们假设idlm和ROM都为Soc外设:
于是我们编写相关函数:

static void nuclei_n_soc_memory_create(Object *obj)
{NucLeiNSoCState *s = NUCLEI_N_SOC(obj);const MemMapEntry *memmap = nuclei_n_memmap;MemoryRegion *sys_mem = get_system_memory();/* Internal ROM */memory_region_init_rom(&s->internal_rom, OBJECT(obj), "riscv.nuclei.n.irom", memmap[NUCLEI_N_ROM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_ROM].base, &s->internal_rom);/* Initialize ilm dlm */memory_region_init_ram(&s->ilm, NULL, "riscv.nuclei.n.ilm", memmap[NUCLEI_N_ILM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_ILM].base, &s->ilm);memory_region_init_ram(&s->dlm, NULL, "riscv.nuclei.n.dlm", memmap[NUCLEI_N_DLM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_DLM].base, &s->dlm);/* SysTimer */create_unimplemented_device("riscv.nuclei.n.timer", memmap[NUCLEI_N_TIMER].base, memmap[NUCLEI_N_TIMER].size);/* Eclic */create_unimplemented_device("riscv.nuclei.n.eclic", memmap[NUCLEI_N_ECLIC].base, memmap[NUCLEI_N_ECLIC].size);/* GPIO */create_unimplemented_device("riscv.nuclei.n.gpio", memmap[NUCLEI_N_GPIO].base, memmap[NUCLEI_N_GPIO].size);
}

因为还没有实现一些设备,所以我们创建unimplemented设备来占用内存:

	 /* SysTimer */create_unimplemented_device("riscv.nuclei.n.timer", memmap[NUCLEI_N_TIMER].base, memmap[NUCLEI_N_TIMER].size);/* Eclic */create_unimplemented_device("riscv.nuclei.n.eclic", memmap[NUCLEI_N_ECLIC].base, memmap[NUCLEI_N_ECLIC].size);/* GPIO */create_unimplemented_device("riscv.nuclei.n.gpio", memmap[NUCLEI_N_GPIO].base, memmap[NUCLEI_N_GPIO].size);

这次我们把CPU的初始化和实例化也类似封装成一个函数:

static void nuclei_n_soc_cpu_create(Object *obj)
{MachineState *ms = MACHINE(qdev_get_machine());NucLeiNSoCState *s = NUCLEI_N_SOC(obj);object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);  //初始化CPUobject_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, &error_abort);      object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort);sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);            		//CPU实例化
}

然后我们在nuclei_n_soc_instance_init中调用:

static void nuclei_n_soc_instance_init(Object *obj)
{/* SOC CPU */nuclei_n_soc_cpu_create(obj);/* SOC Memory */nuclei_n_soc_memory_create(obj);
}

其次是设置ROM和加载kernel,我们在整个Machine实例中进行初始化:

static void nuclei_mcu_machine_init(MachineState *machine)
{NucLeiNState *s = MCU_FPGA_MACHINE(machine);const MemMapEntry *memmap = nuclei_n_memmap;target_ulong start_addr;int i;/* Initialize SOC */object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_NUCLEI_N_SOC);qdev_realize(DEVICE(&s->soc), NULL, &error_abort);//选择启动方式switch (s->msel){case MSEL_ILM:start_addr = memmap[NUCLEI_N_ILM].base;break;case MSEL_FLASH:start_addr = memmap[NUCLEI_N_XIP].base;break;case MSEL_FLASHXIP:start_addr = memmap[NUCLEI_N_XIP].base;break;case MSEL_DDR:start_addr = memmap[NUCLEI_N_DRAM].base;break;default:start_addr = memmap[NUCLEI_N_ILM].base;break;}/* reset vector */uint32_t reset_vec[8] = {0x00000297, /* 1:  auipc  t0, %pcrel_hi(dtb) */0x02028593, /*     addi   a1, t0, %pcrel_lo(1b) */0xf1402573, /*     csrr   a0, mhartid  */
#if defined(TARGET_RISCV32)0x0182a283, /*     lw     t0, 24(t0) */
#elif defined(TARGET_RISCV64)0x0182b283, /*     ld     t0, 24(t0) */
#endif0x00028067, /*     jr     t0 */0x00000000,start_addr, /* start: .dword DRAM_BASE */0x00000000,};/* copy in the reset vector in little_endian byte order */for (i = 0; i < sizeof(reset_vec) >> 2; i++){reset_vec[i] = cpu_to_le32(reset_vec[i]);}rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),memmap[NUCLEI_N_ROM].base, &address_space_memory); //CPU初始化地址/* boot rom */if (machine->kernel_filename){riscv_load_kernel(machine, &s->soc.cpus, start_addr, true, NULL);	//将裸机代码加载到地址start_addr// riscv_load_kernel(machine->kernel_filename, start_addr, NULL);  }
}

测试

我们进行测试:
编译完成后运行程序并执行:

info mtree

测试结果

结尾附上完整代码:
nuclei_n.h

#include "hw/boards.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/sysbus.h"#define TYPE_NUCLEI_N_SOC "riscv.nuclei.n.soc"
#define NUCLEI_N_SOC(obj) \OBJECT_CHECK(NucLeiNSoCState, (obj), TYPE_NUCLEI_N_SOC)/* CPU 定义 */
#define NUCLEI_N_CPU TYPE_RISCV_CPU_NUCLEI_N600typedef struct NucLeiNSoCState
{/*< private >*/DeviceState parent_obj;/*< public >*/RISCVHartArrayState cpus;MemoryRegion internal_rom;MemoryRegion ilm;MemoryRegion dlm;MemoryRegion xip_mem;
} NucLeiNSoCState;/* Machine state定义 */
#define TYPE_NUCLEI_MCU_FPGA_MACHINE MACHINE_TYPE_NAME("mcu_200t")
#define MCU_FPGA_MACHINE(obj) \OBJECT_CHECK(NucLeiNState, (obj), TYPE_NUCLEI_MCU_FPGA_MACHINE)typedef struct NucLeiNState
{/*< private >*/MachineState parent;/*< public >*/NucLeiNSoCState soc;uint32_t msel;
} NucLeiNState;enum
{MSEL_ILM = 1,MSEL_FLASH = 2,MSEL_FLASHXIP = 3,MSEL_DDR = 4
};enum
{NUCLEI_N_DEBUG,NUCLEI_N_ROM,NUCLEI_N_TIMER,NUCLEI_N_ECLIC,NUCLEI_N_GPIO,NUCLEI_N_UART0,NUCLEI_N_QSPI0,NUCLEI_N_PWM0,NUCLEI_N_UART1,NUCLEI_N_QSPI1,NUCLEI_N_PWM1,NUCLEI_N_QSPI2,NUCLEI_N_PWM2,NUCLEI_N_XIP,NUCLEI_N_DRAM,NUCLEI_N_ILM,NUCLEI_N_DLM
};

nuclei_n.c

#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/riscv/nuclei_n.h"
#include "qapi/visitor.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/sysbus.h"
#include "target/riscv/cpu.h"
#include "hw/misc/unimp.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/boot.h"static MemMapEntry nuclei_n_memmap[] = {[NUCLEI_N_DEBUG] 	= 	{0x0, 0x1000},[NUCLEI_N_ROM] 		= 	{0x1000, 0x1000},[NUCLEI_N_TIMER] 	= 	{0x2000000, 0x1000},[NUCLEI_N_ECLIC] 	= 	{0xc000000, 0x10000},[NUCLEI_N_GPIO] 	= 	{0x10012000, 0x1000},[NUCLEI_N_UART0] 	= 	{0x10013000, 0x1000},[NUCLEI_N_QSPI0] 	= 	{0x10014000, 0x1000},[NUCLEI_N_PWM0] 	= 	{0x10015000, 0x1000},[NUCLEI_N_UART1] 	= 	{0x10023000, 0x1000},[NUCLEI_N_QSPI1] 	= 	{0x10024000, 0x1000},[NUCLEI_N_PWM1] 	= 	{0x10025000, 0x1000},[NUCLEI_N_QSPI2] 	= 	{0x10034000, 0x1000},[NUCLEI_N_PWM2] 	= 	{0x10035000, 0x1000},[NUCLEI_N_XIP] 		= 	{0x20000000, 0x10000000},[NUCLEI_N_DRAM] 	= 	{0xa0000000, 0x0},[NUCLEI_N_ILM] 		= 	{0x80000000, 0x20000},[NUCLEI_N_DLM] 		= 	{0x90000000, 0x20000},
};static void nuclei_n_soc_cpu_create(Object *obj)
{MachineState *ms = MACHINE(qdev_get_machine());NucLeiNSoCState *s = NUCLEI_N_SOC(obj);object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);  //初始化CPUobject_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, &error_abort);      object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort);sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);            		//CPU实例化
}static void nuclei_n_soc_memory_create(Object *obj)
{NucLeiNSoCState *s = NUCLEI_N_SOC(obj);const MemMapEntry *memmap = nuclei_n_memmap;MemoryRegion *sys_mem = get_system_memory();/* Internal ROM */memory_region_init_rom(&s->internal_rom, OBJECT(obj), "riscv.nuclei.n.irom", memmap[NUCLEI_N_ROM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_ROM].base, &s->internal_rom);/* Initialize ilm dlm */memory_region_init_ram(&s->ilm, NULL, "riscv.nuclei.n.ilm", memmap[NUCLEI_N_ILM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_ILM].base, &s->ilm);memory_region_init_ram(&s->dlm, NULL, "riscv.nuclei.n.dlm", memmap[NUCLEI_N_DLM].size, &error_fatal);memory_region_add_subregion(sys_mem, memmap[NUCLEI_N_DLM].base, &s->dlm);/* SysTimer */create_unimplemented_device("riscv.nuclei.n.timer",memmap[NUCLEI_N_TIMER].base, memmap[NUCLEI_N_TIMER].size);/* Eclic */create_unimplemented_device("riscv.nuclei.n.eclic",memmap[NUCLEI_N_ECLIC].base, memmap[NUCLEI_N_ECLIC].size);/* GPIO */create_unimplemented_device("riscv.nuclei.n.gpio",memmap[NUCLEI_N_GPIO].base, memmap[NUCLEI_N_GPIO].size);
}static void nuclei_n_soc_instance_init(Object *obj)
{/* SOC CPU */nuclei_n_soc_cpu_create(obj);/* SOC Memory */nuclei_n_soc_memory_create(obj);
}static void nuclei_n_soc_class_init(ObjectClass *oc, void *data)
{DeviceClass *dc = DEVICE_CLASS(oc);dc->user_creatable = false;
}static const TypeInfo nuclei_n_soc_type_info = {.name = TYPE_NUCLEI_N_SOC,.parent = TYPE_DEVICE,.instance_size = sizeof(NucLeiNSoCState),.instance_init = nuclei_n_soc_instance_init,.class_init = nuclei_n_soc_class_init,
};
static void nuclei_n_soc_register_types(void)
{type_register_static(&nuclei_n_soc_type_info);
}
type_init(nuclei_n_soc_register_types)static void nuclei_mcu_machine_init(MachineState *machine)
{NucLeiNState *s = MCU_FPGA_MACHINE(machine);const MemMapEntry *memmap = nuclei_n_memmap;target_ulong start_addr;int i;/* Initialize SOC */object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_NUCLEI_N_SOC);qdev_realize(DEVICE(&s->soc), NULL, &error_abort);//选择启动方式switch (s->msel){case MSEL_ILM:start_addr = memmap[NUCLEI_N_ILM].base;break;case MSEL_FLASH:start_addr = memmap[NUCLEI_N_XIP].base;break;case MSEL_FLASHXIP:start_addr = memmap[NUCLEI_N_XIP].base;break;case MSEL_DDR:start_addr = memmap[NUCLEI_N_DRAM].base;break;default:start_addr = memmap[NUCLEI_N_ILM].base;break;}/* reset vector */uint32_t reset_vec[8] = {0x00000297, /* 1:  auipc  t0, %pcrel_hi(dtb) */0x02028593, /*     addi   a1, t0, %pcrel_lo(1b) */0xf1402573, /*     csrr   a0, mhartid  */
#if defined(TARGET_RISCV32)0x0182a283, /*     lw     t0, 24(t0) */
#elif defined(TARGET_RISCV64)0x0182b283, /*     ld     t0, 24(t0) */
#endif0x00028067, /*     jr     t0 */0x00000000,start_addr, /* start: .dword DRAM_BASE */0x00000000,};/* copy in the reset vector in little_endian byte order */for (i = 0; i < sizeof(reset_vec) >> 2; i++){reset_vec[i] = cpu_to_le32(reset_vec[i]);}rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),memmap[NUCLEI_N_ROM].base, &address_space_memory); //CPU初始化地址/* boot rom */if (machine->kernel_filename){riscv_load_kernel(machine, &s->soc.cpus, start_addr, true, NULL);	//将裸机代码加载到地址start_addr// riscv_load_kernel(machine->kernel_filename, start_addr, NULL);  }
}static void nuclei_machine_class_init(ObjectClass *oc, void *data)
{MachineClass *mc = MACHINE_CLASS(oc);mc->desc = "Nuclei MCU 200T FPGA Evaluation Kit";mc->init = nuclei_mcu_machine_init;mc->max_cpus = 1;mc->default_cpu_type = NUCLEI_N_CPU;
}static const TypeInfo nuclei_machine_typeinfo = {.name = TYPE_NUCLEI_MCU_FPGA_MACHINE,.parent = TYPE_MACHINE,.class_init = nuclei_machine_class_init,.instance_size = sizeof(NucLeiNState),
};
static void nuclei_machine_init_register_types(void)
{type_register_static(&nuclei_machine_typeinfo);
}
type_init(nuclei_machine_init_register_types)

参考资料

  1. [完结]从零开始的RISC-V模拟器开发·第一季·2021春季
  2. 新建quard-star开发板

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/637753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣日记1.21-【回溯算法篇】77. 组合

力扣日记&#xff1a;【回溯算法篇】77. 组合 日期&#xff1a;2023.1.21 参考&#xff1a;代码随想录、力扣 终于结束二叉树了&#xff01;听说回溯篇也是个大头&#xff0c;不知道这一篇得持续多久了…… 77. 组合 题目描述 难度&#xff1a;中等 给定两个整数 n 和 k&#…

LSTM时间序列预测

本文借鉴了数学建模清风老师的课件与思路&#xff0c;可以点击查看链接查看清风老师视频讲解&#xff1a;【1】演示&#xff1a;基于LSTM深度学习网络预测时间序列&#xff08;MATLAB工具箱&#xff09;_哔哩哔哩_bilibili % Forecast of time series based on LSTM deep learn…

敏捷测试和DevOpes自动化测试的区别

敏捷测试和DevOps自动化测试在以下方面存在区别&#x1f447; 1️⃣目标 &#x1f388;敏捷测试的主要目标是提供快速的反馈和持续的改进&#xff0c;以便在开发过程中尽早发现和解决问题&#xff0c;从而提高软件的质量和可靠性。 &#x1f308;DevOps自动化测试的目标是提高软…

Java学习笔记(七)——操作数组工具类Arrays

文章目录 ArraysArrays.toString()Arrays.binarySearch()Arrays.copyOf()Arrays.copyOfRange()Arrays.fill()Arrays.sort()升序排序降序排序 Arrays 操作数组的工具类。 Arrays.toString() import java.util.Arrays;public class test40 {public static void main(String[] a…

【Docker篇】详细讲解容器相关命令

&#x1f38a;专栏【Docker】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f6f8;容器&#x1f339;相关命令&#x1f354;案例⭐创建并运…

第四课:GPT2

文章目录 第四课&#xff1a;GPT21、学习总结&#xff1a;GPT2的学习目标和概念任务调节零样本学习和零短任务迁移 模型结构GPT-2 自注意力掩码实现1- 创建qkv2- 评分3- 合并注意力头4- Projecting 课程ppt及代码地址 2、学习心得&#xff1a;3、经验分享&#xff1a;4、课程反…

AWS 专题学习 P8 (ECS、EKS、Lambda、CloudFront、DynamoDB)

文章目录 什么是 Docker&#xff1f;操作系统上的 DockerDocker 镜像存储Docker vs. Virtual MachinesDocker 入门AWS 中的 Docker Containers Management Amazon ECSEC2 Launch TypeFargate Launch TypeECS 的 IAM RolesLoad Balancer IntegrationsData Volumes (EFS)ECS Serv…

Object.prototype.toString.call个人理解

文章目录 这段代码的常见用处参考文献&#xff1a; 拆分理解1、Object.prototype.toString小问题参考文献&#xff1a; 2、call函数的作用参考文献 3、继续深入一些&#xff08;这部分内容是个人理解&#xff0c;没有明确文献支撑&#xff09; 这段代码的常见用处 Object.prot…

easy Exsel导出

目录 一、首先引入依赖 二、然后封装一个VO 三、Controller层 四、Service实现类 引用样式 自适应列宽 自适应行高 五、测试 postman ​编辑 浏览器 异常 分配到这个任务了&#xff0c;写个小demo记录下&#xff0c;具体可参考EasyExcel官方文档 我用的是web上传…

CCC数字钥匙设计【NFC基础】--LPCD相关介绍

关于NFC卡检测&#xff0c;主要可以分成两个步骤&#xff1a; 1、LPCD低功耗检测&#xff0c;唤醒NFC读卡器。 2、唤醒后&#xff0c;NFC读卡器或MCU控制器轮询Type A、Type B、Type F、Type V&#xff08;ISO15693&#xff09;等卡类型。 本文主要介绍LPCD相关功能&#xff…

设备管理——WinCC 给你神助功

要实现“设备高效”&#xff0c;就必须“管之有道”&#xff0c;来自设备层的数据支撑将是必不可少的&#xff0c;提高设备效能的2个关键在于降低平时停机时间 (MDT) 和提高平均无故障时间 (MTBF)。通常来说&#xff0c;设备维护可大致可分为三个层次&#xff1a;纠正性维护&am…

c++ mysql数据库编程(linux系统)

ubuntu下mysql数据库的安装 ubuntu安装mysql&#xff08;图文详解&#xff09;-CSDN博客https://blog.csdn.net/qq_58158950/article/details/135667062?spm1001.2014.3001.5501 项目目录结构 数据库及表结构 public.h //打印错误信息 #ifndef PUBLIC_h #define PUBLIC_H…

【详解】通讯录项目

目录 通讯录项目要求&#xff1a; 引言&#xff1a; 步骤如下&#xff1a; 用户的数据类型&#xff1a; 初始化循序表&#xff1a; 菜单&#xff1a; 添加联系人&#xff1a; 删除联系人&#xff1a; 修改联系人&#xff1a; 查找联系人&#xff1a; 查看通讯录&…

《WebKit 技术内幕》之七(1): 渲染基础

《WebKit 技术内幕》之七&#xff08;1&#xff09;&#xff1a; 渲染基础 WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树。 RenderObject 树同其他树&#xff08;如 RenderLayer 树等&#xff09;&#xff0c;构成了 WebKit 渲染的为要基础设施。 1…

【RT-DETR有效改进】Google | EfficientNetV1一种超轻量又高效的网络 (附代码 + 添加教程)

前言 大家好&#xff0c;我是Snu77&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持Re…

python算法与数据结构---排序和归并排序

学习目标 掌握归并排序的基本原理使用python语言解答归并排序题目 归并排序 原理及过程 将两个有序的数组合并成一个有序数组称为从上往下分解&#xff1a;把当前区间一分为二&#xff0c;直至分解为若干个长度为1的子数组从上往下的合并&#xff1a;两个有序的子区域两两向…

Pytest 测试框架与Allure 测试报告——Allure2测试报告-L1

目录&#xff1a; allure2安装 Allure2介绍Allure2报告展示Allure2报告展示-首页概览Allure2报告展示-用例详情页Allure2安装Allure2下载与安装Allure环境验证插件安装-Python插件安装-Java验证插件安装-Javaallure2运行方式 生成测试报告流程使用Allure2运行方式-Python使用A…

大模型关键技术:上下文学习、思维链、RLHF、参数微调、并行训练、旋转位置编码、模型加速、大模型注意力机制优化、永久记忆、LangChain、知识图谱、多模态

大模型关键技术 大模型综述上下文学习思维链 CoT奖励建模参数微调并行训练模型加速永久记忆&#xff1a;大模型遗忘LangChain知识图谱多模态大模型系统优化AI 绘图幻觉问题从 GPT1 - GPT4 拆解GPTs 对比主流大模型技术点旋转位置编码层归一化激活函数注意力机制优化 大模型综述…

四种方法将 Docker Registry 迁移至 Harbor

Registry Docker Distribution Docker Distribution 是第一个是实现了打包、发布、存储和镜像分发的工具&#xff0c;起到 docker registry 的作用。&#xff08;目前 Distribution 已经捐赠给了 CNCF&#xff09;。其中 Docker Distribution 中的 spec 规范后来也就成为了 O…

Semaphone应用源码分析

Semaphone应用&源码分析 3.1 Semaphore介绍 sync&#xff0c;ReentrantLock是互斥锁&#xff0c;保证一个资源同一时间只允许被一个线程访问 Semaphore&#xff08;信号量&#xff09;保证1个或多个资源可以被指定数量的线程同时访问 底层实现是基于AQS去做的。 Semap…