From nobody Fri Nov 14 15:22:01 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1762368795; cv=none; d=zohomail.com; s=zohoarc; b=OORtuD7XFaFCXY+DmUAZm7FM7ardZypsai126j08sq8orfrA25KPxnBHoL3ySPBkUlkBCrzm9texKMzYifdf4V3mhJhhcf8d/1cPMZTMSzQB4lBuOtjyalCrWVrEObg4X5JzXDkkRYS9PtK3l2R70W6sqnyvb4/MqbgzRXL8qqA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762368795; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=9kEAytXiAZieewyrZPC8a4xUKmBzYD9xvtE9UlfGJ7s=; b=Y6SlLcCfU4A+LQU9N6XOPO4TEhBk6Mm4r5HU3rehoiL5UtnVSge68rLWGZBy2S77MHM8X1zvdl5htBqNOiyBsJs2314m4gQFQ3zWOfu0j2RVpRDN2sGlTxq5EnudmrWDnDHY7lofGr5gLSSBL9w5dzXJS6DCwth5f2HN8Q8PtJY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762368795042289.5291541456535; Wed, 5 Nov 2025 10:53:15 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGic7-0004Mz-Ol; Wed, 05 Nov 2025 13:52:11 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGib3-0003zW-Mb for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:06 -0500 Received: from mail-wm1-x335.google.com ([2a00:1450:4864:20::335]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vGiaz-0003C0-5I for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:05 -0500 Received: by mail-wm1-x335.google.com with SMTP id 5b1f17b1804b1-47755de027eso1083125e9.0 for ; Wed, 05 Nov 2025 10:51:00 -0800 (PST) Received: from DDesktop.local ([2a10:8012:d:eea4:f4de:376b:66b1:d7b5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb42a354sm270622f8f.20.2025.11.05.10.50.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Nov 2025 10:50:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762368659; x=1762973459; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9kEAytXiAZieewyrZPC8a4xUKmBzYD9xvtE9UlfGJ7s=; b=EFhzQbbv0wPlWykprjP3WKV6CmO+27CXO3CmArVK+u9jl8is1/7IErjbmBb5oYYaos OhXU8AWnL+P0ABXDugPA64dKfztoZW/uC9kZuX/nAMyf+aeT2Rj2q17Wm/oV+GPq5m5K MA0N5af18KTnbDLSVBOUSiC1GdIDPpkh6bhfLVbXaJ17cPztfVtlvgG7sumhV+ABoT+Z KzsqyHyCOVdm69/ykmEbO4WrFr9LL/wsjavTQ1DQQCHzK7ZeBCK3SifhYaJYMWAjEXTS UdP1bN41pZMqrrZ0KgLRaG9SiLq6mdKSIR4CUKHjHWzmHvvYxnIeyhEEVSLamonclYHe a/Ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762368659; x=1762973459; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9kEAytXiAZieewyrZPC8a4xUKmBzYD9xvtE9UlfGJ7s=; b=j6EeavBCFdrTlxD1wxIds8tOBlwB67PoERTrBSjvo723dLgkPWWanPYBE2q12K5okg iYTqe4zBNfZ/vBl/3Emb9J1Nry3SGkQDHTO81TI9TwifIabQN5KPleoOL5ndv1sNwjzN Y1Zd2FYa/T4WSq0eCpiTKxtVyCme/o4IzGJs1OVLsQwuftFfM2JUtXXM2o0+murqszd7 vNpfrkiluipuQebYpcrZW/eR8D5asnYNqQYNhAoTqDUogT99fYbHoLarRIn4cuvvrnp0 FDICk9kKT9FXnb1cLKXdW/+qqW5R+EJ91QmQXuO3dU6V3yvf646BFvFU0QSCt0jIc2oV /71g== X-Gm-Message-State: AOJu0YwwCn39K3OKfVRhmiebLJaI9BoG/FBu9C0lHjlZ4KmDl12iay3x nfWC3Uz3g8GK/Xbd6Cc903IV6/jBIGXsJAFc+np/D04Rm8cSPRN6w76OebgesWjrp6swrHDgduo EOAwvMgk= X-Gm-Gg: ASbGncuyTThAIbabqx7UYUaivDxoeGve0PpCf4mXvBnkKjStGuAg+FzMbCD/NTMzZiu JRsfIWBRKAHFGNt+0gZiSqNuS+ymgvZvD5yglwxGw4KJFnVMu3D7HyyWbrZQlrBmj18w3vff3JP /sxwbLLNsAdFblagMj6Vq7JybHEgtw0O8i4JPWWbv0JbQAbDu6yyVIT94fbbQ0XSeh+6I/fQz2+ 2O/uUBFVf5tGpFClneqz0AYq/BzfqKe9ht8N+ao0cd+xVK6FGHwH6Xd7DDL6Z3/j68E/aHQH3ju 2WjZYQqqljxAzF2x4eW/sLQIHJXrpc7E9sr9hNk1qv/72LSqON3gdFzUGd0k9+/qWvyrqAhsefT zakMJMWFiBqq9y0ma4PZwc/9gFxD4dR8izkRVNInOaXxLf0H5Dd67RtScnlspqCnOUfNbAX3g+G YEVhyq X-Google-Smtp-Source: AGHT+IFr3rNa2xQ4e9vLjx1Y/rvEMSbm9dL0Zbqa9OTH9Dnzfb9Wt0IYMagpbyuMQh9ZJht7z2oaxg== X-Received: by 2002:a05:600c:6288:b0:471:1717:40f with SMTP id 5b1f17b1804b1-4775cdf4317mr34076545e9.22.1762368659324; Wed, 05 Nov 2025 10:50:59 -0800 (PST) From: Michael Levit To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, philmd@linaro.org, pbonzini@redhat.com, dbarboza@ventanamicro.com, zhiwei_liu@linux.alibaba.com, liwei1518@gmail.com, smishash@gmail.com Subject: [PATCH v3 1/5] target/riscv: add NEORV32 RV32 CPU type and vendor CSR hooks Date: Wed, 5 Nov 2025 20:50:52 +0200 Message-ID: <20251105185056.23565-2-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251105185056.23565-1-michael@videogpu.com> References: <20251105185056.23565-1-michael@videogpu.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=2a00:1450:4864:20::335; envelope-from=michael@videogpu.com; helo=mail-wm1-x335.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @videogpu-com.20230601.gappssmtp.com) X-ZM-MESSAGEID: 1762368796507154101 Content-Type: text/plain; charset="utf-8" From: Michael Introduce NEORV32 RV32 CPU type under target/riscv, wire NEORV32 vendor ID, and add a vendor CSR (CSR_MXISA) guarded by mvendorid match, plus meson glu= e. Signed-off-by: Michael Levit Reviewed-by: Daniel Henrique Barboza --- target/riscv/cpu-qom.h | 2 ++ target/riscv/cpu.c | 18 ++++++++++++++ target/riscv/cpu.h | 3 +++ target/riscv/cpu_cfg.h | 1 + target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/cpu_vendorid.h | 2 ++ target/riscv/meson.build | 1 + target/riscv/neorv32_csr.c | 40 +++++++++++++++++++++++++++++++ 8 files changed, 68 insertions(+) create mode 100644 target/riscv/neorv32_csr.c diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 75f4e43408..d091807160 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -43,6 +43,7 @@ #define TYPE_RISCV_CPU_RVA23U64 RISCV_CPU_TYPE_NAME("rva23u64") #define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") #define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") +#define TYPE_RISCV_CPU_NEORV32 RISCV_CPU_TYPE_NAME("neorv32") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") #define TYPE_RISCV_CPU_SIFIVE_E RISCV_CPU_TYPE_NAME("sifive-e") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") @@ -58,6 +59,7 @@ #define TYPE_RISCV_CPU_XIANGSHAN_KMH RISCV_CPU_TYPE_NAME("xiangshan-kun= minghu") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") =20 + OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) =20 #endif /* RISCV_CPU_QOM_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 73d4280d7c..ffe99f71e1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -233,6 +233,7 @@ const RISCVIsaExtData isa_edata_arr[] =3D { ISA_EXT_DATA_ENTRY(svrsw60t59b, PRIV_VERSION_1_13_0, ext_svrsw60t59b), ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), + ISA_EXT_DATA_ENTRY(xneorv32xisa, PRIV_VERSION_1_10_0, ext_xneorv32xisa= ), ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), ISA_EXT_DATA_ENTRY(xtheadbb, PRIV_VERSION_1_11_0, ext_xtheadbb), ISA_EXT_DATA_ENTRY(xtheadbs, PRIV_VERSION_1_11_0, ext_xtheadbs), @@ -1366,6 +1367,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = =3D { MULTI_EXT_CFG_BOOL("xtheadmempair", ext_xtheadmempair, false), MULTI_EXT_CFG_BOOL("xtheadsync", ext_xtheadsync, false), MULTI_EXT_CFG_BOOL("xventanacondops", ext_XVentanaCondOps, false), + MULTI_EXT_CFG_BOOL("xneorv32xisa", ext_xneorv32xisa, false), =20 { }, }; @@ -3032,6 +3034,7 @@ static const TypeInfo riscv_cpu_type_infos[] =3D { .cfg.pmp_regions =3D 8 ), =20 + #if defined(TARGET_RISCV32) || \ (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, @@ -3075,6 +3078,21 @@ static const TypeInfo riscv_cpu_type_infos[] =3D { .misa_mxl_max =3D MXL_RV32, .misa_ext =3D RVE ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_NEORV32, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max =3D MXL_RV32, + .misa_ext =3D RVI | RVM | RVA | RVC | RVU, + .priv_spec =3D PRIV_VERSION_1_10_0, + + .cfg.max_satp_mode =3D VM_1_10_MBARE, + .cfg.ext_zifencei =3D true, + .cfg.ext_zicsr =3D true, + .cfg.pmp =3D true, + .cfg.pmp_regions =3D 16, + .cfg.mvendorid =3D NEORV32_VENDOR_ID, +#ifndef CONFIG_USER_ONLY + .custom_csrs =3D neorv32_csr_list +#endif + ), #endif =20 #if (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 36e7f10037..6a9918a25a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -985,5 +985,8 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32= _bit); /* In th_csr.c */ extern const RISCVCSR th_csr_list[]; =20 +/* Implemented in neorv32_csr.c */ +extern const RISCVCSR neorv32_csr_list[]; + const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index aa28dc8d7e..9ad38506e4 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -64,5 +64,6 @@ MATERIALISE_EXT_PREDICATE(xtheadmemidx) MATERIALISE_EXT_PREDICATE(xtheadmempair) MATERIALISE_EXT_PREDICATE(xtheadsync) MATERIALISE_EXT_PREDICATE(XVentanaCondOps) +MATERIALISE_EXT_PREDICATE(xneorv32xisa) =20 #endif diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_field= s.h.inc index a154ecdc79..b84e1bd287 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -147,6 +147,7 @@ BOOL_FIELD(ext_xtheadmemidx) BOOL_FIELD(ext_xtheadmempair) BOOL_FIELD(ext_xtheadsync) BOOL_FIELD(ext_XVentanaCondOps) +BOOL_FIELD(ext_xneorv32xisa) =20 BOOL_FIELD(mmu) BOOL_FIELD(pmp) diff --git a/target/riscv/cpu_vendorid.h b/target/riscv/cpu_vendorid.h index 96b6b9c2cb..66a8f30b81 100644 --- a/target/riscv/cpu_vendorid.h +++ b/target/riscv/cpu_vendorid.h @@ -7,4 +7,6 @@ #define VEYRON_V1_MIMPID 0x111 #define VEYRON_V1_MVENDORID 0x61f =20 +#define NEORV32_VENDOR_ID 0xF0000001 + #endif /* TARGET_RISCV_CPU_VENDORID_H */ diff --git a/target/riscv/meson.build b/target/riscv/meson.build index fdefe88ccd..44e706ad3f 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -40,6 +40,7 @@ riscv_system_ss.add(files( 'th_csr.c', 'time_helper.c', 'riscv-qmp-cmds.c', + 'neorv32_csr.c', )) =20 subdir('tcg') diff --git a/target/riscv/neorv32_csr.c b/target/riscv/neorv32_csr.c new file mode 100644 index 0000000000..3b0f0cab05 --- /dev/null +++ b/target/riscv/neorv32_csr.c @@ -0,0 +1,40 @@ +/* + * NEORV32-specific CSR. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_vendorid.h" + +#define CSR_MXISA (0xfc0) + +static RISCVException smode(CPURISCVState *env, int csrno) +{ + return RISCV_EXCP_NONE; +} + +static RISCVException read_neorv32_xisa(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* We don't support any extension for now on QEMU */ + *val =3D 0x00; + return RISCV_EXCP_NONE; +} + +static bool test_neorv32_mvendorid(RISCVCPU *cpu) +{ + return cpu->cfg.mvendorid =3D=3D NEORV32_VENDOR_ID; +} + +const RISCVCSR neorv32_csr_list[] =3D { + { + .csrno =3D CSR_MXISA, + .insertion_test =3D test_neorv32_mvendorid, + .csr_ops =3D { "neorv32.xisa", smode, read_neorv32_xisa } + }, + { } +}; + --=20 2.51.1 From nobody Fri Nov 14 15:22:01 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1762368779; cv=none; d=zohomail.com; s=zohoarc; b=G+vb6klBX4AT1Ako3HQcZmvW7oKuYmhzafHJ609XVIzdvGzi4bJ5q89wNm1J4oUFa+fKz16AfIw7Rf9Ia3WtImNRg6QsznkY649JRPwL8chyE1JkRIS+GrndnQYGuGi14izJIPt9rGXIqU5h2Jsxg0Cm0J/tsSV45aBkMnqgUMA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762368779; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=5BHzlRkBUYh2EjQ2JUIqKE4S0PnVMUwsZ8qt/uPZSfU=; b=LD362dzJq1tFcPROvJ5jhvDd43sN4XK07JkT6ZGO11fdAFsm+Wzz+aPthIhefUh+1mX5mcG9xuylur4d72FNRgQAxbaFTxeVcJLkK4q9hwlHGr57LC1/Y6LNjAdvVyMImROpJnjZcssuYVAzkkcIYe16/iTNJkvcudX1WQde1/s= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762368778996756.1298162012534; Wed, 5 Nov 2025 10:52:58 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGibW-00048p-Gp; Wed, 05 Nov 2025 13:51:37 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGib4-0003zY-QB for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:10 -0500 Received: from mail-wm1-x332.google.com ([2a00:1450:4864:20::332]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vGib0-0003CB-RB for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:06 -0500 Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-4775a52e045so376885e9.3 for ; Wed, 05 Nov 2025 10:51:02 -0800 (PST) Received: from DDesktop.local ([2a10:8012:d:eea4:f4de:376b:66b1:d7b5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb42a354sm270622f8f.20.2025.11.05.10.50.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Nov 2025 10:51:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762368661; x=1762973461; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5BHzlRkBUYh2EjQ2JUIqKE4S0PnVMUwsZ8qt/uPZSfU=; b=XmT6qMDahnu8Q6JwaeScbMkTYwtSoC1JUzo8bciC+Q8s+9f+gBG/d1agXsnyTqGSjX +fzH34jfZiCn2GokWuhaQi50F0sZ0852ouyCNLp2GQNQ2TZWmGYixrVoLRFbfJ9tOUtE EtsP/nfDQwrKJ3EKc14/8Cab2gCWVdiUhMP4RfAbDLb8BUmV3i1S35ZlkNc9sPVZzHhh FlZxg25IsCtzELV1hVfq0PjU+flRpZooCny/nFQYDWzqBYii/ir981XoW6dl9bSTbMt5 HTp2Rkc/GPV26AKaEH6WZ33U0MT3xk+rJjtjNVvDqW5ocb+gwHStfewibAUYr0HPZxx3 QmuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762368661; x=1762973461; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5BHzlRkBUYh2EjQ2JUIqKE4S0PnVMUwsZ8qt/uPZSfU=; b=I7LjKtblz/QUTQVJMcBb7q3r+rAYglufR3KLklC//Li3NKOykqQZRN46FK8Uy/mJCu vusVLPzk0t0lScqkqoYAbtiTyoDNEqeOyyTmscJ2vgVzXIIXyEhqwAXdmYKf5U0OTzrr HNp4od6bIdaxvq4JIdlzG4Xv4mTjsHEUv4BskMpI75C7AS8LOoKZYLWEV/U36D6hCM5s BexgxV9/zAKxVw2hX0ZTvH7JiP/4HLxE/dW8m+WgRg9ZBvkzHevI+v1RD9mmDtqTntxJ W68jXLjaUvDRkSR8ePNeF0oIpjUrLLpPQJLAfS5ny5Gz9rrJwLsJZ69PQG6Xxyt8wcvT xssQ== X-Gm-Message-State: AOJu0YxlyGQsCT9Md1gLcoXExj9dU0ElnEcbZBJFoyCLYc11mRmp9agJ BatdrlUn8MyYmT7n83PWMgUt7WUqVxaH8W3ZFh08z3zLj0ePjiz96mUr73jpf8a+7IUZT74ncyB bnbD5F5Q= X-Gm-Gg: ASbGncuoKsU15KpzYteT0pkmrU1Oiku11ae2dqbeomGH8OG8RnYF0Y7vDL7eFFQzTGE VzGJjcGuk4s4J52pAl5mWk7e73T9V3IeDmXsoZYlIEnFC0pRuhrI7UlB9jFRwvE6WxPIHIOFtSa LmqeNkahB1xs9iy+gVMzg/Wtlh1IHhMHxalWniDeM9WwQmzFxW6jUZPbIRzTYHNB/ULCjelgxrT GA2otyNEt7m3Anz1OsO3ymNhlbS4g0MxpHr/nk/svKw1eydQ6dS03zicj9JqMwpK3mA6zlHhs7L fC9rflrNriX1A0DyGtjtT3mXgP332r5JaHuJ6dZ2xBTS8hVVzT5jnN7PofiTyiTQyW5vg09jMh0 8nUz09Rl+gJGeC0HyufAwpYJJjr5pzFLO1P8nT6CDUtEp7ehjR9FqYVHm0Z/YNRpHSQD9dl8Jfj Pm4srOEeU2Na5N4Ec= X-Google-Smtp-Source: AGHT+IGWMsr/Fllml8pRNdOg083d7fdM/SpV6egSwCWWHV6eOq1/B+gOkK386GXBafAZjHffK7VnsA== X-Received: by 2002:a05:6000:2408:b0:429:ca3c:b802 with SMTP id ffacd0b85a97d-429e3309404mr4195497f8f.44.1762368660530; Wed, 05 Nov 2025 10:51:00 -0800 (PST) From: Michael Levit To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, philmd@linaro.org, pbonzini@redhat.com, dbarboza@ventanamicro.com, zhiwei_liu@linux.alibaba.com, liwei1518@gmail.com, smishash@gmail.com Subject: [PATCH v3 2/5] hw/misc: add NEORV32 SYSINFO block (CLK/MISC/SOC/CACHE) Date: Wed, 5 Nov 2025 20:50:53 +0200 Message-ID: <20251105185056.23565-3-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251105185056.23565-1-michael@videogpu.com> References: <20251105185056.23565-1-michael@videogpu.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=2a00:1450:4864:20::332; envelope-from=michael@videogpu.com; helo=mail-wm1-x332.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @videogpu-com.20230601.gappssmtp.com) X-ZM-MESSAGEID: 1762368780515154100 Content-Type: text/plain; charset="utf-8" From: Michael Add a minimal SYSINFO MMIO device compatible with NEORV32 SDK expectations: CLK (rw), MISC/SOC/CACHE (ro) composed from constants. Includes Kconfig/mes= on. Signed-off-by: Michael Levit --- hw/misc/Kconfig | 2 + hw/misc/meson.build | 1 + hw/misc/neorv32_sysinfo.c | 201 ++++++++++++++++++++++++++++ hw/misc/neorv32_sysinfo.h | 88 +++++++++++++ hw/misc/neorv32_sysinfo_rtl.h | 239 ++++++++++++++++++++++++++++++++++ 5 files changed, 531 insertions(+) create mode 100644 hw/misc/neorv32_sysinfo.c create mode 100644 hw/misc/neorv32_sysinfo.h create mode 100644 hw/misc/neorv32_sysinfo_rtl.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index fccd735c24..dba77acb63 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -240,4 +240,6 @@ config IOSB config XLNX_VERSAL_TRNG bool =20 +config NEORV32_SYSINFO_QEMU + bool source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index b1d8d8e5d2..4ea46ec2d1 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -34,6 +34,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: file= s('sifive_e_prci.c')) system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'= )) system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'= )) system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.= c')) +system_ss.add(when: 'CONFIG_NEORV32_SYSINFO_QEMU', if_true: files('neorv32= _sysinfo.c')) =20 subdir('macio') =20 diff --git a/hw/misc/neorv32_sysinfo.c b/hw/misc/neorv32_sysinfo.c new file mode 100644 index 0000000000..e64a003120 --- /dev/null +++ b/hw/misc/neorv32_sysinfo.c @@ -0,0 +1,201 @@ +/* + * Neorv32 SysInfo + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "system/address-spaces.h" +#include "neorv32_sysinfo.h" /* QEMU related */ +#include "neorv32_sysinfo_rtl.h" /* RTL related */ + + +/* Register addresses (offsets) */ +enum { + REG_SYSINFO_CLK =3D 0x00, + REG_SYSINFO_MISC =3D 0x04, + REG_SYSINFO_SOC =3D 0x08, + REG_SYSINFO_CACHE =3D 0x0C, +}; + + +typedef struct Neorv32SysInfoState { + MemoryRegion mmio; + uint32_t clk_hz; /* rw */ + uint32_t misc; /* ro */ + uint32_t soc; /* ro */ + uint32_t cache; /* ro */ +} Neorv32SysInfoState; + + +/* Safe integer log2: assumes power-of-two sizes; returns 0 if size is 0 */ +static unsigned int neorv32_log2u(uint32_t x) +{ + if (x =3D=3D 0U) { + return 0U; + } + unsigned int r =3D 0U; + while ((x >>=3D 1U) !=3D 0U) { + r++; + } + return r; +} + +/* Compose MISC register per the firmware header */ +static uint32_t neorv32_sysinfo_build_misc(void) +{ + const uint32_t imem_log2 =3D + neorv32_log2u(SYSINFO_IMEM_SIZE) & 0xFFU; /* Bits [7:0] */ + const uint32_t dmem_log2 =3D + neorv32_log2u(SYSINFO_DMEM_SIZE) & 0xFFU; /* Bits [15:8] */ + const uint32_t harts =3D + (SYSINFO_NUM_HARTS & 0x0FU); /* Bits [19:16] */ + const uint32_t bootmode =3D + (SYSINFO_BOOTMODE_ID & 0x03U); /* Bits [21:20] */ + const uint32_t intbus_to =3D + (SYSINFO_INTBUS_TO_LOG2 & 0x1FU); /* Bits [26:22] */ + const uint32_t extbus_to =3D + (SYSINFO_EXTBUS_TO_LOG2 & 0x1FU); /* Bits [31:27] */ + + uint32_t v =3D 0U; + v |=3D (imem_log2 << 0); + v |=3D (dmem_log2 << 8); + v |=3D (harts << 16); + v |=3D (bootmode << 20); + v |=3D (intbus_to << 22); + v |=3D (extbus_to << 27); + return v; +} + +/* Compose CACHE register per the firmware header */ +static uint32_t neorv32_sysinfo_build_cache(void) +{ + uint32_t v =3D 0U; + v |=3D ((ICACHE_BLOCK_SIZE_LOG2 & 0x0FU) << 0); + v |=3D ((ICACHE_NUM_BLOCKS_LOG2 & 0x0FU) << 4); + v |=3D ((DCACHE_BLOCK_SIZE_LOG2 & 0x0FU) << 8); + v |=3D ((DCACHE_NUM_BLOCKS_LOG2 & 0x0FU) << 12); + v |=3D ((ICACHE_BURSTS_EN ? 1U : 0U) << 16); + v |=3D ((DCACHE_BURSTS_EN ? 1U : 0U) << 24); + return v; +} + +static uint64_t neorv32_sysinfo_read(void *opaque, hwaddr addr, unsigned s= ize) +{ + Neorv32SysInfoState *s =3D opaque; + uint32_t val =3D 0U; + + switch (addr) { + case REG_SYSINFO_CLK: + val =3D s->clk_hz; + break; + case REG_SYSINFO_MISC: + val =3D s->misc; + break; + case REG_SYSINFO_SOC: + val =3D s->soc; + break; + case REG_SYSINFO_CACHE: + val =3D s->cache; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid read addr=3D0x%" HWADDR_PRIx " size=3D%= u\n", + __func__, addr, size); + return 0; + } + + /* Enforce access size semantics (1/2/4 ok); we just return the low by= tes */ + switch (size) { + case 4: return val; + case 2: return (uint16_t)val; + case 1: return (uint8_t)val; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid read size=3D%u at addr=3D0x%" HWADDR_PR= Ix "\n", + __func__, size, addr); + return 0; + } +} + +static void neorv32_sysinfo_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + Neorv32SysInfoState *s =3D opaque; + + /* Only CLK is writable; others are read-only */ + if (addr =3D=3D REG_SYSINFO_CLK) { + /* Accept 1/2/4 byte writes; update the corresponding bytes of clk= _hz */ + uint32_t old =3D s->clk_hz; + uint32_t val =3D old; + + switch (size) { + case 4: + val =3D (uint32_t)data; + break; + case 2: { + uint16_t part =3D (uint16_t)data; + /* Little-endian halfword at offset (0 or 2) */ + if ((addr & 0x3) =3D=3D 0x0) { + val =3D (old & 0xFFFF0000U) | part; + } else if ((addr & 0x3) =3D=3D 0x2) { + val =3D (old & 0x0000FFFFU) | ((uint32_t)part << 16); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: misaligned 16-bit write at 0x%" + HWADDR_PRIx "\n", + __func__, addr); + + return; + } + break; + } + case 1: { + uint8_t part =3D (uint8_t)data; + uint32_t shift =3D (addr & 0x3) * 8U; + val =3D (old & ~(0xFFU << shift)) | ((uint32_t)part << shift); + break; + } + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid write size=3D%u at addr=3D0x%" + HWADDR_PRIx "\n", + __func__, size, addr); + return; + } + + s->clk_hz =3D val; + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only addr=3D0x%" HWADDR_PRIx " val=3D= 0x%" + PRIx64 " size=3D%u\n", + __func__, addr, data, size); +} + +static const MemoryRegionOps neorv32_sysinfo_ops =3D { + .read =3D neorv32_sysinfo_read, + .write =3D neorv32_sysinfo_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid.min_access_size =3D 1, + .valid.max_access_size =3D 4, +}; + +void neorv32_sysinfo_create(MemoryRegion *address_space, hwaddr base) +{ + Neorv32SysInfoState *s =3D g_new0(Neorv32SysInfoState, 1); + + s->clk_hz =3D SYSINFO_CLK_HZ_DEFAULT; + s->misc =3D neorv32_sysinfo_build_misc(); + s->soc =3D SYSINFO_SOC_VAL; + s->cache =3D neorv32_sysinfo_build_cache(); + + memory_region_init_io(&s->mmio, NULL, &neorv32_sysinfo_ops, + s, "neorv32.sysinfo", 16 /* 4 regs x 4 bytes */); + + memory_region_add_subregion(address_space, base, &s->mmio); +} diff --git a/hw/misc/neorv32_sysinfo.h b/hw/misc/neorv32_sysinfo.h new file mode 100644 index 0000000000..71e6a3a5d1 --- /dev/null +++ b/hw/misc/neorv32_sysinfo.h @@ -0,0 +1,88 @@ +/* + * Neorv32 SysInfo + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_NEORV32_SYSINFO_H +#define HW_NEORV32_SYSINFO_H + +#include "system/memory.h" + + +/* Internal memory sizes (bytes) */ +#define SYSINFO_IMEM_SIZE 0x00008000U /* 32 KiB IMEM */ +#define SYSINFO_DMEM_SIZE 0x00008000U /* 32 KiB DMEM */ + +/* Number of harts (physical cores) */ +#define SYSINFO_NUM_HARTS 1U + +/* Boot mode (matches RTL BOOT_MODE_SELECT encoding used in your firmware)= */ +#define SYSINFO_BOOTMODE_ID 0U /* 0..3 */ + +/* Bus timeout encodings: value is log2(cycles); 0 means "no timeout" */ +#define SYSINFO_INTBUS_TO_LOG2 0U /* 0 =3D> returns 0 */ +#define SYSINFO_EXTBUS_TO_LOG2 0U /* 0 =3D> returns 0 */ + +/* Clock (Hz): writable at runtime via SYSINFO.CLK */ +#define SYSINFO_CLK_HZ_DEFAULT 100000000U /* 100 MHz */ + +/* Cache topology encodings (log2 values ) */ +#define ICACHE_BLOCK_SIZE_LOG2 0U /* bits [3:0] */ +#define ICACHE_NUM_BLOCKS_LOG2 0U /* bits [7:4] */ +#define DCACHE_BLOCK_SIZE_LOG2 0U /* bits [11:8] */ +#define DCACHE_NUM_BLOCKS_LOG2 0U /* bits [15:12] */ +#define ICACHE_BURSTS_EN 0U /* bit 16 */ +#define DCACHE_BURSTS_EN 0U /* bit 24 */ + +/* Feature bitmap for SOC register. */ +#define SYSINFO_SOC_ENABLE(x) (1U << (x)) + +/* Enable Bootloader, IMEM, DMEM, UART and SPI */ +#define SYSINFO_SOC_VAL \ + (SYSINFO_SOC_ENABLE(SYSINFO_SOC_BOOTLOADER) | \ + SYSINFO_SOC_ENABLE(SYSINFO_SOC_IMEM) | \ + SYSINFO_SOC_ENABLE(SYSINFO_SOC_DMEM) | \ + SYSINFO_SOC_ENABLE(SYSINFO_SOC_IO_UART0) | \ + SYSINFO_SOC_ENABLE(SYSINFO_SOC_IO_SPI)) + +/* + * -----------------------------------------------------------------------= -- + * Address map + * -----------------------------------------------------------------------= -- + */ +#define NEORV32_BOOTLOADER_BASE_ADDRESS (0xFFE00000U) +#define NEORV32_IO_BASE_ADDRESS (0xFFE00000U) + +#define NEORV32_IMEM_BASE (0x00000000U) +#define NEORV32_DMEM_BASE (0x80000000U) + +/* IO base addresses */ +#define NEORV32_TWD_BASE (0xFFEA0000U) +#define NEORV32_CFS_BASE (0xFFEB0000U) +#define NEORV32_SLINK_BASE (0xFFEC0000U) +#define NEORV32_DMA_BASE (0xFFED0000U) +#define NEORV32_CRC_BASE (0xFFEE0000U) +#define NEORV32_XIP_BASE (0xFFEF0000U) +#define NEORV32_PWM_BASE (0xFFF00000U) +#define NEORV32_GPTMR_BASE (0xFFF10000U) +#define NEORV32_ONEWIRE_BASE (0xFFF20000U) +#define NEORV32_XIRQ_BASE (0xFFF30000U) +#define NEORV32_MTIME_BASE (0xFFF40000U) +#define NEORV32_UART0_BASE (0xFFF50000U) +#define NEORV32_UART1_BASE (0xFFF60000U) +#define NEORV32_SDI_BASE (0xFFF70000U) +#define NEORV32_SPI_BASE (0xFFF80000U) +#define NEORV32_TWI_BASE (0xFFF90000U) +#define NEORV32_TRNG_BASE (0xFFFA0000U) +#define NEORV32_WDT_BASE (0xFFFB0000U) +#define NEORV32_GPIO_BASE (0xFFFC0000U) +#define NEORV32_NEOLED_BASE (0xFFFD0000U) +#define NEORV32_SYSINFO_BASE (0xFFFE0000U) +#define NEORV32_DM_BASE (0xFFFF0000U) + +/* MMIO creator */ +void neorv32_sysinfo_create(MemoryRegion *address_space, hwaddr base); + +#endif /* HW_NEORV32_SYSINFO_H */ diff --git a/hw/misc/neorv32_sysinfo_rtl.h b/hw/misc/neorv32_sysinfo_rtl.h new file mode 100644 index 0000000000..524400438b --- /dev/null +++ b/hw/misc/neorv32_sysinfo_rtl.h @@ -0,0 +1,239 @@ +/* + * NEORV32 RTL specific definitions. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * NEORV32: neorv32_sysinfo.h - System Information Memory (SYSINFO) HW dri= ver. + * + * BSD 3-Clause License. + * + * Copyright (c) 2023, Stephan Nolting. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are = met: + * + * 1. Redistributions of source code must retain the above copyright notic= e, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED T= O, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The NEORV32 Processor: https://github.com/stnolting/neorv32 + */ + +#ifndef NEORV32_SYSINFO_RTL_H +#define NEORV32_SYSINFO_RTL_H + +/* + * IO Device: System Configuration Information Memory (SYSINFO). + */ + +typedef struct __attribute__((packed, aligned(4))) { + uint32_t CLK; /* Offset 0: Clock speed in Hz. */ + const uint32_t MISC; /* + * Offset 4: Misc system configuration bits. + * See enum NEORV32_SYSINFO_MISC_enum. + */ + const uint32_t SOC; /* + * Offset 8: Implemented SoC features. + * See enum NEORV32_SYSINFO_SOC_enum. + */ + const uint32_t CACHE; /* + * Offset 12: Cache configuration. + * See enum NEORV32_SYSINFO_CACHE_enum. + */ +} neorv32_sysinfo_t; + +/* SYSINFO module hardware access. */ +#define NEORV32_SYSINFO ((neorv32_sysinfo_t *)(NEORV32_SYSINFO_BASE)) + +/* + * NEORV32_SYSINFO.MISC (r/-): Miscellaneous system configurations. + */ +enum NEORV32_SYSINFO_MISC_enum { + /* log2(internal IMEM size in bytes) (via IMEM_SIZE generic). */ + SYSINFO_MISC_IMEM_LSB =3D 0, /* LSB. */ + SYSINFO_MISC_IMEM_MSB =3D 7, /* MSB. */ + + /* log2(internal DMEM size in bytes) (via DMEM_SIZE generic). */ + SYSINFO_MISC_DMEM_LSB =3D 8, /* LSB. */ + SYSINFO_MISC_DMEM_MSB =3D 15, /* MSB. */ + + /* Number of physical CPU cores ("harts"). */ + SYSINFO_MISC_HART_LSB =3D 16, /* LSB. */ + SYSINFO_MISC_HART_MSB =3D 19, /* MSB. */ + + /* Boot mode configuration (via BOOT_MODE_SELECT generic). */ + SYSINFO_MISC_BOOT_LSB =3D 20, /* LSB. */ + SYSINFO_MISC_BOOT_MSB =3D 21, /* MSB. */ + + /* log2(internal bus timeout cycles). */ + SYSINFO_MISC_ITMO_LSB =3D 22, /* LSB. */ + SYSINFO_MISC_ITMO_MSB =3D 26, /* MSB. */ + + /* log2(external bus timeout cycles). */ + SYSINFO_MISC_ETMO_LSB =3D 27, /* LSB. */ + SYSINFO_MISC_ETMO_MSB =3D 31 /* MSB. */ +}; + +/* + * NEORV32_SYSINFO.SOC (r/-): Implemented processor devices/features. + */ +enum NEORV32_SYSINFO_SOC_enum { + /* Bootloader implemented when 1 (via BOOT_MODE_SELECT). */ + SYSINFO_SOC_BOOTLOADER =3D 0, + + /* External bus interface implemented when 1 (via XBUS_EN). */ + SYSINFO_SOC_XBUS =3D 1, + + /* Instruction memory implemented when 1 (via IMEM_EN). */ + SYSINFO_SOC_IMEM =3D 2, + + /* Data memory implemented when 1 (via DMEM_EN). */ + SYSINFO_SOC_DMEM =3D 3, + + /* On-chip debugger implemented when 1 (via OCD_EN). */ + SYSINFO_SOC_OCD =3D 4, + + /* Instruction cache implemented when 1 (via ICACHE_EN). */ + SYSINFO_SOC_ICACHE =3D 5, + + /* Data cache implemented when 1 (via DCACHE_EN). */ + SYSINFO_SOC_DCACHE =3D 6, + + /* Reserved. */ + /* SYSINFO_SOC_reserved =3D 7, */ + + /* Reserved. */ + /* SYSINFO_SOC_reserved =3D 8, */ + + /* Reserved. */ + /* SYSINFO_SOC_reserved =3D 9, */ + + /* Reserved. */ + /* SYSINFO_SOC_reserved =3D 10, */ + + /* On-chip debugger authentication when 1 (via OCD_AUTHENTICATION). */ + SYSINFO_SOC_OCD_AUTH =3D 11, + + /* + * Instruction memory as pre-initialized ROM when 1 + * (via BOOT_MODE_SELECT). + */ + SYSINFO_SOC_IMEM_ROM =3D 12, + + /* Two-wire device implemented when 1 (via IO_TWD_EN). */ + SYSINFO_SOC_IO_TWD =3D 13, + + /* Direct memory access controller when 1 (via IO_DMA_EN). */ + SYSINFO_SOC_IO_DMA =3D 14, + + /* General purpose I/O port when 1 (via IO_GPIO_EN). */ + SYSINFO_SOC_IO_GPIO =3D 15, + + /* Core local interruptor when 1 (via IO_CLINT_EN). */ + SYSINFO_SOC_IO_CLINT =3D 16, + + /* UART0 when 1 (via IO_UART0_EN). */ + SYSINFO_SOC_IO_UART0 =3D 17, + + /* SPI when 1 (via IO_SPI_EN). */ + SYSINFO_SOC_IO_SPI =3D 18, + + /* TWI when 1 (via IO_TWI_EN). */ + SYSINFO_SOC_IO_TWI =3D 19, + + /* PWM unit when 1 (via IO_PWM_EN). */ + SYSINFO_SOC_IO_PWM =3D 20, + + /* Watchdog timer when 1 (via IO_WDT_EN). */ + SYSINFO_SOC_IO_WDT =3D 21, + + /* Custom functions subsystem when 1 (via IO_CFS_EN). */ + SYSINFO_SOC_IO_CFS =3D 22, + + /* True random number generator when 1 (via IO_TRNG_EN). */ + SYSINFO_SOC_IO_TRNG =3D 23, + + /* Serial data interface when 1 (via IO_SDI_EN). */ + SYSINFO_SOC_IO_SDI =3D 24, + + /* UART1 when 1 (via IO_UART1_EN). */ + SYSINFO_SOC_IO_UART1 =3D 25, + + /* NeoPixel-compatible smart LED IF when 1 (via IO_NEOLED_EN). */ + SYSINFO_SOC_IO_NEOLED =3D 26, + + /* Execution tracer when 1 (via IO_TRACER_EN). */ + SYSINFO_SOC_IO_TRACER =3D 27, + + /* General purpose timer when 1 (via IO_GPTMR_EN). */ + SYSINFO_SOC_IO_GPTMR =3D 28, + + /* Stream link interface when 1 (via IO_SLINK_EN). */ + SYSINFO_SOC_IO_SLINK =3D 29, + + /* 1-wire interface controller when 1 (via IO_ONEWIRE_EN). */ + SYSINFO_SOC_IO_ONEWIRE =3D 30 + + /* Reserved. */ + /* SYSINFO_SOC_reserved =3D 31 */ +}; + +/* + * NEORV32_SYSINFO.CACHE (r/-): Cache configuration. + */ +enum NEORV32_SYSINFO_CACHE_enum { + /* I-cache: log2(block size in bytes), bit 0 (via CACHE_BLOCK_SIZE). */ + SYSINFO_CACHE_INST_BLOCK_SIZE_0 =3D 0, + + /* I-cache: log2(block size in bytes), bit 3 (via CACHE_BLOCK_SIZE). */ + SYSINFO_CACHE_INST_BLOCK_SIZE_3 =3D 3, + + /* I-cache: log2(number of cache blocks), bit 0 (via ICACHE_NUM_BLOCKS= ). */ + SYSINFO_CACHE_INST_NUM_BLOCKS_0 =3D 4, + + /* I-cache: log2(number of cache blocks), bit 3 (via ICACHE_NUM_BLOCKS= ). */ + SYSINFO_CACHE_INST_NUM_BLOCKS_3 =3D 7, + + /* D-cache: log2(block size in bytes), bit 0 (via CACHE_BLOCK_SIZE). */ + SYSINFO_CACHE_DATA_BLOCK_SIZE_0 =3D 8, + + /* D-cache: log2(block size in bytes), bit 3 (via CACHE_BLOCK_SIZE). */ + SYSINFO_CACHE_DATA_BLOCK_SIZE_3 =3D 11, + + /* D-cache: log2(number of cache blocks), bit 0 (via DCACHE_NUM_BLOCKS= ). */ + SYSINFO_CACHE_DATA_NUM_BLOCKS_0 =3D 12, + + /* D-cache: log2(number of cache blocks), bit 3 (via DCACHE_NUM_BLOCKS= ). */ + SYSINFO_CACHE_DATA_NUM_BLOCKS_3 =3D 15, + + /* I-cache: issue burst transfers on update (via CACHE_BURSTS_EN). */ + SYSINFO_CACHE_INST_BURSTS_EN =3D 16, + + /* D-cache: issue burst transfers on update (via CACHE_BURSTS_EN). */ + SYSINFO_CACHE_DATA_BURSTS_EN =3D 24 +}; + +#endif /* NEORV32_SYSINFO_RTL_H */ --=20 2.51.1 From nobody Fri Nov 14 15:22:01 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1762368744; cv=none; d=zohomail.com; s=zohoarc; b=eCl1vpeCdrVC7U0wdyH5jtICHg66LXCBQBXDC8UnGxz+f8k8fTJRTWclVD/wHoyJvMkXodZofo8VCVF7befVo6rp5reG78RY+fbQ8gGpPe0aGt0UWndPO2yUCycl7dIn4KZPT1lWdQ4sSNJYuVtuZXMqzFgTKPDdnEoEckJwI8c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762368744; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=coXM3aabJBT21GBiTqQtXzlDQRzt3k5zaMleXYUcg/A=; b=WuUyoL1Iwh7qmHP9E6HvCCTe9HmSwj+q16nwbTFmy2SKmmKIq8V3EnoPETUQcHJ7fgE8fzKs4DvwO0eHLEDWbB40XJAMnUBAohoDn46KVooJudBqRwfpWh0VhxeY8Rk4VWdhai7KuKdhP7bc0HKld9V5cI489ttP0zys0zI2mSk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762368744755365.74885001482903; Wed, 5 Nov 2025 10:52:24 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGibQ-000486-8Z; Wed, 05 Nov 2025 13:51:30 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGib5-0003zb-DZ for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:10 -0500 Received: from mail-wm1-x32d.google.com ([2a00:1450:4864:20::32d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vGib1-0003CR-Ew for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:07 -0500 Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-4775895d69cso739165e9.0 for ; Wed, 05 Nov 2025 10:51:03 -0800 (PST) Received: from DDesktop.local ([2a10:8012:d:eea4:f4de:376b:66b1:d7b5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb42a354sm270622f8f.20.2025.11.05.10.51.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Nov 2025 10:51:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762368662; x=1762973462; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=coXM3aabJBT21GBiTqQtXzlDQRzt3k5zaMleXYUcg/A=; b=28JqkVzo6laD33qWHbvEnGvgE56xMTqkfNUKSEE5+9OFfcvWvEow41wYI7RY2l7+yH qCdrIWB+nGrpXcVwe6lFNb4+jTHM6hjc5JfMzxNKAS190qTL6dwvFsX5qMoSuJ6RTcu9 zOhsmjgm/hR1Y62bnC2oT+Ckgu0geBWYUZSXWdg6448X71eeZ2sHFsChFossxK38a22/ uNFWC5niqa9Glh+z29V7lFgZ1mrwJDGvLLP/oT7sYY2IiSmoyhoLbfiPWNJgVgIeVyVI mvUTAWP66GGlVUPvgR/hVX8uXOeL0Nwb/QC40LMIUELgHumOf3/QPDQGNGMaWu5eJmk7 BAlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762368662; x=1762973462; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=coXM3aabJBT21GBiTqQtXzlDQRzt3k5zaMleXYUcg/A=; b=nOXfoyT8baqT+qejKrVuwBrSBgsJXXnO9db0JvbZBWN9zlTuffTJZTH6U/XadBzKOf DULb6w1VlYd3oUhgnyZVeGeQik8jQ0QbOA1+vOoGVd1zhPwR76pkDMicUFZodp4Jn7Un veQ0s7TnZ/wqI0v0YGyEJXWGwr4JrAPHrfglIHtFJLmAtVnIMx1TVOO5JAwHHCHf2tqe lp7yPfDkS5WRdZrQ63Kp49KJpX5hnH/qD46oLVlHAoliZq+pTUB9YaJbkk2JBV7dUR6K EW1O0rqq5tfvscx3Z++n8Hpn+fkieidfBLEizi0qzLiZmkPXt3Vr6jhOdotGApQtXs7d HpXg== X-Gm-Message-State: AOJu0YznatakrNaT89TYIl7NRnf5IWWnyUMIuCQrp1ZcjtJeg3d8pqtt ly5CxetOkApKEbMZvGkoc6UR6dxidwTtb+VEBcLqVPJRzmHKrSJTIrvVoBFPjqLqsiJmjJZuwUt 0+cehiC8= X-Gm-Gg: ASbGncsMP04kVYFPwXr2Q+qe+lbr0ARBkJUHZ4YTsUCHqFC7XKpKBFeW8+EzZR6VBZy 5Y9QjtIQqjvatbZg2fg7QHPCjgrX8bRPr4OLyi3Ww8wqV3TPFjgLw2UKVIr8me2daWNUqodqhsU cqDCyuPWz/jzYtepFCGtOljngkW5X7TG6yie6TlHIB2hUD5+4e5e7VJL4QV85LYOYA1oJY0jsgF aStDgtVdj4DFTgtiYxyYTnm4CCvtQ6zuEo+iKCWI+r+Q59je+pdsKSo+zjDQsVlRNHGr+xWCj4q A+t8kgPhI/VPcBVgsrTHxjkc+N949RJ/lYSQ0zC11aCTPIbOjJhnIUuRlMt9LVel/b1govHdk/U NdMuiz2VXkprE5yUQF4lol8ZIZn4be1mQ5/IunG9LJJB2cJUe1R0IsJpM6VZ9Yc/nptpYfU1Qji LFEjf/ X-Google-Smtp-Source: AGHT+IGUpsQef6CswpzqnC03cj5TO5S5MaTiNXw8E+nSB9nEM787Ddm0M2MFcpG5ecy8AZ+G/ilOmw== X-Received: by 2002:a05:600c:1e1d:b0:477:a9e:859a with SMTP id 5b1f17b1804b1-4775cdf54e7mr39292695e9.22.1762368661666; Wed, 05 Nov 2025 10:51:01 -0800 (PST) From: Michael Levit To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, philmd@linaro.org, pbonzini@redhat.com, dbarboza@ventanamicro.com, zhiwei_liu@linux.alibaba.com, liwei1518@gmail.com, smishash@gmail.com Subject: [PATCH v3 3/5] hw/char: add NEORV32 UART (CTRL/DATA, fifo, chardev) Date: Wed, 5 Nov 2025 20:50:54 +0200 Message-ID: <20251105185056.23565-4-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251105185056.23565-1-michael@videogpu.com> References: <20251105185056.23565-1-michael@videogpu.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=2a00:1450:4864:20::32d; envelope-from=michael@videogpu.com; helo=mail-wm1-x32d.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @videogpu-com.20230601.gappssmtp.com) X-ZM-MESSAGEID: 1762368746351154100 Content-Type: text/plain; charset="utf-8" From: Michael Add NEORV32 UART device: CTRL/DATA registers, RX FIFO, TX to chardev. Includes Kconfig/meson and public header. Signed-off-by: Michael Levit --- hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/neorv32_uart.c | 285 +++++++++++++++++++++++++++++++++ include/hw/char/neorv32_uart.h | 54 +++++++ 4 files changed, 343 insertions(+) create mode 100644 hw/char/neorv32_uart.c create mode 100644 include/hw/char/neorv32_uart.h diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 020c0a84bb..1fd39c2b30 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -95,3 +95,6 @@ config IP_OCTAL_232 bool default y depends on IPACK + +config NEORV32_UART + bool diff --git a/hw/char/meson.build b/hw/char/meson.build index a9e1dc26c0..2f5bf827a7 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -31,6 +31,7 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_u= art.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) system_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) +system_ss.add(when: 'CONFIG_NEORV32_UART', if_true: files('neorv32_uart.c'= )) system_ss.add(when: 'CONFIG_SH_SCI', if_true: files('sh_serial.c')) system_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_us= art.c')) system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_us= art.c')) diff --git a/hw/char/neorv32_uart.c b/hw/char/neorv32_uart.c new file mode 100644 index 0000000000..a21066d194 --- /dev/null +++ b/hw/char/neorv32_uart.c @@ -0,0 +1,285 @@ +/* + * Neorv32-specific UART. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "hw/irq.h" +#include "hw/char/neorv32_uart.h" +#include "hw/qdev-properties-system.h" + +#define NEORV32_UART_IO_REGION_SIZE (32) + +static Property neorv32_uart_properties[] =3D { + DEFINE_PROP_CHR("chardev", Neorv32UARTState, chr), +}; + +enum { + NEORV32_UART_CTRL =3D 0, /**< offset 0: control register */ + NEORV32_UART_DATA =3D 4 /**< offset 4: data register */ +}; + +/** UART control register bits */ +enum NEORV32_UART_CTRL_enum { + UART_CTRL_EN =3D 0, /* enable */ + UART_CTRL_SIM_MODE =3D 1, /* sim override */ + UART_CTRL_HWFC_EN =3D 2, /* RTS/CTS */ + UART_CTRL_PRSC_LSB =3D 3, /* prescaler sel LSB */ + UART_CTRL_PRSC_MSB =3D 5, /* prescaler sel MSB */ + UART_CTRL_BAUD_LSB =3D 6, /* baud div LSB */ + UART_CTRL_BAUD_MSB =3D 15, /* baud div MSB */ + UART_CTRL_RX_NEMPTY =3D 16, /* RX not empty */ + UART_CTRL_RX_FULL =3D 17, /* RX full */ + UART_CTRL_TX_EMPTY =3D 18, /* TX empty */ + UART_CTRL_TX_NFULL =3D 19, /* TX not full */ + UART_CTRL_IRQ_RX_NEMPTY =3D 20, /* IRQ on RX not empty */ + UART_CTRL_IRQ_RX_FULL =3D 21, /* IRQ on RX full */ + UART_CTRL_IRQ_TX_EMPTY =3D 22, /* IRQ on TX empty */ + UART_CTRL_IRQ_TX_NFULL =3D 23, /* IRQ on TX not full */ + + UART_CTRL_RX_OVER =3D 30, /* RX overflow */ + UART_CTRL_TX_BUSY =3D 31 /* TX busy or not empty */ +}; + +/** bits */ +enum NEORV32_UART_DATA_enum { + UART_DATA_RTX_LSB =3D 0, /**< (r/w): UART rx/tx data, LSB */ + UART_DATA_RTX_MSB =3D 7, /**< (r/w): UART rx/tx data, MSB */ + + UART_DATA_RX_FIFO_SIZE_LSB =3D 8, /**< (r/-): log2(RX FIFO size), LSB = */ + UART_DATA_RX_FIFO_SIZE_MSB =3D 11, /**< (r/-): log2(RX FIFO size), MSB= */ + + UART_DATA_TX_FIFO_SIZE_LSB =3D 12, /**< (r/-): log2(RX FIFO size), LSB= */ + UART_DATA_TX_FIFO_SIZE_MSB =3D 15, /**< (r/-): log2(RX FIFO size), MSB= */ +}; +/**@}*/ + +static void neorv32_uart_update_irq(Neorv32UARTState *s) +{ + int cond =3D 0; + if ((s->ie & NEORV32_UART_IE_TXWM) || + ((s->ie & NEORV32_UART_IE_RXWM) && s->rx_fifo_len)) { + cond =3D 1; + } + if (cond) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static uint64_t +neorv32_uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + Neorv32UARTState *s =3D opaque; + unsigned char r; + + switch (addr) { + case NEORV32_UART_CTRL: + if (s->rx_fifo_len) { + s->CTRL |=3D (1 << UART_CTRL_RX_NEMPTY); /* set data available= */ + } else { + s->CTRL &=3D ~(1 << UART_CTRL_RX_NEMPTY); /* clear data availa= ble */ + } + /* TODO: assuming here TX is always avalable, fix it. */ + s->CTRL |=3D (1 << UART_CTRL_TX_NFULL); /* set TX not full */ + + return s->CTRL; + + case NEORV32_UART_DATA: + if (s->rx_fifo_len) { + r =3D 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); + s->DATA =3D r; + + neorv32_uart_update_irq(s); /* TODO: check if need to call */ + return r; + } + } + + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr =3D 0x%x\n", + __func__, (int)addr); + return 0; +} + + + +static void +neorv32_uart_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int= size) +{ + Neorv32UARTState *s =3D opaque; + uint32_t value =3D val64; + unsigned char ch =3D value; + + /* TODO: check if need to update data and control bits */ + switch (addr) { + case NEORV32_UART_CTRL: + s->CTRL =3D value; + /* TODO: check if need to call, depending on IRQ flags */ + /* neorv32_uart_update_irq(s); */ + return; + case NEORV32_UART_DATA: + s->DATA =3D value; + qemu_chr_fe_write(&s->chr, &ch, 1); + /* neorv32_uart_update_irq(s); TODO: check if need to call */ + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr =3D 0x%x v =3D 0x%= x\n", + __func__, (int)addr, (int)value); +} + +static const MemoryRegionOps neorv32_uart_ops =3D { + .read =3D neorv32_uart_read, + .write =3D neorv32_uart_write, + .endianness =3D DEVICE_NATIVE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4 + } +}; + +static void neorv32_uart_init(Object *obj) +{ + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + Neorv32UARTState *s =3D NEORV32_UART(obj); + + memory_region_init_io(&s->mmio, OBJECT(s), &neorv32_uart_ops, s, + TYPE_NEORV32_UART, NEORV32_UART_IO_REGION_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_irq(sbd, &s->irq); +} + + +static void neorv32_uart_rx(void *opaque, const uint8_t *buf, int size) +{ + Neorv32UARTState *s =3D opaque; + + /* Got a byte. */ + if (s->rx_fifo_len >=3D sizeof(s->rx_fifo)) { + printf("WARNING: UART dropped char.\n"); + return; + } + s->rx_fifo[s->rx_fifo_len++] =3D *buf; + + neorv32_uart_update_irq(s); +} + +static int neorv32_uart_can_rx(void *opaque) +{ + Neorv32UARTState *s =3D opaque; + + return s->rx_fifo_len < sizeof(s->rx_fifo); +} + +static void neorv32_uart_event(void *opaque, QEMUChrEvent event) +{ +} + +static int neorv32_uart_be_change(void *opaque) +{ + Neorv32UARTState *s =3D opaque; + + qemu_chr_fe_set_handlers(&s->chr, neorv32_uart_can_rx, neorv32_uart_rx, + neorv32_uart_event, neorv32_uart_be_change, s, + NULL, true); + + return 0; +} + +static void neorv32_uart_realize(DeviceState *dev, Error **errp) +{ + Neorv32UARTState *s =3D NEORV32_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, neorv32_uart_can_rx, neorv32_uart_rx, + neorv32_uart_event, neorv32_uart_be_change, s, + NULL, true); +} + +static const VMStateDescription vmstate_neorv32_uart =3D { + .name =3D TYPE_NEORV32_UART, + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT8_ARRAY(rx_fifo, Neorv32UARTState, + NEORV32_UART_RX_FIFO_SIZE), + VMSTATE_UINT8(rx_fifo_len, Neorv32UARTState), + VMSTATE_UINT32(ie, Neorv32UARTState), + VMSTATE_END_OF_LIST() + }, +}; + +static void neorv32_uart_reset_enter(Object *obj, ResetType type) +{ + Neorv32UARTState *s =3D NEORV32_UART(obj); + s->rx_fifo_len =3D 0; + s->ie =3D 0; +} + +static void neorv32_uart_reset_hold(Object *obj, ResetType type) +{ + Neorv32UARTState *s =3D NEORV32_UART(obj); + qemu_irq_lower(s->irq); +} + +static void neorv32_uart_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + ResettableClass *rc =3D RESETTABLE_CLASS(oc); + + dc->realize =3D neorv32_uart_realize; + dc->vmsd =3D &vmstate_neorv32_uart; + rc->phases.enter =3D neorv32_uart_reset_enter; + rc->phases.hold =3D neorv32_uart_reset_hold; + device_class_set_props(dc, neorv32_uart_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo neorv32_uart_info =3D { + .name =3D TYPE_NEORV32_UART, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(Neorv32UARTState), + .instance_init =3D neorv32_uart_init, + .class_init =3D neorv32_uart_class_init, +}; + +static void neorv32_uart_register_types(void) +{ + type_register_static(&neorv32_uart_info); +} + +type_init(neorv32_uart_register_types) +/* + * Create UART device. + */ +Neorv32UARTState *neorv32_uart_create(MemoryRegion *address_space, hwaddr = base, + Chardev *chr) +{ + DeviceState *dev; + SysBusDevice *s; + bool succed =3D false; + + dev =3D qdev_new("riscv.neorv32.uart"); + + qdev_prop_set_chr(dev, "chardev", chr); + s =3D SYS_BUS_DEVICE(dev); + succed =3D sysbus_realize_and_unref(s, &error_fatal); + + if (succed) { + memory_region_add_subregion(address_space, base, + sysbus_mmio_get_region(s, 0)); + return NEORV32_UART(dev); + } else { + return NULL; + } +} /* neorv32_uart_create */ diff --git a/include/hw/char/neorv32_uart.h b/include/hw/char/neorv32_uart.h new file mode 100644 index 0000000000..fa33906724 --- /dev/null +++ b/include/hw/char/neorv32_uart.h @@ -0,0 +1,54 @@ +/* + * Neorv32-specific UART. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_NEORV32_UART_H +#define HW_NEORV32_UART_H + +#include "chardev/char-fe.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_NEORV32_UART "riscv.neorv32.uart" +OBJECT_DECLARE_SIMPLE_TYPE(Neorv32UARTState, NEORV32_UART) + +#define QEMU_UART_DATA_RX_FIFO_SIZE_LSB 8 /* log2 RX FIFO size LSB */ +#define QEMU_UART_DATA_RX_FIFO_SIZE_MSB 11 /* log2 RX FIFO size MSB */ + +#define NEORV32_UART_RX_FIFO_SIZE 32 /* in HW it is 2048 + 256 =3D _MSB += _LSB */ + +enum { + NEORV32_UART_IE_TXWM =3D 1, /* Transmit watermark interrupt enable */ + NEORV32_UART_IE_RXWM =3D 2 /* Receive watermark interrupt enable */ +}; + +enum { + NEORV32_UART_IP_TXWM =3D 1, /* Transmit watermark interrupt pending */ + NEORV32_UART_IP_RXWM =3D 2 /* Receive watermark interrupt pending */ +}; + + + +struct Neorv32UARTState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq irq; + MemoryRegion mmio; + CharFrontend chr; + uint8_t rx_fifo[NEORV32_UART_RX_FIFO_SIZE]; + uint8_t rx_fifo_len; + uint32_t ie; /* interrupt enable */ + uint32_t CTRL; + uint32_t DATA; +}; + +Neorv32UARTState *neorv32_uart_create(MemoryRegion *address_space, hwaddr = base, + Chardev *chr); + +#endif /* HW_NEORV32_UART_H */ --=20 2.51.1 From nobody Fri Nov 14 15:22:01 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1762368718; cv=none; d=zohomail.com; s=zohoarc; b=K5ickw60mlFQJ+QJDObNnyrPDOxYETr03JGoH3ZddC3S7rm6a5BHb285uwWUv6yMmjpZ71h9POKrQ/wSVoXjB+8Zz6uWQRCXSUWw2rXYvT3b2nZFl2MNrsKztR9L3l2oIPnd2dNk11tICpwn7ZshqvInIKhh95GY3E+jj/M4aHY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762368718; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=mNQLK4ewOJjPgvmOfboyv0uwibkrxNDgkc5qo0Kq6ic=; b=hCfH8BD4dQu+bemaCEeCZzXPZRPm9QChxRUriIY7f5N6Ph3kKm9at5d+MrLHR8cfWuZUrYeAz3XhUfgayASX8P2AyRISN8nz2HRbo0NmS9080U6iCuH+Nx0WGdqXVwQO+26xMmEbWJDeNTn9oXaGVUlsnwMY94dtAUmgdNFy70w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762368718593240.61192441396815; Wed, 5 Nov 2025 10:51:58 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGibh-00049P-RX; Wed, 05 Nov 2025 13:51:47 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGib6-0003zc-FS for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:10 -0500 Received: from mail-wr1-x432.google.com ([2a00:1450:4864:20::432]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vGib2-0003Cb-P3 for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:08 -0500 Received: by mail-wr1-x432.google.com with SMTP id ffacd0b85a97d-426f1574a14so93368f8f.3 for ; Wed, 05 Nov 2025 10:51:04 -0800 (PST) Received: from DDesktop.local ([2a10:8012:d:eea4:f4de:376b:66b1:d7b5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb42a354sm270622f8f.20.2025.11.05.10.51.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Nov 2025 10:51:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762368663; x=1762973463; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mNQLK4ewOJjPgvmOfboyv0uwibkrxNDgkc5qo0Kq6ic=; b=c795NHVRCD5Aq7kV4fgeE+GW7F1tIODpU+uSpG7R7kuJUZTa3mdLtg8l5h2WYtNNob 8tjf5+2Abkq0daLjM4GjCa/do7AfRX3tC31aUayYoXSGiha85EBGwSSSWFBuNuQGY4WE bfGQrs1qJcmYS7IjzM2G0PQiq59IkEgUyPctIGHo3Okh+DGMU2NFqhtCpy3WwXYXxR6m iWQnWLiLfTa3L+2v5KgHAhZHMVpYOF783/2h4L0ifBnksayx92yBl2VG/BhMIb8MgQZ1 T5yms+5TEE0byZwlev/zxqIUsYnNzYUpMTDEE6Gu9Qb3kznrCpTGva6BBH5xSBjtlTAn qYfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762368663; x=1762973463; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mNQLK4ewOJjPgvmOfboyv0uwibkrxNDgkc5qo0Kq6ic=; b=t9J/HBz9qTOS+7x0dRcYCZLzs9Ms2vfysoyvkt5GC3rY50xiKv3R+gawBa4L6EZrb8 natByfuK99qT2Rn9UEuDBuUwctDcAcOY+yKPJjZO2LmN+fAwoyV/BZaCkgReSvLqHbZC VazrbTtFBBtszytw4Ch0ycQDcbo+8OPGEQkMaDSgQYLtAgqBVnMeXJ4CVPPPX0Q4RDrk U/5jfjmYrpQIMc9T1uTVbG/DD0Kt/S1HlLpGBucRcYw0C4ZqUxnTb8lrM9R2hosx59SD DmdGJDvl3gYqDBJs3uBaGK56PzShLw7MaBOdmhtDhO8SZCt6gz1F+J4JmDxPw0YZbCrG zcAA== X-Gm-Message-State: AOJu0YwcGLzPMwxh81O9CFzaU22EwUPqhGTPaQCeEb7P/bZxuiUkiUWV yvAstGhKM3GDeII5kWcEpHf/rs3kPg8l7sDBgfPJdzHlzSl2yCWPI7LyhfPOXvVGrgoYpVM7d1z 0/1shwMM= X-Gm-Gg: ASbGncvyn7PGmQVPDBPVg5Q6GNJZY72G1k0RKesjNOIsh5SX01unwIiqXngLj+H5BqL 5CDs0As4z4qFHRhR6lMNJwdWjdTEdznjeDN9nnV/9iC+b56O8axHmUYqiI6J3pCHmihTwbP8TU2 11xKP9q0yEx0O2OrTm2QIxjuTOZKSt+SJrWyCy14gSoQr068Yw4VfS40v7t7s3YMmlpzXAJJ/53 AckbLlegsaAZOKntEwnmSFKPVkW3j/ggiCG9vAskNXei+ydMFubk4Flo4xsDaHlhb2bmEwO787+ qpp9Kv7aiWX6mYN+sOT3HxJce3KjT/Tu0BMr7WOJWU8cRb9ihCTtpGir3iw5C2Maw234TyT1dwT glzLhVY0+UziGQNndDRkUByOgO6m/4N4vnt5UtsmU5eGCc8aQflu80HyYw847NQ7dKt8/zZUjAC S6JG6D X-Google-Smtp-Source: AGHT+IE3PY8op3oneZRy/BQm+NSnglC+QUnd4qY5GO3Uah5o5BrC5OrfXSS7h0N0uQo5yC0uj+AF3Q== X-Received: by 2002:a05:6000:2dc6:b0:429:dbed:28fb with SMTP id ffacd0b85a97d-429e32e46aamr3825800f8f.23.1762368662831; Wed, 05 Nov 2025 10:51:02 -0800 (PST) From: Michael Levit To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, philmd@linaro.org, pbonzini@redhat.com, dbarboza@ventanamicro.com, zhiwei_liu@linux.alibaba.com, liwei1518@gmail.com, smishash@gmail.com Subject: [PATCH v3 4/5] hw/ssi: add NEORV32 SPI controller (SSI master, CS command) Date: Wed, 5 Nov 2025 20:50:55 +0200 Message-ID: <20251105185056.23565-5-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251105185056.23565-1-michael@videogpu.com> References: <20251105185056.23565-1-michael@videogpu.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=2a00:1450:4864:20::432; envelope-from=michael@videogpu.com; helo=mail-wr1-x432.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @videogpu-com.20230601.gappssmtp.com) X-ZM-MESSAGEID: 1762368721858158500 Content-Type: text/plain; charset="utf-8" From: Michael Add NEORV32 SPI controller: CTRL/DATA, tiny TX/RX FIFOs, command-mode CS (a= ctive-low), SSI master bus, and helper to attach n25q flash when an MTD drive is provid= ed. Includes Kconfig/meson and public header. Signed-off-by: Michael Levit --- hw/ssi/Kconfig | 4 + hw/ssi/meson.build | 1 + hw/ssi/neorv32_spi.c | 478 +++++++++++++++++++++++++++++++++++ include/hw/ssi/neorv32_spi.h | 57 +++++ 4 files changed, 540 insertions(+) create mode 100644 hw/ssi/neorv32_spi.c create mode 100644 include/hw/ssi/neorv32_spi.h diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 1bd56463c1..5b1a03f3c4 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -32,3 +32,7 @@ config PNV_SPI config ALLWINNER_A10_SPI bool select SSI + +config NEORV32_SPI + bool + select SSI diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 6afb1ea200..5139cc1ca0 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi= .c')) system_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c')) system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c')) +system_ss.add(when: 'CONFIG_NEORV32_SPI', if_true: files('neorv32_spi.c')) diff --git a/hw/ssi/neorv32_spi.c b/hw/ssi/neorv32_spi.c new file mode 100644 index 0000000000..954788a5de --- /dev/null +++ b/hw/ssi/neorv32_spi.c @@ -0,0 +1,478 @@ +/* + * QEMU implementation of the Neorv32 SPI block. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/ssi/ssi.h" +#include "qemu/fifo8.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "trace/trace-root.h" +#include "qapi/error.h" +#include "hw/irq.h" +#include "hw/ssi/neorv32_spi.h" +#include "system/blockdev.h" + +/* SPI control register bits */ +enum NEORV32_SPI_CTRL_enum { + SPI_CTRL_EN =3D 0, /* (r/w): SPI unit enable */ + SPI_CTRL_CPHA =3D 1, /* (r/w): Clock phase */ + SPI_CTRL_CPOL =3D 2, /* (r/w): Clock polarity */ + SPI_CTRL_PRSC0 =3D 3, /* (r/w): Clock prescaler select bit 0 */ + SPI_CTRL_PRSC1 =3D 4, /* (r/w): Clock prescaler select bit 1 */ + SPI_CTRL_PRSC2 =3D 5, /* (r/w): Clock prescaler select bit 2 */ + SPI_CTRL_CDIV0 =3D 6, /* (r/w): Clock divider bit 0 */ + SPI_CTRL_CDIV1 =3D 7, /* (r/w): Clock divider bit 1 */ + SPI_CTRL_CDIV2 =3D 8, /* (r/w): Clock divider bit 2 */ + SPI_CTRL_CDIV3 =3D 9, /* (r/w): Clock divider bit 3 */ + + SPI_CTRL_RX_AVAIL =3D 16, /* (r/-): RX FIFO data available (not empt= y) */ + SPI_CTRL_TX_EMPTY =3D 17, /* (r/-): TX FIFO empty */ + SPI_CTRL_TX_FULL =3D 18, /* (r/-): TX FIFO full */ + + SPI_CTRL_FIFO_LSB =3D 24, /* (r/-): log2(FIFO size), LSB */ + SPI_CTRL_FIFO_MSB =3D 27, /* (r/-): log2(FIFO size), MSB */ + + SPI_CS_ACTIVE =3D 30, /* (r/-): At least one CS line is active */ + SPI_CTRL_BUSY =3D 31 /* (r/-): serial PHY busy or TX FIFO not e= mpty */ +}; + +/* SPI data register bits */ +enum NEORV32_SPI_DATA_enum { + SPI_DATA_LSB =3D 0, /* (r/w): Data byte LSB */ + SPI_DATA_CSEN =3D 3, /* (-/w): Chip select enable (command-mode) */ + SPI_DATA_MSB =3D 7, /* (r/w): Data byte MSB */ + SPI_DATA_CMD =3D 31 /* (-/w): 1=3Dcommand, 0=3Ddata */ +}; + +/* Register offsets */ +#define NEORV32_SPI_CTRL 0x00 +#define NEORV32_SPI_DATA 0x04 +#define NEORV32_SPI_MMIO_SIZE 0x8 /* ctrl + data (8 bytes total) */ +/* Various constants */ +#define NEORV32_SPI_MAX_CS_LINES 7 +#define NEORV32_SPI_FIFO_CAPACITY 8 + +/* Utility functions to get/set bits in ctrl register */ +static inline bool get_ctrl_bit(NEORV32SPIState *s, int bit) +{ + return (s->ctrl & (1 << bit)) !=3D 0; +} + +static inline void set_ctrl_bit(NEORV32SPIState *s, int bit, bool val) +{ + if (val) { + s->ctrl |=3D (1 << bit); + } else { + s->ctrl &=3D ~(1 << bit); + } +} + +static inline bool get_data_bit(uint32_t v, int bit) +{ + return (v >> bit) & 1; +} + +/* Update read-only status bits in CTRL register */ +static void neorv32_spi_update_status(NEORV32SPIState *s) +{ + /* RX_AVAIL: set if RX FIFO not empty */ + set_ctrl_bit(s, SPI_CTRL_RX_AVAIL, !fifo8_is_empty(&s->rx_fifo)); + + /* TX_EMPTY: set if TX FIFO empty */ + set_ctrl_bit(s, SPI_CTRL_TX_EMPTY, fifo8_is_empty(&s->tx_fifo)); + + /* TX_FULL: set if TX FIFO full */ + set_ctrl_bit(s, SPI_CTRL_TX_FULL, fifo8_is_full(&s->tx_fifo)); + + + /* + * BUSY: We'll consider SPI busy if TX FIFO is not empty + * or currently shifting data. + * For simplicity, if TX is not empty we say busy. + */ + bool busy =3D !fifo8_is_empty(&s->tx_fifo); + set_ctrl_bit(s, SPI_CTRL_BUSY, busy); + + /* Update CS status */ + if (s->cmd_cs_active) { + s->ctrl |=3D (1u << SPI_CS_ACTIVE); + } else { + s->ctrl &=3D ~(1u << SPI_CS_ACTIVE); + } + +} + +/* Update chip select lines based on command-mode CS (active-low on the wi= re) */ +static void neorv32_spi_update_cs(NEORV32SPIState *s) +{ + /* Check that input valid */ + if (!s->cs_lines || s->num_cs <=3D 0) { + return; + } + + /* Deassert all CS lines (inactive =3D high) */ + for (int i =3D 0; i < s->num_cs; i++) { + qemu_set_irq(s->cs_lines[i], 1); + } + + /* If DATA command says CS active, assert selected line (low =3D activ= e) */ + if (s->cmd_cs_active) { + int cs_idx =3D s->current_cs; + if (cs_idx < 0 || cs_idx >=3D s->num_cs) { + /* Out of range: keep all deasserted, but warn once per event = */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: CS index %d out of range (num_cs=3D%d)\n", + __func__, cs_idx, s->num_cs); + return; + } + /* Active-low when enabled */ + qemu_set_irq(s->cs_lines[cs_idx], 0); + } + +} + +/* Update IRQ based on conditions */ +static void neorv32_spi_update_irq(NEORV32SPIState *s) +{ + /* + * Conditions for IRQ: + * IRQ if RX data available and IRQ_RX_AVAIL is set: + * if (!RX FIFO empty && SPI_CTRL_IRQ_RX_AVAIL set) + * + * IRQ if TX empty and IRQ_TX_EMPTY is set: + * if (TX empty && SPI_CTRL_IRQ_TX_EMPTY set) + * + * IRQ if TX < half full and IRQ_TX_HALF is set: + * if (TX < half full && SPI_CTRL_IRQ_TX_HALF set) + */ + + bool rx_irq =3D !fifo8_is_empty(&s->rx_fifo); + bool tx_empty_irq =3D fifo8_is_empty(&s->tx_fifo); + int used =3D fifo8_num_used(&s->tx_fifo); + bool tx_half_irq =3D (used < (s->fifo_capacity / 2)); + + bool irq_level =3D rx_irq || tx_empty_irq || tx_half_irq; + qemu_set_irq(s->irq, irq_level ? 1 : 0); +} + +/* + * Flush the TX FIFO to the SPI bus: + * For each byte in TX FIFO, send it out via ssi_transfer. + * If direction is not explicitly given, we assume: + * - On write to DATA, we push to TX FIFO and then transfer out. + * - On receiving data back from ssi_transfer, we push it into RX FIFO + * if SPI is enabled. + */ +static void neorv32_spi_flush_txfifo(NEORV32SPIState *s) +{ + if (!get_ctrl_bit(s, SPI_CTRL_EN)) { + /* SPI not enabled, do nothing */ + return; + } + + while (!fifo8_is_empty(&s->tx_fifo)) { + uint8_t tx =3D fifo8_pop(&s->tx_fifo); + uint8_t rx =3D ssi_transfer(s->bus, tx); + + /* Push received byte into RX FIFO if not full */ + if (!fifo8_is_full(&s->rx_fifo)) { + fifo8_push(&s->rx_fifo, rx); + } + } +} + +/* Reset the device state */ +static void neorv32_spi_reset(DeviceState *d) +{ + NEORV32SPIState *s =3D NEORV32_SPI(d); + + s->ctrl =3D 0; + s->data =3D 0; + + /* Reset FIFOs */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); + + neorv32_spi_update_status(s); + neorv32_spi_update_cs(s); + neorv32_spi_update_irq(s); +} + +/* MMIO read handler */ +static uint64_t neorv32_spi_read(void *opaque, hwaddr addr, unsigned int s= ize) +{ + NEORV32SPIState *s =3D opaque; + uint32_t r =3D 0; + + switch (addr) { + case NEORV32_SPI_CTRL: + /* Return the current CTRL register value (including status bits) = */ + neorv32_spi_update_status(s); + r =3D s->ctrl; + break; + + case NEORV32_SPI_DATA: + /* If RX FIFO is empty, return some default, else pop from RX FIFO= */ + if (fifo8_is_empty(&s->rx_fifo)) { + /* + * No data available, + * could return 0xFFFFFFFF or 0x00000000 as "no data" + */ + r =3D 0x00000000; + } else { + r =3D fifo8_pop(&s->rx_fifo); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + neorv32_spi_update_status(s); + neorv32_spi_update_irq(s); + + return r; +} + +/* MMIO write handler */ +static void neorv32_spi_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + NEORV32SPIState *s =3D opaque; + uint32_t value =3D val64; + + switch (addr) { + case NEORV32_SPI_CTRL: { + + /* + * Writing control register: + * Some bits are read-only (e.g., status bits). + * We should mask them out or ignore writes to them. + * For simplicity, we overwrite ctrl except for RO bits. + */ + + + /* + * Save old RO bits: RX_AVAIL, TX_EMPTY, TX_NHALF, TX_FULL, BUSY + * and FIFO size bits + */ + uint32_t ro_mask =3D ((1 << SPI_CTRL_BUSY) | + (1 << SPI_CTRL_TX_EMPTY) | + (1 << SPI_CTRL_TX_FULL) | + (1 << SPI_CTRL_RX_AVAIL)); + + /* + * FIFO size bits might be hardwired read-only. + * Assume we do not change them: + * FIFO size: bits [SPI_CTRL_FIFO_LSB..SPI_CTRL_FIFO_MSB], + * here assume read-only. + */ + uint32_t fifo_size_mask =3D 0; + for (int b =3D SPI_CTRL_FIFO_LSB; b <=3D SPI_CTRL_FIFO_MSB; b++) { + fifo_size_mask |=3D (1 << b); + } + ro_mask |=3D fifo_size_mask; + + uint32_t ro_bits =3D s->ctrl & ro_mask; + s->ctrl =3D (value & ~ro_mask) | ro_bits; + + neorv32_spi_update_cs(s); + break; + } /* NEORV32_SPI_CTRL */ + + case NEORV32_SPI_DATA: + { + /* If CMD=3D1, this write is a command, not payload */ + const bool is_cmd =3D get_data_bit(value, SPI_DATA_CMD); + + if (is_cmd) { + /* + * DATA command format: + * bit 31: CMD =3D 1 + * bit 3: CSEN (1=3Dassert CS, 0=3Ddeassert All) + * bits [2:0]: CS index (0..7) when asserting + */ + const bool csen =3D get_data_bit(value, SPI_DATA_CSEN); + const int cs_index =3D (int)(value & 0x7); + + if (csen) { + /* Select and assert a single CS */ + s->current_cs =3D cs_index; /* range checking update_= cs() */ + s->cmd_cs_active =3D true; + } else { + /* Deassert all CS lines */ + s->cmd_cs_active =3D false; + } + + /* Drive the wires */ + neorv32_spi_update_cs(s); + /* Update status (SPI_CS_ACTIVE is read-only status bit) */ + neorv32_spi_update_status(s); + neorv32_spi_update_irq(s); + break; /* no FIFO push on command */ + } + + /* Writing DATA puts a byte into TX FIFO if not full */ + if (!fifo8_is_full(&s->tx_fifo)) { + uint8_t tx_byte =3D (uint8_t)value; + + fifo8_push(&s->tx_fifo, tx_byte); + /* After pushing data, flush TX to SPI bus */ + neorv32_spi_flush_txfifo(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: TX FIFO full, cannot write 0x%x\n", + __func__, value); + } + break; + } /* NEORV32_SPI_DATA */ + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at address 0x%" + HWADDR_PRIx " value=3D0x%x\n", __func__, addr, value= ); + break; + + } /* switch (addr) */ + + neorv32_spi_update_status(s); + neorv32_spi_update_irq(s); +} /* neorv32_spi_write */ + +static const MemoryRegionOps neorv32_spi_ops =3D { + .read =3D neorv32_spi_read, + .write =3D neorv32_spi_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +static void neorv32_spi_init(Object *obj) +{ + NEORV32SPIState *s =3D NEORV32_SPI(obj); + s->ctrl =3D 0; + s->data =3D 0; + s->fifo_capacity =3D NEORV32_SPI_FIFO_CAPACITY; + s->num_cs =3D NEORV32_SPI_MAX_CS_LINES; /* Default to 1 CS line= */ + s->cmd_cs_active =3D false; + s->current_cs =3D 0; /* Use CS0 by default */ +} + +/* Realize the device */ +static void neorv32_spi_realize(DeviceState *dev, Error **errp) +{ + NEORV32SPIState *s =3D NEORV32_SPI(dev); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); + + /* Create the SSI master bus */ + s->bus =3D ssi_create_bus(dev, "neorv32-spi-bus"); + + /* 1) IRQ inputs: first the main IRQ, then each CS line */ + sysbus_init_irq(sbd, &s->irq); + s->cs_lines =3D g_new0(qemu_irq, s->num_cs); + for (int i =3D 0; i < s->num_cs; i++) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + qemu_set_irq(s->cs_lines[i], 1); /* deassert CS (high) */ + } + + /* 2) Now map the MMIO region */ + memory_region_init_io(&s->mmio, OBJECT(s), &neorv32_spi_ops, s, + TYPE_NEORV32_SPI, NEORV32_SPI_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + + /* Initialize FIFOs */ + fifo8_create(&s->tx_fifo, s->fifo_capacity); + fifo8_create(&s->rx_fifo, s->fifo_capacity); + + /* + * Set FIFO size bits (log2 of FIFO size =3D 3 for capacity=3D8) + * + * FIFO size bits: from SPI_CTRL_FIFO_LSB to SPI_CTRL_FIFO_MSB + * We'll store a value of 3 (log2(8)=3D3) + */ + int fifo_size_log2 =3D 3; + for (int b =3D SPI_CTRL_FIFO_LSB; b <=3D SPI_CTRL_FIFO_MSB; b++) { + int shift =3D b - SPI_CTRL_FIFO_LSB; + if (fifo_size_log2 & (1 << shift)) { + s->ctrl |=3D (1 << b); + } else { + s->ctrl &=3D ~(1 << b); + } + } +} + +/* Device properties can be added if needed. For now, none. */ +static Property neorv32_spi_properties[] =3D { + DEFINE_PROP_UINT32("num-cs", NEORV32SPIState, num_cs, 1), +}; + +static void neorv32_spi_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + device_class_set_props(dc, neorv32_spi_properties); + device_class_set_legacy_reset(dc, neorv32_spi_reset); + dc->realize =3D neorv32_spi_realize; +} + +static const TypeInfo neorv32_spi_type_info =3D { + .name =3D TYPE_NEORV32_SPI, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(NEORV32SPIState), + .instance_init =3D neorv32_spi_init, + .class_init =3D neorv32_spi_class_init, +}; + +static void neorv32_spi_register_types(void) +{ + type_register_static(&neorv32_spi_type_info); +} + +type_init(neorv32_spi_register_types) + + + +NEORV32SPIState *neorv32_spi_create(MemoryRegion *sys_mem, hwaddr base_add= r) +{ + /* Allocate and initialize the SPI state object */ + NEORV32SPIState *s =3D g_new0(NEORV32SPIState, 1); + object_initialize(&s->parent_obj, sizeof(*s), TYPE_NEORV32_SPI); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(&s->parent_obj); + + /* Realize the SPI controller (sets up mmio, irq, SSI bus, cs_lines) */ + sysbus_realize_and_unref(sbd, &error_fatal); + + /* Map the MMIO region into the system address space */ + sysbus_mmio_map(sbd, 0, base_addr); + + /* Attach an SPI flash to SPI0 if a drive image is provided */ + DriveInfo *dinfo =3D drive_get(IF_MTD, 0, 0); + if (dinfo) { + /* Create the flash device and bind the MTD backend */ + DeviceState *flash =3D qdev_new("n25q512a11"); + qdev_prop_set_drive_err(flash, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); + + /* Realize flash on the same SSI bus created on controller realiz= e */ + qdev_realize_and_unref(flash, BUS(s->bus), &error_fatal); + + /* Retrieve and wire the flash's CS input line to CS0 output */ + qemu_irq flash_cs =3D qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0= ); + sysbus_connect_irq(sbd, 1, flash_cs); + } + + return s; +} + diff --git a/include/hw/ssi/neorv32_spi.h b/include/hw/ssi/neorv32_spi.h new file mode 100644 index 0000000000..8b94d2f1cf --- /dev/null +++ b/include/hw/ssi/neorv32_spi.h @@ -0,0 +1,57 @@ +/* + * QEMU implementation of the Neorv32 SPI block. + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef NEORV32_SPI_H +#define NEORV32_SPI_H + +#include "qemu/osdep.h" +#include "hw/sysbus.h" + +#define TYPE_NEORV32_SPI "neorv32.spi" +#define NEORV32_SPI(obj) OBJECT_CHECK(NEORV32SPIState, (obj), TYPE_NEORV32= _SPI) + +typedef struct NEORV32SPIState { + SysBusDevice parent_obj; + + /* Memory-mapped registers */ + MemoryRegion mmio; + + /* IRQ line */ + qemu_irq irq; + + /* SPI bus (master) */ + SSIBus *bus; + + /* Chip selects (assume up to 3 CS lines) */ + qemu_irq *cs_lines; + uint32_t num_cs; + + /* + * Registers: + * Assume: + * 0x00: CTRL (r/w) + * 0x04: DATA (r/w) + */ + uint32_t ctrl; + uint32_t data; + + /* FIFOs */ + Fifo8 tx_fifo; + Fifo8 rx_fifo; + + /* FIFO capacity */ + int fifo_capacity; + /* Track CS state driven by command writes */ + bool cmd_cs_active; /* true =3D CS asserted (active-low on wire) */ + int current_cs; /* which CS line is active; default 0 for now */ +} NEORV32SPIState; + + + +NEORV32SPIState *neorv32_spi_create(MemoryRegion *sys_mem, hwaddr base_add= r); + +#endif /* NEORV32_SPI_H */ --=20 2.51.1 From nobody Fri Nov 14 15:22:01 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1762368763; cv=none; d=zohomail.com; s=zohoarc; b=fqKYkqt/VcQ6h8BDMmYh3DhLtFiibka7M+Tz7w5nNyxXj2ZCyMV5QR1J5zxf9qZ1VOO4QLFevRA8xW1UuhTVpeZsLbwlXONpTzdmiPORzzUozeRfGmuVeSIETbzAd+m6b+VGL6PTG+Qg18pOk1vcY5PgaCLoAhaPWmg8wuaQZm8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762368763; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=7e8NP/uqAfJjsNCi3DwACx2SSTripBKZFs+t6givG5Y=; b=aq8kClRpyBLyY82C/9ojjywkgWuqx29TxZ2uzXlYUO4Y/kFoO5O174Xe/in/lHM9AbsSwUDRpuaWoNtwFI9JIB5qU6heLuunbKJnx4kHK7lsFN/Fa7yIOzbVXg2ha4GA+SwluK/Kzq3fLSYzPAa402UlFlEaCb/frWXeTY2tKPo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1762368763587459.38879703330804; Wed, 5 Nov 2025 10:52:43 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vGic8-0004OB-CN; Wed, 05 Nov 2025 13:52:12 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vGib7-0003zl-Cs for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:10 -0500 Received: from mail-wr1-x42a.google.com ([2a00:1450:4864:20::42a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vGib4-0003Cq-8K for qemu-devel@nongnu.org; Wed, 05 Nov 2025 13:51:09 -0500 Received: by mail-wr1-x42a.google.com with SMTP id ffacd0b85a97d-429b9b6ce96so134723f8f.3 for ; Wed, 05 Nov 2025 10:51:05 -0800 (PST) Received: from DDesktop.local ([2a10:8012:d:eea4:f4de:376b:66b1:d7b5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb42a354sm270622f8f.20.2025.11.05.10.51.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Nov 2025 10:51:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762368664; x=1762973464; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7e8NP/uqAfJjsNCi3DwACx2SSTripBKZFs+t6givG5Y=; b=gQVFsTr+WS7O+r7g3LcIWfbUiaT2v4nAHaR7gjp4s40GB8GY/WtUyHRehwuQVFdzq8 bJLp4AQ/uEqvz+zs9p2E5DQFvLkXmKDbp5kooZF9ehVFNEsXfVxyDd0oufWvcIc4RyiW FE7vmwp1WrFHQV1YdCnaWP6N497pi83CSbIevcb0XGg4T+cTo+8tN6Kwk8vWcQCaGysD g8S+1PiTbzmGKgtokTs+nRNvCpChYH7fa+jzW8W2aRAOtkWCtTwXgjm/eIeWMxKrcLy1 wTvFNMyWPh50HsSIq/kYHXRI/W70vZtCdH22AU+quX1PVCDk9VX0gLEeooi+KDgt9Kuw jT8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762368664; x=1762973464; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7e8NP/uqAfJjsNCi3DwACx2SSTripBKZFs+t6givG5Y=; b=WXCZ12yUS488HdZVIjuoUN/2Q0uiKiA3NwnUF5ucU2ipslbDstHsOxJRPigLj6cym1 telKfnrxcDUHXIzRBOqZbQLeL7kiNLd4OVgoa7d04esTZIc5o3ADpCg3XD/ZwS0uy35p 8vEyx6cNB3t9MeBurDoi2k+heGiJ708zdoBOJctIp0EWFq5m70+f+p9KjpxQaOcacaOF uiMm4PVTpkbPtCDkb7lAUw4aCkGWtY2JA31qQZ7LcF6hSf9r2UN93BGoyGRc8sXZDkHp 3ZOmI4CJG3UEtBI9qCVTTwo3zckPrcktF8pJJbNVSMINBn53h8y+fDCo+ppCy9A6g+Wm 0Ppg== X-Gm-Message-State: AOJu0YwxF3cHG8Vx4vONLlmivNsWGhfpH49BpOxt9KvxfULFsTndQbqU RM0jDmTkJScbk4p3BJ56v6C6jNAMstnV0TllZE59nIJ/+xb6U171ZlQJdbK5CojCHf/6+3x4fE8 6qZRiun0= X-Gm-Gg: ASbGncvCNwHMIUEU5/vy20EfYBpxm5sNVhDym344CXz3mfTB3JBXZu61EIPTY2bTxy3 XsvkPK0odRQQEO9ONeWKULrVsmwDhHjICjeZSWWXETy36Dw7UZiR1XvV9lEGHCT5U5F9b/R1TTF yoUwfz1oNqjR4mLlgxkDIh/0yiKPlUF40YzMTGkSYrLuG5+CMDppxQHCVNzje1O+ldcYqce5XH5 8ccE/GvAv5/qxhrZYQQ6HYFwaWLIk4VzktgrsWA3Hh2ic0NkH3leh8FawItgBxQveKu5Ag+L7dI aFL1wGeyECLEITlahYeyOO03U0b6KTswuWGe7HtzA/QDwOtMYIeOSnPTGG4wkSJCAyXdq88rh2k kh4wgDhTklvLUdcpy008FIoqAO4FXvB3V4Jz8xuJQiDsP04t5gm8g+GhTAtJxGGdebimDou6nOy dccW8H8c1yd092IqQ= X-Google-Smtp-Source: AGHT+IFWhBX68YLa8DbzevrzJeAhb19pPzjdjMlkMLpb/wHKLP+ULyN5G+836YjNjYuKbg6wPX67tg== X-Received: by 2002:a5d:584b:0:b0:426:d5a0:bac8 with SMTP id ffacd0b85a97d-429e331283emr4406175f8f.56.1762368664056; Wed, 05 Nov 2025 10:51:04 -0800 (PST) From: Michael Levit To: qemu-devel@nongnu.org Cc: qemu-riscv@nongnu.org, philmd@linaro.org, pbonzini@redhat.com, dbarboza@ventanamicro.com, zhiwei_liu@linux.alibaba.com, liwei1518@gmail.com, smishash@gmail.com Subject: [PATCH v3 5/5] hw/riscv: introduce 'neorv32' board, docs, and riscv32 device config Date: Wed, 5 Nov 2025 20:50:56 +0200 Message-ID: <20251105185056.23565-6-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251105185056.23565-1-michael@videogpu.com> References: <20251105185056.23565-1-michael@videogpu.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=2a00:1450:4864:20::42a; envelope-from=michael@videogpu.com; helo=mail-wr1-x42a.google.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @videogpu-com.20230601.gappssmtp.com) X-ZM-MESSAGEID: 1762368769910158500 From: Michael Introduce the 'neorv32' board wiring IMEM/DMEM/BOOTROM, SYSINFO, UART0, and= SPI0; add docs/system/riscv/neorv32.rst and riscv32-softmmu device config entry. Signed-off-by: Michael Levit --- configs/devices/riscv32-softmmu/default.mak | 1 + docs/system/riscv/neorv32.rst | 110 ++++++++++ hw/riscv/Kconfig | 8 + hw/riscv/meson.build | 1 + hw/riscv/neorv32.c | 215 ++++++++++++++++++++ include/hw/riscv/neorv32.h | 54 +++++ 6 files changed, 389 insertions(+) create mode 100644 docs/system/riscv/neorv32.rst create mode 100644 hw/riscv/neorv32.c create mode 100644 include/hw/riscv/neorv32.h diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/= riscv32-softmmu/default.mak index c2cd86ce05..4fdc94ab48 100644 --- a/configs/devices/riscv32-softmmu/default.mak +++ b/configs/devices/riscv32-softmmu/default.mak @@ -10,3 +10,4 @@ # CONFIG_SIFIVE_U=3Dn # CONFIG_RISCV_VIRT=3Dn # CONFIG_OPENTITAN=3Dn +# CONFIG_NEORV32=3Dn diff --git a/docs/system/riscv/neorv32.rst b/docs/system/riscv/neorv32.rst new file mode 100644 index 0000000000..7f9048a7ad --- /dev/null +++ b/docs/system/riscv/neorv32.rst @@ -0,0 +1,110 @@ + +NEORV32 Soft SoC (``neorv32``) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + +The ``neorv32`` machine models a minimal NEORV32-based SoC sufficient to +exercise the stock NEORV32 bootloader and run example applications from an +emulated SPI NOR flash. It exposes a UART for console I/O and an MTD-backed +SPI flash device that can be populated with user binaries. + +Neorv32 full repo: +https://github.com/stnolting/neorv32 + +Current QEMU implementation base on commit 7d0ef6b2 in Neorv32 repo. + +Supported devices +----------------- + +The ``neorv32`` machine provides the core peripherals needed by the +bootloader and examples: + +* UART for console (mapped to the QEMU stdio when ``-nographic`` or + ``-serial stdio`` is used). +* SPI controller connected to an emulated SPI NOR flash (exposed to the + guest via QEMU's ``if=3Dmtd`` backend). +* Basic timer/CLINT-like facilities required by the example software. + +(Exact register maps and optional peripherals depend on the QEMU version a= nd +the specific patch series you are using.) + + +QEMU build configuration: +------------------------ +/path/to/qemu/configure \ + --python=3D/usr/local/bin/python3.12 \ + --target-list=3Driscv32-softmmu \ + --enable-fdt \ + --enable-debug \ + --disable-vnc \ + --disable-gtk + +Boot options +------------ + +Typical usage is to boot the NEORV32 bootloader as the QEMU ``-bios`` imag= e, +and to provide a raw SPI flash image via an MTD drive. The bootloader will +then jump to the application image placed at the configured flash offset. + +Preparing the SPI flash with a =E2=80=9CHello World=E2=80=9D example +---------------------------------------------------- + +1. Create a 64 MiB flash image (filled with zeros):: + + $ dd if=3D/dev/zero of=3D$HOME/flash_contents.bin bs=3D1 count=3D$((0x0= 4000000)) + +2. Place your application binary at the **4 MiB** offset inside the flash. + Replace ``/path/to/neorv32_exe.bin`` with the path to your compiled + example application (e.g., the NEORV32 ``hello_world`` example):: + + $ dd if=3D/path/to/neorv32_exe.bin of=3D$HOME/flash_contents.bin \ + bs=3D1 seek=3D$((0x00400000)) conv=3Dnotrunc + +Running the =E2=80=9CHello World=E2=80=9D example +--------------------------------- + +Run QEMU with the NEORV32 bootloader as ``-bios`` and attach the prepared +flash image via the MTD interface. Replace the placeholder paths with your +local paths:: + + $ /path/to/qemu-system-riscv32 -nographic -machine neorv32 \ + -bios /path/to/neorv32/bootloader/neorv32_raw_exe.bin \ + -drive file=3D$HOME/flash_contents.bin,if=3Dmtd,format=3Draw + +Notes: + +* ``-nographic`` routes the UART to your terminal (Ctrl-A X to quit when + using the QEMU monitor hotkeys; or just close the terminal). +* The bootloader starts first and will transfer control to your application + located at the 4 MiB offset of the flash image. +* If you prefer, you can use ``-serial stdio`` instead of ``-nographic``. + +Machine-specific options +------------------------ + +Unless otherwise noted by the patch series, there are no special board +options beyond the standard QEMU options shown above. Commonly useful +generic options include: + +* ``-s -S`` to open a GDB stub on TCP port 1234 and start paused, so you c= an + debug both QEMU and the guest. +* ``-d guest_errors,unimp`` (or other trace flags) for additional logging. + +Example: debugging with GDB:: + + $ /path/to/qemu-system-riscv32 -nographic -machine neorv32 \ + -bios /path/to/neorv32/bootloader/neorv32_raw_exe.bin \ + -drive file=3D$HOME/flash_contents.bin,if=3Dmtd,format=3Draw \ + -s -S + + # In another shell: + $ riscv32-unknown-elf-gdb /path/to/neorv32/bootloader/main.elf + (gdb) target remote :1234 + + +Known limitations +----------------- + +This is a functional model intended for software bring-up and testing of +example programs. It may not model all timing details or every optional +peripheral available in a specific NEORV32 SoC configuration. + diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index fc9c35bd98..976acd2a1b 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -128,3 +128,11 @@ config XIANGSHAN_KUNMINGHU select RISCV_APLIC select RISCV_IMSIC select SERIAL_MM + +config NEORV32 + bool + default y + depends on RISCV32 + select NEORV32_UART + select NEORV32_SPI + select NEORV32_SYSINFO_QEMU diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 2a8d5b136c..b8788e2035 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -14,5 +14,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files( 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-h= pm.c')) riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-gen= eric.c')) riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan= _kmh.c')) +riscv_ss.add(when: 'CONFIG_NEORV32', if_true: files('neorv32.c')) =20 hw_arch +=3D {'riscv': riscv_ss} diff --git a/hw/riscv/neorv32.c b/hw/riscv/neorv32.c new file mode 100644 index 0000000000..41494d2893 --- /dev/null +++ b/hw/riscv/neorv32.c @@ -0,0 +1,215 @@ +/* + * QEMU RISC-V Board Compatible with Neorv32 IP + * + * Provides a board compatible with the Neorv32 IP: + * + * 0) SYSINFO + * 1) IMEM + * 2) DMEM + * 3) UART + * 4) SPI + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/misc/unimp.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/boot.h" +#include "hw/intc/riscv_aclint.h" +#include "chardev/char.h" +#include "system/system.h" +#include "hw/ssi/ssi.h" /* For ssi_realize_and_unref() */ + +#include "hw/riscv/neorv32.h" +#include "hw/misc/neorv32_sysinfo.h" +#include "hw/char/neorv32_uart.h" +#include "hw/ssi/neorv32_spi.h" + +/* TODO: get BOOTLOADER_ROM, IMEM, DMEM sizes from rtl auto-generated head= er */ +static const MemMapEntry neorv32_memmap[] =3D { + + [NEORV32_IMEM] =3D { NEORV32_IMEM_BASE, SYSINFO_IMEM_SIZE= }, + [NEORV32_BOOTLOADER_ROM] =3D { NEORV32_BOOTLOADER_BASE_ADDRESS, 0x2000= }, + [NEORV32_DMEM] =3D { NEORV32_DMEM_BASE, SYSINFO_DMEM_SIZE= }, + [NEORV32_SYSINFO] =3D { NEORV32_SYSINFO_BASE, 0x100}, + [NEORV32_UART0] =3D { NEORV32_UART0_BASE, 0x100}, + [NEORV32_SPI0] =3D { NEORV32_SPI_BASE, 0x100}, +}; + +static void neorv32_machine_init(MachineState *machine) +{ + MachineClass *mc =3D MACHINE_GET_CLASS(machine); + const MemMapEntry *memmap =3D neorv32_memmap; + + Neorv32State *s =3D NEORV32_MACHINE(machine); + MemoryRegion *sys_mem =3D get_system_memory(); + int i; + RISCVBootInfo boot_info; + hwaddr start_addr =3D memmap[NEORV32_BOOTLOADER_ROM].base; + + if (machine->ram_size !=3D mc->default_ram_size) { + char *sz =3D 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_NEORV32_SOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + /* Data Tightly Integrated Memory */ + memory_region_add_subregion(sys_mem, + memmap[NEORV32_DMEM].base, machine->ram); + + /* Instruction Memory (IMEM) */ + memory_region_init_ram(&s->soc.imem_region, OBJECT(&s->soc), + "riscv.neorv32.imem", + memmap[NEORV32_IMEM].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[NEORV32_IMEM].base, + &s->soc.imem_region); + + /* Mask ROM reset vector */ + uint32_t reset_vec[4]; + + reset_vec[1] =3D 0x204002b7; /* 0x1004: lui t0,0x20400 */ + reset_vec[2] =3D 0x00028067; /* 0x1008: jr t0 */ + reset_vec[0] =3D reset_vec[3] =3D 0; + + /* copy in the reset vector in little_endian byte order */ + for (i =3D 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] =3D cpu_to_le32(reset_vec[i]); + } + + /* Neorv32 bootloader */ + if (machine->firmware) { + riscv_find_and_load_firmware(machine, machine->firmware, + &start_addr, NULL); + } + + /* Neorv32 example applications */ + riscv_boot_info_init(&boot_info, &s->soc.cpus); + if (machine->kernel_filename) { + riscv_load_kernel(machine, &boot_info, + memmap[NEORV32_IMEM].base, + false, NULL); + } +} + +static void neorv32_machine_instance_init(Object *obj) +{ + + /* Placeholder for now */ + /* Neorv32State *s =3D NEORV32_MACHINE(obj); */ +} + +static void neorv32_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc =3D MACHINE_CLASS(oc); + + mc->desc =3D "RISC-V SOC compatible with Neorv32 SDK"; + mc->init =3D neorv32_machine_init; + mc->max_cpus =3D 1; + mc->default_cpu_type =3D NEORV32_CPU; + mc->default_ram_id =3D "riscv.neorv32.dmem"; + mc->default_ram_size =3D neorv32_memmap[NEORV32_DMEM].size; + +} + +static const TypeInfo neorv32_machine_typeinfo =3D { + .name =3D MACHINE_TYPE_NAME("neorv32"), + .parent =3D TYPE_MACHINE, + .class_init =3D neorv32_machine_class_init, + .instance_init =3D neorv32_machine_instance_init, + .instance_size =3D sizeof(Neorv32State), +}; + +static void neorv32_machine_init_register_types(void) +{ + type_register_static(&neorv32_machine_typeinfo); +} + +type_init(neorv32_machine_init_register_types) + +static void neorv32_soc_init(Object *obj) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + Neorv32SoCState *s =3D RISCV_NEORV32_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); + object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, + &error_abort); + + object_property_set_int(OBJECT(&s->cpus), "resetvec", + NEORV32_BOOTLOADER_BASE_ADDRESS, &error_abort); + +} + +static void neorv32_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms =3D MACHINE(qdev_get_machine()); + const MemMapEntry *memmap =3D neorv32_memmap; + Neorv32SoCState *s =3D RISCV_NEORV32_SOC(dev); + MemoryRegion *sys_mem =3D get_system_memory(); + + object_property_set_str(OBJECT(&s->cpus), "cpu-type", ms->cpu_type, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); + + /* Bootloader ROM */ + memory_region_init_rom(&s->bootloader_rom, OBJECT(dev), + "riscv.bootloader.rom", + memmap[NEORV32_BOOTLOADER_ROM].size, &error_fat= al); + memory_region_add_subregion(sys_mem, + memmap[NEORV32_BOOTLOADER_ROM].base, &s->bootloader_rom); + + + /* Sysinfo ROM */ + neorv32_sysinfo_create(sys_mem, memmap[NEORV32_SYSINFO].base); + + /* Uart0 */ + neorv32_uart_create(sys_mem, memmap[NEORV32_UART0].base, serial_hd(0)); + + /* SPI controller */ + NEORV32SPIState *spi =3D neorv32_spi_create(sys_mem, + memmap[NEORV32_SPI0].base); + + if (!spi) { + error_setg(errp, "SPI is not created"); + return; + } +} + +static void neorv32_soc_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + dc->realize =3D neorv32_soc_realize; + dc->user_creatable =3D false; +} + +static const TypeInfo neorv32_soc_type_info =3D { + .name =3D TYPE_RISCV_NEORV32_SOC, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(Neorv32SoCState), + .instance_init =3D neorv32_soc_init, + .class_init =3D neorv32_soc_class_init, +}; + +static void neorv32_soc_register_types(void) +{ + type_register_static(&neorv32_soc_type_info); +} + +type_init(neorv32_soc_register_types) diff --git a/include/hw/riscv/neorv32.h b/include/hw/riscv/neorv32.h new file mode 100644 index 0000000000..bc90c2bc04 --- /dev/null +++ b/include/hw/riscv/neorv32.h @@ -0,0 +1,54 @@ +/* + * NEORV32 SOC presentation in QEMU + * + * Copyright (c) 2025 Michael Levit + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_NEORV32_H +#define HW_NEORV32_H + +#include "hw/riscv/riscv_hart.h" +#include "hw/boards.h" + +#if defined(TARGET_RISCV32) +#define NEORV32_CPU TYPE_RISCV_CPU_NEORV32 +#endif + +#define TYPE_RISCV_NEORV32_SOC "riscv.neorv32.soc" +#define RISCV_NEORV32_SOC(obj) \ + OBJECT_CHECK(Neorv32SoCState, (obj), TYPE_RISCV_NEORV32_SOC) + +typedef struct Neorv32SoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *plic; + MemoryRegion imem_region; + MemoryRegion bootloader_rom; +} Neorv32SoCState; + +typedef struct Neorv32State { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + Neorv32SoCState soc; +} Neorv32State; + +#define TYPE_NEORV32_MACHINE MACHINE_TYPE_NAME("neorv32") +#define NEORV32_MACHINE(obj) \ + OBJECT_CHECK(Neorv32State, (obj), TYPE_NEORV32_MACHINE) + +enum { + NEORV32_IMEM, + NEORV32_BOOTLOADER_ROM, + NEORV32_DMEM, + NEORV32_SYSINFO, + NEORV32_UART0, + NEORV32_SPI0, +}; + +#endif /* HW_NEORV32_H */ --=20 2.51.1