From nobody Fri Nov 14 15:21:56 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=1762715822; cv=none; d=zohomail.com; s=zohoarc; b=fHDxJ+AeyTpKduLuYldm37JEAofgN4LqZ7vJkGvD5rPFYyLsCYh1oEKcIFkhVtdIUnQbHdhneNDwLF+0xe+jUoMO/cAaVBgAKYiitrwL1ab73rxZ1BtRh4dkuwrcM4ZzNi08H26d//419J73DnfIf5tF7sgRSVOIPvWZ1CvZ/jE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762715822; 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=yOCt94/jYqEKQ0fi5IY8khjz3WqWYGUTB8ZkxXFgisw=; b=NXRyRpRF4pfuBTJwBxpxDOPiwAEqXLckw4JOb4+pNdpQe3ihal3bYjPhF/CbCZ/8ZKJ/DEwpTmFQ+k573w91XHxQA2/4lZhZ56ebybl9tacQP3QOceChfwXQ9YadA4NxyQJN99JmKoevyU3NzEx1gn/gLQPvZjglrJ+tWIDWXAo= 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 1762715822138356.89353430117023; Sun, 9 Nov 2025 11:17:02 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vIAt6-0003Ob-2y; Sun, 09 Nov 2025 14:15:44 -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 1vIAt3-0003Mf-KR for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:41 -0500 Received: from mail-wm1-x330.google.com ([2a00:1450:4864:20::330]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vIAt0-0007us-2X for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:41 -0500 Received: by mail-wm1-x330.google.com with SMTP id 5b1f17b1804b1-477563e28a3so15405545e9.1 for ; Sun, 09 Nov 2025 11:15:37 -0800 (PST) Received: from DDesktop.local ([2a10:8012:1:b32a:35:83e6:baea:8ad]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47778daf2c3sm54600265e9.10.2025.11.09.11.15.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Nov 2025 11:15:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762715736; x=1763320536; 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=yOCt94/jYqEKQ0fi5IY8khjz3WqWYGUTB8ZkxXFgisw=; b=gYIUPHIRJPGogFPkXDEcuC8goxDEzgBAApcIklpkQ9UTcQ8u2seXCx64nWqcRD7wNt K497cQBKcMKCO86qQPQWJMMDPwCy/253sTTBFlWAC0ztbyCi20MAzs0qB0hl+pnzf3fk d4jAdnuTWqmiasiAvhjAVdU0HeCnEedLCdb4jObN1HGYKjC9Zm5assYxzoelhDpgD3E3 zkuJrR5BO3mUBAzYj7+QZBZDEyTSrQDGS/uxzIaj6D9LHUhSWy10gwZDvry4Ldho+Jn8 39I7yBsqIaRQUzybWgkbKK38ItZtAsGEW+4i+BP5DoRHY5uwoEVk+ipi4NdyyU3EzNnD rb2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762715736; x=1763320536; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=yOCt94/jYqEKQ0fi5IY8khjz3WqWYGUTB8ZkxXFgisw=; b=JszCOnvKNvMZbcPyRNFTxlsTsI3ljB/fiGBA0mqhulUlxoMHGmmMcjNLly4UHar9tY Btzmr63mqF3UjqQkHLFEzadyLR4Fif0O9JO6xfZhpNjTibS1eZRUZR9PN2IGVatNqB5S EG9m5pxkSFAS+vlmUrdCQl8zCXR1gNMRzqkXepl2Lwkg2p4UFWfOQ/13LTuutWP1oUtb JqG89HLf2llt1SRE8GdAB29lIBUt0qKYioxg3St0TAV1B1xkOg9ZQgI0ru0O7vbesVGx 9ZarR8uJDid43e7R4Zod2nAOyocBuFKXJ6Ihy5fY9JC6/yjCKDbPpHPfuhB690RNKTeS 0B1g== X-Gm-Message-State: AOJu0Ywr1mcaExJzczMrPLw9/YxDq5lav7I8gOcvFfQVefaZY37RWnUJ JblbTfUs//FreCYB1LGZzhBOGZionHDWbcdepfGL5E72xryk+oV1/ct7gN2UbCiiOeWUlshb7gj zaj8mdZo= X-Gm-Gg: ASbGnctZkV0mqPRn9w3YpQZ3hXPxBsqdNc4yupcNxvEir1j+WBuqHdHyg/4dlyUCltR d2n4dulWWcEYuVJvGlq3yty527Rr0lChQBlGWZTLk5MHmb1Kr2ICx/FvL5SuIL76PAqqAEwqBDK Vgy88WkA2zqfF2Bnt8xPmGsmjtE04Uht1uodL7/ZXtAjy6hC2Qqghxg9veGblgoFnkfHt1ke1E6 5SyuJTfkBd8Iv40/AX3QwcWw8m2JCJtmJn3v5PYDVbwtVBuqaiNLxrCeXkrqajeI/IhUGzW42dZ IoZ0UOC4P3xEzqO8SjD4OkTaLuH7ARcv4riS25u+pKLEoqYtMdWeOjcb+AVyGYzi4bz7lnBQjZ+ NzpEixQdQtd5FGpn2VH+XqEMFUmEC/YQkFsdnVRv/lgnpjQ0nUXNlD9YmHpOWBfTLgw/d9sLXvw FXqMSQGaWgyGY= X-Google-Smtp-Source: AGHT+IFJsfVD+5aLGfqGgoEpdZnXSs5ITo7S7ZZi4TxhmchvxvJpfRf7R3MdID25JqgTW7Zc2Sv/Hw== X-Received: by 2002:a05:600c:4251:b0:475:de06:dbaf with SMTP id 5b1f17b1804b1-4776dcd61dfmr44318815e9.17.1762715735995; Sun, 09 Nov 2025 11:15:35 -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 v4 1/5] target/riscv: add NEORV32 RV32 CPU type and vendor CSR hooks Date: Sun, 9 Nov 2025 21:15:28 +0200 Message-ID: <20251109191532.32419-2-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251109191532.32419-1-michael@videogpu.com> References: <20251109191532.32419-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::330; envelope-from=michael@videogpu.com; helo=mail-wm1-x330.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: 1762715822421158500 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 --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 17 +++++++++++++ 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, 66 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..e0c1f5888f 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") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 73d4280d7c..67260ed9d7 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 { }, }; @@ -3075,6 +3077,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:21:56 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=1762715847; cv=none; d=zohomail.com; s=zohoarc; b=SHgpYdQKFiOcXXLlRQN3hTabOp1XF6FG2nIDNYgx9JyelFxumZmJui9OlZY1OzPLWbbgu5IOQq7bfD8dz9GIJQO7aOMS/qLVg1cIRsVJIl6JUkvwE+ET9oRGJDmCtAwxBy9q4jrVR62eXbuuBxFqbugg2ioczSgx4g1/PIEt5s4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762715847; 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=SsYZZjrYuiqtZPbk9BBQOOpqKg8L9RjQoMvhN6mqmMF5jr6yiLnniM+N0mmggyLmVG10U733DgJVYBElqJovbivM4EQJLwvfbELslUdBRIU3jicjO33fGMJr7MpLTaBBbgjV0F+uYuRZv3z2WW6wk7v8NFSGj0zkc9FYIpysRfI= 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 1762715847337822.9536522068454; Sun, 9 Nov 2025 11:17:27 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vIAt7-0003Q6-BR; Sun, 09 Nov 2025 14:15:45 -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 1vIAt4-0003Nf-Oy for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:42 -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 1vIAt0-0007w5-UR for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:42 -0500 Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-477563e28a3so15405565e9.1 for ; Sun, 09 Nov 2025 11:15:38 -0800 (PST) Received: from DDesktop.local ([2a10:8012:1:b32a:35:83e6:baea:8ad]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47778daf2c3sm54600265e9.10.2025.11.09.11.15.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Nov 2025 11:15:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762715737; x=1763320537; 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=XceAn7GgEQJEdzlhxwe8MTt28Kdq4ZGyAJoNnW9C1Tu1d3Lh7G/nopOZ9yWreUYvoX 7Wu0FqJJ/fEIbEIqI1dWMuY+JaDuTfFqc7J7oyCNMvkAMenKUVOF4o8NXOJhdAiZ4Y9c d38MpEoGn1p8pMVo4aRastS1h3SflUt30bccCRAjNSCBN8uUOKYxu9Z2zI0XQPbkiXWy hXeCJ6IUS/YupYL2n39HdXy0avqpYuYYduI7x88Fppdjek2kOrc8CqGCcfAZxyjIWoXY alNNwu/e6fYjjOey4wYI/NNdJyOZyvdcLIXJpqx8I6fBbbiAorYXwPgBEFiMI12z6IWK KRSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762715737; x=1763320537; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=5BHzlRkBUYh2EjQ2JUIqKE4S0PnVMUwsZ8qt/uPZSfU=; b=IokpVb9omkCBsILboFNKDw3LXZ/8qPsqf24gp7JR8zAzQs9vEm4+/0qwGVNg2Wd+Zw B8YL65UYPpQXOHvvzx+OJodZDnbwGsExfyMFjOqdrQ9uWuCMhGJ/xn2RORAwrqXBQNFp B6JyEzzg3Y01TKEkXBAD8SxuEizMR1817hDmpxsgW9UWbPUsp76pdugdGYhLc993u6Zv a5e0o+fv4GE7dtgpL99SuWPAuXDYcXfYhAXkCWusenqXpqkIUR+tV1Am80+U6/RQJbKP 5wAWOFIfvvd/eYBLzIiLaZFwiJSOBxspUR6d4l7xsNz3FmZezxy0avgOq75IuTpkrOgX 3zqg== X-Gm-Message-State: AOJu0YxH+1vICXrFQnrYFqtElSRfnQ64uZSnBxQ4X4eHY6tpSdeGBFCO 3RJx+hUp8VLkVAW90KjQe8neS27psns2aydk/lw/bShlZTgbdCibFUf0hd3vm4KlJNkNUy1+wH1 J9WMaCeE= X-Gm-Gg: ASbGnctI4NWmbNrqVK5P806rfaz2O0WgsWEGUPXIg48ZE3jr0zhpd8J0dIsbqdPWPzv CZPaMiHIfA3WLDegG+TsDELgB+w5NsqfDTP/sR6TV1piTsstNmJYYE5gBD2HvmwdcCyVeQvken7 WxOv1isONV9bOJeAlUBawbH0fGVhwSS/D4fWXFCk6am0RBOAdft39YmI1Hu2tjFf835v2fdY4eh gLdT8pxgSA3+FoFAamgjiJcviOwuk/7I6z4xlaD8W1X9vvhOXeVKjeHsUm+5nf9MmdYqKIsUxoA LAMiC5Jpg0txbeGkrscdsrmKfCnRJNQpgLbPIyLGaH9aoMrMpDQBdNZbdqcneYC8XUrFps0OFy/ oWpV4MuDD0vlYBsaczUDD8GSUO1suK0DHhvchBj/xtTyavPH6OwzR4mZ4LSYUIJG5h1pOPR2N5k SKM/BD2fUg//E= X-Google-Smtp-Source: AGHT+IFrtIaIp35/Z0nrwkFsQ4CyEMFVd7lJ+VDTV7LQqLEYHNseh+u4LSXb7wzqyIikiiP58cik3A== X-Received: by 2002:a05:600c:34cd:b0:477:5639:ff66 with SMTP id 5b1f17b1804b1-4776dcbfed3mr76137585e9.13.1762715737138; Sun, 09 Nov 2025 11:15:37 -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 v4 2/5] hw/misc: add NEORV32 SYSINFO block (CLK/MISC/SOC/CACHE) Date: Sun, 9 Nov 2025 21:15:29 +0200 Message-ID: <20251109191532.32419-3-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251109191532.32419-1-michael@videogpu.com> References: <20251109191532.32419-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: 1762715849127158500 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:21:56 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=1762715819; cv=none; d=zohomail.com; s=zohoarc; b=KvsFpYziZ22tHTbwj/R8kJ3ASht46/4GYl1mPA7ljGk7fggGXAOz2AogxU0u61NH+q9Xn7DaeUnuVI4gDXPaRGEnX08SkonDD+5UWJGYd82pRbzsmD7hSVfGoJlNvomtNmXHaPcPyXOq5KRuDncsV/hnBrxEnTj/ZTixWbts/Ho= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762715819; 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=gxy0vVWjbJWULRkHKNN0pbe2L+7sONhep6H3e6FMCPO1qr31eoMmGz9/rNa/G7P2SAQmlIloyyyFXtTMfDndaFNbpqmN83TaQ/DzoUx33DACJQQG0E8N615kcBCKoj4Kss+Je4zy1d4KVWbpJ/KcFveU5vEFU7O/T4QaA4OW5uk= 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 176271581931269.0595319013836; Sun, 9 Nov 2025 11:16:59 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vIAt8-0003S2-VG; Sun, 09 Nov 2025 14:15:46 -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 1vIAt5-0003OU-D0 for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:43 -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 1vIAt2-0007wZ-GJ for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:42 -0500 Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-4775ae77516so27851895e9.1 for ; Sun, 09 Nov 2025 11:15:40 -0800 (PST) Received: from DDesktop.local ([2a10:8012:1:b32a:35:83e6:baea:8ad]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47778daf2c3sm54600265e9.10.2025.11.09.11.15.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Nov 2025 11:15:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762715739; x=1763320539; 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=tIu3uH/tYy9eAwQ4vP6GZ9OC0QT4Oyq/R5sJttdtdpMGqeJkrgo2knPqA1a6vMK9h5 BMHsqHAF9FImx1Qd3PLSKerlTHtNjWC4o+KQfvXMNoRWtQh2+b5PdelPwgWb1fVHZoP9 sw97lD/QnlgraXu/Vv2J3KHnkr2absVf8qyyYNdPUWChruWVRxVcRNs8q+NlLUtyHEMZ VTcnHoelC5oJiekDM5mJ32OG/wfpZAT4rIBziTsbV6FT4LQBmfL7enBSm+gSBTdNg9MN YXKxqqZGWAx54bQSLNephF0htv9Crppand5rdukFPamlz0Ix1GRC1HvmwMNBHIDNEBgb RxOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762715739; x=1763320539; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=coXM3aabJBT21GBiTqQtXzlDQRzt3k5zaMleXYUcg/A=; b=NUt4DN5/csxtwuREYDZ8G7+AK1XIGl3LuvgaAEQJK4PDhQl1gC/GVSpEtCfn/4jXC1 2SyfCcIIeIL53truGx3B2rOAlSpSyhFDLdaKQZpmbMba02IyZ2ZSAUFoE6TjMH9C6+gT hbwF5NxTbSZrEzpANT2OU3vkYJkG9tO5pA8dEdSmaRS5lLaxXSpj801cQZfoJNjOD3JB yTByPY70vYr1Tjw6HmBr9M0pe3e8Rg/XbXxWbtHl6MDh5hbnIgmEhTf2q0NgccWBX8Fk +CKgIjPwZdi9J6MnXw6Dv5q90WAgoQ+WRk8suiclEW/xDSqn0+4Z23VJ0ZKLcHSTpBI/ G1AQ== X-Gm-Message-State: AOJu0Ywiku0rJET/JzQ1SlUFJ9ZBYEQBqngWcQCTpgBPAz39futleZmH iGNEXyggnEgE1zeCkqtz18eRsdIyzvIq50llg9UOBPz8xD10pH9DQpN6BCQsDcMOIN4zPRjBzgS geyjbLeg= X-Gm-Gg: ASbGncvH4896G4ke2WNu88A5SUAH0hBk6CeZyL7UfTKep+a8ZOvxWfmYXrY1sjMHwX5 K4wAAkEcFdwfnOwvL8bMvk6hfvzqImjtycJZb/0fjXfezPYt9FTdVV49rYM7QEXf4jUG9s0DdHx gWNTCKvBLuzsa2RGAY2pskulXhemB9u2foWkY2pfwL+nfLDVaqRbd2avXCi1YvqR6fd6U+rQKSd zWHazZg7alJypN1oQHrIKyF5rHfveZk4TQuaX2BYeG8QPlmm3m6hkEUUOey3CVcrj4ZnBRcNsft bjM4OTikDecx/NbpilKxNtyWdZmRkQIXd0gYPS/tb2DL9qIMaaO/zDStxhAKExUhXKyKCYClVyI fOyoEiahZZwBrK+eWyRDVzX6tSIhYtjcmHHn/Cf82ZgV+6Aonzy2FqnhQoYnr6WNi1L0pBhoXDl 6CrMl9KnJkBt0= X-Google-Smtp-Source: AGHT+IHULoUwWfdlHahlPFUvs9QbBkIQELFwVug5ZYVttmZL2iYPg71eOpBmmFJz/WuTK7C3A1+QjA== X-Received: by 2002:a05:600c:1550:b0:475:dd59:d8da with SMTP id 5b1f17b1804b1-4777329785cmr53765705e9.40.1762715738540; Sun, 09 Nov 2025 11:15:38 -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 v4 3/5] hw/char: add NEORV32 UART (CTRL/DATA, fifo, chardev) Date: Sun, 9 Nov 2025 21:15:30 +0200 Message-ID: <20251109191532.32419-4-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251109191532.32419-1-michael@videogpu.com> References: <20251109191532.32419-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=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: 1762715821417158500 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:21:56 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=1762715819; cv=none; d=zohomail.com; s=zohoarc; b=YDtbKauTGkc1GmJP8bolTGzmflMIepZfs7lm3bkPbl7h/LsJEsCFjq3egKJq5BoKA4wixkADaNfEOI5Zlz43o9by7sLdkAJhRKIbXesEP77NKT6eRbU7dspRfsLWe1IFzaMfNL1A/5th1rK5g7R5PCMD0LqEXmv1UzmR9YTBw9o= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762715819; 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=Bu9SbyR5WIA7BzFdVJqKbYCvKBKgt8VzHjTsd90CKCs7W1+efbwVokkKCAOzmzIcR6Az1uEfALoz4JVl9m57b1wCOdm/cdi4YISqtvOD/X0di+3sOY3rNeK+VSbAaGUEfkwPgth0bTWGOXkH9zkmpZa03lLfXmjc37En1MWWaKY= 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 17627158196433.096845265344541; Sun, 9 Nov 2025 11:16:59 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vIAt9-0003SJ-AU; Sun, 09 Nov 2025 14:15: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 1vIAt6-0003Q3-ME for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:44 -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 1vIAt3-0007xe-KZ for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:44 -0500 Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-4775e891b5eso11430355e9.2 for ; Sun, 09 Nov 2025 11:15:41 -0800 (PST) Received: from DDesktop.local ([2a10:8012:1:b32a:35:83e6:baea:8ad]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47778daf2c3sm54600265e9.10.2025.11.09.11.15.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Nov 2025 11:15:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762715740; x=1763320540; 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=HQMTOAcubr5pSwIinBJX0wickcH0n8YTzvFoDw22XCFIkT3fHK5OnZQ+xdgO7nE8mc dHzKWyNQ8hXmzgpIeUhmr++tj/IjNfwepsxK/RGUEOTyPTqCNDXDSAcEm9t41ST4jPSn MW10+YFmGKt37kgK59aF1QbrLbaZeXA5eOer4zvZ+5BiF703v4F/HEgW+/D6ARZhpVv6 fiKSV9SdaUx12PuysB1fAH++GHj0ap7Pk990pF92dOdsDJlYuVado1CQ2jxjClt/wM3C FUtWSNDs34gEj0XH0oF4PegLG2KIsYVeW9N3DXUOXF7y2B4bSzKKguCam3g/W/Egm5/a oezw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762715740; x=1763320540; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=mNQLK4ewOJjPgvmOfboyv0uwibkrxNDgkc5qo0Kq6ic=; b=ifkzY7OP909NNEtE7zZhliwBA9FGoJTQ+ELt8jTucy3Mp3FxQyqsAa1d1Gsz3K4jcd SXrdGdcJCmjWCpykG9iEuCvfeB2+PDk6zE8ey2cSaYWH7sLBClStOdYfxI9w/MO18yy1 CJVsbkVq0YlZ2+mkGrH1L6mkbwdgRH020ThrbsPkRUMpZWjKAbNdn7GxOunuRxmO47ZK hPqtA9ly4tNr1vBogzP8a5q2In33a43xgDPQcbpvrstd64niBsCrRIUy4q+ez4qR4pxM tNWHusPUTA0+a2jR3wFV7/la0oIfr54H/fmpZkzn+R+EBWQnWcDeevr+YC0Xqs2C9PVL iefw== X-Gm-Message-State: AOJu0YzywjDJWqJQK8+V/GrVvGJLKKP7RfHx3ja3tdJD2Aus2yw4JCkf is2tCAyJUDacZVSZ2NFWKLqUT82Eem7Ucwy62kxjbrCq4S3wWg4hPJZTBSj9f2Dmc2y8AuvjpDG Mgi9XUGE= X-Gm-Gg: ASbGncv8HvfahaHw0Lot1qW5f20KkUxNKZGG6HLzfpZ7utWR6w12ZVgQrwPPTcI1COn ksCKWbXegmK/+FnLftmTCXbqZJz6Xof3DHNTPofk5ncISMMYWumbYNGKd3GdgTeCRocknObA0Jn zm/fCSNF4C3LFS9F2CiBLBQErKJz/XjUtDcZNQkHA+9fDdaqwJIfJ94lOgvfbKLst2Cymn4YhUT 8cLShp/ds8h6Ds8mHcv4/RK2Antj9EFfjeiTf/hRhBwfCxsATV8wz3cL3TzcDiA0VymkNLtKUhl nNRcP66OaTVamkja999oWOkbBXG7yFDHSZ5fTM7ocVQ0MCOfFfgQtR9AQn5850sgqqKTzp2+FlZ ekXN396yM2EaZdFYRDVc9PTjdGdnIt7AMReOcC1lNLI0QvaL5zFeRw6qdX8kyy3Aer159ykA3xd q8ckXu5XiR1Jk= X-Google-Smtp-Source: AGHT+IFIv+zc6NPt6YaUs7F8NtaDBX7b09plvj/DaBvnCzJBKUVDV1+Xhjfh9K8SqxR3Tp9jgJ+F6Q== X-Received: by 2002:a05:600c:4514:b0:45d:d505:a1c3 with SMTP id 5b1f17b1804b1-47773297430mr43030685e9.37.1762715739795; Sun, 09 Nov 2025 11:15:39 -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 v4 4/5] hw/ssi: add NEORV32 SPI controller (SSI master, CS command) Date: Sun, 9 Nov 2025 21:15:31 +0200 Message-ID: <20251109191532.32419-5-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251109191532.32419-1-michael@videogpu.com> References: <20251109191532.32419-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=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: 1762715821413158500 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:21:56 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=1762715819; cv=none; d=zohomail.com; s=zohoarc; b=GS9BQI+do4eagGvRaFZoVLUdETRF4RlwixsPGf72oHFHJk8IVlv+1ay3L5F1yCUxwcMhU9v9ijQguMy0aayEbenON8oiq5lKb9Fujerx3HNh8o5xxmHxBnr9SoLg30Rzdn7zd1JqDQ451VGKoncln/jZbEwKvs5+/KYsfg7Ta4o= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762715819; 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=9yGSINqfnIqpPp6b2Li10zDlwN1Cykb57MReBjofPH4=; b=oISudwIZqeWKegPl7BNCAZBHkjLGM/NKYEeuvalbdJtnFLzMdFIhm/+3HKKYHBW8il5C75nZ5OoNXWnXp40H5Z++M8ioyxi/KsjfCF3AkMstXUlzs+myUeRGPd8iEXopzaut2AyXHUmZzYN6KkJaqm5oUOaQVzhvrNxB1F/FTi4= 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 1762715819546737.2850518591308; Sun, 9 Nov 2025 11:16:59 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vIAtB-0003Ve-BT; Sun, 09 Nov 2025 14:15:49 -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 1vIAt7-0003Qp-Rc for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:45 -0500 Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vIAt5-0007y7-5s for qemu-devel@nongnu.org; Sun, 09 Nov 2025 14:15:45 -0500 Received: by mail-wm1-x32f.google.com with SMTP id 5b1f17b1804b1-47778704516so2754625e9.3 for ; Sun, 09 Nov 2025 11:15:42 -0800 (PST) Received: from DDesktop.local ([2a10:8012:1:b32a:35:83e6:baea:8ad]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47778daf2c3sm54600265e9.10.2025.11.09.11.15.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Nov 2025 11:15:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=videogpu-com.20230601.gappssmtp.com; s=20230601; t=1762715741; x=1763320541; 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=9yGSINqfnIqpPp6b2Li10zDlwN1Cykb57MReBjofPH4=; b=HvJKiCAh/xeNo+7TQ0QhI81BkTIyGuwVLYfx1Wgz6R42Fsz55HdQZm4y/pGzGaFmDU QW5WnTjkYSMlvxpS6zfWimkQ2a8m9ZqrZf7AlKIFoWO+diWmm2eVQNRqMlqHQ9lXCF1k GLsuz1GeCypqfQ2Asjb1I8Y2GtnYA9epZb1EW/oLbnoTPjfnWvoEnIxc44a2dpWJUsiD /N56wfjYNvKLGZndxIGy1bu/Kox3cfFfsIzWBO5LChuij2pCRuEHHUhXQn2R7fVy/mZW kg6+5IFptz5NGbcy50+UFO/HSchlHIrzMaxQBgWlVq4i50X0wO93vonh5EG/ambGnl80 nXNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762715741; x=1763320541; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=9yGSINqfnIqpPp6b2Li10zDlwN1Cykb57MReBjofPH4=; b=sj1/hlKdqBwfC/T5g08vUAvn/JCuxAh32E2/xLGoZmvj9zHbk/04cnT4Tp/iiQwb7L Y3g/xL0Rw1TJWwQvY61d+GJmf/mtwPHlBENbGxk6JaGJ/9LE9utAkR0rKODee3pIcMQ4 wJB5zWP8jNoE0L1z8HxL2BsgM2J9JWkOUmlIt+mTMHabVIdT35qq/qDxFFdXQxv7EU6o aiYwm3mg5fN/pPlCL+nZ/lrYuMHNGYaoQhduUjPxgSbvOH77GIlxMmelUTvieTk0RZR0 1qOOAJYW5ZSZmLPRjJY/OBDXvQdYgF7FbHp8TsfNJS8bQVPIrHMCd4QweZ437BBkOiBh kf3A== X-Gm-Message-State: AOJu0YyaLfnwFY9FV95VeUZNnQxiKwVXP8ZTeY9a/uIh84ni+41pV5JY +rixgsoiCetGJ2anv4M7lRndEy5HHeFP407xOJXm8xMxNMJtYissdjinJ8sUSGvee6EAzQLDilQ wmLsoHhM= X-Gm-Gg: ASbGncuwzmc9c/TRjdzwGR3NHheJv7+wdGMRE/+rf3zUPysHNmSx4/Mw1f5VKHJRXuy t+vtrPjyVsOVLpr1IemlUMok3arL1RN3nqqw5LxpR4LLFzyts2IFqIMIAe1bitK2NZyHL1UMWtR bZR8oY6qxGrpnw9Jtrr3sQsdfTNdDsJog9OLfYTzCX17arz33LlqkPZ0FFVXDMlpzzbyxzTqo6Y ya3aXK6HHlal4HgyXlv8zQKK3AJlLScS9tys1+dydIZ8UU+sVuedT6ipRord4IG2S8eIFzCQkaz oQ9fQ9u2t+A0h55VvYF4nQn8bWTNpIj6+os4q9X3vyF55aNGDREQQlqrX8nW4A2SRL5phOJxWmg 5WsKGq2uKxeNuqPpItfoMBjXvAEte/06HWlNsjMZ/RiyqCX5HjxfGIeD6XcUyKTkfOO76NwMH2l zc3PiQxoRVw7E= X-Google-Smtp-Source: AGHT+IGzWBq/G0yr9MbLP0dlv/ssCTpTQUOqiQgSZXzNf1+VYkvjEjML9hqHJqEX1i+kXJoDqTz+pA== X-Received: by 2002:a05:600c:3b1d:b0:45d:dc85:c009 with SMTP id 5b1f17b1804b1-47773239bd4mr40072045e9.10.1762715741213; Sun, 09 Nov 2025 11:15:41 -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 v4 5/5] hw/riscv: introduce 'neorv32' board, docs, and riscv32 device config Date: Sun, 9 Nov 2025 21:15:32 +0200 Message-ID: <20251109191532.32419-6-michael@videogpu.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251109191532.32419-1-michael@videogpu.com> References: <20251109191532.32419-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::32f; envelope-from=michael@videogpu.com; helo=mail-wm1-x32f.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: 1762715822518158500 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 | 112 ++++++++++ docs/system/target-riscv.rst | 1 + hw/riscv/Kconfig | 8 + hw/riscv/meson.build | 1 + hw/riscv/neorv32.c | 215 ++++++++++++++++++++ include/hw/riscv/neorv32.h | 54 +++++ 7 files changed, 392 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..93ea751b1e --- /dev/null +++ b/docs/system/riscv/neorv32.rst @@ -0,0 +1,112 @@ + +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: +------------------------- + +From the command line:: + + $ /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/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index 89b2cb732c..fbd8270022 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -68,6 +68,7 @@ undocumented; you can get a complete list by running =20 riscv/microblaze-v-generic riscv/microchip-icicle-kit + riscv/neorv32 riscv/shakti-c riscv/sifive_u riscv/virt 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