* [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit
@ 2021-05-07 8:16 wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
` (5 more replies)
0 siblings, 6 replies; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch series provides an implementation of Nuclei FPGA Machine[1].
At present, only MCU mode support is included. In mcu mode, We will
use eclic[2] as interrupt controller. It is compatible with clic
in Machine mode. But It contains some customized csr[3] that will
take up part of the csr code. If there is any question, please
let me know.
Features:
1.Add Nuclei CSR support in eclic mode
2.Add ECLIC Device
3.Add Systimer Device
4.Add Uart Device
5.Add Nuclei FPGA Machine
It have passed Nuclei SDK[4](not include dsp) and RTThread[5] HBird BSP
Test.
Any comments are welcome.Thanks
wangjunqiang
[1] https://doc.nucleisys.com/nuclei_sdk/design/board/nuclei_fpga_eval.html
[2] https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
[3] https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
[4] https://github.com/Nuclei-Software/nuclei-sdk.git
[5] https://github.com/RT-Thread/rt-thread
wangjunqiang (5):
target/riscv: Add Nuclei CSR and Update interrupt handling
hw/intc: Add Nuclei ECLIC device
hw/intc: Add Nuclei Systimer
hw/char: Add Nuclei Uart
Nuclei FPGA Evaluation Kit MCU Machine
default-configs/devices/riscv32-softmmu.mak | 1 +
default-configs/devices/riscv64-softmmu.mak | 1 +
hw/char/Kconfig | 3 +
hw/char/meson.build | 1 +
hw/char/nuclei_uart.c | 208 ++++++++++
hw/intc/Kconfig | 6 +
hw/intc/meson.build | 2 +
hw/intc/nuclei_eclic.c | 437 ++++++++++++++++++++
hw/intc/nuclei_systimer.c | 254 ++++++++++++
hw/riscv/Kconfig | 9 +
hw/riscv/meson.build | 1 +
hw/riscv/nuclei_n.c | 276 +++++++++++++
include/hw/char/nuclei_uart.h | 73 ++++
include/hw/intc/nuclei_eclic.h | 115 ++++++
include/hw/intc/nuclei_systimer.h | 70 ++++
include/hw/riscv/nuclei_n.h | 136 ++++++
target/riscv/cpu.c | 25 +-
target/riscv/cpu.h | 42 +-
target/riscv/cpu_bits.h | 37 ++
target/riscv/cpu_helper.c | 80 +++-
target/riscv/csr.c | 347 +++++++++++++++-
target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
target/riscv/op_helper.c | 14 +
23 files changed, 2145 insertions(+), 9 deletions(-)
create mode 100644 hw/char/nuclei_uart.c
create mode 100644 hw/intc/nuclei_eclic.c
create mode 100644 hw/intc/nuclei_systimer.c
create mode 100644 hw/riscv/nuclei_n.c
create mode 100644 include/hw/char/nuclei_uart.h
create mode 100644 include/hw/intc/nuclei_eclic.h
create mode 100644 include/hw/intc/nuclei_systimer.h
create mode 100644 include/hw/riscv/nuclei_n.h
--
2.17.1
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
@ 2021-05-07 8:16 ` wangjunqiang
2021-05-10 2:17 ` Alistair Francis
2021-05-07 8:16 ` [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device wangjunqiang
` (4 subsequent siblings)
5 siblings, 1 reply; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch adds Nuclei CSR support for ECLIC and update the
related interrupt handling.
https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
---
target/riscv/cpu.c | 25 +-
target/riscv/cpu.h | 42 ++-
target/riscv/cpu_bits.h | 37 +++
target/riscv/cpu_helper.c | 80 +++++-
target/riscv/csr.c | 347 +++++++++++++++++++++++-
target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
target/riscv/op_helper.c | 14 +
7 files changed, 552 insertions(+), 9 deletions(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 7d6ed80f6b..b2a96effbc 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
set_priv_version(env, PRIV_VERSION_1_10_0);
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
}
+
+static void rv64imafdcu_nuclei_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+ set_priv_version(env, PRIV_VERSION_1_10_0);
+ qdev_prop_set_bit(DEVICE(obj), "mmu", false);
+ set_resetvec(env, DEFAULT_RSTVEC);
+ set_feature(env, RISCV_FEATURE_PMP);
+}
#else
static void rv32_base_cpu_init(Object *obj)
{
@@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
set_resetvec(env, DEFAULT_RSTVEC);
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
}
+
+static void rv32imafdcu_nuclei_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+ set_priv_version(env, PRIV_VERSION_1_10_0);
+ qdev_prop_set_bit(DEVICE(obj), "mmu", false);
+ set_resetvec(env, DEFAULT_RSTVEC);
+ set_feature(env, RISCV_FEATURE_PMP);
+}
#endif
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
@@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
* Definition of the WFI instruction requires it to ignore the privilege
* mode and delegation registers, but respect individual enables
*/
- return (env->mip & env->mie) != 0;
+ return ((env->mip & env->mie) != 0 || (env->exccode != -1));
#else
return true;
#endif
@@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
env->mcause = 0;
env->pc = env->resetvec;
+ env->exccode = -1;
env->two_stage_lookup = false;
#endif
cs->exception_index = EXCP_NONE;
@@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
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),
+ DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
#elif defined(TARGET_RISCV64)
DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
#endif
};
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 0a33d387ba..1d3a1986a6 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -33,6 +33,7 @@
#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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
@@ -43,6 +44,8 @@
#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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
+#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
#if defined(TARGET_RISCV32)
# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
@@ -80,7 +83,8 @@
enum {
RISCV_FEATURE_MMU,
RISCV_FEATURE_PMP,
- RISCV_FEATURE_MISA
+ RISCV_FEATURE_MISA,
+ RISCV_FEATURE_ECLIC
};
#define PRIV_VERSION_1_10_0 0x00011000
@@ -174,10 +178,34 @@ struct CPURISCVState {
target_ulong scause;
target_ulong mtvec;
+ target_ulong mtvt; /* eclic */
target_ulong mepc;
target_ulong mcause;
target_ulong mtval; /* since: priv-1.10.0 */
+ target_ulong mnxti; /* eclic */
+ target_ulong mintstatus; /* eclic */
+ target_ulong mscratchcsw;
+ target_ulong mscratchcswl;
+
+ /* NMI CSR*/
+ target_ulong mnvec;
+ target_ulong msubm;
+ target_ulong mdcause;
+ target_ulong mmisc_ctl;
+ target_ulong msavestatus;
+ target_ulong msaveepc1;
+ target_ulong msavecause1;
+ target_ulong msaveepc2;
+ target_ulong msavecause2;
+ target_ulong msavedcause1;
+ target_ulong msavedcause2;
+ target_ulong pushmsubm;
+ target_ulong mtvt2;
+ target_ulong jalmnxti;
+ target_ulong pushmcause;
+ target_ulong pushmepc;
+
/* Hypervisor CSRs */
target_ulong hstatus;
target_ulong hedeleg;
@@ -228,6 +256,9 @@ struct CPURISCVState {
uint64_t mtohost;
uint64_t timecmp;
+ /*nuclei timer comparators */
+ uint64_t mtimecmp;
+
/* physical memory protection */
pmp_table_t pmp_state;
@@ -243,6 +274,13 @@ struct CPURISCVState {
/* Fields from here on are preserved across CPU reset. */
QEMUTimer *timer; /* Internal timer */
+
+ QEMUTimer *mtimer; /* Nuclei Internal timer */
+ void *eclic;
+ uint32_t exccode; /* irq id: 0~11 shv: 12 */
+ uint32_t eclic_flag;
+
+ bool irq_pending;
};
OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
@@ -364,6 +402,8 @@ void riscv_cpu_list(void);
void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
+void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
+void riscv_cpu_eclic_get_next_interrupt(void *eclic);
#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
uint32_t arg);
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index caf4599207..24ed7a99e1 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -149,6 +149,7 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
+#define CSR_MTVT 0x307 /* customized */
/* 32-bit only */
#define CSR_MSTATUSH 0x310
@@ -165,6 +166,10 @@
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
+#define CSR_MNXTI 0x345 /* customized */
+#define CSR_MINTSTATUS 0x346 /* customized */
+#define CSR_MSCRATCHCSW 0x348 /* customized */
+#define CSR_MSCRATCHCSWL 0x349 /* customized */
/* Legacy Machine Trap Handling (priv v1.9.1) */
#define CSR_MBADADDR 0x343
@@ -265,6 +270,24 @@
#define CSR_DPC 0x7b1
#define CSR_DSCRATCH 0x7b2
+/* Nuclei Customized Registers*/
+#define CSR_MNVEC 0x07c3
+#define CSR_MSUBM 0x07c4
+#define CSR_MDCAUSE 0x07c9
+#define CSR_MMISC_CTL 0x07d0
+#define CSR_MSAVESTATUS 0x07d6
+#define CSR_MSAVEEPC1 0x07d7
+#define CSR_MSAVECAUSE1 0x07d8
+#define CSR_MSAVEEPC2 0x07d9
+#define CSR_MSAVECAUSE2 0x07da
+#define CSR_MSAVEDCAUSE1 0x07db
+#define CSR_MSAVEDCAUSE2 0x07dc
+#define CSR_PUSHMSUBM 0x07eb
+#define CSR_MTVT2 0x07ec
+#define CSR_JALMNXTI 0x07ed
+#define CSR_PUSHMCAUSE 0x07ee
+#define CSR_PUSHMEPC 0x07ef
+
/* Performance Counters */
#define CSR_MHPMCOUNTER3 0xb03
#define CSR_MHPMCOUNTER4 0xb04
@@ -549,6 +572,7 @@
#define RISCV_EXCP_VIRT_INSTRUCTION_FAULT 0x16
#define RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT 0x17
+#define RISCV_EXCP_INT_ECLIC 0x40000000
#define RISCV_EXCP_INT_FLAG 0x80000000
#define RISCV_EXCP_INT_MASK 0x7fffffff
@@ -592,4 +616,17 @@
#define MIE_UTIE (1 << IRQ_U_TIMER)
#define MIE_SSIE (1 << IRQ_S_SOFT)
#define MIE_USIE (1 << IRQ_U_SOFT)
+
+/* mintstatus */
+#define MINTSTATUS_MIL 0xff000000 /* mil[31:24] */
+#define MINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+
+/* mcause */
+#define MCAUSE_INTERRUPT 0x80000000 /* INTERRUPT 31*/
+#define MCAUSE_MINHV 0x40000000 /* minhv 30*/
+#define MCAUSE_MPP 0x30000000 /* mpp[29:28] */
+#define MCAUSE_MPIE 0x08000000 /* mpie 27*/
+#define MCAUSE_MPIL 0x00ff0000 /* mpil[23:16] */
+#define MCAUSE_EXCCODE 0x00000fff /* exccode[11:0] */
+
#endif
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 21c54ef561..e481e1b216 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -36,6 +36,22 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
}
#ifndef CONFIG_USER_ONLY
+static int riscv_cpu_local_irq_mode_enabled(CPURISCVState *env, int mode)
+{
+ switch (mode) {
+ case PRV_M:
+ return env->priv < PRV_M ||
+ (env->priv == PRV_M &&
+ get_field(env->mstatus, MSTATUS_MIE));
+ case PRV_S:
+ return env->priv < PRV_S ||
+ (env->priv == PRV_S &&
+ get_field(env->mstatus, MSTATUS_SIE));
+ default:
+ return false;
+ }
+}
+
static int riscv_cpu_local_irq_pending(CPURISCVState *env)
{
target_ulong irqs;
@@ -90,6 +106,19 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return true;
}
}
+
+ if (interrupt_request & CPU_INTERRUPT_ECLIC) {
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ int mode = PRV_M;
+ int enabled = riscv_cpu_local_irq_mode_enabled(env, mode);
+ if (enabled) {
+ cs->exception_index = RISCV_EXCP_INT_ECLIC | env->exccode;
+ cs->interrupt_request &= ~CPU_INTERRUPT_ECLIC;
+ riscv_cpu_do_interrupt(cs);
+ return true;
+ }
+ }
#endif
return false;
}
@@ -884,6 +913,30 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
#endif
}
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong riscv_intr_pc(CPURISCVState *env, bool async,
+ bool eclic_flag, int cause, bool mode)
+{
+ target_ulong newpc = 0;
+ if (eclic_flag) {
+ if (mode) {
+ uint64_t vec_addr = (cause & 0x3FF) * 4 + env->mtvt;
+ cpu_physical_memory_rw(vec_addr, &newpc, 4, 0);
+ } else {
+ if ((env->mtvt2 & 0x1) == 0) {
+ newpc = env->mtvec & 0xfffffffc;
+ } else if ((env->mtvt2 & 0x1) == 1) {
+ newpc = env->mtvt2 & 0xfffffffc;
+ }
+ }
+ } else {
+ newpc = (env->mtvec >> 2 << 2) +
+ ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ }
+ return newpc;
+}
+#endif
+
/*
* Handle Traps
*
@@ -892,8 +945,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
*/
void riscv_cpu_do_interrupt(CPUState *cs)
{
-#if !defined(CONFIG_USER_ONLY)
+#if !defined(CONFIG_USER_ONLY)
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env);
@@ -903,12 +956,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
* so we mask off the MSB and separate into trap type and cause.
*/
bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG);
+ bool eclic_flag = !!(cs->exception_index & RISCV_EXCP_INT_ECLIC);
target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
target_ulong deleg = async ? env->mideleg : env->medeleg;
bool write_tval = false;
target_ulong tval = 0;
target_ulong htval = 0;
target_ulong mtval2 = 0;
+ bool mode = false;
+ uint8_t level = 0;
if (cause == RISCV_EXCP_SEMIHOST) {
if (env->priv >= PRV_S) {
@@ -919,7 +975,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
cause = RISCV_EXCP_BREAKPOINT;
}
- if (!async) {
+ if (!(async || eclic_flag)) {
/* set tval to badaddr for traps with address information */
switch (cause) {
case RISCV_EXCP_INST_GUEST_PAGE_FAULT:
@@ -958,6 +1014,22 @@ void riscv_cpu_do_interrupt(CPUState *cs)
}
}
+ if (eclic_flag) {
+ mode = (cause >> 12) & 0x1;
+ level = (cause >> 13) & 0xFF;
+ cause &= 0x3ff;
+
+ cause |= get_field(env->mstatus, MSTATUS_MPP) << 28;
+ cause |= get_field(env->mintstatus, MINTSTATUS_MIL) << 16;
+ cause |= get_field(env->mstatus, MSTATUS_MPIE) << 27;
+ cause = set_field(cause, MCAUSE_MPP, PRV_M);
+ cause = set_field(cause, MCAUSE_INTERRUPT, 1);
+
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL, level);
+ } else {
+ cause = set_field(cause, MCAUSE_INTERRUPT, 0);
+ }
+
trace_riscv_trap(env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));
@@ -1030,6 +1102,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
riscv_cpu_set_mode(env, PRV_S);
} else {
/* handle the trap in M-mode */
+
if (riscv_has_ext(env, RVH)) {
if (riscv_cpu_virt_enabled(env)) {
riscv_cpu_swap_hypervisor_regs(env);
@@ -1056,8 +1129,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
env->mepc = env->pc;
env->mbadaddr = tval;
env->mtval2 = mtval2;
- env->pc = (env->mtvec >> 2 << 2) +
- ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ env->pc = riscv_intr_pc(env, async, eclic_flag, cause, mode);
riscv_cpu_set_mode(env, PRV_M);
}
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index d2585395bf..555b29d8f3 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -188,6 +188,11 @@ static int pmp(CPURISCVState *env, int csrno)
{
return -!riscv_feature(env, RISCV_FEATURE_PMP);
}
+
+static int eclic(CPURISCVState *env, int csrno)
+{
+ return !!env->eclic;
+}
#endif
/* User Floating-Point CSRs */
@@ -623,11 +628,20 @@ static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val)
static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val)
{
+ int mode1 = val & 0b11, mode2 = val & 0b111111;
/* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val & 3) < 2) {
+ if (mode1 < 2) {
env->mtvec = val;
} else {
- qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
+ /* bits [5:0] encode extended modes currently used by the ECLIC */
+ switch (mode2) {
+ case 0b000011: /* ECLIC mode */
+ env->mtvec = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "CSR_MTVEC: reserved mode not supported\n");
+ }
}
return 0;
}
@@ -644,6 +658,300 @@ static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val)
return 0;
}
+static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mtvt;
+ return 0;
+}
+
+static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mtvt = val;
+ return 0;
+}
+
+static int read_mnxti(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mnxti;
+ return 0;
+}
+
+static int write_mnxti(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mnxti = val;
+ return 0;
+}
+
+static int rmw_mnxti(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ env->mstatus |= (new_value & write_mask) & 0b11111;
+ if (ret_value) {
+ *ret_value = 0;
+ }
+
+ return 0;
+}
+
+static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mintstatus;
+ return 0;
+}
+
+static int write_mintstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mintstatus = val;
+ return 0;
+}
+
+static int read_mscratchcsw(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mscratchcsw;
+ return 0;
+}
+
+static int write_mscratchcsw(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mscratchcsw = val;
+ return 0;
+}
+
+static int rmw_mscratchcsw(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ target_ulong t;
+ if (get_field(env->mcause, MCAUSE_MPP) != PRV_M) {
+ t = new_value;
+ *ret_value = env->mscratch;
+ env->mscratch = t;
+ }else {
+ *ret_value = new_value;
+ }
+ return 0;
+}
+
+static int read_mscratchcswl(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mscratchcswl;
+ return 0;
+}
+
+static int write_mscratchcswl(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mscratchcswl = val;
+ return 0;
+}
+
+static int rmw_mscratchcswl(CPURISCVState *env, int csrno,
+ target_ulong *ret_value, target_ulong new_value,
+ target_ulong write_mask)
+{
+ target_ulong t;
+ if ((get_field(env->mcause, MCAUSE_MPIL) == 0) !=
+ (get_field(env->mintstatus, MINTSTATUS_MIL) == 0)) {
+ t = new_value;
+ *ret_value = env->mscratch;
+ env->mscratch = t;
+ } else {
+ *ret_value = new_value;
+ }
+ return 0;
+}
+
+static int read_mnvec(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mnvec;
+ return 0;
+}
+
+static int write_mnvec(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mnvec = val;
+ return 0;
+}
+
+static int read_msubm(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msubm;
+ return 0;
+}
+
+static int write_msubm(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msubm = val;
+ return 0;
+}
+
+static int read_mdcause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mdcause;
+ return 0;
+}
+
+static int write_mdcause(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mdcause = val;
+ return 0;
+}
+
+static int read_mmisc_ctl(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mmisc_ctl;
+ return 0;
+}
+
+static int write_mmisc_ctl(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mmisc_ctl = val;
+ return 0;
+}
+
+static int read_msavestatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msavestatus;
+ return 0;
+}
+
+static int write_msavestatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msavestatus = val;
+ return 0;
+}
+
+static int read_msaveepc1(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msaveepc1;
+ return 0;
+}
+
+static int write_msaveepc1(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msaveepc1 = val;
+ return 0;
+}
+
+static int read_msavecause1(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msavecause1;
+ return 0;
+}
+
+static int write_msavecause1(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msavecause1 = val;
+ return 0;
+}
+
+static int read_msaveepc2(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msaveepc2;
+ return 0;
+}
+
+static int write_msaveepc2(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msaveepc2 = val;
+ return 0;
+}
+
+static int read_msavecause2(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msavecause2;
+ return 0;
+}
+
+static int write_msavecause2(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msavecause2 = val;
+ return 0;
+}
+
+static int read_msavedcause1(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msavedcause1;
+ return 0;
+}
+
+static int write_msavedcause1(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msavedcause1 = val;
+ return 0;
+}
+
+static int read_msavedcause2(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->msavedcause2;
+ return 0;
+}
+
+static int write_msavedcause2(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->msavedcause2 = val;
+ return 0;
+}
+
+static int rmw_pushmsubm(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+
+ uint64_t notify_addr = new_value * 4 + env->gpr[2];
+
+ cpu_physical_memory_rw(notify_addr, &env->msubm, 4, 1);
+
+ return 0;
+}
+
+static int read_mtvt2(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mtvt2;
+ return 0;
+}
+
+static int write_mtvt2(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mtvt2 = val;
+ return 0;
+}
+
+static int rmw_jalmnxti(CPURISCVState *env, int csrno,
+ target_ulong *ret_value, target_ulong new_value,
+ target_ulong write_mask)
+{
+ target_ulong addr;
+
+ if (env->irq_pending) {
+ uint64_t vec_addr = (env->mcause & 0x3FF) * 4 + env->mtvt;
+ cpu_physical_memory_rw(vec_addr, &addr, 4, 0);
+ env->gpr[1] = env->pc + 4;
+ env->gpr[5] = env->pc + 4;
+ *ret_value = addr;
+ riscv_cpu_eclic_clean_pending(env->eclic, env->mcause & 0x3ff);
+ env->mstatus = set_field(env->mstatus, MSTATUS_MIE, 1);
+ } else
+ *ret_value = env->pc + 4;
+ return 0;
+}
+
+static int rmw_pushmcause(CPURISCVState *env, int csrno,
+ target_ulong *ret_value, target_ulong new_value,
+ target_ulong write_mask)
+{
+ uint64_t notify_addr = new_value * 4 + env->gpr[2];
+ cpu_physical_memory_rw(notify_addr, &env->mcause, 4, 1);
+
+ return 0;
+}
+
+static int rmw_pushmepc(CPURISCVState *env, int csrno,
+ target_ulong *ret_value, target_ulong new_value,
+ target_ulong write_mask)
+{
+ uint64_t notify_addr = new_value * 4 + env->gpr[2];
+ cpu_physical_memory_rw(notify_addr, &env->mepc, 4, 1);
+ return 0;
+}
+
/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val)
{
@@ -654,6 +962,19 @@ static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val)
return 0;
}
+static int read_mucounteren(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->scounteren;
+ return 0;
+}
+
+static int write_mucounteren(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->scounteren = val;
+ return 0;
+}
+
+
/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val)
{
@@ -1416,9 +1737,31 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MIE] = { "mie", any, read_mie, write_mie },
[CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec },
[CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, write_mcounteren },
+ [CSR_MTVT] = { "mtvt", eclic, read_mtvt, write_mtvt },
+ [CSR_MNXTI] = { "mnxti", eclic, read_mnxti, write_mnxti, rmw_mnxti},
+ [CSR_MINTSTATUS] = {"mintstatus", eclic, read_mintstatus, write_mintstatus },
+ [CSR_MSCRATCHCSW] = {"mscratchcsw", any, read_mscratchcsw, write_mscratchcsw, rmw_mscratchcsw},
+ [CSR_MSCRATCHCSWL] = { "mscratchcswl", any, read_mscratchcswl, write_mscratchcswl, rmw_mscratchcswl},
+ [CSR_MNVEC] = { "mnvec", any, read_mnvec, write_mnvec },
+ [CSR_MSUBM] = { "msubm", any, read_msubm, write_msubm },
+ [CSR_MDCAUSE] = { "mdcause", any, read_mdcause, write_mdcause },
+ [CSR_MMISC_CTL] = { "mmisc_ctl", any, read_mmisc_ctl, write_mmisc_ctl },
+ [CSR_MSAVESTATUS] = { "msavestatus", any, read_msavestatus, write_msavestatus },
+ [CSR_MSAVEEPC1] = { "msaveepc1", any, read_msaveepc1, write_msaveepc1 },
+ [CSR_MSAVECAUSE1] = { "msavecause1", any, read_msavecause1, write_msavecause1 },
+ [CSR_MSAVEEPC2] = { "msaveepc2", any, read_msaveepc2, write_msaveepc2 },
+ [CSR_MSAVECAUSE2] = { "msavecause2", any, read_msavecause2, write_msavecause2 },
+ [CSR_MSAVEDCAUSE1] = { "msavedcause1", any, read_msavedcause1, write_msavedcause1 },
+ [CSR_MSAVEDCAUSE2] = { "msavedcause2", any, read_msavedcause2, write_msavedcause2 },
+ [CSR_PUSHMSUBM] = { "pushmsubm", eclic, NULL, NULL, rmw_pushmsubm },
+ [CSR_MTVT2] = { "mtvt2", eclic, read_mtvt2, write_mtvt2 },
+ [CSR_JALMNXTI] = { "jalmnxti", eclic, NULL, NULL, rmw_jalmnxti },
+ [CSR_PUSHMCAUSE] = { "pushmcause", eclic, NULL, NULL , rmw_pushmcause },
+ [CSR_PUSHMEPC] = { "pushmepc", eclic, NULL, NULL, rmw_pushmepc },
[CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush },
+ [CSR_MUCOUNTEREN] = { "mucounteren", any, read_mucounteren, write_mucounteren },
[CSR_MSCOUNTEREN] = { "msounteren", any, read_mscounteren, write_mscounteren },
/* Machine Trap Handling */
diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
index d04ca0394c..e4bf61f3c0 100644
--- a/target/riscv/insn_trans/trans_rvi.c.inc
+++ b/target/riscv/insn_trans/trans_rvi.c.inc
@@ -521,13 +521,27 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a)
tcg_temp_free(rs1_pass); \
} while (0)
+#define RISCV_OP_CSR_JAL_POST do {\
+ tcg_gen_mov_tl(cpu_pc, dest); \
+ exit_tb(ctx); \
+ ctx->base.is_jmp = DISAS_NORETURN; \
+ tcg_temp_free(source1); \
+ tcg_temp_free(csr_store); \
+ tcg_temp_free(dest); \
+ tcg_temp_free(rs1_pass); \
+} while (0)
static bool trans_csrrw(DisasContext *ctx, arg_csrrw *a)
{
TCGv source1, csr_store, dest, rs1_pass;
RISCV_OP_CSR_PRE;
gen_helper_csrrw(dest, cpu_env, source1, csr_store);
- RISCV_OP_CSR_POST;
+
+ if(a->csr == CSR_JALMNXTI)
+ RISCV_OP_CSR_JAL_POST;
+ else
+ RISCV_OP_CSR_POST;
+
return true;
}
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index f0bbd73ca5..57eda9336a 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -171,6 +171,20 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
riscv_cpu_set_virt_enabled(env, prev_virt);
}
+ if ((env->mtvec & 0b111111) == 0b000011) {
+ target_ulong mpil = get_field(env->mcause, MCAUSE_MPIL);
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL, mpil);
+
+ qemu_mutex_lock_iothread();
+ riscv_cpu_eclic_get_next_interrupt(env->eclic);
+ qemu_mutex_unlock_iothread();
+
+ if (get_field(env->mcause, MCAUSE_INTERRUPT) == 1) {
+ env->mstatus = set_field(env->mstatus, MSTATUS_MPP,
+ get_field(env->mcause, MCAUSE_MPP));
+ }
+ }
+
return retpc;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
@ 2021-05-07 8:16 ` wangjunqiang
2021-05-10 2:20 ` Alistair Francis
2021-05-07 8:16 ` [RFC PATCH 3/5] hw/intc: Add Nuclei Systimer wangjunqiang
` (3 subsequent siblings)
5 siblings, 1 reply; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch provides an implementation of Nuclei ECLIC Device.
Nuclei processor core have been equipped with an Enhanced Core Local
Interrupt Controller (ECLIC), which is optimized based on the RISC-V
standard CLIC, to manage all interrupt sources.
https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
---
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 1 +
hw/intc/nuclei_eclic.c | 437 +++++++++++++++++++++++++++++++++
include/hw/intc/nuclei_eclic.h | 115 +++++++++
4 files changed, 556 insertions(+)
create mode 100644 hw/intc/nuclei_eclic.c
create mode 100644 include/hw/intc/nuclei_eclic.h
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index f4694088a4..eab30f6ffd 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -73,3 +73,6 @@ config GOLDFISH_PIC
config M68K_IRQC
bool
+
+config NUCLEI_ECLIC
+ bool
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 1c299039f6..7577ba69d2 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -50,6 +50,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
+specific_ss.add(when: 'CONFIG_NUCLEI_ECLIC', if_true: files('nuclei_eclic.c'))
specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
if_true: files('xics_kvm.c'))
diff --git a/hw/intc/nuclei_eclic.c b/hw/intc/nuclei_eclic.c
new file mode 100644
index 0000000000..52de83cb1d
--- /dev/null
+++ b/hw/intc/nuclei_eclic.c
@@ -0,0 +1,437 @@
+/*
+ * NUCLEI ECLIC(Enhanced Core Local Interrupt Controller)
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/log.h"
+#include "qemu/module.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "hw/pci/msi.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "target/riscv/cpu.h"
+#include "sysemu/sysemu.h"
+#include "hw/intc/nuclei_eclic.h"
+#include "qapi/error.h"
+
+#define RISCV_DEBUG_ECLIC 0
+
+static void riscv_cpu_eclic_interrupt(RISCVCPU *cpu, int exccode)
+{
+ CPURISCVState *env = &cpu->env;
+ bool locked = false;
+
+ env->exccode = exccode;
+
+ if (!qemu_mutex_iothread_locked()) {
+ locked = true;
+ qemu_mutex_lock_iothread();
+ }
+
+ if (exccode != -1) {
+ env->irq_pending = true;
+ cpu_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
+ } else {
+ env->irq_pending = false;
+ cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
+ }
+
+ if (locked) {
+ qemu_mutex_unlock_iothread();
+ }
+}
+
+static int level_compare(NucLeiECLICState *eclic,
+ ECLICPendingInterrupt *irq1,
+ ECLICPendingInterrupt *irq2)
+{
+ if (irq1->level == irq2->level) {
+ if (irq1->prio == irq2->prio) {
+ if (irq1->irq >= irq2->irq) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (irq1->prio > irq2->level) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (irq1->level > irq2->level) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static void nuclei_eclic_next_interrupt(void *eclic_ptr)
+{
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
+ CPURISCVState *env = &cpu->env;
+ NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
+ ECLICPendingInterrupt *active;
+ target_ulong mil;
+ int shv;
+
+ QLIST_FOREACH(active, &eclic->pending_list, next)
+ {
+ if (active->enable) {
+ mil = get_field(env->mintstatus, MINTSTATUS_MIL);
+ if (active->level >= eclic->mth && active->level > mil) {
+ shv = eclic->clicintattr[active->irq] & 0x1;
+ eclic->active_count++;
+ riscv_cpu_eclic_interrupt(cpu,
+ (active->irq & 0xFFF) | (shv << 12) | (active->level << 13));
+ return;
+ }
+ }
+ }
+}
+
+static void nuclei_eclic_update_intmth(NucLeiECLICState *eclic,
+ int irq, int mth)
+{
+ eclic->mth = mth;
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+static void update_eclic_int_info(NucLeiECLICState *eclic, int irq)
+{
+ int level_width = (eclic->cliccfg >> 1) & 0xF;
+ if (level_width > CLICINTCTLBITS) {
+ level_width = CLICINTCTLBITS;
+ }
+ int prio_width = CLICINTCTLBITS - level_width;
+
+ if (level_width == 0) {
+ eclic->clicintlist[irq].level = 255;
+ } else {
+ eclic->clicintlist[irq].level = ((
+ (eclic->clicintctl[irq] >> (8 - level_width)) &
+ ~((char)0x80 >> (8 - level_width)))
+ << (8 - level_width)) |
+ (0xff >> level_width);
+ }
+
+ if (prio_width == 0) {
+ eclic->clicintlist[irq].prio = 0;
+ } else {
+ eclic->clicintlist[irq].prio =
+ (eclic->clicintctl[irq] >> (8 - level_width)) &
+ ~(0x80 >> (8 - prio_width));
+ }
+
+ eclic->clicintlist[irq].enable = eclic->clicintie[irq] & 0x1;
+ eclic->clicintlist[irq].trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
+}
+
+static void eclic_remove_pending_list(NucLeiECLICState *eclic, int irq)
+{
+ QLIST_REMOVE(&eclic->clicintlist[irq], next);
+}
+
+static void eclic_insert_pending_list(NucLeiECLICState *eclic, int irq)
+{
+ ECLICPendingInterrupt *node;
+ if (QLIST_EMPTY(&eclic->pending_list)) {
+ QLIST_INSERT_HEAD(&eclic->pending_list, &eclic->clicintlist[irq], next);
+ } else {
+ QLIST_FOREACH(node, &eclic->pending_list, next)
+ {
+ if (level_compare(eclic, node, &eclic->clicintlist[irq])) {
+ QLIST_INSERT_BEFORE(node, &eclic->clicintlist[irq], next);
+ break;
+ } else if (node->next.le_next == NULL) {
+ QLIST_INSERT_AFTER(node, &eclic->clicintlist[irq], next);
+ break;
+ }
+ }
+ }
+}
+
+static void nuclei_eclic_update_intip(NucLeiECLICState *eclic, int irq, int new_intip)
+{
+
+ int old_intip = eclic->clicintlist[irq].sig;
+ int trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
+ if (((trigger == 0) && new_intip) ||
+ ((trigger == 1) && !old_intip && new_intip) ||
+ ((trigger == 3) && old_intip && !new_intip)) {
+ eclic->clicintip[irq] = 1;
+ eclic->clicintlist[irq].sig = new_intip;
+ eclic_insert_pending_list(eclic, irq);
+ } else {
+ if (eclic->clicintip[irq]) {
+ eclic_remove_pending_list(eclic, irq);
+ }
+ eclic->clicintip[irq] = 0;
+ eclic->clicintlist[irq].sig = new_intip;
+ }
+
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+static void nuclei_eclic_update_intie(NucLeiECLICState *eclic,
+ int irq, int new_intie)
+{
+ eclic->clicintie[irq] = new_intie;
+ update_eclic_int_info(eclic, irq);
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+static void nuclei_eclic_update_intattr(NucLeiECLICState *eclic,
+ int irq, int new_intattr)
+{
+ eclic->clicintattr[irq] = new_intattr;
+ update_eclic_int_info(eclic, irq);
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+static void nuclei_eclic_update_intctl(NucLeiECLICState *eclic,
+ int irq, int new_intctl)
+{
+ eclic->clicintctl[irq] = new_intctl;
+ update_eclic_int_info(eclic, irq);
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
+ return eclic->irqs[irq];
+}
+
+static uint64_t nuclei_eclic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
+ uint64_t value = 0;
+ uint32_t id = 0;
+ if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
+ if ((offset - 0x1000) % 4 == 0) {
+ id = (offset - 0x1000) / 4;
+ } else if ((offset - 0x1001) % 4 == 0) {
+ id = (offset - 0x1001) / 4;
+ } else if ((offset - 0x1002) % 4 == 0) {
+ id = (offset - 0x1002) / 4;
+ } else if ((offset - 0x1003) % 4 == 0) {
+ id = (offset - 0x1003) / 4;
+ }
+ offset = offset - 4 * id;
+ }
+
+ switch (offset) {
+ case NUCLEI_ECLIC_REG_CLICCFG:
+ value = eclic->cliccfg & 0xFF;
+ break;
+ case NUCLEI_ECLIC_REG_CLICINFO:
+ value = (CLICINTCTLBITS << 21) & 0xFFFFFFFF;
+ break;
+ case NUCLEI_ECLIC_REG_MTH:
+ value = eclic->mth & 0xFF;
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
+ value = eclic->clicintip[id] & 0xFF;
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
+ value = eclic->clicintie[id] & 0xFF;
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
+ value = eclic->clicintattr[id] & 0xFF;
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
+ value = eclic->clicintctl[id] & 0xFF;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static void nuclei_eclic_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
+ uint32_t id = 0;
+ if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
+
+ if ((offset - 0x1000) % 4 == 0) {
+ id = (offset - 0x1000) / 4;
+ } else if ((offset - 0x1001) % 4 == 0) {
+ id = (offset - 0x1001) / 4;
+ } else if ((offset - 0x1002) % 4 == 0) {
+ id = (offset - 0x1002) / 4;
+ } else if ((offset - 0x1003) % 4 == 0) {
+ id = (offset - 0x1003) / 4;
+ }
+ offset = offset - 4 * id;
+ }
+ switch (offset) {
+ case NUCLEI_ECLIC_REG_CLICCFG:
+ eclic->cliccfg = value & 0xFF;
+ for (id = 0; id < eclic->num_sources; id++) {
+ update_eclic_int_info(eclic, id);
+ }
+ break;
+ case NUCLEI_ECLIC_REG_MTH:
+ nuclei_eclic_update_intmth(eclic, id, value & 0xFF);
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
+ if ((eclic->clicintlist[id].trigger & 0x1) != 0) {
+ if ((eclic->clicintip[id] == 0) && (value & 0x1) == 1) {
+ eclic->clicintip[id] = 1;
+ eclic_insert_pending_list(eclic, id);
+ } else if ((eclic->clicintip[id] == 1) && (value & 0x1) == 0) {
+ eclic->clicintip[id] = 0;
+ eclic_remove_pending_list(eclic, id);
+ }
+ }
+ nuclei_eclic_next_interrupt(eclic);
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
+ nuclei_eclic_update_intie(eclic, id, value & 0xFF);
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
+ nuclei_eclic_update_intattr(eclic, id, value & 0xFF);
+ break;
+ case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
+ nuclei_eclic_update_intctl(eclic, id, value & 0xFF);
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps nuclei_eclic_ops = {
+ .read = nuclei_eclic_read,
+ .write = nuclei_eclic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void riscv_cpu_eclic_clean_pending(void *eclic_ptr, int irq)
+{
+ NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
+ if ((eclic->clicintlist[irq].trigger & 0x1) != 0 && irq >= 0) {
+ eclic->clicintip[irq] = 0;
+ eclic_remove_pending_list(eclic, irq);
+ }
+}
+
+void riscv_cpu_eclic_get_next_interrupt(void *eclic_ptr)
+{
+ NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
+ nuclei_eclic_next_interrupt(eclic);
+}
+
+
+
+static void nuclei_eclic_irq_request(void *opaque, int id, int new_intip)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
+ nuclei_eclic_update_intip(eclic, id, new_intip);
+}
+
+static void nuclei_eclic_realize(DeviceState *dev, Error **errp)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
+ int id;
+
+ memory_region_init_io(&eclic->mmio, OBJECT(dev), &nuclei_eclic_ops, eclic,
+ TYPE_NUCLEI_ECLIC, eclic->aperture_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &eclic->mmio);
+
+ eclic->clicintip = g_new0(uint8_t, eclic->num_sources);
+ eclic->clicintlist = g_new0(ECLICPendingInterrupt, eclic->num_sources);
+ eclic->clicintie = g_new0(uint8_t, eclic->num_sources);
+ eclic->clicintattr = g_new0(uint8_t, eclic->num_sources);
+ eclic->clicintctl = g_new0(uint8_t, eclic->num_sources);
+ eclic->irqs = g_new0(qemu_irq, eclic->num_sources);
+ QLIST_INIT(&eclic->pending_list);
+ for (id = 0; id < eclic->num_sources; id++) {
+ eclic->clicintlist[id].irq = id;
+ update_eclic_int_info(eclic, id);
+ }
+ eclic->active_count = 0;
+
+ /* Init ECLIC IRQ */
+ eclic->irqs[Internal_SysTimerSW_IRQn] =
+ qemu_allocate_irq(nuclei_eclic_irq_request,
+ eclic, Internal_SysTimerSW_IRQn);
+ eclic->irqs[Internal_SysTimer_IRQn] =
+ qemu_allocate_irq(nuclei_eclic_irq_request,
+ eclic, Internal_SysTimer_IRQn);
+
+ for (id = Internal_Reserved_Max_IRQn; id < eclic->num_sources; id++) {
+ eclic->irqs[id] = qemu_allocate_irq(nuclei_eclic_irq_request,
+ eclic, id);
+ }
+
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
+ cpu->env.eclic = eclic;
+}
+
+static Property nuclei_eclic_properties[] = {
+ DEFINE_PROP_UINT32("aperture-size", NucLeiECLICState, aperture_size, 0),
+ DEFINE_PROP_UINT32("num-sources", NucLeiECLICState, num_sources, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void nuclei_eclic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, nuclei_eclic_properties);
+ dc->realize = nuclei_eclic_realize;
+}
+
+static const TypeInfo nuclei_eclic_info = {
+ .name = TYPE_NUCLEI_ECLIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NucLeiECLICState),
+ .class_init = nuclei_eclic_class_init,
+};
+
+static void nuclei_eclic_register_types(void)
+{
+ type_register_static(&nuclei_eclic_info);
+}
+
+type_init(nuclei_eclic_register_types);
+
+void nuclei_eclic_systimer_cb(DeviceState *dev)
+{
+ NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
+ nuclei_eclic_irq_request(eclic, Internal_SysTimer_IRQn, 1);
+}
+
+DeviceState *nuclei_eclic_create(hwaddr addr,
+ uint32_t aperture_size, uint32_t num_sources)
+{
+ DeviceState *dev = qdev_new(TYPE_NUCLEI_ECLIC);
+
+ qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
+ qdev_prop_set_uint32(dev, "num-sources", num_sources);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/include/hw/intc/nuclei_eclic.h b/include/hw/intc/nuclei_eclic.h
new file mode 100644
index 0000000000..18b25485b9
--- /dev/null
+++ b/include/hw/intc/nuclei_eclic.h
@@ -0,0 +1,115 @@
+/*
+ * NUCLEI ECLIC (Enhanced Core Local Interrupt Controller) interface
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/>.
+ */
+#ifndef HW_NUCLEI_ECLIC_H
+#define HW_NUCLEI_ECLIC_H
+
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_NUCLEI_ECLIC "riscv.nuclei.eclic"
+
+#define INTERRUPT_SOURCE_MIN_ID (18)
+#define INTERRUPT_SOURCE_MAX_ID (4096)
+
+typedef struct NucLeiECLICState NucLeiECLICState;
+DECLARE_INSTANCE_CHECKER(NucLeiECLICState, NUCLEI_ECLIC,
+ TYPE_NUCLEI_ECLIC)
+
+typedef struct ECLICPendingInterrupt {
+ int irq;
+ int prio;
+ int level;
+ int enable;
+ int trigger;
+ int sig;
+ QLIST_ENTRY(ECLICPendingInterrupt) next;
+} ECLICPendingInterrupt;
+
+#define NUCLEI_ECLIC_REG_CLICCFG 0x0000
+#define NUCLEI_ECLIC_REG_CLICINFO 0x0004
+#define NUCLEI_ECLIC_REG_MTH 0x000b
+#define NUCLEI_ECLIC_REG_CLICINTIP_BASE 0x1000
+#define NUCLEI_ECLIC_REG_CLICINTIE_BASE 0x1001
+#define NUCLEI_ECLIC_REG_CLICINTATTR_BASE 0x1002
+#define NUCLEI_ECLIC_REG_CLICINTCTL_BASE 0x1003
+
+#define CLICINTCTLBITS 0x6
+
+typedef struct NucLeiECLICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+
+ uint32_t num_sources; /* 4-1024 */
+
+ /* config */
+ uint32_t sources_id;
+ uint8_t cliccfg; /* nlbits(1~4) */
+ uint32_t clicinfo;
+ uint8_t mth; /* mth(0~7) */
+ uint8_t *clicintip;
+ uint8_t *clicintie;
+ uint8_t *clicintattr; /* shv(0) trig(1~2)*/
+ uint8_t *clicintctl;
+ ECLICPendingInterrupt *clicintlist;
+ uint32_t aperture_size;
+
+ QLIST_HEAD(, ECLICPendingInterrupt)
+ pending_list;
+ size_t active_count;
+
+ /* ECLIC IRQ handlers */
+ qemu_irq *irqs;
+
+} NucLeiECLICState;
+
+enum {
+ Internal_Reserved0_IRQn = 0, /*!< Internal reserved */
+ Internal_Reserved1_IRQn = 1, /*!< Internal reserved */
+ Internal_Reserved2_IRQn = 2, /*!< Internal reserved */
+ Internal_SysTimerSW_IRQn = 3, /*!< System Timer SW interrupt */
+ Internal_Reserved3_IRQn = 4, /*!< Internal reserved */
+ Internal_Reserved4_IRQn = 5, /*!< Internal reserved */
+ Internal_Reserved5_IRQn = 6, /*!< Internal reserved */
+ Internal_SysTimer_IRQn = 7, /*!< System Timer Interrupt */
+ Internal_Reserved6_IRQn = 8, /*!< Internal reserved */
+ Internal_Reserved7_IRQn = 9, /*!< Internal reserved */
+ Internal_Reserved8_IRQn = 10, /*!< Internal reserved */
+ Internal_Reserved9_IRQn = 11, /*!< Internal reserved */
+ Internal_Reserved10_IRQn = 12, /*!< Internal reserved */
+ Internal_Reserved11_IRQn = 13, /*!< Internal reserved */
+ Internal_Reserved12_IRQn = 14, /*!< Internal reserved */
+ Internal_Reserved13_IRQn = 15, /*!< Internal reserved */
+ Internal_Reserved14_IRQn = 16, /*!< Internal reserved */
+ Internal_BusError_IRQn = 17, /*!< Bus Error interrupt */
+ Internal_PerfMon_IRQn = 18, /*!< Performance Monitor */
+ Internal_Reserved_Max_IRQn = 19, /*!< Internal reserved Max */
+};
+
+DeviceState *nuclei_eclic_create(hwaddr addr,
+ uint32_t aperture_size, uint32_t num_sources);
+qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq);
+void nuclei_eclic_systimer_cb(DeviceState *dev);
+
+#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH 3/5] hw/intc: Add Nuclei Systimer
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device wangjunqiang
@ 2021-05-07 8:16 ` wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 4/5] hw/char: Add Nuclei Uart wangjunqiang
` (2 subsequent siblings)
5 siblings, 0 replies; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch provides an implementation of Nuclei Systimer,
which like clint. In MCU mode, It only work for hart-0.
MultiCore support will run on 200t board for Linux.
https://doc.nucleisys.com/nuclei_spec/isa/timer.html
Signed-off-by: Wang Junqiang <wangjunqiang@iscas.ac.cn>
---
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 1 +
hw/intc/nuclei_systimer.c | 254 ++++++++++++++++++++++++++++++
include/hw/intc/nuclei_systimer.h | 70 ++++++++
4 files changed, 328 insertions(+)
create mode 100644 hw/intc/nuclei_systimer.c
create mode 100644 include/hw/intc/nuclei_systimer.h
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index eab30f6ffd..70059d96fa 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -76,3 +76,6 @@ config M68K_IRQC
config NUCLEI_ECLIC
bool
+
+config NUCLEI_SYSTIMER
+ bool
\ No newline at end of file
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 7577ba69d2..d064f769ee 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -51,6 +51,7 @@ specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
specific_ss.add(when: 'CONFIG_NUCLEI_ECLIC', if_true: files('nuclei_eclic.c'))
+specific_ss.add(when: 'CONFIG_NUCLEI_SYSTIMER', if_true: files('nuclei_systimer.c'))
specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
if_true: files('xics_kvm.c'))
diff --git a/hw/intc/nuclei_systimer.c b/hw/intc/nuclei_systimer.c
new file mode 100644
index 0000000000..7d5f97b54c
--- /dev/null
+++ b/hw/intc/nuclei_systimer.c
@@ -0,0 +1,254 @@
+/*
+ * NUCLEI TIMER (Timer Unit) interface
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This provides a parameterizable timer controller based on NucLei's Systimer.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/log.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/timer.h"
+#include "target/riscv/cpu.h"
+#include "hw/intc/nuclei_systimer.h"
+#include "hw/intc/nuclei_eclic.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static uint64_t cpu_riscv_read_rtc(uint64_t timebase_freq)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ timebase_freq, NANOSECONDS_PER_SECOND);
+}
+
+static void nuclei_timer_update_compare(NucLeiSYSTIMERState *s)
+{
+ CPUState *cpu = qemu_get_cpu(0);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ uint64_t cmp, real_time;
+ int64_t diff;
+
+ real_time = s->mtime_lo | ((uint64_t)s->mtime_hi << 32);
+
+ cmp = (uint64_t)s->mtimecmp_lo | ((uint64_t)s->mtimecmp_hi << 32);
+ env->mtimecmp = cmp;
+ env->timecmp = cmp;
+
+ diff = cmp - real_time;
+
+ if (real_time >= cmp) {
+ qemu_set_irq(*(s->timer_irq), 1);
+ } else {
+ qemu_set_irq(*(s->timer_irq), 0);
+
+ if (s->mtimecmp_hi != 0xffffffff) {
+ uint64_t next_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ muldiv64(diff, NANOSECONDS_PER_SECOND, s->timebase_freq);
+ timer_mod(env->mtimer, next_ns);
+ }
+ }
+}
+
+static void nuclei_timer_reset(DeviceState *dev)
+{
+ NucLeiSYSTIMERState *s = NUCLEI_SYSTIMER(dev);
+ s->mtime_lo = 0x0;
+ s->mtime_hi = 0x0;
+ s->mtimecmp_lo = 0xFFFFFFFF;
+ s->mtimecmp_hi = 0xFFFFFFFF;
+ s->mstop = 0x0;
+ s->mstop = 0x0;
+}
+
+static uint64_t nuclei_timer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ NucLeiSYSTIMERState *s = NUCLEI_SYSTIMER(opaque);
+ CPUState *cpu = qemu_get_cpu(0);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ uint64_t value = 0;
+
+ switch (offset) {
+ case NUCLEI_SYSTIMER_REG_MTIMELO:
+ value = cpu_riscv_read_rtc(s->timebase_freq);
+ s->mtime_lo = value & 0xffffffff;
+ s->mtime_hi = (value >> 32) & 0xffffffff;
+ value = s->mtime_lo;
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMEHI:
+ value = s->mtime_hi;
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMECMPLO:
+ s->mtimecmp_lo = (env->mtimecmp) & 0xFFFFFFFF;
+ value = s->mtimecmp_lo;
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMECMPHI:
+ s->mtimecmp_hi = (env->mtimecmp >> 32) & 0xFFFFFFFF;
+ value = s->mtimecmp_hi;
+ break;
+ case NUCLEI_SYSTIMER_REG_MSFTRST:
+ break;
+ case NUCLEI_SYSTIMER_REG_MSTOP:
+ value = s->mstop;
+ break;
+ case NUCLEI_SYSTIMER_REG_MSIP:
+ value = s->msip;
+ break;
+ default:
+ break;
+ }
+ value &= 0xFFFFFFFF;
+ return value;
+
+}
+
+static void nuclei_timer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ NucLeiSYSTIMERState *s = NUCLEI_SYSTIMER(opaque);
+ CPUState *cpu = qemu_get_cpu(0);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+
+ value = value & 0xFFFFFFFF;
+ switch (offset) {
+ case NUCLEI_SYSTIMER_REG_MTIMELO:
+ s->mtime_lo = value;
+ env->mtimer->expire_time |= (value & 0xFFFFFFFF);
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMEHI:
+ s->mtime_hi = value;
+ env->mtimer->expire_time |= ((value << 32) & 0xFFFFFFFF);
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMECMPLO:
+ s->mtimecmp_lo = value;
+ s->mtimecmp_hi = 0xFFFFFFFF;
+ env->mtimecmp |= (value & 0xFFFFFFFF);
+ nuclei_timer_update_compare(s);
+ break;
+ case NUCLEI_SYSTIMER_REG_MTIMECMPHI:
+ s->mtimecmp_hi = value;
+ env->mtimecmp |= ((value << 32) & 0xFFFFFFFF);
+ nuclei_timer_update_compare(s);
+ break;
+ case NUCLEI_SYSTIMER_REG_MSFTRST:
+ if (!(value & 0x80000000) == 0) {
+ nuclei_timer_reset((DeviceState *)s);
+ }
+ break;
+ case NUCLEI_SYSTIMER_REG_MSTOP:
+ s->mstop = value;
+ break;
+ case NUCLEI_SYSTIMER_REG_MSIP:
+ s->msip = value;
+ if ((s->msip & 0x1) == 1) {
+ qemu_set_irq(*(s->soft_irq), 1);
+ } else {
+ qemu_set_irq(*(s->soft_irq), 0);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps nuclei_timer_ops = {
+ .read = nuclei_timer_read,
+ .write = nuclei_timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static Property nuclei_systimer_properties[] = {
+ DEFINE_PROP_UINT32("aperture-size", NucLeiSYSTIMERState, aperture_size, 0),
+ DEFINE_PROP_UINT32("timebase-freq", NucLeiSYSTIMERState, timebase_freq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void nuclei_timer_realize(DeviceState *dev, Error **errp)
+{
+ NucLeiSYSTIMERState *s = NUCLEI_SYSTIMER(dev);
+
+ if (s->aperture_size == 0) {
+ s->aperture_size = 0x1000;
+ }
+ memory_region_init_io(&s->iomem, OBJECT(dev), &nuclei_timer_ops,
+ s, TYPE_NUCLEI_SYSTIMER, s->aperture_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+}
+
+static void nuclei_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = nuclei_timer_realize;
+ dc->reset = nuclei_timer_reset;
+ dc->desc = "NucLei Systimer Timer";
+ device_class_set_props(dc, nuclei_systimer_properties);
+}
+
+static const TypeInfo nuclei_timer_info = {
+ .name = TYPE_NUCLEI_SYSTIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NucLeiSYSTIMERState),
+ .class_init = nuclei_timer_class_init,
+};
+
+static void nuclei_timer_register_types(void)
+{
+ type_register_static(&nuclei_timer_info);
+}
+type_init(nuclei_timer_register_types);
+
+static void nuclei_mtimecmp_cb(void *opaque)
+{
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
+ CPURISCVState *env = &cpu->env;
+ nuclei_eclic_systimer_cb(((RISCVCPU *)cpu)->env.eclic);
+ timer_del(env->mtimer);
+}
+
+DeviceState *nuclei_systimer_create(hwaddr addr, hwaddr size,
+ DeviceState *eclic, uint32_t timebase_freq)
+{
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
+ CPURISCVState *env = &cpu->env;
+
+ env->features |= (1ULL << RISCV_FEATURE_ECLIC);
+ env->mtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &nuclei_mtimecmp_cb, cpu);
+ env->mtimecmp = 0;
+
+ DeviceState *dev = qdev_new(TYPE_NUCLEI_SYSTIMER);
+ qdev_prop_set_uint32(dev, "aperture-size", size);
+ qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ NucLeiSYSTIMERState *s = NUCLEI_SYSTIMER(dev);
+ if (eclic != NULL) {
+ s->eclic = eclic;
+ s->soft_irq = &(NUCLEI_ECLIC(eclic)->irqs[Internal_SysTimerSW_IRQn]);
+ s->timer_irq = &(NUCLEI_ECLIC(eclic)->irqs[Internal_SysTimer_IRQn]);
+ }
+ return dev;
+}
diff --git a/include/hw/intc/nuclei_systimer.h b/include/hw/intc/nuclei_systimer.h
new file mode 100644
index 0000000000..1f7756bb6f
--- /dev/null
+++ b/include/hw/intc/nuclei_systimer.h
@@ -0,0 +1,70 @@
+/*
+ * NUCLEI TIMER (Timer Unit) interface
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This provides a parameterizable timer controller based on NucLei's Systimer.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/>.
+ */
+#ifndef HW_NUCLEI_SYSTIMER_H
+#define HW_NUCLEI_SYSTIMER_H
+
+#include "hw/intc/nuclei_eclic.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_NUCLEI_SYSTIMER "riscv.nuclei.systimer"
+
+#define NUCLEI_SYSTIMER(obj) \
+ OBJECT_CHECK(NucLeiSYSTIMERState, (obj), TYPE_NUCLEI_SYSTIMER)
+
+#define NUCLEI_SYSTIMER_REG_MTIMELO 0x0000
+#define NUCLEI_SYSTIMER_REG_MTIMEHI 0x0004
+#define NUCLEI_SYSTIMER_REG_MTIMECMPLO 0x0008
+#define NUCLEI_SYSTIMER_REG_MTIMECMPHI 0x000C
+#define NUCLEI_SYSTIMER_REG_MSFTRST 0xFF0
+#define NUCLEI_SYSTIMER_REG_MSTOP 0xFF8
+#define NUCLEI_SYSTIMER_REG_MSIP 0xFFC
+
+typedef struct NucLeiSYSTIMERState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+ qemu_irq *timer_irq;
+ qemu_irq *soft_irq;
+
+ DeviceState *eclic;
+
+ uint32_t mtime_lo;
+ uint32_t mtime_hi;
+ uint32_t mtimecmp_lo;
+ uint32_t mtimecmp_hi;
+ uint32_t mstop;
+ uint32_t msip;
+
+ uint32_t aperture_size;
+ uint32_t timebase_freq;
+
+} NucLeiSYSTIMERState;
+
+#define NUCLEI_GD32_TIMEBASE_FREQ (108000000 * 2)
+#define NUCLEI_HBIRD_TIMEBASE_FREQ (10000000)
+
+DeviceState *nuclei_systimer_create(hwaddr addr, hwaddr size,
+ DeviceState *eclic, uint32_t timebase_freq);
+#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH 4/5] hw/char: Add Nuclei Uart
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
` (2 preceding siblings ...)
2021-05-07 8:16 ` [RFC PATCH 3/5] hw/intc: Add Nuclei Systimer wangjunqiang
@ 2021-05-07 8:16 ` wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 5/5] Nuclei FPGA Evaluation Kit MCU Machine wangjunqiang
2021-05-07 13:33 ` [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit no-reply
5 siblings, 0 replies; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch provides the initial implementation of Nuclei Uart
which is opensource in Nuclei's Hummingbird Project.
Signed-off-by: Wang Junqiang <wangjunqiang@iscas.ac.cn>
---
hw/char/Kconfig | 3 +
hw/char/meson.build | 1 +
hw/char/nuclei_uart.c | 208 ++++++++++++++++++++++++++++++++++
include/hw/char/nuclei_uart.h | 73 ++++++++++++
4 files changed, 285 insertions(+)
create mode 100644 hw/char/nuclei_uart.c
create mode 100644 include/hw/char/nuclei_uart.h
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 4cf36ac637..de003d0609 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -67,3 +67,6 @@ config SIFIVE_UART
config GOLDFISH_TTY
bool
+
+config NUCLEI_UART
+ bool
diff --git a/hw/char/meson.build b/hw/char/meson.build
index da5bb8b762..fd0a0a34f4 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -34,6 +34,7 @@ softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c'))
softmmu_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c'))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c'))
softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c'))
+softmmu_ss.add(when: 'CONFIG_NUCLEI_UART', if_true: files('nuclei_uart.c'))
specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c'))
specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c'))
diff --git a/hw/char/nuclei_uart.c b/hw/char/nuclei_uart.c
new file mode 100644
index 0000000000..0b6bfa33a7
--- /dev/null
+++ b/hw/char/nuclei_uart.c
@@ -0,0 +1,208 @@
+/*
+ * NUCLEI Hummingbird Evaluation Kit 100T/200T UART interface
+ *
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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 "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/char/nuclei_uart.h"
+
+/*
+ * Not yet implemented:
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
+ */
+static uint64_t uart_ip(NucLeiUARTState *s)
+{
+ uint64_t ret = 0;
+
+ uint64_t txcnt = NUCLEI_UART_GET_TXCNT(s->txctrl);
+ uint64_t rxcnt = NUCLEI_UART_GET_RXCNT(s->rxctrl);
+
+ if (txcnt != 0) {
+ ret |= NUCLEI_UART_IP_TXWM;
+ }
+ if (s->rx_fifo_len > rxcnt) {
+ ret |= NUCLEI_UART_IP_RXWM;
+ }
+
+ return ret;
+}
+
+static void update_irq(NucLeiUARTState *s)
+{
+ int cond = 0;
+ s->txctrl |= 0x1;
+ if (s->rx_fifo_len) {
+ s->rxctrl &= ~0x1;
+ } else {
+ s->rxctrl |= 0x1;
+ }
+
+ if ((s->ie & NUCLEI_UART_IE_TXWM) ||
+ ((s->ie & NUCLEI_UART_IE_RXWM) && s->rx_fifo_len)) {
+ cond = 1;
+ }
+
+ if (cond) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr offset, unsigned int size)
+{
+ NucLeiUARTState *s = opaque;
+ uint64_t value = 0;
+ uint8_t fifo_val;
+
+ switch (offset) {
+ case NUCLEI_UART_REG_TXDATA:
+ return 0;
+ case NUCLEI_UART_REG_RXDATA:
+ if (s->rx_fifo_len) {
+ fifo_val = s->rx_fifo[0];
+ memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+ s->rx_fifo_len--;
+ qemu_chr_fe_accept_input(&s->chr);
+ update_irq(s);
+ return fifo_val;
+ }
+ return 0x80000000;
+ case NUCLEI_UART_REG_TXCTRL:
+ value = s->txctrl;
+ break;
+ case NUCLEI_UART_REG_RXCTRL:
+ value = s->rxctrl;
+ break;
+ case NUCLEI_UART_REG_IE:
+ value = s->ie;
+ break;
+ case NUCLEI_UART_REG_IP:
+ value = uart_ip(s);
+ break;
+ case NUCLEI_UART_REG_DIV:
+ value = s->div;
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+static void
+uart_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned int size)
+{
+ NucLeiUARTState *s = opaque;
+ unsigned char ch = value;
+
+ switch (offset) {
+ case NUCLEI_UART_REG_TXDATA:
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ update_irq(s);
+ break;
+ case NUCLEI_UART_REG_TXCTRL:
+ s->txctrl = value;
+ break;
+ case NUCLEI_UART_REG_RXCTRL:
+ s->rxctrl = value;
+ break;
+ case NUCLEI_UART_REG_IE:
+ s->ie = value;
+ update_irq(s);
+ break;
+ case NUCLEI_UART_REG_IP:
+ s->ip = value;
+ break;
+ case NUCLEI_UART_REG_DIV:
+ s->div = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ NucLeiUARTState *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+ update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ NucLeiUARTState *s = opaque;
+ return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, QEMUChrEvent event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+ NucLeiUARTState *s = opaque;
+
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+
+ return 0;
+}
+
+/*
+ * Create UART device.
+ */
+NucLeiUARTState *nuclei_uart_create(MemoryRegion *address_space,
+ hwaddr base, uint64_t size, Chardev *chr, qemu_irq irq)
+{
+ NucLeiUARTState *s = g_malloc0(sizeof(NucLeiUARTState));
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_NUCLEI_UART, size);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+
+ return s;
+}
diff --git a/include/hw/char/nuclei_uart.h b/include/hw/char/nuclei_uart.h
new file mode 100644
index 0000000000..a7f2c72fb7
--- /dev/null
+++ b/include/hw/char/nuclei_uart.h
@@ -0,0 +1,73 @@
+/*
+ * NUCLEI Hummingbird Evaluation Kit 100T/200T UART interface
+ *
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/>.
+ */
+
+#ifndef HW_NUCLEI_UART_H
+#define HW_NUCLEI_UART_H
+
+#include "chardev/char-fe.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_NUCLEI_UART "riscv.nuclei.uart"
+OBJECT_DECLARE_SIMPLE_TYPE(NucLeiUARTState, NUCLEI_UART)
+
+#define NUCLEI_UART_REG_TXDATA 0x000
+#define NUCLEI_UART_REG_RXDATA 0x004
+#define NUCLEI_UART_REG_TXCTRL 0x008
+#define NUCLEI_UART_REG_RXCTRL 0x00C
+#define NUCLEI_UART_REG_IE 0x010
+#define NUCLEI_UART_REG_IP 0x014
+#define NUCLEI_UART_REG_DIV 0x018
+
+#define NUCLEI_UART_GET_TXCNT(txctrl) (txctrl & 0x1)
+#define NUCLEI_UART_GET_RXCNT(rxctrl) (rxctrl & 0x1)
+
+enum {
+ NUCLEI_UART_IE_TXWM = 1, /* Transmit watermark interrupt enable */
+ NUCLEI_UART_IE_RXWM = 2 /* Receive watermark interrupt enable */
+};
+
+enum {
+ NUCLEI_UART_IP_TXWM = 1, /* Transmit watermark interrupt pending */
+ NUCLEI_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */
+};
+
+typedef struct NucLeiUARTState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ qemu_irq irq;
+ MemoryRegion mmio;
+ CharBackend chr;
+ uint8_t rx_fifo[8];
+ unsigned int rx_fifo_len;
+
+ uint32_t txdata;
+ uint32_t rxdata;
+ uint32_t txctrl;
+ uint32_t rxctrl;
+ uint32_t ie;
+ uint32_t ip;
+ uint32_t div;
+} NucLeiUARTState;
+
+NucLeiUARTState *nuclei_uart_create(MemoryRegion *address_space,
+ hwaddr base, uint64_t size, Chardev *chr, qemu_irq irq);
+#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH 5/5] Nuclei FPGA Evaluation Kit MCU Machine
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
` (3 preceding siblings ...)
2021-05-07 8:16 ` [RFC PATCH 4/5] hw/char: Add Nuclei Uart wangjunqiang
@ 2021-05-07 8:16 ` wangjunqiang
2021-05-07 13:33 ` [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit no-reply
5 siblings, 0 replies; 16+ messages in thread
From: wangjunqiang @ 2021-05-07 8:16 UTC (permalink / raw)
To: qemu-riscv, qemu-devel
Cc: liweiwei, wangjunqiang, bin.meng, Alistair.Francis, alapha23, palmer
This patch provides an implementation of Nuclei FPGA Evaluation Kit Board
as MCU mode. The Machine based on Nuclei's specification which has Customized
register. The following machine is implemented:
-"Nuclei 100T": SYSTIMER, ECLIC, UART ...
Signed-off-by: Wang Junqiang <wangjunqiang@iscas.ac.cn>
---
default-configs/devices/riscv32-softmmu.mak | 1 +
default-configs/devices/riscv64-softmmu.mak | 1 +
hw/riscv/Kconfig | 9 +
hw/riscv/meson.build | 1 +
hw/riscv/nuclei_n.c | 276 ++++++++++++++++++++
include/hw/riscv/nuclei_n.h | 136 ++++++++++
6 files changed, 424 insertions(+)
create mode 100644 hw/riscv/nuclei_n.c
create mode 100644 include/hw/riscv/nuclei_n.h
diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
index d847bd5692..52fb26ef01 100644
--- a/default-configs/devices/riscv32-softmmu.mak
+++ b/default-configs/devices/riscv32-softmmu.mak
@@ -13,3 +13,4 @@ CONFIG_SIFIVE_E=y
CONFIG_SIFIVE_U=y
CONFIG_RISCV_VIRT=y
CONFIG_OPENTITAN=y
+CONFIG_NUCLEI_N=y
\ No newline at end of file
diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
index d5eec75f05..ff688bbbc6 100644
--- a/default-configs/devices/riscv64-softmmu.mak
+++ b/default-configs/devices/riscv64-softmmu.mak
@@ -13,3 +13,4 @@ CONFIG_SIFIVE_E=y
CONFIG_SIFIVE_U=y
CONFIG_RISCV_VIRT=y
CONFIG_MICROCHIP_PFSOC=y
+CONFIG_NUCLEI_N=y
\ No newline at end of file
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 1de18cdcf1..427ed3afd3 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -67,3 +67,12 @@ config SPIKE
select MSI_NONBROKEN
select SIFIVE_CLINT
select SIFIVE_PLIC
+
+config NUCLEI_N
+ bool
+ select MSI_NONBROKEN
+ select NUCLEI_SYSTIMER
+ select NUCLEI_ECLIC
+ select SIFIVE_GPIO
+ select NUCLEI_UART
+ select UNIMP
\ No newline at end of file
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 275c0f7eb7..840c2852e2 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -8,5 +8,6 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
+riscv_ss.add(when: 'CONFIG_NUCLEI_N', if_true: files('nuclei_n.c'))
hw_arch += {'riscv': riscv_ss}
diff --git a/hw/riscv/nuclei_n.c b/hw/riscv/nuclei_n.c
new file mode 100644
index 0000000000..a95b8a7d29
--- /dev/null
+++ b/hw/riscv/nuclei_n.c
@@ -0,0 +1,276 @@
+/*
+ * Nuclei N series SOC machine interface
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.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/char/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/intc/nuclei_eclic.h"
+#include "hw/char/nuclei_uart.h"
+#include "hw/riscv/nuclei_n.h"
+#include "hw/riscv/boot.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/qtest.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+
+#include <libfdt.h>
+
+static const struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+} nuclei_memmap[] = {
+ [HBIRD_DEBUG] = {0x0, 0x1000},
+ [HBIRD_ROM] = {0x1000, 0x1000},
+ [HBIRD_TIMER] = {0x2000000, 0x1000},
+ [HBIRD_ECLIC] = {0xc000000, 0x10000},
+ [HBIRD_GPIO] = {0x10012000, 0x1000},
+ [HBIRD_UART0] = {0x10013000, 0x1000},
+ [HBIRD_QSPI0] = {0x10014000, 0x1000},
+ [HBIRD_PWM0] = {0x10015000, 0x1000},
+ [HBIRD_UART1] = {0x10023000, 0x1000},
+ [HBIRD_QSPI1] = {0x10024000, 0x1000},
+ [HBIRD_PWM1] = {0x10025000, 0x1000},
+ [HBIRD_QSPI2] = {0x10034000, 0x1000},
+ [HBIRD_PWM2] = {0x10035000, 0x1000},
+ [HBIRD_XIP] = {0x20000000, 0x10000000},
+ [HBIRD_DRAM] = {0xa0000000, 0x0},
+ [HBIRD_ILM] = {0x80000000, 0x20000},
+ [HBIRD_DLM] = {0x90000000, 0x20000},
+};
+
+static void nuclei_machine_get_uint32_prop(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ visit_type_uint32(v, name, (uint32_t *)opaque, errp);
+}
+
+static void nuclei_machine_set_uint32_prop(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ visit_type_uint32(v, name, (uint32_t *)opaque, errp);
+}
+
+static void nuclei_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = nuclei_memmap;
+ NucleiHBState *s = HBIRD_FPGA_MACHINE(machine);
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *flash = g_new(MemoryRegion, 1);
+ target_ulong start_addr = memmap[HBIRD_ILM].base;
+ int i;
+
+ /* TODO: Add qtest support */
+ /* Initialize SOC */
+ object_initialize_child(OBJECT(machine), "soc",
+ &s->soc, TYPE_NUCLEI_HBIRD_SOC);
+ qdev_realize(DEVICE(&s->soc), NULL, &error_abort);
+
+ memory_region_init_ram(&s->soc.ilm, NULL, "riscv.nuclei.ram.ilm",
+ memmap[HBIRD_ILM].size, &error_fatal);
+ memory_region_add_subregion(system_memory,
+ memmap[HBIRD_ILM].base, &s->soc.ilm);
+
+ memory_region_init_ram(&s->soc.dlm, NULL, "riscv.nuclei.ram.dlm",
+ memmap[HBIRD_DLM].size, &error_fatal);
+ memory_region_add_subregion(system_memory,
+ memmap[HBIRD_DLM].base, &s->soc.dlm);
+
+ /* register DRAM */
+ memory_region_init_ram(main_mem, NULL, "riscv.nuclei.dram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[HBIRD_DRAM].base,
+ main_mem);
+
+ /* Flash memory */
+ memory_region_init_ram(flash, NULL, "riscv.nuclei.xip",
+ memmap[HBIRD_XIP].size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[HBIRD_XIP].base,
+ flash);
+
+ switch (s->msel) {
+ case MSEL_ILM:
+ start_addr = memmap[HBIRD_ILM].base;
+ break;
+ case MSEL_FLASH:
+ start_addr = memmap[HBIRD_XIP].base;
+ break;
+ case MSEL_FLASHXIP:
+ start_addr = memmap[HBIRD_XIP].base;
+ break;
+ case MSEL_DDR:
+ start_addr = memmap[HBIRD_DRAM].base;
+ break;
+ default:
+ start_addr = memmap[HBIRD_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) */
+#endif
+ 0x00028067, /* jr t0 */
+ 0x00000000,
+ start_addr, /* start: .dword DRAM_BASE */
+ 0x00000000,
+ };
+
+ 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[HBIRD_ROM].base, &address_space_memory);
+
+ /* boot rom */
+ if (machine->kernel_filename) {
+ riscv_load_kernel(machine->kernel_filename, start_addr, NULL);
+ }
+}
+
+static void nuclei_soc_init(Object *obj)
+{
+ NucleiHBSoCState *s = RISCV_NUCLEI_HBIRD_SOC(obj);
+
+ object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
+
+ object_initialize_child(obj, "riscv.nuclei.gpio",
+ &s->gpio, TYPE_SIFIVE_GPIO);
+}
+
+static void nuclei_soc_realize(DeviceState *dev, Error **errp)
+{
+ const struct MemmapEntry *memmap = nuclei_memmap;
+ MachineState *ms = MACHINE(qdev_get_machine());
+ NucleiHBSoCState *s = RISCV_NUCLEI_HBIRD_SOC(dev);
+ MemoryRegion *sys_mem = get_system_memory();
+ Error *err = NULL;
+
+ object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type,
+ &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort);
+
+ /* Mask ROM */
+ memory_region_init_rom(&s->internal_rom, OBJECT(dev), "riscv.nuclei.irom",
+ memmap[HBIRD_ROM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem,
+ memmap[HBIRD_ROM].base, &s->internal_rom);
+
+ s->eclic = nuclei_eclic_create(memmap[HBIRD_ECLIC].base,
+ memmap[HBIRD_ECLIC].size, HBIRD_SOC_INT_MAX);
+
+ s->timer = nuclei_systimer_create(memmap[HBIRD_TIMER].base,
+ memmap[HBIRD_TIMER].size,
+ s->eclic,
+ NUCLEI_HBIRD_TIMEBASE_FREQ);
+
+ /* GPIO */
+ sysbus_realize(SYS_BUS_DEVICE(&s->gpio), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[HBIRD_GPIO].base);
+
+ nuclei_uart_create(sys_mem,
+ memmap[HBIRD_UART0].base,
+ memmap[HBIRD_UART0].size,
+ serial_hd(0),
+ nuclei_eclic_get_irq(DEVICE(s->eclic),
+ HBIRD_SOC_INT22_IRQn));
+}
+
+static void nuclei_machine_instance_init(Object *obj)
+{
+ NucleiHBState *s = HBIRD_FPGA_MACHINE(obj);
+
+ s->msel = 0;
+ object_property_add(obj, "msel", "uint32",
+ nuclei_machine_get_uint32_prop,
+ nuclei_machine_set_uint32_prop, NULL, &s->msel);
+ object_property_set_description(obj, "msel",
+ "Mode Select Startup");
+}
+
+static void nuclei_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Nuclei HummingBird Evaluation Kit";
+ mc->init = nuclei_board_init;
+ mc->max_cpus = 1;
+ mc->default_cpu_type = NUCLEI_N_CPU;
+}
+
+static const TypeInfo nuclei_machine_typeinfo = {
+ .name = MACHINE_TYPE_NAME("hbird_fpga"),
+ .parent = TYPE_MACHINE,
+ .class_init = nuclei_machine_class_init,
+ .instance_init = nuclei_machine_instance_init,
+ .instance_size = sizeof(NucleiHBState),
+};
+
+static void nuclei_machine_init_register_types(void)
+{
+ type_register_static(&nuclei_machine_typeinfo);
+}
+
+type_init(nuclei_machine_init_register_types)
+
+ static void nuclei_soc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ dc->realize = nuclei_soc_realize;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo nuclei_soc_type_info = {
+ .name = TYPE_NUCLEI_HBIRD_SOC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(NucleiHBSoCState),
+ .instance_init = nuclei_soc_init,
+ .class_init = nuclei_soc_class_init,
+};
+
+static void nuclei_soc_register_types(void)
+{
+ type_register_static(&nuclei_soc_type_info);
+}
+
+type_init(nuclei_soc_register_types)
diff --git a/include/hw/riscv/nuclei_n.h b/include/hw/riscv/nuclei_n.h
new file mode 100644
index 0000000000..83776c5c22
--- /dev/null
+++ b/include/hw/riscv/nuclei_n.h
@@ -0,0 +1,136 @@
+/*
+ * Nuclei U series SOC machine interface
+ *
+ * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
+ * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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/>.
+ */
+#ifndef HW_RISCV_NUCLEI_HBIRD_H
+#define HW_RISCV_NUCLEI_HBIRD_H
+
+#include "hw/char/nuclei_uart.h"
+#include "hw/gpio/sifive_gpio.h"
+#include "hw/intc/nuclei_eclic.h"
+#include "hw/intc/nuclei_systimer.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/sysbus.h"
+
+#define TYPE_NUCLEI_HBIRD_SOC "riscv.nuclei.hbird.soc"
+#define RISCV_NUCLEI_HBIRD_SOC(obj) \
+ OBJECT_CHECK(NucleiHBSoCState, (obj), TYPE_NUCLEI_HBIRD_SOC)
+
+typedef struct NucleiHBSoCState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ RISCVHartArrayState cpus;
+
+ DeviceState *eclic;
+ MemoryRegion ilm;
+ MemoryRegion dlm;
+ MemoryRegion internal_rom;
+ MemoryRegion xip_mem;
+
+ DeviceState *timer;
+ NucLeiUARTState uart;
+ SIFIVEGPIOState gpio;
+
+} NucleiHBSoCState;
+
+#define TYPE_HBIRD_FPGA_MACHINE MACHINE_TYPE_NAME("hbird_fpga")
+#define HBIRD_FPGA_MACHINE(obj) \
+ OBJECT_CHECK(NucleiHBState, (obj), TYPE_HBIRD_FPGA_MACHINE)
+
+typedef struct NucleiHBState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ NucleiHBSoCState soc;
+
+ uint32_t msel;
+} NucleiHBState;
+
+enum {
+ MSEL_ILM = 1,
+ MSEL_FLASH = 2,
+ MSEL_FLASHXIP = 3,
+ MSEL_DDR = 4
+};
+
+enum {
+ HBIRD_DEBUG,
+ HBIRD_ROM,
+ HBIRD_TIMER,
+ HBIRD_ECLIC,
+ HBIRD_GPIO,
+ HBIRD_UART0,
+ HBIRD_QSPI0,
+ HBIRD_PWM0,
+ HBIRD_UART1,
+ HBIRD_QSPI1,
+ HBIRD_PWM1,
+ HBIRD_QSPI2,
+ HBIRD_PWM2,
+ HBIRD_XIP,
+ HBIRD_DRAM,
+ HBIRD_ILM,
+ HBIRD_DLM
+};
+
+enum {
+ HBIRD_SOC_INT19_IRQn = 19, /*!< Device Interrupt */
+ HBIRD_SOC_INT20_IRQn = 20, /*!< Device Interrupt */
+ HBIRD_SOC_INT21_IRQn = 21, /*!< Device Interrupt */
+ HBIRD_SOC_INT22_IRQn = 22, /*!< Device Interrupt */
+ HBIRD_SOC_INT23_IRQn = 23, /*!< Device Interrupt */
+ HBIRD_SOC_INT24_IRQn = 24, /*!< Device Interrupt */
+ HBIRD_SOC_INT25_IRQn = 25, /*!< Device Interrupt */
+ HBIRD_SOC_INT26_IRQn = 26, /*!< Device Interrupt */
+ HBIRD_SOC_INT27_IRQn = 27, /*!< Device Interrupt */
+ HBIRD_SOC_INT28_IRQn = 28, /*!< Device Interrupt */
+ HBIRD_SOC_INT29_IRQn = 29, /*!< Device Interrupt */
+ HBIRD_SOC_INT30_IRQn = 30, /*!< Device Interrupt */
+ HBIRD_SOC_INT31_IRQn = 31, /*!< Device Interrupt */
+ HBIRD_SOC_INT32_IRQn = 32, /*!< Device Interrupt */
+ HBIRD_SOC_INT33_IRQn = 33, /*!< Device Interrupt */
+ HBIRD_SOC_INT34_IRQn = 34, /*!< Device Interrupt */
+ HBIRD_SOC_INT35_IRQn = 35, /*!< Device Interrupt */
+ HBIRD_SOC_INT36_IRQn = 36, /*!< Device Interrupt */
+ HBIRD_SOC_INT37_IRQn = 37, /*!< Device Interrupt */
+ HBIRD_SOC_INT38_IRQn = 38, /*!< Device Interrupt */
+ HBIRD_SOC_INT39_IRQn = 39, /*!< Device Interrupt */
+ HBIRD_SOC_INT40_IRQn = 40, /*!< Device Interrupt */
+ HBIRD_SOC_INT41_IRQn = 41, /*!< Device Interrupt */
+ HBIRD_SOC_INT42_IRQn = 42, /*!< Device Interrupt */
+ HBIRD_SOC_INT43_IRQn = 43, /*!< Device Interrupt */
+ HBIRD_SOC_INT44_IRQn = 44, /*!< Device Interrupt */
+ HBIRD_SOC_INT45_IRQn = 45, /*!< Device Interrupt */
+ HBIRD_SOC_INT46_IRQn = 46, /*!< Device Interrupt */
+ HBIRD_SOC_INT47_IRQn = 47, /*!< Device Interrupt */
+ HBIRD_SOC_INT48_IRQn = 48, /*!< Device Interrupt */
+ HBIRD_SOC_INT49_IRQn = 49, /*!< Device Interrupt */
+ HBIRD_SOC_INT50_IRQn = 50, /*!< Device Interrupt */
+ HBIRD_SOC_INT_MAX,
+};
+
+#if defined(TARGET_RISCV32)
+#define NUCLEI_N_CPU TYPE_RISCV_CPU_NUCLEI_N307FD
+#elif defined(TARGET_RISCV64)
+#define NUCLEI_N_CPU TYPE_RISCV_CPU_NUCLEI_NX600FD
+#endif
+
+#endif
--
2.17.1
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
` (4 preceding siblings ...)
2021-05-07 8:16 ` [RFC PATCH 5/5] Nuclei FPGA Evaluation Kit MCU Machine wangjunqiang
@ 2021-05-07 13:33 ` no-reply
5 siblings, 0 replies; 16+ messages in thread
From: no-reply @ 2021-05-07 13:33 UTC (permalink / raw)
To: wangjunqiang
Cc: liweiwei, qemu-riscv, wangjunqiang, bin.meng, qemu-devel,
Alistair.Francis, alapha23, palmer
Patchew URL: https://patchew.org/QEMU/20210507081654.11056-1-wangjunqiang@iscas.ac.cn/
Hi,
This series seems to have some coding style problems. See output below for
more information:
Type: series
Message-id: 20210507081654.11056-1-wangjunqiang@iscas.ac.cn
Subject: [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit
=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===
Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
* [new tag] patchew/20210507081654.11056-1-wangjunqiang@iscas.ac.cn -> patchew/20210507081654.11056-1-wangjunqiang@iscas.ac.cn
Switched to a new branch 'test'
cdf3a5a Nuclei FPGA Evaluation Kit MCU Machine
fc461db hw/char: Add Nuclei Uart
749f98b hw/intc: Add Nuclei Systimer
4f82151 hw/intc: Add Nuclei ECLIC device
f0b7d59 target/riscv: Add Nuclei CSR and Update interrupt handling
=== OUTPUT BEGIN ===
1/5 Checking commit f0b7d59bd81c (target/riscv: Add Nuclei CSR and Update interrupt handling)
WARNING: line over 80 characters
#519: FILE: target/riscv/csr.c:720:
+static int rmw_mscratchcsw(CPURISCVState *env, int csrno, target_ulong *ret_value,
ERROR: space required after that close brace '}'
#527: FILE: target/riscv/csr.c:728:
+ }else {
WARNING: line over 80 characters
#783: FILE: target/riscv/csr.c:1742:
+ [CSR_MINTSTATUS] = {"mintstatus", eclic, read_mintstatus, write_mintstatus },
ERROR: line over 90 characters
#784: FILE: target/riscv/csr.c:1743:
+ [CSR_MSCRATCHCSW] = {"mscratchcsw", any, read_mscratchcsw, write_mscratchcsw, rmw_mscratchcsw},
ERROR: line over 90 characters
#785: FILE: target/riscv/csr.c:1744:
+ [CSR_MSCRATCHCSWL] = { "mscratchcswl", any, read_mscratchcswl, write_mscratchcswl, rmw_mscratchcswl},
WARNING: line over 80 characters
#789: FILE: target/riscv/csr.c:1748:
+ [CSR_MMISC_CTL] = { "mmisc_ctl", any, read_mmisc_ctl, write_mmisc_ctl },
WARNING: line over 80 characters
#790: FILE: target/riscv/csr.c:1749:
+ [CSR_MSAVESTATUS] = { "msavestatus", any, read_msavestatus, write_msavestatus },
WARNING: line over 80 characters
#791: FILE: target/riscv/csr.c:1750:
+ [CSR_MSAVEEPC1] = { "msaveepc1", any, read_msaveepc1, write_msaveepc1 },
WARNING: line over 80 characters
#792: FILE: target/riscv/csr.c:1751:
+ [CSR_MSAVECAUSE1] = { "msavecause1", any, read_msavecause1, write_msavecause1 },
WARNING: line over 80 characters
#793: FILE: target/riscv/csr.c:1752:
+ [CSR_MSAVEEPC2] = { "msaveepc2", any, read_msaveepc2, write_msaveepc2 },
WARNING: line over 80 characters
#794: FILE: target/riscv/csr.c:1753:
+ [CSR_MSAVECAUSE2] = { "msavecause2", any, read_msavecause2, write_msavecause2 },
WARNING: line over 80 characters
#795: FILE: target/riscv/csr.c:1754:
+ [CSR_MSAVEDCAUSE1] = { "msavedcause1", any, read_msavedcause1, write_msavedcause1 },
WARNING: line over 80 characters
#796: FILE: target/riscv/csr.c:1755:
+ [CSR_MSAVEDCAUSE2] = { "msavedcause2", any, read_msavedcause2, write_msavedcause2 },
WARNING: line over 80 characters
#805: FILE: target/riscv/csr.c:1764:
+ [CSR_MUCOUNTEREN] = { "mucounteren", any, read_mucounteren, write_mucounteren },
ERROR: Missing Signed-off-by: line(s)
total: 4 errors, 11 warnings, 784 lines checked
Patch 1/5 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
2/5 Checking commit 4f82151c82e4 (hw/intc: Add Nuclei ECLIC device)
ERROR: line over 90 characters
#152: FILE: hw/intc/nuclei_eclic.c:103:
+ (active->irq & 0xFFF) | (shv << 12) | (active->level << 13));
ERROR: line over 90 characters
#178: FILE: hw/intc/nuclei_eclic.c:129:
+ (eclic->clicintctl[irq] >> (8 - level_width)) &
WARNING: line over 80 characters
#220: FILE: hw/intc/nuclei_eclic.c:171:
+static void nuclei_eclic_update_intip(NucLeiECLICState *eclic, int irq, int new_intip)
ERROR: Missing Signed-off-by: line(s)
total: 3 errors, 1 warnings, 565 lines checked
Patch 2/5 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
3/5 Checking commit 749f98b9b790 (hw/intc: Add Nuclei Systimer)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#47:
new file mode 100644
total: 0 errors, 1 warnings, 337 lines checked
Patch 3/5 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
4/5 Checking commit fc461db1fe2c (hw/char: Add Nuclei Uart)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#43:
new file mode 100644
total: 0 errors, 1 warnings, 294 lines checked
Patch 4/5 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
5/5 Checking commit cdf3a5a645a4 (Nuclei FPGA Evaluation Kit MCU Machine)
Use of uninitialized value $acpi_testexpected in string eq at ./scripts/checkpatch.pl line 1529.
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#74:
new file mode 100644
total: 0 errors, 1 warnings, 438 lines checked
Patch 5/5 has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
The full log is available at
http://patchew.org/logs/20210507081654.11056-1-wangjunqiang@iscas.ac.cn/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-07 8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
@ 2021-05-10 2:17 ` Alistair Francis
2021-05-11 3:14 ` Wang Junqiang
0 siblings, 1 reply; 16+ messages in thread
From: Alistair Francis @ 2021-05-10 2:17 UTC (permalink / raw)
To: wangjunqiang
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
C isOn Fri, May 7, 2021 at 11:25 PM wangjunqiang
<wangjunqiang@iscas.ac.cn> wrote:
>
> This patch adds Nuclei CSR support for ECLIC and update the
> related interrupt handling.
>
> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
Hello,
Thanks for the patches!
This patch is very long and you will need to split it up before it can
be merged. I understand this is just an RFC, but it's still best to
start with small patches. Generally each patch should add a feature
and it seems like you have added lots of features in this patch. This
patch could probably be broken into at least 4 different patches.
As well as that you will want to ensure that your commit message and
description explains what you are doing in that patch and in some
cases justify the change. For example adding a new CPU doesn't need a
justification (as that's easy for me to understand), but changing some
existing code might need an explanation of why we need/want that
change.
This is still a great start though! I look forward to your future patches.
I have left a few comments below as well.
> ---
> target/riscv/cpu.c | 25 +-
> target/riscv/cpu.h | 42 ++-
> target/riscv/cpu_bits.h | 37 +++
> target/riscv/cpu_helper.c | 80 +++++-
> target/riscv/csr.c | 347 +++++++++++++++++++++++-
> target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
> target/riscv/op_helper.c | 14 +
> 7 files changed, 552 insertions(+), 9 deletions(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 7d6ed80f6b..b2a96effbc 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
> set_priv_version(env, PRIV_VERSION_1_10_0);
> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> }
> +
> +static void rv64imafdcu_nuclei_cpu_init(Object *obj)
> +{
> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> + set_priv_version(env, PRIV_VERSION_1_10_0);
> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> + set_resetvec(env, DEFAULT_RSTVEC);
> + set_feature(env, RISCV_FEATURE_PMP);
> +}
> #else
> static void rv32_base_cpu_init(Object *obj)
> {
> @@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
> set_resetvec(env, DEFAULT_RSTVEC);
> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> }
> +
> +static void rv32imafdcu_nuclei_cpu_init(Object *obj)
> +{
> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> + set_priv_version(env, PRIV_VERSION_1_10_0);
> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> + set_resetvec(env, DEFAULT_RSTVEC);
> + set_feature(env, RISCV_FEATURE_PMP);
> +}
> #endif
>
> static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
> @@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
> * Definition of the WFI instruction requires it to ignore the privilege
> * mode and delegation registers, but respect individual enables
> */
> - return (env->mip & env->mie) != 0;
> + return ((env->mip & env->mie) != 0 || (env->exccode != -1));
This change for example needs to be explained, I'm not sure what exccode is
> #else
> return true;
> #endif
> @@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
> env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
> env->mcause = 0;
> env->pc = env->resetvec;
> + env->exccode = -1;
> env->two_stage_lookup = false;
> #endif
> cs->exception_index = EXCP_NONE;
> @@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
> 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),
> + DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
> #elif defined(TARGET_RISCV64)
> DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
> #endif
> };
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 0a33d387ba..1d3a1986a6 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
> #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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
>
> #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
> #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
> @@ -43,6 +44,8 @@
> #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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
> +#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
>
> #if defined(TARGET_RISCV32)
> # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
> @@ -80,7 +83,8 @@
> enum {
> RISCV_FEATURE_MMU,
> RISCV_FEATURE_PMP,
> - RISCV_FEATURE_MISA
> + RISCV_FEATURE_MISA,
> + RISCV_FEATURE_ECLIC
The same here, what is ECLIC? The ECLIC should be added in a seperate patch.
> };
>
> #define PRIV_VERSION_1_10_0 0x00011000
> @@ -174,10 +178,34 @@ struct CPURISCVState {
> target_ulong scause;
>
> target_ulong mtvec;
> + target_ulong mtvt; /* eclic */
> target_ulong mepc;
> target_ulong mcause;
> target_ulong mtval; /* since: priv-1.10.0 */
>
> + target_ulong mnxti; /* eclic */
> + target_ulong mintstatus; /* eclic */
> + target_ulong mscratchcsw;
> + target_ulong mscratchcswl;
> +
> + /* NMI CSR*/
> + target_ulong mnvec;
> + target_ulong msubm;
> + target_ulong mdcause;
> + target_ulong mmisc_ctl;
> + target_ulong msavestatus;
> + target_ulong msaveepc1;
> + target_ulong msavecause1;
> + target_ulong msaveepc2;
> + target_ulong msavecause2;
> + target_ulong msavedcause1;
> + target_ulong msavedcause2;
> + target_ulong pushmsubm;
> + target_ulong mtvt2;
> + target_ulong jalmnxti;
> + target_ulong pushmcause;
> + target_ulong pushmepc;
What are NMI CSRs?
> +
> /* Hypervisor CSRs */
> target_ulong hstatus;
> target_ulong hedeleg;
> @@ -228,6 +256,9 @@ struct CPURISCVState {
> uint64_t mtohost;
> uint64_t timecmp;
>
> + /*nuclei timer comparators */
> + uint64_t mtimecmp;
RISC-V has a mtimecmp, does nuclei add another one?
> +
> /* physical memory protection */
> pmp_table_t pmp_state;
>
> @@ -243,6 +274,13 @@ struct CPURISCVState {
>
> /* Fields from here on are preserved across CPU reset. */
> QEMUTimer *timer; /* Internal timer */
> +
> + QEMUTimer *mtimer; /* Nuclei Internal timer */
Why do you need a timer here just for the Nuclei CPU?
> + void *eclic;
> + uint32_t exccode; /* irq id: 0~11 shv: 12 */
> + uint32_t eclic_flag;
> +
> + bool irq_pending;
> };
>
> OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> @@ -364,6 +402,8 @@ void riscv_cpu_list(void);
> void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
> uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
> +void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
> +void riscv_cpu_eclic_get_next_interrupt(void *eclic);
> #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
> void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
> uint32_t arg);
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index caf4599207..24ed7a99e1 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -149,6 +149,7 @@
> #define CSR_MIE 0x304
> #define CSR_MTVEC 0x305
> #define CSR_MCOUNTEREN 0x306
> +#define CSR_MTVT 0x307 /* customized */
So I'm not sure what to do here. This seems to be a custom CSR just
for the Nuclei that isn't part of the RISC-V spec or a draft spec.
The problem is that accepting custom specs into QEMU makes it hard for
us to maintain the RISC-V port. After it has been merged the
maintainers now have to understand the Nuclei CPU and support it as
part of the core RISC-V code.
On the other hand I have seen a few CPUs that use CSRs and I don't
want to not allow implementations that use custom CSRs. I think there
is a compromise here. We probably don't want to support really custom
features, but we probably can afford to support some extra CSRs.
I think the best course of action here is to split this patch up and
we can then think about each custom feature/CSR and accept some
depending on how intrusive they are into the QEMU code. It will also
have to be added in a way that allows other implementations to have
different custom CSRs. We (the QEMU RISC-V community) can help you
with this.
Alistair
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
2021-05-07 8:16 ` [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device wangjunqiang
@ 2021-05-10 2:20 ` Alistair Francis
2021-05-10 2:27 ` Bin Meng
0 siblings, 1 reply; 16+ messages in thread
From: Alistair Francis @ 2021-05-10 2:20 UTC (permalink / raw)
To: wangjunqiang
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
>
> This patch provides an implementation of Nuclei ECLIC Device.
> Nuclei processor core have been equipped with an Enhanced Core Local
> Interrupt Controller (ECLIC), which is optimized based on the RISC-V
> standard CLIC, to manage all interrupt sources.
>
> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
Hello,
There are patches on the QEMU list adding support for the CLIC. How
different is the ECLIC from the CLIC? Could you use the CLIC as a
starting point instead of implementing a new interrupt controller?
Alistair
> ---
> hw/intc/Kconfig | 3 +
> hw/intc/meson.build | 1 +
> hw/intc/nuclei_eclic.c | 437 +++++++++++++++++++++++++++++++++
> include/hw/intc/nuclei_eclic.h | 115 +++++++++
> 4 files changed, 556 insertions(+)
> create mode 100644 hw/intc/nuclei_eclic.c
> create mode 100644 include/hw/intc/nuclei_eclic.h
>
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..eab30f6ffd 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -73,3 +73,6 @@ config GOLDFISH_PIC
>
> config M68K_IRQC
> bool
> +
> +config NUCLEI_ECLIC
> + bool
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..7577ba69d2 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
> specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
> specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
> specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
> +specific_ss.add(when: 'CONFIG_NUCLEI_ECLIC', if_true: files('nuclei_eclic.c'))
> specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
> specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
> if_true: files('xics_kvm.c'))
> diff --git a/hw/intc/nuclei_eclic.c b/hw/intc/nuclei_eclic.c
> new file mode 100644
> index 0000000000..52de83cb1d
> --- /dev/null
> +++ b/hw/intc/nuclei_eclic.c
> @@ -0,0 +1,437 @@
> +/*
> + * NUCLEI ECLIC(Enhanced Core Local Interrupt Controller)
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that 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/log.h"
> +#include "qemu/module.h"
> +#include "qemu/error-report.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/msi.h"
> +#include "hw/boards.h"
> +#include "hw/qdev-properties.h"
> +#include "target/riscv/cpu.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/intc/nuclei_eclic.h"
> +#include "qapi/error.h"
> +
> +#define RISCV_DEBUG_ECLIC 0
> +
> +static void riscv_cpu_eclic_interrupt(RISCVCPU *cpu, int exccode)
> +{
> + CPURISCVState *env = &cpu->env;
> + bool locked = false;
> +
> + env->exccode = exccode;
> +
> + if (!qemu_mutex_iothread_locked()) {
> + locked = true;
> + qemu_mutex_lock_iothread();
> + }
> +
> + if (exccode != -1) {
> + env->irq_pending = true;
> + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> + } else {
> + env->irq_pending = false;
> + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_ECLIC);
> + }
> +
> + if (locked) {
> + qemu_mutex_unlock_iothread();
> + }
> +}
> +
> +static int level_compare(NucLeiECLICState *eclic,
> + ECLICPendingInterrupt *irq1,
> + ECLICPendingInterrupt *irq2)
> +{
> + if (irq1->level == irq2->level) {
> + if (irq1->prio == irq2->prio) {
> + if (irq1->irq >= irq2->irq) {
> + return 0;
> + } else {
> + return 1;
> + }
> + } else if (irq1->prio > irq2->level) {
> + return 0;
> + } else {
> + return 1;
> + }
> + } else if (irq1->level > irq2->level) {
> + return 0;
> + } else {
> + return 1;
> + }
> +}
> +
> +static void nuclei_eclic_next_interrupt(void *eclic_ptr)
> +{
> + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> + CPURISCVState *env = &cpu->env;
> + NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> + ECLICPendingInterrupt *active;
> + target_ulong mil;
> + int shv;
> +
> + QLIST_FOREACH(active, &eclic->pending_list, next)
> + {
> + if (active->enable) {
> + mil = get_field(env->mintstatus, MINTSTATUS_MIL);
> + if (active->level >= eclic->mth && active->level > mil) {
> + shv = eclic->clicintattr[active->irq] & 0x1;
> + eclic->active_count++;
> + riscv_cpu_eclic_interrupt(cpu,
> + (active->irq & 0xFFF) | (shv << 12) | (active->level << 13));
> + return;
> + }
> + }
> + }
> +}
> +
> +static void nuclei_eclic_update_intmth(NucLeiECLICState *eclic,
> + int irq, int mth)
> +{
> + eclic->mth = mth;
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void update_eclic_int_info(NucLeiECLICState *eclic, int irq)
> +{
> + int level_width = (eclic->cliccfg >> 1) & 0xF;
> + if (level_width > CLICINTCTLBITS) {
> + level_width = CLICINTCTLBITS;
> + }
> + int prio_width = CLICINTCTLBITS - level_width;
> +
> + if (level_width == 0) {
> + eclic->clicintlist[irq].level = 255;
> + } else {
> + eclic->clicintlist[irq].level = ((
> + (eclic->clicintctl[irq] >> (8 - level_width)) &
> + ~((char)0x80 >> (8 - level_width)))
> + << (8 - level_width)) |
> + (0xff >> level_width);
> + }
> +
> + if (prio_width == 0) {
> + eclic->clicintlist[irq].prio = 0;
> + } else {
> + eclic->clicintlist[irq].prio =
> + (eclic->clicintctl[irq] >> (8 - level_width)) &
> + ~(0x80 >> (8 - prio_width));
> + }
> +
> + eclic->clicintlist[irq].enable = eclic->clicintie[irq] & 0x1;
> + eclic->clicintlist[irq].trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> +}
> +
> +static void eclic_remove_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> + QLIST_REMOVE(&eclic->clicintlist[irq], next);
> +}
> +
> +static void eclic_insert_pending_list(NucLeiECLICState *eclic, int irq)
> +{
> + ECLICPendingInterrupt *node;
> + if (QLIST_EMPTY(&eclic->pending_list)) {
> + QLIST_INSERT_HEAD(&eclic->pending_list, &eclic->clicintlist[irq], next);
> + } else {
> + QLIST_FOREACH(node, &eclic->pending_list, next)
> + {
> + if (level_compare(eclic, node, &eclic->clicintlist[irq])) {
> + QLIST_INSERT_BEFORE(node, &eclic->clicintlist[irq], next);
> + break;
> + } else if (node->next.le_next == NULL) {
> + QLIST_INSERT_AFTER(node, &eclic->clicintlist[irq], next);
> + break;
> + }
> + }
> + }
> +}
> +
> +static void nuclei_eclic_update_intip(NucLeiECLICState *eclic, int irq, int new_intip)
> +{
> +
> + int old_intip = eclic->clicintlist[irq].sig;
> + int trigger = (eclic->clicintattr[irq] >> 1) & 0x3;
> + if (((trigger == 0) && new_intip) ||
> + ((trigger == 1) && !old_intip && new_intip) ||
> + ((trigger == 3) && old_intip && !new_intip)) {
> + eclic->clicintip[irq] = 1;
> + eclic->clicintlist[irq].sig = new_intip;
> + eclic_insert_pending_list(eclic, irq);
> + } else {
> + if (eclic->clicintip[irq]) {
> + eclic_remove_pending_list(eclic, irq);
> + }
> + eclic->clicintip[irq] = 0;
> + eclic->clicintlist[irq].sig = new_intip;
> + }
> +
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intie(NucLeiECLICState *eclic,
> + int irq, int new_intie)
> +{
> + eclic->clicintie[irq] = new_intie;
> + update_eclic_int_info(eclic, irq);
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intattr(NucLeiECLICState *eclic,
> + int irq, int new_intattr)
> +{
> + eclic->clicintattr[irq] = new_intattr;
> + update_eclic_int_info(eclic, irq);
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +static void nuclei_eclic_update_intctl(NucLeiECLICState *eclic,
> + int irq, int new_intctl)
> +{
> + eclic->clicintctl[irq] = new_intctl;
> + update_eclic_int_info(eclic, irq);
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> + return eclic->irqs[irq];
> +}
> +
> +static uint64_t nuclei_eclic_read(void *opaque, hwaddr offset, unsigned size)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> + uint64_t value = 0;
> + uint32_t id = 0;
> + if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> + if ((offset - 0x1000) % 4 == 0) {
> + id = (offset - 0x1000) / 4;
> + } else if ((offset - 0x1001) % 4 == 0) {
> + id = (offset - 0x1001) / 4;
> + } else if ((offset - 0x1002) % 4 == 0) {
> + id = (offset - 0x1002) / 4;
> + } else if ((offset - 0x1003) % 4 == 0) {
> + id = (offset - 0x1003) / 4;
> + }
> + offset = offset - 4 * id;
> + }
> +
> + switch (offset) {
> + case NUCLEI_ECLIC_REG_CLICCFG:
> + value = eclic->cliccfg & 0xFF;
> + break;
> + case NUCLEI_ECLIC_REG_CLICINFO:
> + value = (CLICINTCTLBITS << 21) & 0xFFFFFFFF;
> + break;
> + case NUCLEI_ECLIC_REG_MTH:
> + value = eclic->mth & 0xFF;
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> + value = eclic->clicintip[id] & 0xFF;
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> + value = eclic->clicintie[id] & 0xFF;
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> + value = eclic->clicintattr[id] & 0xFF;
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> + value = eclic->clicintctl[id] & 0xFF;
> + break;
> + default:
> + break;
> + }
> +
> + return value;
> +}
> +
> +static void nuclei_eclic_write(void *opaque, hwaddr offset, uint64_t value,
> + unsigned size)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> + uint32_t id = 0;
> + if (offset >= NUCLEI_ECLIC_REG_CLICINTIP_BASE) {
> +
> + if ((offset - 0x1000) % 4 == 0) {
> + id = (offset - 0x1000) / 4;
> + } else if ((offset - 0x1001) % 4 == 0) {
> + id = (offset - 0x1001) / 4;
> + } else if ((offset - 0x1002) % 4 == 0) {
> + id = (offset - 0x1002) / 4;
> + } else if ((offset - 0x1003) % 4 == 0) {
> + id = (offset - 0x1003) / 4;
> + }
> + offset = offset - 4 * id;
> + }
> + switch (offset) {
> + case NUCLEI_ECLIC_REG_CLICCFG:
> + eclic->cliccfg = value & 0xFF;
> + for (id = 0; id < eclic->num_sources; id++) {
> + update_eclic_int_info(eclic, id);
> + }
> + break;
> + case NUCLEI_ECLIC_REG_MTH:
> + nuclei_eclic_update_intmth(eclic, id, value & 0xFF);
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTIP_BASE:
> + if ((eclic->clicintlist[id].trigger & 0x1) != 0) {
> + if ((eclic->clicintip[id] == 0) && (value & 0x1) == 1) {
> + eclic->clicintip[id] = 1;
> + eclic_insert_pending_list(eclic, id);
> + } else if ((eclic->clicintip[id] == 1) && (value & 0x1) == 0) {
> + eclic->clicintip[id] = 0;
> + eclic_remove_pending_list(eclic, id);
> + }
> + }
> + nuclei_eclic_next_interrupt(eclic);
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTIE_BASE:
> + nuclei_eclic_update_intie(eclic, id, value & 0xFF);
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTATTR_BASE:
> + nuclei_eclic_update_intattr(eclic, id, value & 0xFF);
> + break;
> + case NUCLEI_ECLIC_REG_CLICINTCTL_BASE:
> + nuclei_eclic_update_intctl(eclic, id, value & 0xFF);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps nuclei_eclic_ops = {
> + .read = nuclei_eclic_read,
> + .write = nuclei_eclic_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +void riscv_cpu_eclic_clean_pending(void *eclic_ptr, int irq)
> +{
> + NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> + if ((eclic->clicintlist[irq].trigger & 0x1) != 0 && irq >= 0) {
> + eclic->clicintip[irq] = 0;
> + eclic_remove_pending_list(eclic, irq);
> + }
> +}
> +
> +void riscv_cpu_eclic_get_next_interrupt(void *eclic_ptr)
> +{
> + NucLeiECLICState *eclic = (NucLeiECLICState *)eclic_ptr;
> + nuclei_eclic_next_interrupt(eclic);
> +}
> +
> +
> +
> +static void nuclei_eclic_irq_request(void *opaque, int id, int new_intip)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(opaque);
> + nuclei_eclic_update_intip(eclic, id, new_intip);
> +}
> +
> +static void nuclei_eclic_realize(DeviceState *dev, Error **errp)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> + int id;
> +
> + memory_region_init_io(&eclic->mmio, OBJECT(dev), &nuclei_eclic_ops, eclic,
> + TYPE_NUCLEI_ECLIC, eclic->aperture_size);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &eclic->mmio);
> +
> + eclic->clicintip = g_new0(uint8_t, eclic->num_sources);
> + eclic->clicintlist = g_new0(ECLICPendingInterrupt, eclic->num_sources);
> + eclic->clicintie = g_new0(uint8_t, eclic->num_sources);
> + eclic->clicintattr = g_new0(uint8_t, eclic->num_sources);
> + eclic->clicintctl = g_new0(uint8_t, eclic->num_sources);
> + eclic->irqs = g_new0(qemu_irq, eclic->num_sources);
> + QLIST_INIT(&eclic->pending_list);
> + for (id = 0; id < eclic->num_sources; id++) {
> + eclic->clicintlist[id].irq = id;
> + update_eclic_int_info(eclic, id);
> + }
> + eclic->active_count = 0;
> +
> + /* Init ECLIC IRQ */
> + eclic->irqs[Internal_SysTimerSW_IRQn] =
> + qemu_allocate_irq(nuclei_eclic_irq_request,
> + eclic, Internal_SysTimerSW_IRQn);
> + eclic->irqs[Internal_SysTimer_IRQn] =
> + qemu_allocate_irq(nuclei_eclic_irq_request,
> + eclic, Internal_SysTimer_IRQn);
> +
> + for (id = Internal_Reserved_Max_IRQn; id < eclic->num_sources; id++) {
> + eclic->irqs[id] = qemu_allocate_irq(nuclei_eclic_irq_request,
> + eclic, id);
> + }
> +
> + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(0));
> + cpu->env.eclic = eclic;
> +}
> +
> +static Property nuclei_eclic_properties[] = {
> + DEFINE_PROP_UINT32("aperture-size", NucLeiECLICState, aperture_size, 0),
> + DEFINE_PROP_UINT32("num-sources", NucLeiECLICState, num_sources, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void nuclei_eclic_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + device_class_set_props(dc, nuclei_eclic_properties);
> + dc->realize = nuclei_eclic_realize;
> +}
> +
> +static const TypeInfo nuclei_eclic_info = {
> + .name = TYPE_NUCLEI_ECLIC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(NucLeiECLICState),
> + .class_init = nuclei_eclic_class_init,
> +};
> +
> +static void nuclei_eclic_register_types(void)
> +{
> + type_register_static(&nuclei_eclic_info);
> +}
> +
> +type_init(nuclei_eclic_register_types);
> +
> +void nuclei_eclic_systimer_cb(DeviceState *dev)
> +{
> + NucLeiECLICState *eclic = NUCLEI_ECLIC(dev);
> + nuclei_eclic_irq_request(eclic, Internal_SysTimer_IRQn, 1);
> +}
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> + uint32_t aperture_size, uint32_t num_sources)
> +{
> + DeviceState *dev = qdev_new(TYPE_NUCLEI_ECLIC);
> +
> + qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
> + qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> + return dev;
> +}
> diff --git a/include/hw/intc/nuclei_eclic.h b/include/hw/intc/nuclei_eclic.h
> new file mode 100644
> index 0000000000..18b25485b9
> --- /dev/null
> +++ b/include/hw/intc/nuclei_eclic.h
> @@ -0,0 +1,115 @@
> +/*
> + * NUCLEI ECLIC (Enhanced Core Local Interrupt Controller) interface
> + *
> + * Copyright (c) 2020 Gao ZhiYuan <alapha23@gmail.com>
> + * Copyright (c) 2020-2021 PLCT Lab.All rights reserved.
> + *
> + * This provides a parameterizable interrupt controller based on NucLei's ECLIC.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that 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/>.
> + */
> +#ifndef HW_NUCLEI_ECLIC_H
> +#define HW_NUCLEI_ECLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_NUCLEI_ECLIC "riscv.nuclei.eclic"
> +
> +#define INTERRUPT_SOURCE_MIN_ID (18)
> +#define INTERRUPT_SOURCE_MAX_ID (4096)
> +
> +typedef struct NucLeiECLICState NucLeiECLICState;
> +DECLARE_INSTANCE_CHECKER(NucLeiECLICState, NUCLEI_ECLIC,
> + TYPE_NUCLEI_ECLIC)
> +
> +typedef struct ECLICPendingInterrupt {
> + int irq;
> + int prio;
> + int level;
> + int enable;
> + int trigger;
> + int sig;
> + QLIST_ENTRY(ECLICPendingInterrupt) next;
> +} ECLICPendingInterrupt;
> +
> +#define NUCLEI_ECLIC_REG_CLICCFG 0x0000
> +#define NUCLEI_ECLIC_REG_CLICINFO 0x0004
> +#define NUCLEI_ECLIC_REG_MTH 0x000b
> +#define NUCLEI_ECLIC_REG_CLICINTIP_BASE 0x1000
> +#define NUCLEI_ECLIC_REG_CLICINTIE_BASE 0x1001
> +#define NUCLEI_ECLIC_REG_CLICINTATTR_BASE 0x1002
> +#define NUCLEI_ECLIC_REG_CLICINTCTL_BASE 0x1003
> +
> +#define CLICINTCTLBITS 0x6
> +
> +typedef struct NucLeiECLICState {
> + /*< private >*/
> + SysBusDevice parent_obj;
> +
> + /*< public >*/
> + MemoryRegion mmio;
> +
> + uint32_t num_sources; /* 4-1024 */
> +
> + /* config */
> + uint32_t sources_id;
> + uint8_t cliccfg; /* nlbits(1~4) */
> + uint32_t clicinfo;
> + uint8_t mth; /* mth(0~7) */
> + uint8_t *clicintip;
> + uint8_t *clicintie;
> + uint8_t *clicintattr; /* shv(0) trig(1~2)*/
> + uint8_t *clicintctl;
> + ECLICPendingInterrupt *clicintlist;
> + uint32_t aperture_size;
> +
> + QLIST_HEAD(, ECLICPendingInterrupt)
> + pending_list;
> + size_t active_count;
> +
> + /* ECLIC IRQ handlers */
> + qemu_irq *irqs;
> +
> +} NucLeiECLICState;
> +
> +enum {
> + Internal_Reserved0_IRQn = 0, /*!< Internal reserved */
> + Internal_Reserved1_IRQn = 1, /*!< Internal reserved */
> + Internal_Reserved2_IRQn = 2, /*!< Internal reserved */
> + Internal_SysTimerSW_IRQn = 3, /*!< System Timer SW interrupt */
> + Internal_Reserved3_IRQn = 4, /*!< Internal reserved */
> + Internal_Reserved4_IRQn = 5, /*!< Internal reserved */
> + Internal_Reserved5_IRQn = 6, /*!< Internal reserved */
> + Internal_SysTimer_IRQn = 7, /*!< System Timer Interrupt */
> + Internal_Reserved6_IRQn = 8, /*!< Internal reserved */
> + Internal_Reserved7_IRQn = 9, /*!< Internal reserved */
> + Internal_Reserved8_IRQn = 10, /*!< Internal reserved */
> + Internal_Reserved9_IRQn = 11, /*!< Internal reserved */
> + Internal_Reserved10_IRQn = 12, /*!< Internal reserved */
> + Internal_Reserved11_IRQn = 13, /*!< Internal reserved */
> + Internal_Reserved12_IRQn = 14, /*!< Internal reserved */
> + Internal_Reserved13_IRQn = 15, /*!< Internal reserved */
> + Internal_Reserved14_IRQn = 16, /*!< Internal reserved */
> + Internal_BusError_IRQn = 17, /*!< Bus Error interrupt */
> + Internal_PerfMon_IRQn = 18, /*!< Performance Monitor */
> + Internal_Reserved_Max_IRQn = 19, /*!< Internal reserved Max */
> +};
> +
> +DeviceState *nuclei_eclic_create(hwaddr addr,
> + uint32_t aperture_size, uint32_t num_sources);
> +qemu_irq nuclei_eclic_get_irq(DeviceState *dev, int irq);
> +void nuclei_eclic_systimer_cb(DeviceState *dev);
> +
> +#endif
> --
> 2.17.1
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
2021-05-10 2:20 ` Alistair Francis
@ 2021-05-10 2:27 ` Bin Meng
2021-05-10 5:26 ` Bin Meng
0 siblings, 1 reply; 16+ messages in thread
From: Bin Meng @ 2021-05-10 2:27 UTC (permalink / raw)
To: Alistair Francis
Cc: liweiwei, open list:RISC-V, wangjunqiang, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On Mon, May 10, 2021 at 10:21 AM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
> >
> > This patch provides an implementation of Nuclei ECLIC Device.
> > Nuclei processor core have been equipped with an Enhanced Core Local
> > Interrupt Controller (ECLIC), which is optimized based on the RISC-V
> > standard CLIC, to manage all interrupt sources.
> >
> > https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
>
> Hello,
>
> There are patches on the QEMU list adding support for the CLIC. How
> different is the ECLIC from the CLIC? Could you use the CLIC as a
> starting point instead of implementing a new interrupt controller?
>
That's my thought too when I saw this patch at first.
A better way is to scandalize the CLIC support in QEMU first, then we
will see how Nuclei's eCLIC could be built on top of that. Thanks!
Regards,
Bin
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
2021-05-10 2:27 ` Bin Meng
@ 2021-05-10 5:26 ` Bin Meng
2021-05-11 3:18 ` Wang Junqiang
0 siblings, 1 reply; 16+ messages in thread
From: Bin Meng @ 2021-05-10 5:26 UTC (permalink / raw)
To: Alistair Francis
Cc: liweiwei, open list:RISC-V, wangjunqiang, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On Mon, May 10, 2021 at 10:27 AM Bin Meng <bmeng.cn@gmail.com> wrote:
>
> On Mon, May 10, 2021 at 10:21 AM Alistair Francis <alistair23@gmail.com> wrote:
> >
> > On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
> > >
> > > This patch provides an implementation of Nuclei ECLIC Device.
> > > Nuclei processor core have been equipped with an Enhanced Core Local
> > > Interrupt Controller (ECLIC), which is optimized based on the RISC-V
> > > standard CLIC, to manage all interrupt sources.
> > >
> > > https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
> >
> > Hello,
> >
> > There are patches on the QEMU list adding support for the CLIC. How
> > different is the ECLIC from the CLIC? Could you use the CLIC as a
> > starting point instead of implementing a new interrupt controller?
> >
>
> That's my thought too when I saw this patch at first.
>
> A better way is to scandalize the CLIC support in QEMU first, then we
Sorry for the typo. I meant to say: standardize the CLIC support
> will see how Nuclei's eCLIC could be built on top of that. Thanks!
Regards,
Bin
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-10 2:17 ` Alistair Francis
@ 2021-05-11 3:14 ` Wang Junqiang
2021-05-11 3:43 ` Alistair Francis
0 siblings, 1 reply; 16+ messages in thread
From: Wang Junqiang @ 2021-05-11 3:14 UTC (permalink / raw)
To: Alistair Francis
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On 2021/5/10 上午10:17, Alistair Francis wrote:
> C isOn Fri, May 7, 2021 at 11:25 PM wangjunqiang
> <wangjunqiang@iscas.ac.cn> wrote:
>>
>> This patch adds Nuclei CSR support for ECLIC and update the
>> related interrupt handling.
>>
>> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
>
> Hello,
>
> Thanks for the patches!
>
> This patch is very long and you will need to split it up before it can
> be merged. I understand this is just an RFC, but it's still best to
> start with small patches. Generally each patch should add a feature
> and it seems like you have added lots of features in this patch. This
> patch could probably be broken into at least 4 different patches.
>
> As well as that you will want to ensure that your commit message and
> description explains what you are doing in that patch and in some
> cases justify the change. For example adding a new CPU doesn't need a
> justification (as that's easy for me to understand), but changing some
> existing code might need an explanation of why we need/want that
> change.
>
> This is still a great start though! I look forward to your future patches.
>
> I have left a few comments below as well.
Thank you for your reply and comments.I will split it into small patches
by feature in next version.And add more detailed description. To make a
brief explanation, add cpu here to simplify the command line when using
-cpu.
>
>> ---
>> target/riscv/cpu.c | 25 +-
>> target/riscv/cpu.h | 42 ++-
>> target/riscv/cpu_bits.h | 37 +++
>> target/riscv/cpu_helper.c | 80 +++++-
>> target/riscv/csr.c | 347 +++++++++++++++++++++++-
>> target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
>> target/riscv/op_helper.c | 14 +
>> 7 files changed, 552 insertions(+), 9 deletions(-)
>>
>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>> index 7d6ed80f6b..b2a96effbc 100644
>> --- a/target/riscv/cpu.c
>> +++ b/target/riscv/cpu.c
>> @@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
>> set_priv_version(env, PRIV_VERSION_1_10_0);
>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>> }
>> +
>> +static void rv64imafdcu_nuclei_cpu_init(Object *obj)
>> +{
>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
>> + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
>> + set_priv_version(env, PRIV_VERSION_1_10_0);
>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>> + set_resetvec(env, DEFAULT_RSTVEC);
>> + set_feature(env, RISCV_FEATURE_PMP);
>> +}
>> #else
>> static void rv32_base_cpu_init(Object *obj)
>> {
>> @@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
>> set_resetvec(env, DEFAULT_RSTVEC);
>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>> }
>> +
>> +static void rv32imafdcu_nuclei_cpu_init(Object *obj)
>> +{
>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
>> + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
>> + set_priv_version(env, PRIV_VERSION_1_10_0);
>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>> + set_resetvec(env, DEFAULT_RSTVEC);
>> + set_feature(env, RISCV_FEATURE_PMP);
>> +}
>> #endif
>>
>> static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
>> @@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
>> * Definition of the WFI instruction requires it to ignore the privilege
>> * mode and delegation registers, but respect individual enables
>> */
>> - return (env->mip & env->mie) != 0;
>> + return ((env->mip & env->mie) != 0 || (env->exccode != -1));
>
> This change for example needs to be explained, I'm not sure what exccode is
>
>> #else
>> return true;
>> #endif
>> @@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
>> env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
>> env->mcause = 0;
>> env->pc = env->resetvec;
>> + env->exccode = -1;
>> env->two_stage_lookup = false;
>> #endif
>> cs->exception_index = EXCP_NONE;
>> @@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
>> 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),
>> + DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
>> #elif defined(TARGET_RISCV64)
>> DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
>> #endif
>> };
>>
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index 0a33d387ba..1d3a1986a6 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -33,6 +33,7 @@
>> #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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
>>
>> #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
>> #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
>> @@ -43,6 +44,8 @@
>> #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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
>> +#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
>>
>> #if defined(TARGET_RISCV32)
>> # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
>> @@ -80,7 +83,8 @@
>> enum {
>> RISCV_FEATURE_MMU,
>> RISCV_FEATURE_PMP,
>> - RISCV_FEATURE_MISA
>> + RISCV_FEATURE_MISA,
>> + RISCV_FEATURE_ECLIC
>
> The same here, what is ECLIC? The ECLIC should be added in a seperate patch.
>
ECLIC is Enhanced Core Local Interrupt Controller.And added some
customized csr on the basis of clic to speed up Tail-Chaining processing.
https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
>> };
>>
>> #define PRIV_VERSION_1_10_0 0x00011000
>> @@ -174,10 +178,34 @@ struct CPURISCVState {
>> target_ulong scause;
>>
>> target_ulong mtvec;
>> + target_ulong mtvt; /* eclic */
>> target_ulong mepc;
>> target_ulong mcause;
>> target_ulong mtval; /* since: priv-1.10.0 */
>>
>> + target_ulong mnxti; /* eclic */
>> + target_ulong mintstatus; /* eclic */
>> + target_ulong mscratchcsw;
>> + target_ulong mscratchcswl;
>> +
>> + /* NMI CSR*/
>> + target_ulong mnvec;
>> + target_ulong msubm;
>> + target_ulong mdcause;
>> + target_ulong mmisc_ctl;
>> + target_ulong msavestatus;
>> + target_ulong msaveepc1;
>> + target_ulong msavecause1;
>> + target_ulong msaveepc2;
>> + target_ulong msavecause2;
>> + target_ulong msavedcause1;
>> + target_ulong msavedcause2;
>> + target_ulong pushmsubm;
>> + target_ulong mtvt2;
>> + target_ulong jalmnxti;
>> + target_ulong pushmcause;
>> + target_ulong pushmepc;
>
> What are NMI CSRs?
>
Nuclei's Customized registers are used for NMI related processing
https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
>> +
>> /* Hypervisor CSRs */
>> target_ulong hstatus;
>> target_ulong hedeleg;
>> @@ -228,6 +256,9 @@ struct CPURISCVState {
>> uint64_t mtohost;
>> uint64_t timecmp;
>>
>> + /*nuclei timer comparators */
>> + uint64_t mtimecmp;
>
> RISC-V has a mtimecmp, does nuclei add another one?
>
I will delete it, it was originally used for Shadow copy, I can move it
to the device
https://doc.nucleisys.com/nuclei_spec/isa/timer.html
>> +
>> /* physical memory protection */
>> pmp_table_t pmp_state;
>>
>> @@ -243,6 +274,13 @@ struct CPURISCVState {
>>
>> /* Fields from here on are preserved across CPU reset. */
>> QEMUTimer *timer; /* Internal timer */
>> +
>> + QEMUTimer *mtimer; /* Nuclei Internal timer */
>
> Why do you need a timer here just for the Nuclei CPU?
>
same as above
>> + void *eclic;
>> + uint32_t exccode; /* irq id: 0~11 shv: 12 */
>> + uint32_t eclic_flag;
>> +
>> + bool irq_pending;
>> };
>>
>> OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> @@ -364,6 +402,8 @@ void riscv_cpu_list(void);
>> void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
>> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
>> uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
>> +void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
>> +void riscv_cpu_eclic_get_next_interrupt(void *eclic);
>> #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
>> void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
>> uint32_t arg);
>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>> index caf4599207..24ed7a99e1 100644
>> --- a/target/riscv/cpu_bits.h
>> +++ b/target/riscv/cpu_bits.h
>> @@ -149,6 +149,7 @@
>> #define CSR_MIE 0x304
>> #define CSR_MTVEC 0x305
>> #define CSR_MCOUNTEREN 0x306
>> +#define CSR_MTVT 0x307 /* customized */
>
> So I'm not sure what to do here. This seems to be a custom CSR just
> for the Nuclei that isn't part of the RISC-V spec or a draft spec.
>
> The problem is that accepting custom specs into QEMU makes it hard for
> us to maintain the RISC-V port. After it has been merged the
> maintainers now have to understand the Nuclei CPU and support it as
> part of the core RISC-V code.
>
> On the other hand I have seen a few CPUs that use CSRs and I don't
> want to not allow implementations that use custom CSRs. I think there
> is a compromise here. We probably don't want to support really custom
> features, but we probably can afford to support some extra CSRs.
>
> I think the best course of action here is to split this patch up and
> we can then think about each custom feature/CSR and accept some
> depending on how intrusive they are into the QEMU code. It will also
> have to be added in a way that allows other implementations to have
> different custom CSRs. We (the QEMU RISC-V community) can help you
> with this.
>
> Alistair
>
Thanks for your comment. About customized csr, I have a rough idea,
whether it is possible to open the interface for manufacturers to allow
them to implement their own csr.To implement the registration callback
interface, add a branch to the riscv_csrrw function, and define a switch
for the cpu. When a custom csr is supported, the vendor registration is
preferred.The manufacturer maintains its own csr and does not invade the
qemu code much. Of course, there may be some unknown security and
stability issues.
Regards
Wang Junqiang
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device
2021-05-10 5:26 ` Bin Meng
@ 2021-05-11 3:18 ` Wang Junqiang
0 siblings, 0 replies; 16+ messages in thread
From: Wang Junqiang @ 2021-05-11 3:18 UTC (permalink / raw)
To: Bin Meng, Alistair Francis
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On 2021/5/10 下午1:26, Bin Meng wrote:
> On Mon, May 10, 2021 at 10:27 AM Bin Meng <bmeng.cn@gmail.com> wrote:
>>
>> On Mon, May 10, 2021 at 10:21 AM Alistair Francis <alistair23@gmail.com> wrote:
>>>
>>> On Fri, May 7, 2021 at 11:24 PM wangjunqiang <wangjunqiang@iscas.ac.cn> wrote:
>>>>
>>>> This patch provides an implementation of Nuclei ECLIC Device.
>>>> Nuclei processor core have been equipped with an Enhanced Core Local
>>>> Interrupt Controller (ECLIC), which is optimized based on the RISC-V
>>>> standard CLIC, to manage all interrupt sources.
>>>>
>>>> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
>>>
>>> Hello,
>>>
>>> There are patches on the QEMU list adding support for the CLIC. How
>>> different is the ECLIC from the CLIC? Could you use the CLIC as a
>>> starting point instead of implementing a new interrupt controller?
>>>
>>
>> That's my thought too when I saw this patch at first.
>>
>> A better way is to scandalize the CLIC support in QEMU first, then we
>
> Sorry for the typo. I meant to say: standardize the CLIC support
>
>> will see how Nuclei's eCLIC could be built on top of that. Thanks!
>
> Regards,
> Bin
>
I agree with both of you.the CLIC support in QEMU first. I read the
patch of clic, and there is no problem with compatibility in the
target/riscv directory.I will split eclic in next version. Thanks
Regards
Wang Junqiang
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-11 3:14 ` Wang Junqiang
@ 2021-05-11 3:43 ` Alistair Francis
2021-05-11 4:00 ` Wang Junqiang
0 siblings, 1 reply; 16+ messages in thread
From: Alistair Francis @ 2021-05-11 3:43 UTC (permalink / raw)
To: Wang Junqiang
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On Tue, May 11, 2021 at 1:14 PM Wang Junqiang <wangjunqiang@iscas.ac.cn> wrote:
>
>
>
> On 2021/5/10 上午10:17, Alistair Francis wrote:
> > C isOn Fri, May 7, 2021 at 11:25 PM wangjunqiang
> > <wangjunqiang@iscas.ac.cn> wrote:
> >>
> >> This patch adds Nuclei CSR support for ECLIC and update the
> >> related interrupt handling.
> >>
> >> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
> >
> > Hello,
> >
> > Thanks for the patches!
> >
> > This patch is very long and you will need to split it up before it can
> > be merged. I understand this is just an RFC, but it's still best to
> > start with small patches. Generally each patch should add a feature
> > and it seems like you have added lots of features in this patch. This
> > patch could probably be broken into at least 4 different patches.
> >
> > As well as that you will want to ensure that your commit message and
> > description explains what you are doing in that patch and in some
> > cases justify the change. For example adding a new CPU doesn't need a
> > justification (as that's easy for me to understand), but changing some
> > existing code might need an explanation of why we need/want that
> > change.
> >
> > This is still a great start though! I look forward to your future patches.
> >
> > I have left a few comments below as well.
>
> Thank you for your reply and comments.I will split it into small patches
> by feature in next version.And add more detailed description. To make a
> brief explanation, add cpu here to simplify the command line when using
> -cpu.
>
> >
> >> ---
> >> target/riscv/cpu.c | 25 +-
> >> target/riscv/cpu.h | 42 ++-
> >> target/riscv/cpu_bits.h | 37 +++
> >> target/riscv/cpu_helper.c | 80 +++++-
> >> target/riscv/csr.c | 347 +++++++++++++++++++++++-
> >> target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
> >> target/riscv/op_helper.c | 14 +
> >> 7 files changed, 552 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> >> index 7d6ed80f6b..b2a96effbc 100644
> >> --- a/target/riscv/cpu.c
> >> +++ b/target/riscv/cpu.c
> >> @@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
> >> set_priv_version(env, PRIV_VERSION_1_10_0);
> >> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >> }
> >> +
> >> +static void rv64imafdcu_nuclei_cpu_init(Object *obj)
> >> +{
> >> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> >> + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> >> + set_priv_version(env, PRIV_VERSION_1_10_0);
> >> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >> + set_resetvec(env, DEFAULT_RSTVEC);
> >> + set_feature(env, RISCV_FEATURE_PMP);
> >> +}
> >> #else
> >> static void rv32_base_cpu_init(Object *obj)
> >> {
> >> @@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
> >> set_resetvec(env, DEFAULT_RSTVEC);
> >> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >> }
> >> +
> >> +static void rv32imafdcu_nuclei_cpu_init(Object *obj)
> >> +{
> >> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> >> + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> >> + set_priv_version(env, PRIV_VERSION_1_10_0);
> >> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >> + set_resetvec(env, DEFAULT_RSTVEC);
> >> + set_feature(env, RISCV_FEATURE_PMP);
> >> +}
> >> #endif
> >>
> >> static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
> >> @@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
> >> * Definition of the WFI instruction requires it to ignore the privilege
> >> * mode and delegation registers, but respect individual enables
> >> */
> >> - return (env->mip & env->mie) != 0;
> >> + return ((env->mip & env->mie) != 0 || (env->exccode != -1));
> >
> > This change for example needs to be explained, I'm not sure what exccode is
> >
> >> #else
> >> return true;
> >> #endif
> >> @@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
> >> env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
> >> env->mcause = 0;
> >> env->pc = env->resetvec;
> >> + env->exccode = -1;
> >> env->two_stage_lookup = false;
> >> #endif
> >> cs->exception_index = EXCP_NONE;
> >> @@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
> >> 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),
> >> + DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
> >> #elif defined(TARGET_RISCV64)
> >> DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
> >> #endif
> >> };
> >>
> >> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> >> index 0a33d387ba..1d3a1986a6 100644
> >> --- a/target/riscv/cpu.h
> >> +++ b/target/riscv/cpu.h
> >> @@ -33,6 +33,7 @@
> >> #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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
> >>
> >> #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
> >> #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
> >> @@ -43,6 +44,8 @@
> >> #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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
> >> +#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
> >>
> >> #if defined(TARGET_RISCV32)
> >> # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
> >> @@ -80,7 +83,8 @@
> >> enum {
> >> RISCV_FEATURE_MMU,
> >> RISCV_FEATURE_PMP,
> >> - RISCV_FEATURE_MISA
> >> + RISCV_FEATURE_MISA,
> >> + RISCV_FEATURE_ECLIC
> >
> > The same here, what is ECLIC? The ECLIC should be added in a seperate patch.
> >
>
> ECLIC is Enhanced Core Local Interrupt Controller.And added some
> customized csr on the basis of clic to speed up Tail-Chaining processing.
>
> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
>
> >> };
> >>
> >> #define PRIV_VERSION_1_10_0 0x00011000
> >> @@ -174,10 +178,34 @@ struct CPURISCVState {
> >> target_ulong scause;
> >>
> >> target_ulong mtvec;
> >> + target_ulong mtvt; /* eclic */
> >> target_ulong mepc;
> >> target_ulong mcause;
> >> target_ulong mtval; /* since: priv-1.10.0 */
> >>
> >> + target_ulong mnxti; /* eclic */
> >> + target_ulong mintstatus; /* eclic */
> >> + target_ulong mscratchcsw;
> >> + target_ulong mscratchcswl;
> >> +
> >> + /* NMI CSR*/
> >> + target_ulong mnvec;
> >> + target_ulong msubm;
> >> + target_ulong mdcause;
> >> + target_ulong mmisc_ctl;
> >> + target_ulong msavestatus;
> >> + target_ulong msaveepc1;
> >> + target_ulong msavecause1;
> >> + target_ulong msaveepc2;
> >> + target_ulong msavecause2;
> >> + target_ulong msavedcause1;
> >> + target_ulong msavedcause2;
> >> + target_ulong pushmsubm;
> >> + target_ulong mtvt2;
> >> + target_ulong jalmnxti;
> >> + target_ulong pushmcause;
> >> + target_ulong pushmepc;
> >
> > What are NMI CSRs?
> >
>
> Nuclei's Customized registers are used for NMI related processing
>
> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
>
>
> >> +
> >> /* Hypervisor CSRs */
> >> target_ulong hstatus;
> >> target_ulong hedeleg;
> >> @@ -228,6 +256,9 @@ struct CPURISCVState {
> >> uint64_t mtohost;
> >> uint64_t timecmp;
> >>
> >> + /*nuclei timer comparators */
> >> + uint64_t mtimecmp;
> >
> > RISC-V has a mtimecmp, does nuclei add another one?
> >
>
> I will delete it, it was originally used for Shadow copy, I can move it
> to the device
>
> https://doc.nucleisys.com/nuclei_spec/isa/timer.html
>
> >> +
> >> /* physical memory protection */
> >> pmp_table_t pmp_state;
> >>
> >> @@ -243,6 +274,13 @@ struct CPURISCVState {
> >>
> >> /* Fields from here on are preserved across CPU reset. */
> >> QEMUTimer *timer; /* Internal timer */
> >> +
> >> + QEMUTimer *mtimer; /* Nuclei Internal timer */
> >
> > Why do you need a timer here just for the Nuclei CPU?
> >
>
> same as above
>
> >> + void *eclic;
> >> + uint32_t exccode; /* irq id: 0~11 shv: 12 */
> >> + uint32_t eclic_flag;
> >> +
> >> + bool irq_pending;
> >> };
> >>
> >> OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> >> @@ -364,6 +402,8 @@ void riscv_cpu_list(void);
> >> void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
> >> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
> >> uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
> >> +void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
> >> +void riscv_cpu_eclic_get_next_interrupt(void *eclic);
> >> #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
> >> void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
> >> uint32_t arg);
> >> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> >> index caf4599207..24ed7a99e1 100644
> >> --- a/target/riscv/cpu_bits.h
> >> +++ b/target/riscv/cpu_bits.h
> >> @@ -149,6 +149,7 @@
> >> #define CSR_MIE 0x304
> >> #define CSR_MTVEC 0x305
> >> #define CSR_MCOUNTEREN 0x306
> >> +#define CSR_MTVT 0x307 /* customized */
> >
> > So I'm not sure what to do here. This seems to be a custom CSR just
> > for the Nuclei that isn't part of the RISC-V spec or a draft spec.
> >
> > The problem is that accepting custom specs into QEMU makes it hard for
> > us to maintain the RISC-V port. After it has been merged the
> > maintainers now have to understand the Nuclei CPU and support it as
> > part of the core RISC-V code.
> >
> > On the other hand I have seen a few CPUs that use CSRs and I don't
> > want to not allow implementations that use custom CSRs. I think there
> > is a compromise here. We probably don't want to support really custom
> > features, but we probably can afford to support some extra CSRs.
> >
> > I think the best course of action here is to split this patch up and
> > we can then think about each custom feature/CSR and accept some
> > depending on how intrusive they are into the QEMU code. It will also
> > have to be added in a way that allows other implementations to have
> > different custom CSRs. We (the QEMU RISC-V community) can help you
> > with this.
> >
> > Alistair
> >
>
> Thanks for your comment. About customized csr, I have a rough idea,
> whether it is possible to open the interface for manufacturers to allow
> them to implement their own csr.To implement the registration callback
> interface, add a branch to the riscv_csrrw function, and define a switch
> for the cpu. When a custom csr is supported, the vendor registration is
> preferred.The manufacturer maintains its own csr and does not invade the
> qemu code much. Of course, there may be some unknown security and
> stability issues.
That sounds like a great idea!
If we could contain all vendor changes to a single file (for example a
nuclei.c file in target/riscv/) I think it would be much easier to
maintain.
Do you want to start by adding something like that in the next patch series?
Alistair
>
> Regards
> Wang Junqiang
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-11 3:43 ` Alistair Francis
@ 2021-05-11 4:00 ` Wang Junqiang
2021-05-11 4:03 ` Alistair Francis
0 siblings, 1 reply; 16+ messages in thread
From: Wang Junqiang @ 2021-05-11 4:00 UTC (permalink / raw)
To: Alistair Francis
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On 2021/5/11 上午11:43, Alistair Francis wrote:
> On Tue, May 11, 2021 at 1:14 PM Wang Junqiang <wangjunqiang@iscas.ac.cn> wrote:
>>
>>
>>
>> On 2021/5/10 上午10:17, Alistair Francis wrote:
>>> C isOn Fri, May 7, 2021 at 11:25 PM wangjunqiang
>>> <wangjunqiang@iscas.ac.cn> wrote:
>>>>
>>>> This patch adds Nuclei CSR support for ECLIC and update the
>>>> related interrupt handling.
>>>>
>>>> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
>>>
>>> Hello,
>>>
>>> Thanks for the patches!
>>>
>>> This patch is very long and you will need to split it up before it can
>>> be merged. I understand this is just an RFC, but it's still best to
>>> start with small patches. Generally each patch should add a feature
>>> and it seems like you have added lots of features in this patch. This
>>> patch could probably be broken into at least 4 different patches.
>>>
>>> As well as that you will want to ensure that your commit message and
>>> description explains what you are doing in that patch and in some
>>> cases justify the change. For example adding a new CPU doesn't need a
>>> justification (as that's easy for me to understand), but changing some
>>> existing code might need an explanation of why we need/want that
>>> change.
>>>
>>> This is still a great start though! I look forward to your future patches.
>>>
>>> I have left a few comments below as well.
>>
>> Thank you for your reply and comments.I will split it into small patches
>> by feature in next version.And add more detailed description. To make a
>> brief explanation, add cpu here to simplify the command line when using
>> -cpu.
>>
>>>
>>>> ---
>>>> target/riscv/cpu.c | 25 +-
>>>> target/riscv/cpu.h | 42 ++-
>>>> target/riscv/cpu_bits.h | 37 +++
>>>> target/riscv/cpu_helper.c | 80 +++++-
>>>> target/riscv/csr.c | 347 +++++++++++++++++++++++-
>>>> target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
>>>> target/riscv/op_helper.c | 14 +
>>>> 7 files changed, 552 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>>>> index 7d6ed80f6b..b2a96effbc 100644
>>>> --- a/target/riscv/cpu.c
>>>> +++ b/target/riscv/cpu.c
>>>> @@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
>>>> set_priv_version(env, PRIV_VERSION_1_10_0);
>>>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>>>> }
>>>> +
>>>> +static void rv64imafdcu_nuclei_cpu_init(Object *obj)
>>>> +{
>>>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
>>>> + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
>>>> + set_priv_version(env, PRIV_VERSION_1_10_0);
>>>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>>>> + set_resetvec(env, DEFAULT_RSTVEC);
>>>> + set_feature(env, RISCV_FEATURE_PMP);
>>>> +}
>>>> #else
>>>> static void rv32_base_cpu_init(Object *obj)
>>>> {
>>>> @@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
>>>> set_resetvec(env, DEFAULT_RSTVEC);
>>>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>>>> }
>>>> +
>>>> +static void rv32imafdcu_nuclei_cpu_init(Object *obj)
>>>> +{
>>>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
>>>> + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
>>>> + set_priv_version(env, PRIV_VERSION_1_10_0);
>>>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
>>>> + set_resetvec(env, DEFAULT_RSTVEC);
>>>> + set_feature(env, RISCV_FEATURE_PMP);
>>>> +}
>>>> #endif
>>>>
>>>> static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
>>>> @@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
>>>> * Definition of the WFI instruction requires it to ignore the privilege
>>>> * mode and delegation registers, but respect individual enables
>>>> */
>>>> - return (env->mip & env->mie) != 0;
>>>> + return ((env->mip & env->mie) != 0 || (env->exccode != -1));
>>>
>>> This change for example needs to be explained, I'm not sure what exccode is
>>>
>>>> #else
>>>> return true;
>>>> #endif
>>>> @@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
>>>> env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
>>>> env->mcause = 0;
>>>> env->pc = env->resetvec;
>>>> + env->exccode = -1;
>>>> env->two_stage_lookup = false;
>>>> #endif
>>>> cs->exception_index = EXCP_NONE;
>>>> @@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
>>>> 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),
>>>> + DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
>>>> #elif defined(TARGET_RISCV64)
>>>> DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
>>>> #endif
>>>> };
>>>>
>>>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>>> index 0a33d387ba..1d3a1986a6 100644
>>>> --- a/target/riscv/cpu.h
>>>> +++ b/target/riscv/cpu.h
>>>> @@ -33,6 +33,7 @@
>>>> #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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
>>>>
>>>> #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
>>>> #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
>>>> @@ -43,6 +44,8 @@
>>>> #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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
>>>> +#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
>>>>
>>>> #if defined(TARGET_RISCV32)
>>>> # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
>>>> @@ -80,7 +83,8 @@
>>>> enum {
>>>> RISCV_FEATURE_MMU,
>>>> RISCV_FEATURE_PMP,
>>>> - RISCV_FEATURE_MISA
>>>> + RISCV_FEATURE_MISA,
>>>> + RISCV_FEATURE_ECLIC
>>>
>>> The same here, what is ECLIC? The ECLIC should be added in a seperate patch.
>>>
>>
>> ECLIC is Enhanced Core Local Interrupt Controller.And added some
>> customized csr on the basis of clic to speed up Tail-Chaining processing.
>>
>> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
>>
>>>> };
>>>>
>>>> #define PRIV_VERSION_1_10_0 0x00011000
>>>> @@ -174,10 +178,34 @@ struct CPURISCVState {
>>>> target_ulong scause;
>>>>
>>>> target_ulong mtvec;
>>>> + target_ulong mtvt; /* eclic */
>>>> target_ulong mepc;
>>>> target_ulong mcause;
>>>> target_ulong mtval; /* since: priv-1.10.0 */
>>>>
>>>> + target_ulong mnxti; /* eclic */
>>>> + target_ulong mintstatus; /* eclic */
>>>> + target_ulong mscratchcsw;
>>>> + target_ulong mscratchcswl;
>>>> +
>>>> + /* NMI CSR*/
>>>> + target_ulong mnvec;
>>>> + target_ulong msubm;
>>>> + target_ulong mdcause;
>>>> + target_ulong mmisc_ctl;
>>>> + target_ulong msavestatus;
>>>> + target_ulong msaveepc1;
>>>> + target_ulong msavecause1;
>>>> + target_ulong msaveepc2;
>>>> + target_ulong msavecause2;
>>>> + target_ulong msavedcause1;
>>>> + target_ulong msavedcause2;
>>>> + target_ulong pushmsubm;
>>>> + target_ulong mtvt2;
>>>> + target_ulong jalmnxti;
>>>> + target_ulong pushmcause;
>>>> + target_ulong pushmepc;
>>>
>>> What are NMI CSRs?
>>>
>>
>> Nuclei's Customized registers are used for NMI related processing
>>
>> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
>>
>>
>>>> +
>>>> /* Hypervisor CSRs */
>>>> target_ulong hstatus;
>>>> target_ulong hedeleg;
>>>> @@ -228,6 +256,9 @@ struct CPURISCVState {
>>>> uint64_t mtohost;
>>>> uint64_t timecmp;
>>>>
>>>> + /*nuclei timer comparators */
>>>> + uint64_t mtimecmp;
>>>
>>> RISC-V has a mtimecmp, does nuclei add another one?
>>>
>>
>> I will delete it, it was originally used for Shadow copy, I can move it
>> to the device
>>
>> https://doc.nucleisys.com/nuclei_spec/isa/timer.html
>>
>>>> +
>>>> /* physical memory protection */
>>>> pmp_table_t pmp_state;
>>>>
>>>> @@ -243,6 +274,13 @@ struct CPURISCVState {
>>>>
>>>> /* Fields from here on are preserved across CPU reset. */
>>>> QEMUTimer *timer; /* Internal timer */
>>>> +
>>>> + QEMUTimer *mtimer; /* Nuclei Internal timer */
>>>
>>> Why do you need a timer here just for the Nuclei CPU?
>>>
>>
>> same as above
>>
>>>> + void *eclic;
>>>> + uint32_t exccode; /* irq id: 0~11 shv: 12 */
>>>> + uint32_t eclic_flag;
>>>> +
>>>> + bool irq_pending;
>>>> };
>>>>
>>>> OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>>>> @@ -364,6 +402,8 @@ void riscv_cpu_list(void);
>>>> void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
>>>> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
>>>> uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
>>>> +void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
>>>> +void riscv_cpu_eclic_get_next_interrupt(void *eclic);
>>>> #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
>>>> void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
>>>> uint32_t arg);
>>>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>>>> index caf4599207..24ed7a99e1 100644
>>>> --- a/target/riscv/cpu_bits.h
>>>> +++ b/target/riscv/cpu_bits.h
>>>> @@ -149,6 +149,7 @@
>>>> #define CSR_MIE 0x304
>>>> #define CSR_MTVEC 0x305
>>>> #define CSR_MCOUNTEREN 0x306
>>>> +#define CSR_MTVT 0x307 /* customized */
>>>
>>> So I'm not sure what to do here. This seems to be a custom CSR just
>>> for the Nuclei that isn't part of the RISC-V spec or a draft spec.
>>>
>>> The problem is that accepting custom specs into QEMU makes it hard for
>>> us to maintain the RISC-V port. After it has been merged the
>>> maintainers now have to understand the Nuclei CPU and support it as
>>> part of the core RISC-V code.
>>>
>>> On the other hand I have seen a few CPUs that use CSRs and I don't
>>> want to not allow implementations that use custom CSRs. I think there
>>> is a compromise here. We probably don't want to support really custom
>>> features, but we probably can afford to support some extra CSRs.
>>>
>>> I think the best course of action here is to split this patch up and
>>> we can then think about each custom feature/CSR and accept some
>>> depending on how intrusive they are into the QEMU code. It will also
>>> have to be added in a way that allows other implementations to have
>>> different custom CSRs. We (the QEMU RISC-V community) can help you
>>> with this.
>>>
>>> Alistair
>>>
>>
>> Thanks for your comment. About customized csr, I have a rough idea,
>> whether it is possible to open the interface for manufacturers to allow
>> them to implement their own csr.To implement the registration callback
>> interface, add a branch to the riscv_csrrw function, and define a switch
>> for the cpu. When a custom csr is supported, the vendor registration is
>> preferred.The manufacturer maintains its own csr and does not invade the
>> qemu code much. Of course, there may be some unknown security and
>> stability issues.
>
> That sounds like a great idea!
>
> If we could contain all vendor changes to a single file (for example a
> nuclei.c file in target/riscv/) I think it would be much easier to
> maintain.
>
> Do you want to start by adding something like that in the next patch series?
>
> Alistair
>
OK, I will try to do it and separate the custom CSR of nuclei into the
new file.
>>
>> Regards
>> Wang Junqiang
>>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling
2021-05-11 4:00 ` Wang Junqiang
@ 2021-05-11 4:03 ` Alistair Francis
0 siblings, 0 replies; 16+ messages in thread
From: Alistair Francis @ 2021-05-11 4:03 UTC (permalink / raw)
To: Wang Junqiang
Cc: liweiwei, open list:RISC-V, Bin Meng,
qemu-devel@nongnu.org Developers, Alistair Francis, alapha23,
Palmer Dabbelt
On Tue, May 11, 2021 at 2:01 PM Wang Junqiang <wangjunqiang@iscas.ac.cn> wrote:
>
>
>
> On 2021/5/11 上午11:43, Alistair Francis wrote:
> > On Tue, May 11, 2021 at 1:14 PM Wang Junqiang <wangjunqiang@iscas.ac.cn> wrote:
> >>
> >>
> >>
> >> On 2021/5/10 上午10:17, Alistair Francis wrote:
> >>> C isOn Fri, May 7, 2021 at 11:25 PM wangjunqiang
> >>> <wangjunqiang@iscas.ac.cn> wrote:
> >>>>
> >>>> This patch adds Nuclei CSR support for ECLIC and update the
> >>>> related interrupt handling.
> >>>>
> >>>> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
> >>>
> >>> Hello,
> >>>
> >>> Thanks for the patches!
> >>>
> >>> This patch is very long and you will need to split it up before it can
> >>> be merged. I understand this is just an RFC, but it's still best to
> >>> start with small patches. Generally each patch should add a feature
> >>> and it seems like you have added lots of features in this patch. This
> >>> patch could probably be broken into at least 4 different patches.
> >>>
> >>> As well as that you will want to ensure that your commit message and
> >>> description explains what you are doing in that patch and in some
> >>> cases justify the change. For example adding a new CPU doesn't need a
> >>> justification (as that's easy for me to understand), but changing some
> >>> existing code might need an explanation of why we need/want that
> >>> change.
> >>>
> >>> This is still a great start though! I look forward to your future patches.
> >>>
> >>> I have left a few comments below as well.
> >>
> >> Thank you for your reply and comments.I will split it into small patches
> >> by feature in next version.And add more detailed description. To make a
> >> brief explanation, add cpu here to simplify the command line when using
> >> -cpu.
> >>
> >>>
> >>>> ---
> >>>> target/riscv/cpu.c | 25 +-
> >>>> target/riscv/cpu.h | 42 ++-
> >>>> target/riscv/cpu_bits.h | 37 +++
> >>>> target/riscv/cpu_helper.c | 80 +++++-
> >>>> target/riscv/csr.c | 347 +++++++++++++++++++++++-
> >>>> target/riscv/insn_trans/trans_rvi.c.inc | 16 +-
> >>>> target/riscv/op_helper.c | 14 +
> >>>> 7 files changed, 552 insertions(+), 9 deletions(-)
> >>>>
> >>>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> >>>> index 7d6ed80f6b..b2a96effbc 100644
> >>>> --- a/target/riscv/cpu.c
> >>>> +++ b/target/riscv/cpu.c
> >>>> @@ -173,6 +173,16 @@ static void rv64_sifive_e_cpu_init(Object *obj)
> >>>> set_priv_version(env, PRIV_VERSION_1_10_0);
> >>>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >>>> }
> >>>> +
> >>>> +static void rv64imafdcu_nuclei_cpu_init(Object *obj)
> >>>> +{
> >>>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> >>>> + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> >>>> + set_priv_version(env, PRIV_VERSION_1_10_0);
> >>>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >>>> + set_resetvec(env, DEFAULT_RSTVEC);
> >>>> + set_feature(env, RISCV_FEATURE_PMP);
> >>>> +}
> >>>> #else
> >>>> static void rv32_base_cpu_init(Object *obj)
> >>>> {
> >>>> @@ -212,6 +222,16 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
> >>>> set_resetvec(env, DEFAULT_RSTVEC);
> >>>> qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >>>> }
> >>>> +
> >>>> +static void rv32imafdcu_nuclei_cpu_init(Object *obj)
> >>>> +{
> >>>> + CPURISCVState *env = &RISCV_CPU(obj)->env;
> >>>> + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
> >>>> + set_priv_version(env, PRIV_VERSION_1_10_0);
> >>>> + qdev_prop_set_bit(DEVICE(obj), "mmu", false);
> >>>> + set_resetvec(env, DEFAULT_RSTVEC);
> >>>> + set_feature(env, RISCV_FEATURE_PMP);
> >>>> +}
> >>>> #endif
> >>>>
> >>>> static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
> >>>> @@ -331,7 +351,7 @@ static bool riscv_cpu_has_work(CPUState *cs)
> >>>> * Definition of the WFI instruction requires it to ignore the privilege
> >>>> * mode and delegation registers, but respect individual enables
> >>>> */
> >>>> - return (env->mip & env->mie) != 0;
> >>>> + return ((env->mip & env->mie) != 0 || (env->exccode != -1));
> >>>
> >>> This change for example needs to be explained, I'm not sure what exccode is
> >>>
> >>>> #else
> >>>> return true;
> >>>> #endif
> >>>> @@ -356,6 +376,7 @@ static void riscv_cpu_reset(DeviceState *dev)
> >>>> env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
> >>>> env->mcause = 0;
> >>>> env->pc = env->resetvec;
> >>>> + env->exccode = -1;
> >>>> env->two_stage_lookup = false;
> >>>> #endif
> >>>> cs->exception_index = EXCP_NONE;
> >>>> @@ -704,10 +725,12 @@ static const TypeInfo riscv_cpu_type_infos[] = {
> >>>> 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),
> >>>> + DEFINE_CPU(TYPE_RISCV_CPU_NUCLEI_N307FD, rv32imafdcu_nuclei_cpu_init),
> >>>> #elif defined(TARGET_RISCV64)
> >>>> DEFINE_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_NUCLEI_NX600FD, rv64imafdcu_nuclei_cpu_init),
> >>>> #endif
> >>>> };
> >>>>
> >>>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> >>>> index 0a33d387ba..1d3a1986a6 100644
> >>>> --- a/target/riscv/cpu.h
> >>>> +++ b/target/riscv/cpu.h
> >>>> @@ -33,6 +33,7 @@
> >>>> #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 CPU_INTERRUPT_ECLIC CPU_INTERRUPT_TGT_EXT_0
> >>>>
> >>>> #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
> >>>> #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
> >>>> @@ -43,6 +44,8 @@
> >>>> #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_NUCLEI_N307FD RISCV_CPU_TYPE_NAME("nuclei-n307fd")
> >>>> +#define TYPE_RISCV_CPU_NUCLEI_NX600FD RISCV_CPU_TYPE_NAME("nuclei-nx600fd")
> >>>>
> >>>> #if defined(TARGET_RISCV32)
> >>>> # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
> >>>> @@ -80,7 +83,8 @@
> >>>> enum {
> >>>> RISCV_FEATURE_MMU,
> >>>> RISCV_FEATURE_PMP,
> >>>> - RISCV_FEATURE_MISA
> >>>> + RISCV_FEATURE_MISA,
> >>>> + RISCV_FEATURE_ECLIC
> >>>
> >>> The same here, what is ECLIC? The ECLIC should be added in a seperate patch.
> >>>
> >>
> >> ECLIC is Enhanced Core Local Interrupt Controller.And added some
> >> customized csr on the basis of clic to speed up Tail-Chaining processing.
> >>
> >> https://doc.nucleisys.com/nuclei_spec/isa/eclic.html
> >>
> >>>> };
> >>>>
> >>>> #define PRIV_VERSION_1_10_0 0x00011000
> >>>> @@ -174,10 +178,34 @@ struct CPURISCVState {
> >>>> target_ulong scause;
> >>>>
> >>>> target_ulong mtvec;
> >>>> + target_ulong mtvt; /* eclic */
> >>>> target_ulong mepc;
> >>>> target_ulong mcause;
> >>>> target_ulong mtval; /* since: priv-1.10.0 */
> >>>>
> >>>> + target_ulong mnxti; /* eclic */
> >>>> + target_ulong mintstatus; /* eclic */
> >>>> + target_ulong mscratchcsw;
> >>>> + target_ulong mscratchcswl;
> >>>> +
> >>>> + /* NMI CSR*/
> >>>> + target_ulong mnvec;
> >>>> + target_ulong msubm;
> >>>> + target_ulong mdcause;
> >>>> + target_ulong mmisc_ctl;
> >>>> + target_ulong msavestatus;
> >>>> + target_ulong msaveepc1;
> >>>> + target_ulong msavecause1;
> >>>> + target_ulong msaveepc2;
> >>>> + target_ulong msavecause2;
> >>>> + target_ulong msavedcause1;
> >>>> + target_ulong msavedcause2;
> >>>> + target_ulong pushmsubm;
> >>>> + target_ulong mtvt2;
> >>>> + target_ulong jalmnxti;
> >>>> + target_ulong pushmcause;
> >>>> + target_ulong pushmepc;
> >>>
> >>> What are NMI CSRs?
> >>>
> >>
> >> Nuclei's Customized registers are used for NMI related processing
> >>
> >> https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html
> >>
> >>
> >>>> +
> >>>> /* Hypervisor CSRs */
> >>>> target_ulong hstatus;
> >>>> target_ulong hedeleg;
> >>>> @@ -228,6 +256,9 @@ struct CPURISCVState {
> >>>> uint64_t mtohost;
> >>>> uint64_t timecmp;
> >>>>
> >>>> + /*nuclei timer comparators */
> >>>> + uint64_t mtimecmp;
> >>>
> >>> RISC-V has a mtimecmp, does nuclei add another one?
> >>>
> >>
> >> I will delete it, it was originally used for Shadow copy, I can move it
> >> to the device
> >>
> >> https://doc.nucleisys.com/nuclei_spec/isa/timer.html
> >>
> >>>> +
> >>>> /* physical memory protection */
> >>>> pmp_table_t pmp_state;
> >>>>
> >>>> @@ -243,6 +274,13 @@ struct CPURISCVState {
> >>>>
> >>>> /* Fields from here on are preserved across CPU reset. */
> >>>> QEMUTimer *timer; /* Internal timer */
> >>>> +
> >>>> + QEMUTimer *mtimer; /* Nuclei Internal timer */
> >>>
> >>> Why do you need a timer here just for the Nuclei CPU?
> >>>
> >>
> >> same as above
> >>
> >>>> + void *eclic;
> >>>> + uint32_t exccode; /* irq id: 0~11 shv: 12 */
> >>>> + uint32_t eclic_flag;
> >>>> +
> >>>> + bool irq_pending;
> >>>> };
> >>>>
> >>>> OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> >>>> @@ -364,6 +402,8 @@ void riscv_cpu_list(void);
> >>>> void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
> >>>> int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
> >>>> uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
> >>>> +void riscv_cpu_eclic_clean_pending(void *eclic, int irq);
> >>>> +void riscv_cpu_eclic_get_next_interrupt(void *eclic);
> >>>> #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
> >>>> void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
> >>>> uint32_t arg);
> >>>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> >>>> index caf4599207..24ed7a99e1 100644
> >>>> --- a/target/riscv/cpu_bits.h
> >>>> +++ b/target/riscv/cpu_bits.h
> >>>> @@ -149,6 +149,7 @@
> >>>> #define CSR_MIE 0x304
> >>>> #define CSR_MTVEC 0x305
> >>>> #define CSR_MCOUNTEREN 0x306
> >>>> +#define CSR_MTVT 0x307 /* customized */
> >>>
> >>> So I'm not sure what to do here. This seems to be a custom CSR just
> >>> for the Nuclei that isn't part of the RISC-V spec or a draft spec.
> >>>
> >>> The problem is that accepting custom specs into QEMU makes it hard for
> >>> us to maintain the RISC-V port. After it has been merged the
> >>> maintainers now have to understand the Nuclei CPU and support it as
> >>> part of the core RISC-V code.
> >>>
> >>> On the other hand I have seen a few CPUs that use CSRs and I don't
> >>> want to not allow implementations that use custom CSRs. I think there
> >>> is a compromise here. We probably don't want to support really custom
> >>> features, but we probably can afford to support some extra CSRs.
> >>>
> >>> I think the best course of action here is to split this patch up and
> >>> we can then think about each custom feature/CSR and accept some
> >>> depending on how intrusive they are into the QEMU code. It will also
> >>> have to be added in a way that allows other implementations to have
> >>> different custom CSRs. We (the QEMU RISC-V community) can help you
> >>> with this.
> >>>
> >>> Alistair
> >>>
> >>
> >> Thanks for your comment. About customized csr, I have a rough idea,
> >> whether it is possible to open the interface for manufacturers to allow
> >> them to implement their own csr.To implement the registration callback
> >> interface, add a branch to the riscv_csrrw function, and define a switch
> >> for the cpu. When a custom csr is supported, the vendor registration is
> >> preferred.The manufacturer maintains its own csr and does not invade the
> >> qemu code much. Of course, there may be some unknown security and
> >> stability issues.
> >
> > That sounds like a great idea!
> >
> > If we could contain all vendor changes to a single file (for example a
> > nuclei.c file in target/riscv/) I think it would be much easier to
> > maintain.
> >
> > Do you want to start by adding something like that in the next patch series?
> >
> > Alistair
> >
>
> OK, I will try to do it and separate the custom CSR of nuclei into the
> new file.
Great! Feel free to reach out if you would like help.
Alistair
>
> >>
> >> Regards
> >> Wang Junqiang
> >>
> >
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2021-05-11 4:05 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-07 8:16 [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 1/5] target/riscv: Add Nuclei CSR and Update interrupt handling wangjunqiang
2021-05-10 2:17 ` Alistair Francis
2021-05-11 3:14 ` Wang Junqiang
2021-05-11 3:43 ` Alistair Francis
2021-05-11 4:00 ` Wang Junqiang
2021-05-11 4:03 ` Alistair Francis
2021-05-07 8:16 ` [RFC PATCH 2/5] hw/intc: Add Nuclei ECLIC device wangjunqiang
2021-05-10 2:20 ` Alistair Francis
2021-05-10 2:27 ` Bin Meng
2021-05-10 5:26 ` Bin Meng
2021-05-11 3:18 ` Wang Junqiang
2021-05-07 8:16 ` [RFC PATCH 3/5] hw/intc: Add Nuclei Systimer wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 4/5] hw/char: Add Nuclei Uart wangjunqiang
2021-05-07 8:16 ` [RFC PATCH 5/5] Nuclei FPGA Evaluation Kit MCU Machine wangjunqiang
2021-05-07 13:33 ` [RFC PATCH 0/5] RISC-V:support Nuclei FPGA Evaluation Kit no-reply
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).