qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [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).