* [PATCH v5 0/5] Add support for K230 board
@ 2026-01-30 7:19 Chao Liu
2026-01-30 7:19 ` [PATCH v5 1/5] target/riscv: add thead-c908 cpu support Chao Liu
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:19 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Fabiano Rosas, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches
Hi Alistair,
This patch series adds support for U-Boot + OpenSBI + standard Linux kernel on
K230 board. Thanks to Peng Jiang, Mig Yang, Renzao Ren, Yao Zi for their help.
This v5 series has been rebased on Alistair's riscv-to-apply.next branch [1]
and is ready for review.
Test command:
```
$QEMU -M k230 -bios [OpenSBI binary] -m 2G -dtb k230_canmv.dtb -kernel [img]
```
PATCH v5 changelog:
- Rebased on Alistair's riscv-to-apply.next branch.
- Patch 2: Fixed reset vector ROM jump to trap-handler bug.
PATCH v4 changelog:
- Rebased on the latest master branch.
- No functional changes from v3.
PATCH v3 changelog:
- Patch 1: Align T-Head C908 CPU's RISC-V extension with XUANTIE-QEMU.
- Patch 2: Adjust PLIC and CLINT addresses to match K230 datasheet.
PATCH v2 changelog:
- Patch 1: Add Svpbmt extension support for the T-Head C908 CPU.
- Patch 2: Move the k230.rst definition from MAINTAINERS to Patch 5.
- Patch 5: Apply Daniel's bugfix to build the k230 documentation successfully.
PATCH v1 changelog:
- Patch1: Add T-Head C908 and C908v CPU support.
- Patch2: Add K230 board initial support(big core is not supported yet).
- Patch3: Add Programmable Watchdog Timer (WDT) peripheral support.
- Patch4: Add QEMU test for K230 watchdog.
- Patch5: Add documentation for K230 machine.
[1] https://github.com/alistair23/qemu/tree/riscv-to-apply.next
Thanks,
Chao
Chao Liu (5):
target/riscv: add thead-c908 cpu support
hw/riscv: add k230 board initial support
hw/watchdog: add k230 watchdog initial support
tests/qtest: add test for K230 watchdog
docs/system/riscv: add documentation for k230 machine
MAINTAINERS | 12 +-
docs/system/riscv/k230.rst | 48 ++++
docs/system/target-riscv.rst | 1 +
hw/riscv/Kconfig | 11 +
hw/riscv/k230.c | 502 +++++++++++++++++++++++++++++++++
hw/riscv/meson.build | 2 +-
hw/watchdog/Kconfig | 4 +
hw/watchdog/k230_wdt.c | 307 ++++++++++++++++++++
hw/watchdog/meson.build | 1 +
hw/watchdog/trace-events | 9 +
include/hw/riscv/k230.h | 154 ++++++++++
include/hw/watchdog/k230_wdt.h | 132 +++++++++
target/riscv/cpu-qom.h | 2 +
target/riscv/cpu.c | 51 ++++
target/riscv/th_csr.c | 380 ++++++++++++++++++++++++-
tests/qtest/k230-wdt-test.c | 199 +++++++++++++
tests/qtest/meson.build | 3 +-
17 files changed, 1814 insertions(+), 4 deletions(-)
create mode 100644 docs/system/riscv/k230.rst
create mode 100644 hw/riscv/k230.c
create mode 100644 hw/watchdog/k230_wdt.c
create mode 100644 include/hw/riscv/k230.h
create mode 100644 include/hw/watchdog/k230_wdt.h
create mode 100644 tests/qtest/k230-wdt-test.c
--
2.52.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v5 1/5] target/riscv: add thead-c908 cpu support
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
@ 2026-01-30 7:19 ` Chao Liu
2026-01-30 7:19 ` [PATCH v5 2/5] hw/riscv: add k230 board initial support Chao Liu
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:19 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Fabiano Rosas, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu,
Conor Dooley, Peng Jiang
From: Chao Liu <chao.liu@zevorn.cn>
The C908 processor is based on the RV64GCB[V] instruction
set, compatible to RVA22 Profile and implements the XIE
(XuanTie Instruction Extension) technology.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Suggested-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Conor Dooley <conor@kernel.org>
Reported-by: Peng Jiang <3160104094@zju.edu.cn>
---
target/riscv/cpu-qom.h | 2 +
target/riscv/cpu.c | 51 ++++++
target/riscv/th_csr.c | 380 ++++++++++++++++++++++++++++++++++++++++-
3 files changed, 432 insertions(+), 1 deletion(-)
diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
index 30dcdcfaae..1a28f1369c 100644
--- a/target/riscv/cpu-qom.h
+++ b/target/riscv/cpu-qom.h
@@ -52,6 +52,8 @@
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
#define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906")
+#define TYPE_RISCV_CPU_THEAD_C908 RISCV_CPU_TYPE_NAME("thead-c908")
+#define TYPE_RISCV_CPU_THEAD_C908V RISCV_CPU_TYPE_NAME("thead-c908v")
#define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1")
#define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon")
#define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu")
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index fa7079d86e..aa3ac557e1 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3134,6 +3134,57 @@ static const TypeInfo riscv_cpu_type_infos[] = {
#endif
),
+ DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C908, TYPE_RISCV_VENDOR_CPU,
+ .misa_mxl_max = MXL_RV64,
+ .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU,
+ .priv_spec = PRIV_VERSION_1_12_0,
+
+ /* ISA extensions */
+ .cfg.ext_xtheadba = true,
+ .cfg.ext_xtheadbb = true,
+ .cfg.ext_xtheadbs = true,
+ .cfg.ext_xtheadcmo = true,
+ .cfg.ext_xtheadcondmov = true,
+ .cfg.ext_xtheadfmv = true,
+ .cfg.ext_xtheadfmemidx = true,
+ .cfg.ext_xtheadmac = true,
+ .cfg.ext_xtheadmemidx = true,
+ .cfg.ext_xtheadmempair = true,
+ .cfg.ext_xtheadsync = true,
+ .cfg.ext_smepmp = true,
+ .cfg.ext_sscofpmf = true,
+ .cfg.ext_sstc = true,
+ .cfg.ext_svpbmt = true,
+ .cfg.ext_svinval = true,
+ .cfg.ext_svnapot = true,
+ .cfg.ext_zba = true,
+ .cfg.ext_zbb = true,
+ .cfg.ext_zbc = true,
+ .cfg.ext_zbs = true,
+ .cfg.ext_zkt = true,
+ .cfg.ext_zbkc = true,
+ .cfg.ext_zicsr = true,
+ .cfg.ext_zifencei = true,
+ .cfg.ext_zihintpause = true,
+ .cfg.ext_zicbom = true,
+ .cfg.ext_zicboz = true,
+
+ .cfg.pmp = true,
+ .cfg.mmu = true,
+ .cfg.max_satp_mode = VM_1_10_SV48,
+
+ .cfg.marchid = 0x8d143000,
+ .cfg.mvendorid = THEAD_VENDOR_ID,
+#ifndef CONFIG_USER_ONLY
+ .custom_csrs = th_csr_list,
+#endif
+ ),
+
+ DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C908V, TYPE_RISCV_CPU_THEAD_C908,
+ .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU | RVV,
+ .vext_spec = VEXT_VERSION_1_00_0,
+ ),
+
DEFINE_RISCV_CPU(TYPE_RISCV_CPU_TT_ASCALON, TYPE_RISCV_VENDOR_CPU,
.misa_mxl_max = MXL_RV64,
.misa_ext = RVG | RVC | RVS | RVU | RVH | RVV,
diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c
index 49eb7bbab5..e19cab5414 100644
--- a/target/riscv/th_csr.c
+++ b/target/riscv/th_csr.c
@@ -2,6 +2,9 @@
* T-Head-specific CSRs.
*
* Copyright (c) 2024 VRULL GmbH
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * For more information, see XuanTie-C908-UserManual_xrvm_20240530.pdf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -20,13 +23,88 @@
#include "cpu.h"
#include "cpu_vendorid.h"
-#define CSR_TH_SXSTATUS 0x5c0
+/* Extended M-mode control registers of T-Head */
+#define CSR_TH_MXSTATUS 0x7c0
+#define CSR_TH_MHCR 0x7c1
+#define CSR_TH_MCOR 0x7c2
+#define CSR_TH_MCCR2 0x7c3
+#define CSR_TH_MHINT 0x7c5
+#define CSR_TH_MRVBR 0x7c7
+#define CSR_TH_MCOUNTERWEN 0x7c9
+#define CSR_TH_MCOUNTERINTEN 0x7ca
+#define CSR_TH_MCOUNTEROF 0x7cb
+#define CSR_TH_MCINS 0x7d2
+#define CSR_TH_MCINDEX 0x7d3
+#define CSR_TH_MCDATA0 0x7d4
+#define CSR_TH_MCDATA1 0x7d5
+#define CSR_TH_MSMPR 0x7f3
+#define CSR_TH_CPUID 0xfc0
+#define CSR_TH_MAPBADDR 0xfc1
+
+/* TH_MXSTATUS bits */
+#define TH_MXSTATUS_UCME BIT(16)
+#define TH_MXSTATUS_MAEE BIT(21)
+#define TH_MXSTATUS_THEADISAEE BIT(22)
+
+/* Extended S-mode control registers of T-Head */
+#define CSR_TH_SXSTATUS 0x5c0
+#define CSR_TH_SHCR 0x5c1
+#define CSR_TH_SCER2 0x5c2
+#define CSR_TH_SCER 0x5c3
+#define CSR_TH_SCOUNTERINTEN 0x5c4
+#define CSR_TH_SCOUNTEROF 0x5c5
+#define CSR_TH_SCYCLE 0x5e0
+#define CSR_TH_SHPMCOUNTER3 0x5e3
+#define CSR_TH_SHPMCOUNTER4 0x5e4
+#define CSR_TH_SHPMCOUNTER5 0x5e5
+#define CSR_TH_SHPMCOUNTER6 0x5e6
+#define CSR_TH_SHPMCOUNTER7 0x5e7
+#define CSR_TH_SHPMCOUNTER8 0x5e8
+#define CSR_TH_SHPMCOUNTER9 0x5e9
+#define CSR_TH_SHPMCOUNTER10 0x5ea
+#define CSR_TH_SHPMCOUNTER11 0x5eb
+#define CSR_TH_SHPMCOUNTER12 0x5ec
+#define CSR_TH_SHPMCOUNTER13 0x5ed
+#define CSR_TH_SHPMCOUNTER14 0x5ee
+#define CSR_TH_SHPMCOUNTER15 0x5ef
+#define CSR_TH_SHPMCOUNTER16 0x5f0
+#define CSR_TH_SHPMCOUNTER17 0x5f1
+#define CSR_TH_SHPMCOUNTER18 0x5f2
+#define CSR_TH_SHPMCOUNTER19 0x5f3
+#define CSR_TH_SHPMCOUNTER20 0x5f4
+#define CSR_TH_SHPMCOUNTER21 0x5f5
+#define CSR_TH_SHPMCOUNTER22 0x5f6
+#define CSR_TH_SHPMCOUNTER23 0x5f7
+#define CSR_TH_SHPMCOUNTER24 0x5f8
+#define CSR_TH_SHPMCOUNTER25 0x5f9
+#define CSR_TH_SHPMCOUNTER26 0x5fa
+#define CSR_TH_SHPMCOUNTER27 0x5fb
+#define CSR_TH_SHPMCOUNTER28 0x5fc
+#define CSR_TH_SHPMCOUNTER29 0x5fd
+#define CSR_TH_SHPMCOUNTER30 0x5fe
+#define CSR_TH_SHPMCOUNTER31 0x5ff
+#define CSR_TH_SMIR 0x9c0
+#define CSR_TH_SMLO0 0x9c1
+#define CSR_TH_SMEH 0x9c2
+#define CSR_TH_SMCIR 0x9c3
+
+/* Extended U-mode control registers of T-Head */
+#define CSR_TH_FXCR 0x800
/* TH_SXSTATUS bits */
#define TH_SXSTATUS_UCME BIT(16)
#define TH_SXSTATUS_MAEE BIT(21)
#define TH_SXSTATUS_THEADISAEE BIT(22)
+static RISCVException mmode(CPURISCVState *env, int csrno)
+{
+ if (riscv_has_ext(env, RVM)) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
+
static RISCVException smode(CPURISCVState *env, int csrno)
{
if (riscv_has_ext(env, RVS)) {
@@ -36,11 +114,31 @@ static RISCVException smode(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+static RISCVException any(CPURISCVState *env, int csrno)
+{
+ return RISCV_EXCP_NONE;
+}
+
static bool test_thead_mvendorid(RISCVCPU *cpu)
{
return cpu->cfg.mvendorid == THEAD_VENDOR_ID;
}
+static RISCVException read_th_mxstatus(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ /* We don't set MAEE here, because QEMU does not implement MAEE. */
+ *val = TH_MXSTATUS_UCME | TH_MXSTATUS_THEADISAEE;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_unimp_th_csr(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = 0;
+ return RISCV_EXCP_NONE;
+}
+
static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
target_ulong *val)
{
@@ -50,10 +148,290 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
}
const RISCVCSR th_csr_list[] = {
+ {
+ .csrno = CSR_TH_MXSTATUS,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mxstatus", mmode, read_th_mxstatus }
+ },
+ {
+ .csrno = CSR_TH_MHCR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mhcr", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCOR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcor", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCCR2,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mccr2", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MHINT,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mhint", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MRVBR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mrvbr", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCOUNTERWEN,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcounterwen", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCOUNTERINTEN,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcounterinten", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCOUNTEROF,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcounterof", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCINS,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcins", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCINDEX,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcindex", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCDATA0,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcdata0", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MCDATA1,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcdata1", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MSMPR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.msmpr", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_CPUID,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.cpuid", mmode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_MAPBADDR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mapbaddr", mmode, read_unimp_th_csr }
+ },
{
.csrno = CSR_TH_SXSTATUS,
.insertion_test = test_thead_mvendorid,
.csr_ops = { "th.sxstatus", smode, read_th_sxstatus }
},
+ {
+ .csrno = CSR_TH_SHCR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shcr", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SCER2,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.scer2", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SCER,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.scer", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SCOUNTERINTEN,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.scounterinten", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SCOUNTEROF,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.scounterof", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SCYCLE,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.scycle", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER3,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter3", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER4,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter4", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER5,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter5", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER6,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter6", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER7,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter7", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER8,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter8", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER9,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter9", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER10,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter10", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER11,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter11", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER12,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter12", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER13,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter13", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER14,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter14", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER15,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter15", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER16,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter16", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER17,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter17", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER18,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter18", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER19,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter19", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER20,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter20", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER21,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter21", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER22,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter22", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER23,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter23", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER24,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter24", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER25,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter25", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER26,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter26", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER27,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter27", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER28,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter28", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER29,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter29", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER30,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter30", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SHPMCOUNTER31,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.shpmcounter31", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SMIR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.smir", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SMLO0,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.smlo0", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SMEH,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.smeh", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_SMCIR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.smcir", smode, read_unimp_th_csr }
+ },
+ {
+ .csrno = CSR_TH_FXCR,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.fxcr", any, read_unimp_th_csr }
+ },
{ }
};
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 2/5] hw/riscv: add k230 board initial support
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
2026-01-30 7:19 ` [PATCH v5 1/5] target/riscv: add thead-c908 cpu support Chao Liu
@ 2026-01-30 7:19 ` Chao Liu
2026-01-30 7:19 ` [PATCH v5 3/5] hw/watchdog: add k230 watchdog " Chao Liu
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:19 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Fabiano Rosas, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu,
Peng Jiang
From: Chao Liu <chao.liu@zevorn.cn>
K230 Board Compatible with kendryte K230 SDK.
Preliminarily supports the C908 small core, which can
run the U-Boot and Linux kernel compiled by the K230 SDK.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reported-by: Peng Jiang <3160104094@zju.edu.cn>
---
MAINTAINERS | 8 +-
hw/riscv/Kconfig | 10 +
hw/riscv/k230.c | 484 ++++++++++++++++++++++++++++++++++++++++
hw/riscv/meson.build | 2 +-
include/hw/riscv/k230.h | 150 +++++++++++++
5 files changed, 652 insertions(+), 2 deletions(-)
create mode 100644 hw/riscv/k230.c
create mode 100644 include/hw/riscv/k230.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 4ddbfba9f0..c0c56d6ce7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1758,6 +1758,12 @@ F: docs/system/riscv/xiangshan-kunminghu.rst
F: hw/riscv/xiangshan_kmh.c
F: include/hw/riscv/xiangshan_kmh.h
+K230 Machines
+M: Chao Liu <chao.liu.zevorn@gmail.com>
+S: Maintained
+F: hw/riscv/k230.c
+F: include/hw/riscv/k230.h
+
RX Machines
-----------
rx-gdbsim
@@ -3576,7 +3582,7 @@ M: Alexander Bulekov <alxndr@bu.edu>
R: Paolo Bonzini <pbonzini@redhat.com>
R: Stefan Hajnoczi <stefanha@redhat.com>
R: Fabiano Rosas <farosas@suse.de>
-R: Darren Kenny <darren.kenny@oracle.com>
+R: Darren Kenny <darren.kenny@oracle.com>
R: Qiuhao Li <Qiuhao.Li@outlook.com>
S: Maintained
F: tests/qtest/fuzz/
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 0222c93f87..b1a7357866 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -134,3 +134,13 @@ config MIPS_BOSTON_AIA
default y
select PCI_EXPRESS
select PCI_EXPRESS_XILINX
+
+config K230
+ bool
+ default y
+ depends on RISCV64
+ select RISCV_ACLINT
+ select RISCV_APLIC
+ select RISCV_IMSIC
+ select SERIAL_MM
+ select UNIMP
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
new file mode 100644
index 0000000000..2537023a05
--- /dev/null
+++ b/hw/riscv/k230.c
@@ -0,0 +1,484 @@
+/*
+ * QEMU RISC-V Virt Board Compatible with Kendryte K230 SDK
+ *
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Provides a board compatible with the Kendryte K230 SDK
+ *
+ * Documentation: K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * For more information, see <https://www.kendryte.com/en/proDetail/230>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu-qom.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "system/system.h"
+#include "system/memory.h"
+#include "target/riscv/cpu.h"
+#include "chardev/char.h"
+#include "hw/core/loader.h"
+#include "hw/core/sysbus.h"
+#include "hw/riscv/k230.h"
+#include "hw/riscv/boot.h"
+#include "hw/intc/riscv_aclint.h"
+#include "hw/intc/sifive_plic.h"
+#include "hw/char/serial-mm.h"
+#include "hw/misc/unimp.h"
+
+static const MemMapEntry memmap[] = {
+ [K230_DEV_DDRC] = { 0x00000000, 0x80000000 },
+ [K230_DEV_KPU_L2_CACHE] = { 0x80000000, 0x00200000 },
+ [K230_DEV_SRAM] = { 0x80200000, 0x00200000 },
+ [K230_DEV_KPU_CFG] = { 0x80400000, 0x00000800 },
+ [K230_DEV_FFT] = { 0x80400800, 0x00000400 },
+ [K230_DEV_AI_2D_ENGINE] = { 0x80400C00, 0x00000800 },
+ [K230_DEV_GSDMA] = { 0x80800000, 0x00004000 },
+ [K230_DEV_DMA] = { 0x80804000, 0x00004000 },
+ [K230_DEV_DECOMP_GZIP] = { 0x80808000, 0x00004000 },
+ [K230_DEV_NON_AI_2D] = { 0x8080C000, 0x00004000 },
+ [K230_DEV_ISP] = { 0x90000000, 0x00008000 },
+ [K230_DEV_DEWARP] = { 0x90008000, 0x00001000 },
+ [K230_DEV_RX_CSI] = { 0x90009000, 0x00002000 },
+ [K230_DEV_H264] = { 0x90400000, 0x00010000 },
+ [K230_DEV_2P5D] = { 0x90800000, 0x00040000 },
+ [K230_DEV_VO] = { 0x90840000, 0x00010000 },
+ [K230_DEV_VO_CFG] = { 0x90850000, 0x00001000 },
+ [K230_DEV_3D_ENGINE] = { 0x90A00000, 0x00000800 },
+ [K230_DEV_PMU] = { 0x91000000, 0x00000C00 },
+ [K230_DEV_RTC] = { 0x91000C00, 0x00000400 },
+ [K230_DEV_CMU] = { 0x91100000, 0x00001000 },
+ [K230_DEV_RMU] = { 0x91101000, 0x00001000 },
+ [K230_DEV_BOOT] = { 0x91102000, 0x00001000 },
+ [K230_DEV_PWR] = { 0x91103000, 0x00001000 },
+ [K230_DEV_MAILBOX] = { 0x91104000, 0x00001000 },
+ [K230_DEV_IOMUX] = { 0x91105000, 0x00000800 },
+ [K230_DEV_TIMER] = { 0x91105800, 0x00000800 },
+ [K230_DEV_WDT0] = { 0x91106000, 0x00000800 },
+ [K230_DEV_WDT1] = { 0x91106800, 0x00000800 },
+ [K230_DEV_TS] = { 0x91107000, 0x00000800 },
+ [K230_DEV_HDI] = { 0x91107800, 0x00000800 },
+ [K230_DEV_STC] = { 0x91108000, 0x00000800 },
+ [K230_DEV_BOOTROM] = { 0x91200000, 0x00010000 },
+ [K230_DEV_SECURITY] = { 0x91210000, 0x00008000 },
+ [K230_DEV_UART0] = { 0x91400000, 0x00001000 },
+ [K230_DEV_UART1] = { 0x91401000, 0x00001000 },
+ [K230_DEV_UART2] = { 0x91402000, 0x00001000 },
+ [K230_DEV_UART3] = { 0x91403000, 0x00001000 },
+ [K230_DEV_UART4] = { 0x91404000, 0x00001000 },
+ [K230_DEV_I2C0] = { 0x91405000, 0x00001000 },
+ [K230_DEV_I2C1] = { 0x91406000, 0x00001000 },
+ [K230_DEV_I2C2] = { 0x91407000, 0x00001000 },
+ [K230_DEV_I2C3] = { 0x91408000, 0x00001000 },
+ [K230_DEV_I2C4] = { 0x91409000, 0x00001000 },
+ [K230_DEV_PWM] = { 0x9140A000, 0x00001000 },
+ [K230_DEV_GPIO0] = { 0x9140B000, 0x00001000 },
+ [K230_DEV_GPIO1] = { 0x9140C000, 0x00001000 },
+ [K230_DEV_ADC] = { 0x9140D000, 0x00001000 },
+ [K230_DEV_CODEC] = { 0x9140E000, 0x00001000 },
+ [K230_DEV_I2S] = { 0x9140F000, 0x00001000 },
+ [K230_DEV_USB0] = { 0x91500000, 0x00010000 },
+ [K230_DEV_USB1] = { 0x91540000, 0x00010000 },
+ [K230_DEV_SD0] = { 0x91580000, 0x00001000 },
+ [K230_DEV_SD1] = { 0x91581000, 0x00001000 },
+ [K230_DEV_QSPI0] = { 0x91582000, 0x00001000 },
+ [K230_DEV_QSPI1] = { 0x91583000, 0x00001000 },
+ [K230_DEV_SPI] = { 0x91584000, 0x00001000 },
+ [K230_DEV_HI_SYS_CFG] = { 0x91585000, 0x00000400 },
+ [K230_DEV_DDRC_CFG] = { 0x98000000, 0x02000000 },
+ [K230_DEV_FLASH] = { 0xC0000000, 0x08000000 },
+ [K230_DEV_PLIC] = { 0xF00000000, 0x00400000 },
+ [K230_DEV_CLINT] = { 0xF04000000, 0x00400000 },
+};
+
+static void k230_soc_init(Object *obj)
+{
+ K230SoCState *s = RISCV_K230_SOC(obj);
+ RISCVHartArrayState *cpu0 = &s->c908_cpu;
+
+ object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY);
+ qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0);
+ qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908);
+ qdev_prop_set_uint64(DEVICE(cpu0), "resetvec",
+ memmap[K230_DEV_BOOTROM].base);
+}
+
+static DeviceState *k230_create_plic(int base_hartid, int hartid_count)
+{
+ g_autofree char *plic_hart_config = NULL;
+
+ /* Per-socket PLIC hart topology configuration string */
+ plic_hart_config = riscv_plic_hart_config_string(hartid_count);
+
+ /* Per-socket PLIC */
+ return sifive_plic_create(memmap[K230_DEV_PLIC].base,
+ plic_hart_config, hartid_count, base_hartid,
+ K230_PLIC_NUM_SOURCES,
+ K230_PLIC_NUM_PRIORITIES,
+ K230_PLIC_PRIORITY_BASE, K230_PLIC_PENDING_BASE,
+ K230_PLIC_ENABLE_BASE, K230_PLIC_ENABLE_STRIDE,
+ K230_PLIC_CONTEXT_BASE,
+ K230_PLIC_CONTEXT_STRIDE,
+ memmap[K230_DEV_PLIC].size);
+}
+
+static void k230_soc_realize(DeviceState *dev, Error **errp)
+{
+ K230SoCState *s = RISCV_K230_SOC(dev);
+ MemoryRegion *sys_mem = get_system_memory();
+ int c908_cpus;
+
+ sysbus_realize(SYS_BUS_DEVICE(&s->c908_cpu), &error_fatal);
+
+ c908_cpus = s->c908_cpu.num_harts;
+
+ /* SRAM */
+ memory_region_init_ram(&s->sram, OBJECT(dev), "sram",
+ memmap[K230_DEV_SRAM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem, memmap[K230_DEV_SRAM].base,
+ &s->sram);
+
+ /* BootROM */
+ memory_region_init_rom(&s->bootrom, OBJECT(dev), "bootrom",
+ memmap[K230_DEV_BOOTROM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem, memmap[K230_DEV_BOOTROM].base,
+ &s->bootrom);
+
+ /* PLIC */
+ s->c908_plic = k230_create_plic(C908_CPU_HARTID, c908_cpus);
+
+ /* CLINT */
+ riscv_aclint_swi_create(memmap[K230_DEV_CLINT].base,
+ C908_CPU_HARTID, c908_cpus, false);
+ riscv_aclint_mtimer_create(memmap[K230_DEV_CLINT].base + 0x4000,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
+ C908_CPU_HARTID, c908_cpus,
+ RISCV_ACLINT_DEFAULT_MTIMECMP,
+ RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
+
+ /* UART */
+ serial_mm_init(sys_mem, memmap[K230_DEV_UART0].base, 2,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART0_IRQ),
+ 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+ serial_mm_init(sys_mem, memmap[K230_DEV_UART1].base, 2,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART1_IRQ),
+ 399193, serial_hd(1), DEVICE_LITTLE_ENDIAN);
+ serial_mm_init(sys_mem, memmap[K230_DEV_UART2].base, 2,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART2_IRQ),
+ 399193, serial_hd(2), DEVICE_LITTLE_ENDIAN);
+ serial_mm_init(sys_mem, memmap[K230_DEV_UART3].base, 2,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART3_IRQ),
+ 399193, serial_hd(3), DEVICE_LITTLE_ENDIAN);
+ serial_mm_init(sys_mem, memmap[K230_DEV_UART4].base, 2,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART4_IRQ),
+ 399193, serial_hd(4), DEVICE_LITTLE_ENDIAN);
+
+ /* unimplemented devices */
+ create_unimplemented_device("kpu.l2-cache",
+ memmap[K230_DEV_KPU_L2_CACHE].base,
+ memmap[K230_DEV_KPU_L2_CACHE].size);
+
+ create_unimplemented_device("kpu_cfg", memmap[K230_DEV_KPU_CFG].base,
+ memmap[K230_DEV_KPU_CFG].size);
+
+ create_unimplemented_device("fft", memmap[K230_DEV_FFT].base,
+ memmap[K230_DEV_FFT].size);
+
+ create_unimplemented_device("2d-engine.ai",
+ memmap[K230_DEV_AI_2D_ENGINE].base,
+ memmap[K230_DEV_AI_2D_ENGINE].size);
+
+ create_unimplemented_device("gsdma", memmap[K230_DEV_GSDMA].base,
+ memmap[K230_DEV_GSDMA].size);
+
+ create_unimplemented_device("dma", memmap[K230_DEV_DMA].base,
+ memmap[K230_DEV_DMA].size);
+
+ create_unimplemented_device("decomp-gzip",
+ memmap[K230_DEV_DECOMP_GZIP].base,
+ memmap[K230_DEV_DECOMP_GZIP].size);
+
+ create_unimplemented_device("2d-engine.non-ai",
+ memmap[K230_DEV_NON_AI_2D].base,
+ memmap[K230_DEV_NON_AI_2D].size);
+
+ create_unimplemented_device("isp", memmap[K230_DEV_ISP].base,
+ memmap[K230_DEV_ISP].size);
+
+ create_unimplemented_device("dewarp", memmap[K230_DEV_DEWARP].base,
+ memmap[K230_DEV_DEWARP].size);
+
+ create_unimplemented_device("rx-csi", memmap[K230_DEV_RX_CSI].base,
+ memmap[K230_DEV_RX_CSI].size);
+
+ create_unimplemented_device("vpu", memmap[K230_DEV_H264].base,
+ memmap[K230_DEV_H264].size);
+
+ create_unimplemented_device("gpu", memmap[K230_DEV_2P5D].base,
+ memmap[K230_DEV_2P5D].size);
+
+ create_unimplemented_device("vo", memmap[K230_DEV_VO].base,
+ memmap[K230_DEV_VO].size);
+
+ create_unimplemented_device("vo_cfg", memmap[K230_DEV_VO_CFG].base,
+ memmap[K230_DEV_VO_CFG].size);
+
+ create_unimplemented_device("3d-engine", memmap[K230_DEV_3D_ENGINE].base,
+ memmap[K230_DEV_3D_ENGINE].size);
+
+ create_unimplemented_device("pmu", memmap[K230_DEV_PMU].base,
+ memmap[K230_DEV_PMU].size);
+
+ create_unimplemented_device("rtc", memmap[K230_DEV_RTC].base,
+ memmap[K230_DEV_RTC].size);
+
+ create_unimplemented_device("cmu", memmap[K230_DEV_CMU].base,
+ memmap[K230_DEV_CMU].size);
+
+ create_unimplemented_device("rmu", memmap[K230_DEV_RMU].base,
+ memmap[K230_DEV_RMU].size);
+
+ create_unimplemented_device("boot", memmap[K230_DEV_BOOT].base,
+ memmap[K230_DEV_BOOT].size);
+
+ create_unimplemented_device("pwr", memmap[K230_DEV_PWR].base,
+ memmap[K230_DEV_PWR].size);
+
+ create_unimplemented_device("ipcm", memmap[K230_DEV_MAILBOX].base,
+ memmap[K230_DEV_MAILBOX].size);
+
+ create_unimplemented_device("iomux", memmap[K230_DEV_IOMUX].base,
+ memmap[K230_DEV_IOMUX].size);
+
+ create_unimplemented_device("timer", memmap[K230_DEV_TIMER].base,
+ memmap[K230_DEV_TIMER].size);
+
+ create_unimplemented_device("wdt0", memmap[K230_DEV_WDT0].base,
+ memmap[K230_DEV_WDT0].size);
+
+ create_unimplemented_device("wdt1", memmap[K230_DEV_WDT1].base,
+ memmap[K230_DEV_WDT1].size);
+
+ create_unimplemented_device("ts", memmap[K230_DEV_TS].base,
+ memmap[K230_DEV_TS].size);
+
+ create_unimplemented_device("hdi", memmap[K230_DEV_HDI].base,
+ memmap[K230_DEV_HDI].size);
+
+ create_unimplemented_device("stc", memmap[K230_DEV_STC].base,
+ memmap[K230_DEV_STC].size);
+
+ create_unimplemented_device("security", memmap[K230_DEV_SECURITY].base,
+ memmap[K230_DEV_SECURITY].size);
+
+ create_unimplemented_device("i2c0", memmap[K230_DEV_I2C0].base,
+ memmap[K230_DEV_I2C0].size);
+
+ create_unimplemented_device("i2c1", memmap[K230_DEV_I2C1].base,
+ memmap[K230_DEV_I2C1].size);
+
+ create_unimplemented_device("i2c2", memmap[K230_DEV_I2C2].base,
+ memmap[K230_DEV_I2C2].size);
+
+ create_unimplemented_device("i2c3", memmap[K230_DEV_I2C3].base,
+ memmap[K230_DEV_I2C3].size);
+
+ create_unimplemented_device("i2c4", memmap[K230_DEV_I2C4].base,
+ memmap[K230_DEV_I2C4].size);
+
+ create_unimplemented_device("pwm", memmap[K230_DEV_PWM].base,
+ memmap[K230_DEV_PWM].size);
+
+ create_unimplemented_device("gpio0", memmap[K230_DEV_GPIO0].base,
+ memmap[K230_DEV_GPIO0].size);
+
+ create_unimplemented_device("gpio1", memmap[K230_DEV_GPIO1].base,
+ memmap[K230_DEV_GPIO1].size);
+
+ create_unimplemented_device("adc", memmap[K230_DEV_ADC].base,
+ memmap[K230_DEV_ADC].size);
+
+ create_unimplemented_device("codec", memmap[K230_DEV_CODEC].base,
+ memmap[K230_DEV_CODEC].size);
+
+ create_unimplemented_device("i2s", memmap[K230_DEV_I2S].base,
+ memmap[K230_DEV_I2S].size);
+
+ create_unimplemented_device("usb0", memmap[K230_DEV_USB0].base,
+ memmap[K230_DEV_USB0].size);
+
+ create_unimplemented_device("usb1", memmap[K230_DEV_USB1].base,
+ memmap[K230_DEV_USB1].size);
+
+ create_unimplemented_device("sd0", memmap[K230_DEV_SD0].base,
+ memmap[K230_DEV_SD0].size);
+
+ create_unimplemented_device("sd1", memmap[K230_DEV_SD1].base,
+ memmap[K230_DEV_SD1].size);
+
+ create_unimplemented_device("qspi0", memmap[K230_DEV_QSPI0].base,
+ memmap[K230_DEV_QSPI0].size);
+
+ create_unimplemented_device("qspi1", memmap[K230_DEV_QSPI1].base,
+ memmap[K230_DEV_QSPI1].size);
+
+ create_unimplemented_device("spi", memmap[K230_DEV_SPI].base,
+ memmap[K230_DEV_SPI].size);
+
+ create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].base,
+ memmap[K230_DEV_HI_SYS_CFG].size);
+
+ create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base,
+ memmap[K230_DEV_DDRC_CFG].size);
+
+ create_unimplemented_device("flash", memmap[K230_DEV_FLASH].base,
+ memmap[K230_DEV_FLASH].size);
+}
+
+static void k230_soc_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = k230_soc_realize;
+}
+
+static const TypeInfo k230_soc_type_info = {
+ .name = TYPE_RISCV_K230_SOC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(K230SoCState),
+ .instance_init = k230_soc_init,
+ .class_init = k230_soc_class_init,
+};
+
+static void k230_soc_register_types(void)
+{
+ type_register_static(&k230_soc_type_info);
+}
+
+type_init(k230_soc_register_types)
+
+static void k230_machine_done(Notifier *notifier, void *data)
+{
+ K230MachineState *s = container_of(notifier, K230MachineState,
+ machine_done);
+ MachineState *machine = MACHINE(s);
+ hwaddr start_addr = memmap[K230_DEV_DDRC].base;
+ target_ulong firmware_end_addr, kernel_start_addr;
+ const char *firmware_name = riscv_default_firmware_name(&s->soc.c908_cpu);
+ uint64_t kernel_entry = 0;
+ RISCVBootInfo boot_info;
+
+ firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ &start_addr, NULL);
+
+ /* Mask ROM reset vector */
+ uint32_t reset_vec[] = {
+ /* 0x91200000: auipc t0, 0x0 */ 0x00000297,
+ /* 0x91200004: addi t0, t0, 36 # <trap> */ 0x03028293,
+ /* 0x91200008: csrw mtvec, t0 */ 0x30529073,
+ /* 0x9120000C: csrr a0, misa */ 0x301012F3,
+ /* 0x91200010: lui t0, 0x1 */ 0x000012B7,
+ /* 0x91200014: slli t0, t0, 1 */ 0x00129293,
+ /* 0x91200018: and t0, a0, t0 */ 0x005572B3,
+ /* 0x9120001C: bnez t0, loop */ 0x00511063,
+ /* entry: */
+ /* 0x91200020: addiw t0, zero, 1 */ 0x0010029b,
+ /* 0x91200024: slli t0, t0, 0x1b */ 0x01b29293,
+ /* 0x91200028: jr t0 # uboot 0x8000000 */ 0x00028067,
+ /* loop: */
+ /* 0x9120002C: j 0x9120002C # <loop> */ 0x0000006f,
+ /* trap: */
+ /* 0x91200030: j 0x91200030 # <trap> */ 0x0000006f,
+ };
+
+ /* copy in the reset vector in little_endian byte order */
+ for (int 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[K230_DEV_BOOTROM].base, &address_space_memory);
+
+ riscv_boot_info_init(&boot_info, &s->soc.c908_cpu);
+
+ if (machine->kernel_filename && !kernel_entry) {
+ kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
+ firmware_end_addr);
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr,
+ true, NULL);
+ kernel_entry = boot_info.image_low_addr;
+ }
+}
+
+static void k230_machine_init(MachineState *machine)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+ K230MachineState *s = RISCV_K230_MACHINE(machine);
+ MemoryRegion *sys_mem = get_system_memory();
+
+ if (machine->ram_size < mc->default_ram_size) {
+ char *sz = size_to_str(mc->default_ram_size);
+ error_report("Invalid RAM size, should be %s", sz);
+ g_free(sz);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initialize SoC */
+ object_initialize_child(OBJECT(machine), "soc", &s->soc,
+ TYPE_RISCV_K230_SOC);
+ qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
+
+ /* Data Memory */
+ memory_region_add_subregion(sys_mem, memmap[K230_DEV_DDRC].base,
+ machine->ram);
+
+ s->machine_done.notify = k230_machine_done;
+ qemu_add_machine_init_done_notifier(&s->machine_done);
+}
+
+static void k230_machine_instance_init(Object *obj)
+{
+}
+
+static void k230_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "RISC-V Board compatible with Kendryte K230 SDK";
+ mc->init = k230_machine_init;
+ mc->default_cpus = 2;
+ mc->default_ram_id = "riscv.K230.ram"; /* DDR */
+ mc->default_ram_size = memmap[K230_DEV_DDRC].size;
+}
+
+static const TypeInfo k230_machine_typeinfo = {
+ .name = TYPE_RISCV_K230_MACHINE,
+ .parent = TYPE_MACHINE,
+ .class_init = k230_machine_class_init,
+ .instance_init = k230_machine_instance_init,
+ .instance_size = sizeof(K230MachineState),
+};
+
+static void k230_machine_init_register_types(void)
+{
+ type_register_static(&k230_machine_typeinfo);
+}
+
+type_init(k230_machine_init_register_types)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 533472e22a..09cf855984 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -14,8 +14,8 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
-
riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c'))
riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-aia.c'))
+riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c'))
hw_arch += {'riscv': riscv_ss}
diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h
new file mode 100644
index 0000000000..830175b756
--- /dev/null
+++ b/include/hw/riscv/k230.h
@@ -0,0 +1,150 @@
+/*
+ * QEMU RISC-V Virt Board Compatible with kendryte K230 SDK
+ *
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Provides a board compatible with the kendryte K230 SDK
+ *
+ * Documentation: K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * For more information, see <https://www.kendryte.com/en/proDetail/230>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef HW_K230_H
+#define HW_K230_H
+
+#include "hw/core/boards.h"
+#include "hw/riscv/riscv_hart.h"
+
+#define C908_CPU_HARTID (0)
+
+#define TYPE_RISCV_K230_SOC "riscv.k230.soc"
+#define RISCV_K230_SOC(obj) \
+ OBJECT_CHECK(K230SoCState, (obj), TYPE_RISCV_K230_SOC)
+
+typedef struct K230SoCState {
+ /*< private >*/
+ DeviceState parent_obj;
+
+ /*< public >*/
+ RISCVHartArrayState c908_cpu; /* Small core */
+
+ MemoryRegion sram;
+ MemoryRegion bootrom;
+
+ DeviceState *c908_plic;
+} K230SoCState;
+
+#define TYPE_RISCV_K230_MACHINE MACHINE_TYPE_NAME("k230")
+#define RISCV_K230_MACHINE(obj) \
+ OBJECT_CHECK(K230MachineState, (obj), TYPE_RISCV_K230_MACHINE)
+
+typedef struct K230MachineState {
+ /*< private >*/
+ MachineState parent_obj;
+
+ /*< public >*/
+ K230SoCState soc;
+ Notifier machine_done;
+} K230MachineState;
+
+enum {
+ K230_DEV_DDRC,
+ K230_DEV_KPU_L2_CACHE,
+ K230_DEV_SRAM,
+ K230_DEV_KPU_CFG,
+ K230_DEV_FFT,
+ K230_DEV_AI_2D_ENGINE,
+ K230_DEV_GSDMA,
+ K230_DEV_DMA,
+ K230_DEV_DECOMP_GZIP,
+ K230_DEV_NON_AI_2D,
+ K230_DEV_ISP,
+ K230_DEV_DEWARP,
+ K230_DEV_RX_CSI,
+ K230_DEV_H264,
+ K230_DEV_2P5D,
+ K230_DEV_VO,
+ K230_DEV_VO_CFG,
+ K230_DEV_3D_ENGINE,
+ K230_DEV_PMU,
+ K230_DEV_RTC,
+ K230_DEV_CMU,
+ K230_DEV_RMU,
+ K230_DEV_BOOT,
+ K230_DEV_PWR,
+ K230_DEV_MAILBOX,
+ K230_DEV_IOMUX,
+ K230_DEV_TIMER,
+ K230_DEV_WDT0,
+ K230_DEV_WDT1,
+ K230_DEV_TS,
+ K230_DEV_HDI,
+ K230_DEV_STC,
+ K230_DEV_BOOTROM,
+ K230_DEV_SECURITY,
+ K230_DEV_UART0,
+ K230_DEV_UART1,
+ K230_DEV_UART2,
+ K230_DEV_UART3,
+ K230_DEV_UART4,
+ K230_DEV_I2C0,
+ K230_DEV_I2C1,
+ K230_DEV_I2C2,
+ K230_DEV_I2C3,
+ K230_DEV_I2C4,
+ K230_DEV_PWM,
+ K230_DEV_GPIO0,
+ K230_DEV_GPIO1,
+ K230_DEV_ADC,
+ K230_DEV_CODEC,
+ K230_DEV_I2S,
+ K230_DEV_USB0,
+ K230_DEV_USB1,
+ K230_DEV_SD0,
+ K230_DEV_SD1,
+ K230_DEV_QSPI0,
+ K230_DEV_QSPI1,
+ K230_DEV_SPI,
+ K230_DEV_HI_SYS_CFG,
+ K230_DEV_DDRC_CFG,
+ K230_DEV_FLASH,
+ K230_DEV_PLIC,
+ K230_DEV_CLINT,
+};
+
+enum {
+ K230_UART0_IRQ = 16,
+ K230_UART1_IRQ = 17,
+ K230_UART2_IRQ = 18,
+ K230_UART3_IRQ = 19,
+ K230_UART4_IRQ = 20,
+};
+
+/*
+ * Integrates with the interrupt controller (PLIC),
+ * which can process 208 interrupt external sources
+ */
+#define K230_PLIC_NUM_SOURCES 208
+#define K230_PLIC_NUM_PRIORITIES 7
+#define K230_PLIC_PRIORITY_BASE 0x00
+#define K230_PLIC_PENDING_BASE 0x1000
+#define K230_PLIC_ENABLE_BASE 0x2000
+#define K230_PLIC_ENABLE_STRIDE 0x80
+#define K230_PLIC_CONTEXT_BASE 0x200000
+#define K230_PLIC_CONTEXT_STRIDE 0x1000
+
+#endif
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 3/5] hw/watchdog: add k230 watchdog initial support
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
2026-01-30 7:19 ` [PATCH v5 1/5] target/riscv: add thead-c908 cpu support Chao Liu
2026-01-30 7:19 ` [PATCH v5 2/5] hw/riscv: add k230 board initial support Chao Liu
@ 2026-01-30 7:19 ` Chao Liu
2026-01-30 7:19 ` [PATCH v5 4/5] tests/qtest: add test for K230 watchdog Chao Liu
2026-01-30 7:33 ` [PATCH v5 5/5] docs/system/riscv: add documentation for k230 machine Chao Liu
4 siblings, 0 replies; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:19 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Fabiano Rosas, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu,
Mig Yang
From: Chao Liu <chao.liu@zevorn.cn>
Add programmable Watchdog Timer (WDT) peripheral for K230 machine.
Signed-off-by: Mig Yang <temashking@foxmail.com>
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
MAINTAINERS | 2 +
hw/riscv/Kconfig | 1 +
hw/riscv/k230.c | 18 ++
hw/watchdog/Kconfig | 4 +
hw/watchdog/k230_wdt.c | 307 +++++++++++++++++++++++++++++++++
hw/watchdog/meson.build | 1 +
hw/watchdog/trace-events | 9 +
include/hw/riscv/k230.h | 4 +
include/hw/watchdog/k230_wdt.h | 132 ++++++++++++++
9 files changed, 478 insertions(+)
create mode 100644 hw/watchdog/k230_wdt.c
create mode 100644 include/hw/watchdog/k230_wdt.h
diff --git a/MAINTAINERS b/MAINTAINERS
index c0c56d6ce7..64a5f3b986 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1762,7 +1762,9 @@ K230 Machines
M: Chao Liu <chao.liu.zevorn@gmail.com>
S: Maintained
F: hw/riscv/k230.c
+F: hw/watchdog/k230_wdt.c
F: include/hw/riscv/k230.h
+F: include/hw/watchdog/k230_wdt.h
RX Machines
-----------
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index b1a7357866..5f8511cb92 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -144,3 +144,4 @@ config K230
select RISCV_IMSIC
select SERIAL_MM
select UNIMP
+ select K230_WDT
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index 2537023a05..7df8a3298c 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -113,6 +113,9 @@ static void k230_soc_init(Object *obj)
RISCVHartArrayState *cpu0 = &s->c908_cpu;
object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY);
+ object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT);
+ object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT);
+
qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0);
qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908);
qdev_prop_set_uint64(DEVICE(cpu0), "resetvec",
@@ -190,6 +193,21 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(s->c908_plic), K230_UART4_IRQ),
399193, serial_hd(4), DEVICE_LITTLE_ENDIAN);
+ /* Watchdog */
+ for (int i = 0; i < 2; i++) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) {
+ return;
+ }
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[0]), 0,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT0_IRQ));
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[1]), 0, memmap[K230_DEV_WDT1].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0,
+ qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ));
+
/* unimplemented devices */
create_unimplemented_device("kpu.l2-cache",
memmap[K230_DEV_KPU_L2_CACHE].base,
diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig
index 861fd00334..55f77c5c84 100644
--- a/hw/watchdog/Kconfig
+++ b/hw/watchdog/Kconfig
@@ -18,6 +18,10 @@ config WDT_DIAG288
config WDT_IMX2
bool
+config K230_WDT
+ bool
+ select PTIMER
+
config WDT_SBSA
bool
diff --git a/hw/watchdog/k230_wdt.c b/hw/watchdog/k230_wdt.c
new file mode 100644
index 0000000000..66df0b738b
--- /dev/null
+++ b/hw/watchdog/k230_wdt.c
@@ -0,0 +1,307 @@
+/*
+ * * K230 Watchdog Compatible with kendryte K230 SDK
+ *
+ * Copyright (c) 2025 Mig Yang <temashking@foxmail.com>
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Provides a board compatible with the kendryte K230 SDK
+ *
+ * Documentation: K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * For more information, see <https://www.kendryte.com/en/proDetail/230>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/module.h"
+#include "system/watchdog.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/watchdog/k230_wdt.h"
+#include "trace.h"
+
+static void k230_wdt_timeout(void *opaque)
+{
+ K230WdtState *s = K230_WDT(opaque);
+
+ trace_k230_wdt_timeout();
+
+ /* Set interrupt status if in interrupt mode */
+ if (s->cr & K230_WDT_CR_RMOD) {
+ s->stat |= K230_WDT_STAT_INT;
+ s->interrupt_pending = true;
+ qemu_set_irq(s->irq, 1);
+ trace_k230_wdt_interrupt();
+ } else {
+ /* Direct reset mode */
+ trace_k230_wdt_reset();
+ watchdog_perform_action();
+ }
+
+ /* Restart counter */
+ s->current_count = s->timeout_value;
+ ptimer_set_count(s->timer, s->current_count);
+ ptimer_run(s->timer, 1);
+}
+
+static void k230_wdt_reset(DeviceState *dev)
+{
+ K230WdtState *s = K230_WDT(dev);
+
+ trace_k230_wdt_reset_device();
+
+ ptimer_transaction_begin(s->timer);
+ ptimer_stop(s->timer);
+ ptimer_transaction_commit(s->timer);
+
+ /* Reset registers to default values */
+ s->cr = 0;
+ s->torr = 0;
+ s->ccvr = 0xFFFFFFFF;
+ s->stat = 0;
+ s->prot_level = 0x2;
+
+ s->interrupt_pending = false;
+ s->enabled = false;
+ s->timeout_value = 0;
+ s->current_count = 0xFFFFFFFF;
+}
+
+static uint64_t k230_wdt_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ K230WdtState *s = K230_WDT(opaque);
+ uint32_t value = 0;
+
+ switch (addr) {
+ case K230_WDT_CR:
+ value = s->cr;
+ break;
+ case K230_WDT_TORR:
+ value = s->torr;
+ break;
+ case K230_WDT_CCVR:
+ if (s->enabled) {
+ value = ptimer_get_count(s->timer);
+ } else {
+ value = s->current_count;
+ }
+ break;
+ case K230_WDT_STAT:
+ value = s->stat;
+ break;
+ case K230_WDT_PROT_LEVEL:
+ value = s->prot_level;
+ break;
+ case K230_WDT_COMP_PARAM_5:
+ value = 0; /* Upper limit of Timeout Period parameters */
+ break;
+ case K230_WDT_COMP_PARAM_4:
+ value = 0; /* Upper limit of Initial Timeout Period parameters */
+ break;
+ case K230_WDT_COMP_PARAM_3:
+ value = 0; /* Derived from WDT_TOP_RST parameter */
+ break;
+ case K230_WDT_COMP_PARAM_2:
+ value = 0xFFFFFFFF; /* Derived from WDT_RST_CNT parameter */
+ break;
+ case K230_WDT_COMP_PARAM_1:
+ /* Component parameters */
+ value = (32 << K230_WDT_CNT_WIDTH_SHIFT) | /* 32-bit counter */
+ (0 << K230_WDT_DFLT_TOP_INIT_SHIFT) |
+ (0 << K230_WDT_DFLT_TOP_SHIFT) |
+ (K230_WDT_RPL_16_CYCLES << K230_WDT_DFLT_RPL_SHIFT) |
+ (2 << K230_WDT_APB_DATA_WIDTH_SHIFT) | /* 32-bit APB */
+ K230_WDT_USE_FIX_TOP; /* Use fixed timeout values */
+ break;
+ case K230_WDT_COMP_VERSION:
+ value = K230_WDT_COMP_VERSION_VAL;
+ break;
+ case K230_WDT_COMP_TYPE:
+ value = K230_WDT_COMP_TYPE_VAL;
+ break;
+ default:
+ /* Other registers return 0 */
+ break;
+ }
+
+ trace_k230_wdt_read(addr, value);
+ return value;
+}
+
+static void k230_wdt_update_timer(K230WdtState *s)
+{
+ ptimer_transaction_begin(s->timer);
+
+ if (s->enabled && s->timeout_value > 0) {
+ ptimer_set_count(s->timer, s->current_count);
+ ptimer_run(s->timer, 1);
+ } else {
+ ptimer_stop(s->timer);
+ }
+
+ ptimer_transaction_commit(s->timer);
+}
+
+static uint32_t k230_wdt_calculate_timeout(uint32_t top_value)
+{
+ /* Calculate timeout based on TOP value */
+ /* For fixed timeout mode: 2^(16 + top_value) */
+ if (top_value <= 15) {
+ return 1 << (16 + top_value);
+ }
+ return 1 << 31; /* Maximum value for 32-bit counter */
+}
+
+static void k230_wdt_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned int size)
+{
+ K230WdtState *s = K230_WDT(opaque);
+
+ trace_k230_wdt_write(addr, value);
+
+ switch (addr) {
+ case K230_WDT_CR:
+ s->cr = value & (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT |
+ K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
+
+ /* Update enabled state */
+ s->enabled = (s->cr & K230_WDT_CR_WDT_EN) != 0;
+
+ /* Update timer */
+ k230_wdt_update_timer(s);
+ break;
+
+ case K230_WDT_TORR:
+ s->torr = value & K230_WDT_TORR_TOP_MASK;
+
+ /* Calculate new timeout value */
+ s->timeout_value = k230_wdt_calculate_timeout(s->torr);
+ s->current_count = s->timeout_value;
+
+ /* Update timer if enabled */
+ if (s->enabled) {
+ k230_wdt_update_timer(s);
+ }
+ break;
+
+ case K230_WDT_CRR:
+ /* Restart counter with magic value 0x76 */
+ if ((value & 0xFF) == K230_WDT_CRR_RESTART) {
+ trace_k230_wdt_restart();
+ s->current_count = s->timeout_value;
+
+ /* Clear interrupt if pending */
+ if (s->interrupt_pending) {
+ s->stat &= ~K230_WDT_STAT_INT;
+ s->interrupt_pending = false;
+ qemu_set_irq(s->irq, 0);
+ }
+
+ /* Update timer */
+ k230_wdt_update_timer(s);
+ }
+ break;
+
+ case K230_WDT_EOI:
+ /* Clear interrupt */
+ s->stat &= ~K230_WDT_STAT_INT;
+ s->interrupt_pending = false;
+ qemu_set_irq(s->irq, 0);
+ break;
+
+ case K230_WDT_PROT_LEVEL:
+ s->prot_level = value & 0x7;
+ break;
+
+ default:
+ /* Read-only registers, ignore writes */
+ break;
+ }
+}
+
+static const MemoryRegionOps k230_wdt_ops = {
+ .read = k230_wdt_read,
+ .write = k230_wdt_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static const VMStateDescription vmstate_k230_wdt = {
+ .name = "k230.wdt",
+ .fields = (const VMStateField[]) {
+ VMSTATE_PTIMER(timer, K230WdtState),
+ VMSTATE_UINT32(cr, K230WdtState),
+ VMSTATE_UINT32(torr, K230WdtState),
+ VMSTATE_UINT32(ccvr, K230WdtState),
+ VMSTATE_UINT32(stat, K230WdtState),
+ VMSTATE_UINT32(prot_level, K230WdtState),
+ VMSTATE_BOOL(interrupt_pending, K230WdtState),
+ VMSTATE_BOOL(enabled, K230WdtState),
+ VMSTATE_UINT32(timeout_value, K230WdtState),
+ VMSTATE_UINT32(current_count, K230WdtState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void k230_wdt_realize(DeviceState *dev, Error **errp)
+{
+ K230WdtState *s = K230_WDT(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->mmio, OBJECT(dev),
+ &k230_wdt_ops, s,
+ TYPE_K230_WDT,
+ K230_WDT_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->mmio);
+ sysbus_init_irq(sbd, &s->irq);
+
+ s->timer = ptimer_init(k230_wdt_timeout, s,
+ PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
+ PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
+ PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+
+ ptimer_transaction_begin(s->timer);
+ ptimer_set_freq(s->timer, K230_WDT_DEFAULT_FREQ);
+ ptimer_transaction_commit(s->timer);
+}
+
+static void k230_wdt_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = k230_wdt_realize;
+ device_class_set_legacy_reset(dc, k230_wdt_reset);
+ dc->vmsd = &vmstate_k230_wdt;
+ dc->desc = "K230 watchdog timer";
+ set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
+}
+
+static const TypeInfo k230_wdt_info = {
+ .name = TYPE_K230_WDT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(K230WdtState),
+ .class_init = k230_wdt_class_init,
+};
+
+static void k230_wdt_register_type(void)
+{
+ type_register_static(&k230_wdt_info);
+}
+type_init(k230_wdt_register_type)
diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build
index 15370565bd..5edae65a36 100644
--- a/hw/watchdog/meson.build
+++ b/hw/watchdog/meson.build
@@ -6,5 +6,6 @@ system_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c'))
system_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c'))
system_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c'))
+system_ss.add(when: 'CONFIG_K230_WDT', if_true: files('k230_wdt.c'))
system_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_watchdog.c'))
diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events
index ad3be1e9bd..d85b3ca769 100644
--- a/hw/watchdog/trace-events
+++ b/hw/watchdog/trace-events
@@ -33,3 +33,12 @@ spapr_watchdog_expired(uint64_t num, unsigned action) "num=%" PRIu64 " action=%u
# watchdog.c
watchdog_perform_action(unsigned int action) "action=%u"
watchdog_set_action(unsigned int action) "action=%u"
+
+# k230_wdt.c
+k230_wdt_read(uint64_t addr, uint32_t data) "K230 WDT read: [0x%" PRIx64 "] -> 0x%" PRIx32
+k230_wdt_write(uint64_t addr, uint64_t data) "K230 WDT write: [0x%" PRIx64 "] <- 0x%" PRIx64
+k230_wdt_timeout(void) "K230 WDT timeout"
+k230_wdt_interrupt(void) "K230 WDT interrupt"
+k230_wdt_reset(void) "K230 WDT system reset"
+k230_wdt_restart(void) "K230 WDT restart"
+k230_wdt_reset_device(void) "K230 WDT device reset"
diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h
index 830175b756..84bd431bc9 100644
--- a/include/hw/riscv/k230.h
+++ b/include/hw/riscv/k230.h
@@ -28,6 +28,7 @@
#include "hw/core/boards.h"
#include "hw/riscv/riscv_hart.h"
+#include "hw/watchdog/k230_wdt.h"
#define C908_CPU_HARTID (0)
@@ -42,6 +43,7 @@ typedef struct K230SoCState {
/*< public >*/
RISCVHartArrayState c908_cpu; /* Small core */
+ K230WdtState wdt[2];
MemoryRegion sram;
MemoryRegion bootrom;
@@ -132,6 +134,8 @@ enum {
K230_UART2_IRQ = 18,
K230_UART3_IRQ = 19,
K230_UART4_IRQ = 20,
+ K230_WDT0_IRQ = 32,
+ K230_WDT1_IRQ = 33,
};
/*
diff --git a/include/hw/watchdog/k230_wdt.h b/include/hw/watchdog/k230_wdt.h
new file mode 100644
index 0000000000..ec80567780
--- /dev/null
+++ b/include/hw/watchdog/k230_wdt.h
@@ -0,0 +1,132 @@
+/*
+ * K230 Watchdog Timer
+ *
+ * Based on K230 technical documentation
+ *
+ * Copyright (c) 2025 Mig Yang <temashking@foxmail.com>
+ * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef K230_WDT_H
+#define K230_WDT_H
+
+#include "qemu/bitops.h"
+#include "hw/core/sysbus.h"
+#include "hw/core/irq.h"
+#include "hw/core/ptimer.h"
+#include "qom/object.h"
+
+#define TYPE_K230_WDT "riscv.k230.wdt"
+OBJECT_DECLARE_SIMPLE_TYPE(K230WdtState, K230_WDT)
+
+#define K230_WDT_DEFAULT_FREQ (32768)
+
+/* K230 Watchdog Register Map */
+enum K230WdtRegisters {
+ K230_WDT_CR = 0x00, /* Control Register */
+ K230_WDT_TORR = 0x04, /* Timeout Range Register */
+ K230_WDT_CCVR = 0x08, /* Current Counter Value Register */
+ K230_WDT_CRR = 0x0c, /* Counter Restart Register */
+ K230_WDT_STAT = 0x10, /* Interrupt Status Register */
+ K230_WDT_EOI = 0x14, /* Interrupt Clear Register */
+ K230_WDT_PROT_LEVEL = 0x1c, /* Protection Level Register */
+ K230_WDT_COMP_PARAM_5 = 0xe4, /* Component Parameters Register 5 */
+ K230_WDT_COMP_PARAM_4 = 0xe8, /* Component Parameters Register 4 */
+ K230_WDT_COMP_PARAM_3 = 0xec, /* Component Parameters Register 3 */
+ K230_WDT_COMP_PARAM_2 = 0xf0, /* Component Parameters Register 2 */
+ K230_WDT_COMP_PARAM_1 = 0xf4, /* Component Parameters Register 1 */
+ K230_WDT_COMP_VERSION = 0xf8, /* Component Version Register */
+ K230_WDT_COMP_TYPE = 0xfc, /* Component Type Register */
+};
+
+#define K230_WDT_MMIO_SIZE 0x100
+
+/* Control Register (WDT_CR) definitions */
+#define K230_WDT_CR_RPL_MASK 0x7 /* Reset Pulse Length */
+#define K230_WDT_CR_RPL_SHIFT 2
+#define K230_WDT_CR_RMOD BIT(1) /* Response Mode */
+#define K230_WDT_CR_WDT_EN BIT(0) /* Watchdog Enable */
+
+/* Reset Pulse Length values */
+#define K230_WDT_RPL_2_CYCLES 0x0
+#define K230_WDT_RPL_4_CYCLES 0x1
+#define K230_WDT_RPL_8_CYCLES 0x2
+#define K230_WDT_RPL_16_CYCLES 0x3
+#define K230_WDT_RPL_32_CYCLES 0x4
+#define K230_WDT_RPL_64_CYCLES 0x5
+#define K230_WDT_RPL_128_CYCLES 0x6
+#define K230_WDT_RPL_256_CYCLES 0x7
+
+/* Timeout Range Register (WDT_TORR) definitions */
+#define K230_WDT_TORR_TOP_MASK 0xf /* Timeout Period */
+
+/* Interrupt Status Register (WDT_STAT) definitions */
+#define K230_WDT_STAT_INT BIT(0) /* Interrupt Status */
+
+/* Counter Restart Register (WDT_CRR) magic value */
+#define K230_WDT_CRR_RESTART 0x76 /* Restart command */
+
+/* Component Parameters Register 1 (WDT_COMP_PARAM_1) definitions */
+#define K230_WDT_CNT_WIDTH_MASK 0x1f000000 /* Counter Width */
+#define K230_WDT_CNT_WIDTH_SHIFT 24
+#define K230_WDT_DFLT_TOP_INIT_MASK 0xf00000 /* Default Initial Timeout */
+#define K230_WDT_DFLT_TOP_INIT_SHIFT 20
+#define K230_WDT_DFLT_TOP_MASK 0xf0000 /* Default Timeout */
+#define K230_WDT_DFLT_TOP_SHIFT 16
+#define K230_WDT_DFLT_RPL_MASK 0x7 /* Default Reset Pulse Length */
+#define K230_WDT_DFLT_RPL_SHIFT 10
+#define K230_WDT_APB_DATA_WIDTH_MASK 0x3 /* APB Data Width */
+#define K230_WDT_APB_DATA_WIDTH_SHIFT 8
+#define K230_WDT_USE_FIX_TOP BIT(6) /* Use Fixed Timeout Values */
+#define K230_WDT_HC_TOP BIT(5) /* Hard-coded Timeout */
+#define K230_WDT_HC_RPL BIT(4) /* Hard-coded Reset Pulse Length */
+#define K230_WDT_HC_RMOD BIT(3) /* Hard-coded Response Mode */
+#define K230_WDT_DUAL_TOP BIT(2) /* Dual Timeout Period */
+#define K230_WDT_DFLT_RMOD BIT(1) /* Default Response Mode */
+#define K230_WDT_ALWAYS_EN BIT(0) /* Always Enabled */
+
+/* Component Type Register value */
+#define K230_WDT_COMP_TYPE_VAL 0x44570120
+
+/* Component Version Register value */
+#define K230_WDT_COMP_VERSION_VAL 0x3131302a /* "110*" */
+
+struct K230WdtState {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion mmio;
+ qemu_irq irq;
+
+ struct ptimer_state *timer;
+
+ /* Register state */
+ uint32_t cr; /* Control Register */
+ uint32_t torr; /* Timeout Range Register */
+ uint32_t ccvr; /* Current Counter Value Register */
+ uint32_t stat; /* Interrupt Status Register */
+ uint32_t prot_level; /* Protection Level Register */
+
+ /* Internal state */
+ bool interrupt_pending;
+ bool enabled;
+ uint32_t timeout_value;
+ uint32_t current_count;
+};
+
+#endif /* K230_WDT_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 4/5] tests/qtest: add test for K230 watchdog
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
` (2 preceding siblings ...)
2026-01-30 7:19 ` [PATCH v5 3/5] hw/watchdog: add k230 watchdog " Chao Liu
@ 2026-01-30 7:19 ` Chao Liu
2026-02-02 19:38 ` Fabiano Rosas
2026-01-30 7:33 ` [PATCH v5 5/5] docs/system/riscv: add documentation for k230 machine Chao Liu
4 siblings, 1 reply; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:19 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Fabiano Rosas, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu,
Mig Yang
From: Chao Liu <chao.liu@zevorn.cn>
Testing the Basic Functions of K230 WDT:
1. Reset Function
2. Timeout Check
3. Interrupt Function
Signed-off-by: Mig Yang <temashking@foxmail.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
MAINTAINERS | 1 +
tests/qtest/k230-wdt-test.c | 199 ++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 3 +-
3 files changed, 202 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/k230-wdt-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 64a5f3b986..84c980d252 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1765,6 +1765,7 @@ F: hw/riscv/k230.c
F: hw/watchdog/k230_wdt.c
F: include/hw/riscv/k230.h
F: include/hw/watchdog/k230_wdt.h
+F: tests/qtest/k230-wdt-test.c
RX Machines
-----------
diff --git a/tests/qtest/k230-wdt-test.c b/tests/qtest/k230-wdt-test.c
new file mode 100644
index 0000000000..2550cebd10
--- /dev/null
+++ b/tests/qtest/k230-wdt-test.c
@@ -0,0 +1,199 @@
+/*
+ * QTest testcase for K230 Watchdog
+ *
+ * Copyright (c) 2025 Mig Yang <temashking@foxmail.com>
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Provides a board compatible with the kendryte K230 SDK
+ *
+ * Documentation: K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * For more information, see <https://www.kendryte.com/en/proDetail/230>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/timer.h"
+#include "qemu/bitops.h"
+#include "libqtest.h"
+#include "hw/watchdog/k230_wdt.h"
+
+/* K230 WDT0 base address */
+#define K230_WDT0_BASE 0x91106000
+#define K230_WDT1_BASE 0x91106800
+
+/* Test WDT0 by default */
+#define WDT_BASE K230_WDT0_BASE
+
+static void test_register_read_write(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Test Control Register (CR) read/write */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0xFFFFFFFF);
+ g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_CR), ==,
+ (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) |
+ K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
+
+ /* Test Timeout Range Register (TORR) read/write */
+ qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0xFFFFFFFF);
+ g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_TORR), ==,
+ K230_WDT_TORR_TOP_MASK);
+
+ /* Test Protection Level Register read/write */
+ qtest_writel(qts, WDT_BASE + K230_WDT_PROT_LEVEL, 0xFFFFFFFF);
+ g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_PROT_LEVEL), ==, 0x7);
+
+ qtest_quit(qts);
+}
+
+static void test_counter_restart(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Enable watchdog and set timeout */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
+ qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x5); /* TOP = 5 */
+
+ /* Read current counter value */
+ uint32_t initial_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
+ g_assert_cmpuint(initial_count, >, 0);
+
+ /* Restart counter with magic value */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CRR, K230_WDT_CRR_RESTART);
+
+ /* Wait for time */
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
+
+ /* Counter should be reset to timeout value */
+ uint32_t new_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
+ g_assert_cmpuint(new_count, >, 0);
+ g_assert_cmpuint(new_count, !=, initial_count);
+
+ qtest_quit(qts);
+}
+
+static void test_interrupt_mode(void)
+{
+ QTestState *qts = qtest_init("-machine k230 --trace k230_*,file=k230.log");
+
+ /* Set interrupt mode and enable watchdog */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR,
+ K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
+ qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */
+
+ /* Wait for timeout to trigger interrupt */
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10);
+
+ /* Check interrupt status */
+ uint32_t stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT);
+ g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, K230_WDT_STAT_INT);
+
+ /* Clear interrupt */
+ qtest_writel(qts, WDT_BASE + K230_WDT_EOI, 0x1);
+ stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT);
+ g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, 0);
+
+ qtest_quit(qts);
+}
+
+static void test_reset_mode(void)
+{
+ QTestState *qts = qtest_init("-machine k230 -no-reboot");
+
+ /* Set reset mode and enable watchdog */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
+ qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */
+
+ /* Wait for timeout to trigger reset */
+ qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
+
+ /* In reset mode, the system should reset */
+ /* This test verifies that reset mode is properly configured */
+
+ qtest_quit(qts);
+}
+
+static void test_timeout_calculation(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Test different timeout values */
+ for (uint32_t top = 0; top <= 15; top++) {
+ qtest_writel(qts, WDT_BASE + K230_WDT_TORR, top);
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
+
+ /* Read current counter value */
+ uint32_t count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
+ g_assert_cmpuint(count, >, 0);
+
+ /* Disable watchdog for next iteration */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0);
+ }
+
+ qtest_quit(qts);
+}
+
+static void test_wdt1_registers(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Test WDT1 registers (second watchdog) */
+ qtest_writel(qts, K230_WDT1_BASE + K230_WDT_CR, 0xFFFFFFFF);
+ g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_CR), ==,
+ (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) |
+ K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
+
+ qtest_writel(qts, K230_WDT1_BASE + K230_WDT_TORR, 0xFFFFFFFF);
+ g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_TORR), ==,
+ K230_WDT_TORR_TOP_MASK);
+
+ qtest_quit(qts);
+}
+
+static void test_enable_disable(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Initially disabled */
+ uint32_t cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
+ g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0);
+
+ /* Enable watchdog */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
+ cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
+ g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, K230_WDT_CR_WDT_EN);
+
+ /* Disable watchdog */
+ qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0);
+ cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
+ g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/k230-wdt/register_read_write", test_register_read_write);
+ qtest_add_func("/k230-wdt/counter_restart", test_counter_restart);
+ qtest_add_func("/k230-wdt/interrupt_mode", test_interrupt_mode);
+ qtest_add_func("/k230-wdt/reset_mode", test_reset_mode);
+ qtest_add_func("/k230-wdt/timeout_calculation", test_timeout_calculation);
+ qtest_add_func("/k230-wdt/wdt1_registers", test_wdt1_registers);
+ qtest_add_func("/k230-wdt/enable_disable", test_enable_disable);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 0f053fb56d..54c3ba5dea 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -283,7 +283,8 @@ qtests_riscv32 = \
(config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
qtests_riscv64 = ['riscv-csr-test'] + \
- (unpack_edk2_blobs ? ['bios-tables-test'] : [])
+ (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : [])
qos_test_ss = ss.source_set()
qos_test_ss.add(
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 5/5] docs/system/riscv: add documentation for k230 machine
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
` (3 preceding siblings ...)
2026-01-30 7:19 ` [PATCH v5 4/5] tests/qtest: add test for K230 watchdog Chao Liu
@ 2026-01-30 7:33 ` Chao Liu
4 siblings, 0 replies; 7+ messages in thread
From: Chao Liu @ 2026-01-30 7:33 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza, Palmer Dabbelt,
Weiwei Li, Liu Zhiwei, Chao Liu
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu
From: Chao Liu <chao.liu@zevorn.cn>
Add documentation for k230 virt reference platform.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
MAINTAINERS | 1 +
docs/system/riscv/k230.rst | 48 ++++++++++++++++++++++++++++++++++++
docs/system/target-riscv.rst | 1 +
3 files changed, 50 insertions(+)
create mode 100644 docs/system/riscv/k230.rst
diff --git a/MAINTAINERS b/MAINTAINERS
index 84c980d252..876aafe9f3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1761,6 +1761,7 @@ F: include/hw/riscv/xiangshan_kmh.h
K230 Machines
M: Chao Liu <chao.liu.zevorn@gmail.com>
S: Maintained
+F: docs/system/riscv/k230.rst
F: hw/riscv/k230.c
F: hw/watchdog/k230_wdt.c
F: include/hw/riscv/k230.h
diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst
new file mode 100644
index 0000000000..3e6ca295df
--- /dev/null
+++ b/docs/system/riscv/k230.rst
@@ -0,0 +1,48 @@
+Kendryte K230 virt reference platform (``k230``)
+==========================================================================
+The ``k230`` machine is compatible with with Kendryte K230 SDK.
+
+The K230 is a chip from the AIoT SoC series made by Kendryte ® — a part of
+Canaan Inc. It uses a brand-new multi-heterogeneous unit accelerated computing
+structure.
+
+This chip has 2 RISC-V computing cores and a new-generation KPU (Knowledge
+Process Unit) smart computing unit.
+
+It has multi-precision AI computing ability, works with many common AI computing
+frameworks, and for some typical networks, its usage rate is over 70%. Besides,
+the K230 chip supports many peripheral connections and has several special
+hardware acceleration units (like 2D and 2.5D accelerators). It can speed up
+different tasks (such as image processing, video processing, audio processing
+and AI computing). It also has many good features: low delay, high performance,
+low power use and fast start-up.
+
+For more information, see <https://www.kendryte.com/en/proDetail/230>
+
+Supported devices
+-----------------
+The ``k230`` machine supports the following devices:
+
+* 1 c908 cores
+* Core Local Interruptor (CLINT)
+* Incoming MSI Controller (IMSIC)
+* 2 K230 Watchdog Timer
+* 4 UART
+
+Boot options
+------------
+The ``k230`` machine can start using the standard ``-bios``
+functionality for loading the boot image. You need to compile and link
+the firmware, kernel, and Device Tree (FDT) into a single binary file with
+K230 SDK(k230_canmv_defconfig), such as ``uboot``.
+
+Running
+-------
+Below is an example command line for running the ``k230``
+machine:
+
+.. code-block:: bash
+
+ $ qemu-system-riscv64 -machine k230 \
+ -bios k230_sdk/output/k230_canmv_defconfig/little/uboot/u-boot \
+ -nographic
diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 3ad5d1ddaf..b0b2f9584f 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -66,6 +66,7 @@ undocumented; you can get a complete list by running
.. toctree::
:maxdepth: 1
+ riscv/k230
riscv/microblaze-v-generic
riscv/microchip-icicle-kit
riscv/mips
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v5 4/5] tests/qtest: add test for K230 watchdog
2026-01-30 7:19 ` [PATCH v5 4/5] tests/qtest: add test for K230 watchdog Chao Liu
@ 2026-02-02 19:38 ` Fabiano Rosas
0 siblings, 0 replies; 7+ messages in thread
From: Fabiano Rosas @ 2026-02-02 19:38 UTC (permalink / raw)
To: Chao Liu, Alistair Francis, Daniel Henrique Barboza,
Palmer Dabbelt, Weiwei Li, Liu Zhiwei, Chao Liu, Paolo Bonzini,
Christoph Muellner, Laurent Vivier
Cc: qemu-devel, qemu-riscv, hust-os-kernel-patches, Chao Liu,
Mig Yang
Chao Liu <chao.liu.zevorn@gmail.com> writes:
> From: Chao Liu <chao.liu@zevorn.cn>
>
> Testing the Basic Functions of K230 WDT:
> 1. Reset Function
> 2. Timeout Check
> 3. Interrupt Function
>
> Signed-off-by: Mig Yang <temashking@foxmail.com>
> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
> ---
> MAINTAINERS | 1 +
> tests/qtest/k230-wdt-test.c | 199 ++++++++++++++++++++++++++++++++++++
> tests/qtest/meson.build | 3 +-
> 3 files changed, 202 insertions(+), 1 deletion(-)
> create mode 100644 tests/qtest/k230-wdt-test.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 64a5f3b986..84c980d252 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1765,6 +1765,7 @@ F: hw/riscv/k230.c
> F: hw/watchdog/k230_wdt.c
> F: include/hw/riscv/k230.h
> F: include/hw/watchdog/k230_wdt.h
> +F: tests/qtest/k230-wdt-test.c
>
> RX Machines
> -----------
> diff --git a/tests/qtest/k230-wdt-test.c b/tests/qtest/k230-wdt-test.c
> new file mode 100644
> index 0000000000..2550cebd10
> --- /dev/null
> +++ b/tests/qtest/k230-wdt-test.c
> @@ -0,0 +1,199 @@
> +/*
> + * QTest testcase for K230 Watchdog
> + *
> + * Copyright (c) 2025 Mig Yang <temashking@foxmail.com>
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Provides a board compatible with the kendryte K230 SDK
> + *
> + * Documentation: K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
> + *
> + * For more information, see <https://www.kendryte.com/en/proDetail/230>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/timer.h"
> +#include "qemu/bitops.h"
> +#include "libqtest.h"
> +#include "hw/watchdog/k230_wdt.h"
> +
> +/* K230 WDT0 base address */
> +#define K230_WDT0_BASE 0x91106000
> +#define K230_WDT1_BASE 0x91106800
> +
> +/* Test WDT0 by default */
> +#define WDT_BASE K230_WDT0_BASE
> +
> +static void test_register_read_write(void)
> +{
> + QTestState *qts = qtest_init("-machine k230");
> +
> + /* Test Control Register (CR) read/write */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0xFFFFFFFF);
> + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_CR), ==,
> + (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) |
> + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
> +
> + /* Test Timeout Range Register (TORR) read/write */
> + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0xFFFFFFFF);
> + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_TORR), ==,
> + K230_WDT_TORR_TOP_MASK);
> +
> + /* Test Protection Level Register read/write */
> + qtest_writel(qts, WDT_BASE + K230_WDT_PROT_LEVEL, 0xFFFFFFFF);
> + g_assert_cmphex(qtest_readl(qts, WDT_BASE + K230_WDT_PROT_LEVEL), ==, 0x7);
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_counter_restart(void)
> +{
> + QTestState *qts = qtest_init("-machine k230");
> +
> + /* Enable watchdog and set timeout */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
> + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x5); /* TOP = 5 */
> +
> + /* Read current counter value */
> + uint32_t initial_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
> + g_assert_cmpuint(initial_count, >, 0);
> +
> + /* Restart counter with magic value */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CRR, K230_WDT_CRR_RESTART);
> +
> + /* Wait for time */
> + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
> +
> + /* Counter should be reset to timeout value */
> + uint32_t new_count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
> + g_assert_cmpuint(new_count, >, 0);
> + g_assert_cmpuint(new_count, !=, initial_count);
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_interrupt_mode(void)
> +{
> + QTestState *qts = qtest_init("-machine k230 --trace k230_*,file=k230.log");
> +
> + /* Set interrupt mode and enable watchdog */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR,
> + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
> + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */
> +
> + /* Wait for timeout to trigger interrupt */
> + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10);
> +
> + /* Check interrupt status */
> + uint32_t stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT);
> + g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, K230_WDT_STAT_INT);
> +
> + /* Clear interrupt */
> + qtest_writel(qts, WDT_BASE + K230_WDT_EOI, 0x1);
> + stat = qtest_readl(qts, WDT_BASE + K230_WDT_STAT);
> + g_assert_cmphex(stat & K230_WDT_STAT_INT, ==, 0);
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_reset_mode(void)
> +{
> + QTestState *qts = qtest_init("-machine k230 -no-reboot");
> +
> + /* Set reset mode and enable watchdog */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
> + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, 0x1); /* Short timeout */
> +
> + /* Wait for timeout to trigger reset */
> + qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
> +
> + /* In reset mode, the system should reset */
> + /* This test verifies that reset mode is properly configured */
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_timeout_calculation(void)
> +{
> + QTestState *qts = qtest_init("-machine k230");
> +
> + /* Test different timeout values */
> + for (uint32_t top = 0; top <= 15; top++) {
> + qtest_writel(qts, WDT_BASE + K230_WDT_TORR, top);
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
> +
> + /* Read current counter value */
> + uint32_t count = qtest_readl(qts, WDT_BASE + K230_WDT_CCVR);
> + g_assert_cmpuint(count, >, 0);
> +
> + /* Disable watchdog for next iteration */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0);
> + }
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_wdt1_registers(void)
> +{
> + QTestState *qts = qtest_init("-machine k230");
> +
> + /* Test WDT1 registers (second watchdog) */
> + qtest_writel(qts, K230_WDT1_BASE + K230_WDT_CR, 0xFFFFFFFF);
> + g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_CR), ==,
> + (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT) |
> + K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
> +
> + qtest_writel(qts, K230_WDT1_BASE + K230_WDT_TORR, 0xFFFFFFFF);
> + g_assert_cmphex(qtest_readl(qts, K230_WDT1_BASE + K230_WDT_TORR), ==,
> + K230_WDT_TORR_TOP_MASK);
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_enable_disable(void)
> +{
> + QTestState *qts = qtest_init("-machine k230");
> +
> + /* Initially disabled */
> + uint32_t cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
> + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0);
> +
> + /* Enable watchdog */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, K230_WDT_CR_WDT_EN);
> + cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
> + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, K230_WDT_CR_WDT_EN);
> +
> + /* Disable watchdog */
> + qtest_writel(qts, WDT_BASE + K230_WDT_CR, 0);
> + cr = qtest_readl(qts, WDT_BASE + K230_WDT_CR);
> + g_assert_cmphex(cr & K230_WDT_CR_WDT_EN, ==, 0);
> +
> + qtest_quit(qts);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + g_test_init(&argc, &argv, NULL);
> +
> + qtest_add_func("/k230-wdt/register_read_write", test_register_read_write);
> + qtest_add_func("/k230-wdt/counter_restart", test_counter_restart);
> + qtest_add_func("/k230-wdt/interrupt_mode", test_interrupt_mode);
> + qtest_add_func("/k230-wdt/reset_mode", test_reset_mode);
> + qtest_add_func("/k230-wdt/timeout_calculation", test_timeout_calculation);
> + qtest_add_func("/k230-wdt/wdt1_registers", test_wdt1_registers);
> + qtest_add_func("/k230-wdt/enable_disable", test_enable_disable);
> +
> + return g_test_run();
> +}
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 0f053fb56d..54c3ba5dea 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -283,7 +283,8 @@ qtests_riscv32 = \
> (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
>
> qtests_riscv64 = ['riscv-csr-test'] + \
> - (unpack_edk2_blobs ? ['bios-tables-test'] : [])
> + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
> + (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : [])
>
> qos_test_ss = ss.source_set()
> qos_test_ss.add(
Acked-by: Fabiano Rosas <farosas@suse.de>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-02-02 19:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-30 7:19 [PATCH v5 0/5] Add support for K230 board Chao Liu
2026-01-30 7:19 ` [PATCH v5 1/5] target/riscv: add thead-c908 cpu support Chao Liu
2026-01-30 7:19 ` [PATCH v5 2/5] hw/riscv: add k230 board initial support Chao Liu
2026-01-30 7:19 ` [PATCH v5 3/5] hw/watchdog: add k230 watchdog " Chao Liu
2026-01-30 7:19 ` [PATCH v5 4/5] tests/qtest: add test for K230 watchdog Chao Liu
2026-02-02 19:38 ` Fabiano Rosas
2026-01-30 7:33 ` [PATCH v5 5/5] docs/system/riscv: add documentation for k230 machine Chao Liu
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.