At the same time deprecate the ISA string CPUs. It is dobtful anyone specifies the CPUs, but we are keeping them for the Spike machine (which is about to be depreated) so we may as well just mark them as deprecated. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Palmer Dabbelt <palmer@sifive.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
511 lines
15 KiB
C
511 lines
15 KiB
C
/*
|
|
* QEMU RISC-V CPU
|
|
*
|
|
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
|
|
* Copyright (c) 2017-2018 SiFive, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2 or later, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/qemu-print.h"
|
|
#include "qemu/log.h"
|
|
#include "cpu.h"
|
|
#include "exec/exec-all.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "migration/vmstate.h"
|
|
|
|
/* RISC-V CPU definitions */
|
|
|
|
static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
|
|
|
|
const char * const riscv_int_regnames[] = {
|
|
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
|
|
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
|
|
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
|
|
"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
|
|
};
|
|
|
|
const char * const riscv_fpr_regnames[] = {
|
|
"ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
|
|
"fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
|
|
"fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
|
|
"fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"
|
|
};
|
|
|
|
const char * const riscv_excp_names[] = {
|
|
"misaligned_fetch",
|
|
"fault_fetch",
|
|
"illegal_instruction",
|
|
"breakpoint",
|
|
"misaligned_load",
|
|
"fault_load",
|
|
"misaligned_store",
|
|
"fault_store",
|
|
"user_ecall",
|
|
"supervisor_ecall",
|
|
"hypervisor_ecall",
|
|
"machine_ecall",
|
|
"exec_page_fault",
|
|
"load_page_fault",
|
|
"reserved",
|
|
"store_page_fault"
|
|
};
|
|
|
|
const char * const riscv_intr_names[] = {
|
|
"u_software",
|
|
"s_software",
|
|
"h_software",
|
|
"m_software",
|
|
"u_timer",
|
|
"s_timer",
|
|
"h_timer",
|
|
"m_timer",
|
|
"u_external",
|
|
"s_external",
|
|
"h_external",
|
|
"m_external",
|
|
"reserved",
|
|
"reserved",
|
|
"reserved",
|
|
"reserved"
|
|
};
|
|
|
|
static void set_misa(CPURISCVState *env, target_ulong misa)
|
|
{
|
|
env->misa_mask = env->misa = misa;
|
|
}
|
|
|
|
static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
|
|
{
|
|
env->user_ver = user_ver;
|
|
env->priv_ver = priv_ver;
|
|
}
|
|
|
|
static void set_feature(CPURISCVState *env, int feature)
|
|
{
|
|
env->features |= (1ULL << feature);
|
|
}
|
|
|
|
static void set_resetvec(CPURISCVState *env, int resetvec)
|
|
{
|
|
#ifndef CONFIG_USER_ONLY
|
|
env->resetvec = resetvec;
|
|
#endif
|
|
}
|
|
|
|
static void riscv_any_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
}
|
|
|
|
#if defined(TARGET_RISCV32)
|
|
|
|
static void riscv_base32_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
}
|
|
|
|
static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv32imacu_nommu_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
#elif defined(TARGET_RISCV64)
|
|
|
|
static void riscv_base64_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
}
|
|
|
|
static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
static void rv64imacu_nommu_cpu_init(Object *obj)
|
|
{
|
|
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
|
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
|
|
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
#endif
|
|
|
|
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
|
|
{
|
|
ObjectClass *oc;
|
|
char *typename;
|
|
char **cpuname;
|
|
|
|
cpuname = g_strsplit(cpu_model, ",", 1);
|
|
typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
|
|
oc = object_class_by_name(typename);
|
|
g_strfreev(cpuname);
|
|
g_free(typename);
|
|
if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
|
|
object_class_is_abstract(oc)) {
|
|
return NULL;
|
|
}
|
|
return oc;
|
|
}
|
|
|
|
static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
int i;
|
|
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc);
|
|
#ifndef CONFIG_USER_ONLY
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ",
|
|
(target_ulong)atomic_read(&env->mip));
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc);
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause);
|
|
#endif
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
qemu_fprintf(f, " %s " TARGET_FMT_lx,
|
|
riscv_int_regnames[i], env->gpr[i]);
|
|
if ((i & 3) == 3) {
|
|
qemu_fprintf(f, "\n");
|
|
}
|
|
}
|
|
if (flags & CPU_DUMP_FPU) {
|
|
for (i = 0; i < 32; i++) {
|
|
qemu_fprintf(f, " %s %016" PRIx64,
|
|
riscv_fpr_regnames[i], env->fpr[i]);
|
|
if ((i & 3) == 3) {
|
|
qemu_fprintf(f, "\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
env->pc = value;
|
|
}
|
|
|
|
static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
env->pc = tb->pc;
|
|
}
|
|
|
|
static bool riscv_cpu_has_work(CPUState *cs)
|
|
{
|
|
#ifndef CONFIG_USER_ONLY
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
CPURISCVState *env = &cpu->env;
|
|
/*
|
|
* Definition of the WFI instruction requires it to ignore the privilege
|
|
* mode and delegation registers, but respect individual enables
|
|
*/
|
|
return (atomic_read(&env->mip) & env->mie) != 0;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
|
|
target_ulong *data)
|
|
{
|
|
env->pc = data[0];
|
|
}
|
|
|
|
static void riscv_cpu_reset(CPUState *cs)
|
|
{
|
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
|
CPURISCVState *env = &cpu->env;
|
|
|
|
mcc->parent_reset(cs);
|
|
#ifndef CONFIG_USER_ONLY
|
|
env->priv = PRV_M;
|
|
env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
|
|
env->mcause = 0;
|
|
env->pc = env->resetvec;
|
|
#endif
|
|
cs->exception_index = EXCP_NONE;
|
|
set_default_nan_mode(1, &env->fp_status);
|
|
}
|
|
|
|
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
|
{
|
|
#if defined(TARGET_RISCV32)
|
|
info->print_insn = print_insn_riscv32;
|
|
#elif defined(TARGET_RISCV64)
|
|
info->print_insn = print_insn_riscv64;
|
|
#endif
|
|
}
|
|
|
|
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
CPUState *cs = CPU(dev);
|
|
RISCVCPU *cpu = RISCV_CPU(dev);
|
|
CPURISCVState *env = &cpu->env;
|
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
|
int priv_version = PRIV_VERSION_1_10_0;
|
|
int user_version = USER_VERSION_2_02_0;
|
|
Error *local_err = NULL;
|
|
|
|
cpu_exec_realizefn(cs, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
if (cpu->cfg.priv_spec) {
|
|
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
|
|
priv_version = PRIV_VERSION_1_10_0;
|
|
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.9.1")) {
|
|
priv_version = PRIV_VERSION_1_09_1;
|
|
} else {
|
|
error_setg(errp,
|
|
"Unsupported privilege spec version '%s'",
|
|
cpu->cfg.priv_spec);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cpu->cfg.user_spec) {
|
|
if (!g_strcmp0(cpu->cfg.user_spec, "v2.02.0")) {
|
|
user_version = USER_VERSION_2_02_0;
|
|
} else {
|
|
error_setg(errp,
|
|
"Unsupported user spec version '%s'",
|
|
cpu->cfg.user_spec);
|
|
return;
|
|
}
|
|
}
|
|
|
|
set_versions(env, user_version, priv_version);
|
|
set_resetvec(env, DEFAULT_RSTVEC);
|
|
|
|
if (cpu->cfg.mmu) {
|
|
set_feature(env, RISCV_FEATURE_MMU);
|
|
}
|
|
|
|
if (cpu->cfg.pmp) {
|
|
set_feature(env, RISCV_FEATURE_PMP);
|
|
}
|
|
|
|
riscv_cpu_register_gdb_regs_for_features(cs);
|
|
|
|
qemu_init_vcpu(cs);
|
|
cpu_reset(cs);
|
|
|
|
mcc->parent_realize(dev, errp);
|
|
}
|
|
|
|
static void riscv_cpu_init(Object *obj)
|
|
{
|
|
CPUState *cs = CPU(obj);
|
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
|
|
|
cs->env_ptr = &cpu->env;
|
|
}
|
|
|
|
static const VMStateDescription vmstate_riscv_cpu = {
|
|
.name = "cpu",
|
|
.unmigratable = 1,
|
|
};
|
|
|
|
static Property riscv_cpu_properties[] = {
|
|
DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
|
|
DEFINE_PROP_STRING("user_spec", RISCVCPU, cfg.user_spec),
|
|
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
|
|
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
|
{
|
|
RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
|
|
CPUClass *cc = CPU_CLASS(c);
|
|
DeviceClass *dc = DEVICE_CLASS(c);
|
|
|
|
device_class_set_parent_realize(dc, riscv_cpu_realize,
|
|
&mcc->parent_realize);
|
|
|
|
mcc->parent_reset = cc->reset;
|
|
cc->reset = riscv_cpu_reset;
|
|
|
|
cc->class_by_name = riscv_cpu_class_by_name;
|
|
cc->has_work = riscv_cpu_has_work;
|
|
cc->do_interrupt = riscv_cpu_do_interrupt;
|
|
cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
|
|
cc->dump_state = riscv_cpu_dump_state;
|
|
cc->set_pc = riscv_cpu_set_pc;
|
|
cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
|
|
cc->gdb_read_register = riscv_cpu_gdb_read_register;
|
|
cc->gdb_write_register = riscv_cpu_gdb_write_register;
|
|
cc->gdb_num_core_regs = 33;
|
|
#if defined(TARGET_RISCV32)
|
|
cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
|
|
#elif defined(TARGET_RISCV64)
|
|
cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
|
|
#endif
|
|
cc->gdb_stop_before_watchpoint = true;
|
|
cc->disas_set_info = riscv_cpu_disas_set_info;
|
|
#ifndef CONFIG_USER_ONLY
|
|
cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
|
|
cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
|
|
#endif
|
|
#ifdef CONFIG_TCG
|
|
cc->tcg_initialize = riscv_translate_init;
|
|
cc->tlb_fill = riscv_cpu_tlb_fill;
|
|
#endif
|
|
/* For now, mark unmigratable: */
|
|
cc->vmsd = &vmstate_riscv_cpu;
|
|
dc->props = riscv_cpu_properties;
|
|
}
|
|
|
|
char *riscv_isa_string(RISCVCPU *cpu)
|
|
{
|
|
int i;
|
|
const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
|
|
char *isa_str = g_new(char, maxlen);
|
|
char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
|
|
for (i = 0; i < sizeof(riscv_exts); i++) {
|
|
if (cpu->env.misa & RV(riscv_exts[i])) {
|
|
*p++ = qemu_tolower(riscv_exts[i]);
|
|
}
|
|
}
|
|
*p = '\0';
|
|
return isa_str;
|
|
}
|
|
|
|
static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b)
|
|
{
|
|
ObjectClass *class_a = (ObjectClass *)a;
|
|
ObjectClass *class_b = (ObjectClass *)b;
|
|
const char *name_a, *name_b;
|
|
|
|
name_a = object_class_get_name(class_a);
|
|
name_b = object_class_get_name(class_b);
|
|
return strcmp(name_a, name_b);
|
|
}
|
|
|
|
static void riscv_cpu_list_entry(gpointer data, gpointer user_data)
|
|
{
|
|
const char *typename = object_class_get_name(OBJECT_CLASS(data));
|
|
int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX);
|
|
|
|
qemu_printf("%.*s\n", len, typename);
|
|
}
|
|
|
|
void riscv_cpu_list(void)
|
|
{
|
|
GSList *list;
|
|
|
|
list = object_class_get_list(TYPE_RISCV_CPU, false);
|
|
list = g_slist_sort(list, riscv_cpu_list_compare);
|
|
g_slist_foreach(list, riscv_cpu_list_entry, NULL);
|
|
g_slist_free(list);
|
|
}
|
|
|
|
#define DEFINE_CPU(type_name, initfn) \
|
|
{ \
|
|
.name = type_name, \
|
|
.parent = TYPE_RISCV_CPU, \
|
|
.instance_init = initfn \
|
|
}
|
|
|
|
static const TypeInfo riscv_cpu_type_infos[] = {
|
|
{
|
|
.name = TYPE_RISCV_CPU,
|
|
.parent = TYPE_CPU,
|
|
.instance_size = sizeof(RISCVCPU),
|
|
.instance_init = riscv_cpu_init,
|
|
.abstract = true,
|
|
.class_size = sizeof(RISCVCPUClass),
|
|
.class_init = riscv_cpu_class_init,
|
|
},
|
|
DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
|
|
#if defined(TARGET_RISCV32)
|
|
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init)
|
|
#elif defined(TARGET_RISCV64)
|
|
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init),
|
|
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init)
|
|
#endif
|
|
};
|
|
|
|
DEFINE_TYPES(riscv_cpu_type_infos)
|