* [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests
@ 2024-08-05 20:16 Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 01/23] fifo32: add peek function Octavian Purdila
` (23 more replies)
0 siblings, 24 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
This patch set adds support for NXP's RT500 MCU [1] and the RT595
EVK[2]. More RT500 device models will be submitted in future patch sets.
The goal of this first patch set is to provide a minimal set that
allows running the NXP MCU SDK hello world example[4] and to get feedback
on a couple of new / non-standard approaches.
First, this patch set introduces a (python) tool that generates C
header files from ARM SVD files[3]. This significantly reduces the
effort to write a new device model by automatically generating:
register definitions and layout (including bit fields), register names
for easier debugging and tracing, reset register values, etc.
It also introduces unit tests for device models. To allow accessing
registers from unit tests a system bus mock is created. The main
advantage of unit tests for device models over QTest is that device
models can be tested in isolation and do not require a full qemu
machine.
[1] https://www.nxp.com/docs/en/data-sheet/IMXRT500EC.pdf
[2] https://www.nxp.com/webapp/Download?colCode=MIMXRT595EVKHUG
[3] https://arm-software.github.io/CMSIS_5/SVD/html/index.html
[4] Building and running the NXP MCU SDK hello world example
Clone the following git repos:
https://github.com/nxp-mcuxpresso/cmsis.git,
https://github.com/nxp-mcuxpresso/mcux-sdk.git
https://github.com/nxp-mcuxpresso/mcux-sdk-examples.git
in the following directories: CMSIS, core, examples.
cd examples/evkmimxrt595/demo_apps/hello_world/armgcc
ARMGCC_DIR=/usr CFLAGS=-I../../../../../CMSIS/CMSIS/Core/Include \
sh build_flash_debug.sh
qemu-system-arm --machine rt595-evk -kernel flash_debug/hello_world.elf \
-global armv7m.init-nsvtor=0x08001000 -global armv7m.init-svtor=0x08001000 \
-chardev stdio,id=flexcomm0
Octavian Purdila (19):
fifo32: add peek function
tests/unit: add fifo test
hw/arm: add SVD file for NXP i.MX RT595
hw: add register access utility functions
hw/misc: add basic flexcomm device model
test/unit: add register access macros and functions
test/unit: add flexcomm unit test
hw/char: add support for flexcomm usart
test/unit: add flexcomm usart unit test
hw/i2c: add support for flexcomm i2c
test/unit: add i2c-tester
test/unit: add unit tests for flexcomm i2c
test/unit: add spi-tester
hw/misc: add support for RT500's clock controller
test/unit: add unit tests for RT500's clock controller
hw/ssi: add support for flexspi
hw/misc: add support for RT500 reset controller
hw/arm: add basic support for the RT500 SoC
hw/arm: add RT595-EVK board
Sebastian Ene (2):
hw/ssi: add support for flexcomm spi
test/unit: add unit tests for flexcomm spi
Stefan Stanacar (1):
scripts: add script to generate C header files from SVD XML files
Valentin Ghita (1):
tests/unit: add system bus mock
configure | 2 +-
hw/arm/Kconfig | 13 +
hw/arm/meson.build | 4 +
hw/arm/rt500.c | 348 +
hw/arm/rt595-evk.c | 64 +
hw/arm/svd/MIMXRT595S_cm33.xml | 224052 +++++++++++++++++++++++++++
hw/arm/svd/meson.build | 42 +
hw/char/flexcomm_usart.c | 302 +
hw/char/meson.build | 1 +
hw/char/trace-events | 9 +
hw/i2c/flexcomm_i2c.c | 224 +
hw/i2c/meson.build | 1 +
hw/i2c/trace-events | 10 +
hw/misc/Kconfig | 12 +
hw/misc/flexcomm.c | 304 +
hw/misc/meson.build | 5 +
hw/misc/rt500_clkctl0.c | 243 +
hw/misc/rt500_clkctl1.c | 224 +
hw/misc/rt500_rstctl.c | 219 +
hw/misc/trace-events | 18 +
hw/ssi/Kconfig | 4 +
hw/ssi/flexcomm_spi.c | 443 +
hw/ssi/flexspi.c | 216 +
hw/ssi/meson.build | 2 +
hw/ssi/trace-events | 12 +
include/hw/arm/rt500.h | 49 +
include/hw/char/flexcomm_usart.h | 20 +
include/hw/i2c/flexcomm_i2c.h | 27 +
include/hw/misc/flexcomm.h | 92 +
include/hw/misc/rt500_clk_freqs.h | 18 +
include/hw/misc/rt500_clkctl0.h | 37 +
include/hw/misc/rt500_clkctl1.h | 38 +
include/hw/misc/rt500_rstctl.h | 38 +
include/hw/regs.h | 89 +
include/hw/ssi/flexcomm_spi.h | 20 +
include/hw/ssi/flexspi.h | 34 +
include/qemu/fifo32.h | 29 +
meson.build | 4 +
python/setup.cfg | 1 +
python/tests/minreqs.txt | 3 +
pythondeps.toml | 3 +
scripts/svd-gen-header.py | 342 +
tests/unit/i2c_tester.c | 111 +
tests/unit/i2c_tester.h | 34 +
tests/unit/meson.build | 56 +-
tests/unit/reg-utils.h | 103 +
tests/unit/spi_tester.c | 60 +
tests/unit/spi_tester.h | 32 +
tests/unit/sysbus-mock.c | 314 +
tests/unit/sysbus-mock.h | 82 +
tests/unit/test-fifo.c | 98 +
tests/unit/test-flexcomm-i2c.c | 209 +
tests/unit/test-flexcomm-spi.c | 204 +
tests/unit/test-flexcomm-usart.c | 321 +
tests/unit/test-flexcomm.c | 215 +
tests/unit/test-rt500-clkctl.c | 270 +
56 files changed, 229725 insertions(+), 2 deletions(-)
create mode 100644 hw/arm/rt500.c
create mode 100644 hw/arm/rt595-evk.c
create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
create mode 100644 hw/arm/svd/meson.build
create mode 100644 hw/char/flexcomm_usart.c
create mode 100644 hw/i2c/flexcomm_i2c.c
create mode 100644 hw/misc/flexcomm.c
create mode 100644 hw/misc/rt500_clkctl0.c
create mode 100644 hw/misc/rt500_clkctl1.c
create mode 100644 hw/misc/rt500_rstctl.c
create mode 100644 hw/ssi/flexcomm_spi.c
create mode 100644 hw/ssi/flexspi.c
create mode 100644 include/hw/arm/rt500.h
create mode 100644 include/hw/char/flexcomm_usart.h
create mode 100644 include/hw/i2c/flexcomm_i2c.h
create mode 100644 include/hw/misc/flexcomm.h
create mode 100644 include/hw/misc/rt500_clk_freqs.h
create mode 100644 include/hw/misc/rt500_clkctl0.h
create mode 100644 include/hw/misc/rt500_clkctl1.h
create mode 100644 include/hw/misc/rt500_rstctl.h
create mode 100644 include/hw/regs.h
create mode 100644 include/hw/ssi/flexcomm_spi.h
create mode 100644 include/hw/ssi/flexspi.h
create mode 100755 scripts/svd-gen-header.py
create mode 100644 tests/unit/i2c_tester.c
create mode 100644 tests/unit/i2c_tester.h
create mode 100644 tests/unit/reg-utils.h
create mode 100644 tests/unit/spi_tester.c
create mode 100644 tests/unit/spi_tester.h
create mode 100644 tests/unit/sysbus-mock.c
create mode 100644 tests/unit/sysbus-mock.h
create mode 100644 tests/unit/test-fifo.c
create mode 100644 tests/unit/test-flexcomm-i2c.c
create mode 100644 tests/unit/test-flexcomm-spi.c
create mode 100644 tests/unit/test-flexcomm-usart.c
create mode 100644 tests/unit/test-flexcomm.c
create mode 100644 tests/unit/test-rt500-clkctl.c
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply [flat|nested] 56+ messages in thread
* [RFC PATCH 01/23] fifo32: add peek function
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
@ 2024-08-05 20:16 ` Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 02/23] tests/unit: add fifo test Octavian Purdila
` (22 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add fifo32_peek() that returns the first element from the queue
without popping it.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
include/qemu/fifo32.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/include/qemu/fifo32.h b/include/qemu/fifo32.h
index 4e9fd1b5ef..c9befc47c8 100644
--- a/include/qemu/fifo32.h
+++ b/include/qemu/fifo32.h
@@ -140,6 +140,35 @@ static inline uint32_t fifo32_pop(Fifo32 *fifo)
return ret;
}
+/**
+ * fifo32_peek:
+ * @fifo: fifo to peek at
+ *
+ * Returns the value from the FIFO's head without poping it. Behaviour
+ * is undefined if the FIFO is empty. Clients are responsible for
+ * checking for emptiness using fifo32_is_empty().
+ *
+ * Returns: the value from the FIFO's head
+ */
+
+static inline uint32_t fifo32_peek(Fifo32 *fifo)
+{
+ uint32_t ret = 0, num;
+ const uint8_t *buf;
+ int i;
+
+ buf = fifo8_peek_buf(&fifo->fifo, 4, &num);
+ if (num != 4) {
+ return ret;
+ }
+
+ for (i = 0; i < sizeof(uint32_t); i++) {
+ ret |= buf[i] << (i * 8);
+ }
+
+ return ret;
+}
+
/**
* There is no fifo32_pop_buf() because the data is not stored in the buffer
* as a set of native-order words.
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 02/23] tests/unit: add fifo test
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 01/23] fifo32: add peek function Octavian Purdila
@ 2024-08-05 20:16 ` Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files Octavian Purdila
` (21 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add a simple FIFO unit test that test wrap around and push, pop and
peek for both fifo8 and fifo32.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 1 +
tests/unit/test-fifo.c | 98 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)
create mode 100644 tests/unit/test-fifo.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 26c109c968..397f2503f8 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -47,6 +47,7 @@ tests = {
'test-logging': [],
'test-qapi-util': [],
'test-interval-tree': [],
+ 'test-fifo': [],
}
if have_system or have_tools
diff --git a/tests/unit/test-fifo.c b/tests/unit/test-fifo.c
new file mode 100644
index 0000000000..1686f8bd59
--- /dev/null
+++ b/tests/unit/test-fifo.c
@@ -0,0 +1,98 @@
+/*
+ * QEMU FIFO testing
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/fifo8.h"
+#include "qemu/fifo32.h"
+
+typedef struct {
+ Fifo8 fifo8;
+ Fifo32 fifo32;
+} TestFixture;
+
+#define FIFO_SIZE 13
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ int n = (uintptr_t) data;
+
+ fifo8_create(&f->fifo8, n);
+ fifo32_create(&f->fifo32, n);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ fifo8_destroy(&f->fifo8);
+ fifo32_destroy(&f->fifo32);
+}
+
+static void test_push_pop_batch(TestFixture *f, int n)
+{
+ uint8_t i;
+
+ /* push and check peek */
+ for (i = 0; i < n; i++) {
+ uint8_t val8 = i;
+ uint32_t val32 = i | ((i + 1) << 8) | ((i + 2) << 16) | ((i + 3) << 24);
+
+ fifo8_push(&f->fifo8, val8);
+ if (i == 0) {
+ g_assert(*fifo8_peek_buf(&f->fifo8, 1, NULL) == val8);
+ }
+
+ fifo32_push(&f->fifo32, val32);
+ if (i == 0) {
+ g_assert(fifo32_peek(&f->fifo32) == val32);
+ }
+ }
+
+ /* check peek and pop */
+ for (i = 0; i < n; i++) {
+ uint8_t val8 = i;
+ uint32_t val32 = i | ((i + 1) << 8) | ((i + 2) << 16) | ((i + 3) << 24);
+
+ g_assert(*fifo8_peek_buf(&f->fifo8, 1, NULL) == val8);
+ g_assert(fifo8_pop(&f->fifo8) == val8);
+
+ g_assert(fifo32_peek(&f->fifo32) == val32);
+ g_assert(fifo32_pop(&f->fifo32) == val32);
+ }
+}
+
+/* max n should be less then 256 - 3 */
+static void wrap_around_test(TestFixture *f, gconstpointer user_data)
+{
+ int n = (uintptr_t) user_data;
+ const int cycles = 3;
+ int i;
+
+ for (i = 0; i < cycles; i++) {
+ test_push_pop_batch(f, n / 2 + 1);
+ }
+}
+
+/* mock-ups */
+void *vmstate_info_buffer;
+uint32_t vmstate_info_uint32;
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add("/fifo/wrap-around", TestFixture, (gconstpointer)FIFO_SIZE,
+ set_up, wrap_around_test, tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 01/23] fifo32: add peek function Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 02/23] tests/unit: add fifo test Octavian Purdila
@ 2024-08-05 20:16 ` Octavian Purdila
2024-08-08 21:56 ` John Snow
2024-08-12 15:27 ` Peter Maydell
2024-08-05 20:16 ` [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595 Octavian Purdila
` (20 subsequent siblings)
23 siblings, 2 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
From: Stefan Stanacar <stefanst@google.com>
From: Stefan Stanacar <stefanst@google.com>
The CMSIS System View Description format(CMSIS-SVD) is an XML based
description of Arm Cortex-M microcontrollers provided and maintained
by sillicon vendors. It includes details such as peripherals registers
(down to bitfields), peripheral register block addresses, reset
values, etc.
This script uses this information to create header files that makes it
easier to emulate peripherals.
The script can be used to create either peripheral specific headers or
board / system specific information. The script generated headers are
similar to the SVDConv utility.
Peripheral specific headers contains information such as register
layout, register names and reset values for registers:
typedef struct {
...
union {
uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
* Flexcomm module ID */
struct {
uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
uint32_t : 4;
uint32_t ID : 20; /* [31..12] Flexcomm ID */
} PSELID_b;
};
...
} FLEXCOMM_Type; /* Size = 4096 (0x1000) */
#define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
#define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
#define FLEXCOMM_PSELID_LOCK_Pos (3UL)
#define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
...
typedef enum { /* FLEXCOMM_PSELID_LOCK */
/* Peripheral select can be changed by software. */
FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
/* Peripheral select is locked and cannot be changed until this
* Flexcomm module or the entire device is reset. */
FLEXCOMM_PSELID_LOCK_LOCKED = 1,
} FLEXCOMM_PSELID_LOCK_Enum;
...
#define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
const char *_name[sizeof(FLEXCOMM_Type)] = { \
[4088 ... 4091] = "PSELID", \
[4092 ... 4095] = "PID", \
}
Board specific headers contains information about peripheral base
register addresses.
Signed-off-by: Stefan Stanacar <stefanst@google.com>
Signed-off-by: Octavian Purdila <tavip@google.com>
---
configure | 2 +-
meson.build | 4 +
python/setup.cfg | 1 +
python/tests/minreqs.txt | 3 +
pythondeps.toml | 3 +
| 342 ++++++++++++++++++++++++++++++++++++++
6 files changed, 354 insertions(+), 1 deletion(-)
create mode 100755 scripts/svd-gen-header.py
diff --git a/configure b/configure
index 5ad1674ca5..811bfa5d54 100755
--- a/configure
+++ b/configure
@@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
# Finish preparing the virtual environment using vendored .whl files
$mkvenv ensuregroup --dir "${source_path}/python/wheels" \
- ${source_path}/pythondeps.toml meson || exit 1
+ ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
# At this point, we expect Meson to be installed and available.
# We expect mkvenv or pip to have created pyvenv/bin/meson for us.
diff --git a/meson.build b/meson.build
index ec59effca2..dee587483b 100644
--- a/meson.build
+++ b/meson.build
@@ -3235,6 +3235,10 @@ tracetool_depends = files(
'scripts/tracetool/vcpu.py'
)
+svd_gen_header = [
+ python, files('scripts/svd-gen-header.py')
+]
+
qemu_version_cmd = [find_program('scripts/qemu-version.sh'),
meson.current_source_dir(),
get_option('pkgversion'), meson.project_version()]
diff --git a/python/setup.cfg b/python/setup.cfg
index 48668609d3..bc830c541a 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -45,6 +45,7 @@ devel =
urwid >= 2.1.2
urwid-readline >= 0.13
Pygments >= 2.9.0
+ pysvd >= 0.2.3
# Provides qom-fuse functionality
fuse =
diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt
index a3f423efd8..7993fcd23c 100644
--- a/python/tests/minreqs.txt
+++ b/python/tests/minreqs.txt
@@ -22,6 +22,9 @@ distlib==0.3.6
# Dependencies for FUSE support for qom-fuse
fusepy==2.0.4
+# Dependencies for svd-gen-regs
+pysvd==0.2.3
+
# Test-runners, utilities, etc.
avocado-framework==90.0
diff --git a/pythondeps.toml b/pythondeps.toml
index 9c16602d30..8416b17650 100644
--- a/pythondeps.toml
+++ b/pythondeps.toml
@@ -32,3 +32,6 @@ sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" }
# avocado-framework, for example right now the limit is 92.x.
avocado-framework = { accepted = "(>=88.1, <93.0)", installed = "88.1", canary = "avocado" }
pycdlib = { accepted = ">=1.11.0" }
+
+[svd-gen-header]
+pysvd = { accepted = ">=0.2.3.", installed = "0.2.3" }
--git a/scripts/svd-gen-header.py b/scripts/svd-gen-header.py
new file mode 100755
index 0000000000..ab8cb4b665
--- /dev/null
+++ b/scripts/svd-gen-header.py
@@ -0,0 +1,342 @@
+#!/usr/bin/env python3
+
+# Copyright 2024 Google LLC
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# Use this script to generate a C header file from an SVD xml
+#
+# Two mode of operations are supported: peripheral and system.
+#
+# When running in peripheral mode a header for a specific peripheral
+# is going to be generated. It will define a type and structure with
+# all of the available registers at the bitfield level. An array that
+# contains the reigster names indexed by address is also going to be
+# generated as well as a function to initialize registers to their
+# reset values.
+#
+# Invocation example:
+#
+# svd_gen_header -i MIMXRT595S_cm33.xml -o flexcomm.h -p FLEXCOMM0 -t FLEXCOMM
+#
+# When running in system mode a header for a specific system /
+# platform will be generated. It will define register base addresses
+# and interrupt numbers for selected peripherals.
+#
+# Invocation example:
+#
+# svd_gen_header -i MIMXRT595S_cm33.xml -o rt500.h -s RT500 -p FLEXCOMM0 \
+# -p CLKCTL0 -p CLKCTL1
+#
+
+import argparse
+import datetime
+import re
+import os
+import sys
+import xml.etree.ElementTree
+import pysvd
+
+data_type_by_bits = {
+ 8: "uint8_t",
+ 16: "uint16_t",
+ 32: "uint32_t",
+}
+
+
+def get_register_array_name_and_size(register):
+ """Return register name and register array size.
+
+ The SVD can define register arrays and pysvd encodes the whole set
+ as as regular register with their name prepended by [<array size>].
+
+ Returns a tuple with the register name and the size of the array or
+ zero if this is not a register set.
+
+ """
+
+ split = re.split(r"[\[\]]", register.name)
+ return (split[0], int(split[1]) if len(split) > 1 else 0)
+
+
+def generate_register(register):
+ """Generate register data.
+
+ This include a field for accessing the full 32bits as we as
+ bitfield based register fields.
+
+ """
+
+ data_type = data_type_by_bits[register.size]
+
+ out = f" /* 0x{register.addressOffset:08X} {register.description} */\n"
+ out += " union {\n"
+ out += f" {data_type} {register.name};\n"
+ out += " struct {\n"
+
+ fields = sorted(register.fields, key=lambda field: field.bitOffset)
+ last_msb = -1
+ for field in fields:
+ reserve_bits = 0
+ lsb = field.bitOffset
+ msb = field.bitWidth + lsb - 1
+
+ if last_msb == -1 and lsb > 0:
+ reserve_bits = lsb
+
+ if last_msb != -1 and (lsb - last_msb) > 1:
+ reserve_bits = lsb - last_msb - 1
+
+ if reserve_bits > 0:
+ out += f" {data_type}:{reserve_bits};\n"
+
+ out += f" /* [{msb}..{lsb}] {field.description} */\n"
+ out += f" {data_type} {field.name} : {field.bitWidth};\n"
+
+ last_msb = msb
+
+ if register.size - last_msb > 1:
+ out += f" {data_type}:{register.size - last_msb - 1};\n"
+
+ reg_name, reg_array_size = get_register_array_name_and_size(register)
+ if reg_array_size > 0:
+ out += f" }} {reg_name}_b[{reg_array_size}];\n"
+ else:
+ out += f" }} {reg_name}_b;\n"
+ out += " };\n\n"
+
+ return out
+
+
+def generate_pos_and_msk_defines(name, periph):
+ """Generate Pos and Msk defines"""
+
+ out = "\n"
+ for reg in periph.registers:
+ for field in reg.fields:
+ reg_name, _ = get_register_array_name_and_size(reg)
+ field_name = f"{name}_{reg_name}_{field.name}"
+ out += f"#define {field_name}_Pos ({field.bitOffset}UL)\n"
+ mask = ((1 << field.bitWidth) - 1) << field.bitOffset
+ out += f"#define {field_name}_Msk (0x{mask:x}UL)\n"
+
+ return out
+
+
+def generate_enum_values(name, periph):
+ """Generate enum values"""
+
+ out = "\n"
+ for reg in periph.registers:
+ reg_name, _ = get_register_array_name_and_size(reg)
+ for field in reg.fields:
+ if hasattr(field, "enumeratedValues"):
+ out += "\n"
+ out += "typedef enum {\n"
+ for enum in field.enumeratedValues.enumeratedValues:
+ enum_name = f"{name}_{reg_name}_{field.name}_{enum.name}"
+ out += f" /* {enum.description} */\n"
+ out += f" {enum_name} = {enum.value},\n"
+ out += f"}} {name}_{reg_name}_{field.name}_Enum;\n"
+
+ return out
+
+
+def generate_register_names_array_macro(name, periph):
+ """Generate register names array macro"""
+
+ out = f"#define {name}_REGISTER_NAMES_ARRAY(_name) \\\n"
+ out += f" const char *_name[sizeof({name}_Type)] = {{ \\\n"
+ for reg in periph.registers:
+ reg_name, reg_array_size = get_register_array_name_and_size(reg)
+ if reg_array_size > 0:
+ for i in range(0, reg_array_size):
+ start = reg.addressOffset + i * reg.size // 8
+ stop = reg.addressOffset + (i + 1) * reg.size // 8 - 1
+ out += f' [{start} ... {stop}] = "{reg_name}{i}", \\\n'
+ else:
+ start = reg.addressOffset
+ stop = reg.addressOffset + reg.size // 8 - 1
+ out += f' [{start} ... {stop}] = "{reg.name}", \\\n'
+ out += " }\n"
+
+ return out
+
+
+def generate_reset_registers_function(name, periph):
+ """Generate reset registers function"""
+
+ out = "\n"
+ fname = f"{name.lower()}_reset_registers"
+ out += f"static inline void {fname}({name}_Type *regs)\n"
+ out += "{\n"
+ for reg in periph.registers:
+ reg_name, reg_array_size = get_register_array_name_and_size(reg)
+ if reg_array_size > 0:
+ for i in range(0, int(reg_array_size)):
+ out += f" regs->{reg_name}[{i}] = {hex(reg.resetValue)};\n"
+ else:
+ out += f" regs->{reg_name} = {hex(reg.resetValue)};\n"
+ out += "}\n"
+
+ return out
+
+
+def generate_peripheral_header(periph, name):
+ """Generate peripheral header
+
+ The following information is generated:
+
+ * typedef with all of the available registers and register fields,
+ position and mask defines for register fields.
+
+ * enum values that encode register fields options.
+
+ * a macro that defines the register names indexed by the relative
+ address of the register.
+
+ * a function that sets the registers to their reset values
+
+ """
+
+ out = f"/* {name} - {periph.description} */\n\n"
+ out += "typedef struct {\n"
+
+ last_reg_offset = 0
+ cnt = 0
+ for reg in periph.registers:
+ reserved_words = 0
+ if (reg.addressOffset - last_reg_offset) > 0:
+ reserved_words = int((reg.addressOffset - last_reg_offset) // 4)
+ cnt += 1
+
+ if cnt:
+ show_count = cnt
+ else:
+ show_count = ""
+
+ if reserved_words == 1:
+ out += f" uint32_t RESERVED{show_count};\n\n"
+ elif reserved_words > 1:
+ out += f" uint32_t RESERVED{show_count}[{reserved_words}];\n\n"
+
+ out += generate_register(reg)
+ last_reg_offset = reg.addressOffset + reg.size // 8
+
+ size = periph.addressBlocks[0].size
+ out += f"}} {name}_Type; /* Size = {size} (0x{size:X}) */\n"
+
+ out += "\n\n"
+
+ out += generate_pos_and_msk_defines(name, periph)
+
+ out += generate_enum_values(name, periph)
+
+ out += generate_register_names_array_macro(name, periph)
+
+ out += generate_reset_registers_function(name, periph)
+
+ return out
+
+
+def get_same_class_peripherals(svd, periph):
+ """Get a list of peripherals that are instances of the same class."""
+
+ return [periph] + [
+ p
+ for p in svd.peripherals
+ if p.derivedFrom and p.derivedFrom.name == periph.name
+ ]
+
+
+def generate_system_header(system, svd, periph):
+ """Generate base and irq defines for given list of peripherals"""
+
+ out = ""
+
+ for p in get_same_class_peripherals(svd, periph):
+ out += f"#define {system}_{p.name}_BASE 0x{p.baseAddress:X}UL\n"
+ out += "\n"
+
+ for p in get_same_class_peripherals(svd, periph):
+ for irq in p.interrupts:
+ out += f"#define {system}_{irq.name}_IRQn 0x{irq.value}UL\n"
+ out += "\n"
+
+ return out
+
+
+def main():
+ """Script to generate C header file from an SVD file"""
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-i", "--input", type=str, help="Input SVD file", required=True
+ )
+ parser.add_argument(
+ "-o", "--output", type=str, help="Output .h file", required=True
+ )
+ parser.add_argument(
+ "-p",
+ "--peripheral",
+ action="append",
+ help="peripheral name from the SVD file",
+ required=True,
+ )
+ parser.add_argument(
+ "-t",
+ "--type-name",
+ type=str,
+ help="name to be used for peripheral definitions",
+ required=False,
+ )
+ parser.add_argument(
+ "-s",
+ "--system",
+ type=str,
+ help="name to be used for the system definitions",
+ required=False,
+ )
+
+ args = parser.parse_args()
+
+ node = xml.etree.ElementTree.parse(args.input).getroot()
+ svd = pysvd.element.Device(node)
+
+ # Write license header
+ out = "/*\n"
+ license_text = svd.licenseText.split("\\n")
+ for line in license_text:
+ sline = f" * {line.strip()}"
+ out += f"{sline.rstrip()}\n"
+
+ now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
+ out += f" * @note Automatically generated by {os.path.basename(__file__)}"
+ out += f" on {now} UTC from {os.path.basename(args.input)}.\n"
+ out += " *\n */\n\n"
+
+ # Write some generic defines
+ out += "#pragma once\n\n"
+
+ for name in args.peripheral:
+ periph = svd.find(name)
+ if periph:
+ if args.system:
+ out += generate_system_header(args.system, svd, periph)
+ else:
+ out += generate_peripheral_header(
+ periph, args.type_name if args.type_name else periph.name
+ )
+ else:
+ print(f"No such peripheral: {name}")
+ return 1
+
+ with open(args.output, "w", encoding="ascii") as output:
+ output.write(out)
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (2 preceding siblings ...)
2024-08-05 20:16 ` [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files Octavian Purdila
@ 2024-08-05 20:16 ` Octavian Purdila
2024-08-06 14:06 ` Alex Bennée
2024-08-05 20:17 ` [RFC PATCH 05/23] hw: add register access utility functions Octavian Purdila
` (19 subsequent siblings)
23 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:16 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Picked from:
https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
NOTE: the file is truncated to keep the email size reasonable. Please
use the link above and download the full file if you want to try out
the patch.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/MIMXRT595S_cm33.xml | 224052 ++++++++++++++++++++++++++++++
1 file changed, 224052 insertions(+)
create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
new file mode 100644
index 0000000000..8943aa3555
--- /dev/null
+++ b/hw/arm/svd/MIMXRT595S_cm33.xml
@@ -0,0 +1,1725 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
+ <vendor>nxp.com</vendor>
+ <name>MIMXRT595S_cm33</name>
+ <version>1.0</version>
+ <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
+ <licenseText>
+Copyright 2016-2023 NXP
+SPDX-License-Identifier: BSD-3-Clause
+ </licenseText>
+ <cpu>
+ <name>CM33</name>
+ <revision>r2p0</revision>
+ <endian>little</endian>
+ <mpuPresent>true</mpuPresent>
+ <fpuPresent>true</fpuPresent>
+ <vtorPresent>true</vtorPresent>
+ <nvicPrioBits>3</nvicPrioBits>
+ <vendorSystickConfig>false</vendorSystickConfig>
+ </cpu>
+ <addressUnitBits>8</addressUnitBits>
+ <width>32</width>
+ <peripherals>
+ <peripheral>
+ <name>RSTCTL0</name>
+ <description>Reset Controller 0</description>
+ <groupName>RSTCTL0</groupName>
+ <baseAddress>0x40000000</baseAddress>
+ <addressBlock>
+ <offset>0</offset>
+ <size>0x7C</size>
+ <usage>registers</usage>
+ </addressBlock>
+ <registers>
+ <register>
+ <name>SYSRSTSTAT</name>
+ <description>System Reset Status Register</description>
+ <addressOffset>0</addressOffset>
+ <size>32</size>
+ <access>read-write</access>
+ <resetValue>0x1</resetValue>
+ <resetMask>0xFFFFFFFF</resetMask>
+ <fields>
+ <field>
+ <name>VDD_POR</name>
+ <description>VDD CORE Power-On Reset (POR) was detected</description>
+ <bitOffset>0</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>VDD_POR_EVENT_IS_NOT_DETECTED</name>
+ <description>No VDD CORE POR event is detected</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>VDD_POR_EVENT_WAS_DETECTED</name>
+ <description>VDD CORE POR event was detected</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>PAD_RESET</name>
+ <description>RESETN pin reset was detected</description>
+ <bitOffset>4</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>PAD_RESET_IS_NOT_DETECTED</name>
+ <description>No RESETN pin event is detected</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>PAD_RESET_WAS_DETECTED</name>
+ <description>RESETN pin event was detected. Write '1' to clear this bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ARM_RESET</name>
+ <description>ARM reset was detected</description>
+ <bitOffset>5</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ARM_RESET_IS_NOT_DETECTED</name>
+ <description>No ARM reset event is detected</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ARM_RESET_WAS_DETECTED</name>
+ <description>ARM reset was detected. Write '1' to clear this bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>WDT0_RESET</name>
+ <description>WatchDog Timer 0 reset was detected</description>
+ <bitOffset>6</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>WDT0_RESET_IS_NOT_DETECTED</name>
+ <description>No WDT0 reset event detected</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>WDT0_RESET_WAS_DETECTED</name>
+ <description>WDT0 reset event detected. Write '1' to clear this bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>WDT1_RESET</name>
+ <description>WatchDog Timer 1 reset was detected</description>
+ <bitOffset>7</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>WDT1_RESET_IS_NOT_DETECTED</name>
+ <description>No WDT1 reset event detected</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>WDT1_RESET_WAS_DETECTED</name>
+ <description>WDT1 reset event detected. Write '1' to clear this bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL0</name>
+ <description>Peripheral Reset Control Register 0</description>
+ <addressOffset>0x10</addressOffset>
+ <size>32</size>
+ <access>read-write</access>
+ <resetValue>0x7DF51F0A</resetValue>
+ <resetMask>0xFFFFFFFF</resetMask>
+ <fields>
+ <field>
+ <name>DSP</name>
+ <description>Fusion F1 DSP reset control</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DSP_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DSP_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>AXI_SWITCH</name>
+ <description>AXI Switch reset control</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>AXI_SWITCH_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>AXI_SWITCH_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>POWERQUAD</name>
+ <description>POWERQUAD reset control</description>
+ <bitOffset>8</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>POWERQUAD_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>POWERQUAD_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>CASPER</name>
+ <description>CASPER reset control</description>
+ <bitOffset>9</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>CASPER_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>CASPER_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>HASHCRYPT</name>
+ <description>Hash-Crypt reset control</description>
+ <bitOffset>10</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>HASHCRYPT_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>HASHCRYPT_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>PUF</name>
+ <description>PUF reset control</description>
+ <bitOffset>11</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>PUF_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>PUF_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>RNG</name>
+ <description>RNG reset control</description>
+ <bitOffset>12</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>RNG_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>RNG_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI0_OTFAD</name>
+ <description>FLEXSPI0 and OTFAD reset control</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI1</name>
+ <description>FLEXSPI1 reset control</description>
+ <bitOffset>18</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI1_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI1_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_PHY</name>
+ <description>USB PHY reset control</description>
+ <bitOffset>20</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_PHY_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_PHY_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_DEVICE</name>
+ <description>USB HS Device reset control</description>
+ <bitOffset>21</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_HOST</name>
+ <description>USB HOST reset control</description>
+ <bitOffset>22</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_HOST_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_HOST_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_SRAM</name>
+ <description>USB RAM reset control</description>
+ <bitOffset>23</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_SRAM_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_SRAM_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SCT</name>
+ <description>SCTimer reset control</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SCT_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SCT_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>GPU</name>
+ <description>GPU reset control</description>
+ <bitOffset>26</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>GPU_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>GPU_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>DISPLAY_CONTROLLER</name>
+ <description>LCDIF Display Controller reset control</description>
+ <bitOffset>27</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_CONTROLLER</name>
+ <description>MIPI Digital serial Interface controller reset control</description>
+ <bitOffset>28</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_PHY</name>
+ <description>MIPI DSI PHY reset control</description>
+ <bitOffset>29</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SMARTDMA</name>
+ <description>SMARTDMA Event/Algorithm handler reset control</description>
+ <bitOffset>30</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SMARTDMA_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SMARTDMA_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL1</name>
+ <description>Peripheral Reset Control Register 1</description>
+ <addressOffset>0x14</addressOffset>
+ <size>32</size>
+ <access>read-write</access>
+ <resetValue>0x101800C</resetValue>
+ <resetMask>0xFFFFFFFF</resetMask>
+ <fields>
+ <field>
+ <name>SDIO0</name>
+ <description>SDIO0 reset control</description>
+ <bitOffset>2</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SDIO1</name>
+ <description>SDIO1 reset control</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO1_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO1_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ACMP0</name>
+ <description>Analog comparator reset control</description>
+ <bitOffset>15</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ACMP0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ACMP0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ADC0</name>
+ <description>Analog-to-Digital converter reset control</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ADC0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ADC0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SHSGPIO0</name>
+ <description>Secure GPIO 0 reset control</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SHSGPIO0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SHSGPIO0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL2</name>
+ <description>Peripheral Reset Control Register 2</description>
+ <addressOffset>0x18</addressOffset>
+ <size>32</size>
+ <access>read-write</access>
+ <resetValue>0x1C000001</resetValue>
+ <resetMask>0xFFFFFFFF</resetMask>
+ <fields>
+ <field>
+ <name>UTICK0</name>
+ <description>Micro-tick timer reset control</description>
+ <bitOffset>0</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>UTICK0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>UTICK0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>WWDT0</name>
+ <description>Watchdog timer reset control</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>read-write</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>WWDT0_CLR</name>
+ <description>Clear Reset</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>WWDT0_SET</name>
+ <description>Set Reset</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL0_SET</name>
+ <description>Peripheral Reset Control Register 0 SET</description>
+ <addressOffset>0x40</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>DSP</name>
+ <description>Fusion_ DSP reset set</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DSP_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DSP_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>AXI_SWITCH</name>
+ <description>AXI SWITCH reset set</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>AXI_SWITCH_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>AXI_SWITCH_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>POWERQUAD</name>
+ <description>POWERQUAD reset set</description>
+ <bitOffset>8</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>POWERQUAD_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>POWERQUAD_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>CASPER</name>
+ <description>CASPER reset set</description>
+ <bitOffset>9</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>CASPER_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>CASPER_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>HASHCRYPT</name>
+ <description>HASHCRYPT reset set</description>
+ <bitOffset>10</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>HASHCRYPT_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>HASHCRYPT_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>PUF</name>
+ <description>PUF reset set</description>
+ <bitOffset>11</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>PUF_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>PUF_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>RNG</name>
+ <description>RNG reset set</description>
+ <bitOffset>12</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>RNG_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>RNG_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI0_OTFAD</name>
+ <description>FLEXSPI0 and OTFAD reset set</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI1</name>
+ <description>FLEXSPI1 reset set</description>
+ <bitOffset>18</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI1_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI1_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_PHY</name>
+ <description>USB PHY reset set</description>
+ <bitOffset>20</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_PHY_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_PHY_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_DEVICE</name>
+ <description>USB Device reset set</description>
+ <bitOffset>21</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_HOST</name>
+ <description>USB HOST reset set</description>
+ <bitOffset>22</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_HOST_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_HOST_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_SRAM</name>
+ <description>USBHS SRAM reset set</description>
+ <bitOffset>23</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_SRAM_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_SRAM_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SCT</name>
+ <description>SCTimer reset set</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SCT_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SCT_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>GPU</name>
+ <description>GPU reset set</description>
+ <bitOffset>26</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>GPU_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>GPU_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>DISPLAY_CONTROLLER</name>
+ <description>LCDIF DISPLAY CONTROLLER reset set</description>
+ <bitOffset>27</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_CONTROLLER</name>
+ <description>MIPI DSI controller reset set</description>
+ <bitOffset>28</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_PHY</name>
+ <description>MIPI DSI PHY reset set</description>
+ <bitOffset>29</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SMARTDMA</name>
+ <description>SMARTDMA Event/Algorithm handler reset set</description>
+ <bitOffset>30</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SMARTDMA_CLR</name>
+ <description>No Effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SMARTDMA_SET</name>
+ <description>Sets the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL1_SET</name>
+ <description>Peripheral Reset Control Register 1 SET</description>
+ <addressOffset>0x44</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>SDIO0</name>
+ <description>SDIO0 reset set</description>
+ <bitOffset>2</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO0_SET</name>
+ <description>Sets the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SDIO1</name>
+ <description>SDIO1 reset set</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO1_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO1_SET</name>
+ <description>Sets the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ACMP0</name>
+ <description>ACMP0 reset set</description>
+ <bitOffset>15</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ACMP0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ACMP0_SET</name>
+ <description>Sets the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ADC0</name>
+ <description>ADC0 reset set</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ADC0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ADC0_SET</name>
+ <description>Sets the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SHSGPIO0</name>
+ <description>SHSGPIO0 reset set</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SHSGPIO0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SHSGPIO0_SET</name>
+ <description>Sets the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL2_SET</name>
+ <description>Peripheral Reset Control Register 2 SET</description>
+ <addressOffset>0x48</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>UTICK0</name>
+ <description>Micro-tick timer 0 reset set</description>
+ <bitOffset>0</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>UTICK0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>UTICK0_SET</name>
+ <description>Sets the PRSTCTL2 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>WWDT0</name>
+ <description>WWDT0 reset set</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>WWDT0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>WWDT0_SET</name>
+ <description>Sets the PRSTCTL2 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL0_CLR</name>
+ <description>Peripheral Reset Control Register 0 CLR</description>
+ <addressOffset>0x70</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>DSP</name>
+ <description>Fusion_ F1 DSP reset clear</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DSP_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DSP_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>AXI_SWITCH</name>
+ <description>AXI SWITCH reset clear</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>AXI_SWITCH_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>AXI_SWITCH_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>POWERQUAD</name>
+ <description>POWERQUAD reset clear</description>
+ <bitOffset>8</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>POWERQUAD_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>POWERQUAD_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>CASPER</name>
+ <description>CASPER reset clear</description>
+ <bitOffset>9</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>CASPER_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>CASPER_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>HASHCRYPT</name>
+ <description>HASHCRYPT reset clear</description>
+ <bitOffset>10</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>HASHCRYPT_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>HASHCRYPT_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>PUF</name>
+ <description>PUF reset clear</description>
+ <bitOffset>11</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>PUF_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>PUF_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>RNG</name>
+ <description>RNG reset clear</description>
+ <bitOffset>12</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>RNG_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>RNG_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI0_OTFAD</name>
+ <description>FLEXSPI0 and OTFAD reset clear</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI0_OTFAD_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>FLEXSPI1</name>
+ <description>FLEXSPI1 reset clear</description>
+ <bitOffset>18</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>FLEXSPI1_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>FLEXSPI1_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_PHY</name>
+ <description>USB PHY reset clear</description>
+ <bitOffset>20</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_PHY_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_PHY_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_DEVICE</name>
+ <description>USB DEVICE reset clear</description>
+ <bitOffset>21</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_DEVICE_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_HOST</name>
+ <description>USB HOST reset clear</description>
+ <bitOffset>22</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_HOST_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_HOST_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>USBHS_SRAM</name>
+ <description>USBHS SRAM reset clear</description>
+ <bitOffset>23</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>USBHS_SRAM_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>USBHS_SRAM_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SCT</name>
+ <description>SCT reset clear</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SCT_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SCT_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>GPU</name>
+ <description>GPU reset clear</description>
+ <bitOffset>26</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>GPU_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>GPU_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>DISPLAY_CONTROLLER</name>
+ <description>LCDIF DISPLAY CONTROLLER reset clear</description>
+ <bitOffset>27</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>DISPLAY_CONTROLLER_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_CONTROLLER</name>
+ <description>MIPI DSI controller reset clear</description>
+ <bitOffset>28</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_CONTROLLER_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>MIPI_DSI_PHY</name>
+ <description>MIPI DSI PHY reset clear</description>
+ <bitOffset>29</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>MIPI_DSI_PHY_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SMARTDMA</name>
+ <description>SMARTDMA Event/Algorithm handler reset clear</description>
+ <bitOffset>30</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SMARTDMA_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SMARTDMA_SET</name>
+ <description>Clears the PRSTCTL0 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL1_CLR</name>
+ <description>Peripheral Reset Control Register 1 CLR</description>
+ <addressOffset>0x74</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>SDIO0</name>
+ <description>SDIO0 reset clear</description>
+ <bitOffset>2</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO0_SET</name>
+ <description>Clears the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SDIO1</name>
+ <description>SDIO1 reset clear</description>
+ <bitOffset>3</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SDIO1_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SDIO1_SET</name>
+ <description>Clears the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ACMP0</name>
+ <description>ACMP0 reset clear</description>
+ <bitOffset>15</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ACMP0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ACMP0_SET</name>
+ <description>Clears the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>ADC0</name>
+ <description>ADC0 reset clear</description>
+ <bitOffset>16</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>ADC0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>ADC0_SET</name>
+ <description>Clears the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>SHSGPIO0</name>
+ <description>Secure HSGPIO0 reset clear</description>
+ <bitOffset>24</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>SHSGPIO0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>SHSGPIO0_SET</name>
+ <description>Clears the PRSTCTL1 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ <register>
+ <name>PRSTCTL2_CLR</name>
+ <description>Peripheral Reset Control Register 2 CLR</description>
+ <addressOffset>0x78</addressOffset>
+ <size>32</size>
+ <access>write-only</access>
+ <resetValue>0</resetValue>
+ <resetMask>0</resetMask>
+ <fields>
+ <field>
+ <name>UTICK0</name>
+ <description>Micro-tick timer 0 reset clear</description>
+ <bitOffset>0</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>UTICK0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>UTICK0_SET</name>
+ <description>Clears the PRSTCTL2 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ <field>
+ <name>WWDT0</name>
+ <description>WWDT0 reset clear</description>
+ <bitOffset>1</bitOffset>
+ <bitWidth>1</bitWidth>
+ <access>write-only</access>
+ <enumeratedValues>
+ <enumeratedValue>
+ <name>WWDT0_CLR</name>
+ <description>No effect</description>
+ <value>0</value>
+ </enumeratedValue>
+ <enumeratedValue>
+ <name>WWDT0_SET</name>
+ <description>Clears the PRSTCTL2 Bit</description>
+ <value>0x1</value>
+ </enumeratedValue>
+ </enumeratedValues>
+ </field>
+ </fields>
+ </register>
+ </registers>
+ </peripheral>
+ </peripherals>
+</device>
\ No newline at end of file
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 05/23] hw: add register access utility functions
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (3 preceding siblings ...)
2024-08-05 20:16 ` [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595 Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-12 15:32 ` Peter Maydell
2024-08-05 20:17 ` [RFC PATCH 06/23] hw/misc: add basic flexcomm device model Octavian Purdila
` (18 subsequent siblings)
23 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add register access utility functions for device models, like checking
aligned access and reading and writing to a register backstore.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
include/hw/regs.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 include/hw/regs.h
diff --git a/include/hw/regs.h b/include/hw/regs.h
new file mode 100644
index 0000000000..8d0da0629d
--- /dev/null
+++ b/include/hw/regs.h
@@ -0,0 +1,89 @@
+/*
+ * Useful macros/functions for register handling.
+ *
+ * Copyright (c) 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_REGS_H
+#define HW_REGS_H
+
+#include "exec/hwaddr.h"
+#include "exec/memattrs.h"
+
+#define BITS(hi, lo) (BIT(hi + 1) - BIT(lo))
+#define BIT_IS_SET(v, b) (((v) & BIT(b)) != 0)
+
+/*
+ * reg32_aligned_access
+ * @addr: address to check
+ * @size: size of access
+ *
+ * Check if access to a hardware address is 32bit aligned.
+ *
+ * Returns: true if access is 32bit aligned, false otherwise
+ */
+static inline bool reg32_aligned_access(hwaddr addr, unsigned size)
+{
+ if (size != 4 || addr % 4 != 0) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * reg32_write
+ * @base: base address
+ * @addr: register offset in bytes
+ * @val: value to write
+ * @wr_bits_array: RW bitmask array
+ *
+ * Update the RW/WO bits of a 32bit register backstore with a given value
+ * (discarding updats to the RO bits). The RW/WO bits are encoded in the
+ * @wr_bits_array with bits set being RW and bits unset being RO.
+ *
+ * Usage example:
+ *
+ * wr_bits_array[] = {
+ * [REG1_ADDR/4] = 0xFF000000, // MSB byte writable
+ * [REG2_ADDR/4] = 0xFF, // LSB byte writable
+ * // all other registers are read-only
+ * };
+ *
+ * // backstore is updated to 0x12000000
+ * reg32_write(&backstore, REG1_ADDR, 0x12345678, wr_bits_array);
+ * // backstore is updated to 0x78
+ * reg32_write(&backstore, REG2_ADDR, 0x12345678, wr_bits_array);
+ */
+static inline uint32_t reg32_write(void *base, uint32_t off, uint32_t val,
+ const uint32_t *rw_bits_array)
+{
+ uint32_t *ptr = base + addr;
+ uint32_t old_value = *ptr;
+ uint32_t mask = rw_bits_array ? rw_bits_array[addr / 4] : 0xFFFFFFFF;
+
+ /* set WO/RW bits */
+ *ptr |= val & mask;
+ /* clear RO bits */
+ *ptr &= val | ~mask;
+
+ return old_value;
+}
+
+/*
+ * reg32_read
+ * @base: base address
+ * @addr: register offset in bytes
+ *
+ * Returns: 32bit value from register backstore
+ */
+static inline uint32_t reg32_read(void *base, uint32_t addr)
+{
+ return *(uint32_t *)(base + addr);
+}
+
+#endif /* HW_REGS_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 06/23] hw/misc: add basic flexcomm device model
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (4 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 05/23] hw: add register access utility functions Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 07/23] tests/unit: add system bus mock Octavian Purdila
` (17 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add support for NXP's FLEXCOMM device model. It uses the NXP RT595 SVD
file to generate the register structure.
FLEXCOMM is a generic serial communication module which support
multiple functions: UART, SPI and I2C. These are configurable at runtime.
This patch adds the infrastructure to support adding the above
mentioned hardware functions in a modular fashion in subsequent
patches as well as switching between functions.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/meson.build | 2 +
hw/arm/svd/meson.build | 4 +
hw/misc/Kconfig | 3 +
hw/misc/flexcomm.c | 283 +++++++++++++++++++++++++++++++++++++
hw/misc/meson.build | 2 +
hw/misc/trace-events | 6 +
include/hw/misc/flexcomm.h | 74 ++++++++++
7 files changed, 374 insertions(+)
create mode 100644 hw/arm/svd/meson.build
create mode 100644 hw/misc/flexcomm.c
create mode 100644 include/hw/misc/flexcomm.h
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index aefde0c69a..eb604d00cf 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -78,3 +78,5 @@ system_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
system_ss.add(when: 'CONFIG_Z2', if_true: files('z2.c'))
hw_arch += {'arm': arm_ss}
+
+subdir('svd')
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
new file mode 100644
index 0000000000..9ce6c1d838
--- /dev/null
+++ b/hw/arm/svd/meson.build
@@ -0,0 +1,4 @@
+genh += custom_target('flexcomm.h',
+ output: 'flexcomm.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'FLEXCOMM0', '-t', 'FLEXCOMM'])
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1e08785b83..14167ae9e8 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -213,4 +213,7 @@ config IOSB
config XLNX_VERSAL_TRNG
bool
+config FLEXCOMM
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/flexcomm.c b/hw/misc/flexcomm.c
new file mode 100644
index 0000000000..6ec3773910
--- /dev/null
+++ b/hw/misc/flexcomm.c
@@ -0,0 +1,283 @@
+/*
+ * QEMU model for NXP's FLEXCOMM
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/mmap-alloc.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/regs.h"
+#include "hw/misc/flexcomm.h"
+
+#define reg(field) offsetof(FLEXCOMM_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(FLEXCOMM_Type) / sizeof(uint32_t))
+
+static FLEXCOMM_REGISTER_NAMES_ARRAY(reg_names);
+
+#define modname "flexcomm: "
+
+static const FlexcommFunctionOps *flexcomm_fops[FLEXCOMM_FUNCTIONS];
+static void *flexcomm_farg[FLEXCOMM_FUNCTIONS];
+
+static inline bool has_function(FlexcommState *s, int function)
+{
+ return s->functions & (1 << function);
+}
+
+static inline int persel_to_function(FlexcommState *s)
+{
+ switch (s->regs.flex.PSELID_b.PERSEL) {
+ case FLEXCOMM_PERSEL_USART:
+ return FLEXCOMM_FUNC_USART;
+ case FLEXCOMM_PERSEL_SPI:
+ return FLEXCOMM_FUNC_SPI;
+ case FLEXCOMM_PERSEL_I2C:
+ return FLEXCOMM_FUNC_I2C;
+ case FLEXCOMM_PERSEL_I2S_TX:
+ case FLEXCOMM_PERSEL_I2S_RX:
+ return FLEXCOMM_FUNC_I2S;
+ default:
+ return -1;
+ }
+}
+
+static void flexcomm_func_select(FlexcommState *s, bool selected)
+{
+ int f = persel_to_function(s);
+
+ if (f < 0 || !flexcomm_fops[f] || !flexcomm_fops[f]->select) {
+ return;
+ }
+
+ flexcomm_fops[f]->select(flexcomm_farg[f], s, f, selected);
+}
+
+static MemTxResult flexcomm_func_reg_read(FlexcommState *s, hwaddr addr,
+ uint64_t *data, unsigned size)
+{
+ int f = persel_to_function(s);
+
+ if (f < 0 || !flexcomm_fops[f] || !flexcomm_fops[f]->reg_read) {
+ return MEMTX_ERROR;
+ }
+
+ return flexcomm_fops[f]->reg_read(flexcomm_farg[f], s, f, addr, data, size);
+}
+
+static MemTxResult flexcomm_func_reg_write(FlexcommState *s, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ int f = persel_to_function(s);
+
+ if (f < 0 || !flexcomm_fops[f] || !flexcomm_fops[f]->reg_write) {
+ return MEMTX_ERROR;
+ }
+
+ return flexcomm_fops[f]->reg_write(flexcomm_farg[f], s, f, addr, data,
+ size);
+}
+
+static void flexcomm_reset(DeviceState *dev)
+{
+ FlexcommState *s = FLEXCOMM(dev);
+
+ trace_flexcomm_reset();
+
+ flexcomm_func_select(s, false);
+
+ flexcomm_reset_registers(&s->regs.flex);
+
+ s->regs.flex.PSELID_b.USARTPRESENT = has_function(s, FLEXCOMM_FUNC_USART);
+ s->regs.flex.PSELID_b.SPIPRESENT = has_function(s, FLEXCOMM_FUNC_SPI);
+ s->regs.flex.PSELID_b.I2CPRESENT = has_function(s, FLEXCOMM_FUNC_I2C);
+ s->regs.flex.PSELID_b.I2SPRESENT = has_function(s, FLEXCOMM_FUNC_I2S);
+
+ s->irq_state = false;
+ qemu_set_irq(s->irq, s->irq_state);
+}
+
+static MemTxResult flexcomm_reg_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ FlexcommState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ switch (addr) {
+ case reg(PSELID):
+ case reg(PID):
+ {
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ } else {
+ *data = reg32_read(&s->regs, addr);
+ }
+ break;
+ }
+ default:
+ return flexcomm_func_reg_read(s, addr, data, size);
+ }
+
+ trace_flexcomm_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static int flexcomm_check_function(FlexcommState *s)
+{
+ int f = persel_to_function(s);
+
+ if (f < 0 || !has_function(s, f)) {
+ return -1;
+ }
+
+ return f;
+}
+
+static MemTxResult flexcomm_reg_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ FlexcommState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t mask[] = {
+ [regi(PSELID)] = BITS(3, 0),
+ };
+
+ trace_flexcomm_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+ switch (addr) {
+ case reg(PID):
+ /* RO register, nothing do to */
+ break;
+ case reg(PSELID):
+ {
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ break;
+ }
+
+ if (s->regs.flex.PSELID_b.LOCK) {
+ break;
+ }
+
+ flexcomm_func_select(s, false);
+
+ reg32_write(&s->regs, addr, value, mask);
+
+ if (flexcomm_check_function(s) < 0) {
+ s->regs.flex.PSELID_b.PERSEL = 0;
+ }
+ s->regs.flex.PID_b.ID = s->regs.flex.PSELID_b.PERSEL;
+
+ flexcomm_func_select(s, true);
+ break;
+ }
+ default:
+ return flexcomm_func_reg_write(s, addr, value, size);
+ }
+
+ return ret;
+}
+
+
+static const MemoryRegionOps flexcomm_ops = {
+ .read_with_attrs = flexcomm_reg_read,
+ .write_with_attrs = flexcomm_reg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static Property flexcomm_properties[] = {
+ DEFINE_PROP_UINT32("functions", FlexcommState, functions,
+ FLEXCOMM_FULL),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void flexcomm_init(Object *obj)
+{
+ FlexcommState *s = FLEXCOMM(obj);
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->mmio, obj, &flexcomm_ops, s,
+ TYPE_FLEXCOMM, sizeof(FLEXCOMM_Type));
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void flexcomm_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void flexcomm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = flexcomm_reset;
+ device_class_set_props(dc, flexcomm_properties);
+ dc->realize = flexcomm_realize;
+}
+
+static const TypeInfo flexcomm_info = {
+ .name = TYPE_FLEXCOMM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(FlexcommState),
+ .instance_init = flexcomm_init,
+ .class_init = flexcomm_class_init,
+};
+
+static void flexcomm_register_types(void)
+{
+ type_register_static(&flexcomm_info);
+}
+
+type_init(flexcomm_register_types)
+
+void flexcomm_irq(FlexcommState *s, bool irq)
+{
+ if (s->irq_state != irq) {
+ trace_flexcomm_irq(DEVICE(s)->id, irq);
+ qemu_set_irq(s->irq, irq);
+ s->irq_state = irq;
+ }
+}
+
+bool flexcomm_register_ops(int f, void *arg, const FlexcommFunctionOps *ops,
+ Error **errp)
+{
+ if (f < 0 || f >= FLEXCOMM_FUNCTIONS) {
+ error_setg(errp, modname "invalid function %d", f);
+ return false;
+ }
+
+ if (flexcomm_fops[f]) {
+ error_setg(errp, modname "function %d already registered", f);
+ return false;
+ }
+
+ flexcomm_fops[f] = ops;
+ flexcomm_farg[f] = arg;
+
+ return true;
+}
+
+/* for unit tests */
+void flexcomm_unregister_ops(int f)
+{
+ flexcomm_fops[f] = NULL;
+ flexcomm_farg[f] = NULL;
+}
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 86596a3888..8414767ae3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -156,3 +156,5 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c'))
# HPPA devices
system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))
+
+system_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 5d241cb40a..71ec77de29 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -351,3 +351,9 @@ djmemc_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRI
# iosb.c
iosb_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
iosb_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
+
+# flexcomm
+flexcomm_reset(void) ""
+flexcomm_irq(const char *id, uint8_t irq) "%s %d"
+flexcomm_reg_read(const char *devname, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] -> 0x%08x"
+flexcomm_reg_write(const char *dename, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
diff --git a/include/hw/misc/flexcomm.h b/include/hw/misc/flexcomm.h
new file mode 100644
index 0000000000..422452bd96
--- /dev/null
+++ b/include/hw/misc/flexcomm.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU model for NXP's FLEXCOMM
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_FLEXCOMM_H
+#define HW_FLEXCOMM_H
+
+#include "hw/sysbus.h"
+#include "hw/arm/svd/flexcomm.h"
+#include "qemu/fifo32.h"
+
+#define TYPE_FLEXCOMM "flexcomm"
+#define FLEXCOMM(obj) OBJECT_CHECK(FlexcommState, (obj), TYPE_FLEXCOMM)
+
+#define FLEXCOMM_FUNC_USART 0
+#define FLEXCOMM_FUNC_SPI 1
+#define FLEXCOMM_FUNC_I2C 2
+#define FLEXCOMM_FUNC_I2S 3
+#define FLEXCOMM_FUNCTIONS 4
+
+#define FLEXCOMM_FULL 0xF
+#define FLEXCOMM_HSSPI (1 << FLEXCOMM_FUNC_SPI)
+#define FLEXCOMM_PMICI2C (1 << FLEXCOMM_FUNC_I2C)
+
+#define FLEXCOMM_PERSEL_USART 1
+#define FLEXCOMM_PERSEL_SPI 2
+#define FLEXCOMM_PERSEL_I2C 3
+#define FLEXCOMM_PERSEL_I2S_TX 4
+#define FLEXCOMM_PERSEL_I2S_RX 5
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ union {
+ FLEXCOMM_Type flex;
+ } regs;
+ uint32_t functions;
+ qemu_irq irq;
+ bool irq_state;
+} FlexcommState;
+
+typedef struct {
+ /* argument to pass to functions */
+ void *arg;
+
+ /* function / submodule has been selected / deselected. */
+ void (*select)(void *o, FlexcommState *s, int f, bool selected);
+
+ /* read register */
+ MemTxResult (*reg_read)(void *o, FlexcommState *s, int f, hwaddr addr,
+ uint64_t *data, unsigned size);
+
+ /* write register */
+ MemTxResult (*reg_write)(void *o, FlexcommState *s, int f, hwaddr addr,
+ uint64_t data, unsigned size);
+} FlexcommFunctionOps;
+
+
+void flexcomm_irq(FlexcommState *s, bool irq);
+bool flexcomm_register_ops(int f, void *arg, const FlexcommFunctionOps *ops,
+ Error **errp);
+void flexcomm_unregister_ops(int f);
+
+#endif /* HW_FLEXCOMM_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 07/23] tests/unit: add system bus mock
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (5 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 06/23] hw/misc: add basic flexcomm device model Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 08/23] test/unit: add register access macros and functions Octavian Purdila
` (16 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
From: Valentin Ghita <valentinghita@google.com>
From: Valentin Ghita <valentinghita@google.com>
Add a system bus mock and the necessary memory access functions to be
able to create unit tests for device models.
Signed-off-by: Valentin Ghita <valentinghita@google.com>
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/sysbus-mock.c | 314 +++++++++++++++++++++++++++++++++++++++
tests/unit/sysbus-mock.h | 82 ++++++++++
2 files changed, 396 insertions(+)
create mode 100644 tests/unit/sysbus-mock.c
create mode 100644 tests/unit/sysbus-mock.h
diff --git a/tests/unit/sysbus-mock.c b/tests/unit/sysbus-mock.c
new file mode 100644
index 0000000000..c6c654eabc
--- /dev/null
+++ b/tests/unit/sysbus-mock.c
@@ -0,0 +1,314 @@
+/*
+ * System Bus Mock
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-core.h"
+
+#include "sysbus-mock.h"
+
+AddressSpace address_space_memory;
+
+/* Simulates guest memory space. */
+static uint8_t *guest_mem;
+static size_t guest_mem_size;
+
+static uint64_t memory_region_ram_device_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ uint64_t data = (uint64_t)~0;
+ uint8_t *buf = opaque;
+
+ switch (size) {
+ case 1:
+ data = *(uint8_t *)(buf + addr);
+ break;
+ case 2:
+ data = *(uint16_t *)(buf + addr);
+ break;
+ case 4:
+ data = *(uint32_t *)(buf + addr);
+ break;
+ case 8:
+ data = *(uint64_t *)(buf + addr);
+ break;
+ }
+
+ return data;
+}
+
+static void memory_region_ram_device_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ uint8_t *buf = opaque;
+
+ switch (size) {
+ case 1:
+ *(uint8_t *)(buf + addr) = (uint8_t)data;
+ break;
+ case 2:
+ *(uint16_t *)(buf + addr) = (uint16_t)data;
+ break;
+ case 4:
+ *(uint32_t *)(buf + addr) = (uint32_t)data;
+ break;
+ case 8:
+ *(uint64_t *)(buf + addr) = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps ram_device_mem_ops = {
+ .read = memory_region_ram_device_read,
+ .write = memory_region_ram_device_write,
+ .endianness = DEVICE_HOST_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ .unaligned = true,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ .unaligned = true,
+ },
+};
+
+void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, bool is_write)
+{
+ /* Mock implementation. Return a pointer inside the guest_mem buffer. */
+ g_assert(guest_mem != NULL);
+ g_assert(guest_mem_size <= addr + (size_t)plen);
+
+ return guest_mem + addr;
+}
+
+void cpu_physical_memory_unmap(void *buffer, hwaddr len,
+ bool is_write, hwaddr access_len)
+{
+ /* Mock implementation. */
+}
+
+MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
+ MemTxAttrs attrs, void *buf, hwaddr len)
+{
+ /* Mock implementation */
+ g_assert(guest_mem != NULL);
+
+ if (guest_mem_size < addr + (size_t)len) {
+ return MEMTX_ERROR;
+ }
+
+ memcpy(buf, guest_mem + addr, len);
+
+ return MEMTX_OK;
+}
+
+MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
+ MemTxAttrs attrs,
+ const void *buf, hwaddr len)
+{
+ /* Mock implementation */
+ g_assert(guest_mem != NULL);
+
+ if (guest_mem_size < addr + (size_t)len) {
+ return MEMTX_ERROR;
+ }
+
+ memcpy(guest_mem + addr, buf, len);
+
+ return MEMTX_OK;
+}
+
+MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
+ void *buf, hwaddr len, bool is_write)
+{
+ if (is_write) {
+ return address_space_write(as, addr, attrs, buf, len);
+ } else {
+ return address_space_read_full(as, addr, attrs, buf, len);
+ }
+}
+
+void cpu_physical_memory_rw(hwaddr addr, void *buf,
+ hwaddr len, bool is_write)
+{
+ address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED,
+ buf, len, is_write);
+}
+
+void memory_region_init_io(MemoryRegion *mr, Object *owner,
+ const MemoryRegionOps *ops, void *opaque,
+ const char *name, uint64_t size)
+{
+ /* Mock implementation. */
+ mr->size = size;
+ mr->ops = ops;
+ mr->opaque = opaque;
+}
+
+void memory_region_init_ram_device_ptr(MemoryRegion *mr, Object *owner,
+ const char *name, uint64_t size,
+ void *ptr)
+{
+ mr->size = size;
+ mr->ops = &ram_device_mem_ops;
+ mr->opaque = ptr;
+}
+
+void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
+{
+ if (mr->readonly != readonly) {
+ mr->readonly = readonly;
+ }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+{
+ assert(n >= 0 && n < dev->num_mmio);
+ dev->mmio[n].addr = addr;
+ dev->mmio[n].memory->addr = addr;
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory)
+{
+ /* Mock implementation. */
+ assert(dev->num_mmio < QDEV_MAX_MMIO);
+ int n = dev->num_mmio++;
+ dev->mmio[n].addr = -1;
+ dev->mmio[n].memory = memory;
+}
+
+static void sysbus_device_class_init(ObjectClass *klass, void *data)
+{
+ /* Mock implementation. */
+}
+
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+ qdev_init_gpio_out_named(DEVICE(dev), p, SYSBUS_DEVICE_GPIO_IRQ, 1);
+}
+
+/*
+ * Mock implementation of the sysbus device class.
+ * Including the sysbus source code is difficult because of the dependencies,
+ * so it is easier to define the type here.
+ */
+static const TypeInfo sysbus_device_type_info = {
+ .name = TYPE_SYS_BUS_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(SysBusDevice),
+ .abstract = true,
+ .class_size = sizeof(SysBusDeviceClass),
+ .class_init = sysbus_device_class_init,
+};
+
+void sysbus_mock_init(void)
+{
+ type_register_static(&sysbus_device_type_info);
+}
+
+/* Find the mmio region containing an address. */
+static MemoryRegion *find_region(SysBusDevice *dev, hwaddr addr)
+{
+ int i;
+
+ for (i = 0; i < dev->num_mmio; i++) {
+ if (dev->mmio[i].addr <= addr &&
+ (addr - dev->mmio[i].addr) < dev->mmio[i].memory->size) {
+
+ return dev->mmio[i].memory;
+ }
+ }
+
+ return NULL;
+}
+
+uint32_t sysbus_mmio_read_addr(DeviceState *dev, hwaddr addr, unsigned size)
+{
+ uint64_t value;
+ MemTxResult result;
+ MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+ assert(mem != NULL);
+ assert(mem->ops->read_with_attrs != NULL || mem->ops->read != NULL);
+
+ if (mem->ops->read_with_attrs != NULL) {
+ result = mem->ops->read_with_attrs(mem->opaque, addr - mem->addr,
+ &value, size,
+ MEMTXATTRS_UNSPECIFIED);
+ assert(result == MEMTX_OK);
+ } else {
+ value = mem->ops->read(mem->opaque, addr - mem->addr, size);
+ }
+
+ return (uint32_t)value;
+}
+
+void sysbus_mmio_write_addr(DeviceState *dev, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MemTxResult result;
+ MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+ assert(mem != NULL);
+ assert(mem->ops->write_with_attrs != NULL || mem->ops->write != NULL);
+ assert(!mem->readonly);
+
+ if (mem->ops->write_with_attrs != NULL) {
+ result = mem->ops->write_with_attrs(mem->opaque, addr - mem->addr,
+ value, size,
+ MEMTXATTRS_UNSPECIFIED);
+ g_assert(result == MEMTX_OK);
+ } else {
+ mem->ops->write(mem->opaque, addr - mem->addr, value, size);
+ }
+}
+
+void sysbus_dev_set_guest_mem(void *mem, size_t size)
+{
+ guest_mem = mem;
+ guest_mem_size = size;
+}
+
+MemTxResult sysbus_mmio_read_addr_raw(DeviceState *dev, hwaddr addr,
+ uint64_t *value, unsigned size)
+{
+ uint64_t tmp;
+ MemTxResult result;
+ MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+
+ assert(mem != NULL);
+
+ result = mem->ops->read_with_attrs(dev, addr - mem->addr, &tmp,
+ size,
+ MEMTXATTRS_UNSPECIFIED);
+ *value = tmp;
+ return result;
+}
+
+MemTxResult sysbus_mmio_write_addr_raw(DeviceState *dev, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ MemoryRegion *mem = find_region(SYS_BUS_DEVICE(dev), addr);
+ assert(mem != NULL);
+ assert(!mem->readonly);
+
+ return mem->ops->write_with_attrs(dev, addr - mem->addr, value,
+ size,
+ MEMTXATTRS_UNSPECIFIED);
+}
diff --git a/tests/unit/sysbus-mock.h b/tests/unit/sysbus-mock.h
new file mode 100644
index 0000000000..7a4c2e7b9a
--- /dev/null
+++ b/tests/unit/sysbus-mock.h
@@ -0,0 +1,82 @@
+/*
+ * System Bus Mock
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef SYSBUS_MOCK_H
+#define SYSBUS_MOCK_H
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/sysbus.h"
+
+/*
+ * sysbus_mock_init
+ *
+ * Initialize the sysbus mock implementation.
+ */
+void sysbus_mock_init(void);
+
+/*
+ * sysbus_mmio_read_addr
+ * @dev: device structure
+ * @addr: address to read from
+ *
+ * Read from an address in a mmio region and assert on errors.
+ */
+uint32_t sysbus_mmio_read_addr(DeviceState *dev, hwaddr addr, unsigned size);
+
+/*
+ * sysbus_mmio_write_addr
+ * @dev: device structure
+ * @addr: address to write to
+ * @value: value to write
+ *
+ * Write to an address in a mmio region and assert on errors.
+ */
+void sysbus_mmio_write_addr(DeviceState *dev, hwaddr addr, uint64_t value,
+ unsigned size);
+
+/*
+ * sysbus_dev_set_guest_mem
+ *
+ * Set guest generic memory space.
+ */
+void sysbus_dev_set_guest_mem(void *mem, size_t size);
+
+
+/*
+ * sysbus_mmio_read_addr_raw
+ * @dev: device structure
+ * @addr: address to write to
+ * @size: access size
+ *
+ * Read from an address in a mmio region and return errors.
+ *
+ * Returns: MEMTX_OK if the access was successful, MEMTX_ERROR otherwise
+ */
+MemTxResult sysbus_mmio_read_addr_raw(DeviceState *dev, hwaddr addr,
+ uint64_t *value, unsigned size);
+
+/*
+ * sysbus_mmio_write_addr_raw
+ * @dev: device structure
+ * @addr: address to write to
+ * @value: value to write
+ * @size: access size
+ *
+ * Write to an address in a mmio region and return errors.
+ *
+ * Returns: MEMTX_OK if the access was successful, MEMTX_ERROR otherwise
+ */
+MemTxResult sysbus_mmio_write_addr_raw(DeviceState *dev, hwaddr addr,
+ uint64_t value, unsigned size);
+
+#endif /* SYSBUS_MOCK_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 08/23] test/unit: add register access macros and functions
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (6 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 07/23] tests/unit: add system bus mock Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 09/23] test/unit: add flexcomm unit test Octavian Purdila
` (15 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add register access macros for devices models that use SVD generated
registers. This allows accessing register or register bit fields in
unit tests, e.g.:
REG32_WRITE(f->dev, FLEXCOMM, PSELID, persel);
g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL) == persel);
Also add support for accessing 32bit registers with memory transaction
state, e.g.:
/* no register access until a function is selected */
g_assert(reg32_addr_read_raw(f->dev, FLEXCOMM_BASE, &tmp, 4) == MEMTX_ERROR);
Signed-off-by: Octavian Purdila <tavip@google.com>
---
include/hw/regs.h | 2 +-
tests/unit/reg-utils.h | 103 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 1 deletion(-)
create mode 100644 tests/unit/reg-utils.h
diff --git a/include/hw/regs.h b/include/hw/regs.h
index 8d0da0629d..fd6576ba2b 100644
--- a/include/hw/regs.h
+++ b/include/hw/regs.h
@@ -59,7 +59,7 @@ static inline bool reg32_aligned_access(hwaddr addr, unsigned size)
* // backstore is updated to 0x78
* reg32_write(&backstore, REG2_ADDR, 0x12345678, wr_bits_array);
*/
-static inline uint32_t reg32_write(void *base, uint32_t off, uint32_t val,
+static inline uint32_t reg32_write(void *base, uint32_t addr, uint32_t val,
const uint32_t *rw_bits_array)
{
uint32_t *ptr = base + addr;
diff --git a/tests/unit/reg-utils.h b/tests/unit/reg-utils.h
new file mode 100644
index 0000000000..f18ee07d20
--- /dev/null
+++ b/tests/unit/reg-utils.h
@@ -0,0 +1,103 @@
+/*
+ * Register access utilities for peripheral device tests.
+ *
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef _REG_UTILS_H
+#define _REG_UTILS_H
+
+#ifdef DEBUG_REG
+#define debug(fmt, args...) fprintf(stderr, fmt, ## args)
+#else
+#define debug(fmt, args...)
+#endif
+
+#define _REG_OFF(mod, field) (offsetof(mod##_Type, field))
+
+#define REG32_READ(dev, mod, reg) \
+ ({ \
+ uint32_t value; \
+ value = sysbus_mmio_read_addr(dev, mod##_BASE + _REG_OFF(mod, reg), \
+ sizeof(uint32_t)); \
+ debug("[%s] -> %08x\n", #reg, value); \
+ value; \
+ })
+
+#define REG32_WRITE(dev, mod, reg, value) \
+ do { \
+ debug("[%s] <- %08x\n", #reg, value); \
+ sysbus_mmio_write_addr(dev, mod##_BASE + _REG_OFF(mod, reg), value, \
+ sizeof(uint32_t)); \
+ } while (0)
+
+#define REG_FIELD_VAL(v, mod, reg, field) \
+ ((v & mod##_##reg##_##field##_Msk) >> mod##_##reg##_##field##_Pos)
+
+#define REG32_READ_FIELD(dev, mod, reg, field) \
+ REG_FIELD_VAL(REG32_READ(dev, mod, reg), mod, reg, field)
+
+#define REG32_WRITE_FIELD(dev, mod, reg, field, val) \
+ do { \
+ uint32_t _tmp = REG32_READ(dev, mod, reg); \
+ \
+ _tmp &= ~mod##_##reg##_##field##_Msk; \
+ _tmp |= (val << mod##_##reg##_##field##_Pos) & \
+ mod##_##reg##_##field##_Msk; \
+ REG32_WRITE(dev, mod, reg, _tmp); \
+ } while (0)
+
+#define REG32_WRITE_FIELD_NOUPDATE(dev, mod, reg, field, val) \
+ do { \
+ uint32_t _tmp; \
+ \
+ _tmp = (val << mod##_##reg##_##field##_Pos) & \
+ mod##_##reg##_##field##_Msk; \
+ REG32_WRITE(dev, mod, reg, _tmp); \
+ } while (0)
+
+#define WAIT_REG32_FIELD(ms, dev, mod, reg, field, val) \
+ { \
+ int remaining = ms; \
+ \
+ while (remaining) { \
+ if (REG32_READ_FIELD(dev, mod, reg, field) == val) { \
+ break; \
+ } \
+ main_loop_wait(false); \
+ usleep(1000); \
+ remaining--; \
+ } \
+ \
+ g_assert(remaining); \
+ }
+
+static inline MemTxResult reg32_addr_read_raw(DeviceState *dev, uint32_t addr,
+ uint32_t *value, uint32_t size)
+{
+ MemTxResult res;
+ uint64_t tmp;
+
+ res = sysbus_mmio_read_addr_raw(dev, addr, &tmp, size);
+ if (res == MEMTX_OK) {
+ *value = tmp;
+ }
+ debug("%d: [%x] -[%d]-> %08x\n", res, addr, size, *value);
+ return res;
+}
+
+static inline MemTxResult reg32_addr_write_raw(DeviceState *dev, uint32_t addr,
+ uint32_t value, uint32_t size)
+{
+ MemTxResult res;
+
+ res = sysbus_mmio_write_addr_raw(dev, addr, value, size);
+ debug("%d: [%x] <-[%d]- %08x\n", res, addr, size, value);
+ return res;
+}
+
+#endif /* _REG_UTILS_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 09/23] test/unit: add flexcomm unit test
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (7 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 08/23] test/unit: add register access macros and functions Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 10/23] hw/char: add support for flexcomm usart Octavian Purdila
` (14 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add flexcomm function selection unit tests.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 8 +-
tests/unit/test-flexcomm.c | 215 +++++++++++++++++++++++++++++++++++++
2 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 tests/unit/test-flexcomm.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 397f2503f8..4ccb15404d 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -141,7 +141,13 @@ if have_system
'test-bufferiszero': [],
'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'],
'test-vmstate': [migration, io],
- 'test-yank': ['socket-helpers.c', qom, io, chardev]
+ 'test-yank': ['socket-helpers.c', qom, io, chardev],
+ 'test-flexcomm': [
+ hwcore,
+ meson.project_source_root() / 'hw/core/gpio.c',
+ meson.project_source_root() / 'tests/unit/sysbus-mock.c',
+ meson.project_source_root() / 'hw/misc/flexcomm.c',
+ ],
}
if config_host_data.get('CONFIG_INOTIFY1')
tests += {'test-util-filemonitor': []}
diff --git a/tests/unit/test-flexcomm.c b/tests/unit/test-flexcomm.c
new file mode 100644
index 0000000000..f4efe2ac52
--- /dev/null
+++ b/tests/unit/test-flexcomm.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+#include "hw/misc/flexcomm.h"
+#include "sysbus-mock.h"
+#include "reg-utils.h"
+
+#define MAX_MSG_STACK 2
+
+typedef struct {
+ DeviceState *dev;
+ char *msg[MAX_MSG_STACK];
+ int msg_count;
+} TestFixture;
+
+#define SELECT_MSG(f, selected) "f[%d]select(%d)", f, selected
+#define REG_READ_MSG(f, addr, size) "f[%d]reg_read(%x, %d)", f, \
+ (uint32_t)addr, size
+#define REG_WRITE_MSG(f, addr, data, size) "f[%d]reg_write(%x, %x, %d)", \
+ f, (uint32_t)addr, (uint32_t)data, size
+
+#define FLEXCOMM_BASE 0x40106000UL
+
+
+static void f_ops_select(void *opaque, FlexcommState *s, int f,
+ bool selected)
+{
+ TestFixture *tf = (TestFixture *)opaque;
+
+ tf->msg[tf->msg_count++] = g_strdup_printf(SELECT_MSG(f, selected));
+}
+
+static MemTxResult f_ops_reg_read(void *opaque, FlexcommState *s, int f,
+ hwaddr addr, uint64_t *data, unsigned size)
+{
+ TestFixture *tf = (TestFixture *)opaque;
+
+ tf->msg[tf->msg_count++] = g_strdup_printf(REG_READ_MSG(f, addr, size));
+ return MEMTX_OK;
+}
+
+static MemTxResult f_ops_reg_write(void *opaque, FlexcommState *s, int f,
+ hwaddr addr, uint64_t data, unsigned size)
+{
+ TestFixture *tf = (TestFixture *)opaque;
+
+ tf->msg[tf->msg_count++] = g_strdup_printf(REG_WRITE_MSG(f, addr, data,
+ size));
+ return MEMTX_OK;
+}
+
+static void assert_msg(TestFixture *f, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+static void assert_msg(TestFixture *f, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ va_start(ap, fmt);
+ msg = g_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ g_assert_cmpstr(msg, ==, f->msg[--f->msg_count]);
+}
+
+static const FlexcommFunctionOps f_ops = {
+ .select = f_ops_select,
+ .reg_read = f_ops_reg_read,
+ .reg_write = f_ops_reg_write,
+};
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ int i;
+
+ f->dev = qdev_new(TYPE_FLEXCOMM);
+ g_assert(f->dev);
+
+ if (data != NULL) {
+ qdev_prop_set_int32(DEVICE(f->dev), "functions", (uintptr_t)data);
+ }
+
+ qdev_realize_and_unref(f->dev, NULL, NULL);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->dev), 0, FLEXCOMM_BASE);
+
+ for (i = 0; i < FLEXCOMM_FUNCTIONS; i++) {
+ /* replace functions ops */
+ flexcomm_unregister_ops(i);
+ assert(flexcomm_register_ops(i, f, &f_ops, NULL));
+ assert(!flexcomm_register_ops(i, f, &f_ops, NULL));
+ }
+
+ device_cold_reset(f->dev);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ qdev_unrealize(f->dev);
+ g_free(f->dev);
+}
+
+static void select_test(TestFixture *f, gconstpointer user_data)
+{
+ uint32_t tmp = 0;
+ struct {
+ int persel;
+ int func;
+ } persel_func_map[] = {
+ { FLEXCOMM_PERSEL_USART, FLEXCOMM_FUNC_USART },
+ { FLEXCOMM_PERSEL_SPI, FLEXCOMM_FUNC_SPI },
+ { FLEXCOMM_PERSEL_I2C, FLEXCOMM_FUNC_I2C },
+ { FLEXCOMM_PERSEL_I2S_TX, FLEXCOMM_FUNC_I2S },
+ { FLEXCOMM_PERSEL_I2S_RX, FLEXCOMM_FUNC_I2S },
+ };
+ int i;
+
+ /* test that no function is selected */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL) == 0);
+
+ /* no register access until a function is selected */
+ g_assert(reg32_addr_read_raw(f->dev, FLEXCOMM_BASE, &tmp, 4)
+ == MEMTX_ERROR);
+ g_assert(reg32_addr_write_raw(f->dev, FLEXCOMM_BASE, tmp, 4)
+ == MEMTX_ERROR);
+
+ /* test that we can select all functions (including I2S RX) */
+ for (i = 0; i < ARRAY_SIZE(persel_func_map); i++) {
+ int persel = persel_func_map[i].persel;
+ int func = persel_func_map[i].func;
+
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, persel);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL) == persel);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PID, ID) == persel);
+
+ /* check that current function was selected */
+ assert_msg(f, SELECT_MSG(func, 1));
+
+ /* check that previous function was de-selected */
+ if (i > 0) {
+ int prev_func = persel_func_map[i - 1].func;
+
+ assert_msg(f, SELECT_MSG(prev_func, 0));
+ }
+
+ /* test that we can access function registers */
+ reg32_addr_write_raw(f->dev, FLEXCOMM_BASE + 0x100, 0xabcd, 4);
+ assert_msg(f, REG_WRITE_MSG(func, 0x100, 0xabcd, 4));
+
+ reg32_addr_read_raw(f->dev, FLEXCOMM_BASE + 0x100, &tmp, 4);
+ assert_msg(f, REG_READ_MSG(func, 0x100, 4));
+ }
+
+ /* try to select something invalid */
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, 7);
+ /* check for no function selected */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL) == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PID, ID) == 0);
+ /* check that previous function was deselected */
+ assert_msg(f, SELECT_MSG(FLEXCOMM_FUNC_I2S, 0));
+
+ /* now select and lock USART */
+ tmp = FLEXCOMM_PSELID_LOCK_Msk | FLEXCOMM_PERSEL_USART;
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, tmp);
+ tmp = REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL);
+ g_assert(tmp == FLEXCOMM_PERSEL_USART);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, LOCK) == 1);
+ tmp = REG32_READ_FIELD(f->dev, FLEXCOMM, PID, ID);
+ g_assert(tmp == FLEXCOMM_PERSEL_USART);
+ assert_msg(f, SELECT_MSG(FLEXCOMM_FUNC_USART, 1));
+
+ /* try to change the selection to spi */
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, FLEXCOMM_PERSEL_SPI);
+ /* it should still be locked USART */
+ tmp = REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, PERSEL);
+ g_assert(tmp == FLEXCOMM_PERSEL_USART);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM, PSELID, LOCK) == 1);
+ tmp = REG32_READ_FIELD(f->dev, FLEXCOMM, PID, ID);
+ g_assert(tmp == FLEXCOMM_PERSEL_USART);
+}
+
+/* mock-up */
+const PropertyInfo qdev_prop_chr;
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ /* Initialize object types. */
+ sysbus_mock_init();
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add("/flexcomm/select_test", TestFixture,
+ (gconstpointer)FLEXCOMM_FULL, set_up, select_test,
+ tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 10/23] hw/char: add support for flexcomm usart
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (8 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 09/23] test/unit: add flexcomm unit test Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 11/23] test/unit: add flexcomm usart unit test Octavian Purdila
` (13 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add support for NXP's flexcomm usart. It supports interupts and FIFO
access but no DMA.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 4 +
hw/char/flexcomm_usart.c | 302 +++++++++++++++++++++++++++++++
hw/char/meson.build | 1 +
hw/char/trace-events | 9 +
hw/misc/flexcomm.c | 9 +
include/hw/char/flexcomm_usart.h | 20 ++
include/hw/misc/flexcomm.h | 6 +
tests/unit/meson.build | 3 +-
8 files changed, 353 insertions(+), 1 deletion(-)
create mode 100644 hw/char/flexcomm_usart.c
create mode 100644 include/hw/char/flexcomm_usart.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index 9ce6c1d838..ed0f69f437 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -2,3 +2,7 @@ genh += custom_target('flexcomm.h',
output: 'flexcomm.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'FLEXCOMM0', '-t', 'FLEXCOMM'])
+genh += custom_target('flexcomm_usart.h',
+ output: 'flexcomm_usart.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'USART0', '-t', 'FLEXCOMM_USART'])
diff --git a/hw/char/flexcomm_usart.c b/hw/char/flexcomm_usart.c
new file mode 100644
index 0000000000..c00106eee6
--- /dev/null
+++ b/hw/char/flexcomm_usart.c
@@ -0,0 +1,302 @@
+/*
+ * QEMU model for NXP's FLEXCOMM USART
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/regs.h"
+#include "hw/char/flexcomm_usart.h"
+
+#define reg(field) offsetof(FLEXCOMM_USART_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(FLEXCOMM_USART_Type) / sizeof(uint32_t))
+
+static FLEXCOMM_USART_REGISTER_NAMES_ARRAY(reg_names);
+
+static void flexcomm_usart_reset(FlexcommState *s)
+{
+ flexcomm_usart_reset_registers(&s->regs.usart);
+}
+
+static void update_fifo_stat(FlexcommState *s)
+{
+ int rxlvl = fifo32_num_used(&s->rx_fifo);
+ int txlvl = fifo32_num_used(&s->tx_fifo);
+
+ s->regs.usart.FIFOSTAT_b.RXLVL = fifo32_num_used(&s->rx_fifo);
+ s->regs.usart.FIFOSTAT_b.TXLVL = fifo32_num_used(&s->tx_fifo);
+ s->regs.usart.FIFOSTAT_b.RXFULL = fifo32_is_full(&s->rx_fifo) ? 1 : 0;
+ s->regs.usart.FIFOSTAT_b.RXNOTEMPTY = !fifo32_is_empty(&s->rx_fifo) ? 1 : 0;
+ s->regs.usart.FIFOSTAT_b.TXNOTFULL = !fifo32_is_full(&s->tx_fifo) ? 1 : 0;
+ s->regs.usart.FIFOSTAT_b.TXEMPTY = fifo32_is_empty(&s->tx_fifo) ? 1 : 0;
+
+ if (s->regs.usart.FIFOTRIG_b.RXLVLENA &&
+ (rxlvl > s->regs.usart.FIFOTRIG_b.RXLVL)) {
+ s->regs.usart.FIFOINTSTAT_b.RXLVL = 1;
+ } else {
+ s->regs.usart.FIFOINTSTAT_b.RXLVL = 0;
+ }
+
+ if (s->regs.usart.FIFOTRIG_b.TXLVLENA &&
+ (txlvl <= s->regs.usart.FIFOTRIG_b.TXLVL)) {
+ s->regs.usart.FIFOINTSTAT_b.TXLVL = 1;
+ } else {
+ s->regs.usart.FIFOINTSTAT_b.TXLVL = 0;
+ }
+
+ trace_flexcomm_usart_fifostat(DEVICE(s)->id, s->regs.usart.FIFOSTAT,
+ s->regs.usart.FIFOINTSTAT);
+}
+
+static void flexcomm_usart_irq_update(FlexcommState *s)
+{
+ bool irq, per_irqs, fifo_irqs, enabled = s->regs.usart.CFG_b.ENABLE;
+
+ update_fifo_stat(s);
+ fifo_irqs = s->regs.usart.FIFOINTSTAT & s->regs.usart.FIFOINTENSET;
+
+ s->regs.usart.INTSTAT = s->regs.usart.STAT & s->regs.usart.INTENSET;
+ per_irqs = s->regs.usart.INTSTAT != 0;
+
+ irq = enabled && (fifo_irqs || per_irqs);
+
+ trace_flexcomm_usart_irq(DEVICE(s)->id, irq, fifo_irqs, per_irqs, enabled);
+ flexcomm_irq(s, irq);
+}
+
+static int flexcomm_usart_rx_space(void *opaque)
+{
+ FlexcommState *s = opaque;
+ uint32_t ret = fifo32_num_free(&s->rx_fifo);
+
+ if (!s->regs.usart.CFG_b.ENABLE || !s->regs.usart.FIFOCFG_b.ENABLERX) {
+ ret = 0;
+ }
+
+ trace_flexcomm_usart_rx_space(DEVICE(s)->id, ret);
+
+ return ret;
+}
+
+static void flexcomm_usart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ FlexcommState *s = opaque;
+
+ if (!s->regs.usart.CFG_b.ENABLE || !s->regs.usart.FIFOCFG_b.ENABLERX) {
+ return;
+ }
+
+ trace_flexcomm_usart_rx(DEVICE(s)->id);
+
+ while (!fifo32_is_full(&s->rx_fifo) && size) {
+ fifo32_push(&s->rx_fifo, *buf++);
+ size--;
+ }
+
+ flexcomm_usart_irq_update(s);
+}
+
+static MemTxResult flexcomm_usart_reg_read(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t *data,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(FIFORD):
+ {
+ if (!fifo32_is_empty(&s->rx_fifo)) {
+ *data = fifo32_pop(&s->rx_fifo);
+ qemu_chr_fe_accept_input(&s->chr);
+ }
+ break;
+ }
+ case reg(FIFORDNOPOP):
+ {
+ if (!fifo32_is_empty(&s->rx_fifo)) {
+ *data = fifo32_peek(&s->rx_fifo);
+ }
+ break;
+ }
+ default:
+ *data = reg32_read(&s->regs, addr);
+ break;
+ }
+
+ flexcomm_usart_irq_update(s);
+
+out:
+ trace_flexcomm_usart_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static MemTxResult flexcomm_usart_reg_write(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t mask[REG_NO] = {
+ [regi(CFG)] = BITS(23, 18) | BITS(15, 14) | BITS(12, 11) | BITS(9, 2) |
+ BIT(0),
+ [regi(CTL)] = BIT(16) | BITS(9, 8) | BIT(6) | BITS(2, 1),
+ [regi(STAT)] = BITS(16, 11) | BIT(5),
+ [regi(INTENSET)] = BITS(16, 11) | BITS(6, 5) | BIT(3),
+ [regi(INTENCLR)] = BITS(16, 11) | BITS(6, 5) | BIT(3),
+ [regi(BRG)] = BITS(15, 0),
+ [regi(OSR)] = BITS(3, 0),
+ [regi(ADDR)] = BITS(7, 0),
+ [regi(FIFOCFG)] = BITS(16, 12) | BITS(5, 4) | BITS(1, 0),
+ [regi(FIFOSTAT)] = BITS(1, 0),
+ [regi(FIFOTRIG)] = BITS(19, 16) | BITS(11, 8) | BITS(1, 0),
+ [regi(FIFOINTENSET)] = BITS(3, 0),
+ [regi(FIFOINTENCLR)] = BITS(3, 0),
+ [regi(FIFOWR)] = BITS(8, 0),
+ };
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(INTENCLR):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ s->regs.usart.INTENSET &= ~s->regs.usart.INTENCLR;
+ break;
+ }
+ case reg(FIFOCFG):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ if (s->regs.usart.FIFOCFG_b.EMPTYRX) {
+ s->regs.usart.FIFOCFG_b.EMPTYRX = 0;
+ fifo32_reset(&s->rx_fifo);
+ }
+ if (s->regs.usart.FIFOCFG_b.EMPTYTX) {
+ s->regs.usart.FIFOCFG_b.EMPTYTX = 0;
+ fifo32_reset(&s->tx_fifo);
+ }
+ break;
+ }
+ case reg(FIFOSTAT):
+ {
+ bool rxerr = s->regs.usart.FIFOSTAT_b.RXERR;
+ bool txerr = s->regs.usart.FIFOSTAT_b.TXERR;
+
+ reg32_write(&s->regs, addr, value, mask);
+
+ if (rxerr && s->regs.usart.FIFOSTAT_b.RXERR) {
+ rxerr = false;
+ }
+ if (txerr && s->regs.usart.FIFOSTAT_b.TXERR) {
+ txerr = false;
+ }
+
+ s->regs.usart.FIFOSTAT_b.RXERR = rxerr;
+ s->regs.usart.FIFOSTAT_b.TXERR = txerr;
+ break;
+ }
+ case reg(FIFOINTENSET):
+ {
+ s->regs.usart.FIFOINTENSET |= value & mask[addr / 4];
+ break;
+ }
+ case reg(FIFOINTENCLR):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ s->regs.usart.FIFOINTENSET &= ~s->regs.usart.FIFOINTENCLR;
+ break;
+ }
+ case reg(FIFOWR):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ if (!fifo32_is_full(&s->tx_fifo)) {
+ fifo32_push(&s->tx_fifo, s->regs.usart.FIFOWR);
+ }
+
+ if (!s->regs.usart.CFG_b.ENABLE || !s->regs.usart.FIFOCFG_b.ENABLETX) {
+ break;
+ }
+
+ while (!fifo32_is_empty(&s->tx_fifo)) {
+ uint32_t val32 = fifo32_pop(&s->tx_fifo);
+ uint8_t val8 = val32 & 0xff;
+
+ trace_flexcomm_usart_tx(DEVICE(s)->id);
+ qemu_chr_fe_write_all(&s->chr, &val8, sizeof(val8));
+ }
+ break;
+ }
+ case reg(CFG):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ break;
+ }
+ default:
+ reg32_write(&s->regs, addr, value, mask);
+ break;
+ }
+
+ flexcomm_usart_irq_update(s);
+
+out:
+ trace_flexcomm_usart_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+ return ret;
+}
+
+static void flexcomm_usart_select(void *opaque, FlexcommState *s, int f,
+ bool set)
+{
+ if (set) {
+ qemu_chr_fe_set_handlers(&s->chr, flexcomm_usart_rx_space,
+ flexcomm_usart_rx, NULL, NULL,
+ s, NULL, true);
+ flexcomm_usart_reset(s);
+ fifo32_create(&s->rx_fifo, s->regs.usart.FIFOSIZE_b.FIFOSIZE);
+ fifo32_create(&s->tx_fifo, s->regs.usart.FIFOSIZE_b.FIFOSIZE);
+ } else {
+ qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL, NULL,
+ false);
+ fifo32_destroy(&s->rx_fifo);
+ fifo32_destroy(&s->tx_fifo);
+ }
+}
+
+static const FlexcommFunctionOps flexcomm_usart_ops = {
+ .select = flexcomm_usart_select,
+ .reg_read = flexcomm_usart_reg_read,
+ .reg_write = flexcomm_usart_reg_write,
+};
+
+void flexcomm_usart_init(FlexcommState *s)
+{
+}
+
+void flexcomm_usart_register(void)
+{
+ Error *err = NULL;
+
+ if (!flexcomm_register_ops(FLEXCOMM_FUNC_USART, NULL,
+ &flexcomm_usart_ops, &err)) {
+ error_report_err(err);
+ }
+}
diff --git a/hw/char/meson.build b/hw/char/meson.build
index e5b13b6958..8f8c17ae66 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -39,3 +39,4 @@ system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c'))
specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c'))
specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c'))
+system_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm_usart.c'))
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 8875758076..19fcf1f832 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -125,3 +125,12 @@ xen_console_unrealize(unsigned int idx) "idx %u"
xen_console_realize(unsigned int idx, const char *chrdev) "idx %u chrdev %s"
xen_console_device_create(unsigned int idx) "idx %u"
xen_console_device_destroy(unsigned int idx) "idx %u"
+
+# flexcomm_usart.c
+flexcomm_usart_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
+flexcomm_usart_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
+flexcomm_usart_rx_space(const char *id, uint32_t rx) "%s: %d"
+flexcomm_usart_rx(const char *id) "%s"
+flexcomm_usart_tx(const char *id) "%s"
+flexcomm_usart_fifostat(const char *id, uint32_t fifostat, uint32_t fifoinstat) "%s: %08x %08x"
+flexcomm_usart_irq(const char *id, bool irq, bool fifoirqs, bool perirqs, bool enabled) "%s: %d %d %d %d"
diff --git a/hw/misc/flexcomm.c b/hw/misc/flexcomm.c
index 6ec3773910..0c94928aa2 100644
--- a/hw/misc/flexcomm.c
+++ b/hw/misc/flexcomm.c
@@ -22,6 +22,7 @@
#include "trace.h"
#include "hw/regs.h"
#include "hw/misc/flexcomm.h"
+#include "hw/char/flexcomm_usart.h"
#define reg(field) offsetof(FLEXCOMM_Type, field)
#define regi(x) (reg(x) / sizeof(uint32_t))
@@ -203,6 +204,7 @@ static const MemoryRegionOps flexcomm_ops = {
static Property flexcomm_properties[] = {
DEFINE_PROP_UINT32("functions", FlexcommState, functions,
FLEXCOMM_FULL),
+ DEFINE_PROP_CHR("chardev", FlexcommState, chr),
DEFINE_PROP_END_OF_LIST(),
};
@@ -221,6 +223,11 @@ static void flexcomm_init(Object *obj)
static void flexcomm_realize(DeviceState *dev, Error **errp)
{
+ FlexcommState *s = FLEXCOMM(dev);
+
+ if (has_function(s, FLEXCOMM_FUNC_USART)) {
+ flexcomm_usart_init(s);
+ }
}
static void flexcomm_class_init(ObjectClass *klass, void *data)
@@ -230,6 +237,8 @@ static void flexcomm_class_init(ObjectClass *klass, void *data)
dc->reset = flexcomm_reset;
device_class_set_props(dc, flexcomm_properties);
dc->realize = flexcomm_realize;
+
+ flexcomm_usart_register();
}
static const TypeInfo flexcomm_info = {
diff --git a/include/hw/char/flexcomm_usart.h b/include/hw/char/flexcomm_usart.h
new file mode 100644
index 0000000000..07d14cb330
--- /dev/null
+++ b/include/hw/char/flexcomm_usart.h
@@ -0,0 +1,20 @@
+/*
+ * QEMU model for NXP's FLEXCOMM USART
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_CHAR_FLEXCOMM_USART_H
+#define HW_CHAR_FLEXCOMM_USART_H
+
+#include "hw/misc/flexcomm.h"
+
+void flexcomm_usart_init(FlexcommState *s);
+void flexcomm_usart_register(void);
+
+#endif /* HW_CHAR_RT500_FLEXCOMM_USART_H */
diff --git a/include/hw/misc/flexcomm.h b/include/hw/misc/flexcomm.h
index 422452bd96..db76e32c6d 100644
--- a/include/hw/misc/flexcomm.h
+++ b/include/hw/misc/flexcomm.h
@@ -13,7 +13,9 @@
#define HW_FLEXCOMM_H
#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
#include "hw/arm/svd/flexcomm.h"
+#include "hw/arm/svd/flexcomm_usart.h"
#include "qemu/fifo32.h"
#define TYPE_FLEXCOMM "flexcomm"
@@ -43,10 +45,14 @@ typedef struct {
MemoryRegion mmio;
union {
FLEXCOMM_Type flex;
+ FLEXCOMM_USART_Type usart;
} regs;
uint32_t functions;
qemu_irq irq;
bool irq_state;
+ CharBackend chr;
+ Fifo32 tx_fifo;
+ Fifo32 rx_fifo;
} FlexcommState;
typedef struct {
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 4ccb15404d..70e816c034 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -143,10 +143,11 @@ if have_system
'test-vmstate': [migration, io],
'test-yank': ['socket-helpers.c', qom, io, chardev],
'test-flexcomm': [
- hwcore,
+ hwcore, chardev, qom, migration,
meson.project_source_root() / 'hw/core/gpio.c',
meson.project_source_root() / 'tests/unit/sysbus-mock.c',
meson.project_source_root() / 'hw/misc/flexcomm.c',
+ meson.project_source_root() / 'hw/char/flexcomm_usart.c',
],
}
if config_host_data.get('CONFIG_INOTIFY1')
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 11/23] test/unit: add flexcomm usart unit test
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (9 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 10/23] hw/char: add support for flexcomm usart Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 12/23] hw/i2c: add support for flexcomm i2c Octavian Purdila
` (12 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add polling and irq unit tests for the flexcomm usart device model.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 7 +
tests/unit/test-flexcomm-usart.c | 321 +++++++++++++++++++++++++++++++
2 files changed, 328 insertions(+)
create mode 100644 tests/unit/test-flexcomm-usart.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 70e816c034..dcfd2e661c 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -149,6 +149,13 @@ if have_system
meson.project_source_root() / 'hw/misc/flexcomm.c',
meson.project_source_root() / 'hw/char/flexcomm_usart.c',
],
+ 'test-flexcomm-usart': [
+ hwcore, chardev, qom, migration,
+ meson.project_source_root() / 'hw/core/gpio.c',
+ meson.project_source_root() / 'tests/unit/sysbus-mock.c',
+ meson.project_source_root() / 'hw/misc/flexcomm.c',
+ meson.project_source_root() / 'hw/char/flexcomm_usart.c',
+ ],
}
if config_host_data.get('CONFIG_INOTIFY1')
tests += {'test-util-filemonitor': []}
diff --git a/tests/unit/test-flexcomm-usart.c b/tests/unit/test-flexcomm-usart.c
new file mode 100644
index 0000000000..645f3b4c26
--- /dev/null
+++ b/tests/unit/test-flexcomm-usart.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/config-file.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+#include "qemu/option.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+#include "hw/misc/flexcomm.h"
+#include "sysbus-mock.h"
+#include "reg-utils.h"
+
+typedef struct {
+ DeviceState *dev;
+ int sock;
+ Chardev *chr;
+ bool irq;
+} TestFixture;
+
+#define FLEXCOMM_BASE 0x40106000UL
+#define FLEXCOMM_USART_BASE FLEXCOMM_BASE
+
+/* Callback for the interrupt line. */
+static void usart_irq_set(void *opaque, int line, int level)
+{
+ TestFixture *f = (TestFixture *)opaque;
+
+ f->irq = level;
+}
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ struct sockaddr_in sockaddr = {
+ .sin_family = AF_INET,
+ };
+ socklen_t sockaddr_len = sizeof(sockaddr);
+ char chr_opts[] = "udp:127.0.0.1:xxxxx";
+ FlexcommState *s;
+ char buf[] = "xxx";
+ int port;
+
+ /* create "server" socket and bind to a random port */
+ f->sock = socket(AF_INET, SOCK_DGRAM, 0);
+ g_assert(f->sock >= 0);
+ g_assert(bind(f->sock, &sockaddr, sizeof(sockaddr)) == 0);
+ g_assert(getsockname(f->sock, &sockaddr, &sockaddr_len) == 0);
+
+ /* create the an UDP char device and connect it to the sever */
+ port = ntohs(sockaddr.sin_port);
+ g_assert(port != 0);
+ snprintf(chr_opts, sizeof(chr_opts), "udp:127.0.0.1:%d", port);
+ f->chr = qemu_chr_new("udp", chr_opts, NULL);
+ g_assert_nonnull(f->chr);
+
+ /* test connectivity and connect server to UDP char device */
+ qemu_chr_write_all(f->chr, (const uint8_t *)"210", sizeof("210"));
+ recvfrom(f->sock, buf, sizeof(buf), 0, &sockaddr, &sockaddr_len);
+ g_assert(strcmp(buf, "210") == 0);
+ g_assert(sockaddr_len == sizeof(sockaddr));
+ g_assert(connect(f->sock, &sockaddr, sockaddr_len) == 0);
+
+ f->dev = qdev_new(TYPE_FLEXCOMM);
+ g_assert(f->dev);
+
+ s = FLEXCOMM(f->dev);
+ s->irq = qemu_allocate_irq(usart_irq_set, f, 0);
+ g_assert(qemu_chr_fe_init(&s->chr, f->chr, &error_abort));
+
+ if (data != NULL) {
+ qdev_prop_set_int32(DEVICE(f->dev), "functions", (uintptr_t)data);
+ }
+
+ qdev_realize_and_unref(f->dev, NULL, &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->dev), 0, FLEXCOMM_BASE);
+
+ qemu_chr_be_update_read_handlers(f->chr, NULL);
+
+ device_cold_reset(f->dev);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ qdev_unrealize(f->dev);
+ object_unparent(OBJECT(f->chr));
+ close(f->sock);
+ g_free(f->dev);
+}
+
+static void polling_test(TestFixture *f, gconstpointer user_data)
+{
+ int i;
+ uint32_t tmp;
+ unsigned char byte;
+ int fifo_size;
+
+ /* select and lock USART */
+ tmp = FLEXCOMM_PSELID_LOCK_Msk | FLEXCOMM_PERSEL_USART;
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, tmp);
+
+ fifo_size = REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSIZE, FIFOSIZE);
+
+ /* enable USART */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, CFG, ENABLE, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, CFG, ENABLE) == 1);
+
+ /* enable TX and RX FIFO */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLETX, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLETX) == 1);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLERX, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLERX) == 1);
+
+ /* test writes and fifo counters wrap */
+ for (i = 0; i < fifo_size / 2; i++) {
+ /* check fifostat */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXFULL) ==
+ 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXNOTEMPTY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXNOTFULL)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXEMPTY) ==
+ 1);
+
+ REG32_WRITE(f->dev, FLEXCOMM_USART, FIFOWR, 'a' + i);
+ recv(f->sock, &byte, 1, 0);
+ g_assert_cmpuint(byte, ==, 'a' + i);
+ }
+
+ /* test reads and fifo level */
+
+ for (i = 0; i < fifo_size / 2; i++) {
+ byte = 'A' + i;
+ g_assert(send(f->sock, &byte, 1, 0) == 1);
+ }
+
+ /* wait for the RXLVL to update */
+ WAIT_REG32_FIELD(1000, f->dev, FLEXCOMM_USART, FIFOSTAT, RXLVL,
+ fifo_size / 2);
+
+ /* check fifo stat */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXFULL) == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXNOTEMPTY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXNOTFULL)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXEMPTY)
+ == 1);
+
+ /* send until FIFO is full */
+ for (i = fifo_size / 2; i < fifo_size; i++) {
+ byte = 'A' + i;
+ g_assert(send(f->sock, &byte, 1, 0) == 1);
+ }
+
+ /* wait for the RXLVL to update */
+ WAIT_REG32_FIELD(1000, f->dev, FLEXCOMM_USART, FIFOSTAT, RXLVL, fifo_size);
+
+ /* check fifo stat */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXFULL) == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXNOTEMPTY) ==
+ 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXNOTFULL) ==
+ 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXEMPTY) ==
+ 1);
+
+ /* check read no pop */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFORDNOPOP, RXDATA) ==
+ 'A');
+
+ /* now read from the fifo */
+ for (i = 0; i < fifo_size; i++) {
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFORD, RXDATA) ==
+ 'A' + i);
+ }
+
+ /* check fifostat */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXFULL) == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXNOTEMPTY) ==
+ 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXNOTFULL) ==
+ 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, TXEMPTY) == 1);
+}
+
+static void irq_test(TestFixture *f, gconstpointer user_data)
+{
+ char buf[256] = { 0, };
+ uint32_t tmp;
+
+ /* select and lock FLEXCOMM_USART */
+ tmp = FLEXCOMM_PSELID_LOCK_Msk | FLEXCOMM_PERSEL_USART;
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, tmp);
+
+ /*
+ * set RX IRQ/DMA trigger level to 4 bytes - value 3 in FIFOTRIG
+ *
+ * 0000 - Trigger when the RX FIFO has received 1 entry (is no longer empty)
+ * 0001 - Trigger when the RX FIFO has received 2 entries
+ * 1111 - Trigger when the RX FIFO has received 16 entries (has become full)
+ */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, RXLVL, 3);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, RXLVL) == 3);
+
+ /* enable RX trigger for IRQ/DMA */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, RXLVLENA, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, RXLVLENA) == 1);
+
+ /* enable RXLVL interrupt */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOINTENSET, RXLVL, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTENSET, RXLVL)
+ == 1);
+
+ /* enable FLEXCOMM_USART */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, CFG, ENABLE, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, CFG, ENABLE) == 1);
+
+ /* enable TX and RX FIFO */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLETX, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLETX) == 1);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLERX, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOCFG, ENABLERX) == 1);
+
+ /* check interrupt status */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, RXLVL) == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, TXLVL) == 0);
+ g_assert(f->irq == false);
+
+ /* enable TX trigger for IRQ/DMA */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, TXLVLENA, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, TXLVLENA) == 1);
+
+ /* enable irq for TX */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOINTENSET, TXLVL, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTENSET, TXLVL) ==
+ 1);
+
+ /* check TX irq */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, TXLVL) == 1);
+ g_assert(f->irq == true);
+
+ /* disable irq for TX */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, TXLVLENA, 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOTRIG, TXLVLENA) == 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, TXLVL) == 0);
+ g_assert(f->irq == false);
+
+ /* send 3 bytes */
+ g_assert(send(f->sock, buf, 3, 0) == 3);
+
+ /* check that we have 3 bytes in the fifo */
+ WAIT_REG32_FIELD(1000, f->dev, FLEXCOMM_USART, FIFOSTAT, RXLVL, 3);
+
+ /* and no interrupt has been triggered yet */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, RXLVL) == 0);
+ g_assert(f->irq == false);
+
+ /* push it over the edge */
+ g_assert(send(f->sock, buf, 1, 0) == 1);
+
+ /* check that we have 4 bytes in the fifo */
+ WAIT_REG32_FIELD(1000, f->dev, FLEXCOMM_USART, FIFOSTAT, RXLVL, 4);
+
+ /* and the interrupt has been triggered */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, RXLVL) == 1);
+ g_assert(f->irq == true);
+
+ /* read one byte from the fifo */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFORD, RXDATA) == 0);
+
+ /* we should have 3 bytes in the FIFO */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOSTAT, RXLVL) == 3);
+
+ /* and no interrupts active */
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_USART, FIFOINTSTAT, RXLVL) == 0);
+ g_assert(f->irq == false);
+}
+
+/* mock-up */
+const PropertyInfo qdev_prop_chr;
+
+int main(int argc, char **argv)
+{
+ qemu_init_main_loop(&error_abort);
+ socket_init();
+
+ g_test_init(&argc, &argv, NULL);
+
+ /* Initialize object types. */
+ sysbus_mock_init();
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_chardev_opts);
+
+ g_test_add("/flexcomm-usart/polling", TestFixture,
+ (gconstpointer)(1 << FLEXCOMM_FUNC_USART),
+ set_up, polling_test, tear_down);
+
+ g_test_add("/flexcomm-usart/irq", TestFixture,
+ (gconstpointer)(1 << FLEXCOMM_FUNC_USART),
+ set_up, irq_test, tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 12/23] hw/i2c: add support for flexcomm i2c
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (10 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 11/23] test/unit: add flexcomm usart unit test Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 13/23] test/unit: add i2c-tester Octavian Purdila
` (11 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add support for NXP's flexcomm i2c. It does not support slave mode or
DMA.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 4 +
hw/i2c/flexcomm_i2c.c | 224 ++++++++++++++++++++++++++++++++++
hw/i2c/meson.build | 1 +
hw/i2c/trace-events | 10 ++
hw/misc/flexcomm.c | 6 +
include/hw/i2c/flexcomm_i2c.h | 27 ++++
include/hw/misc/flexcomm.h | 4 +
tests/unit/meson.build | 4 +
8 files changed, 280 insertions(+)
create mode 100644 hw/i2c/flexcomm_i2c.c
create mode 100644 include/hw/i2c/flexcomm_i2c.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index ed0f69f437..fa0d3da829 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -6,3 +6,7 @@ genh += custom_target('flexcomm_usart.h',
output: 'flexcomm_usart.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'USART0', '-t', 'FLEXCOMM_USART'])
+genh += custom_target('flexcomm_i2c_regs.h',
+ output: 'flexcomm_i2c.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'I2C0', '-t', 'FLEXCOMM_I2C'])
diff --git a/hw/i2c/flexcomm_i2c.c b/hw/i2c/flexcomm_i2c.c
new file mode 100644
index 0000000000..7e74f858ce
--- /dev/null
+++ b/hw/i2c/flexcomm_i2c.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU model for NXP's FLEXCOMM I2C
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/regs.h"
+#include "hw/i2c/flexcomm_i2c.h"
+
+#define reg(field) offsetof(FLEXCOMM_I2C_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(FLEXCOMM_I2C_Type) / sizeof(uint32_t))
+
+static FLEXCOMM_I2C_REGISTER_NAMES_ARRAY(reg_names);
+
+static void flexcomm_i2c_reset(FlexcommState *s)
+{
+ flexcomm_i2c_reset_registers(&s->regs.i2c);
+}
+
+static void flexcomm_i2c_irq_update(FlexcommState *s)
+{
+ bool enabled = s->regs.i2c.CFG_b.MSTEN;
+ bool irq, per_irqs;
+
+ s->regs.i2c.INTSTAT = s->regs.i2c.STAT & s->regs.i2c.INTENSET;
+ per_irqs = s->regs.i2c.INTSTAT != 0;
+
+ irq = enabled && per_irqs;
+
+ trace_flexcomm_i2c_irq(DEVICE(s)->id, irq, per_irqs, enabled);
+ flexcomm_irq(s, irq);
+}
+
+static MemTxResult flexcomm_i2c_reg_read(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t *data,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ *data = reg32_read(&s->regs, addr);
+
+ flexcomm_i2c_irq_update(s);
+
+out:
+ trace_flexcomm_i2c_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static MemTxResult flexcomm_i2c_reg_write(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t mask[REG_NO] = {
+ [regi(CFG)] = BITS(5, 0),
+ [regi(STAT)] = BITS(25, 24) | BIT(19) | BIT(17) | BIT(15) | BIT(6),
+ [regi(INTENSET)] = BITS(25, 24) | BIT(19) | BITS(17, 15) | BIT(11) |
+ BIT(8) | BIT(6) | BIT(4) | BIT(0),
+ [regi(INTENCLR)] = BITS(25, 24) | BIT(19) | BITS(17, 15) | BIT(11) |
+ BIT(8) | BIT(6) | BIT(4) | BIT(0),
+ [regi(TIMEOUT)] = BITS(15, 0),
+ [regi(CLKDIV)] = BITS(15, 0),
+ [regi(MSTCTL)] = BITS(3, 0),
+ [regi(MSTTIME)] = BITS(6, 4) | BITS(2, 0),
+ [regi(MSTDAT)] = BITS(7, 0),
+ [regi(SLVCTL)] = BITS(9, 8) | BIT(3) | BITS(1, 0),
+ [regi(SLVDAT)] = BITS(7, 0),
+ [regi(SLVADR0)] = BIT(15) | BITS(7, 0),
+ [regi(SLVADR1)] = BIT(15) | BITS(7, 0),
+ [regi(SLVADR2)] = BIT(15) | BITS(7, 0),
+ [regi(SLVADR3)] = BIT(15) | BITS(7, 0),
+ [regi(SLVQUAL0)] = BITS(7, 0),
+ };
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(CFG):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ if (s->regs.i2c.CFG_b.SLVEN) {
+ qemu_log_mask(LOG_GUEST_ERROR, "I2C slave not supported");
+ }
+ if (s->regs.i2c.CFG_b.MONEN) {
+ qemu_log_mask(LOG_GUEST_ERROR, "I2C monitoring not supported");
+ }
+ break;
+ }
+ case reg(INTENCLR):
+ {
+ s->regs.i2c.INTENSET &= ~(value & mask[addr / 4]);
+ break;
+ }
+ case reg(TIMEOUT):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ /* The bottom 4 bits are hard-wired to 0xF */
+ s->regs.i2c.TIMEOUT_b.TOMIN = 0xf;
+ break;
+ }
+ case reg(MSTCTL):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ if (s->regs.i2c.MSTCTL_b.MSTSTART) {
+ uint8_t i2c_addr = s->regs.i2c.MSTDAT_b.DATA;
+ bool recv = i2c_addr & 1;
+
+ trace_flexcomm_i2c_start(DEVICE(s)->id, i2c_addr, recv);
+ if (i2c_start_transfer(s->i2c, i2c_addr, recv)) {
+ s->regs.i2c.STAT_b.MSTSTATE = MSTSTATE_NAKADR;
+ trace_flexcomm_i2c_nak(DEVICE(s)->id);
+ } else {
+ if (recv) {
+ uint8_t data = i2c_recv(s->i2c);
+
+ s->regs.i2c.MSTDAT_b.DATA = data;
+ trace_flexcomm_i2c_rx(DEVICE(s)->id, data);
+ s->regs.i2c.STAT_b.MSTSTATE = MSTSTATE_RXRDY;
+ } else {
+ s->regs.i2c.STAT_b.MSTSTATE = MSTSTATE_TXRDY;
+ }
+ }
+ }
+ if (s->regs.i2c.MSTCTL_b.MSTSTOP) {
+ s->regs.i2c.STAT_b.MSTSTATE = MSTSTATE_IDLE;
+ i2c_end_transfer(s->i2c);
+ }
+ if (s->regs.i2c.MSTCTL_b.MSTCONTINUE) {
+ if (s->regs.i2c.STAT_b.MSTSTATE == MSTSTATE_TXRDY) {
+ uint8_t data = s->regs.i2c.MSTDAT_b.DATA;
+
+ trace_flexcomm_i2c_tx(DEVICE(s)->id, data);
+ if (i2c_send(s->i2c, data)) {
+ s->regs.i2c.STAT_b.MSTSTATE = MSTSTATE_NAKDAT;
+ }
+ } else if (s->regs.i2c.STAT_b.MSTSTATE == MSTSTATE_RXRDY) {
+ uint8_t data = i2c_recv(s->i2c);
+
+ s->regs.i2c.MSTDAT_b.DATA = data;
+ trace_flexcomm_i2c_rx(DEVICE(s)->id, data);
+ }
+ }
+ break;
+ }
+ case reg(STAT):
+ {
+ /* write 1 to clear bits */
+ s->regs.i2c.STAT &= ~(value & mask[addr / 4]);
+ break;
+ }
+ case reg(SLVCTL):
+ case reg(SLVDAT):
+ case reg(SLVADR0):
+ case reg(SLVADR1):
+ case reg(SLVADR2):
+ case reg(SLVADR3):
+ case reg(SLVQUAL0):
+ {
+ qemu_log_mask(LOG_GUEST_ERROR, "I2C slave not supported\n");
+ break;
+ }
+ default:
+ reg32_write(&s->regs, addr, value, mask);
+ break;
+ }
+
+ flexcomm_i2c_irq_update(s);
+
+out:
+ trace_flexcomm_i2c_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+ return ret;
+}
+
+static void flexcomm_i2c_select(void *opaque, FlexcommState *s, int f,
+ bool set)
+{
+ if (set) {
+ flexcomm_i2c_reset(s);
+ }
+}
+
+static const FlexcommFunctionOps flexcomm_i2c_ops = {
+ .select = flexcomm_i2c_select,
+ .reg_read = flexcomm_i2c_reg_read,
+ .reg_write = flexcomm_i2c_reg_write,
+};
+
+void flexcomm_i2c_init(FlexcommState *s)
+{
+ s->i2c = i2c_init_bus(DEVICE(s), "i2c");
+}
+
+void flexcomm_i2c_register(void)
+{
+ Error *err = NULL;
+
+ if (!flexcomm_register_ops(FLEXCOMM_FUNC_I2C, NULL,
+ &flexcomm_i2c_ops, &err)) {
+ error_report_err(err);
+ }
+}
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index c459adcb59..e7d79e6938 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -18,4 +18,5 @@ i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
i2c_ss.add(when: 'CONFIG_BCM2835_I2C', if_true: files('bcm2835_i2c.c'))
+i2c_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm_i2c.c'))
system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 6900e06eda..9f0175fab7 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -51,3 +51,13 @@ npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected) "%s
pca954x_write_bytes(uint8_t value) "PCA954X write data: 0x%02x"
pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x"
+
+# flexcomm_i2c.c
+
+flexcomm_i2c_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
+flexcomm_i2c_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
+flexcomm_i2c_start(const char *id, uint8_t addr, uint8_t recv) "%s: 0x%02x %d"
+flexcomm_i2c_rx(const char *id, uint8_t data) "%s: <- 0x%02x"
+flexcomm_i2c_tx(const char *id, uint8_t data) "%s: -> 0x%02x"
+flexcomm_i2c_nak(const char *id) "%s: <- nak"
+flexcomm_i2c_irq(const char *id, bool irq, bool perirqs, bool enabled) "%s: %d %d %d"
diff --git a/hw/misc/flexcomm.c b/hw/misc/flexcomm.c
index 0c94928aa2..2722c1d6a9 100644
--- a/hw/misc/flexcomm.c
+++ b/hw/misc/flexcomm.c
@@ -23,6 +23,7 @@
#include "hw/regs.h"
#include "hw/misc/flexcomm.h"
#include "hw/char/flexcomm_usart.h"
+#include "hw/i2c/flexcomm_i2c.h"
#define reg(field) offsetof(FLEXCOMM_Type, field)
#define regi(x) (reg(x) / sizeof(uint32_t))
@@ -228,6 +229,10 @@ static void flexcomm_realize(DeviceState *dev, Error **errp)
if (has_function(s, FLEXCOMM_FUNC_USART)) {
flexcomm_usart_init(s);
}
+
+ if (has_function(s, FLEXCOMM_FUNC_I2C)) {
+ flexcomm_i2c_init(s);
+ }
}
static void flexcomm_class_init(ObjectClass *klass, void *data)
@@ -239,6 +244,7 @@ static void flexcomm_class_init(ObjectClass *klass, void *data)
dc->realize = flexcomm_realize;
flexcomm_usart_register();
+ flexcomm_i2c_register();
}
static const TypeInfo flexcomm_info = {
diff --git a/include/hw/i2c/flexcomm_i2c.h b/include/hw/i2c/flexcomm_i2c.h
new file mode 100644
index 0000000000..aea01f13bd
--- /dev/null
+++ b/include/hw/i2c/flexcomm_i2c.h
@@ -0,0 +1,27 @@
+/*
+ * QEMU model for NXP's FLEXCOMM I2C
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_CHAR_FLEXCOMM_I2C_H
+#define HW_CHAR_FLEXCOMM_I2C_H
+
+#include "hw/misc/flexcomm.h"
+
+void flexcomm_i2c_init(FlexcommState *s);
+void flexcomm_i2c_register(void);
+
+#define MSTSTATE_IDLE 0
+#define MSTSTATE_RXRDY 1
+#define MSTSTATE_TXRDY 2
+#define MSTSTATE_NAKADR 3
+#define MSTSTATE_NAKDAT 4
+
+
+#endif /* HW_CHAR_FLEXCOMM_I2C_H */
diff --git a/include/hw/misc/flexcomm.h b/include/hw/misc/flexcomm.h
index db76e32c6d..3d042a3511 100644
--- a/include/hw/misc/flexcomm.h
+++ b/include/hw/misc/flexcomm.h
@@ -14,8 +14,10 @@
#include "hw/sysbus.h"
#include "chardev/char-fe.h"
+#include "hw/i2c/i2c.h"
#include "hw/arm/svd/flexcomm.h"
#include "hw/arm/svd/flexcomm_usart.h"
+#include "hw/arm/svd/flexcomm_i2c.h"
#include "qemu/fifo32.h"
#define TYPE_FLEXCOMM "flexcomm"
@@ -46,6 +48,7 @@ typedef struct {
union {
FLEXCOMM_Type flex;
FLEXCOMM_USART_Type usart;
+ FLEXCOMM_I2C_Type i2c;
} regs;
uint32_t functions;
qemu_irq irq;
@@ -53,6 +56,7 @@ typedef struct {
CharBackend chr;
Fifo32 tx_fifo;
Fifo32 rx_fifo;
+ I2CBus *i2c;
} FlexcommState;
typedef struct {
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index dcfd2e661c..4c22cb3ccc 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -148,6 +148,8 @@ if have_system
meson.project_source_root() / 'tests/unit/sysbus-mock.c',
meson.project_source_root() / 'hw/misc/flexcomm.c',
meson.project_source_root() / 'hw/char/flexcomm_usart.c',
+ meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
+ meson.project_source_root() / 'hw/i2c/core.c',
],
'test-flexcomm-usart': [
hwcore, chardev, qom, migration,
@@ -155,6 +157,8 @@ if have_system
meson.project_source_root() / 'tests/unit/sysbus-mock.c',
meson.project_source_root() / 'hw/misc/flexcomm.c',
meson.project_source_root() / 'hw/char/flexcomm_usart.c',
+ meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
+ meson.project_source_root() / 'hw/i2c/core.c',
],
}
if config_host_data.get('CONFIG_INOTIFY1')
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 13/23] test/unit: add i2c-tester
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (11 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 12/23] hw/i2c: add support for flexcomm i2c Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 14/23] test/unit: add unit tests for flexcomm i2c Octavian Purdila
` (10 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add a simple i2c peripheral to be used for testing I2C device
models. The peripheral has a fixed number of registers that can be
read and written.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/i2c_tester.c | 111 ++++++++++++++++++++++++++++++++++++++++
tests/unit/i2c_tester.h | 34 ++++++++++++
2 files changed, 145 insertions(+)
create mode 100644 tests/unit/i2c_tester.c
create mode 100644 tests/unit/i2c_tester.h
diff --git a/tests/unit/i2c_tester.c b/tests/unit/i2c_tester.c
new file mode 100644
index 0000000000..ea36a17f1f
--- /dev/null
+++ b/tests/unit/i2c_tester.c
@@ -0,0 +1,111 @@
+/*
+ * Simple I2C peripheral for testing I2C device models.
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "i2c_tester.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static void i2c_tester_reset(DeviceState *ds)
+{
+ I2cTesterState *s = I2C_TESTER(ds);
+
+ s->set_reg_idx = false;
+ s->reg_idx = 0;
+ memset(s->regs, 0, I2C_TESTER_NUM_REGS);
+}
+
+static int i2c_tester_event(I2CSlave *i2c, enum i2c_event event)
+{
+ I2cTesterState *s = I2C_TESTER(i2c);
+
+ if (event == I2C_START_SEND) {
+ s->set_reg_idx = true;
+ }
+
+ return 0;
+}
+
+static uint8_t i2c_tester_rx(I2CSlave *i2c)
+{
+ I2cTesterState *s = I2C_TESTER(i2c);
+
+ if (s->reg_idx >= I2C_TESTER_NUM_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid reg 0x%02x\n", __func__,
+ s->reg_idx);
+ return I2C_NACK;
+ }
+
+ return s->regs[s->reg_idx];
+}
+
+static int i2c_tester_tx(I2CSlave *i2c, uint8_t data)
+{
+ I2cTesterState *s = I2C_TESTER(i2c);
+
+ if (s->set_reg_idx) {
+ /* Setting the register in which the operation will be done. */
+ s->reg_idx = data;
+ s->set_reg_idx = false;
+ return 0;
+ }
+
+ if (s->reg_idx >= I2C_TESTER_NUM_REGS) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid reg 0x%02x\n", __func__,
+ s->reg_idx);
+ return I2C_NACK;
+ }
+
+ /* Write reg data. */
+ s->regs[s->reg_idx] = data;
+
+ return 0;
+}
+
+static void i2c_tester_init(Object *obj)
+{
+}
+
+static void i2c_tester_realize(DeviceState *ds, Error **errp)
+{
+}
+
+static void i2c_tester_unrealize(DeviceState *dev)
+{
+}
+
+static void i2c_tester_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
+
+ dc->reset = i2c_tester_reset;
+ dc->realize = i2c_tester_realize;
+ dc->unrealize = i2c_tester_unrealize;
+
+ isc->event = i2c_tester_event;
+ isc->recv = i2c_tester_rx;
+ isc->send = i2c_tester_tx;
+}
+
+static const TypeInfo i2c_tester_info = {
+ .name = TYPE_I2C_TESTER,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(I2cTesterState),
+ .instance_init = i2c_tester_init,
+ .class_init = i2c_tester_class_init
+};
+
+static void i2c_tester_register_type(void)
+{
+ type_register_static(&i2c_tester_info);
+}
+
+type_init(i2c_tester_register_type);
diff --git a/tests/unit/i2c_tester.h b/tests/unit/i2c_tester.h
new file mode 100644
index 0000000000..9eebe1b6e3
--- /dev/null
+++ b/tests/unit/i2c_tester.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef TESTS_UNIT_I2C_TESTER_H
+#define TESTS_UNIT_I2C_TESTER_H
+
+#include "qemu/osdep.h"
+#include "hw/i2c/i2c.h"
+#include "hw/irq.h"
+
+#define I2C_TESTER_NUM_REGS 0x31
+
+#define TYPE_I2C_TESTER "i2c_tester"
+#define I2C_TESTER(obj) OBJECT_CHECK(I2cTesterState, (obj), TYPE_I2C_TESTER)
+
+typedef struct {
+ /* <private> */
+ I2CSlave i2c;
+
+ /* <public> */
+ bool set_reg_idx;
+
+ uint8_t reg_idx;
+ uint8_t regs[I2C_TESTER_NUM_REGS];
+} I2cTesterState;
+
+#endif /* TESTS_UNIT_I2C_TESTER_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 14/23] test/unit: add unit tests for flexcomm i2c
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (12 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 13/23] test/unit: add i2c-tester Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 15/23] hw/ssi: add support for flexcomm spi Octavian Purdila
` (9 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add master mode tests for flexcomm i2c.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 10 ++
tests/unit/test-flexcomm-i2c.c | 209 +++++++++++++++++++++++++++++++++
2 files changed, 219 insertions(+)
create mode 100644 tests/unit/test-flexcomm-i2c.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 4c22cb3ccc..3491e2003b 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -160,6 +160,16 @@ if have_system
meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
meson.project_source_root() / 'hw/i2c/core.c',
],
+ 'test-flexcomm-i2c': [
+ hwcore, chardev, qom, migration,
+ meson.project_source_root() / 'hw/core/gpio.c',
+ meson.project_source_root() / 'tests/unit/sysbus-mock.c',
+ meson.project_source_root() / 'hw/misc/flexcomm.c',
+ meson.project_source_root() / 'hw/char/flexcomm_usart.c',
+ meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
+ meson.project_source_root() / 'hw/i2c/core.c',
+ 'i2c_tester.c',
+ ],
}
if config_host_data.get('CONFIG_INOTIFY1')
tests += {'test-util-filemonitor': []}
diff --git a/tests/unit/test-flexcomm-i2c.c b/tests/unit/test-flexcomm-i2c.c
new file mode 100644
index 0000000000..9d02c94101
--- /dev/null
+++ b/tests/unit/test-flexcomm-i2c.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2024 Google LLC.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/config-file.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+#include "qemu/option.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-core.h"
+
+#include "hw/i2c/flexcomm_i2c.h"
+#include "i2c_tester.h"
+#include "sysbus-mock.h"
+#include "reg-utils.h"
+
+#define PERIPH_ADDR (0x20)
+#define INVALID_ADDR (0x10)
+
+#define REG_ADDR 0x11
+#define REG_VALUE 0xAA
+
+#define FLEXCOMM_BASE 0x40106000UL
+#define FLEXCOMM_I2C_BASE FLEXCOMM_BASE
+
+typedef struct {
+ DeviceState *dev;
+ I2CSlave *periph;
+ bool irq;
+} TestFixture;
+
+/* Callback for the interrupt line. */
+static void spi_irq_set(void *opaque, int line, int level)
+{
+ TestFixture *f = (TestFixture *)opaque;
+
+ f->irq = level;
+}
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ FlexcommState *s;
+
+ f->dev = qdev_new(TYPE_FLEXCOMM);
+ g_assert(f->dev);
+
+ s = FLEXCOMM(f->dev);
+ s->irq = qemu_allocate_irq(spi_irq_set, f, 0);
+
+ if (data != NULL) {
+ qdev_prop_set_int32(DEVICE(f->dev), "functions", (uintptr_t)data);
+ }
+
+ qdev_realize_and_unref(f->dev, NULL, &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->dev), 0, FLEXCOMM_BASE);
+
+ device_cold_reset(f->dev);
+
+ f->periph = i2c_slave_create_simple(s->i2c, TYPE_I2C_TESTER, PERIPH_ADDR);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ qdev_unrealize(f->dev);
+ qdev_unrealize(DEVICE(f->periph));
+}
+
+static void master_test(TestFixture *f, gconstpointer user_data)
+{
+ uint32_t tmp;
+
+ /* Select and lock I2C */
+ tmp = FLEXCOMM_PSELID_LOCK_Msk | FLEXCOMM_PERSEL_I2C;
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, tmp);
+
+ /* Enable master mode */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, CFG, MSTEN, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, CFG, MSTEN) == 1);
+
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTPENDING) == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_IDLE);
+
+ /* Enable interrupts */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, INTENSET, MSTPENDINGEN, 1);
+ g_assert(f->irq == true);
+
+ /* start for invalid address */
+ REG32_WRITE(f->dev, FLEXCOMM_I2C, MSTDAT, INVALID_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_NAKADR);
+ g_assert(f->irq == true);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+
+ /* write past the last register */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, PERIPH_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA,
+ (I2C_TESTER_NUM_REGS + 10));
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_NAKDAT);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+
+ /* write value to register */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, PERIPH_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, REG_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, REG_VALUE);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_IDLE);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+
+ /* read value back from register */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, PERIPH_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, REG_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_TXRDY);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, (PERIPH_ADDR + 1));
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+ g_assert(f->irq == true);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_RXRDY);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTCONTINUE, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA) == REG_VALUE);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+
+ /*
+ * Check that the master ended the transaction (i.e. i2c_end_transfer was
+ * called). If the master does not properly end the transaction this would
+ * be seen as a restart and it would not be NACKed.
+ */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, MSTDAT, DATA, INVALID_ADDR);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTART, 1);
+
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_I2C, STAT, MSTSTATE) ==
+ MSTSTATE_NAKADR);
+ g_assert(f->irq == true);
+ REG32_WRITE_FIELD_NOUPDATE(f->dev, FLEXCOMM_I2C, MSTCTL, MSTSTOP, 1);
+
+ /* Disable interrupts */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_I2C, INTENCLR, MSTPENDINGCLR, 1);
+ g_assert(f->irq == false);
+}
+
+/* mock-up */
+const PropertyInfo qdev_prop_chr;
+
+int main(int argc, char **argv)
+{
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+
+ /* Initialize object types. */
+ sysbus_mock_init();
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_chardev_opts);
+
+ g_test_add("/flexcomm-i2c/master", TestFixture,
+ (gconstpointer)(1 << FLEXCOMM_FUNC_I2C),
+ set_up, master_test, tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 15/23] hw/ssi: add support for flexcomm spi
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (13 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 14/23] test/unit: add unit tests for flexcomm i2c Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 16/23] test/unit: add spi-tester Octavian Purdila
` (8 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
From: Sebastian Ene <sebastianene@google.com>
From: Sebastian Ene <sebastianene@google.com>
Add support for NXP's flexcomm spi. It supports FIFO access,
interrupts and master mode only. It does not support DMA.
Signed-off-by: Sebastian Ene <sebastianene@google.com>
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 4 +
hw/misc/flexcomm.c | 6 +
hw/ssi/flexcomm_spi.c | 443 ++++++++++++++++++++++++++++++++++
hw/ssi/meson.build | 1 +
hw/ssi/trace-events | 8 +
include/hw/misc/flexcomm.h | 8 +
include/hw/ssi/flexcomm_spi.h | 20 ++
tests/unit/meson.build | 7 +
8 files changed, 497 insertions(+)
create mode 100644 hw/ssi/flexcomm_spi.c
create mode 100644 include/hw/ssi/flexcomm_spi.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index fa0d3da829..8b3b045137 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -10,3 +10,7 @@ genh += custom_target('flexcomm_i2c_regs.h',
output: 'flexcomm_i2c.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'I2C0', '-t', 'FLEXCOMM_I2C'])
+genh += custom_target('flexcomm_spi.h',
+ output: 'flexcomm_spi.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'SPI0', '-t', 'FLEXCOMM_SPI'])
diff --git a/hw/misc/flexcomm.c b/hw/misc/flexcomm.c
index 2722c1d6a9..6f3ce62448 100644
--- a/hw/misc/flexcomm.c
+++ b/hw/misc/flexcomm.c
@@ -24,6 +24,7 @@
#include "hw/misc/flexcomm.h"
#include "hw/char/flexcomm_usart.h"
#include "hw/i2c/flexcomm_i2c.h"
+#include "hw/ssi/flexcomm_spi.h"
#define reg(field) offsetof(FLEXCOMM_Type, field)
#define regi(x) (reg(x) / sizeof(uint32_t))
@@ -233,6 +234,10 @@ static void flexcomm_realize(DeviceState *dev, Error **errp)
if (has_function(s, FLEXCOMM_FUNC_I2C)) {
flexcomm_i2c_init(s);
}
+
+ if (has_function(s, FLEXCOMM_FUNC_SPI)) {
+ flexcomm_spi_init(s);
+ }
}
static void flexcomm_class_init(ObjectClass *klass, void *data)
@@ -245,6 +250,7 @@ static void flexcomm_class_init(ObjectClass *klass, void *data)
flexcomm_usart_register();
flexcomm_i2c_register();
+ flexcomm_spi_register();
}
static const TypeInfo flexcomm_info = {
diff --git a/hw/ssi/flexcomm_spi.c b/hw/ssi/flexcomm_spi.c
new file mode 100644
index 0000000000..7b649ba17e
--- /dev/null
+++ b/hw/ssi/flexcomm_spi.c
@@ -0,0 +1,443 @@
+/*
+ * QEMU model for NXP's FLEXCOMM SPI
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/cutils.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/regs.h"
+#include "hw/ssi/flexcomm_spi.h"
+
+#define reg(field) offsetof(FLEXCOMM_SPI_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(FLEXCOMM_SPI_Type) / sizeof(uint32_t))
+
+#define FLEXCOMM_SSEL_ASSERTED (0)
+#define FLEXCOMM_SSEL_DEASSERTED (1)
+
+#define FLEXCOMM_SPI_FIFOWR_LEN_MIN (3)
+#define FLEXCOMM_SPI_FIFOWR_LEN_MAX (15)
+
+static FLEXCOMM_SPI_REGISTER_NAMES_ARRAY(reg_names);
+
+static void flexcomm_spi_reset(FlexcommState *s)
+{
+ flexcomm_spi_reset_registers(&s->regs.spi);
+ s->regs.spi.FIFOSIZE_b.FIFOSIZE = 0x8;
+}
+
+static void update_fifo_stat(FlexcommState *s)
+{
+ int rxlvl = fifo32_num_used(&s->rx_fifo);
+ int txlvl = fifo32_num_used(&s->tx_fifo);
+
+ s->regs.spi.FIFOSTAT_b.RXLVL = fifo32_num_used(&s->rx_fifo);
+ s->regs.spi.FIFOSTAT_b.TXLVL = fifo32_num_used(&s->tx_fifo);
+ s->regs.spi.FIFOSTAT_b.RXFULL = fifo32_is_full(&s->rx_fifo) ? 1 : 0;
+ s->regs.spi.FIFOSTAT_b.RXNOTEMPTY = !fifo32_is_empty(&s->rx_fifo) ? 1 : 0;
+ s->regs.spi.FIFOSTAT_b.TXNOTFULL = !fifo32_is_full(&s->tx_fifo) ? 1 : 0;
+ s->regs.spi.FIFOSTAT_b.TXEMPTY = fifo32_is_empty(&s->tx_fifo) ? 1 : 0;
+
+ if (s->regs.spi.FIFOTRIG_b.RXLVLENA &&
+ (rxlvl > s->regs.spi.FIFOTRIG_b.RXLVL)) {
+ s->regs.spi.FIFOINTSTAT_b.RXLVL = 1;
+ } else {
+ s->regs.spi.FIFOINTSTAT_b.RXLVL = 0;
+ }
+
+ if (s->regs.spi.FIFOTRIG_b.TXLVLENA &&
+ (txlvl <= s->regs.spi.FIFOTRIG_b.TXLVL)) {
+ s->regs.spi.FIFOINTSTAT_b.TXLVL = 1;
+ } else {
+ s->regs.spi.FIFOINTSTAT_b.TXLVL = 0;
+ }
+
+ trace_flexcomm_spi_fifostat(DEVICE(s)->id, s->regs.spi.FIFOSTAT,
+ s->regs.spi.FIFOINTSTAT);
+}
+
+static void flexcomm_spi_irq_update(FlexcommState *s)
+{
+ bool irq, per_irqs, fifo_irqs, enabled = s->regs.spi.CFG_b.ENABLE;
+
+ update_fifo_stat(s);
+ fifo_irqs = s->regs.spi.FIFOINTSTAT & s->regs.spi.FIFOINTENSET;
+
+ s->regs.spi.INTSTAT = s->regs.spi.STAT & s->regs.spi.INTENSET;
+ per_irqs = s->regs.spi.INTSTAT != 0;
+
+ irq = enabled && (fifo_irqs || per_irqs);
+
+ trace_flexcomm_spi_irq(DEVICE(s)->id, irq, fifo_irqs, per_irqs, enabled);
+ flexcomm_irq(s, irq);
+}
+
+static void flexcomm_spi_select(void *opaque, FlexcommState *s, int f,
+ bool set)
+{
+ if (set) {
+ int i;
+
+ flexcomm_spi_reset(s);
+ fifo32_create(&s->rx_fifo, s->regs.spi.FIFOSIZE_b.FIFOSIZE);
+ fifo32_create(&s->tx_fifo, s->regs.spi.FIFOSIZE_b.FIFOSIZE);
+ for (i = 0; i < ARRAY_SIZE(s->cs); i++) {
+ bool spol = s->regs.spi.CFG & (FLEXCOMM_SPI_CFG_SPOL0_Msk << i);
+
+ s->cs_asserted[i] = false;
+ qemu_set_irq(s->cs[i], !spol);
+ }
+ } else {
+ fifo32_destroy(&s->rx_fifo);
+ fifo32_destroy(&s->tx_fifo);
+ }
+}
+
+static MemTxResult flexcomm_spi_reg_read(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t *data,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+
+ /*
+ * Allow 8/16 bits access to the FIFORD LSB half-word. This is supported by
+ * hardware and required for 1/2 byte(s) width DMA transfers.
+ */
+ if (!reg32_aligned_access(addr, size) && addr != reg(FIFORD)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(FIFORD):
+ {
+ /* If we are running in loopback mode get the data from TX FIFO */
+ if (s->regs.spi.CFG_b.LOOP &&
+ s->regs.spi.CFG_b.MASTER)
+ {
+ if (!fifo32_is_empty(&s->tx_fifo)) {
+ *data = fifo32_pop(&s->tx_fifo);
+ }
+ break;
+ }
+
+ if (!fifo32_is_empty(&s->rx_fifo)) {
+ *data = fifo32_pop(&s->rx_fifo);
+ qemu_chr_fe_accept_input(&s->chr);
+ }
+ break;
+ }
+ case reg(FIFORDNOPOP):
+ {
+ if (!fifo32_is_empty(&s->rx_fifo)) {
+ *data = fifo32_peek(&s->rx_fifo);
+ }
+ break;
+ }
+ default:
+ *data = reg32_read(&s->regs, addr);
+ break;
+ }
+
+ flexcomm_spi_irq_update(s);
+
+out:
+ trace_flexcomm_spi_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static uint32_t fifowr_len_bits(uint32_t val)
+{
+ int len = (val & FLEXCOMM_SPI_FIFOWR_LEN_Msk) >>
+ FLEXCOMM_SPI_FIFOWR_LEN_Pos;
+
+ if (len < FLEXCOMM_SPI_FIFOWR_LEN_MIN ||
+ len > FLEXCOMM_SPI_FIFOWR_LEN_MAX) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid spi xfer len %d\n",
+ __func__, val);
+ return 0;
+ }
+
+ return len + 1;
+}
+
+static inline uint32_t fifowr_len_mask(uint32_t val)
+{
+ return (1 << fifowr_len_bits(val)) - 1;
+}
+
+static inline uint32_t fifowr_len_bytes(uint32_t val)
+{
+ return fifowr_len_bits(val) > 8 ? 2 : 1;
+}
+
+static uint32_t flexcomm_spi_xfer_word(FlexcommState *s,
+ uint32_t out_data,
+ int bytes,
+ bool be)
+{
+ uint32_t word = 0;
+ int i;
+ int out = 0;
+
+ for (i = 0; i < bytes; i++) {
+ if (be) {
+ int byte_offset = bytes - i - 1;
+ out = (out_data & (0xFF << byte_offset * 8)) >> byte_offset * 8;
+ word |= ssi_transfer(s->spi, out) << byte_offset * 8;
+ } else {
+ out = (out_data & (0xFF << i * 8)) >> i * 8;
+ word |= ssi_transfer(s->spi, out) << i * 8;
+ }
+ }
+
+ return word;
+}
+
+static uint32_t flexcomm_spi_get_ss_mask(FlexcommState *s,
+ uint32_t txfifo_val)
+{
+ uint32_t slave_select_mask = 0;
+ for (int i = 0; i < ARRAY_SIZE(s->cs); i++) {
+ int tx_ss_pos = FLEXCOMM_SPI_FIFOWR_TXSSEL0_N_Pos + i;
+ uint32_t state = (txfifo_val & (1 << tx_ss_pos)) >> tx_ss_pos;
+ bool spol = s->regs.spi.CFG & (FLEXCOMM_SPI_CFG_SPOL0_Msk << i);
+ int irq_level = state ? spol : !spol;
+
+ slave_select_mask |= (state << i);
+ s->cs_asserted[i] = state;
+ qemu_set_irq(s->cs[i], irq_level);
+ }
+
+ return slave_select_mask;
+}
+
+static MemTxResult flexcomm_spi_reg_write(void *opaque, FlexcommState *s,
+ int f, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t mask[REG_NO] = {
+ [regi(CFG)] = BITS(11, 7) | BITS(5, 2) | BIT(0),
+ [regi(DLY)] = BITS(15, 0),
+ [regi(STAT)] = BIT(7) | BIT(5) | BIT(4),
+ [regi(INTENSET)] = BIT(8) | BITS(5, 4),
+ [regi(INTENCLR)] = BIT(8) | BITS(5, 4),
+ [regi(DIV)] = BITS(15, 0),
+ [regi(FIFOCFG)] = BITS(18, 12) | BITS(1, 0),
+ [regi(FIFOSTAT)] = BITS(1, 0),
+ [regi(FIFOTRIG)] = BITS(19, 16) | BITS(11, 8) | BITS(1, 0),
+ [regi(FIFOINTENSET)] = BITS(3, 0),
+ [regi(FIFOINTENCLR)] = BITS(3, 0),
+ [regi(FIFOWR)] = BITS(27, 0),
+ };
+
+ /*
+ * Allow 8/16 bits access to both the FIFOWR MSB and LSB half-words. The
+ * former is required for updating the control bits while the latter for DMA
+ * transfers of 1/2 byte(s) width.
+ */
+ if (!reg32_aligned_access(addr, size) && (addr / 4 * 4 != reg(FIFOWR))) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(CFG):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+
+ if (s->regs.spi.CFG_b.ENABLE) {
+ qemu_chr_fe_accept_input(&s->chr);
+ }
+
+ break;
+ }
+ case reg(INTENCLR):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ s->regs.spi.INTENSET &= ~s->regs.spi.INTENCLR;
+ break;
+ }
+ case reg(FIFOCFG):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ if (s->regs.spi.FIFOCFG_b.EMPTYRX) {
+ s->regs.spi.FIFOCFG_b.EMPTYRX = 0;
+ fifo32_reset(&s->rx_fifo);
+ }
+ if (s->regs.spi.FIFOCFG_b.EMPTYTX) {
+ s->regs.spi.FIFOCFG_b.EMPTYTX = 0;
+ fifo32_reset(&s->tx_fifo);
+ }
+ if (s->regs.spi.FIFOCFG_b.ENABLERX) {
+ qemu_chr_fe_accept_input(&s->chr);
+ }
+ break;
+ }
+ case reg(FIFOSTAT):
+ {
+ bool rxerr = s->regs.spi.FIFOSTAT_b.RXERR;
+ bool txerr = s->regs.spi.FIFOSTAT_b.TXERR;
+
+ reg32_write(&s->regs, addr, value, mask);
+
+ if (rxerr && s->regs.spi.FIFOSTAT_b.RXERR) {
+ rxerr = false;
+ }
+ if (txerr && s->regs.spi.FIFOSTAT_b.TXERR) {
+ txerr = false;
+ }
+
+ s->regs.spi.FIFOSTAT_b.RXERR = rxerr;
+ s->regs.spi.FIFOSTAT_b.TXERR = txerr;
+ break;
+ }
+ case reg(FIFOINTENSET):
+ {
+ s->regs.spi.FIFOINTENSET |= value & mask[addr / 4];
+ break;
+ }
+ case reg(FIFOINTENCLR):
+ {
+ reg32_write(&s->regs, addr, value, mask);
+ s->regs.spi.FIFOINTENSET &= ~s->regs.spi.FIFOINTENCLR;
+ break;
+ }
+ /* update control bits but don't push into the FIFO */
+ case reg(FIFOWR) + 2:
+ {
+ if (size > 2) {
+ ret = MEMTX_ERROR;
+ break;
+ }
+ if (value != 0) {
+ s->spi_tx_ctrl = value << 16;
+ }
+ break;
+ }
+ /* update control bits but don't push into the FIFO */
+ case reg(FIFOWR) + 3:
+ {
+ if (size > 1) {
+ ret = MEMTX_ERROR;
+ break;
+ }
+ if (value != 0) {
+ s->spi_tx_ctrl = value << 24;
+ }
+ break;
+ }
+ case reg(FIFOWR):
+ {
+ /* fifo value contains both data and control bits */
+ uint32_t txfifo_val;
+
+ if (size > 2 && (value & ~FLEXCOMM_SPI_FIFOWR_TXDATA_Msk) != 0) {
+ /* non-zero writes to control bits updates them */
+ s->spi_tx_ctrl = value & ~FLEXCOMM_SPI_FIFOWR_TXDATA_Msk;
+ txfifo_val = value;
+ } else {
+ /* otherwise reuse previous control bits */
+ txfifo_val = value | s->spi_tx_ctrl;
+ }
+
+ if (!fifo32_is_full(&s->tx_fifo)) {
+ fifo32_push(&s->tx_fifo, txfifo_val);
+ }
+
+ if (!s->regs.spi.CFG_b.ENABLE || !s->regs.spi.FIFOCFG_b.ENABLETX) {
+ break;
+ }
+
+ /*
+ * On loopback mode we just insert the values in the TX FIFO. On slave
+ * mode master needs to initiate the SPI transfer.
+ */
+ if (s->regs.spi.CFG_b.LOOP || !s->regs.spi.CFG_b.MASTER) {
+ break;
+ }
+
+ while (!fifo32_is_empty(&s->tx_fifo)) {
+ txfifo_val = fifo32_pop(&s->tx_fifo);
+
+ uint32_t ss_mask = flexcomm_spi_get_ss_mask(s, txfifo_val);
+ uint32_t data = txfifo_val & fifowr_len_mask(txfifo_val);
+ uint8_t bytes = fifowr_len_bytes(txfifo_val);
+ bool msb = !s->regs.spi.CFG_b.LSBF;
+ uint32_t val32;
+
+ val32 = flexcomm_spi_xfer_word(s, data, bytes, msb);
+
+ if (!fifo32_is_full(&s->rx_fifo)) {
+ /* Append the mask that informs which client is active */
+ val32 |= (ss_mask << FLEXCOMM_SPI_FIFORD_RXSSEL0_N_Pos);
+ fifo32_push(&s->rx_fifo, val32);
+ }
+
+ /* If this is the end of the transfer raise the CS line */
+ if (txfifo_val & FLEXCOMM_SPI_FIFOWR_EOT_Msk) {
+ bool spol[ARRAY_SIZE(s->cs)] = {
+ s->regs.spi.CFG_b.SPOL0,
+ s->regs.spi.CFG_b.SPOL1,
+ s->regs.spi.CFG_b.SPOL2,
+ s->regs.spi.CFG_b.SPOL3,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(s->cs); i++) {
+ if (s->cs_asserted[i]) {
+ s->cs_asserted[i] = false;
+ qemu_set_irq(s->cs[i], !spol[i]);
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ reg32_write(&s->regs, addr, value, mask);
+ break;
+ }
+
+ flexcomm_spi_irq_update(s);
+
+out:
+ trace_flexcomm_spi_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+ return ret;
+}
+
+static const FlexcommFunctionOps flexcomm_spi_ops = {
+ .select = flexcomm_spi_select,
+ .reg_read = flexcomm_spi_reg_read,
+ .reg_write = flexcomm_spi_reg_write,
+};
+
+void flexcomm_spi_init(FlexcommState *s)
+{
+ s->spi = ssi_create_bus(DEVICE(s), "spi");
+ qdev_init_gpio_out_named(DEVICE(s), &s->cs[0], "cs", ARRAY_SIZE(s->cs));
+}
+
+/* Register the SPI operations with the flexcomm upper layer */
+void flexcomm_spi_register(void)
+{
+ Error *err = NULL;
+
+ if (!flexcomm_register_ops(FLEXCOMM_FUNC_SPI, NULL,
+ &flexcomm_spi_ops, &err)) {
+ error_report_err(err);
+ }
+}
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index b999aeb027..57d3e14727 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_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_FLEXCOMM', if_true: files('flexcomm_spi.c'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 2d5bd2b83d..5caa1c17ac 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -32,3 +32,11 @@ ibex_spi_host_reset(const char *msg) "%s"
ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32
ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:"
+
+# flexcomm_spi.c
+flexcomm_spi_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
+flexcomm_spi_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
+flexcomm_spi_fifostat(const char *id, uint32_t fifostat, uint32_t fifoinstat) "%s: %08x %08x"
+flexcomm_spi_irq(const char *id, bool irq, bool fifoirqs, bool perirqs, bool enabled) "%s: %d %d %d %d"
+flexcomm_spi_chr_rx_space(const char *id, uint32_t rx) "%s: %d"
+flexcomm_spi_chr_rx(const char *id) "%s"
diff --git a/include/hw/misc/flexcomm.h b/include/hw/misc/flexcomm.h
index 3d042a3511..62f327925d 100644
--- a/include/hw/misc/flexcomm.h
+++ b/include/hw/misc/flexcomm.h
@@ -15,9 +15,12 @@
#include "hw/sysbus.h"
#include "chardev/char-fe.h"
#include "hw/i2c/i2c.h"
+#include "hw/ssi/ssi.h"
#include "hw/arm/svd/flexcomm.h"
#include "hw/arm/svd/flexcomm_usart.h"
#include "hw/arm/svd/flexcomm_i2c.h"
+#undef EOF
+#include "hw/arm/svd/flexcomm_spi.h"
#include "qemu/fifo32.h"
#define TYPE_FLEXCOMM "flexcomm"
@@ -49,6 +52,7 @@ typedef struct {
FLEXCOMM_Type flex;
FLEXCOMM_USART_Type usart;
FLEXCOMM_I2C_Type i2c;
+ FLEXCOMM_SPI_Type spi;
} regs;
uint32_t functions;
qemu_irq irq;
@@ -57,6 +61,10 @@ typedef struct {
Fifo32 tx_fifo;
Fifo32 rx_fifo;
I2CBus *i2c;
+ SSIBus *spi;
+ qemu_irq cs[4];
+ bool cs_asserted[4];
+ uint32_t spi_tx_ctrl;
} FlexcommState;
typedef struct {
diff --git a/include/hw/ssi/flexcomm_spi.h b/include/hw/ssi/flexcomm_spi.h
new file mode 100644
index 0000000000..d5567aa1e6
--- /dev/null
+++ b/include/hw/ssi/flexcomm_spi.h
@@ -0,0 +1,20 @@
+/*
+ * QEMU model for NXP's FLEXCOMM SPI
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_CHAR_FLEXCOMM_SPI_H
+#define HW_CHAR_FLEXCOMM_SPI_H
+
+#include "hw/misc/flexcomm.h"
+
+void flexcomm_spi_init(FlexcommState *s);
+void flexcomm_spi_register(void);
+
+#endif /* HW_CHAR_FLEXCOMM_SPI_H */
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 3491e2003b..1ddd174576 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -150,6 +150,8 @@ if have_system
meson.project_source_root() / 'hw/char/flexcomm_usart.c',
meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
meson.project_source_root() / 'hw/i2c/core.c',
+ meson.project_source_root() / 'hw/ssi/flexcomm_spi.c',
+ meson.project_source_root() / 'hw/ssi/ssi.c',
],
'test-flexcomm-usart': [
hwcore, chardev, qom, migration,
@@ -159,6 +161,8 @@ if have_system
meson.project_source_root() / 'hw/char/flexcomm_usart.c',
meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
meson.project_source_root() / 'hw/i2c/core.c',
+ meson.project_source_root() / 'hw/ssi/flexcomm_spi.c',
+ meson.project_source_root() / 'hw/ssi/ssi.c',
],
'test-flexcomm-i2c': [
hwcore, chardev, qom, migration,
@@ -169,6 +173,9 @@ if have_system
meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
meson.project_source_root() / 'hw/i2c/core.c',
'i2c_tester.c',
+ meson.project_source_root() / 'hw/ssi/flexcomm_spi.c',
+ meson.project_source_root() / 'hw/ssi/ssi.c',
+ ],
],
}
if config_host_data.get('CONFIG_INOTIFY1')
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 16/23] test/unit: add spi-tester
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (14 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 15/23] hw/ssi: add support for flexcomm spi Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 17/23] test/unit: add unit tests for flexcomm spi Octavian Purdila
` (7 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add a simple SPI peripheral that echoes back received data. Useful for
testing SPI controllers.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/spi_tester.c | 60 +++++++++++++++++++++++++++++++++++++++++
tests/unit/spi_tester.h | 32 ++++++++++++++++++++++
2 files changed, 92 insertions(+)
create mode 100644 tests/unit/spi_tester.c
create mode 100644 tests/unit/spi_tester.h
diff --git a/tests/unit/spi_tester.c b/tests/unit/spi_tester.c
new file mode 100644
index 0000000000..7bccc680cc
--- /dev/null
+++ b/tests/unit/spi_tester.c
@@ -0,0 +1,60 @@
+/*
+ * Simple SPI peripheral echo device used for SPI controller testing.
+ *
+ * Copyright (c) 2024 Google LLC.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "spi_tester.h"
+
+static uint32_t spi_tester_transfer(SSIPeripheral *dev, uint32_t value)
+{
+ SpiTesterState *s = SPI_TESTER(dev);
+
+ if (s->cs) {
+ return 0;
+ }
+
+ return value;
+}
+
+static void spi_tester_realize(SSIPeripheral *d, Error **errp)
+{
+}
+
+static int spi_tester_set_cs(SSIPeripheral *dev, bool select)
+{
+ SpiTesterState *s = SPI_TESTER(dev);
+
+ s->cs = select;
+
+ return 0;
+}
+
+static void spi_tester_class_init(ObjectClass *klass, void *data)
+{
+ SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
+
+ k->realize = spi_tester_realize;
+ k->transfer = spi_tester_transfer;
+ k->set_cs = spi_tester_set_cs;
+ k->cs_polarity = SSI_CS_LOW;
+}
+
+static const TypeInfo spi_tester_info = {
+ .name = TYPE_SPI_TESTER,
+ .parent = TYPE_SSI_PERIPHERAL,
+ .instance_size = sizeof(SpiTesterState),
+ .class_init = spi_tester_class_init,
+};
+
+static void spi_tester_register_types(void)
+{
+ type_register_static(&spi_tester_info);
+}
+
+type_init(spi_tester_register_types)
diff --git a/tests/unit/spi_tester.h b/tests/unit/spi_tester.h
new file mode 100644
index 0000000000..16e08d2b5c
--- /dev/null
+++ b/tests/unit/spi_tester.h
@@ -0,0 +1,32 @@
+/*
+ * Simple SPI peripheral device used for SPI controller testing.
+ *
+ * Copyright (c) 2024 Google LLC.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef TESTS_UNIT_SPI_TESTER_H
+#define TESTS_UNIT_SPI_TESTER_H
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bswap.h"
+#include "hw/irq.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/timer.h"
+#include "hw/qdev-properties.h"
+
+#define TYPE_SPI_TESTER "spi-tester"
+#define SPI_TESTER(obj) OBJECT_CHECK(SpiTesterState, (obj), TYPE_SPI_TESTER)
+
+typedef struct {
+ SSIPeripheral ssidev;
+ bool cs;
+} SpiTesterState;
+
+#endif /* TESTS_UNIT_SPI_TESTER_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 17/23] test/unit: add unit tests for flexcomm spi
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (15 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 16/23] test/unit: add spi-tester Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 18/23] hw/misc: add support for RT500's clock controller Octavian Purdila
` (6 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
From: Sebastian Ene <sebastianene@google.com>
From: Sebastian Ene <sebastianene@google.com>
Add master and loopback tests for flexcomm spi.
Signed-off-by: Sebastian Ene <sebastianene@google.com>
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 11 ++
tests/unit/test-flexcomm-spi.c | 204 +++++++++++++++++++++++++++++++++
2 files changed, 215 insertions(+)
create mode 100644 tests/unit/test-flexcomm-spi.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 1ddd174576..7a28e7b521 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -176,6 +176,17 @@ if have_system
meson.project_source_root() / 'hw/ssi/flexcomm_spi.c',
meson.project_source_root() / 'hw/ssi/ssi.c',
],
+ 'test-flexcomm-spi': [
+ qom, hwcore, migration, chardev,
+ meson.project_source_root() / 'hw/core/gpio.c',
+ meson.project_source_root() / 'tests/unit/sysbus-mock.c',
+ meson.project_source_root() / 'hw/misc/flexcomm.c',
+ meson.project_source_root() / 'hw/char/flexcomm_usart.c',
+ meson.project_source_root() / 'hw/i2c/flexcomm_i2c.c',
+ meson.project_source_root() / 'hw/i2c/core.c',
+ meson.project_source_root() / 'hw/ssi/flexcomm_spi.c',
+ meson.project_source_root() / 'hw/ssi/ssi.c',
+ 'spi_tester.c',
],
}
if config_host_data.get('CONFIG_INOTIFY1')
diff --git a/tests/unit/test-flexcomm-spi.c b/tests/unit/test-flexcomm-spi.c
new file mode 100644
index 0000000000..4aaf511d70
--- /dev/null
+++ b/tests/unit/test-flexcomm-spi.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2024 Google LLC.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/config-file.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "sysemu/sysemu.h"
+#include "qemu/main-loop.h"
+#include "qemu/option.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-core.h"
+
+#include "hw/misc/flexcomm.h"
+#include "spi_tester.h"
+#include "sysbus-mock.h"
+#include "reg-utils.h"
+
+/* The number of words sent on the SPI in loopback mode. */
+#define SEQ_LOOPBACK_MODE (8)
+
+/* This value is used to set the cycle counter for the spi tester */
+#define SPI_TESTER_CONFIG (0x10)
+
+#define FLEXCOMM_BASE 0x40106000UL
+#define FLEXCOMM_SPI_BASE FLEXCOMM_BASE
+
+typedef struct {
+ DeviceState *dev;
+ DeviceState *periph;
+ bool irq;
+} TestFixture;
+
+/* Callback for the interrupt line. */
+static void spi_irq_set(void *opaque, int line, int level)
+{
+ TestFixture *f = (TestFixture *)opaque;
+
+ f->irq = level;
+}
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ FlexcommState *s;
+
+ f->dev = qdev_new(TYPE_FLEXCOMM);
+ g_assert(f->dev);
+
+ s = FLEXCOMM(f->dev);
+ s->irq = qemu_allocate_irq(spi_irq_set, f, 0);
+
+ if (data != NULL) {
+ qdev_prop_set_int32(DEVICE(f->dev), "functions", (uintptr_t)data);
+ }
+
+ qdev_realize_and_unref(f->dev, NULL, &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->dev), 0, FLEXCOMM_BASE);
+
+ device_cold_reset(f->dev);
+
+ f->periph = ssi_create_peripheral(s->spi, TYPE_SPI_TESTER);
+ s->cs[0] = qdev_get_gpio_in_named(f->periph, SSI_GPIO_CS, 0);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ qdev_unrealize(f->dev);
+ qdev_unrealize(DEVICE(f->periph));
+ g_free(f->dev);
+}
+
+static void configure_spi(TestFixture *f, bool master, bool is_loopback_mode)
+{
+ uint32_t tmp;
+
+ /* Select and lock SPI */
+ tmp = FLEXCOMM_PSELID_LOCK_Msk | FLEXCOMM_PERSEL_SPI;
+ REG32_WRITE(f->dev, FLEXCOMM, PSELID, tmp);
+
+ /* Disable the FIFO */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, ENABLE, 0);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, FIFOCFG, ENABLETX, 0);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, FIFOCFG, ENABLERX, 0);
+
+ if (is_loopback_mode) {
+ /* Set up SPI interface - loop mode, master mode */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, LOOP, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, CFG, LOOP) == 1);
+ }
+
+ if (master) {
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, MASTER, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, CFG, MASTER) == 1);
+ } else {
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, MASTER, 0);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, CFG, MASTER) == 0);
+ }
+
+ /* Enable the FIFO */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, FIFOCFG, ENABLETX, 1);
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, FIFOCFG, ENABLERX, 1);
+
+ /* Enable the SPI */
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, ENABLE, 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, CFG, ENABLE) == 1);
+}
+
+/* The SPI controller running in master mode can run in loopback mode for */
+/* internal testing. Transmit and receive lines are connected together. */
+static void loopback_test(TestFixture *f, gconstpointer user_data)
+{
+ int i;
+
+ configure_spi(f, true, true);
+
+ /* Write a sequence */
+ for (i = 0; i < SEQ_LOOPBACK_MODE; i++) {
+ REG32_WRITE(f->dev, FLEXCOMM_SPI, FIFOWR, i);
+ }
+
+ /* Read the sequence back */
+ for (i = 0; i < SEQ_LOOPBACK_MODE; i++) {
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFORD, RXDATA) == i);
+ }
+}
+
+static void spi_master_test(TestFixture *f, gconstpointer user_data)
+{
+ uint32_t tmp;
+
+ configure_spi(f, true, false);
+
+ REG32_WRITE_FIELD(f->dev, FLEXCOMM_SPI, CFG, LSBF, 1);
+
+ /* single 16bit word transfer */
+
+ tmp = 0x1122;
+ tmp |= FLEXCOMM_SPI_FIFOWR_EOT_Msk;
+ tmp |= FLEXCOMM_SPI_FIFOWR_TXSSEL0_N_Msk;
+ tmp |= (0xF << FLEXCOMM_SPI_FIFOWR_LEN_Pos);
+ REG32_WRITE(f->dev, FLEXCOMM_SPI, FIFOWR, tmp);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFOSTAT, RXNOTEMPTY) == 1);
+ g_assert_cmpuint(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFORD, RXDATA),
+ ==, 0x1122);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFOSTAT, RXNOTEMPTY) == 0);
+
+ /* multi word 8 bits transfer */
+
+ tmp = 0x11;
+ tmp |= FLEXCOMM_SPI_FIFOWR_TXSSEL0_N_Msk;
+ tmp |= (0x7 << FLEXCOMM_SPI_FIFOWR_LEN_Pos);
+ REG32_WRITE(f->dev, FLEXCOMM_SPI, FIFOWR, tmp);
+ tmp = 0x22;
+ tmp |= FLEXCOMM_SPI_FIFOWR_EOT_Msk;
+ tmp |= FLEXCOMM_SPI_FIFOWR_TXSSEL0_N_Msk;
+ tmp |= (0x7 << FLEXCOMM_SPI_FIFOWR_LEN_Pos);
+ REG32_WRITE(f->dev, FLEXCOMM_SPI, FIFOWR, tmp);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFOSTAT, RXNOTEMPTY) == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFORD, RXDATA) == 0x11);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFOSTAT, RXNOTEMPTY) == 1);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFORD, RXDATA) == 0x22);
+ g_assert(REG32_READ_FIELD(f->dev, FLEXCOMM_SPI, FIFOSTAT, RXNOTEMPTY) == 0);
+}
+
+
+/* mock-up */
+const PropertyInfo qdev_prop_chr;
+
+int main(int argc, char **argv)
+{
+ qemu_init_main_loop(&error_abort);
+ socket_init();
+
+ g_test_init(&argc, &argv, NULL);
+
+ /* Initialize object types. */
+ sysbus_mock_init();
+ module_call_init(MODULE_INIT_QOM);
+ qemu_add_opts(&qemu_chardev_opts);
+
+ g_test_add("/flexcomm-spi/loopback", TestFixture,
+ (gconstpointer)(1 << FLEXCOMM_FUNC_SPI),
+ set_up, loopback_test, tear_down);
+
+ g_test_add("/flexcomm-spi/master", TestFixture,
+ (gconstpointer)(1 << FLEXCOMM_FUNC_SPI),
+ set_up, spi_master_test, tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 18/23] hw/misc: add support for RT500's clock controller
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (16 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 17/23] test/unit: add unit tests for flexcomm spi Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 19/23] test/unit: add unit tests " Octavian Purdila
` (5 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
It supports system and audio PLL initialization and SYSTICK and
OSTIMER clock source selection.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 8 +
hw/misc/Kconfig | 6 +
hw/misc/meson.build | 2 +
hw/misc/rt500_clkctl0.c | 243 ++++++++++++++++++++++++++++++
hw/misc/rt500_clkctl1.c | 224 +++++++++++++++++++++++++++
hw/misc/trace-events | 8 +
include/hw/misc/rt500_clk_freqs.h | 18 +++
include/hw/misc/rt500_clkctl0.h | 37 +++++
include/hw/misc/rt500_clkctl1.h | 38 +++++
9 files changed, 584 insertions(+)
create mode 100644 hw/misc/rt500_clkctl0.c
create mode 100644 hw/misc/rt500_clkctl1.c
create mode 100644 include/hw/misc/rt500_clk_freqs.h
create mode 100644 include/hw/misc/rt500_clkctl0.h
create mode 100644 include/hw/misc/rt500_clkctl1.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index 8b3b045137..6ab13f8757 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -14,3 +14,11 @@ genh += custom_target('flexcomm_spi.h',
output: 'flexcomm_spi.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'SPI0', '-t', 'FLEXCOMM_SPI'])
+genh += custom_target('rt500_clkctl0.h',
+ output: 'rt500_clkctl0.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'CLKCTL0', '-t', 'RT500_CLKCTL0'])
+genh += custom_target('rt500_clkctl1.h',
+ output: 'rt500_clkctl1.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'CLKCTL1', '-t', 'RT500_CLKCTL1'])
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 14167ae9e8..392ae9e84f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -216,4 +216,10 @@ config XLNX_VERSAL_TRNG
config FLEXCOMM
bool
+config RT500_CLKCTL0
+ bool
+
+config RT500_CLKCTL1
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 8414767ae3..68929949a6 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -158,3 +158,5 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c'))
system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))
system_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm.c'))
+system_ss.add(when: 'CONFIG_RT500_CLKCTL0', if_true: files('rt500_clkctl0.c'))
+system_ss.add(when: 'CONFIG_RT500_CLKCTL1', if_true: files('rt500_clkctl1.c'))
diff --git a/hw/misc/rt500_clkctl0.c b/hw/misc/rt500_clkctl0.c
new file mode 100644
index 0000000000..84b5631924
--- /dev/null
+++ b/hw/misc/rt500_clkctl0.c
@@ -0,0 +1,243 @@
+/*
+ * QEMU model for RT500 Clock Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/clock.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "hw/regs.h"
+#include "hw/misc/rt500_clkctl0.h"
+#include "hw/misc/rt500_clk_freqs.h"
+
+#include "trace.h"
+
+#define reg(field) offsetof(RT500_CLKCTL0_Type, field)
+#define REG_NO (sizeof(RT500_CLKCTL0_Type) / sizeof(uint32_t))
+#define regi(x) (reg(x) / sizeof(uint32_t))
+
+static RT500_CLKCTL0_REGISTER_NAMES_ARRAY(reg_names);
+
+static MemTxResult rt500_clkctl0_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500ClkCtl0State *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(PSCCTL0_SET):
+ case reg(PSCCTL1_SET):
+ case reg(PSCCTL2_SET):
+ case reg(PSCCTL0_CLR):
+ case reg(PSCCTL1_CLR):
+ case reg(PSCCTL2_CLR):
+ /* write only registers */
+ ret = MEMTX_ERROR;
+ break;
+ default:
+ *data = reg32_read(&s->regs, addr);
+ break;
+ }
+
+out:
+ trace_rt500_clkctl0_reg_read(reg_names[addr], addr, *data);
+ return ret;
+}
+
+static inline void set_systick_clk_from_div(RT500ClkCtl0State *s)
+{
+ uint32_t div = s->regs.SYSTICKFCLKDIV_b.DIV + 1;
+ uint32_t rate = clock_get_hz(s->sysclk);
+
+ clock_set_hz(s->systick_clk, rate / div);
+}
+
+static MemTxResult rt500_clkctl0_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500ClkCtl0State *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t wr_mask[REG_NO] = {
+ [0 ... REG_NO - 1] = BITS(31, 0),
+ [regi(FRO_CAPVAL)] = 0,
+ [regi(FROCLKSTATUS)] = 0,
+ };
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(PSCCTL0):
+ case reg(PSCCTL1):
+ case reg(PSCCTL2):
+ {
+ reg32_write(&s->regs, addr, value, wr_mask);
+ break;
+ }
+ case reg(PSCCTL0_SET):
+ case reg(PSCCTL1_SET):
+ case reg(PSCCTL2_SET):
+ {
+ uint32_t update_addr = reg(PSCCTL0) + (addr - reg(PSCCTL0_SET));
+ reg32_write(&s->regs, update_addr,
+ reg32_read(&s->regs, update_addr) | value, wr_mask);
+ break;
+ }
+ case reg(PSCCTL0_CLR):
+ case reg(PSCCTL1_CLR):
+ case reg(PSCCTL2_CLR):
+ {
+ uint32_t update_addr = reg(PSCCTL0) + (addr - reg(PSCCTL0_CLR));
+ reg32_write(&s->regs, update_addr,
+ reg32_read(&s->regs, update_addr) & ~value, wr_mask);
+ break;
+ }
+ default:
+ reg32_write(&s->regs, addr, value, wr_mask);
+ }
+
+ switch (addr) {
+ case reg(SYSPLL0PFD):
+ {
+ if (!s->regs.SYSPLL0PFD_b.PFD0_CLKGATE) {
+ s->regs.SYSPLL0PFD_b.PFD0_CLKRDY = 1;
+ } else {
+ s->regs.SYSPLL0PFD_b.PFD0_CLKRDY = 0;
+ }
+ if (!s->regs.SYSPLL0PFD_b.PFD1_CLKGATE) {
+ s->regs.SYSPLL0PFD_b.PFD1_CLKRDY = 1;
+ } else {
+ s->regs.SYSPLL0PFD_b.PFD1_CLKRDY = 0;
+ }
+ if (!s->regs.SYSPLL0PFD_b.PFD2_CLKGATE) {
+ s->regs.SYSPLL0PFD_b.PFD2_CLKRDY = 1;
+ } else {
+ s->regs.SYSPLL0PFD_b.PFD2_CLKRDY = 0;
+ }
+ if (!s->regs.SYSPLL0PFD_b.PFD3_CLKGATE) {
+ s->regs.SYSPLL0PFD_b.PFD3_CLKRDY = 1;
+ } else {
+ s->regs.SYSPLL0PFD_b.PFD3_CLKRDY = 0;
+ }
+ break;
+ }
+ case reg(SYSTICKFCLKSEL):
+ {
+ switch (s->regs.SYSTICKFCLKSEL_b.SEL) {
+ case SYSTICKFCLKSEL_DIVOUT:
+ {
+ set_systick_clk_from_div(s);
+ break;
+ }
+ case SYSTICKFCLKSEL_LPOSC:
+ {
+ clock_set_hz(s->systick_clk, LPOSC_CLK_HZ);
+ break;
+ }
+ case SYSTICKFCLKSEL_32KHZRTC:
+ {
+ clock_set_hz(s->systick_clk, RTC32KHZ_CLK_HZ);
+ break;
+ }
+ case SYSTICKFCLKSEL_NONE:
+ {
+ clock_set_hz(s->systick_clk, 0);
+ break;
+ }
+ }
+ clock_propagate(s->systick_clk);
+ break;
+ }
+ case reg(SYSTICKFCLKDIV):
+ {
+ if (s->regs.SYSTICKFCLKSEL_b.SEL == SYSTICKFCLKSEL_DIVOUT) {
+ set_systick_clk_from_div(s);
+ clock_propagate(s->systick_clk);
+ }
+ break;
+ }
+ }
+
+out:
+ trace_rt500_clkctl0_reg_write(reg_names[addr], addr, value);
+ return ret;
+
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps rt500_clkctl0_ops = {
+ .read_with_attrs = rt500_clkctl0_read,
+ .write_with_attrs = rt500_clkctl0_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void rt500_clkctl0_reset(DeviceState *dev)
+{
+ RT500ClkCtl0State *s = RT500_CLKCTL0(dev);
+
+ memset(&s->regs, 0, sizeof(s->regs));
+
+ rt500_clkctl0_reset_registers(&s->regs);
+
+ /* clock OK immediately after reset */
+ s->regs.FROCLKSTATUS = 0x00000001;
+}
+
+static void rt500_clkctl0_init(Object *obj)
+{
+ RT500ClkCtl0State *s = RT500_CLKCTL0(obj);
+
+ memory_region_init_io(&s->mmio, obj, &rt500_clkctl0_ops, s,
+ TYPE_RT500_CLKCTL0, sizeof(s->regs));
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+ s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+ s->systick_clk = qdev_init_clock_out(DEVICE(s), "systick_clk");
+}
+
+static void rt500_clkctl0_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void rt500_clkctl0_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = rt500_clkctl0_reset;
+ dc->realize = rt500_clkctl0_realize;
+}
+
+static const TypeInfo rt500_clkctl0_info = {
+ .name = TYPE_RT500_CLKCTL0,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RT500ClkCtl0State),
+ .instance_init = rt500_clkctl0_init,
+ .class_init = rt500_clkctl0_class_init,
+};
+
+static void rt500_clkctl0_register_types(void)
+{
+ type_register_static(&rt500_clkctl0_info);
+}
+
+type_init(rt500_clkctl0_register_types)
+
diff --git a/hw/misc/rt500_clkctl1.c b/hw/misc/rt500_clkctl1.c
new file mode 100644
index 0000000000..a5a8cf6564
--- /dev/null
+++ b/hw/misc/rt500_clkctl1.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU model for RT500 Clock Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/clock.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "hw/regs.h"
+#include "hw/misc/rt500_clkctl1.h"
+#include "hw/misc/rt500_clk_freqs.h"
+
+#include "trace.h"
+
+#define reg(field) offsetof(RT500_CLKCTL1_Type, field)
+#define REG_NO (sizeof(RT500_CLKCTL1_Type) / sizeof(uint32_t))
+#define regi(x) (reg(x) / sizeof(uint32_t))
+
+static RT500_CLKCTL1_REGISTER_NAMES_ARRAY(reg_names);
+
+static MemTxResult rt500_clkctl1_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500ClkCtl1State *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(PSCCTL0_SET):
+ case reg(PSCCTL1_SET):
+ case reg(PSCCTL2_SET):
+ case reg(PSCCTL0_CLR):
+ case reg(PSCCTL1_CLR):
+ case reg(PSCCTL2_CLR):
+ /* write only registers */
+ ret = MEMTX_ERROR;
+ break;
+ default:
+ *data = reg32_read(&s->regs, addr);
+ break;
+ }
+
+out:
+ trace_rt500_clkctl1_reg_read(reg_names[addr], addr, *data);
+ return ret;
+}
+
+static MemTxResult rt500_clkctl1_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500ClkCtl1State *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+ static uint32_t wr_mask[REG_NO] = {
+ [0 ... REG_NO - 1] = BITS(31, 0),
+ };
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(PSCCTL0):
+ case reg(PSCCTL1):
+ case reg(PSCCTL2):
+ {
+ reg32_write(&s->regs, addr, value, wr_mask);
+ break;
+ }
+ case reg(PSCCTL0_SET):
+ case reg(PSCCTL1_SET):
+ case reg(PSCCTL2_SET):
+ {
+ uint32_t update_addr = reg(PSCCTL0) + (addr - reg(PSCCTL0_SET));
+ reg32_write(&s->regs, update_addr,
+ reg32_read(&s->regs, update_addr) | value, wr_mask);
+ break;
+ }
+ case reg(PSCCTL0_CLR):
+ case reg(PSCCTL1_CLR):
+ case reg(PSCCTL2_CLR):
+ {
+ uint32_t update_addr = reg(PSCCTL0) + (addr - reg(PSCCTL0_CLR));
+ reg32_write(&s->regs, update_addr,
+ reg32_read(&s->regs, update_addr) & ~value, wr_mask);
+ break;
+ }
+ default:
+ reg32_write(&s->regs, addr, value, wr_mask);
+ }
+
+ switch (addr) {
+ case reg(AUDIOPLL0PFD):
+ {
+ if (!s->regs.AUDIOPLL0PFD_b.PFD0_CLKGATE) {
+ s->regs.AUDIOPLL0PFD_b.PFD0_CLKRDY = 1;
+ } else {
+ s->regs.AUDIOPLL0PFD_b.PFD0_CLKRDY = 0;
+ }
+ if (!s->regs.AUDIOPLL0PFD_b.PFD1_CLKGATE) {
+ s->regs.AUDIOPLL0PFD_b.PFD1_CLKRDY = 1;
+ } else {
+ s->regs.AUDIOPLL0PFD_b.PFD1_CLKRDY = 0;
+ }
+ if (!s->regs.AUDIOPLL0PFD_b.PFD2_CLKGATE) {
+ s->regs.AUDIOPLL0PFD_b.PFD2_CLKRDY = 1;
+ } else {
+ s->regs.AUDIOPLL0PFD_b.PFD2_CLKRDY = 0;
+ }
+ if (!s->regs.AUDIOPLL0PFD_b.PFD3_CLKGATE) {
+ s->regs.AUDIOPLL0PFD_b.PFD3_CLKRDY = 1;
+ } else {
+ s->regs.AUDIOPLL0PFD_b.PFD3_CLKRDY = 0;
+ }
+ break;
+ }
+ case reg(OSEVENTTFCLKSEL):
+ {
+ switch (s->regs.OSEVENTTFCLKSEL_b.SEL) {
+ case OSEVENTTFCLKSEL_LPOSC:
+ {
+ clock_set_hz(s->ostimer_clk, LPOSC_CLK_HZ);
+ break;
+ }
+ case OSEVENTTFCLKSEL_32KHZRTC:
+ {
+ clock_set_hz(s->ostimer_clk, RTC32KHZ_CLK_HZ);
+ break;
+ }
+ case OSEVENTTFCLKSEL_HCLK:
+ {
+ clock_set_hz(s->ostimer_clk, clock_get_hz(s->sysclk));
+ break;
+ }
+ case OSEVENTTFCLKSEL_NONE:
+ {
+ clock_set_hz(s->ostimer_clk, 0);
+ break;
+ }
+ }
+
+ clock_propagate(s->ostimer_clk);
+ break;
+ }
+ }
+
+out:
+ trace_rt500_clkctl1_reg_write(reg_names[addr], addr, value);
+ return ret;
+
+ return MEMTX_OK;
+}
+
+
+static const MemoryRegionOps rt500_clkctl1_ops = {
+ .read_with_attrs = rt500_clkctl1_read,
+ .write_with_attrs = rt500_clkctl1_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void rt500_clkctl1_reset(DeviceState *dev)
+{
+ RT500ClkCtl1State *s = RT500_CLKCTL1(dev);
+
+ memset(&s->regs, 0, sizeof(s->regs));
+
+ rt500_clkctl1_reset_registers(&s->regs);
+}
+
+static void rt500_clkctl1_init(Object *obj)
+{
+ RT500ClkCtl1State *s = RT500_CLKCTL1(obj);
+
+ memory_region_init_io(&s->mmio, obj, &rt500_clkctl1_ops, s,
+ TYPE_RT500_CLKCTL1, sizeof(s->regs));
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+ s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+ s->ostimer_clk = qdev_init_clock_out(DEVICE(s), "ostimer_clk");
+}
+
+static void rt500_clkctl1_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void rt500_clkctl1_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = rt500_clkctl1_reset;
+ dc->realize = rt500_clkctl1_realize;
+}
+
+static const TypeInfo rt500_clkctl1_info = {
+ .name = TYPE_RT500_CLKCTL1,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RT500ClkCtl1State),
+ .instance_init = rt500_clkctl1_init,
+ .class_init = rt500_clkctl1_class_init,
+};
+
+static void rt500_clkctl1_register_types(void)
+{
+ type_register_static(&rt500_clkctl1_info);
+}
+
+type_init(rt500_clkctl1_register_types)
+
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 71ec77de29..e65fcfa613 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -357,3 +357,11 @@ flexcomm_reset(void) ""
flexcomm_irq(const char *id, uint8_t irq) "%s %d"
flexcomm_reg_read(const char *devname, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] -> 0x%08x"
flexcomm_reg_write(const char *dename, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
+
+# rt500_clkctl0.c
+rt500_clkctl0_reg_read(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] -> 0x%08x"
+rt500_clkctl0_reg_write(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] <- 0x%08x"
+
+# rt500_clkctl1.c
+rt500_clkctl1_reg_read(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] -> 0x%08x"
+rt500_clkctl1_reg_write(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] <- 0x%08x"
diff --git a/include/hw/misc/rt500_clk_freqs.h b/include/hw/misc/rt500_clk_freqs.h
new file mode 100644
index 0000000000..1e366d4967
--- /dev/null
+++ b/include/hw/misc/rt500_clk_freqs.h
@@ -0,0 +1,18 @@
+/*
+ * QEMU model for RT500 Clock Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_MISC_RT500_CLK_FREQS_H
+#define HW_MISC_RT500_CLK_FREQS_H
+
+#define RTC32KHZ_CLK_HZ 32000
+#define LPOSC_CLK_HZ 1000000
+
+#endif /* HW_MISC_RT500_CLK_FREQS_H */
diff --git a/include/hw/misc/rt500_clkctl0.h b/include/hw/misc/rt500_clkctl0.h
new file mode 100644
index 0000000000..798ded96e3
--- /dev/null
+++ b/include/hw/misc/rt500_clkctl0.h
@@ -0,0 +1,37 @@
+/*
+ * QEMU model for RT500 Clock Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_MISC_RT500_CLKCTL0_H
+#define HW_MISC_RT500_CLKCTL0_H
+
+#include "hw/arm/svd/rt500_clkctl0.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RT500_CLKCTL0 "rt500-clkctl0"
+#define RT500_CLKCTL0(o) OBJECT_CHECK(RT500ClkCtl0State, o, TYPE_RT500_CLKCTL0)
+
+#define SYSTICKFCLKSEL_DIVOUT 0
+#define SYSTICKFCLKSEL_LPOSC 1
+#define SYSTICKFCLKSEL_32KHZRTC 2
+#define SYSTICKFCLKSEL_NONE 7
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ RT500_CLKCTL0_Type regs;
+ Clock *systick_clk;
+ Clock *sysclk;
+} RT500ClkCtl0State;
+
+#endif /* HW_MISC_RT500_CLKCTL0_H */
diff --git a/include/hw/misc/rt500_clkctl1.h b/include/hw/misc/rt500_clkctl1.h
new file mode 100644
index 0000000000..7b6e56e294
--- /dev/null
+++ b/include/hw/misc/rt500_clkctl1.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU model for RT500 Clock Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+
+#ifndef HW_MISC_RT500_CLKCTL1_H
+#define HW_MISC_RT500_CLKCTL1_H
+
+#include "hw/arm/svd/rt500_clkctl1.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RT500_CLKCTL1 "rt500-clkctl1"
+#define RT500_CLKCTL1(o) OBJECT_CHECK(RT500ClkCtl1State, o, TYPE_RT500_CLKCTL1)
+
+#define OSEVENTTFCLKSEL_LPOSC 0
+#define OSEVENTTFCLKSEL_32KHZRTC 1
+#define OSEVENTTFCLKSEL_HCLK 2
+#define OSEVENTTFCLKSEL_NONE 7
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ RT500_CLKCTL1_Type regs;
+ Clock *sysclk;
+ Clock *ostimer_clk;
+} RT500ClkCtl1State;
+
+#endif /* HW_MISC_RT500_CLKCTL1_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 19/23] test/unit: add unit tests for RT500's clock controller
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (17 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 18/23] hw/misc: add support for RT500's clock controller Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 20/23] hw/ssi: add support for flexspi Octavian Purdila
` (4 subsequent siblings)
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add test to exercise clocks set and clear, system PLL initialization,
audio PLL initialization, systick and ostimer clock source selection.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
tests/unit/meson.build | 7 +
tests/unit/test-rt500-clkctl.c | 270 +++++++++++++++++++++++++++++++++
2 files changed, 277 insertions(+)
create mode 100644 tests/unit/test-rt500-clkctl.c
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 7a28e7b521..be3062acbf 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -188,6 +188,13 @@ if have_system
meson.project_source_root() / 'hw/ssi/ssi.c',
'spi_tester.c',
],
+ 'test-rt500-clkctl': [
+ hwcore,
+ meson.project_source_root() / 'hw/core/gpio.c',
+ meson.project_source_root() / 'hw/misc/rt500_clkctl0.c',
+ meson.project_source_root() / 'hw/misc/rt500_clkctl1.c',
+ meson.project_source_root() / 'tests/unit/sysbus-mock.c',
+ ],
}
if config_host_data.get('CONFIG_INOTIFY1')
tests += {'test-util-filemonitor': []}
diff --git a/tests/unit/test-rt500-clkctl.c b/tests/unit/test-rt500-clkctl.c
new file mode 100644
index 0000000000..9312091c46
--- /dev/null
+++ b/tests/unit/test-rt500-clkctl.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "exec/memory.h"
+#include "hw/clock.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+
+#include "hw/misc/rt500_clkctl0.h"
+#include "hw/misc/rt500_clkctl1.h"
+#include "hw/misc/rt500_clk_freqs.h"
+#include "sysbus-mock.h"
+#include "reg-utils.h"
+
+typedef struct {
+ DeviceState *clk0;
+ DeviceState *clk1;
+ Clock *sysclk;
+ Clock *systick_clk;
+ Clock *ostimer_clk;
+} TestFixture;
+
+#define SYSCLK_HZ 100000000
+
+#define RT500_CLKCTL0_BASE 0x40001000UL
+#define RT500_CLKCTL1_BASE 0x40021000UL
+
+/*
+ * Test fixture initialization.
+ */
+static void set_up(TestFixture *f, gconstpointer data)
+{
+ f->clk0 = qdev_new(TYPE_RT500_CLKCTL0);
+ g_assert(f->clk0);
+
+ f->clk1 = qdev_new(TYPE_RT500_CLKCTL1);
+ g_assert(f->clk1);
+
+ f->sysclk = clock_new(OBJECT(&f->clk0->parent_obj), "SYSCLK");
+ clock_set_hz(f->sysclk, SYSCLK_HZ);
+
+ qdev_connect_clock_in(f->clk0, "sysclk", f->sysclk);
+ f->systick_clk = RT500_CLKCTL0(f->clk0)->systick_clk;
+
+ qdev_connect_clock_in(f->clk1, "sysclk", f->sysclk);
+ f->ostimer_clk = RT500_CLKCTL1(f->clk1)->ostimer_clk;
+
+ qdev_realize_and_unref(f->clk0, NULL, NULL);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->clk0), 0, RT500_CLKCTL0_BASE);
+
+ qdev_realize_and_unref(f->clk1, NULL, NULL);
+ sysbus_mmio_map(SYS_BUS_DEVICE(f->clk1), 0, RT500_CLKCTL1_BASE);
+
+ device_cold_reset(f->clk0);
+ device_cold_reset(f->clk1);
+}
+
+static void tear_down(TestFixture *f, gconstpointer user_data)
+{
+ qdev_unrealize(f->clk0);
+ qdev_unrealize(f->clk1);
+ g_free(f->clk0);
+ g_free(f->clk1);
+}
+
+#undef CLKCTL0
+#undef CLKCTL1
+
+static void pscctl_test(TestFixture *f, gconstpointer user_data)
+{
+ /* rom controller clock should be enabled at reset */
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, PSCCTL0, ROM_CTRLR_CLK)
+ == 1);
+
+ /* DSP clk is disabled at reset */
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, PSCCTL0, DSP_CLK) == 0);
+
+ /* check PSCTL_SET functionality */
+ REG32_WRITE(f->clk0, RT500_CLKCTL0, PSCCTL0_SET,
+ RT500_CLKCTL0_PSCCTL0_DSP_CLK_Msk);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, PSCCTL0, DSP_CLK) == 1);
+
+ /* check PSCTL_CLR functionality */
+ REG32_WRITE(f->clk0, RT500_CLKCTL0, PSCCTL0_CLR,
+ RT500_CLKCTL0_PSCCTL0_DSP_CLK_Msk);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, PSCCTL0, DSP_CLK) == 0);
+
+ /* FLEXIO clk is disabled at reset */
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, PSCCTL0, FlexIO) == 0);
+
+ /* check PSCTL_SET functionality */
+ REG32_WRITE(f->clk1, RT500_CLKCTL1, PSCCTL0_SET,
+ RT500_CLKCTL1_PSCCTL0_FlexIO_Msk);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, PSCCTL0, FlexIO) == 1);
+
+ /* check PSCTL_CLR functionality */
+ REG32_WRITE(f->clk1, RT500_CLKCTL1, PSCCTL0_CLR,
+ RT500_CLKCTL1_PSCCTL0_FlexIO_Msk);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, PSCCTL0, FlexIO) == 0);
+}
+
+static void audiopll0pfd_test(TestFixture *f, gconstpointer user_data)
+{
+ /* audio plls are gated at boot */
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD,
+ PFD3_CLKGATE) == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD,
+ PFD2_CLKGATE) == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD,
+ PFD1_CLKGATE) == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD,
+ PFD0_CLKGATE) == 1);
+
+ /* ,,, and clocks are not ready */
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD3_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD2_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD1_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD0_CLKRDY)
+ == 0);
+
+ /* ungate all plls and check that clocks are ready */
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD3_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD2_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD1_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD0_CLKGATE, 0);
+
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD3_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD2_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD1_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk1, RT500_CLKCTL1, AUDIOPLL0PFD, PFD0_CLKRDY)
+ == 1);
+}
+
+static void syspll0pfd_test(TestFixture *f, gconstpointer user_data)
+{
+ /* system plls are gated at boot */
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD3_CLKGATE)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD2_CLKGATE)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD1_CLKGATE)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD0_CLKGATE)
+ == 1);
+
+ /* ,,, and clocks are not ready */
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD3_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD2_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD1_CLKRDY)
+ == 0);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD0_CLKRDY)
+ == 0);
+
+ /* ungate all plls and check that clocks are ready */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD3_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD2_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD1_CLKGATE, 0);
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD0_CLKGATE, 0);
+
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD3_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD2_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD1_CLKRDY)
+ == 1);
+ g_assert(REG32_READ_FIELD(f->clk0, RT500_CLKCTL0, SYSPLL0PFD, PFD0_CLKRDY)
+ == 1);
+}
+
+static void systick_clk_test(TestFixture *f, gconstpointer user_data)
+{
+ /* systick is not running at reset */
+ g_assert(clock_get_hz(f->systick_clk) == 0);
+
+ /* select divout no divisor */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSTICKFCLKSEL, SEL,
+ SYSTICKFCLKSEL_DIVOUT);
+ g_assert(clock_get_hz(f->systick_clk) == SYSCLK_HZ);
+
+ /* change divisor to 2 */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSTICKFCLKDIV, DIV, 1);
+ g_assert(clock_get_hz(f->systick_clk) == SYSCLK_HZ / 2);
+
+ /* select lpsoc */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSTICKFCLKSEL, SEL,
+ SYSTICKFCLKSEL_LPOSC);
+ g_assert(clock_get_hz(f->systick_clk) == LPOSC_CLK_HZ);
+
+ /* select lpsoc */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSTICKFCLKSEL, SEL,
+ SYSTICKFCLKSEL_32KHZRTC);
+ g_assert(clock_get_hz(f->systick_clk) == RTC32KHZ_CLK_HZ);
+
+ /* disable clock */
+ REG32_WRITE_FIELD(f->clk0, RT500_CLKCTL0, SYSTICKFCLKSEL, SEL,
+ SYSTICKFCLKSEL_NONE);
+ g_assert(clock_get_hz(f->systick_clk) == 0);
+}
+
+static void ostimer_clk_test(TestFixture *f, gconstpointer user_data)
+{
+ /* systick is not running at reset */
+ g_assert(clock_get_hz(f->ostimer_clk) == 0);
+
+ /* select lpsoc */
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, OSEVENTTFCLKSEL, SEL,
+ OSEVENTTFCLKSEL_LPOSC);
+ g_assert(clock_get_hz(f->ostimer_clk) == LPOSC_CLK_HZ);
+
+
+ /* select 32khz RTC */
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, OSEVENTTFCLKSEL, SEL,
+ OSEVENTTFCLKSEL_32KHZRTC);
+ g_assert(clock_get_hz(f->ostimer_clk) == RTC32KHZ_CLK_HZ);
+
+ /* select hclk */
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, OSEVENTTFCLKSEL, SEL,
+ OSEVENTTFCLKSEL_HCLK);
+ g_assert(clock_get_hz(f->ostimer_clk) == SYSCLK_HZ);
+
+ /* disable clock */
+ REG32_WRITE_FIELD(f->clk1, RT500_CLKCTL1, OSEVENTTFCLKSEL, SEL,
+ OSEVENTTFCLKSEL_NONE);
+ g_assert(clock_get_hz(f->ostimer_clk) == 0);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ /* Initialize object types. */
+ sysbus_mock_init();
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_add("/rt500-clkctl/pscctl-test", TestFixture, NULL,
+ set_up, pscctl_test, tear_down);
+
+ g_test_add("/rt500-clkctl/syspll0pfd-test", TestFixture, NULL,
+ set_up, syspll0pfd_test, tear_down);
+
+ g_test_add("/rt500-clkctl/audiopll0pfd-test", TestFixture, NULL,
+ set_up, audiopll0pfd_test, tear_down);
+
+ g_test_add("/rt500-clkctl/systick-test", TestFixture, NULL,
+ set_up, systick_clk_test, tear_down);
+
+ g_test_add("/rt500-clkctl/ostimer-clk-test", TestFixture, NULL,
+ set_up, ostimer_clk_test, tear_down);
+
+ return g_test_run();
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 20/23] hw/ssi: add support for flexspi
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (18 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 19/23] test/unit: add unit tests " Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-08 5:11 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller Octavian Purdila
` (3 subsequent siblings)
23 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
This is mostly a stub which completes SPI transactions as noops
by masking out the error interrupts and never clearing the IPCMDDONE
interrupt.
Although incomplete, this allows software that uses NXP's mcuxpresso
SDK to run the SDK board initialization functions.
It also supports AHB memory access, aka XIP, for now as simple RAM
memory regions.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 4 +
hw/ssi/Kconfig | 4 +
hw/ssi/flexspi.c | 216 +++++++++++++++++++++++++++++++++++++++
hw/ssi/meson.build | 1 +
hw/ssi/trace-events | 4 +
include/hw/ssi/flexspi.h | 34 ++++++
6 files changed, 263 insertions(+)
create mode 100644 hw/ssi/flexspi.c
create mode 100644 include/hw/ssi/flexspi.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index 6ab13f8757..22f75880f5 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -22,3 +22,7 @@ genh += custom_target('rt500_clkctl1.h',
output: 'rt500_clkctl1.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'CLKCTL1', '-t', 'RT500_CLKCTL1'])
+genh += custom_target('flexspi.h',
+ output: 'flexspi.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'FLEXSPI0', '-t', 'FLEXSPI'])
diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig
index 83ee53c1d0..fb8feeb024 100644
--- a/hw/ssi/Kconfig
+++ b/hw/ssi/Kconfig
@@ -24,3 +24,7 @@ config STM32F2XX_SPI
config BCM2835_SPI
bool
select SSI
+
+config FLEXSPI
+ bool
+ select SSI
diff --git a/hw/ssi/flexspi.c b/hw/ssi/flexspi.c
new file mode 100644
index 0000000000..305d1a5bac
--- /dev/null
+++ b/hw/ssi/flexspi.c
@@ -0,0 +1,216 @@
+/*
+ * QEMU model for FLEXSPI
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/mmap-alloc.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "exec/address-spaces.h"
+#include "hw/regs.h"
+#include "hw/ssi/flexspi.h"
+#include "hw/arm/svd/flexspi.h"
+
+#include "trace.h"
+
+#define reg(field) offsetof(FLEXSPI_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(FLEXSPI_Type) / sizeof(uint32_t))
+
+static FLEXSPI_REGISTER_NAMES_ARRAY(reg_names);
+
+static void flexspi_reset(DeviceState *dev)
+{
+ FlexSpiState *s = FLEXSPI(dev);
+
+ memset(&s->regs, 0, sizeof(s->regs));
+
+ flexspi_reset_registers(&s->regs);
+
+ /* idle immediately after reset */
+ s->regs.STS0_b.SEQIDLE = 1;
+}
+
+static MemTxResult flexspi_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ FlexSpiState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ default:
+ *data = reg32_read(&s->regs, addr);
+ break;
+ }
+
+out:
+ trace_flexspi_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static uint32_t wr_mask[REG_NO] = {
+ [regi(MCR0)] = BITS(31, 14) | BITS(12, 8) | BITS(5, 4) | BITS(1, 0),
+ [regi(MCR1)] = BITS(31, 0),
+ [regi(MCR2)] = BITS(31, 24) | BIT(11),
+ [regi(AHBCR)] = BIT(10) | BITS(7, 2) | BIT(0),
+ [regi(INTEN)] = BITS(13, 0),
+ /*
+ * TODO: once SPI transfers are implemented restore mask to:
+ *
+ * [regi(INTR)] = BIT(16) | BITS(12, 0).
+ *
+ * In the meantime this INTR mask allows for fake SPI transfers.
+ */
+ [regi(INTR)] = BIT(0),
+ [regi(LUTKEY)] = BITS(31, 0),
+ [regi(LUTCR)] = BITS(1, 0),
+ [regi(AHBRXBUF0CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF1CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF2CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF3CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF4CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF5CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF6CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(AHBRXBUF7CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
+ [regi(FLSHA1CR0)] = BITS(22, 0),
+ [regi(FLSHA2CR0)] = BITS(22, 0),
+ [regi(FLSHB1CR0)] = BITS(22, 0),
+ [regi(FLSHB2CR0)] = BITS(22, 0),
+ [regi(FLSHCR1A1)] = BITS(31, 0),
+ [regi(FLSHCR1A2)] = BITS(31, 0),
+ [regi(FLSHCR1B1)] = BITS(31, 0),
+ [regi(FLSHCR1B2)] = BITS(31, 0),
+ [regi(FLSHCR2A1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
+ [regi(FLSHCR2A2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
+ [regi(FLSHCR2B1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
+ [regi(FLSHCR2B2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
+ [regi(FLSHCR4)] = BITS(3, 2) | BIT(0),
+ [regi(IPCR0)] = BITS(31, 0),
+ [regi(IPCR1)] = BIT(31) | BITS(26, 24) | BITS(19, 0),
+ [regi(IPCMD)] = BIT(1),
+ [regi(DLPR)] = BITS(31, 0),
+ [regi(IPRXFCR)] = BITS(8, 0),
+ [regi(IPTXFCR)] = BITS(8, 0),
+ [regi(DLLCRA)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
+ [regi(DLLCRB)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
+ [regi(HADDRSTART)] = BITS(31, 12) | BIT(0),
+ [regi(HADDREND)] = BITS(31, 12),
+ [regi(HADDROFFSET)] = BITS(31, 12),
+};
+
+static MemTxResult flexspi_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ FlexSpiState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ trace_flexspi_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+
+ if (!reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(MCR0):
+ {
+ reg32_write(&s->regs, addr, value, wr_mask);
+
+ if (s->regs.MCR0_b.SWRESET) {
+ s->regs.MCR0_b.SWRESET = 0;
+ }
+ break;
+ }
+
+ default:
+ reg32_write(&s->regs, addr, value, wr_mask);
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static const MemoryRegionOps flexspi_ops = {
+ .read_with_attrs = flexspi_read,
+ .write_with_attrs = flexspi_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static Property flexspi_properties[] = {
+ DEFINE_PROP_UINT32("mmap_base", FlexSpiState, mmap_base, 0),
+ DEFINE_PROP_UINT32("mmap_size", FlexSpiState, mmap_size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void flexspi_init(Object *obj)
+{
+ FlexSpiState *s = FLEXSPI(obj);
+
+ memory_region_init_io(&s->mmio, obj, &flexspi_ops, s, TYPE_FLEXSPI,
+ sizeof(FLEXSPI_Type));
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void flexspi_realize(DeviceState *dev, Error **errp)
+{
+ FlexSpiState *s = FLEXSPI(dev);
+
+ if (s->mmap_size) {
+ memory_region_init_ram(&s->mem, OBJECT(s), DEVICE(s)->id, s->mmap_size,
+ NULL);
+ memory_region_add_subregion(get_system_memory(), s->mmap_base, &s->mem);
+ }
+}
+
+static void flexspi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = flexspi_reset;
+ dc->realize = flexspi_realize;
+ device_class_set_props(dc, flexspi_properties);
+}
+
+static const TypeInfo flexspi_info = {
+ .name = TYPE_FLEXSPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(FlexSpiState),
+ .instance_init = flexspi_init,
+ .class_init = flexspi_class_init,
+};
+
+static void flexspi_register_types(void)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ wr_mask[regi(TFDR[i])] = BITS(31, 0);
+ }
+ for (i = 0; i < 64; i++) {
+ wr_mask[regi(LUT[i])] = BITS(31, 0);
+ }
+
+ type_register_static(&flexspi_info);
+}
+
+type_init(flexspi_register_types)
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index 57d3e14727..c5b7e0a6e2 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_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_FLEXCOMM', if_true: files('flexcomm_spi.c'))
+system_ss.add(when: 'CONFIG_FLEXSPI', if_true: files('flexspi.c'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 5caa1c17ac..d623022a79 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -40,3 +40,7 @@ flexcomm_spi_fifostat(const char *id, uint32_t fifostat, uint32_t fifoinstat) "%
flexcomm_spi_irq(const char *id, bool irq, bool fifoirqs, bool perirqs, bool enabled) "%s: %d %d %d %d"
flexcomm_spi_chr_rx_space(const char *id, uint32_t rx) "%s: %d"
flexcomm_spi_chr_rx(const char *id) "%s"
+
+# flexspi.c
+flexspi_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
+flexspi_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
diff --git a/include/hw/ssi/flexspi.h b/include/hw/ssi/flexspi.h
new file mode 100644
index 0000000000..f5fea9dee9
--- /dev/null
+++ b/include/hw/ssi/flexspi.h
@@ -0,0 +1,34 @@
+/*
+ * QEMU model for FLEXSPI
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_RT500_FLEXSPI_H
+#define HW_RT500_FLEXSPI_H
+
+#include "hw/sysbus.h"
+#include "hw/ssi/ssi.h"
+#include "hw/arm/svd/flexspi.h"
+
+#define TYPE_FLEXSPI "flexspi"
+#define FLEXSPI(obj) OBJECT_CHECK(FlexSpiState, (obj), TYPE_FLEXSPI)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ FLEXSPI_Type regs;
+ MemoryRegion mem;
+ uint32_t mmap_base;
+ uint32_t mmap_size;
+} FlexSpiState;
+
+#endif /* HW_RT500_FLEXSPI_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (19 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 20/23] hw/ssi: add support for flexspi Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-08 5:00 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC Octavian Purdila
` (2 subsequent siblings)
23 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
The RT500 reset controller has two instances that have the same
register layout but with different fields for some registers.
The model only provides set and clear functionality for the various
reset lines which is common for both instances. Because of that only
one type is implemented for both controllers.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/svd/meson.build | 8 ++
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/rt500_rstctl.c | 219 +++++++++++++++++++++++++++++++++
hw/misc/trace-events | 4 +
include/hw/misc/rt500_rstctl.h | 38 ++++++
6 files changed, 273 insertions(+)
create mode 100644 hw/misc/rt500_rstctl.c
create mode 100644 include/hw/misc/rt500_rstctl.h
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index 22f75880f5..72a7421c6f 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -26,3 +26,11 @@ genh += custom_target('flexspi.h',
output: 'flexspi.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'FLEXSPI0', '-t', 'FLEXSPI'])
+genh += custom_target('rt500_rstctl0.h',
+ output: 'rt500_rstctl0.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'RSTCTL0', '-t', 'RT500_RSTCTL0'])
+genh += custom_target('rt500_rstctl1.h',
+ output: 'rt500_rstctl1.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'RSTCTL1', '-t', 'RT500_RSTCTL1'])
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 392ae9e84f..70a2a269ac 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -222,4 +222,7 @@ config RT500_CLKCTL0
config RT500_CLKCTL1
bool
+config RT500_RSTCTL
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 68929949a6..5e2728e982 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -160,3 +160,4 @@ system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))
system_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm.c'))
system_ss.add(when: 'CONFIG_RT500_CLKCTL0', if_true: files('rt500_clkctl0.c'))
system_ss.add(when: 'CONFIG_RT500_CLKCTL1', if_true: files('rt500_clkctl1.c'))
+system_ss.add(when: 'CONFIG_RT500_RSTCTL', if_true: files('rt500_rstctl.c'))
diff --git a/hw/misc/rt500_rstctl.c b/hw/misc/rt500_rstctl.c
new file mode 100644
index 0000000000..2806a94150
--- /dev/null
+++ b/hw/misc/rt500_rstctl.c
@@ -0,0 +1,219 @@
+/*
+ * QEMU model for RT500 Reset Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "exec/address-spaces.h"
+#include "hw/regs.h"
+#include "hw/misc/rt500_rstctl.h"
+
+#include "trace.h"
+
+/*
+ * There are two intances for RSTCTL with the same register names but
+ * with different fields.
+ */
+#define reg(field) offsetof(RT500_RSTCTL0_Type, field)
+#define regi(x) (reg(x) / sizeof(uint32_t))
+#define REG_NO (sizeof(RT500_RSTCTL0_Type) / sizeof(uint32_t))
+
+#define RSTCTL_SYSRSTSTAT_WMASK (BITS(7, 4) | BIT(0))
+#define RSTCL0_PRSCTL0_WMASK (BITS(30, 26) | BITS(24, 20) | BIT(18) | \
+ BIT(16) | BITS(12, 8) | BIT(3) | BIT(1))
+#define RSTCL0_PRSCTL1_WMASK (BIT(24) | BITS(16, 15) | BITS(3, 2))
+#define RSTCL0_PRSCTL2_WMASK (BITS(1, 0))
+#define RSTCL1_PRSCTL0_WMASK (BIT(29) | BIT(27) | BITS(25, 8))
+#define RSTCL1_PRSCTL1_WMASK (BIT(31) | BITS(29, 28) | BITS(24, 23) | \
+ BIT(16) | BITS(7, 0))
+#define RSTCL1_PRSCTL2_WMASK (BITS(31, 30) | BITS(17, 16) | BIT(10) | \
+ BIT(8) | BITS(4, 0))
+
+static RT500_RSTCTL0_REGISTER_NAMES_ARRAY(reg_names);
+
+static MemTxResult rt500_rstctl_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500RstCtlState *s = opaque;
+ MemTxResult ret = MEMTX_OK;
+
+ if (s->num > 1 || !reg32_aligned_access(addr, size)) {
+ ret = MEMTX_ERROR;
+ goto out;
+ }
+
+ switch (addr) {
+ case reg(SYSRSTSTAT):
+ case reg(PRSTCTL0):
+ case reg(PRSTCTL1):
+ case reg(PRSTCTL2):
+ *data = reg32_read(&s->regs.ctl0, addr);
+ break;
+ default:
+ ret = MEMTX_ERROR;
+ }
+
+out:
+ trace_rt500_rstctl_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
+ return ret;
+}
+
+static MemTxResult rt500_rstctl_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ RT500RstCtlState *s = opaque;
+ static uint32_t mask0[REG_NO] = {
+ [regi(SYSRSTSTAT)] = RSTCTL_SYSRSTSTAT_WMASK,
+ [regi(PRSTCTL0)] = RSTCL0_PRSCTL0_WMASK,
+ [regi(PRSTCTL1)] = RSTCL0_PRSCTL1_WMASK,
+ [regi(PRSTCTL2)] = RSTCL0_PRSCTL2_WMASK,
+ [regi(PRSTCTL0_SET)] = RSTCL0_PRSCTL0_WMASK,
+ [regi(PRSTCTL1_SET)] = RSTCL0_PRSCTL1_WMASK,
+ [regi(PRSTCTL2_SET)] = RSTCL0_PRSCTL2_WMASK,
+ [regi(PRSTCTL0_CLR)] = RSTCL0_PRSCTL0_WMASK,
+ [regi(PRSTCTL1_CLR)] = RSTCL0_PRSCTL1_WMASK,
+ [regi(PRSTCTL2_CLR)] = RSTCL0_PRSCTL2_WMASK,
+ };
+ static uint32_t mask1[REG_NO] = {
+ [regi(SYSRSTSTAT)] = RSTCTL_SYSRSTSTAT_WMASK,
+ [regi(PRSTCTL0)] = RSTCL1_PRSCTL0_WMASK,
+ [regi(PRSTCTL1)] = RSTCL1_PRSCTL1_WMASK,
+ [regi(PRSTCTL2)] = RSTCL1_PRSCTL2_WMASK,
+ [regi(PRSTCTL0_SET)] = RSTCL1_PRSCTL0_WMASK,
+ [regi(PRSTCTL1_SET)] = RSTCL1_PRSCTL1_WMASK,
+ [regi(PRSTCTL2_SET)] = RSTCL1_PRSCTL2_WMASK,
+ [regi(PRSTCTL0_CLR)] = RSTCL1_PRSCTL0_WMASK,
+ [regi(PRSTCTL1_CLR)] = RSTCL1_PRSCTL1_WMASK,
+ [regi(PRSTCTL2_CLR)] = RSTCL1_PRSCTL2_WMASK,
+ };
+ uint32_t mask;
+
+ trace_rt500_rstctl_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
+
+ if (s->num > 1 || !reg32_aligned_access(addr, size)) {
+ return MEMTX_ERROR;
+ }
+
+ if (s->num == 0) {
+ mask = mask0[addr / sizeof(uint32_t)];
+ } else {
+ mask = mask1[addr / sizeof(uint32_t)];
+ }
+
+ switch (addr) {
+ case reg(SYSRSTSTAT):
+ {
+ /* write 1 to clear bits */
+ s->regs.ctl0.SYSRSTSTAT &= ~(value & mask);
+ break;
+ }
+ case reg(PRSTCTL0):
+ case reg(PRSTCTL1):
+ case reg(PRSTCTL2):
+ {
+ uint32_t idx = addr / sizeof(uint32_t);
+
+ s->regs.raw[idx] = (value & mask);
+ break;
+ }
+ case reg(PRSTCTL0_SET):
+ case reg(PRSTCTL1_SET):
+ case reg(PRSTCTL2_SET):
+ {
+ uint32_t idx;
+
+ idx = (reg(PRSTCTL0) + (addr - reg(PRSTCTL0_SET))) / sizeof(uint32_t);
+ s->regs.raw[idx] |= (value & mask);
+ break;
+ }
+ case reg(PRSTCTL0_CLR):
+ case reg(PRSTCTL1_CLR):
+ case reg(PRSTCTL2_CLR):
+ {
+ uint32_t idx;
+
+ idx = (reg(PRSTCTL0) + (addr - reg(PRSTCTL0_CLR))) / sizeof(uint32_t);
+ s->regs.raw[idx] &= ~(value & mask);
+ break;
+ }
+ }
+
+ return MEMTX_OK;
+}
+
+
+static const MemoryRegionOps rt500_rstctl_ops = {
+ .read_with_attrs = rt500_rstctl_read,
+ .write_with_attrs = rt500_rstctl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static Property rt500_rstctl_properties[] = {
+ DEFINE_PROP_UINT32("num", RT500RstCtlState, num, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rt500_rstctl_reset(DeviceState *dev)
+{
+ RT500RstCtlState *s = RT500_RSTCTL(dev);
+
+ memset(&s->regs, 0, sizeof(s->regs));
+
+ switch (s->num) {
+ case 0:
+ rt500_rstctl0_reset_registers(&s->regs.ctl0);
+ break;
+ case 1:
+ rt500_rstctl1_reset_registers(&s->regs.ctl1);
+ break;
+ }
+}
+
+static void rt500_rstctl_init(Object *obj)
+{
+ RT500RstCtlState *s = RT500_RSTCTL(obj);
+
+ memory_region_init_io(&s->mmio, obj, &rt500_rstctl_ops, s,
+ TYPE_RT500_RSTCTL, sizeof(s->regs));
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void rt500_rstctl_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static void rt500_rstctl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = rt500_rstctl_reset;
+ device_class_set_props(dc, rt500_rstctl_properties);
+ dc->realize = rt500_rstctl_realize;
+}
+
+static const TypeInfo rt500_rstctl_info = {
+ .name = TYPE_RT500_RSTCTL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RT500RstCtlState),
+ .instance_init = rt500_rstctl_init,
+ .class_init = rt500_rstctl_class_init,
+};
+
+static void rt500_rstctl_register_types(void)
+{
+ type_register_static(&rt500_rstctl_info);
+}
+
+type_init(rt500_rstctl_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index e65fcfa613..41a94d5ef6 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -365,3 +365,7 @@ rt500_clkctl0_reg_write(const char *regname, uint32_t addr, uint32_t val) "%s[0x
# rt500_clkctl1.c
rt500_clkctl1_reg_read(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] -> 0x%08x"
rt500_clkctl1_reg_write(const char *regname, uint32_t addr, uint32_t val) "%s[0x%04x] <- 0x%08x"
+
+# rt500_rstctl.c
+rt500_rstctl_reg_read(const char *id, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] -> 0x%08x"
+rt500_rstctl_reg_write(const char *id, const char *regname, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
diff --git a/include/hw/misc/rt500_rstctl.h b/include/hw/misc/rt500_rstctl.h
new file mode 100644
index 0000000000..d9726df5f6
--- /dev/null
+++ b/include/hw/misc/rt500_rstctl.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU model for RT500 Reset Controller
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_MISC_RT500_RSTCTL_H
+#define HW_MISC_RT500_RSTCTL_H
+
+#include "hw/arm/svd/rt500_rstctl0.h"
+#include "hw/arm/svd/rt500_rstctl1.h"
+#include "hw/sysbus.h"
+
+#define RSTCTL_Type RSTCTL0_Type
+
+#define TYPE_RT500_RSTCTL "rt500-rstctl"
+#define RT500_RSTCTL(o) OBJECT_CHECK(RT500RstCtlState, o, TYPE_RT500_RSTCTL)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ uint32_t num;
+ union {
+ RT500_RSTCTL0_Type ctl0;
+ RT500_RSTCTL1_Type ctl1;
+ uint32_t raw[sizeof(RT500_RSTCTL0_Type) / sizeof(uint32_t)];
+ } regs;
+} RT500RstCtlState;
+
+#endif /* HW_MISC_RT500_RSTCTL_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (20 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-06 14:51 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 23/23] hw/arm: add RT595-EVK board Octavian Purdila
2024-08-12 16:10 ` [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Peter Maydell
23 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add basic support for the RT500 SoC. It supports enough peripherals to
run the NXP's microXpresso SDK hello world example.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/Kconfig | 8 +
hw/arm/meson.build | 1 +
hw/arm/rt500.c | 348 +++++++++++++++++++++++++++++++++++++++++
hw/arm/svd/meson.build | 6 +
include/hw/arm/rt500.h | 49 ++++++
5 files changed, 412 insertions(+)
create mode 100644 hw/arm/rt500.c
create mode 100644 include/hw/arm/rt500.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 1ad60da7aa..7ffece3dec 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -712,3 +712,11 @@ config ARMSSE
select UNIMP
select SSE_COUNTER
select SSE_TIMER
+
+config RT500
+ bool
+ select FLEXCOMM
+ select RT500_CLKCTL0
+ select RT500_CLKCTL1
+ select FLEXSPI
+ select RT500_RSTCTL
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index eb604d00cf..7d827d512c 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -59,6 +59,7 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
+arm_ss.add(when: 'CONFIG_RT500', if_true: files('rt500.c'))
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
system_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
diff --git a/hw/arm/rt500.c b/hw/arm/rt500.c
new file mode 100644
index 0000000000..0866ef3ef6
--- /dev/null
+++ b/hw/arm/rt500.c
@@ -0,0 +1,348 @@
+/*
+ * i.MX RT500 platforms.
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "hw/arm/boot.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "qemu/log.h"
+#include "qemu/datadir.h"
+#include "exec/address-spaces.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "sysemu/sysemu.h"
+#include "hw/arm/armv7m.h"
+#include "hw/loader.h"
+#include "hw/qdev-clock.h"
+#include "hw/misc/unimp.h"
+#include "hw/arm/rt500.h"
+#include "hw/arm/svd/rt500.h"
+
+#define MMAP_SRAM_CODE_BASE (0x0)
+#define MMAP_SRAM_DATA_BASE (0x20000000)
+#define MMAP_SRAM_SIZE (5 * MiB)
+#define MMAP_BOOT_ROM_BASE (0x03000000)
+#define MMAP_BOOT_ROM_SIZE (192 * KiB)
+#define MMAP_SDMA_RAM_BASE (0x24100000)
+#define MMAP_SDMA_RAM_SIZE (32 * KiB)
+#define MMAP_FLEXSPI0_BASE (0x08000000)
+#define MMAP_FLEXSPI0_SIZE (128 * MiB)
+#define MMAP_FLEXSPI1_BASE (0x28000000)
+#define MMAP_FLEXSPI1_SIZE (128 * MiB)
+
+#define SECURE_OFFSET (0x10000000)
+
+typedef enum MemInfoType {
+ MEM_RAM,
+ MEM_ROM,
+ MEM_ALIAS
+} MemInfoType;
+
+static void do_sys_reset(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+}
+
+static void rt500_init(Object *obj)
+{
+ RT500State *s = RT500(obj);
+ int i;
+
+ /* Add ARMv7-M device */
+ object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+
+ for (i = 0; i < RT500_FLEXCOMM_NUM; i++) {
+ char id[] = "flexcommXX";
+
+ snprintf(id, sizeof(id), "flexcomm%d", i);
+ object_initialize_child(obj, id, &s->flexcomm[i], TYPE_FLEXCOMM);
+ DEVICE(&s->flexcomm[i])->id = g_strdup(id);
+ }
+
+ object_initialize_child(obj, "clkctl0", &s->clkctl0, TYPE_RT500_CLKCTL0);
+ object_initialize_child(obj, "clkctl1", &s->clkctl1, TYPE_RT500_CLKCTL1);
+
+ /* Initialize clocks */
+ s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+ s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
+
+ for (i = 0; i < RT500_FLEXSPI_NUM; i++) {
+ char id[] = "flexspiXX";
+
+ snprintf(id, sizeof(id), "flexspi%d", i);
+ object_initialize_child(obj, id, &s->flexspi[i], TYPE_FLEXSPI);
+ DEVICE(&s->flexspi[i])->id = g_strdup(id);
+ }
+
+ for (i = 0; i < RT500_RSTCTL_NUM; i++) {
+ char id[] = "rstctlX";
+
+ snprintf(id, sizeof(id), "rstctl%d", i);
+ object_initialize_child(obj, id, &s->rstctl[i],
+ TYPE_RT500_RSTCTL);
+ DEVICE(&s->rstctl[i])->id = g_strdup(id);
+ }
+}
+
+static void rt500_realize_memory(RT500State *s, Error **errp)
+{
+ const struct {
+ const char *name;
+ hwaddr base;
+ size_t size;
+ MemInfoType type;
+ int alias_for;
+ } mem_info[] = {
+ {
+ .name = "SRAM (code bus)",
+ .base = MMAP_SRAM_CODE_BASE,
+ .size = MMAP_SRAM_SIZE,
+ .type = MEM_RAM,
+ },
+ {
+ .name = "BOOT-ROM",
+ .base = MMAP_BOOT_ROM_BASE,
+ .size = MMAP_BOOT_ROM_SIZE,
+ .type = MEM_ROM,
+ },
+ {
+ .name = "Smart DMA RAM",
+ .base = MMAP_SDMA_RAM_BASE,
+ .size = MMAP_SDMA_RAM_SIZE,
+ .type = MEM_RAM,
+ },
+ {
+ .name = "SRAM (data bus)",
+ .base = MMAP_SRAM_DATA_BASE,
+ .size = MMAP_SRAM_SIZE,
+ .type = MEM_ALIAS,
+ .alias_for = 0
+ },
+ };
+ int i;
+
+ s->mem = g_malloc_n(2 * ARRAY_SIZE(mem_info), sizeof(MemoryRegion));
+ for (i = 0; i < ARRAY_SIZE(mem_info); i++) {
+ const char *name = mem_info[i].name;
+ int size = mem_info[i].size;
+ int type = mem_info[i].type;
+ int alias_for = mem_info[i].alias_for;
+ MemoryRegion *mem = &s->mem[i];
+ uint32_t base = mem_info[i].base;
+ MemoryRegion *sec_mem;
+ char sec_name[256];
+
+ switch (type) {
+ case MEM_RAM:
+ memory_region_init_ram(mem, OBJECT(s), name, size, errp);
+ break;
+ case MEM_ROM:
+ memory_region_init_rom(mem, OBJECT(s), name, size, errp);
+ break;
+ case MEM_ALIAS:
+ {
+ MemoryRegion *orig = &s->mem[alias_for];
+
+ memory_region_init_alias(mem, OBJECT(s), name, orig, 0, size);
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ memory_region_add_subregion(get_system_memory(), base, mem);
+
+ /* create secure alias */
+ snprintf(sec_name, sizeof(sec_name), "SECURE %s", name);
+ sec_mem = &s->mem[ARRAY_SIZE(mem_info) + i];
+ if (type == MEM_ALIAS) {
+ mem = &s->mem[alias_for];
+ }
+ memory_region_init_alias(sec_mem, OBJECT(s), sec_name, mem, 0, size);
+ memory_region_add_subregion(get_system_memory(), base + SECURE_OFFSET,
+ sec_mem);
+
+ if (mem_info[i].type == MEM_ROM) {
+ char *fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, "rt500.rom");
+
+ if (fname) {
+ int fsize = get_image_size(fname);
+ int ret;
+
+ if (fsize > size) {
+ error_setg(errp, "rom file too big: %d > %d", fsize, size);
+ } else {
+ ret = load_image_targphys(fname, base, size);
+ if (ret < 0) {
+ error_setg(errp, "could not load rom: %s", fname);
+ }
+ }
+ }
+ g_free(fname);
+ }
+ }
+}
+
+static void rt500_realize(DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ RT500State *s = RT500(dev);
+ int i;
+
+ rt500_realize_memory(s, errp);
+
+ /* Setup ARMv7M CPU */
+ qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq",
+ RT500_FLEXCOMM16_IRQn + 1);
+ qdev_prop_set_uint8(DEVICE(&s->armv7m), "num-prio-bits", 3);
+ qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ms->cpu_type);
+ object_property_set_link(OBJECT(&s->armv7m), "memory",
+ OBJECT(get_system_memory()), &error_abort);
+ if (!ms->kernel_filename) {
+ qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-nsvtor",
+ MMAP_BOOT_ROM_BASE);
+ qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor",
+ MMAP_BOOT_ROM_BASE + SECURE_OFFSET);
+ }
+
+ qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk);
+ qdev_connect_clock_in(DEVICE(&s->armv7m), "refclk",
+ qdev_get_clock_out(DEVICE(&s->clkctl0), "systick_clk"));
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->armv7m), errp);
+ qdev_connect_gpio_out_named(DEVICE(&s->armv7m), "SYSRESETREQ", 0,
+ qemu_allocate_irq(&do_sys_reset, NULL, 0));
+
+ /* Setup FLEXCOMM */
+ for (i = 0; i < RT500_FLEXCOMM_NUM; i++) {
+ const uint32_t addr[] = {
+ RT500_FLEXCOMM0_BASE, RT500_FLEXCOMM1_BASE, RT500_FLEXCOMM2_BASE,
+ RT500_FLEXCOMM3_BASE, RT500_FLEXCOMM4_BASE, RT500_FLEXCOMM5_BASE,
+ RT500_FLEXCOMM6_BASE, RT500_FLEXCOMM7_BASE, RT500_FLEXCOMM8_BASE,
+ RT500_FLEXCOMM8_BASE, RT500_FLEXCOMM10_BASE, RT500_FLEXCOMM11_BASE,
+ RT500_FLEXCOMM12_BASE, RT500_FLEXCOMM13_BASE, RT500_FLEXCOMM14_BASE,
+ RT500_FLEXCOMM15_BASE, RT500_FLEXCOMM16_BASE
+ };
+ const int irq[] = {
+ RT500_FLEXCOMM0_IRQn, RT500_FLEXCOMM1_IRQn, RT500_FLEXCOMM2_IRQn,
+ RT500_FLEXCOMM3_IRQn, RT500_FLEXCOMM4_IRQn, RT500_FLEXCOMM5_IRQn,
+ RT500_FLEXCOMM6_IRQn, RT500_FLEXCOMM7_IRQn, RT500_FLEXCOMM8_IRQn,
+ RT500_FLEXCOMM9_IRQn, RT500_FLEXCOMM10_IRQn, RT500_FLEXCOMM11_IRQn,
+ RT500_FLEXCOMM12_IRQn, RT500_FLEXCOMM13_IRQn, RT500_FLEXCOMM14_IRQn,
+ RT500_FLEXCOMM15_IRQn, RT500_FLEXCOMM16_IRQn
+ };
+ const int functions[] = {
+ FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
+ FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
+ FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
+ FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
+ FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_HSSPI,
+ FLEXCOMM_PMICI2C, FLEXCOMM_HSSPI
+ };
+ DeviceState *ds = DEVICE(&s->flexcomm[i]);
+ char id[] = "flexcommXX";
+
+ snprintf(id, sizeof(id), "flexcomm%d", i);
+ qdev_prop_set_uint32(ds, "functions", functions[i]);
+ qdev_prop_set_chr(ds, "chardev", qemu_chr_find(id));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0,
+ qdev_get_gpio_in(DEVICE(&s->armv7m), irq[i]));
+ }
+
+ /* Setup CTLCTL0 */
+ qdev_connect_clock_in(DEVICE(&s->clkctl0), "sysclk", s->sysclk);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(DEVICE(&s->clkctl0)), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(DEVICE(&s->clkctl0)), 0, RT500_CLKCTL0_BASE);
+
+ /* Setup CTLCTL1 */
+ qdev_connect_clock_in(DEVICE(&s->clkctl1), "sysclk", s->sysclk);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(DEVICE(&s->clkctl1)), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(DEVICE(&s->clkctl1)), 0, RT500_CLKCTL1_BASE);
+
+ /* Setup FlexSPI */
+ for (i = 0; i < RT500_FLEXSPI_NUM; i++) {
+ const uint32_t addr[] = {
+ RT500_FLEXSPI0_BASE, RT500_FLEXSPI1_BASE
+ };
+ const uint32_t mmap_base[] = {
+ MMAP_FLEXSPI0_BASE, MMAP_FLEXSPI1_BASE
+ };
+ const uint32_t mmap_size[] = {
+ MMAP_FLEXSPI0_SIZE, MMAP_FLEXSPI1_SIZE,
+ };
+ DeviceState *ds = DEVICE(&s->flexspi[i]);
+
+ qdev_prop_set_uint32(ds, "mmap_base", mmap_base[i]);
+ qdev_prop_set_uint32(ds, "mmap_size", mmap_size[i]);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
+ }
+
+ /* Setup reset controllers */
+ for (i = 0; i < RT500_RSTCTL_NUM; i++) {
+ DeviceState *ds = DEVICE(&s->rstctl[i]);
+
+ const uint32_t addr[] = {
+ RT500_RSTCTL0_BASE, RT500_RSTCTL1_BASE
+ };
+
+ qdev_prop_set_uint32(ds, "num", i);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
+ }
+}
+
+static void rt500_finalize(Object *obj)
+{
+ RT500State *s = RT500(obj);
+
+ g_free(s->mem);
+}
+
+static void rt500_reset(DeviceState *ds)
+{
+}
+
+static Property rt500_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rt500_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ device_class_set_props(dc, rt500_properties);
+ dc->realize = rt500_realize;
+ dc->desc = "RT500 (ARM Cortex-M33)";
+ dc->reset = rt500_reset;
+}
+
+static const TypeInfo rt500_type_info = {
+ .name = TYPE_RT500,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RT500State),
+ .instance_init = rt500_init,
+ .instance_finalize = rt500_finalize,
+ .class_init = rt500_class_init,
+};
+
+static void rt500_register_types(void)
+{
+ type_register_static(&rt500_type_info);
+}
+
+type_init(rt500_register_types)
diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
index 72a7421c6f..930a8b7343 100644
--- a/hw/arm/svd/meson.build
+++ b/hw/arm/svd/meson.build
@@ -34,3 +34,9 @@ genh += custom_target('rt500_rstctl1.h',
output: 'rt500_rstctl1.h',
input: 'MIMXRT595S_cm33.xml',
command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'RSTCTL1', '-t', 'RT500_RSTCTL1'])
+genh += custom_target('rt500.h',
+ output: 'rt500.h',
+ input: 'MIMXRT595S_cm33.xml',
+ command: [ svd_gen_header,'-i', '@INPUT@', '-o', '@OUTPUT@', '-s', 'RT500',
+ '-p', 'FLEXCOMM0', '-p', 'CLKCTL0', '-p', 'CLKCTL1',
+ '-p', 'FLEXSPI0', '-p', 'FLEXSPI1', '-p', 'RSTCTL0', '-p', 'RSTCTL1'])
diff --git a/include/hw/arm/rt500.h b/include/hw/arm/rt500.h
new file mode 100644
index 0000000000..8ca7972f8a
--- /dev/null
+++ b/include/hw/arm/rt500.h
@@ -0,0 +1,49 @@
+/*
+ * i.MX RT500 platforms.
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#ifndef HW_ARM_RT500_H
+#define HW_ARM_RT500_H
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/armv7m.h"
+#include "hw/misc/flexcomm.h"
+#include "hw/misc/rt500_clkctl0.h"
+#include "hw/misc/rt500_clkctl1.h"
+#include "hw/ssi/flexspi.h"
+#include "hw/misc/rt500_rstctl.h"
+
+#define TYPE_RT500 "rt500"
+#define RT500(obj) OBJECT_CHECK(RT500State, (obj), TYPE_RT500)
+
+#define RT500_FLEXCOMM_NUM (17)
+#define RT500_FLEXSPI_NUM (2)
+#define RT500_RSTCTL_NUM (2)
+
+typedef struct RT500State {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ ARMv7MState armv7m;
+ MemoryRegion *mem;
+ FlexcommState flexcomm[RT500_FLEXCOMM_NUM];
+ RT500ClkCtl0State clkctl0;
+ RT500ClkCtl1State clkctl1;
+ FlexSpiState flexspi[RT500_FLEXSPI_NUM];
+ RT500RstCtlState rstctl[RT500_RSTCTL_NUM];
+
+ Clock *sysclk;
+ Clock *refclk;
+} RT500State;
+
+#endif /* HW_ARM_RT500_H */
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [RFC PATCH 23/23] hw/arm: add RT595-EVK board
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (21 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC Octavian Purdila
@ 2024-08-05 20:17 ` Octavian Purdila
2024-08-12 16:10 ` [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Peter Maydell
23 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-05 20:17 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Add basic support for the RT595-EVK board, enough to be able to run
the NXP's microXpresso SDK hello world example.
Signed-off-by: Octavian Purdila <tavip@google.com>
---
hw/arm/Kconfig | 5 ++++
hw/arm/meson.build | 1 +
hw/arm/rt595-evk.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)
create mode 100644 hw/arm/rt595-evk.c
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 7ffece3dec..26ec3d391a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -720,3 +720,8 @@ config RT500
select RT500_CLKCTL1
select FLEXSPI
select RT500_RSTCTL
+
+config RT595_EVK
+ bool
+ default y
+ select RT500
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 7d827d512c..9792c93142 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -60,6 +60,7 @@ arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-e
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
arm_ss.add(when: 'CONFIG_RT500', if_true: files('rt500.c'))
+arm_ss.add(when: 'CONFIG_RT595_EVK', if_true: files('rt595-evk.c'))
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
system_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
diff --git a/hw/arm/rt595-evk.c b/hw/arm/rt595-evk.c
new file mode 100644
index 0000000000..fff3004195
--- /dev/null
+++ b/hw/arm/rt595-evk.c
@@ -0,0 +1,64 @@
+/*
+ * i.MX RT595 EVK
+ *
+ * Copyright (c) 2024 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "elf.h"
+#include "exec/address-spaces.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/boards.h"
+#include "qemu/log.h"
+#include "hw/arm/armv7m.h"
+#include "hw/misc/unimp.h"
+#include "hw/arm/rt500.h"
+#include "hw/qdev-clock.h"
+#include "sysemu/reset.h"
+
+static void rt595_evk_reset(MachineState *ms, ShutdownCause reason)
+{
+ /*
+ * CPU reset is not done by default, we need to do it manually when the
+ * machine is reset.
+ */
+ cpu_reset(first_cpu);
+
+ qemu_devices_reset(reason);
+}
+
+static void rt595_evk_init(MachineState *ms)
+{
+ RT500State *s;
+ Clock *sysclk;
+
+ sysclk = clock_new(OBJECT(ms), "SYSCLK");
+ clock_set_hz(sysclk, 200000000);
+
+ s = RT500(object_new(TYPE_RT500));
+ qdev_connect_clock_in(DEVICE(s), "sysclk", sysclk);
+ object_property_add_child(OBJECT(ms), "soc", OBJECT(s));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal);
+
+ if (ms->kernel_filename) {
+ armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, 0, 0);
+ }
+}
+
+static void rt595_evk_machine_init(MachineClass *mc)
+{
+ mc->desc = "RT595 EVK Machine (ARM Cortex-M33)";
+ mc->init = rt595_evk_init;
+ mc->reset = rt595_evk_reset;
+
+ mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33");
+ mc->ignore_memory_transaction_failures = true;
+}
+
+DEFINE_MACHINE("rt595-evk", rt595_evk_machine_init);
--
2.46.0.rc2.264.g509ed76dc8-goog
^ permalink raw reply related [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-05 20:16 ` [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595 Octavian Purdila
@ 2024-08-06 14:06 ` Alex Bennée
2024-08-06 20:31 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Alex Bennée @ 2024-08-06 14:06 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
Octavian Purdila <tavip@google.com> writes:
> Picked from:
>
> https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
>
> NOTE: the file is truncated to keep the email size reasonable. Please
> use the link above and download the full file if you want to try out
> the patch.
>
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> hw/arm/svd/MIMXRT595S_cm33.xml | 224052
> ++++++++++++++++++++++++++++++
I guess one thing we need to decide is if the source XML should live in
the repository as the preferred method of making changes or just the
translations generated by the tool.
> 1 file changed, 224052 insertions(+)
> create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
>
> diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
> new file mode 100644
> index 0000000000..8943aa3555
> --- /dev/null
> +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
> @@ -0,0 +1,1725 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
> + <vendor>nxp.com</vendor>
> + <name>MIMXRT595S_cm33</name>
> + <version>1.0</version>
> + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
> + <licenseText>
> +Copyright 2016-2023 NXP
> +SPDX-License-Identifier: BSD-3-Clause
> + </licenseText>
This certainly seems compatible. XML is not the medium I personally
would have chosen as a register specification language but I guess there
are no other alternatives?
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC
2024-08-05 20:17 ` [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC Octavian Purdila
@ 2024-08-06 14:51 ` Philippe Mathieu-Daudé
2024-08-07 23:57 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-06 14:51 UTC (permalink / raw)
To: Octavian Purdila, qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, jsnow, crosa, bleal
Hi Octavian,
Few comments inlined.
On 5/8/24 22:17, Octavian Purdila wrote:
> Add basic support for the RT500 SoC. It supports enough peripherals to
> run the NXP's microXpresso SDK hello world example.
>
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> hw/arm/Kconfig | 8 +
> hw/arm/meson.build | 1 +
> hw/arm/rt500.c | 348 +++++++++++++++++++++++++++++++++++++++++
> hw/arm/svd/meson.build | 6 +
> include/hw/arm/rt500.h | 49 ++++++
> 5 files changed, 412 insertions(+)
> create mode 100644 hw/arm/rt500.c
> create mode 100644 include/hw/arm/rt500.h
If possible please setup scripts/git.orderfile, it help to
review.
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 1ad60da7aa..7ffece3dec 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -712,3 +712,11 @@ config ARMSSE
> select UNIMP
> select SSE_COUNTER
> select SSE_TIMER
> +
> +config RT500
> + bool
> + select FLEXCOMM
> + select RT500_CLKCTL0
> + select RT500_CLKCTL1
> + select FLEXSPI
> + select RT500_RSTCTL
> diff --git a/hw/arm/meson.build b/hw/arm/meson.build
> index eb604d00cf..7d827d512c 100644
> --- a/hw/arm/meson.build
> +++ b/hw/arm/meson.build
> @@ -59,6 +59,7 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
> arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
> arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
> arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
> +arm_ss.add(when: 'CONFIG_RT500', if_true: files('rt500.c'))
>
> system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
> system_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))
> diff --git a/hw/arm/rt500.c b/hw/arm/rt500.c
> new file mode 100644
> index 0000000000..0866ef3ef6
> --- /dev/null
> +++ b/hw/arm/rt500.c
> @@ -0,0 +1,348 @@
> +/*
> + * i.MX RT500 platforms.
> + *
> + * Copyright (c) 2024 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/sysbus.h"
> +#include "hw/arm/boot.h"
> +#include "hw/boards.h"
> +#include "hw/irq.h"
> +#include "qemu/log.h"
> +#include "qemu/datadir.h"
> +#include "exec/address-spaces.h"
> +#include "sysemu/reset.h"
> +#include "sysemu/runstate.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/arm/armv7m.h"
> +#include "hw/loader.h"
> +#include "hw/qdev-clock.h"
> +#include "hw/misc/unimp.h"
> +#include "hw/arm/rt500.h"
> +#include "hw/arm/svd/rt500.h"
> +
> +#define MMAP_SRAM_CODE_BASE (0x0)
> +#define MMAP_SRAM_DATA_BASE (0x20000000)
> +#define MMAP_SRAM_SIZE (5 * MiB)
> +#define MMAP_BOOT_ROM_BASE (0x03000000)
> +#define MMAP_BOOT_ROM_SIZE (192 * KiB)
> +#define MMAP_SDMA_RAM_BASE (0x24100000)
> +#define MMAP_SDMA_RAM_SIZE (32 * KiB)
> +#define MMAP_FLEXSPI0_BASE (0x08000000)
> +#define MMAP_FLEXSPI0_SIZE (128 * MiB)
> +#define MMAP_FLEXSPI1_BASE (0x28000000)
> +#define MMAP_FLEXSPI1_SIZE (128 * MiB)
> +
> +#define SECURE_OFFSET (0x10000000)
> +
> +typedef enum MemInfoType {
> + MEM_RAM,
> + MEM_ROM,
> + MEM_ALIAS
> +} MemInfoType;
> +
> +static void do_sys_reset(void *opaque, int n, int level)
> +{
> + if (level) {
> + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> + }
> +}
> +
> +static void rt500_init(Object *obj)
> +{
> + RT500State *s = RT500(obj);
> + int i;
> +
> + /* Add ARMv7-M device */
> + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
> +
> + for (i = 0; i < RT500_FLEXCOMM_NUM; i++) {
> + char id[] = "flexcommXX";
> +
> + snprintf(id, sizeof(id), "flexcomm%d", i);
> + object_initialize_child(obj, id, &s->flexcomm[i], TYPE_FLEXCOMM);
> + DEVICE(&s->flexcomm[i])->id = g_strdup(id);
Directly g_strdup_printf().
> + }
> +
> + object_initialize_child(obj, "clkctl0", &s->clkctl0, TYPE_RT500_CLKCTL0);
> + object_initialize_child(obj, "clkctl1", &s->clkctl1, TYPE_RT500_CLKCTL1);
> +
> + /* Initialize clocks */
> + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
> + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> +
> + for (i = 0; i < RT500_FLEXSPI_NUM; i++) {
> + char id[] = "flexspiXX";
> +
> + snprintf(id, sizeof(id), "flexspi%d", i);
> + object_initialize_child(obj, id, &s->flexspi[i], TYPE_FLEXSPI);
> + DEVICE(&s->flexspi[i])->id = g_strdup(id);
> + }
> +
> + for (i = 0; i < RT500_RSTCTL_NUM; i++) {
> + char id[] = "rstctlX";
> +
> + snprintf(id, sizeof(id), "rstctl%d", i);
> + object_initialize_child(obj, id, &s->rstctl[i],
> + TYPE_RT500_RSTCTL);
> + DEVICE(&s->rstctl[i])->id = g_strdup(id);
> + }
> +}
> +
> +static void rt500_realize_memory(RT500State *s, Error **errp)
> +{
static
> + const struct {
> + const char *name;
> + hwaddr base;
> + size_t size;
> + MemInfoType type;
> + int alias_for;
> + } mem_info[] = {
> + {
> + .name = "SRAM (code bus)",
> + .base = MMAP_SRAM_CODE_BASE,
> + .size = MMAP_SRAM_SIZE,
> + .type = MEM_RAM,
> + },
> + {
> + .name = "BOOT-ROM",
> + .base = MMAP_BOOT_ROM_BASE,
> + .size = MMAP_BOOT_ROM_SIZE,
> + .type = MEM_ROM,
> + },
> + {
> + .name = "Smart DMA RAM",
> + .base = MMAP_SDMA_RAM_BASE,
> + .size = MMAP_SDMA_RAM_SIZE,
> + .type = MEM_RAM,
> + },
> + {
> + .name = "SRAM (data bus)",
> + .base = MMAP_SRAM_DATA_BASE,
> + .size = MMAP_SRAM_SIZE,
> + .type = MEM_ALIAS,
> + .alias_for = 0
> + },
> + };
> + int i;
> +
> + s->mem = g_malloc_n(2 * ARRAY_SIZE(mem_info), sizeof(MemoryRegion));
> + for (i = 0; i < ARRAY_SIZE(mem_info); i++) {
> + const char *name = mem_info[i].name;
> + int size = mem_info[i].size;
> + int type = mem_info[i].type;
> + int alias_for = mem_info[i].alias_for;
> + MemoryRegion *mem = &s->mem[i];
> + uint32_t base = mem_info[i].base;
> + MemoryRegion *sec_mem;
> + char sec_name[256];
> +
> + switch (type) {
> + case MEM_RAM:
> + memory_region_init_ram(mem, OBJECT(s), name, size, errp);
> + break;
> + case MEM_ROM:
> + memory_region_init_rom(mem, OBJECT(s), name, size, errp);
> + break;
> + case MEM_ALIAS:
> + {
> + MemoryRegion *orig = &s->mem[alias_for];
> +
> + memory_region_init_alias(mem, OBJECT(s), name, orig, 0, size);
> + break;
> + }
> + default:
> + g_assert_not_reached();
> + }
> +
> + memory_region_add_subregion(get_system_memory(), base, mem);
> +
> + /* create secure alias */
> + snprintf(sec_name, sizeof(sec_name), "SECURE %s", name);
> + sec_mem = &s->mem[ARRAY_SIZE(mem_info) + i];
> + if (type == MEM_ALIAS) {
> + mem = &s->mem[alias_for];
> + }
> + memory_region_init_alias(sec_mem, OBJECT(s), sec_name, mem, 0, size);
> + memory_region_add_subregion(get_system_memory(), base + SECURE_OFFSET,
> + sec_mem);
> +
> + if (mem_info[i].type == MEM_ROM) {
> + char *fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, "rt500.rom");
> +
> + if (fname) {
> + int fsize = get_image_size(fname);
> + int ret;
> +
> + if (fsize > size) {
> + error_setg(errp, "rom file too big: %d > %d", fsize, size);
> + } else {
> + ret = load_image_targphys(fname, base, size);
> + if (ret < 0) {
> + error_setg(errp, "could not load rom: %s", fname);
> + }
> + }
> + }
> + g_free(fname);
> + }
> + }
> +}
> +
> +static void rt500_realize(DeviceState *dev, Error **errp)
> +{
> + MachineState *ms = MACHINE(qdev_get_machine());
> + RT500State *s = RT500(dev);
> + int i;
Preferably reduce iterator variables scope.
> +
> + rt500_realize_memory(s, errp);
> +
> + /* Setup ARMv7M CPU */
> + qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq",
> + RT500_FLEXCOMM16_IRQn + 1);
Preferably a definition:
#define RT500_NUM_IRQ (RT500_FLEXCOMM16_IRQn + 1)
> + qdev_prop_set_uint8(DEVICE(&s->armv7m), "num-prio-bits", 3);
> + qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ms->cpu_type);
Do you plan to add RT500 machines not based on the Cortex-M33?
Otherwise simpler to hardcode the CPU type here.
> + object_property_set_link(OBJECT(&s->armv7m), "memory",
> + OBJECT(get_system_memory()), &error_abort);
> + if (!ms->kernel_filename) {
> + qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-nsvtor",
> + MMAP_BOOT_ROM_BASE);
> + qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor",
> + MMAP_BOOT_ROM_BASE + SECURE_OFFSET);
> + }
> +
> + qdev_connect_clock_in(DEVICE(&s->armv7m), "cpuclk", s->sysclk);
> + qdev_connect_clock_in(DEVICE(&s->armv7m), "refclk",
> + qdev_get_clock_out(DEVICE(&s->clkctl0), "systick_clk"));
> +
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->armv7m), errp);
> + qdev_connect_gpio_out_named(DEVICE(&s->armv7m), "SYSRESETREQ", 0,
> + qemu_allocate_irq(&do_sys_reset, NULL, 0));
> +
> + /* Setup FLEXCOMM */
> + for (i = 0; i < RT500_FLEXCOMM_NUM; i++) {
> + const uint32_t addr[] = {
static
> + RT500_FLEXCOMM0_BASE, RT500_FLEXCOMM1_BASE, RT500_FLEXCOMM2_BASE,
> + RT500_FLEXCOMM3_BASE, RT500_FLEXCOMM4_BASE, RT500_FLEXCOMM5_BASE,
> + RT500_FLEXCOMM6_BASE, RT500_FLEXCOMM7_BASE, RT500_FLEXCOMM8_BASE,
> + RT500_FLEXCOMM8_BASE, RT500_FLEXCOMM10_BASE, RT500_FLEXCOMM11_BASE,
> + RT500_FLEXCOMM12_BASE, RT500_FLEXCOMM13_BASE, RT500_FLEXCOMM14_BASE,
> + RT500_FLEXCOMM15_BASE, RT500_FLEXCOMM16_BASE
> + };
> + const int irq[] = {
> + RT500_FLEXCOMM0_IRQn, RT500_FLEXCOMM1_IRQn, RT500_FLEXCOMM2_IRQn,
> + RT500_FLEXCOMM3_IRQn, RT500_FLEXCOMM4_IRQn, RT500_FLEXCOMM5_IRQn,
> + RT500_FLEXCOMM6_IRQn, RT500_FLEXCOMM7_IRQn, RT500_FLEXCOMM8_IRQn,
> + RT500_FLEXCOMM9_IRQn, RT500_FLEXCOMM10_IRQn, RT500_FLEXCOMM11_IRQn,
> + RT500_FLEXCOMM12_IRQn, RT500_FLEXCOMM13_IRQn, RT500_FLEXCOMM14_IRQn,
> + RT500_FLEXCOMM15_IRQn, RT500_FLEXCOMM16_IRQn
> + };
> + const int functions[] = {
> + FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
> + FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
> + FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
> + FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_FULL,
> + FLEXCOMM_FULL, FLEXCOMM_FULL, FLEXCOMM_HSSPI,
> + FLEXCOMM_PMICI2C, FLEXCOMM_HSSPI
> + };
> + DeviceState *ds = DEVICE(&s->flexcomm[i]);
> + char id[] = "flexcommXX";
> +
> + snprintf(id, sizeof(id), "flexcomm%d", i);
> + qdev_prop_set_uint32(ds, "functions", functions[i]);
> + qdev_prop_set_chr(ds, "chardev", qemu_chr_find(id));
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
> + sysbus_connect_irq(SYS_BUS_DEVICE(ds), 0,
> + qdev_get_gpio_in(DEVICE(&s->armv7m), irq[i]));
> + }
> +
> + /* Setup CTLCTL0 */
> + qdev_connect_clock_in(DEVICE(&s->clkctl0), "sysclk", s->sysclk);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(DEVICE(&s->clkctl0)), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(DEVICE(&s->clkctl0)), 0, RT500_CLKCTL0_BASE);
> +
> + /* Setup CTLCTL1 */
> + qdev_connect_clock_in(DEVICE(&s->clkctl1), "sysclk", s->sysclk);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(DEVICE(&s->clkctl1)), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(DEVICE(&s->clkctl1)), 0, RT500_CLKCTL1_BASE);
> +
> + /* Setup FlexSPI */
> + for (i = 0; i < RT500_FLEXSPI_NUM; i++) {
> + const uint32_t addr[] = {
static
> + RT500_FLEXSPI0_BASE, RT500_FLEXSPI1_BASE
> + };
> + const uint32_t mmap_base[] = {
> + MMAP_FLEXSPI0_BASE, MMAP_FLEXSPI1_BASE
> + };
> + const uint32_t mmap_size[] = {
> + MMAP_FLEXSPI0_SIZE, MMAP_FLEXSPI1_SIZE,
> + };
> + DeviceState *ds = DEVICE(&s->flexspi[i]);
> +
> + qdev_prop_set_uint32(ds, "mmap_base", mmap_base[i]);
> + qdev_prop_set_uint32(ds, "mmap_size", mmap_size[i]);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
> + }
> +
> + /* Setup reset controllers */
> + for (i = 0; i < RT500_RSTCTL_NUM; i++) {
> + DeviceState *ds = DEVICE(&s->rstctl[i]);
> +
> + const uint32_t addr[] = {
static
> + RT500_RSTCTL0_BASE, RT500_RSTCTL1_BASE
> + };
> +
> + qdev_prop_set_uint32(ds, "num", i);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, addr[i]);
> + }
> +}
> +
> +static void rt500_finalize(Object *obj)
> +{
> + RT500State *s = RT500(obj);
> +
> + g_free(s->mem);
Allocated via rt500_realize() -> rt500_realize_memory() so
to be released in a DeviceUnrealize() handler.
> +}
> +
> +static void rt500_reset(DeviceState *ds)
> +{
> +}
> +
> +static Property rt500_properties[] = {
To be filled later?
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void rt500_class_init(ObjectClass *oc, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(oc);
> +
> + device_class_set_props(dc, rt500_properties);
> + dc->realize = rt500_realize;
> + dc->desc = "RT500 (ARM Cortex-M33)";
> + dc->reset = rt500_reset;
> +}
> +
> +static const TypeInfo rt500_type_info = {
> + .name = TYPE_RT500,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(RT500State),
> + .instance_init = rt500_init,
> + .instance_finalize = rt500_finalize,
> + .class_init = rt500_class_init,
> +};
> +
> +static void rt500_register_types(void)
> +{
> + type_register_static(&rt500_type_info);
Preferably use the DEFINE_TYPES() macro.
> +}
> +
> +type_init(rt500_register_types)
> diff --git a/hw/arm/svd/meson.build b/hw/arm/svd/meson.build
> index 72a7421c6f..930a8b7343 100644
> --- a/hw/arm/svd/meson.build
> +++ b/hw/arm/svd/meson.build
> @@ -34,3 +34,9 @@ genh += custom_target('rt500_rstctl1.h',
> output: 'rt500_rstctl1.h',
> input: 'MIMXRT595S_cm33.xml',
> command: [ svd_gen_header, '-i', '@INPUT@', '-o', '@OUTPUT@', '-p', 'RSTCTL1', '-t', 'RT500_RSTCTL1'])
> +genh += custom_target('rt500.h',
> + output: 'rt500.h',
> + input: 'MIMXRT595S_cm33.xml',
> + command: [ svd_gen_header,'-i', '@INPUT@', '-o', '@OUTPUT@', '-s', 'RT500',
> + '-p', 'FLEXCOMM0', '-p', 'CLKCTL0', '-p', 'CLKCTL1',
> + '-p', 'FLEXSPI0', '-p', 'FLEXSPI1', '-p', 'RSTCTL0', '-p', 'RSTCTL1'])
> diff --git a/include/hw/arm/rt500.h b/include/hw/arm/rt500.h
> new file mode 100644
> index 0000000000..8ca7972f8a
> --- /dev/null
> +++ b/include/hw/arm/rt500.h
> @@ -0,0 +1,49 @@
> +/*
> + * i.MX RT500 platforms.
> + *
> + * Copyright (c) 2024 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Contributions after 2012-01-13 are licensed under the terms of the
> + * GNU GPL, version 2 or (at your option) any later version.
> + */
> +
> +#ifndef HW_ARM_RT500_H
> +#define HW_ARM_RT500_H
> +
> +#include "qemu/osdep.h"
No osdep.h in header.
> +#include "qemu/units.h"
Not used (move to source).
> +#include "hw/arm/boot.h"
Not used.
> +#include "hw/arm/armv7m.h"
> +#include "hw/misc/flexcomm.h"
> +#include "hw/misc/rt500_clkctl0.h"
> +#include "hw/misc/rt500_clkctl1.h"
> +#include "hw/ssi/flexspi.h"
> +#include "hw/misc/rt500_rstctl.h"
> +
> +#define TYPE_RT500 "rt500"
> +#define RT500(obj) OBJECT_CHECK(RT500State, (obj), TYPE_RT500)
> +
> +#define RT500_FLEXCOMM_NUM (17)
> +#define RT500_FLEXSPI_NUM (2)
> +#define RT500_RSTCTL_NUM (2)
> +
> +typedef struct RT500State {
> + /*< private >*/
> + SysBusDevice parent_obj;
> +
> + /*< public >*/
Please drop private/public comments.
> + ARMv7MState armv7m;
> + MemoryRegion *mem;
> + FlexcommState flexcomm[RT500_FLEXCOMM_NUM];
> + RT500ClkCtl0State clkctl0;
> + RT500ClkCtl1State clkctl1;
> + FlexSpiState flexspi[RT500_FLEXSPI_NUM];
> + RT500RstCtlState rstctl[RT500_RSTCTL_NUM];
> +
> + Clock *sysclk;
> + Clock *refclk;
> +} RT500State;
> +
> +#endif /* HW_ARM_RT500_H */
LGTM,
Phil.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-06 14:06 ` Alex Bennée
@ 2024-08-06 20:31 ` Octavian Purdila
2024-08-07 11:24 ` Philippe Mathieu-Daudé
2024-08-09 9:13 ` Daniel P. Berrangé
0 siblings, 2 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-06 20:31 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Tue, Aug 6, 2024 at 7:06 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Octavian Purdila <tavip@google.com> writes:
>
> > Picked from:
> >
> > https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
> >
> > NOTE: the file is truncated to keep the email size reasonable. Please
> > use the link above and download the full file if you want to try out
> > the patch.
> >
> > Signed-off-by: Octavian Purdila <tavip@google.com>
> > ---
> > hw/arm/svd/MIMXRT595S_cm33.xml | 224052
> > ++++++++++++++++++++++++++++++
>
> I guess one thing we need to decide is if the source XML should live in
> the repository as the preferred method of making changes or just the
> translations generated by the tool.
>
I think we might want to store the XML in the qemu repo, even if we
don't use it to generate the header files at compile time. This avoids
issues with the original XML moving, going away, changed in
incompatible ways, etc.
As for generating the headers at compile time, I don't have a strong
preference. I like it because there is slightly less work to do and it
avoids dealing with resolving changes on both the SVD and the
generated headers. For example, the initial headers are committed,
then some changes are done directly to the headers and then we want to
pick up a new SVD from the vendor to support a new hardware revision.
There are disadvantages as well: pysvd dependency for building qemu,
hard to review if the vendor dumps a new version with lots of changes
and we want to update to it for a new hardware revision, slight
increase in build time.
> > 1 file changed, 224052 insertions(+)
> > create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
> >
> > diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
> > new file mode 100644
> > index 0000000000..8943aa3555
> > --- /dev/null
> > +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
> > @@ -0,0 +1,1725 @@
> > +<?xml version="1.0" encoding="UTF-8"?>
> > +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
> > + <vendor>nxp.com</vendor>
> > + <name>MIMXRT595S_cm33</name>
> > + <version>1.0</version>
> > + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
> > + <licenseText>
> > +Copyright 2016-2023 NXP
> > +SPDX-License-Identifier: BSD-3-Clause
> > + </licenseText>
>
> This certainly seems compatible. XML is not the medium I personally
> would have chosen as a register specification language but I guess there
> are no other alternatives?
>
I agree that the choice of XML is unfortunate but I am not aware of
alternatives, this is what vendors will provide.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-06 20:31 ` Octavian Purdila
@ 2024-08-07 11:24 ` Philippe Mathieu-Daudé
2024-08-07 16:36 ` Octavian Purdila
2024-08-09 9:13 ` Daniel P. Berrangé
1 sibling, 1 reply; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-07 11:24 UTC (permalink / raw)
To: Octavian Purdila, Alex Bennée
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, jsnow, crosa, bleal,
Daniel P. Berrangé
On 6/8/24 22:31, Octavian Purdila wrote:
> On Tue, Aug 6, 2024 at 7:06 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Octavian Purdila <tavip@google.com> writes:
>>
>>> Picked from:
>>>
>>> https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
>>>
>>> NOTE: the file is truncated to keep the email size reasonable. Please
>>> use the link above and download the full file if you want to try out
>>> the patch.
>>>
>>> Signed-off-by: Octavian Purdila <tavip@google.com>
>>> ---
>>> hw/arm/svd/MIMXRT595S_cm33.xml | 224052
>>> ++++++++++++++++++++++++++++++
>>
>> I guess one thing we need to decide is if the source XML should live in
>> the repository as the preferred method of making changes or just the
>> translations generated by the tool.
>>
>
> I think we might want to store the XML in the qemu repo, even if we
> don't use it to generate the header files at compile time. This avoids
> issues with the original XML moving, going away, changed in
> incompatible ways, etc.
Until now we tracked external sources with git submodules or meson
wrap files (see commit 2019cabfee) forked into our GitLab namespace
(https://gitlab.com/qemu-project/) at a particular commit, so if
the external project is modified, we aren't disturbed, or have to
adapt our source to update the submodule. Isn't it good enough?
>
> As for generating the headers at compile time, I don't have a strong
> preference. I like it because there is slightly less work to do and it
> avoids dealing with resolving changes on both the SVD and the
> generated headers. For example, the initial headers are committed,
> then some changes are done directly to the headers and then we want to
> pick up a new SVD from the vendor to support a new hardware revision.
>
> There are disadvantages as well: pysvd dependency for building qemu,
> hard to review if the vendor dumps a new version with lots of changes
> and we want to update to it for a new hardware revision, slight
> increase in build time.
>
>>> 1 file changed, 224052 insertions(+)
>>> create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
>>>
>>> diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
>>> new file mode 100644
>>> index 0000000000..8943aa3555
>>> --- /dev/null
>>> +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
>>> @@ -0,0 +1,1725 @@
>>> +<?xml version="1.0" encoding="UTF-8"?>
>>> +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
>>> + <vendor>nxp.com</vendor>
>>> + <name>MIMXRT595S_cm33</name>
>>> + <version>1.0</version>
>>> + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
>>> + <licenseText>
>>> +Copyright 2016-2023 NXP
>>> +SPDX-License-Identifier: BSD-3-Clause
>>> + </licenseText>
>>
>> This certainly seems compatible. XML is not the medium I personally
>> would have chosen as a register specification language but I guess there
>> are no other alternatives?
>>
>
> I agree that the choice of XML is unfortunate but I am not aware of
> alternatives, this is what vendors will provide.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-07 11:24 ` Philippe Mathieu-Daudé
@ 2024-08-07 16:36 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-07 16:36 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Alex Bennée, qemu-devel, qemu-arm, stefanst, pbonzini, thuth,
peter.maydell, marcandre.lureau, alistair, jsnow, crosa, bleal,
Daniel P. Berrangé
On Wed, Aug 7, 2024 at 4:24 AM Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 6/8/24 22:31, Octavian Purdila wrote:
> > On Tue, Aug 6, 2024 at 7:06 AM Alex Bennée <alex.bennee@linaro.org> wrote:
> >>
> >> Octavian Purdila <tavip@google.com> writes:
> >>
> >>> Picked from:
> >>>
> >>> https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
> >>>
> >>> NOTE: the file is truncated to keep the email size reasonable. Please
> >>> use the link above and download the full file if you want to try out
> >>> the patch.
> >>>
> >>> Signed-off-by: Octavian Purdila <tavip@google.com>
> >>> ---
> >>> hw/arm/svd/MIMXRT595S_cm33.xml | 224052
> >>> ++++++++++++++++++++++++++++++
> >>
> >> I guess one thing we need to decide is if the source XML should live in
> >> the repository as the preferred method of making changes or just the
> >> translations generated by the tool.
> >>
> >
> > I think we might want to store the XML in the qemu repo, even if we
> > don't use it to generate the header files at compile time. This avoids
> > issues with the original XML moving, going away, changed in
> > incompatible ways, etc.
>
> Until now we tracked external sources with git submodules or meson
> wrap files (see commit 2019cabfee) forked into our GitLab namespace
> (https://gitlab.com/qemu-project/) at a particular commit, so if
> the external project is modified, we aren't disturbed, or have to
> adapt our source to update the submodule. Isn't it good enough?
>
Yes, this should definitely work. Thanks for pointing out the wrap
files, I'll give it a try.
> >
> > As for generating the headers at compile time, I don't have a strong
> > preference. I like it because there is slightly less work to do and it
> > avoids dealing with resolving changes on both the SVD and the
> > generated headers. For example, the initial headers are committed,
> > then some changes are done directly to the headers and then we want to
> > pick up a new SVD from the vendor to support a new hardware revision.
> >
> > There are disadvantages as well: pysvd dependency for building qemu,
> > hard to review if the vendor dumps a new version with lots of changes
> > and we want to update to it for a new hardware revision, slight
> > increase in build time.
> >
> >>> 1 file changed, 224052 insertions(+)
> >>> create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
> >>>
> >>> diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
> >>> new file mode 100644
> >>> index 0000000000..8943aa3555
> >>> --- /dev/null
> >>> +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
> >>> @@ -0,0 +1,1725 @@
> >>> +<?xml version="1.0" encoding="UTF-8"?>
> >>> +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
> >>> + <vendor>nxp.com</vendor>
> >>> + <name>MIMXRT595S_cm33</name>
> >>> + <version>1.0</version>
> >>> + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
> >>> + <licenseText>
> >>> +Copyright 2016-2023 NXP
> >>> +SPDX-License-Identifier: BSD-3-Clause
> >>> + </licenseText>
> >>
> >> This certainly seems compatible. XML is not the medium I personally
> >> would have chosen as a register specification language but I guess there
> >> are no other alternatives?
> >>
> >
> > I agree that the choice of XML is unfortunate but I am not aware of
> > alternatives, this is what vendors will provide.
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC
2024-08-06 14:51 ` Philippe Mathieu-Daudé
@ 2024-08-07 23:57 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-07 23:57 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, jsnow, crosa,
bleal
On Tue, Aug 6, 2024 at 7:51 AM Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> Hi Octavian,
>
> Few comments inlined.
>
Hi Philippe,
Thank you for the review!
I have queued fixes for all of the patches in the set where
applicable, I'll wait for more feedback on the rest of the patches
before sending v2.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller
2024-08-05 20:17 ` [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller Octavian Purdila
@ 2024-08-08 5:00 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-08 5:00 UTC (permalink / raw)
To: Octavian Purdila, qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, jsnow, crosa, bleal
Hi Octavian,
On 5/8/24 22:17, Octavian Purdila wrote:
> The RT500 reset controller has two instances that have the same
> register layout but with different fields for some registers.
>
> The model only provides set and clear functionality for the various
> reset lines which is common for both instances. Because of that only
> one type is implemented for both controllers.
>
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> hw/arm/svd/meson.build | 8 ++
> hw/misc/Kconfig | 3 +
> hw/misc/meson.build | 1 +
> hw/misc/rt500_rstctl.c | 219 +++++++++++++++++++++++++++++++++
> hw/misc/trace-events | 4 +
> include/hw/misc/rt500_rstctl.h | 38 ++++++
> 6 files changed, 273 insertions(+)
> create mode 100644 hw/misc/rt500_rstctl.c
> create mode 100644 include/hw/misc/rt500_rstctl.h
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 68929949a6..5e2728e982 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -160,3 +160,4 @@ system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))
> system_ss.add(when: 'CONFIG_FLEXCOMM', if_true: files('flexcomm.c'))
> system_ss.add(when: 'CONFIG_RT500_CLKCTL0', if_true: files('rt500_clkctl0.c'))
> system_ss.add(when: 'CONFIG_RT500_CLKCTL1', if_true: files('rt500_clkctl1.c'))
> +system_ss.add(when: 'CONFIG_RT500_RSTCTL', if_true: files('rt500_rstctl.c'))
> diff --git a/hw/misc/rt500_rstctl.c b/hw/misc/rt500_rstctl.c
> new file mode 100644
> index 0000000000..2806a94150
> --- /dev/null
> +++ b/hw/misc/rt500_rstctl.c
> @@ -0,0 +1,219 @@
> +/*
> + * QEMU model for RT500 Reset Controller
> + *
> + * Copyright (c) 2024 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "exec/address-spaces.h"
> +#include "hw/regs.h"
> +#include "hw/misc/rt500_rstctl.h"
> +
> +#include "trace.h"
> +
> +/*
> + * There are two intances for RSTCTL with the same register names but
> + * with different fields.
> + */
> +#define reg(field) offsetof(RT500_RSTCTL0_Type, field)
> +#define regi(x) (reg(x) / sizeof(uint32_t))
> +#define REG_NO (sizeof(RT500_RSTCTL0_Type) / sizeof(uint32_t))
> +
> +#define RSTCTL_SYSRSTSTAT_WMASK (BITS(7, 4) | BIT(0))
> +#define RSTCL0_PRSCTL0_WMASK (BITS(30, 26) | BITS(24, 20) | BIT(18) | \
> + BIT(16) | BITS(12, 8) | BIT(3) | BIT(1))
> +#define RSTCL0_PRSCTL1_WMASK (BIT(24) | BITS(16, 15) | BITS(3, 2))
> +#define RSTCL0_PRSCTL2_WMASK (BITS(1, 0))
> +#define RSTCL1_PRSCTL0_WMASK (BIT(29) | BIT(27) | BITS(25, 8))
> +#define RSTCL1_PRSCTL1_WMASK (BIT(31) | BITS(29, 28) | BITS(24, 23) | \
> + BIT(16) | BITS(7, 0))
> +#define RSTCL1_PRSCTL2_WMASK (BITS(31, 30) | BITS(17, 16) | BIT(10) | \
> + BIT(8) | BITS(4, 0))
> +
> +static RT500_RSTCTL0_REGISTER_NAMES_ARRAY(reg_names);
> +
> +static MemTxResult rt500_rstctl_read(void *opaque, hwaddr addr,
> + uint64_t *data, unsigned size,
> + MemTxAttrs attrs)
> +{
> + RT500RstCtlState *s = opaque;
> + MemTxResult ret = MEMTX_OK;
> +
> + if (s->num > 1 || !reg32_aligned_access(addr, size)) {
IIUC s->num is a model property set when the device is created,
and can not be changed by the guest. We want to check it
in rt500_rstctl_realize() so we can return an error when out
of range.
> + ret = MEMTX_ERROR;
> + goto out;
> + }
> +
> + switch (addr) {
> + case reg(SYSRSTSTAT):
> + case reg(PRSTCTL0):
> + case reg(PRSTCTL1):
> + case reg(PRSTCTL2):
> + *data = reg32_read(&s->regs.ctl0, addr);
> + break;
> + default:
> + ret = MEMTX_ERROR;
> + }
> +
> +out:
> + trace_rt500_rstctl_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
> + return ret;
> +}
> +
> +static MemTxResult rt500_rstctl_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size,
> + MemTxAttrs attrs)
> +{
> + RT500RstCtlState *s = opaque;
> + static uint32_t mask0[REG_NO] = {
const
> + [regi(SYSRSTSTAT)] = RSTCTL_SYSRSTSTAT_WMASK,
> + [regi(PRSTCTL0)] = RSTCL0_PRSCTL0_WMASK,
> + [regi(PRSTCTL1)] = RSTCL0_PRSCTL1_WMASK,
> + [regi(PRSTCTL2)] = RSTCL0_PRSCTL2_WMASK,
> + [regi(PRSTCTL0_SET)] = RSTCL0_PRSCTL0_WMASK,
> + [regi(PRSTCTL1_SET)] = RSTCL0_PRSCTL1_WMASK,
> + [regi(PRSTCTL2_SET)] = RSTCL0_PRSCTL2_WMASK,
> + [regi(PRSTCTL0_CLR)] = RSTCL0_PRSCTL0_WMASK,
> + [regi(PRSTCTL1_CLR)] = RSTCL0_PRSCTL1_WMASK,
> + [regi(PRSTCTL2_CLR)] = RSTCL0_PRSCTL2_WMASK,
> + };
> + static uint32_t mask1[REG_NO] = {
> + [regi(SYSRSTSTAT)] = RSTCTL_SYSRSTSTAT_WMASK,
> + [regi(PRSTCTL0)] = RSTCL1_PRSCTL0_WMASK,
> + [regi(PRSTCTL1)] = RSTCL1_PRSCTL1_WMASK,
> + [regi(PRSTCTL2)] = RSTCL1_PRSCTL2_WMASK,
> + [regi(PRSTCTL0_SET)] = RSTCL1_PRSCTL0_WMASK,
> + [regi(PRSTCTL1_SET)] = RSTCL1_PRSCTL1_WMASK,
> + [regi(PRSTCTL2_SET)] = RSTCL1_PRSCTL2_WMASK,
> + [regi(PRSTCTL0_CLR)] = RSTCL1_PRSCTL0_WMASK,
> + [regi(PRSTCTL1_CLR)] = RSTCL1_PRSCTL1_WMASK,
> + [regi(PRSTCTL2_CLR)] = RSTCL1_PRSCTL2_WMASK,
> + };
Possibly better, have a common abstract TYPE_RT500_RSTCTL class
and 2 TYPE_RT500_RSTCTL[01] concrete classes, wmask being set for
each rt500_rstctl[01]_class_init(). Then you don't need the "num"
property. See how hw/arm/raspi.c is modelled.
> + uint32_t mask;
> +
> + trace_rt500_rstctl_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
> +
> + if (s->num > 1 || !reg32_aligned_access(addr, size)) {
> + return MEMTX_ERROR;
> + }
> +
> + if (s->num == 0) {
> + mask = mask0[addr / sizeof(uint32_t)];
> + } else {
> + mask = mask1[addr / sizeof(uint32_t)];
> + }
> +
> + switch (addr) {
> + case reg(SYSRSTSTAT):
> + {
> + /* write 1 to clear bits */
> + s->regs.ctl0.SYSRSTSTAT &= ~(value & mask);
> + break;
> + }
> + case reg(PRSTCTL0):
> + case reg(PRSTCTL1):
> + case reg(PRSTCTL2):
> + {
> + uint32_t idx = addr / sizeof(uint32_t);
> +
> + s->regs.raw[idx] = (value & mask);
> + break;
> + }
> + case reg(PRSTCTL0_SET):
> + case reg(PRSTCTL1_SET):
> + case reg(PRSTCTL2_SET):
> + {
> + uint32_t idx;
> +
> + idx = (reg(PRSTCTL0) + (addr - reg(PRSTCTL0_SET))) / sizeof(uint32_t);
> + s->regs.raw[idx] |= (value & mask);
> + break;
> + }
> + case reg(PRSTCTL0_CLR):
> + case reg(PRSTCTL1_CLR):
> + case reg(PRSTCTL2_CLR):
> + {
> + uint32_t idx;
> +
> + idx = (reg(PRSTCTL0) + (addr - reg(PRSTCTL0_CLR))) / sizeof(uint32_t);
> + s->regs.raw[idx] &= ~(value & mask);
> + break;
> + }
> + }
> +
> + return MEMTX_OK;
> +}
> +
> +
> +static const MemoryRegionOps rt500_rstctl_ops = {
> + .read_with_attrs = rt500_rstctl_read,
> + .write_with_attrs = rt500_rstctl_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static Property rt500_rstctl_properties[] = {
> + DEFINE_PROP_UINT32("num", RT500RstCtlState, num, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void rt500_rstctl_reset(DeviceState *dev)
> +{
> + RT500RstCtlState *s = RT500_RSTCTL(dev);
> +
> + memset(&s->regs, 0, sizeof(s->regs));
> +
> + switch (s->num) {
> + case 0:
> + rt500_rstctl0_reset_registers(&s->regs.ctl0);
> + break;
> + case 1:
> + rt500_rstctl1_reset_registers(&s->regs.ctl1);
> + break;
> + }
> +}
> +
> +static void rt500_rstctl_init(Object *obj)
> +{
> + RT500RstCtlState *s = RT500_RSTCTL(obj);
> +
> + memory_region_init_io(&s->mmio, obj, &rt500_rstctl_ops, s,
> + TYPE_RT500_RSTCTL, sizeof(s->regs));
> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +}
> +
> +static void rt500_rstctl_realize(DeviceState *dev, Error **errp)
> +{
> +}
> +
> +static void rt500_rstctl_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->reset = rt500_rstctl_reset;
> + device_class_set_props(dc, rt500_rstctl_properties);
> + dc->realize = rt500_rstctl_realize;
> +}
> +
> +static const TypeInfo rt500_rstctl_info = {
> + .name = TYPE_RT500_RSTCTL,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(RT500RstCtlState),
> + .instance_init = rt500_rstctl_init,
> + .class_init = rt500_rstctl_class_init,
> +};
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 20/23] hw/ssi: add support for flexspi
2024-08-05 20:17 ` [RFC PATCH 20/23] hw/ssi: add support for flexspi Octavian Purdila
@ 2024-08-08 5:11 ` Philippe Mathieu-Daudé
2024-08-08 21:31 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-08 5:11 UTC (permalink / raw)
To: Octavian Purdila, qemu-devel
Cc: qemu-arm, stefanst, pbonzini, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, jsnow, crosa, bleal
Hi Octavian,
On 5/8/24 22:17, Octavian Purdila wrote:
> This is mostly a stub which completes SPI transactions as noops
> by masking out the error interrupts and never clearing the IPCMDDONE
> interrupt.
>
> Although incomplete, this allows software that uses NXP's mcuxpresso
> SDK to run the SDK board initialization functions.
>
> It also supports AHB memory access, aka XIP, for now as simple RAM
> memory regions.
>
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> hw/arm/svd/meson.build | 4 +
> hw/ssi/Kconfig | 4 +
> hw/ssi/flexspi.c | 216 +++++++++++++++++++++++++++++++++++++++
> hw/ssi/meson.build | 1 +
> hw/ssi/trace-events | 4 +
> include/hw/ssi/flexspi.h | 34 ++++++
> 6 files changed, 263 insertions(+)
> create mode 100644 hw/ssi/flexspi.c
> create mode 100644 include/hw/ssi/flexspi.h
> diff --git a/hw/ssi/flexspi.c b/hw/ssi/flexspi.c
> new file mode 100644
> index 0000000000..305d1a5bac
> --- /dev/null
> +++ b/hw/ssi/flexspi.c
> @@ -0,0 +1,216 @@
> +/*
> + * QEMU model for FLEXSPI
> + *
> + * Copyright (c) 2024 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/mmap-alloc.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "migration/vmstate.h"
> +#include "exec/address-spaces.h"
> +#include "hw/regs.h"
> +#include "hw/ssi/flexspi.h"
> +#include "hw/arm/svd/flexspi.h"
> +
> +#include "trace.h"
> +
> +#define reg(field) offsetof(FLEXSPI_Type, field)
> +#define regi(x) (reg(x) / sizeof(uint32_t))
> +#define REG_NO (sizeof(FLEXSPI_Type) / sizeof(uint32_t))
> +
> +static FLEXSPI_REGISTER_NAMES_ARRAY(reg_names);
> +
> +static void flexspi_reset(DeviceState *dev)
> +{
> + FlexSpiState *s = FLEXSPI(dev);
> +
> + memset(&s->regs, 0, sizeof(s->regs));
> +
> + flexspi_reset_registers(&s->regs);
> +
> + /* idle immediately after reset */
> + s->regs.STS0_b.SEQIDLE = 1;
> +}
> +
> +static MemTxResult flexspi_read(void *opaque, hwaddr addr,
> + uint64_t *data, unsigned size,
> + MemTxAttrs attrs)
> +{
> + FlexSpiState *s = opaque;
> + MemTxResult ret = MEMTX_OK;
> +
> + if (!reg32_aligned_access(addr, size)) {
> + ret = MEMTX_ERROR;
> + goto out;
> + }
> +
> + switch (addr) {
> + default:
> + *data = reg32_read(&s->regs, addr);
> + break;
> + }
> +
> +out:
> + trace_flexspi_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
> + return ret;
> +}
> +
> +static uint32_t wr_mask[REG_NO] = {
> + [regi(MCR0)] = BITS(31, 14) | BITS(12, 8) | BITS(5, 4) | BITS(1, 0),
> + [regi(MCR1)] = BITS(31, 0),
> + [regi(MCR2)] = BITS(31, 24) | BIT(11),
> + [regi(AHBCR)] = BIT(10) | BITS(7, 2) | BIT(0),
> + [regi(INTEN)] = BITS(13, 0),
> + /*
> + * TODO: once SPI transfers are implemented restore mask to:
> + *
> + * [regi(INTR)] = BIT(16) | BITS(12, 0).
> + *
> + * In the meantime this INTR mask allows for fake SPI transfers.
> + */
> + [regi(INTR)] = BIT(0),
> + [regi(LUTKEY)] = BITS(31, 0),
> + [regi(LUTCR)] = BITS(1, 0),
> + [regi(AHBRXBUF0CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF1CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF2CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF3CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF4CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF5CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF6CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(AHBRXBUF7CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> + [regi(FLSHA1CR0)] = BITS(22, 0),
> + [regi(FLSHA2CR0)] = BITS(22, 0),
> + [regi(FLSHB1CR0)] = BITS(22, 0),
> + [regi(FLSHB2CR0)] = BITS(22, 0),
> + [regi(FLSHCR1A1)] = BITS(31, 0),
> + [regi(FLSHCR1A2)] = BITS(31, 0),
> + [regi(FLSHCR1B1)] = BITS(31, 0),
> + [regi(FLSHCR1B2)] = BITS(31, 0),
> + [regi(FLSHCR2A1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> + [regi(FLSHCR2A2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> + [regi(FLSHCR2B1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> + [regi(FLSHCR2B2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> + [regi(FLSHCR4)] = BITS(3, 2) | BIT(0),
> + [regi(IPCR0)] = BITS(31, 0),
> + [regi(IPCR1)] = BIT(31) | BITS(26, 24) | BITS(19, 0),
> + [regi(IPCMD)] = BIT(1),
> + [regi(DLPR)] = BITS(31, 0),
> + [regi(IPRXFCR)] = BITS(8, 0),
> + [regi(IPTXFCR)] = BITS(8, 0),
> + [regi(DLLCRA)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
> + [regi(DLLCRB)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
> + [regi(HADDRSTART)] = BITS(31, 12) | BIT(0),
> + [regi(HADDREND)] = BITS(31, 12),
> + [regi(HADDROFFSET)] = BITS(31, 12),
> +};
> +
> +static MemTxResult flexspi_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size,
> + MemTxAttrs attrs)
> +{
> + FlexSpiState *s = opaque;
> + MemTxResult ret = MEMTX_OK;
> +
> + trace_flexspi_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
> +
> + if (!reg32_aligned_access(addr, size)) {
> + ret = MEMTX_ERROR;
> + goto out;
> + }
> +
> + switch (addr) {
> + case reg(MCR0):
> + {
> + reg32_write(&s->regs, addr, value, wr_mask);
> +
> + if (s->regs.MCR0_b.SWRESET) {
> + s->regs.MCR0_b.SWRESET = 0;
> + }
> + break;
> + }
> +
> + default:
> + reg32_write(&s->regs, addr, value, wr_mask);
> + break;
> + }
> +
> +out:
> + return ret;
> +}
> +
> +static const MemoryRegionOps flexspi_ops = {
> + .read_with_attrs = flexspi_read,
> + .write_with_attrs = flexspi_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
I'm not a fan of your reg32_aligned_access() method, I'd rather
use the generic path with:
.valid = {
.max_access_size = 4,
.min_access_size = 4,
.unaligned = false
},
> +};
> +
> +static Property flexspi_properties[] = {
> + DEFINE_PROP_UINT32("mmap_base", FlexSpiState, mmap_base, 0),
> + DEFINE_PROP_UINT32("mmap_size", FlexSpiState, mmap_size, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void flexspi_init(Object *obj)
> +{
> + FlexSpiState *s = FLEXSPI(obj);
> +
> + memory_region_init_io(&s->mmio, obj, &flexspi_ops, s, TYPE_FLEXSPI,
> + sizeof(FLEXSPI_Type));
> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +}
> +
> +static void flexspi_realize(DeviceState *dev, Error **errp)
> +{
> + FlexSpiState *s = FLEXSPI(dev);
> +
> + if (s->mmap_size) {
> + memory_region_init_ram(&s->mem, OBJECT(s), DEVICE(s)->id, s->mmap_size,
> + NULL);
> + memory_region_add_subregion(get_system_memory(), s->mmap_base, &s->mem);
Where is this region used?
> + }
> +}
> +
> +static void flexspi_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->reset = flexspi_reset;
> + dc->realize = flexspi_realize;
> + device_class_set_props(dc, flexspi_properties);
> +}
> +
> +static const TypeInfo flexspi_info = {
> + .name = TYPE_FLEXSPI,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(FlexSpiState),
> + .instance_init = flexspi_init,
> + .class_init = flexspi_class_init,
> +};
> +
> +static void flexspi_register_types(void)
> +{
> + int i;
> +
> + for (i = 0; i < 32; i++) {
> + wr_mask[regi(TFDR[i])] = BITS(31, 0);
> + }
> + for (i = 0; i < 64; i++) {
> + wr_mask[regi(LUT[i])] = BITS(31, 0);
> + }
> +
> + type_register_static(&flexspi_info);
> +}
> +
> +type_init(flexspi_register_types)
> diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
> index 57d3e14727..c5b7e0a6e2 100644
> --- a/hw/ssi/meson.build
> +++ b/hw/ssi/meson.build
> @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_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_FLEXCOMM', if_true: files('flexcomm_spi.c'))
> +system_ss.add(when: 'CONFIG_FLEXSPI', if_true: files('flexspi.c'))
> diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
> index 5caa1c17ac..d623022a79 100644
> --- a/hw/ssi/trace-events
> +++ b/hw/ssi/trace-events
> @@ -40,3 +40,7 @@ flexcomm_spi_fifostat(const char *id, uint32_t fifostat, uint32_t fifoinstat) "%
> flexcomm_spi_irq(const char *id, bool irq, bool fifoirqs, bool perirqs, bool enabled) "%s: %d %d %d %d"
> flexcomm_spi_chr_rx_space(const char *id, uint32_t rx) "%s: %d"
> flexcomm_spi_chr_rx(const char *id) "%s"
> +
> +# flexspi.c
> +flexspi_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
> +flexspi_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
> diff --git a/include/hw/ssi/flexspi.h b/include/hw/ssi/flexspi.h
> new file mode 100644
> index 0000000000..f5fea9dee9
> --- /dev/null
> +++ b/include/hw/ssi/flexspi.h
> @@ -0,0 +1,34 @@
> +/*
> + * QEMU model for FLEXSPI
> + *
> + * Copyright (c) 2024 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef HW_RT500_FLEXSPI_H
> +#define HW_RT500_FLEXSPI_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/ssi/ssi.h"
> +#include "hw/arm/svd/flexspi.h"
> +
> +#define TYPE_FLEXSPI "flexspi"
> +#define FLEXSPI(obj) OBJECT_CHECK(FlexSpiState, (obj), TYPE_FLEXSPI)
> +
> +typedef struct {
> + /* <private> */
> + SysBusDevice parent_obj;
> +
> + /* <public> */
> + MemoryRegion mmio;
> + FLEXSPI_Type regs;
> + MemoryRegion mem;
> + uint32_t mmap_base;
> + uint32_t mmap_size;
We usually use uint64_t for MR base/size. I.e., althought your SoC
address space is 32-bit, this model could be use in another one,
mapped at a 64-bit base.
> +} FlexSpiState;
> +
> +#endif /* HW_RT500_FLEXSPI_H */
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 20/23] hw/ssi: add support for flexspi
2024-08-08 5:11 ` Philippe Mathieu-Daudé
@ 2024-08-08 21:31 ` Octavian Purdila
2024-08-09 8:54 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-08 21:31 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, jsnow, crosa,
bleal
On Wed, Aug 7, 2024 at 10:11 PM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> Hi Octavian,
>
Hi Philippe,
Thanks for the review!
> On 5/8/24 22:17, Octavian Purdila wrote:
> > This is mostly a stub which completes SPI transactions as noops
> > by masking out the error interrupts and never clearing the IPCMDDONE
> > interrupt.
> >
> > Although incomplete, this allows software that uses NXP's mcuxpresso
> > SDK to run the SDK board initialization functions.
> >
> > It also supports AHB memory access, aka XIP, for now as simple RAM
> > memory regions.
> >
> > Signed-off-by: Octavian Purdila <tavip@google.com>
> > ---
> > hw/arm/svd/meson.build | 4 +
> > hw/ssi/Kconfig | 4 +
> > hw/ssi/flexspi.c | 216 +++++++++++++++++++++++++++++++++++++++
> > hw/ssi/meson.build | 1 +
> > hw/ssi/trace-events | 4 +
> > include/hw/ssi/flexspi.h | 34 ++++++
> > 6 files changed, 263 insertions(+)
> > create mode 100644 hw/ssi/flexspi.c
> > create mode 100644 include/hw/ssi/flexspi.h
>
>
> > diff --git a/hw/ssi/flexspi.c b/hw/ssi/flexspi.c
> > new file mode 100644
> > index 0000000000..305d1a5bac
> > --- /dev/null
> > +++ b/hw/ssi/flexspi.c
> > @@ -0,0 +1,216 @@
> > +/*
> > + * QEMU model for FLEXSPI
> > + *
> > + * Copyright (c) 2024 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/mmap-alloc.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/units.h"
> > +#include "hw/irq.h"
> > +#include "hw/qdev-properties.h"
> > +#include "hw/qdev-properties-system.h"
> > +#include "migration/vmstate.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/regs.h"
> > +#include "hw/ssi/flexspi.h"
> > +#include "hw/arm/svd/flexspi.h"
> > +
> > +#include "trace.h"
> > +
> > +#define reg(field) offsetof(FLEXSPI_Type, field)
> > +#define regi(x) (reg(x) / sizeof(uint32_t))
> > +#define REG_NO (sizeof(FLEXSPI_Type) / sizeof(uint32_t))
> > +
> > +static FLEXSPI_REGISTER_NAMES_ARRAY(reg_names);
> > +
> > +static void flexspi_reset(DeviceState *dev)
> > +{
> > + FlexSpiState *s = FLEXSPI(dev);
> > +
> > + memset(&s->regs, 0, sizeof(s->regs));
> > +
> > + flexspi_reset_registers(&s->regs);
> > +
> > + /* idle immediately after reset */
> > + s->regs.STS0_b.SEQIDLE = 1;
> > +}
> > +
> > +static MemTxResult flexspi_read(void *opaque, hwaddr addr,
> > + uint64_t *data, unsigned size,
> > + MemTxAttrs attrs)
> > +{
> > + FlexSpiState *s = opaque;
> > + MemTxResult ret = MEMTX_OK;
> > +
> > + if (!reg32_aligned_access(addr, size)) {
> > + ret = MEMTX_ERROR;
> > + goto out;
> > + }
> > +
> > + switch (addr) {
> > + default:
> > + *data = reg32_read(&s->regs, addr);
> > + break;
> > + }
> > +
> > +out:
> > + trace_flexspi_reg_read(DEVICE(s)->id, reg_names[addr], addr, *data);
> > + return ret;
> > +}
> > +
> > +static uint32_t wr_mask[REG_NO] = {
> > + [regi(MCR0)] = BITS(31, 14) | BITS(12, 8) | BITS(5, 4) | BITS(1, 0),
> > + [regi(MCR1)] = BITS(31, 0),
> > + [regi(MCR2)] = BITS(31, 24) | BIT(11),
> > + [regi(AHBCR)] = BIT(10) | BITS(7, 2) | BIT(0),
> > + [regi(INTEN)] = BITS(13, 0),
> > + /*
> > + * TODO: once SPI transfers are implemented restore mask to:
> > + *
> > + * [regi(INTR)] = BIT(16) | BITS(12, 0).
> > + *
> > + * In the meantime this INTR mask allows for fake SPI transfers.
> > + */
> > + [regi(INTR)] = BIT(0),
> > + [regi(LUTKEY)] = BITS(31, 0),
> > + [regi(LUTCR)] = BITS(1, 0),
> > + [regi(AHBRXBUF0CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF1CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF2CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF3CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF4CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF5CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF6CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(AHBRXBUF7CR0)] = BIT(31) | BITS(26, 24) | BITS(19, 16) | BITS(7, 0),
> > + [regi(FLSHA1CR0)] = BITS(22, 0),
> > + [regi(FLSHA2CR0)] = BITS(22, 0),
> > + [regi(FLSHB1CR0)] = BITS(22, 0),
> > + [regi(FLSHB2CR0)] = BITS(22, 0),
> > + [regi(FLSHCR1A1)] = BITS(31, 0),
> > + [regi(FLSHCR1A2)] = BITS(31, 0),
> > + [regi(FLSHCR1B1)] = BITS(31, 0),
> > + [regi(FLSHCR1B2)] = BITS(31, 0),
> > + [regi(FLSHCR2A1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> > + [regi(FLSHCR2A2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> > + [regi(FLSHCR2B1)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> > + [regi(FLSHCR2B2)] = BITS(30, 13) | BITS(11, 5) | BITS(3, 0),
> > + [regi(FLSHCR4)] = BITS(3, 2) | BIT(0),
> > + [regi(IPCR0)] = BITS(31, 0),
> > + [regi(IPCR1)] = BIT(31) | BITS(26, 24) | BITS(19, 0),
> > + [regi(IPCMD)] = BIT(1),
> > + [regi(DLPR)] = BITS(31, 0),
> > + [regi(IPRXFCR)] = BITS(8, 0),
> > + [regi(IPTXFCR)] = BITS(8, 0),
> > + [regi(DLLCRA)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
> > + [regi(DLLCRB)] = BITS(14, 8) | BITS(6, 3) | BITS(1, 0),
> > + [regi(HADDRSTART)] = BITS(31, 12) | BIT(0),
> > + [regi(HADDREND)] = BITS(31, 12),
> > + [regi(HADDROFFSET)] = BITS(31, 12),
> > +};
> > +
> > +static MemTxResult flexspi_write(void *opaque, hwaddr addr,
> > + uint64_t value, unsigned size,
> > + MemTxAttrs attrs)
> > +{
> > + FlexSpiState *s = opaque;
> > + MemTxResult ret = MEMTX_OK;
> > +
> > + trace_flexspi_reg_write(DEVICE(s)->id, reg_names[addr], addr, value);
> > +
> > + if (!reg32_aligned_access(addr, size)) {
> > + ret = MEMTX_ERROR;
> > + goto out;
> > + }
> > +
> > + switch (addr) {
> > + case reg(MCR0):
> > + {
> > + reg32_write(&s->regs, addr, value, wr_mask);
> > +
> > + if (s->regs.MCR0_b.SWRESET) {
> > + s->regs.MCR0_b.SWRESET = 0;
> > + }
> > + break;
> > + }
> > +
> > + default:
> > + reg32_write(&s->regs, addr, value, wr_mask);
> > + break;
> > + }
> > +
> > +out:
> > + return ret;
> > +}
> > +
> > +static const MemoryRegionOps flexspi_ops = {
> > + .read_with_attrs = flexspi_read,
> > + .write_with_attrs = flexspi_write,
> > + .endianness = DEVICE_NATIVE_ENDIAN,
>
> I'm not a fan of your reg32_aligned_access() method, I'd rather
> use the generic path with:
>
> .valid = {
> .max_access_size = 4,
> .min_access_size = 4,
> .unaligned = false
> },
>
Noted. I will switch to use this approach where possible.
Note that flexcom_spi allows 8/16/32 bit access on some registers, so
I would still like to keep the reg32_aligned_access() method for that
case, unless there is a better option.
> > +};
> > +
> > +static Property flexspi_properties[] = {
> > + DEFINE_PROP_UINT32("mmap_base", FlexSpiState, mmap_base, 0),
> > + DEFINE_PROP_UINT32("mmap_size", FlexSpiState, mmap_size, 0),
> > + DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void flexspi_init(Object *obj)
> > +{
> > + FlexSpiState *s = FLEXSPI(obj);
> > +
> > + memory_region_init_io(&s->mmio, obj, &flexspi_ops, s, TYPE_FLEXSPI,
> > + sizeof(FLEXSPI_Type));
> > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> > +}
> > +
> > +static void flexspi_realize(DeviceState *dev, Error **errp)
> > +{
> > + FlexSpiState *s = FLEXSPI(dev);
> > +
> > + if (s->mmap_size) {
> > + memory_region_init_ram(&s->mem, OBJECT(s), DEVICE(s)->id, s->mmap_size,
> > + NULL);
> > + memory_region_add_subregion(get_system_memory(), s->mmap_base, &s->mem);
>
> Where is this region used?
>
These regions are enabled in rt500.c when instantiating the flexspi
peripherals. As implemented now they are backed by RAM, the full
implementation should translate accesses to spi commands to FLASH or
PSRAM devices.
We need the memory regions because even the simplest NXP SDK examples
are using the memory mapped flexspi0 region.
> > + }
> > +}
> > +
> > +static void flexspi_class_init(ObjectClass *klass, void *data)
> > +{
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > + dc->reset = flexspi_reset;
> > + dc->realize = flexspi_realize;
> > + device_class_set_props(dc, flexspi_properties);
> > +}
> > +
> > +static const TypeInfo flexspi_info = {
> > + .name = TYPE_FLEXSPI,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(FlexSpiState),
> > + .instance_init = flexspi_init,
> > + .class_init = flexspi_class_init,
> > +};
> > +
> > +static void flexspi_register_types(void)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < 32; i++) {
> > + wr_mask[regi(TFDR[i])] = BITS(31, 0);
> > + }
> > + for (i = 0; i < 64; i++) {
> > + wr_mask[regi(LUT[i])] = BITS(31, 0);
> > + }
> > +
> > + type_register_static(&flexspi_info);
> > +}
> > +
> > +type_init(flexspi_register_types)
> > diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
> > index 57d3e14727..c5b7e0a6e2 100644
> > --- a/hw/ssi/meson.build
> > +++ b/hw/ssi/meson.build
> > @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_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_FLEXCOMM', if_true: files('flexcomm_spi.c'))
> > +system_ss.add(when: 'CONFIG_FLEXSPI', if_true: files('flexspi.c'))
> > diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
> > index 5caa1c17ac..d623022a79 100644
> > --- a/hw/ssi/trace-events
> > +++ b/hw/ssi/trace-events
> > @@ -40,3 +40,7 @@ flexcomm_spi_fifostat(const char *id, uint32_t fifostat, uint32_t fifoinstat) "%
> > flexcomm_spi_irq(const char *id, bool irq, bool fifoirqs, bool perirqs, bool enabled) "%s: %d %d %d %d"
> > flexcomm_spi_chr_rx_space(const char *id, uint32_t rx) "%s: %d"
> > flexcomm_spi_chr_rx(const char *id) "%s"
> > +
> > +# flexspi.c
> > +flexspi_reg_read(const char *id, const char *reg_name, uint32_t addr, uint32_t val) " %s: %s[0x%04x] -> 0x%08x"
> > +flexspi_reg_write(const char *id, const char *reg_name, uint32_t addr, uint32_t val) "%s: %s[0x%04x] <- 0x%08x"
> > diff --git a/include/hw/ssi/flexspi.h b/include/hw/ssi/flexspi.h
> > new file mode 100644
> > index 0000000000..f5fea9dee9
> > --- /dev/null
> > +++ b/include/hw/ssi/flexspi.h
> > @@ -0,0 +1,34 @@
> > +/*
> > + * QEMU model for FLEXSPI
> > + *
> > + * Copyright (c) 2024 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#ifndef HW_RT500_FLEXSPI_H
> > +#define HW_RT500_FLEXSPI_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/ssi/ssi.h"
> > +#include "hw/arm/svd/flexspi.h"
> > +
> > +#define TYPE_FLEXSPI "flexspi"
> > +#define FLEXSPI(obj) OBJECT_CHECK(FlexSpiState, (obj), TYPE_FLEXSPI)
> > +
> > +typedef struct {
> > + /* <private> */
> > + SysBusDevice parent_obj;
> > +
> > + /* <public> */
> > + MemoryRegion mmio;
> > + FLEXSPI_Type regs;
> > + MemoryRegion mem;
> > + uint32_t mmap_base;
> > + uint32_t mmap_size;
>
> We usually use uint64_t for MR base/size. I.e., althought your SoC
> address space is 32-bit, this model could be use in another one,
> mapped at a 64-bit base.
>
I will fix it, thanks.
> > +} FlexSpiState;
> > +
> > +#endif /* HW_RT500_FLEXSPI_H */
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-05 20:16 ` [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files Octavian Purdila
@ 2024-08-08 21:56 ` John Snow
2024-08-08 22:30 ` Octavian Purdila
2024-08-09 6:34 ` Paolo Bonzini
2024-08-12 15:27 ` Peter Maydell
1 sibling, 2 replies; 56+ messages in thread
From: John Snow @ 2024-08-08 21:56 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, philmd,
crosa, bleal
[-- Attachment #1: Type: text/plain, Size: 19035 bytes --]
On Mon, Aug 5, 2024 at 4:17 PM Octavian Purdila <tavip@google.com> wrote:
> From: Stefan Stanacar <stefanst@google.com>
>
> From: Stefan Stanacar <stefanst@google.com>
>
> The CMSIS System View Description format(CMSIS-SVD) is an XML based
> description of Arm Cortex-M microcontrollers provided and maintained
> by sillicon vendors. It includes details such as peripherals registers
> (down to bitfields), peripheral register block addresses, reset
> values, etc.
>
> This script uses this information to create header files that makes it
> easier to emulate peripherals.
>
> The script can be used to create either peripheral specific headers or
> board / system specific information. The script generated headers are
> similar to the SVDConv utility.
>
> Peripheral specific headers contains information such as register
> layout, register names and reset values for registers:
>
> typedef struct {
> ...
> union {
> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
> * Flexcomm module ID */
> struct {
> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
> uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
> uint32_t : 4;
> uint32_t ID : 20; /* [31..12] Flexcomm ID */
> } PSELID_b;
> };
> ...
> } FLEXCOMM_Type; /* Size = 4096 (0x1000) */
>
> #define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
> #define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
> #define FLEXCOMM_PSELID_LOCK_Pos (3UL)
> #define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
> ...
>
> typedef enum { /* FLEXCOMM_PSELID_LOCK */
> /* Peripheral select can be changed by software. */
> FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
> /* Peripheral select is locked and cannot be changed until this
> * Flexcomm module or the entire device is reset. */
> FLEXCOMM_PSELID_LOCK_LOCKED = 1,
> } FLEXCOMM_PSELID_LOCK_Enum;
> ...
>
> #define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
> const char *_name[sizeof(FLEXCOMM_Type)] = { \
> [4088 ... 4091] = "PSELID", \
> [4092 ... 4095] = "PID", \
> }
>
> Board specific headers contains information about peripheral base
> register addresses.
>
> Signed-off-by: Stefan Stanacar <stefanst@google.com>
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> configure | 2 +-
> meson.build | 4 +
> python/setup.cfg | 1 +
> python/tests/minreqs.txt | 3 +
> pythondeps.toml | 3 +
> scripts/svd-gen-header.py | 342 ++++++++++++++++++++++++++++++++++++++
> 6 files changed, 354 insertions(+), 1 deletion(-)
> create mode 100755 scripts/svd-gen-header.py
>
> diff --git a/configure b/configure
> index 5ad1674ca5..811bfa5d54 100755
> --- a/configure
> +++ b/configure
> @@ -956,7 +956,7 @@ mkvenv="$python
> ${source_path}/python/scripts/mkvenv.py"
> # Finish preparing the virtual environment using vendored .whl files
>
> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
> - ${source_path}/pythondeps.toml meson || exit 1
> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
>
I haven't read the rest of this series; I'm chiming in solely from the
build/python maintainer angle. Do we *always* need pysvd, no matter how
QEMU was configured? Adding it to the meson line here is a very big hammer.
If not, consider looking at how sphinx (the "docs" group) is only
conditionally installed into the configure venv and mimic that using the
appropriate configure flags that necessitate the availability of pyvsd for
the QEMU build.
We also need to provide a way for pysvd to be available offline; some
packages are available via distro libs and if this package is available for
every distro we officially support, that's sufficient (but requires updates
to our various docker and VM test configuration files to add the new
dependency). Otherwise, like we do for meson, we need to vendor the wheel
in the tree so offline tarball builds will continue to work.
It looks like pysvd is a pure python package with no dependencies, so it
should be OK to vendor it in qemu.git/python/wheels/ - look at
qemu.git/python/scripts/vendor.py and consider updating and running this
script.
(The real blocker here is that RPM builds are performed offline and
dependencies that cannot be satisfied via rpm can't be added through pip.
We need any one of these to be true: (A) pyvsd is available (of a
sufficient version) in all distro repositories we target; (B) This build
feature is conditional and nobody minds if it never gets enabled for RPM
builds; (C) The package can be vendored.)
~~js
That said, you might be the first person I've seen outside of Paolo and I
to brave mucking around with the python build venv. You deserve a bravery
sticker :)
> # At this point, we expect Meson to be installed and available.
> # We expect mkvenv or pip to have created pyvenv/bin/meson for us.
> diff --git a/meson.build b/meson.build
> index ec59effca2..dee587483b 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -3235,6 +3235,10 @@ tracetool_depends = files(
> 'scripts/tracetool/vcpu.py'
> )
>
> +svd_gen_header = [
> + python, files('scripts/svd-gen-header.py')
> +]
> +
> qemu_version_cmd = [find_program('scripts/qemu-version.sh'),
> meson.current_source_dir(),
> get_option('pkgversion'), meson.project_version()]
> diff --git a/python/setup.cfg b/python/setup.cfg
> index 48668609d3..bc830c541a 100644
> --- a/python/setup.cfg
> +++ b/python/setup.cfg
> @@ -45,6 +45,7 @@ devel =
> urwid >= 2.1.2
> urwid-readline >= 0.13
> Pygments >= 2.9.0
> + pysvd >= 0.2.3
>
> # Provides qom-fuse functionality
> fuse =
> diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt
> index a3f423efd8..7993fcd23c 100644
> --- a/python/tests/minreqs.txt
> +++ b/python/tests/minreqs.txt
> @@ -22,6 +22,9 @@ distlib==0.3.6
> # Dependencies for FUSE support for qom-fuse
> fusepy==2.0.4
>
> +# Dependencies for svd-gen-regs
> +pysvd==0.2.3
> +
> # Test-runners, utilities, etc.
> avocado-framework==90.0
>
> diff --git a/pythondeps.toml b/pythondeps.toml
> index 9c16602d30..8416b17650 100644
> --- a/pythondeps.toml
> +++ b/pythondeps.toml
> @@ -32,3 +32,6 @@ sphinx_rtd_theme = { accepted = ">=0.5", installed =
> "1.1.1" }
> # avocado-framework, for example right now the limit is 92.x.
> avocado-framework = { accepted = "(>=88.1, <93.0)", installed = "88.1",
> canary = "avocado" }
> pycdlib = { accepted = ">=1.11.0" }
> +
> +[svd-gen-header]
> +pysvd = { accepted = ">=0.2.3.", installed = "0.2.3" }
> diff --git a/scripts/svd-gen-header.py b/scripts/svd-gen-header.py
> new file mode 100755
> index 0000000000..ab8cb4b665
> --- /dev/null
> +++ b/scripts/svd-gen-header.py
> @@ -0,0 +1,342 @@
> +#!/usr/bin/env python3
> +
> +# Copyright 2024 Google LLC
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> later.
> +# See the COPYING file in the top-level directory.
> +#
> +# Use this script to generate a C header file from an SVD xml
> +#
> +# Two mode of operations are supported: peripheral and system.
> +#
> +# When running in peripheral mode a header for a specific peripheral
> +# is going to be generated. It will define a type and structure with
> +# all of the available registers at the bitfield level. An array that
> +# contains the reigster names indexed by address is also going to be
> +# generated as well as a function to initialize registers to their
> +# reset values.
> +#
> +# Invocation example:
> +#
> +# svd_gen_header -i MIMXRT595S_cm33.xml -o flexcomm.h -p FLEXCOMM0 -t
> FLEXCOMM
> +#
> +# When running in system mode a header for a specific system /
> +# platform will be generated. It will define register base addresses
> +# and interrupt numbers for selected peripherals.
> +#
> +# Invocation example:
> +#
> +# svd_gen_header -i MIMXRT595S_cm33.xml -o rt500.h -s RT500 -p FLEXCOMM0 \
> +# -p CLKCTL0 -p CLKCTL1
> +#
> +
> +import argparse
> +import datetime
> +import re
> +import os
> +import sys
> +import xml.etree.ElementTree
> +import pysvd
> +
> +data_type_by_bits = {
> + 8: "uint8_t",
> + 16: "uint16_t",
> + 32: "uint32_t",
> +}
> +
> +
> +def get_register_array_name_and_size(register):
> + """Return register name and register array size.
> +
> + The SVD can define register arrays and pysvd encodes the whole set
> + as as regular register with their name prepended by [<array size>].
> +
> + Returns a tuple with the register name and the size of the array or
> + zero if this is not a register set.
> +
> + """
> +
> + split = re.split(r"[\[\]]", register.name)
> + return (split[0], int(split[1]) if len(split) > 1 else 0)
> +
> +
> +def generate_register(register):
> + """Generate register data.
> +
> + This include a field for accessing the full 32bits as we as
> + bitfield based register fields.
> +
> + """
> +
> + data_type = data_type_by_bits[register.size]
> +
> + out = f" /* 0x{register.addressOffset:08X} {register.description}
> */\n"
> + out += " union {\n"
> + out += f" {data_type} {register.name};\n"
> + out += " struct {\n"
> +
> + fields = sorted(register.fields, key=lambda field: field.bitOffset)
> + last_msb = -1
> + for field in fields:
> + reserve_bits = 0
> + lsb = field.bitOffset
> + msb = field.bitWidth + lsb - 1
> +
> + if last_msb == -1 and lsb > 0:
> + reserve_bits = lsb
> +
> + if last_msb != -1 and (lsb - last_msb) > 1:
> + reserve_bits = lsb - last_msb - 1
> +
> + if reserve_bits > 0:
> + out += f" {data_type}:{reserve_bits};\n"
> +
> + out += f" /* [{msb}..{lsb}] {field.description} */\n"
> + out += f" {data_type} {field.name} :
> {field.bitWidth};\n"
> +
> + last_msb = msb
> +
> + if register.size - last_msb > 1:
> + out += f" {data_type}:{register.size - last_msb -
> 1};\n"
> +
> + reg_name, reg_array_size = get_register_array_name_and_size(register)
> + if reg_array_size > 0:
> + out += f" }} {reg_name}_b[{reg_array_size}];\n"
> + else:
> + out += f" }} {reg_name}_b;\n"
> + out += " };\n\n"
> +
> + return out
> +
> +
> +def generate_pos_and_msk_defines(name, periph):
> + """Generate Pos and Msk defines"""
> +
> + out = "\n"
> + for reg in periph.registers:
> + for field in reg.fields:
> + reg_name, _ = get_register_array_name_and_size(reg)
> + field_name = f"{name}_{reg_name}_{field.name}"
> + out += f"#define {field_name}_Pos ({field.bitOffset}UL)\n"
> + mask = ((1 << field.bitWidth) - 1) << field.bitOffset
> + out += f"#define {field_name}_Msk (0x{mask:x}UL)\n"
> +
> + return out
> +
> +
> +def generate_enum_values(name, periph):
> + """Generate enum values"""
> +
> + out = "\n"
> + for reg in periph.registers:
> + reg_name, _ = get_register_array_name_and_size(reg)
> + for field in reg.fields:
> + if hasattr(field, "enumeratedValues"):
> + out += "\n"
> + out += "typedef enum {\n"
> + for enum in field.enumeratedValues.enumeratedValues:
> + enum_name = f"{name}_{reg_name}_{field.name}_{
> enum.name}"
> + out += f" /* {enum.description} */\n"
> + out += f" {enum_name} = {enum.value},\n"
> + out += f"}} {name}_{reg_name}_{field.name}_Enum;\n"
> +
> + return out
> +
> +
> +def generate_register_names_array_macro(name, periph):
> + """Generate register names array macro"""
> +
> + out = f"#define {name}_REGISTER_NAMES_ARRAY(_name) \\\n"
> + out += f" const char *_name[sizeof({name}_Type)] = {{ \\\n"
> + for reg in periph.registers:
> + reg_name, reg_array_size = get_register_array_name_and_size(reg)
> + if reg_array_size > 0:
> + for i in range(0, reg_array_size):
> + start = reg.addressOffset + i * reg.size // 8
> + stop = reg.addressOffset + (i + 1) * reg.size // 8 - 1
> + out += f' [{start} ... {stop}] = "{reg_name}{i}",
> \\\n'
> + else:
> + start = reg.addressOffset
> + stop = reg.addressOffset + reg.size // 8 - 1
> + out += f' [{start} ... {stop}] = "{reg.name}", \\\n'
> + out += " }\n"
> +
> + return out
> +
> +
> +def generate_reset_registers_function(name, periph):
> + """Generate reset registers function"""
> +
> + out = "\n"
> + fname = f"{name.lower()}_reset_registers"
> + out += f"static inline void {fname}({name}_Type *regs)\n"
> + out += "{\n"
> + for reg in periph.registers:
> + reg_name, reg_array_size = get_register_array_name_and_size(reg)
> + if reg_array_size > 0:
> + for i in range(0, int(reg_array_size)):
> + out += f" regs->{reg_name}[{i}] =
> {hex(reg.resetValue)};\n"
> + else:
> + out += f" regs->{reg_name} = {hex(reg.resetValue)};\n"
> + out += "}\n"
> +
> + return out
> +
> +
> +def generate_peripheral_header(periph, name):
> + """Generate peripheral header
> +
> + The following information is generated:
> +
> + * typedef with all of the available registers and register fields,
> + position and mask defines for register fields.
> +
> + * enum values that encode register fields options.
> +
> + * a macro that defines the register names indexed by the relative
> + address of the register.
> +
> + * a function that sets the registers to their reset values
> +
> + """
> +
> + out = f"/* {name} - {periph.description} */\n\n"
> + out += "typedef struct {\n"
> +
> + last_reg_offset = 0
> + cnt = 0
> + for reg in periph.registers:
> + reserved_words = 0
> + if (reg.addressOffset - last_reg_offset) > 0:
> + reserved_words = int((reg.addressOffset - last_reg_offset) //
> 4)
> + cnt += 1
> +
> + if cnt:
> + show_count = cnt
> + else:
> + show_count = ""
> +
> + if reserved_words == 1:
> + out += f" uint32_t RESERVED{show_count};\n\n"
> + elif reserved_words > 1:
> + out += f" uint32_t
> RESERVED{show_count}[{reserved_words}];\n\n"
> +
> + out += generate_register(reg)
> + last_reg_offset = reg.addressOffset + reg.size // 8
> +
> + size = periph.addressBlocks[0].size
> + out += f"}} {name}_Type; /* Size = {size} (0x{size:X}) */\n"
> +
> + out += "\n\n"
> +
> + out += generate_pos_and_msk_defines(name, periph)
> +
> + out += generate_enum_values(name, periph)
> +
> + out += generate_register_names_array_macro(name, periph)
> +
> + out += generate_reset_registers_function(name, periph)
> +
> + return out
> +
> +
> +def get_same_class_peripherals(svd, periph):
> + """Get a list of peripherals that are instances of the same class."""
> +
> + return [periph] + [
> + p
> + for p in svd.peripherals
> + if p.derivedFrom and p.derivedFrom.name == periph.name
> + ]
> +
> +
> +def generate_system_header(system, svd, periph):
> + """Generate base and irq defines for given list of peripherals"""
> +
> + out = ""
> +
> + for p in get_same_class_peripherals(svd, periph):
> + out += f"#define {system}_{p.name}_BASE 0x{p.baseAddress:X}UL\n"
> + out += "\n"
> +
> + for p in get_same_class_peripherals(svd, periph):
> + for irq in p.interrupts:
> + out += f"#define {system}_{irq.name}_IRQn 0x{irq.value}UL\n"
> + out += "\n"
> +
> + return out
> +
> +
> +def main():
> + """Script to generate C header file from an SVD file"""
> +
> + parser = argparse.ArgumentParser()
> + parser.add_argument(
> + "-i", "--input", type=str, help="Input SVD file", required=True
> + )
> + parser.add_argument(
> + "-o", "--output", type=str, help="Output .h file", required=True
> + )
> + parser.add_argument(
> + "-p",
> + "--peripheral",
> + action="append",
> + help="peripheral name from the SVD file",
> + required=True,
> + )
> + parser.add_argument(
> + "-t",
> + "--type-name",
> + type=str,
> + help="name to be used for peripheral definitions",
> + required=False,
> + )
> + parser.add_argument(
> + "-s",
> + "--system",
> + type=str,
> + help="name to be used for the system definitions",
> + required=False,
> + )
> +
> + args = parser.parse_args()
> +
> + node = xml.etree.ElementTree.parse(args.input).getroot()
> + svd = pysvd.element.Device(node)
> +
> + # Write license header
> + out = "/*\n"
> + license_text = svd.licenseText.split("\\n")
> + for line in license_text:
> + sline = f" * {line.strip()}"
> + out += f"{sline.rstrip()}\n"
> +
> + now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
> + out += f" * @note Automatically generated by
> {os.path.basename(__file__)}"
> + out += f" on {now} UTC from {os.path.basename(args.input)}.\n"
> + out += " *\n */\n\n"
> +
> + # Write some generic defines
> + out += "#pragma once\n\n"
> +
> + for name in args.peripheral:
> + periph = svd.find(name)
> + if periph:
> + if args.system:
> + out += generate_system_header(args.system, svd, periph)
> + else:
> + out += generate_peripheral_header(
> + periph, args.type_name if args.type_name else
> periph.name
> + )
> + else:
> + print(f"No such peripheral: {name}")
> + return 1
> +
> + with open(args.output, "w", encoding="ascii") as output:
> + output.write(out)
> +
> + return 0
> +
> +
> +if __name__ == "__main__":
> + sys.exit(main())
> --
> 2.46.0.rc2.264.g509ed76dc8-goog
>
>
[-- Attachment #2: Type: text/html, Size: 24170 bytes --]
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-08 21:56 ` John Snow
@ 2024-08-08 22:30 ` Octavian Purdila
2024-08-08 23:06 ` John Snow
2024-08-09 9:30 ` Philippe Mathieu-Daudé
2024-08-09 6:34 ` Paolo Bonzini
1 sibling, 2 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-08 22:30 UTC (permalink / raw)
To: John Snow
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, philmd,
crosa, bleal
On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
>
>
>
> On Mon, Aug 5, 2024 at 4:17 PM Octavian Purdila <tavip@google.com> wrote:
>>
>> From: Stefan Stanacar <stefanst@google.com>
>>
>> From: Stefan Stanacar <stefanst@google.com>
>>
>> The CMSIS System View Description format(CMSIS-SVD) is an XML based
>> description of Arm Cortex-M microcontrollers provided and maintained
>> by sillicon vendors. It includes details such as peripherals registers
>> (down to bitfields), peripheral register block addresses, reset
>> values, etc.
>>
>> This script uses this information to create header files that makes it
>> easier to emulate peripherals.
>>
>> The script can be used to create either peripheral specific headers or
>> board / system specific information. The script generated headers are
>> similar to the SVDConv utility.
>>
>> Peripheral specific headers contains information such as register
>> layout, register names and reset values for registers:
>>
>> typedef struct {
>> ...
>> union {
>> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
>> * Flexcomm module ID */
>> struct {
>> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
>> uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
>> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
>> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
>> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
>> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
>> uint32_t : 4;
>> uint32_t ID : 20; /* [31..12] Flexcomm ID */
>> } PSELID_b;
>> };
>> ...
>> } FLEXCOMM_Type; /* Size = 4096 (0x1000) */
>>
>> #define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
>> #define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
>> #define FLEXCOMM_PSELID_LOCK_Pos (3UL)
>> #define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
>> ...
>>
>> typedef enum { /* FLEXCOMM_PSELID_LOCK */
>> /* Peripheral select can be changed by software. */
>> FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
>> /* Peripheral select is locked and cannot be changed until this
>> * Flexcomm module or the entire device is reset. */
>> FLEXCOMM_PSELID_LOCK_LOCKED = 1,
>> } FLEXCOMM_PSELID_LOCK_Enum;
>> ...
>>
>> #define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
>> const char *_name[sizeof(FLEXCOMM_Type)] = { \
>> [4088 ... 4091] = "PSELID", \
>> [4092 ... 4095] = "PID", \
>> }
>>
>> Board specific headers contains information about peripheral base
>> register addresses.
>>
>> Signed-off-by: Stefan Stanacar <stefanst@google.com>
>> Signed-off-by: Octavian Purdila <tavip@google.com>
>> ---
>> configure | 2 +-
>> meson.build | 4 +
>> python/setup.cfg | 1 +
>> python/tests/minreqs.txt | 3 +
>> pythondeps.toml | 3 +
>> scripts/svd-gen-header.py | 342 ++++++++++++++++++++++++++++++++++++++
>> 6 files changed, 354 insertions(+), 1 deletion(-)
>> create mode 100755 scripts/svd-gen-header.py
>>
>> diff --git a/configure b/configure
>> index 5ad1674ca5..811bfa5d54 100755
>> --- a/configure
>> +++ b/configure
>> @@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
>> # Finish preparing the virtual environment using vendored .whl files
>>
>> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
>> - ${source_path}/pythondeps.toml meson || exit 1
>> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
>
Hi John,
Thanks for reviewing!
>
> I haven't read the rest of this series; I'm chiming in solely from the build/python maintainer angle. Do we *always* need pysvd, no matter how QEMU was configured? Adding it to the meson line here is a very big hammer.
>
I think the minimum we can do is to install it only if CONFIG_ARM is
enabled. That might change in the future if the models we create with
pysvd are enabled for other architectures.
> If not, consider looking at how sphinx (the "docs" group) is only conditionally installed into the configure venv and mimic that using the appropriate configure flags that necessitate the availability of pyvsd for the QEMU build.
Thanks for the pointer, I'll take a look.
>
> We also need to provide a way for pysvd to be available offline; some packages are available via distro libs and if this package is available for every distro we officially support, that's sufficient (but requires updates to our various docker and VM test configuration files to add the new dependency). Otherwise, like we do for meson, we need to vendor the wheel in the tree so offline tarball builds will continue to work.
>
> It looks like pysvd is a pure python package with no dependencies, so it should be OK to vendor it in qemu.git/python/wheels/ - look at qemu.git/python/scripts/vendor.py and consider updating and running this script.
Thanks, I'll look at it and add it in v2.
>
> (The real blocker here is that RPM builds are performed offline and dependencies that cannot be satisfied via rpm can't be added through pip. We need any one of these to be true: (A) pyvsd is available (of a sufficient version) in all distro repositories we target; (B) This build feature is conditional and nobody minds if it never gets enabled for RPM builds; (C) The package can be vendored.)
>
> ~~js
>
> That said, you might be the first person I've seen outside of Paolo and I to brave mucking around with the python build venv. You deserve a bravery sticker :)
>
>>
>> # At this point, we expect Meson to be installed and available.
>> # We expect mkvenv or pip to have created pyvenv/bin/meson for us.
>> diff --git a/meson.build b/meson.build
>> index ec59effca2..dee587483b 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -3235,6 +3235,10 @@ tracetool_depends = files(
>> 'scripts/tracetool/vcpu.py'
>> )
>>
>> +svd_gen_header = [
>> + python, files('scripts/svd-gen-header.py')
>> +]
>> +
>> qemu_version_cmd = [find_program('scripts/qemu-version.sh'),
>> meson.current_source_dir(),
>> get_option('pkgversion'), meson.project_version()]
>> diff --git a/python/setup.cfg b/python/setup.cfg
>> index 48668609d3..bc830c541a 100644
>> --- a/python/setup.cfg
>> +++ b/python/setup.cfg
>> @@ -45,6 +45,7 @@ devel =
>> urwid >= 2.1.2
>> urwid-readline >= 0.13
>> Pygments >= 2.9.0
>> + pysvd >= 0.2.3
>>
>> # Provides qom-fuse functionality
>> fuse =
>> diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt
>> index a3f423efd8..7993fcd23c 100644
>> --- a/python/tests/minreqs.txt
>> +++ b/python/tests/minreqs.txt
>> @@ -22,6 +22,9 @@ distlib==0.3.6
>> # Dependencies for FUSE support for qom-fuse
>> fusepy==2.0.4
>>
>> +# Dependencies for svd-gen-regs
>> +pysvd==0.2.3
>> +
>> # Test-runners, utilities, etc.
>> avocado-framework==90.0
>>
>> diff --git a/pythondeps.toml b/pythondeps.toml
>> index 9c16602d30..8416b17650 100644
>> --- a/pythondeps.toml
>> +++ b/pythondeps.toml
>> @@ -32,3 +32,6 @@ sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" }
>> # avocado-framework, for example right now the limit is 92.x.
>> avocado-framework = { accepted = "(>=88.1, <93.0)", installed = "88.1", canary = "avocado" }
>> pycdlib = { accepted = ">=1.11.0" }
>> +
>> +[svd-gen-header]
>> +pysvd = { accepted = ">=0.2.3.", installed = "0.2.3" }
>> diff --git a/scripts/svd-gen-header.py b/scripts/svd-gen-header.py
>> new file mode 100755
>> index 0000000000..ab8cb4b665
>> --- /dev/null
>> +++ b/scripts/svd-gen-header.py
>> @@ -0,0 +1,342 @@
>> +#!/usr/bin/env python3
>> +
>> +# Copyright 2024 Google LLC
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2 or later.
>> +# See the COPYING file in the top-level directory.
>> +#
>> +# Use this script to generate a C header file from an SVD xml
>> +#
>> +# Two mode of operations are supported: peripheral and system.
>> +#
>> +# When running in peripheral mode a header for a specific peripheral
>> +# is going to be generated. It will define a type and structure with
>> +# all of the available registers at the bitfield level. An array that
>> +# contains the reigster names indexed by address is also going to be
>> +# generated as well as a function to initialize registers to their
>> +# reset values.
>> +#
>> +# Invocation example:
>> +#
>> +# svd_gen_header -i MIMXRT595S_cm33.xml -o flexcomm.h -p FLEXCOMM0 -t FLEXCOMM
>> +#
>> +# When running in system mode a header for a specific system /
>> +# platform will be generated. It will define register base addresses
>> +# and interrupt numbers for selected peripherals.
>> +#
>> +# Invocation example:
>> +#
>> +# svd_gen_header -i MIMXRT595S_cm33.xml -o rt500.h -s RT500 -p FLEXCOMM0 \
>> +# -p CLKCTL0 -p CLKCTL1
>> +#
>> +
>> +import argparse
>> +import datetime
>> +import re
>> +import os
>> +import sys
>> +import xml.etree.ElementTree
>> +import pysvd
>> +
>> +data_type_by_bits = {
>> + 8: "uint8_t",
>> + 16: "uint16_t",
>> + 32: "uint32_t",
>> +}
>> +
>> +
>> +def get_register_array_name_and_size(register):
>> + """Return register name and register array size.
>> +
>> + The SVD can define register arrays and pysvd encodes the whole set
>> + as as regular register with their name prepended by [<array size>].
>> +
>> + Returns a tuple with the register name and the size of the array or
>> + zero if this is not a register set.
>> +
>> + """
>> +
>> + split = re.split(r"[\[\]]", register.name)
>> + return (split[0], int(split[1]) if len(split) > 1 else 0)
>> +
>> +
>> +def generate_register(register):
>> + """Generate register data.
>> +
>> + This include a field for accessing the full 32bits as we as
>> + bitfield based register fields.
>> +
>> + """
>> +
>> + data_type = data_type_by_bits[register.size]
>> +
>> + out = f" /* 0x{register.addressOffset:08X} {register.description} */\n"
>> + out += " union {\n"
>> + out += f" {data_type} {register.name};\n"
>> + out += " struct {\n"
>> +
>> + fields = sorted(register.fields, key=lambda field: field.bitOffset)
>> + last_msb = -1
>> + for field in fields:
>> + reserve_bits = 0
>> + lsb = field.bitOffset
>> + msb = field.bitWidth + lsb - 1
>> +
>> + if last_msb == -1 and lsb > 0:
>> + reserve_bits = lsb
>> +
>> + if last_msb != -1 and (lsb - last_msb) > 1:
>> + reserve_bits = lsb - last_msb - 1
>> +
>> + if reserve_bits > 0:
>> + out += f" {data_type}:{reserve_bits};\n"
>> +
>> + out += f" /* [{msb}..{lsb}] {field.description} */\n"
>> + out += f" {data_type} {field.name} : {field.bitWidth};\n"
>> +
>> + last_msb = msb
>> +
>> + if register.size - last_msb > 1:
>> + out += f" {data_type}:{register.size - last_msb - 1};\n"
>> +
>> + reg_name, reg_array_size = get_register_array_name_and_size(register)
>> + if reg_array_size > 0:
>> + out += f" }} {reg_name}_b[{reg_array_size}];\n"
>> + else:
>> + out += f" }} {reg_name}_b;\n"
>> + out += " };\n\n"
>> +
>> + return out
>> +
>> +
>> +def generate_pos_and_msk_defines(name, periph):
>> + """Generate Pos and Msk defines"""
>> +
>> + out = "\n"
>> + for reg in periph.registers:
>> + for field in reg.fields:
>> + reg_name, _ = get_register_array_name_and_size(reg)
>> + field_name = f"{name}_{reg_name}_{field.name}"
>> + out += f"#define {field_name}_Pos ({field.bitOffset}UL)\n"
>> + mask = ((1 << field.bitWidth) - 1) << field.bitOffset
>> + out += f"#define {field_name}_Msk (0x{mask:x}UL)\n"
>> +
>> + return out
>> +
>> +
>> +def generate_enum_values(name, periph):
>> + """Generate enum values"""
>> +
>> + out = "\n"
>> + for reg in periph.registers:
>> + reg_name, _ = get_register_array_name_and_size(reg)
>> + for field in reg.fields:
>> + if hasattr(field, "enumeratedValues"):
>> + out += "\n"
>> + out += "typedef enum {\n"
>> + for enum in field.enumeratedValues.enumeratedValues:
>> + enum_name = f"{name}_{reg_name}_{field.name}_{enum.name}"
>> + out += f" /* {enum.description} */\n"
>> + out += f" {enum_name} = {enum.value},\n"
>> + out += f"}} {name}_{reg_name}_{field.name}_Enum;\n"
>> +
>> + return out
>> +
>> +
>> +def generate_register_names_array_macro(name, periph):
>> + """Generate register names array macro"""
>> +
>> + out = f"#define {name}_REGISTER_NAMES_ARRAY(_name) \\\n"
>> + out += f" const char *_name[sizeof({name}_Type)] = {{ \\\n"
>> + for reg in periph.registers:
>> + reg_name, reg_array_size = get_register_array_name_and_size(reg)
>> + if reg_array_size > 0:
>> + for i in range(0, reg_array_size):
>> + start = reg.addressOffset + i * reg.size // 8
>> + stop = reg.addressOffset + (i + 1) * reg.size // 8 - 1
>> + out += f' [{start} ... {stop}] = "{reg_name}{i}", \\\n'
>> + else:
>> + start = reg.addressOffset
>> + stop = reg.addressOffset + reg.size // 8 - 1
>> + out += f' [{start} ... {stop}] = "{reg.name}", \\\n'
>> + out += " }\n"
>> +
>> + return out
>> +
>> +
>> +def generate_reset_registers_function(name, periph):
>> + """Generate reset registers function"""
>> +
>> + out = "\n"
>> + fname = f"{name.lower()}_reset_registers"
>> + out += f"static inline void {fname}({name}_Type *regs)\n"
>> + out += "{\n"
>> + for reg in periph.registers:
>> + reg_name, reg_array_size = get_register_array_name_and_size(reg)
>> + if reg_array_size > 0:
>> + for i in range(0, int(reg_array_size)):
>> + out += f" regs->{reg_name}[{i}] = {hex(reg.resetValue)};\n"
>> + else:
>> + out += f" regs->{reg_name} = {hex(reg.resetValue)};\n"
>> + out += "}\n"
>> +
>> + return out
>> +
>> +
>> +def generate_peripheral_header(periph, name):
>> + """Generate peripheral header
>> +
>> + The following information is generated:
>> +
>> + * typedef with all of the available registers and register fields,
>> + position and mask defines for register fields.
>> +
>> + * enum values that encode register fields options.
>> +
>> + * a macro that defines the register names indexed by the relative
>> + address of the register.
>> +
>> + * a function that sets the registers to their reset values
>> +
>> + """
>> +
>> + out = f"/* {name} - {periph.description} */\n\n"
>> + out += "typedef struct {\n"
>> +
>> + last_reg_offset = 0
>> + cnt = 0
>> + for reg in periph.registers:
>> + reserved_words = 0
>> + if (reg.addressOffset - last_reg_offset) > 0:
>> + reserved_words = int((reg.addressOffset - last_reg_offset) // 4)
>> + cnt += 1
>> +
>> + if cnt:
>> + show_count = cnt
>> + else:
>> + show_count = ""
>> +
>> + if reserved_words == 1:
>> + out += f" uint32_t RESERVED{show_count};\n\n"
>> + elif reserved_words > 1:
>> + out += f" uint32_t RESERVED{show_count}[{reserved_words}];\n\n"
>> +
>> + out += generate_register(reg)
>> + last_reg_offset = reg.addressOffset + reg.size // 8
>> +
>> + size = periph.addressBlocks[0].size
>> + out += f"}} {name}_Type; /* Size = {size} (0x{size:X}) */\n"
>> +
>> + out += "\n\n"
>> +
>> + out += generate_pos_and_msk_defines(name, periph)
>> +
>> + out += generate_enum_values(name, periph)
>> +
>> + out += generate_register_names_array_macro(name, periph)
>> +
>> + out += generate_reset_registers_function(name, periph)
>> +
>> + return out
>> +
>> +
>> +def get_same_class_peripherals(svd, periph):
>> + """Get a list of peripherals that are instances of the same class."""
>> +
>> + return [periph] + [
>> + p
>> + for p in svd.peripherals
>> + if p.derivedFrom and p.derivedFrom.name == periph.name
>> + ]
>> +
>> +
>> +def generate_system_header(system, svd, periph):
>> + """Generate base and irq defines for given list of peripherals"""
>> +
>> + out = ""
>> +
>> + for p in get_same_class_peripherals(svd, periph):
>> + out += f"#define {system}_{p.name}_BASE 0x{p.baseAddress:X}UL\n"
>> + out += "\n"
>> +
>> + for p in get_same_class_peripherals(svd, periph):
>> + for irq in p.interrupts:
>> + out += f"#define {system}_{irq.name}_IRQn 0x{irq.value}UL\n"
>> + out += "\n"
>> +
>> + return out
>> +
>> +
>> +def main():
>> + """Script to generate C header file from an SVD file"""
>> +
>> + parser = argparse.ArgumentParser()
>> + parser.add_argument(
>> + "-i", "--input", type=str, help="Input SVD file", required=True
>> + )
>> + parser.add_argument(
>> + "-o", "--output", type=str, help="Output .h file", required=True
>> + )
>> + parser.add_argument(
>> + "-p",
>> + "--peripheral",
>> + action="append",
>> + help="peripheral name from the SVD file",
>> + required=True,
>> + )
>> + parser.add_argument(
>> + "-t",
>> + "--type-name",
>> + type=str,
>> + help="name to be used for peripheral definitions",
>> + required=False,
>> + )
>> + parser.add_argument(
>> + "-s",
>> + "--system",
>> + type=str,
>> + help="name to be used for the system definitions",
>> + required=False,
>> + )
>> +
>> + args = parser.parse_args()
>> +
>> + node = xml.etree.ElementTree.parse(args.input).getroot()
>> + svd = pysvd.element.Device(node)
>> +
>> + # Write license header
>> + out = "/*\n"
>> + license_text = svd.licenseText.split("\\n")
>> + for line in license_text:
>> + sline = f" * {line.strip()}"
>> + out += f"{sline.rstrip()}\n"
>> +
>> + now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
>> + out += f" * @note Automatically generated by {os.path.basename(__file__)}"
>> + out += f" on {now} UTC from {os.path.basename(args.input)}.\n"
>> + out += " *\n */\n\n"
>> +
>> + # Write some generic defines
>> + out += "#pragma once\n\n"
>> +
>> + for name in args.peripheral:
>> + periph = svd.find(name)
>> + if periph:
>> + if args.system:
>> + out += generate_system_header(args.system, svd, periph)
>> + else:
>> + out += generate_peripheral_header(
>> + periph, args.type_name if args.type_name else periph.name
>> + )
>> + else:
>> + print(f"No such peripheral: {name}")
>> + return 1
>> +
>> + with open(args.output, "w", encoding="ascii") as output:
>> + output.write(out)
>> +
>> + return 0
>> +
>> +
>> +if __name__ == "__main__":
>> + sys.exit(main())
>> --
>> 2.46.0.rc2.264.g509ed76dc8-goog
>>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-08 22:30 ` Octavian Purdila
@ 2024-08-08 23:06 ` John Snow
2024-08-09 9:30 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 56+ messages in thread
From: John Snow @ 2024-08-08 23:06 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, philmd,
crosa, bleal
[-- Attachment #1: Type: text/plain, Size: 7682 bytes --]
On Thu, Aug 8, 2024 at 6:30 PM Octavian Purdila <tavip@google.com> wrote:
> On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
> >
> >
> >
> > On Mon, Aug 5, 2024 at 4:17 PM Octavian Purdila <tavip@google.com>
> wrote:
> >>
> >> From: Stefan Stanacar <stefanst@google.com>
> >>
> >> From: Stefan Stanacar <stefanst@google.com>
> >>
> >> The CMSIS System View Description format(CMSIS-SVD) is an XML based
> >> description of Arm Cortex-M microcontrollers provided and maintained
> >> by sillicon vendors. It includes details such as peripherals registers
> >> (down to bitfields), peripheral register block addresses, reset
> >> values, etc.
> >>
> >> This script uses this information to create header files that makes it
> >> easier to emulate peripherals.
> >>
> >> The script can be used to create either peripheral specific headers or
> >> board / system specific information. The script generated headers are
> >> similar to the SVDConv utility.
> >>
> >> Peripheral specific headers contains information such as register
> >> layout, register names and reset values for registers:
> >>
> >> typedef struct {
> >> ...
> >> union {
> >> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
> >> * Flexcomm module ID */
> >> struct {
> >> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
> >> uint32_t LOCK : 1; /* [3..3] Lock the peripheral
> select */
> >> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
> >> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
> >> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
> >> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
> >> uint32_t : 4;
> >> uint32_t ID : 20; /* [31..12] Flexcomm ID */
> >> } PSELID_b;
> >> };
> >> ...
> >> } FLEXCOMM_Type; /* Size = 4096 (0x1000) */
> >>
> >> #define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
> >> #define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
> >> #define FLEXCOMM_PSELID_LOCK_Pos (3UL)
> >> #define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
> >> ...
> >>
> >> typedef enum { /* FLEXCOMM_PSELID_LOCK */
> >> /* Peripheral select can be changed by software. */
> >> FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
> >> /* Peripheral select is locked and cannot be changed until this
> >> * Flexcomm module or the entire device is reset. */
> >> FLEXCOMM_PSELID_LOCK_LOCKED = 1,
> >> } FLEXCOMM_PSELID_LOCK_Enum;
> >> ...
> >>
> >> #define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
> >> const char *_name[sizeof(FLEXCOMM_Type)] = { \
> >> [4088 ... 4091] = "PSELID", \
> >> [4092 ... 4095] = "PID", \
> >> }
> >>
> >> Board specific headers contains information about peripheral base
> >> register addresses.
> >>
> >> Signed-off-by: Stefan Stanacar <stefanst@google.com>
> >> Signed-off-by: Octavian Purdila <tavip@google.com>
> >> ---
> >> configure | 2 +-
> >> meson.build | 4 +
> >> python/setup.cfg | 1 +
> >> python/tests/minreqs.txt | 3 +
> >> pythondeps.toml | 3 +
> >> scripts/svd-gen-header.py | 342 ++++++++++++++++++++++++++++++++++++++
> >> 6 files changed, 354 insertions(+), 1 deletion(-)
> >> create mode 100755 scripts/svd-gen-header.py
> >>
> >> diff --git a/configure b/configure
> >> index 5ad1674ca5..811bfa5d54 100755
> >> --- a/configure
> >> +++ b/configure
> >> @@ -956,7 +956,7 @@ mkvenv="$python
> ${source_path}/python/scripts/mkvenv.py"
> >> # Finish preparing the virtual environment using vendored .whl files
> >>
> >> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
> >> - ${source_path}/pythondeps.toml meson || exit 1
> >> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
> >
>
> Hi John,
>
> Thanks for reviewing!
>
:)
>
> >
> > I haven't read the rest of this series; I'm chiming in solely from the
> build/python maintainer angle. Do we *always* need pysvd, no matter how
> QEMU was configured? Adding it to the meson line here is a very big hammer.
> >
>
> I think the minimum we can do is to install it only if CONFIG_ARM is
> enabled. That might change in the future if the models we create with
> pysvd are enabled for other architectures.
>
OK, Understood. I will see if Paolo has any thoughts on this; I am
admittedly a little hesitant to add only our 2nd unconditional python
dependency. For context, meson is our first and only such dependency and
Paolo has been very active in development of that project as well; this
would be the first that none of us have any connection with. If the project
were to ever update in such a way that it became not pure-python or
required additional dependencies, the challenges in vendoring it might
cause unscheduled future headaches.
We can always pin to/vendor versions that suit our needs, but it may be
worth sending the developer an email to ask what his policies on
dependencies are, if only to let him know he might have a user out there
that's hedging on it remaining pure python and dep-free, as a courtesy.
(For my money, personally: it's probably fine; but very possibly any
boards/devices/models you add that require it *might* wind up needing a
configure flag that add this dependency, like --enable-svd-devices or
something of the sort. I'll let Paolo comment on that, don't take my word
as gospel - I handle the Python more than I do the build system; you
managed to find the precise intersection of the two.)
>
> > If not, consider looking at how sphinx (the "docs" group) is only
> conditionally installed into the configure venv and mimic that using the
> appropriate configure flags that necessitate the availability of pyvsd for
> the QEMU build.
>
> Thanks for the pointer, I'll take a look.
>
> >
> > We also need to provide a way for pysvd to be available offline; some
> packages are available via distro libs and if this package is available for
> every distro we officially support, that's sufficient (but requires updates
> to our various docker and VM test configuration files to add the new
> dependency). Otherwise, like we do for meson, we need to vendor the wheel
> in the tree so offline tarball builds will continue to work.
> >
> > It looks like pysvd is a pure python package with no dependencies, so it
> should be OK to vendor it in qemu.git/python/wheels/ - look at
> qemu.git/python/scripts/vendor.py and consider updating and running this
> script.
>
> Thanks, I'll look at it and add it in v2.
>
Thanks! Sorry this area of the build system is so persnickety. If you run
into troubles please reach out. I've also pinged Paolo to take a look as
the build system "SME" in case there's something I overlooked, too.
Good luck! :)
~js
>
> >
> > (The real blocker here is that RPM builds are performed offline and
> dependencies that cannot be satisfied via rpm can't be added through pip.
> We need any one of these to be true: (A) pyvsd is available (of a
> sufficient version) in all distro repositories we target; (B) This build
> feature is conditional and nobody minds if it never gets enabled for RPM
> builds; (C) The package can be vendored.)
> >
> > ~~js
> >
> > That said, you might be the first person I've seen outside of Paolo and
> I to brave mucking around with the python build venv. You deserve a bravery
> sticker :)
> >
> >>
>
[...]
[-- Attachment #2: Type: text/html, Size: 10137 bytes --]
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-08 21:56 ` John Snow
2024-08-08 22:30 ` Octavian Purdila
@ 2024-08-09 6:34 ` Paolo Bonzini
2024-08-09 19:28 ` Octavian Purdila
1 sibling, 1 reply; 56+ messages in thread
From: Paolo Bonzini @ 2024-08-09 6:34 UTC (permalink / raw)
To: John Snow, Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, berrange, philmd, crosa, bleal
On 8/8/24 23:56, John Snow wrote:
> I haven't read the rest of this series; I'm chiming in solely from the
> build/python maintainer angle. Do we *always* need pysvd, no matter how
> QEMU was configured? Adding it to the meson line here is a very big hammer.
In general I'd agree, though for a 7.5 kB package with no other
dependencies I'm willing to make an exception.
The alternative would be pretty complex:
- check if pysvd is installed in the host
- check if a machine type that needs pysvd is enabled, defaulting to y
or n depending on the previous step and --enable-download
- use that to decide between doing nothing, installing pysvd or erroring out
- pass the availability of pysvd to Kconfig
- use that to make the final determination on whether to enable those
machine types that use pysvd
This is quite obviously overengineered compared to the alternative.
Another possibility is to ship the generated file, skip regeneration if
pysvd is not installed (on the host), and not touch pythondeps.toml at
all. Whether shipping a generated file is acceptable should be decided
by Peter as ARM maintainer, personally I would go the way that Octavian
is going already and I'm mentioning the rest only for completeness and
education.
However...
> We also need to provide a way for pysvd to be available offline; some
> packages are available via distro libs and if this package is available
> for every distro we officially support, that's sufficient (but requires
> updates to our various docker and VM test configuration files to add the
> new dependency). Otherwise, like we do for meson, we need to vendor the
> wheel in the tree so offline tarball builds will continue to work.
>
> It looks like pysvd is a pure python package with no dependencies, so it
> should be OK to vendor it in qemu.git/python/wheels/ - look at
> qemu.git/python/scripts/vendor.py and consider updating and running this
> script.
... this is indeed correct. It's not hard and it helps building on
older distros. Future versions of Debian or Fedora might package pysvd,
but right now it's not included anywhere
(https://repology.org/project/python:pysvd/history).
> That said, you might be the first person I've seen outside of Paolo and
> I to brave mucking around with the python build venv. You deserve a
> bravery sticker :)
It's not that bad, come on. :) But yeah, I'm positively surprised by
the effort to include pysvd in the virtual environment, and any
suggestions to improve the documentation and discoverability of the venv
setup are welcome. I'm curious whether you figured it out yourself or
found https://www.qemu.org/docs/master/devel/build-system.html.
One thing I heard from others is that pythondeps.toml looks "too much
like" a fringe standard Python feature that no one has heard about. In
some sense that's a great compliment, but I can see how it can be a bit
disconcerting.
Paolo
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 20/23] hw/ssi: add support for flexspi
2024-08-08 21:31 ` Octavian Purdila
@ 2024-08-09 8:54 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-09 8:54 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, jsnow, crosa,
bleal
On 8/8/24 23:31, Octavian Purdila wrote:
>>> +static Property flexspi_properties[] = {
>>> + DEFINE_PROP_UINT32("mmap_base", FlexSpiState, mmap_base, 0),
>>> + DEFINE_PROP_UINT32("mmap_size", FlexSpiState, mmap_size, 0),
Preferably simply 'size'.
>>> + DEFINE_PROP_END_OF_LIST(),
>>> +};
>>> +
>>> +static void flexspi_init(Object *obj)
>>> +{
>>> + FlexSpiState *s = FLEXSPI(obj);
>>> +
>>> + memory_region_init_io(&s->mmio, obj, &flexspi_ops, s, TYPE_FLEXSPI,
>>> + sizeof(FLEXSPI_Type));
>>> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
>>> +}
>>> +
>>> +static void flexspi_realize(DeviceState *dev, Error **errp)
>>> +{
>>> + FlexSpiState *s = FLEXSPI(dev);
>>> +
>>> + if (s->mmap_size) {
>>> + memory_region_init_ram(&s->mem, OBJECT(s), DEVICE(s)->id, s->mmap_size,
>>> + NULL);
>>> + memory_region_add_subregion(get_system_memory(), s->mmap_base, &s->mem);
>>
>> Where is this region used?
>>
>
> These regions are enabled in rt500.c when instantiating the flexspi
> peripherals. As implemented now they are backed by RAM, the full
> implementation should translate accesses to spi commands to FLASH or
> PSRAM devices.
>
> We need the memory regions because even the simplest NXP SDK examples
> are using the memory mapped flexspi0 region.
Devices shouldn't access get_system_memory() directly (it is
documented as kind of deprecated =) ). Since you implement a
sysbus device, you need to export the region with sysbus_init_mmio()
then the upper layer (SoC) instantiating it gets the regions calling
sysbus_mmio_get_region() and maps it. Then you don't need the
'mmap_base' property.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-06 20:31 ` Octavian Purdila
2024-08-07 11:24 ` Philippe Mathieu-Daudé
@ 2024-08-09 9:13 ` Daniel P. Berrangé
2024-08-09 22:40 ` Octavian Purdila
1 sibling, 1 reply; 56+ messages in thread
From: Daniel P. Berrangé @ 2024-08-09 9:13 UTC (permalink / raw)
To: Octavian Purdila
Cc: Alex Bennée, qemu-devel, qemu-arm, stefanst, pbonzini, thuth,
peter.maydell, marcandre.lureau, alistair, philmd, jsnow, crosa,
bleal
On Tue, Aug 06, 2024 at 01:31:51PM -0700, Octavian Purdila wrote:
> On Tue, Aug 6, 2024 at 7:06 AM Alex Bennée <alex.bennee@linaro.org> wrote:
> >
> > Octavian Purdila <tavip@google.com> writes:
> >
> > > Picked from:
> > >
> > > https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
> > >
> > > NOTE: the file is truncated to keep the email size reasonable. Please
> > > use the link above and download the full file if you want to try out
> > > the patch.
"the file is truncated" wins understatement of the week.
The full XML file that would need to be in QEMU git for this is 8.6 MB in
size.
The overall generated headers that we get from it are ~16k lines,
or ~0.5 MB
$ wc -l build/hw/arm/svd/*.h
135 build/hw/arm/svd/flexcomm.h
1227 build/hw/arm/svd/flexcomm_i2c.h
1161 build/hw/arm/svd/flexcomm_spi.h
1231 build/hw/arm/svd/flexcomm_usart.h
2243 build/hw/arm/svd/flexspi.h
3100 build/hw/arm/svd/rt500_clkctl0.h
4022 build/hw/arm/svd/rt500_clkctl1.h
64 build/hw/arm/svd/rt500.h
1073 build/hw/arm/svd/rt500_rstctl0.h
1697 build/hw/arm/svd/rt500_rstctl1.h
15953 total
$ wc -c build/hw/arm/svd/*.h
4349 build/hw/arm/svd/flexcomm.h
51135 build/hw/arm/svd/flexcomm_i2c.h
43822 build/hw/arm/svd/flexcomm_spi.h
46331 build/hw/arm/svd/flexcomm_usart.h
89224 build/hw/arm/svd/flexspi.h
113952 build/hw/arm/svd/rt500_clkctl0.h
141885 build/hw/arm/svd/rt500_clkctl1.h
1881 build/hw/arm/svd/rt500.h
38881 build/hw/arm/svd/rt500_rstctl0.h
61449 build/hw/arm/svd/rt500_rstctl1.h
592909 total
> > >
> > > Signed-off-by: Octavian Purdila <tavip@google.com>
> > > ---
> > > hw/arm/svd/MIMXRT595S_cm33.xml | 224052
> > > ++++++++++++++++++++++++++++++
> >
> > I guess one thing we need to decide is if the source XML should live in
> > the repository as the preferred method of making changes or just the
> > translations generated by the tool.
> >
>
> I think we might want to store the XML in the qemu repo, even if we
> don't use it to generate the header files at compile time. This avoids
> issues with the original XML moving, going away, changed in
> incompatible ways, etc.
>
> As for generating the headers at compile time, I don't have a strong
> preference. I like it because there is slightly less work to do and it
> avoids dealing with resolving changes on both the SVD and the
> generated headers. For example, the initial headers are committed,
> then some changes are done directly to the headers and then we want to
> pick up a new SVD from the vendor to support a new hardware revision.
IIUC the structs/enums/etc are defining guest ABI. So if we want to
preserve guest ABI for these devices across QEMU releases, we don't
want the generated output to be arbitrarily changing. If there are
different revisions of a device, we might need separate structs for
each maintained in parallel.
>
> There are disadvantages as well: pysvd dependency for building qemu,
> hard to review if the vendor dumps a new version with lots of changes
> and we want to update to it for a new hardware revision, slight
> increase in build time.
>
> > > 1 file changed, 224052 insertions(+)
> > > create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
> > >
> > > diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
> > > new file mode 100644
> > > index 0000000000..8943aa3555
> > > --- /dev/null
> > > +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
> > > @@ -0,0 +1,1725 @@
> > > +<?xml version="1.0" encoding="UTF-8"?>
> > > +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
> > > + <vendor>nxp.com</vendor>
> > > + <name>MIMXRT595S_cm33</name>
> > > + <version>1.0</version>
> > > + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
> > > + <licenseText>
> > > +Copyright 2016-2023 NXP
> > > +SPDX-License-Identifier: BSD-3-Clause
> > > + </licenseText>
> >
> > This certainly seems compatible. XML is not the medium I personally
> > would have chosen as a register specification language but I guess there
> > are no other alternatives?
> >
>
> I agree that the choice of XML is unfortunate but I am not aware of
> alternatives, this is what vendors will provide.
Given the size of the XML I'm inclined to say that we should just be
committing the generated header files to qemu.git
Then add https://github.com/nxp-mcuxpresso/mcux-soc-svd as a git
submodule, and provide some meson rules for triggering re-generation
that are off-by-default.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-08 22:30 ` Octavian Purdila
2024-08-08 23:06 ` John Snow
@ 2024-08-09 9:30 ` Philippe Mathieu-Daudé
2024-08-09 9:42 ` Paolo Bonzini
1 sibling, 1 reply; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-09 9:30 UTC (permalink / raw)
To: Octavian Purdila, John Snow
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, crosa, bleal
On 9/8/24 00:30, Octavian Purdila wrote:
> On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
>>> diff --git a/configure b/configure
>>> index 5ad1674ca5..811bfa5d54 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
>>> # Finish preparing the virtual environment using vendored .whl files
>>>
>>> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
>>> - ${source_path}/pythondeps.toml meson || exit 1
>>> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
>>
>
> Hi John,
>
> Thanks for reviewing!
>
>>
>> I haven't read the rest of this series; I'm chiming in solely from the build/python maintainer angle. Do we *always* need pysvd, no matter how QEMU was configured? Adding it to the meson line here is a very big hammer.
>>
>
> I think the minimum we can do is to install it only if CONFIG_ARM is
> enabled. That might change in the future if the models we create with
> pysvd are enabled for other architectures.
Similarly on how we manage libfdt, you can have meson defines
SVDGEN as:
config_host_data.set('CONFIG_SVDGEN', svd_gen_header.found())
Then declare SVDGEN in Kconfig.host, and finally use in the Kconfigs:
config FLEXCOMM_UART
bool
depends on SVDGEN
config RT500
bool
depends on ARM
select FLEXCOMM_UART
See for FDT examples:
$ git grep 'depends on.*FDT'
hw/core/Kconfig:10: depends on FDT
hw/i386/Kconfig:118: depends on I386 && FDT
hw/loongarch/Kconfig:4: depends on LOONGARCH64 && FDT
hw/mips/Kconfig:84: depends on MIPS64 && !TARGET_BIG_ENDIAN && FDT
hw/ppc/Kconfig:4: depends on PPC64 && FDT
hw/ppc/Kconfig:29: depends on PPC64 && FDT
hw/ppc/Kconfig:58: depends on PPC && FDT
hw/ppc/Kconfig:77: depends on PPC && FDT
hw/ppc/Kconfig:174: depends on PPC && FDT
hw/ppc/Kconfig:180: depends on PPC && FDT
hw/ppc/Kconfig:186: depends on PPC && FDT
hw/rx/Kconfig:11: depends on RX && FDT
hw/xtensa/Kconfig:17: depends on XTENSA && FDT
>> If not, consider looking at how sphinx (the "docs" group) is only conditionally installed into the configure venv and mimic that using the appropriate configure flags that necessitate the availability of pyvsd for the QEMU build.
>
> Thanks for the pointer, I'll take a look.
>
>>
>> We also need to provide a way for pysvd to be available offline; some packages are available via distro libs and if this package is available for every distro we officially support, that's sufficient (but requires updates to our various docker and VM test configuration files to add the new dependency). Otherwise, like we do for meson, we need to vendor the wheel in the tree so offline tarball builds will continue to work.
>>
>> It looks like pysvd is a pure python package with no dependencies, so it should be OK to vendor it in qemu.git/python/wheels/ - look at qemu.git/python/scripts/vendor.py and consider updating and running this script.
>
> Thanks, I'll look at it and add it in v2.
>
>>
>> (The real blocker here is that RPM builds are performed offline and dependencies that cannot be satisfied via rpm can't be added through pip. We need any one of these to be true: (A) pyvsd is available (of a sufficient version) in all distro repositories we target; (B) This build feature is conditional and nobody minds if it never gets enabled for RPM builds; (C) The package can be vendored.)
>>
>> ~~js
>>
>> That said, you might be the first person I've seen outside of Paolo and I to brave mucking around with the python build venv. You deserve a bravery sticker :)
>>
>>>
>>> # At this point, we expect Meson to be installed and available.
>>> # We expect mkvenv or pip to have created pyvenv/bin/meson for us.
>>> diff --git a/meson.build b/meson.build
>>> index ec59effca2..dee587483b 100644
>>> --- a/meson.build
>>> +++ b/meson.build
>>> @@ -3235,6 +3235,10 @@ tracetool_depends = files(
>>> 'scripts/tracetool/vcpu.py'
>>> )
>>>
>>> +svd_gen_header = [
>>> + python, files('scripts/svd-gen-header.py')
>>> +]
>>> +
>>> qemu_version_cmd = [find_program('scripts/qemu-version.sh'),
>>> meson.current_source_dir(),
>>> get_option('pkgversion'), meson.project_version()]
>>> diff --git a/python/setup.cfg b/python/setup.cfg
>>> index 48668609d3..bc830c541a 100644
>>> --- a/python/setup.cfg
>>> +++ b/python/setup.cfg
>>> @@ -45,6 +45,7 @@ devel =
>>> urwid >= 2.1.2
>>> urwid-readline >= 0.13
>>> Pygments >= 2.9.0
>>> + pysvd >= 0.2.3
>>>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-09 9:30 ` Philippe Mathieu-Daudé
@ 2024-08-09 9:42 ` Paolo Bonzini
2024-08-09 9:59 ` Daniel P. Berrangé
0 siblings, 1 reply; 56+ messages in thread
From: Paolo Bonzini @ 2024-08-09 9:42 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Octavian Purdila, John Snow, qemu-devel, qemu-arm, stefanst,
alex.bennee, thuth, peter.maydell, marcandre.lureau, alistair,
berrange, crosa, bleal
On Fri, Aug 9, 2024 at 11:30 AM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> On 9/8/24 00:30, Octavian Purdila wrote:
> > On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
>
>
> >>> diff --git a/configure b/configure
> >>> index 5ad1674ca5..811bfa5d54 100755
> >>> --- a/configure
> >>> +++ b/configure
> >>> @@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
> >>> # Finish preparing the virtual environment using vendored .whl files
> >>>
> >>> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
> >>> - ${source_path}/pythondeps.toml meson || exit 1
> >>> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
> >>
> >
> > Hi John,
> >
> > Thanks for reviewing!
> >
> >>
> >> I haven't read the rest of this series; I'm chiming in solely from the build/python maintainer angle. Do we *always* need pysvd, no matter how QEMU was configured? Adding it to the meson line here is a very big hammer.
> >>
> >
> > I think the minimum we can do is to install it only if CONFIG_ARM is
> > enabled. That might change in the future if the models we create with
> > pysvd are enabled for other architectures.
>
> Similarly on how we manage libfdt, you can have meson defines
> SVDGEN as:
>
> config_host_data.set('CONFIG_SVDGEN', svd_gen_header.found())
>
> Then declare SVDGEN in Kconfig.host, and finally use in the Kconfigs:
That would force people to install pysvd on the host, which is a pity
for such a small and little-known library. We have used submodules
in the past for much larger and common dependencies, for example
capstone.
Paolo
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-09 9:42 ` Paolo Bonzini
@ 2024-08-09 9:59 ` Daniel P. Berrangé
2024-08-13 8:32 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 56+ messages in thread
From: Daniel P. Berrangé @ 2024-08-09 9:59 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Philippe Mathieu-Daudé, Octavian Purdila, John Snow,
qemu-devel, qemu-arm, stefanst, alex.bennee, thuth, peter.maydell,
marcandre.lureau, alistair, crosa, bleal
On Fri, Aug 09, 2024 at 11:42:38AM +0200, Paolo Bonzini wrote:
> On Fri, Aug 9, 2024 at 11:30 AM Philippe Mathieu-Daudé
> <philmd@linaro.org> wrote:
> >
> > On 9/8/24 00:30, Octavian Purdila wrote:
> > > On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
> >
> >
> > >>> diff --git a/configure b/configure
> > >>> index 5ad1674ca5..811bfa5d54 100755
> > >>> --- a/configure
> > >>> +++ b/configure
> > >>> @@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
> > >>> # Finish preparing the virtual environment using vendored .whl files
> > >>>
> > >>> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
> > >>> - ${source_path}/pythondeps.toml meson || exit 1
> > >>> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
> > >>
> > >
> > > Hi John,
> > >
> > > Thanks for reviewing!
> > >
> > >>
> > >> I haven't read the rest of this series; I'm chiming in solely from the build/python maintainer angle. Do we *always* need pysvd, no matter how QEMU was configured? Adding it to the meson line here is a very big hammer.
> > >>
> > >
> > > I think the minimum we can do is to install it only if CONFIG_ARM is
> > > enabled. That might change in the future if the models we create with
> > > pysvd are enabled for other architectures.
> >
> > Similarly on how we manage libfdt, you can have meson defines
> > SVDGEN as:
> >
> > config_host_data.set('CONFIG_SVDGEN', svd_gen_header.found())
> >
> > Then declare SVDGEN in Kconfig.host, and finally use in the Kconfigs:
>
> That would force people to install pysvd on the host, which is a pity
> for such a small and little-known library. We have used submodules
> in the past for much larger and common dependencies, for example
> capstone.
As mentioned elsewhere in thsi threads, IMHO we shoud not be running
this generator during the build at all. It should be run once to
create the headers needed for a particular device & the output committed
to QEMU. There after any changes to the header (if any) need reviewing
to ensure they don't imply an impact on guest ABI. This avoids having
the 8.6 MB XML file in QEMU git too, as well as the pysvd dep on the
host. Only the very rare times when we create a new device would need
to have the pysvd code & XML.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-09 6:34 ` Paolo Bonzini
@ 2024-08-09 19:28 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-09 19:28 UTC (permalink / raw)
To: Paolo Bonzini
Cc: John Snow, qemu-devel, qemu-arm, stefanst, alex.bennee, thuth,
peter.maydell, marcandre.lureau, alistair, berrange, philmd,
crosa, bleal
On Thu, Aug 8, 2024 at 11:34 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 8/8/24 23:56, John Snow wrote:
> > I haven't read the rest of this series; I'm chiming in solely from the
> > build/python maintainer angle. Do we *always* need pysvd, no matter how
> > QEMU was configured? Adding it to the meson line here is a very big hammer.
>
> In general I'd agree, though for a 7.5 kB package with no other
> dependencies I'm willing to make an exception.
>
> The alternative would be pretty complex:
>
> - check if pysvd is installed in the host
>
> - check if a machine type that needs pysvd is enabled, defaulting to y
> or n depending on the previous step and --enable-download
>
> - use that to decide between doing nothing, installing pysvd or erroring out
>
> - pass the availability of pysvd to Kconfig
>
> - use that to make the final determination on whether to enable those
> machine types that use pysvd
>
> This is quite obviously overengineered compared to the alternative.
>
> Another possibility is to ship the generated file, skip regeneration if
> pysvd is not installed (on the host), and not touch pythondeps.toml at
> all. Whether shipping a generated file is acceptable should be decided
> by Peter as ARM maintainer, personally I would go the way that Octavian
> is going already and I'm mentioning the rest only for completeness and
> education.
>
> However...
>
> > We also need to provide a way for pysvd to be available offline; some
> > packages are available via distro libs and if this package is available
> > for every distro we officially support, that's sufficient (but requires
> > updates to our various docker and VM test configuration files to add the
> > new dependency). Otherwise, like we do for meson, we need to vendor the
> > wheel in the tree so offline tarball builds will continue to work.
> >
> > It looks like pysvd is a pure python package with no dependencies, so it
> > should be OK to vendor it in qemu.git/python/wheels/ - look at
> > qemu.git/python/scripts/vendor.py and consider updating and running this
> > script.
>
> ... this is indeed correct. It's not hard and it helps building on
> older distros. Future versions of Debian or Fedora might package pysvd,
> but right now it's not included anywhere
> (https://repology.org/project/python:pysvd/history).
>
> > That said, you might be the first person I've seen outside of Paolo and
> > I to brave mucking around with the python build venv. You deserve a
> > bravery sticker :)
>
> It's not that bad, come on. :) But yeah, I'm positively surprised by
> the effort to include pysvd in the virtual environment, and any
> suggestions to improve the documentation and discoverability of the venv
> setup are welcome. I'm curious whether you figured it out yourself or
> found https://www.qemu.org/docs/master/devel/build-system.html.
>
A combination of reading the docs above and looking at how things are
done for other packages (meson, sphinx). It really was
straightforward.
> One thing I heard from others is that pythondeps.toml looks "too much
> like" a fringe standard Python feature that no one has heard about. In
> some sense that's a great compliment, but I can see how it can be a bit
> disconcerting.
>
Now that you mentioned it I can see how people might think that :) But
the build system docs are pretty clear IMO.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595
2024-08-09 9:13 ` Daniel P. Berrangé
@ 2024-08-09 22:40 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-09 22:40 UTC (permalink / raw)
To: Daniel P. Berrangé, peter.maydell
Cc: Alex Bennée, qemu-devel, qemu-arm, stefanst, pbonzini, thuth,
marcandre.lureau, alistair, philmd, jsnow, crosa, bleal
On Fri, Aug 9, 2024 at 2:13 AM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Tue, Aug 06, 2024 at 01:31:51PM -0700, Octavian Purdila wrote:
> > On Tue, Aug 6, 2024 at 7:06 AM Alex Bennée <alex.bennee@linaro.org> wrote:
> > >
> > > Octavian Purdila <tavip@google.com> writes:
> > >
> > > > Picked from:
> > > >
> > > > https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT595S/MIMXRT595S_cm33.xml
> > > >
> > > > NOTE: the file is truncated to keep the email size reasonable. Please
> > > > use the link above and download the full file if you want to try out
> > > > the patch.
>
> "the file is truncated" wins understatement of the week.
>
> The full XML file that would need to be in QEMU git for this is 8.6 MB in
> size.
>
> The overall generated headers that we get from it are ~16k lines,
> or ~0.5 MB
>
> $ wc -l build/hw/arm/svd/*.h
> 135 build/hw/arm/svd/flexcomm.h
> 1227 build/hw/arm/svd/flexcomm_i2c.h
> 1161 build/hw/arm/svd/flexcomm_spi.h
> 1231 build/hw/arm/svd/flexcomm_usart.h
> 2243 build/hw/arm/svd/flexspi.h
> 3100 build/hw/arm/svd/rt500_clkctl0.h
> 4022 build/hw/arm/svd/rt500_clkctl1.h
> 64 build/hw/arm/svd/rt500.h
> 1073 build/hw/arm/svd/rt500_rstctl0.h
> 1697 build/hw/arm/svd/rt500_rstctl1.h
> 15953 total
>
> $ wc -c build/hw/arm/svd/*.h
> 4349 build/hw/arm/svd/flexcomm.h
> 51135 build/hw/arm/svd/flexcomm_i2c.h
> 43822 build/hw/arm/svd/flexcomm_spi.h
> 46331 build/hw/arm/svd/flexcomm_usart.h
> 89224 build/hw/arm/svd/flexspi.h
> 113952 build/hw/arm/svd/rt500_clkctl0.h
> 141885 build/hw/arm/svd/rt500_clkctl1.h
> 1881 build/hw/arm/svd/rt500.h
> 38881 build/hw/arm/svd/rt500_rstctl0.h
> 61449 build/hw/arm/svd/rt500_rstctl1.h
> 592909 total
>
>
>
> > > >
> > > > Signed-off-by: Octavian Purdila <tavip@google.com>
> > > > ---
> > > > hw/arm/svd/MIMXRT595S_cm33.xml | 224052
> > > > ++++++++++++++++++++++++++++++
> > >
> > > I guess one thing we need to decide is if the source XML should live in
> > > the repository as the preferred method of making changes or just the
> > > translations generated by the tool.
> > >
> >
> > I think we might want to store the XML in the qemu repo, even if we
> > don't use it to generate the header files at compile time. This avoids
> > issues with the original XML moving, going away, changed in
> > incompatible ways, etc.
> >
> > As for generating the headers at compile time, I don't have a strong
> > preference. I like it because there is slightly less work to do and it
> > avoids dealing with resolving changes on both the SVD and the
> > generated headers. For example, the initial headers are committed,
> > then some changes are done directly to the headers and then we want to
> > pick up a new SVD from the vendor to support a new hardware revision.
>
> IIUC the structs/enums/etc are defining guest ABI. So if we want to
> preserve guest ABI for these devices across QEMU releases, we don't
> want the generated output to be arbitrarily changing. If there are
> different revisions of a device, we might need separate structs for
> each maintained in parallel.
>
I think we can review changes to prevent ABI breakages both for XML
and generated headers - unless we use meson git-wrap submodules.
>
>
> >
> > There are disadvantages as well: pysvd dependency for building qemu,
> > hard to review if the vendor dumps a new version with lots of changes
> > and we want to update to it for a new hardware revision, slight
> > increase in build time.
> >
> > > > 1 file changed, 224052 insertions(+)
> > > > create mode 100644 hw/arm/svd/MIMXRT595S_cm33.xml
> > > >
> > > > diff --git a/hw/arm/svd/MIMXRT595S_cm33.xml b/hw/arm/svd/MIMXRT595S_cm33.xml
> > > > new file mode 100644
> > > > index 0000000000..8943aa3555
> > > > --- /dev/null
> > > > +++ b/hw/arm/svd/MIMXRT595S_cm33.xml
> > > > @@ -0,0 +1,1725 @@
> > > > +<?xml version="1.0" encoding="UTF-8"?>
> > > > +<device schemaVersion="1.3" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd">
> > > > + <vendor>nxp.com</vendor>
> > > > + <name>MIMXRT595S_cm33</name>
> > > > + <version>1.0</version>
> > > > + <description>MIMXRT595SFAWC,MIMXRT595SFFOC</description>
> > > > + <licenseText>
> > > > +Copyright 2016-2023 NXP
> > > > +SPDX-License-Identifier: BSD-3-Clause
> > > > + </licenseText>
> > >
> > > This certainly seems compatible. XML is not the medium I personally
> > > would have chosen as a register specification language but I guess there
> > > are no other alternatives?
> > >
> >
> > I agree that the choice of XML is unfortunate but I am not aware of
> > alternatives, this is what vendors will provide.
>
> Given the size of the XML I'm inclined to say that we should just be
> committing the generated header files to qemu.git
>
Fine with me. I think we can also reconsider later with minimal effort
if this gets widely used and there is lots of churn.
Peter, what do you think?
> Then add https://github.com/nxp-mcuxpresso/mcux-soc-svd as a git
> submodule, and provide some meson rules for triggering re-generation
> that are off-by-default.
>
Sounds good. I was thinking of adding mcux-soc-svd as a meson
subproject via git-wrap as suggested by Philippe.
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-05 20:16 ` [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files Octavian Purdila
2024-08-08 21:56 ` John Snow
@ 2024-08-12 15:27 ` Peter Maydell
2024-08-12 17:56 ` Octavian Purdila
1 sibling, 1 reply; 56+ messages in thread
From: Peter Maydell @ 2024-08-12 15:27 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
>
> From: Stefan Stanacar <stefanst@google.com>
>
> From: Stefan Stanacar <stefanst@google.com>
>
> The CMSIS System View Description format(CMSIS-SVD) is an XML based
> description of Arm Cortex-M microcontrollers provided and maintained
> by sillicon vendors. It includes details such as peripherals registers
> (down to bitfields), peripheral register block addresses, reset
> values, etc.
>
> This script uses this information to create header files that makes it
> easier to emulate peripherals.
>
> The script can be used to create either peripheral specific headers or
> board / system specific information. The script generated headers are
> similar to the SVDConv utility.
>
> Peripheral specific headers contains information such as register
> layout, register names and reset values for registers:
>
> typedef struct {
> ...
> union {
> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
> * Flexcomm module ID */
> struct {
> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
> uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
> uint32_t : 4;
> uint32_t ID : 20; /* [31..12] Flexcomm ID */
> } PSELID_b;
> };
Bitfield layout in C isn't portable, so don't generate this kind
of union-of-a-integer-and-some-bitfields, please. You can
generate FIELD() macro invocations (see include/hw/registerfields.h)
which define shift/mask/length macros that can be used with
FIELD_EX*/FIELD_DP* to do extract/deposit operations.
> ...
> } FLEXCOMM_Type; /* Size = 4096 (0x1000) */
>
> #define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
> #define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
> #define FLEXCOMM_PSELID_LOCK_Pos (3UL)
> #define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
> ...
>
> typedef enum { /* FLEXCOMM_PSELID_LOCK */
> /* Peripheral select can be changed by software. */
> FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
> /* Peripheral select is locked and cannot be changed until this
> * Flexcomm module or the entire device is reset. */
> FLEXCOMM_PSELID_LOCK_LOCKED = 1,
> } FLEXCOMM_PSELID_LOCK_Enum;
> ...
>
> #define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
> const char *_name[sizeof(FLEXCOMM_Type)] = { \
> [4088 ... 4091] = "PSELID", \
> [4092 ... 4095] = "PID", \
> }
>
> Board specific headers contains information about peripheral base
> register addresses.
thanks
-- PMM
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 05/23] hw: add register access utility functions
2024-08-05 20:17 ` [RFC PATCH 05/23] hw: add register access utility functions Octavian Purdila
@ 2024-08-12 15:32 ` Peter Maydell
2024-08-12 21:14 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Peter Maydell @ 2024-08-12 15:32 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
>
> Add register access utility functions for device models, like checking
> aligned access and reading and writing to a register backstore.
> Signed-off-by: Octavian Purdila <tavip@google.com>
> ---
> include/hw/regs.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 89 insertions(+)
> create mode 100644 include/hw/regs.h
>
> diff --git a/include/hw/regs.h b/include/hw/regs.h
> new file mode 100644
> index 0000000000..8d0da0629d
> --- /dev/null
> +++ b/include/hw/regs.h
> @@ -0,0 +1,89 @@
> +/*
> + * Useful macros/functions for register handling.
> + *
> + * Copyright (c) 2021 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef HW_REGS_H
> +#define HW_REGS_H
> +
> +#include "exec/hwaddr.h"
> +#include "exec/memattrs.h"
> +
> +#define BITS(hi, lo) (BIT(hi + 1) - BIT(lo))
> +#define BIT_IS_SET(v, b) (((v) & BIT(b)) != 0)
To the extent we need these we should be putting them in
bits.h with the other bit-related operations. (But
prefer the existing MAKE_64BIT_MASK over adding a
second macro that evaluates to a mask with a given
run of bits set).
> +
> +/*
> + * reg32_aligned_access
> + * @addr: address to check
> + * @size: size of access
> + *
> + * Check if access to a hardware address is 32bit aligned.
> + *
> + * Returns: true if access is 32bit aligned, false otherwise
> + */
> +static inline bool reg32_aligned_access(hwaddr addr, unsigned size)
> +{
> + if (size != 4 || addr % 4 != 0) {
> + return false;
> + }
> + return true;
> +}
> +
> +/*
> + * reg32_write
> + * @base: base address
> + * @addr: register offset in bytes
> + * @val: value to write
> + * @wr_bits_array: RW bitmask array
> + *
> + * Update the RW/WO bits of a 32bit register backstore with a given value
> + * (discarding updats to the RO bits). The RW/WO bits are encoded in the
> + * @wr_bits_array with bits set being RW and bits unset being RO.
> + *
> + * Usage example:
> + *
> + * wr_bits_array[] = {
> + * [REG1_ADDR/4] = 0xFF000000, // MSB byte writable
> + * [REG2_ADDR/4] = 0xFF, // LSB byte writable
> + * // all other registers are read-only
> + * };
> + *
> + * // backstore is updated to 0x12000000
> + * reg32_write(&backstore, REG1_ADDR, 0x12345678, wr_bits_array);
> + * // backstore is updated to 0x78
> + * reg32_write(&backstore, REG2_ADDR, 0x12345678, wr_bits_array);
> + */
This seems like it's reimplementing include/hw/register.h
functionality. I'm not personally a super-fan of that API,
but I definitely would prefer it if we didn't have more than
one way to do this.
> +static inline uint32_t reg32_write(void *base, uint32_t off, uint32_t val,
> + const uint32_t *rw_bits_array)
> +{
> + uint32_t *ptr = base + addr;
> + uint32_t old_value = *ptr;
> + uint32_t mask = rw_bits_array ? rw_bits_array[addr / 4] : 0xFFFFFFFF;
> +
> + /* set WO/RW bits */
> + *ptr |= val & mask;
> + /* clear RO bits */
> + *ptr &= val | ~mask;
> +
> + return old_value;
> +}
> +
> +/*
> + * reg32_read
> + * @base: base address
> + * @addr: register offset in bytes
> + *
> + * Returns: 32bit value from register backstore
> + */
> +static inline uint32_t reg32_read(void *base, uint32_t addr)
> +{
> + return *(uint32_t *)(base + addr);
> +}
Pointer type handling looks suspicious here -- if
the thing we're accessing is really a uint32_t* then
we should take that; if it isn't then casting it to
one and dereferencing might be reading unaligned memory.
thanks
-- PMM
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
` (22 preceding siblings ...)
2024-08-05 20:17 ` [RFC PATCH 23/23] hw/arm: add RT595-EVK board Octavian Purdila
@ 2024-08-12 16:10 ` Peter Maydell
2024-08-12 16:22 ` Daniel P. Berrangé
23 siblings, 1 reply; 56+ messages in thread
From: Peter Maydell @ 2024-08-12 16:10 UTC (permalink / raw)
To: Octavian Purdila
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
> It also introduces unit tests for device models. To allow accessing
> registers from unit tests a system bus mock is created. The main
> advantage of unit tests for device models over QTest is that device
> models can be tested in isolation and do not require a full qemu
> machine.
On the other hand the disadvantage is that you need to add a
bunch of extra code to mock the interfaces that the device
connects to, and then you compile into the test binary a
subset of C files which weren't written with the expectation
that they got compiled into tests like that, so it feels a
bit brittle. The nice thing about qtest is that it doesn't
need you to do any of that -- you just run the QEMU machine
model and prod the devices it already has.
Do you have examples in this series of tests which you
were able to write with this unit test setup that you
wouldn't have been able to write equivalents of with the
qtest framework ?
thanks
-- PMM
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests
2024-08-12 16:10 ` [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Peter Maydell
@ 2024-08-12 16:22 ` Daniel P. Berrangé
2024-08-12 18:39 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Daniel P. Berrangé @ 2024-08-12 16:22 UTC (permalink / raw)
To: Peter Maydell
Cc: Octavian Purdila, qemu-devel, qemu-arm, stefanst, pbonzini,
alex.bennee, thuth, marcandre.lureau, alistair, philmd, jsnow,
crosa, bleal
On Mon, Aug 12, 2024 at 05:10:52PM +0100, Peter Maydell wrote:
> On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
> > It also introduces unit tests for device models. To allow accessing
> > registers from unit tests a system bus mock is created. The main
> > advantage of unit tests for device models over QTest is that device
> > models can be tested in isolation and do not require a full qemu
> > machine.
>
> On the other hand the disadvantage is that you need to add a
> bunch of extra code to mock the interfaces that the device
> connects to, and then you compile into the test binary a
> subset of C files which weren't written with the expectation
> that they got compiled into tests like that, so it feels a
> bit brittle. The nice thing about qtest is that it doesn't
> need you to do any of that -- you just run the QEMU machine
> model and prod the devices it already has.
Yep, mocking often ends up being rather a double-edged sword.
You can do some really powerful things with it, and in particular
it can enable you test scenarios / code paths that are hard or
impractical to exercise with regular functional tests where fault
injection is difficult/impossible.
The cost of this though is that the mocks can pose a significant
ongoing maint burden on anyone who refactors code in future, as
the mocks need to evolve in lockstep with the code refactoring.
12 months down the line, it can be hard for another maintainer
to understand the full purpose of the mocks & thus even harder
to know what todo as they refactor the code. The future burden
of mocks rarely falls on the person who creates them in the
first place.
> Do you have examples in this series of tests which you
> were able to write with this unit test setup that you
> wouldn't have been able to write equivalents of with the
> qtest framework ?
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-12 15:27 ` Peter Maydell
@ 2024-08-12 17:56 ` Octavian Purdila
2024-08-12 22:43 ` Richard Henderson
0 siblings, 1 reply; 56+ messages in thread
From: Octavian Purdila @ 2024-08-12 17:56 UTC (permalink / raw)
To: Peter Maydell
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Mon, Aug 12, 2024 at 8:27 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>
Hi Peter,
Thanks for the review!
> On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
> >
> > From: Stefan Stanacar <stefanst@google.com>
> >
> > From: Stefan Stanacar <stefanst@google.com>
> >
> > The CMSIS System View Description format(CMSIS-SVD) is an XML based
> > description of Arm Cortex-M microcontrollers provided and maintained
> > by sillicon vendors. It includes details such as peripherals registers
> > (down to bitfields), peripheral register block addresses, reset
> > values, etc.
> >
> > This script uses this information to create header files that makes it
> > easier to emulate peripherals.
> >
> > The script can be used to create either peripheral specific headers or
> > board / system specific information. The script generated headers are
> > similar to the SVDConv utility.
> >
> > Peripheral specific headers contains information such as register
> > layout, register names and reset values for registers:
> >
> > typedef struct {
> > ...
> > union {
> > uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
> > * Flexcomm module ID */
> > struct {
> > uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
> > uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
> > uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
> > uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
> > uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
> > uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
> > uint32_t : 4;
> > uint32_t ID : 20; /* [31..12] Flexcomm ID */
> > } PSELID_b;
> > };
>
> Bitfield layout in C isn't portable, so don't generate this kind
> of union-of-a-integer-and-some-bitfields, please. You can
> generate FIELD() macro invocations (see include/hw/registerfields.h)
> which define shift/mask/length macros that can be used with
> FIELD_EX*/FIELD_DP* to do extract/deposit operations.
>
I see that C bitfields are already used in a few places in qemu. Could
you please elaborate on the portability issue?
> > ...
> > } FLEXCOMM_Type; /* Size = 4096 (0x1000) */
> >
> > #define FLEXCOMM_PSELID_PERSEL_Pos (0UL)
> > #define FLEXCOMM_PSELID_PERSEL_Msk (0x7UL)
> > #define FLEXCOMM_PSELID_LOCK_Pos (3UL)
> > #define FLEXCOMM_PSELID_LOCK_Msk (0x8UL)
> > ...
> >
> > typedef enum { /* FLEXCOMM_PSELID_LOCK */
> > /* Peripheral select can be changed by software. */
> > FLEXCOMM_PSELID_LOCK_UNLOCKED = 0,
> > /* Peripheral select is locked and cannot be changed until this
> > * Flexcomm module or the entire device is reset. */
> > FLEXCOMM_PSELID_LOCK_LOCKED = 1,
> > } FLEXCOMM_PSELID_LOCK_Enum;
> > ...
> >
> > #define FLEXCOMM_REGISTER_NAMES_ARRAY(_name) \
> > const char *_name[sizeof(FLEXCOMM_Type)] = { \
> > [4088 ... 4091] = "PSELID", \
> > [4092 ... 4095] = "PID", \
> > }
> >
> > Board specific headers contains information about peripheral base
> > register addresses.
>
> thanks
> -- PMM
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests
2024-08-12 16:22 ` Daniel P. Berrangé
@ 2024-08-12 18:39 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-12 18:39 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Peter Maydell, qemu-devel, qemu-arm, stefanst, pbonzini,
alex.bennee, thuth, marcandre.lureau, alistair, philmd, jsnow,
crosa, bleal
On Mon, Aug 12, 2024 at 9:22 AM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Mon, Aug 12, 2024 at 05:10:52PM +0100, Peter Maydell wrote:
> > On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
> > > It also introduces unit tests for device models. To allow accessing
> > > registers from unit tests a system bus mock is created. The main
> > > advantage of unit tests for device models over QTest is that device
> > > models can be tested in isolation and do not require a full qemu
> > > machine.
> >
> > On the other hand the disadvantage is that you need to add a
> > bunch of extra code to mock the interfaces that the device
> > connects to, and then you compile into the test binary a
> > subset of C files which weren't written with the expectation
> > that they got compiled into tests like that, so it feels a
> > bit brittle. The nice thing about qtest is that it doesn't
> > need you to do any of that -- you just run the QEMU machine
> > model and prod the devices it already has.
>
> Yep, mocking often ends up being rather a double-edged sword.
> You can do some really powerful things with it, and in particular
> it can enable you test scenarios / code paths that are hard or
> impractical to exercise with regular functional tests where fault
> injection is difficult/impossible.
>
> The cost of this though is that the mocks can pose a significant
> ongoing maint burden on anyone who refactors code in future, as
> the mocks need to evolve in lockstep with the code refactoring.
>
> 12 months down the line, it can be hard for another maintainer
> to understand the full purpose of the mocks & thus even harder
> to know what todo as they refactor the code. The future burden
> of mocks rarely falls on the person who creates them in the
> first place.
>
I agree that there are potentially maintenance issues. For what is
worth, AFAIR, we only ran into one breaking change in about 3 years
for which we used this approach which was also simple to fix (gpio was
removed from hwcore so we had to pull in gpio.c directly).
> > Do you have examples in this series of tests which you
> > were able to write with this unit test setup that you
> > wouldn't have been able to write equivalents of with the
> > qtest framework ?
>
No, I think all of the tests in the patch set could be written in qtest.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 05/23] hw: add register access utility functions
2024-08-12 15:32 ` Peter Maydell
@ 2024-08-12 21:14 ` Octavian Purdila
2024-08-12 22:35 ` Richard Henderson
2024-08-13 8:28 ` Philippe Mathieu-Daudé
0 siblings, 2 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-12 21:14 UTC (permalink / raw)
To: Peter Maydell
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, philmd, jsnow, crosa, bleal
On Mon, Aug 12, 2024 at 8:33 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
> >
> > Add register access utility functions for device models, like checking
> > aligned access and reading and writing to a register backstore.
>
>
> > Signed-off-by: Octavian Purdila <tavip@google.com>
> > ---
> > include/hw/regs.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 89 insertions(+)
> > create mode 100644 include/hw/regs.h
> >
> > diff --git a/include/hw/regs.h b/include/hw/regs.h
> > new file mode 100644
> > index 0000000000..8d0da0629d
> > --- /dev/null
> > +++ b/include/hw/regs.h
> > @@ -0,0 +1,89 @@
> > +/*
> > + * Useful macros/functions for register handling.
> > + *
> > + * Copyright (c) 2021 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#ifndef HW_REGS_H
> > +#define HW_REGS_H
> > +
> > +#include "exec/hwaddr.h"
> > +#include "exec/memattrs.h"
> > +
> > +#define BITS(hi, lo) (BIT(hi + 1) - BIT(lo))
> > +#define BIT_IS_SET(v, b) (((v) & BIT(b)) != 0)
>
> To the extent we need these we should be putting them in
> bits.h with the other bit-related operations. (But
> prefer the existing MAKE_64BIT_MASK over adding a
> second macro that evaluates to a mask with a given
> run of bits set).
>
BITS is useful when defining masks from datasheets. Specifically, in
this patch set they are used to define writable bits. While it is
possible to use MAKE_64BIT_MASK, I find it less error prone to use
BITS.
However, it just occurred to me that we might be able to use the SVD
information to derive this. If possible I'll just remove it in v2
(otherwise I will move it to include/qemu/bitops.h).
> > +
> > +/*
> > + * reg32_aligned_access
> > + * @addr: address to check
> > + * @size: size of access
> > + *
> > + * Check if access to a hardware address is 32bit aligned.
> > + *
> > + * Returns: true if access is 32bit aligned, false otherwise
> > + */
> > +static inline bool reg32_aligned_access(hwaddr addr, unsigned size)
> > +{
> > + if (size != 4 || addr % 4 != 0) {
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > +/*
> > + * reg32_write
> > + * @base: base address
> > + * @addr: register offset in bytes
> > + * @val: value to write
> > + * @wr_bits_array: RW bitmask array
> > + *
> > + * Update the RW/WO bits of a 32bit register backstore with a given value
> > + * (discarding updats to the RO bits). The RW/WO bits are encoded in the
> > + * @wr_bits_array with bits set being RW and bits unset being RO.
> > + *
> > + * Usage example:
> > + *
> > + * wr_bits_array[] = {
> > + * [REG1_ADDR/4] = 0xFF000000, // MSB byte writable
> > + * [REG2_ADDR/4] = 0xFF, // LSB byte writable
> > + * // all other registers are read-only
> > + * };
> > + *
> > + * // backstore is updated to 0x12000000
> > + * reg32_write(&backstore, REG1_ADDR, 0x12345678, wr_bits_array);
> > + * // backstore is updated to 0x78
> > + * reg32_write(&backstore, REG2_ADDR, 0x12345678, wr_bits_array);
> > + */
>
> This seems like it's reimplementing include/hw/register.h
> functionality. I'm not personally a super-fan of that API,
> but I definitely would prefer it if we didn't have more than
> one way to do this.
>
Interesting, I was not aware of this thanks for pointing it out!
It seems a bit heavy to be used just for the write masking part, but
I'll take a closer look at it.
> > +static inline uint32_t reg32_write(void *base, uint32_t off, uint32_t val,
> > + const uint32_t *rw_bits_array)
> > +{
> > + uint32_t *ptr = base + addr;
> > + uint32_t old_value = *ptr;
> > + uint32_t mask = rw_bits_array ? rw_bits_array[addr / 4] : 0xFFFFFFFF;
> > +
> > + /* set WO/RW bits */
> > + *ptr |= val & mask;
> > + /* clear RO bits */
> > + *ptr &= val | ~mask;
> > +
> > + return old_value;
> > +}
> > +
> > +/*
> > + * reg32_read
> > + * @base: base address
> > + * @addr: register offset in bytes
> > + *
> > + * Returns: 32bit value from register backstore
> > + */
> > +static inline uint32_t reg32_read(void *base, uint32_t addr)
> > +{
> > + return *(uint32_t *)(base + addr);
> > +}
>
> Pointer type handling looks suspicious here -- if
> the thing we're accessing is really a uint32_t* then
> we should take that; if it isn't then casting it to
> one and dereferencing might be reading unaligned memory.
>
It is used for performing generic accesses to generated structs (patch
3/23) which should be aligned in the way that are used in the patch
set. If we decide to keep it, I'll add a note regarding alignment.
> thanks
> -- PMM
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 05/23] hw: add register access utility functions
2024-08-12 21:14 ` Octavian Purdila
@ 2024-08-12 22:35 ` Richard Henderson
2024-08-13 8:28 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 56+ messages in thread
From: Richard Henderson @ 2024-08-12 22:35 UTC (permalink / raw)
To: Octavian Purdila; +Cc: qemu-devel
On 8/13/24 07:14, Octavian Purdila wrote:
>>> +#define BITS(hi, lo) (BIT(hi + 1) - BIT(lo))
>>> +#define BIT_IS_SET(v, b) (((v) & BIT(b)) != 0)
>>
>> To the extent we need these we should be putting them in
>> bits.h with the other bit-related operations. (But
>> prefer the existing MAKE_64BIT_MASK over adding a
>> second macro that evaluates to a mask with a given
>> run of bits set).
>>
>
> BITS is useful when defining masks from datasheets. Specifically, in
> this patch set they are used to define writable bits. While it is
> possible to use MAKE_64BIT_MASK, I find it less error prone to use
> BITS.
The formulation of BITS is less than perfect: BITS(63, 62) cannot be evaluated because
BIT(64) is undefined.
r~
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-12 17:56 ` Octavian Purdila
@ 2024-08-12 22:43 ` Richard Henderson
2024-08-13 15:47 ` Octavian Purdila
0 siblings, 1 reply; 56+ messages in thread
From: Richard Henderson @ 2024-08-12 22:43 UTC (permalink / raw)
To: Octavian Purdila; +Cc: qemu-devel
On 8/13/24 03:56, Octavian Purdila wrote:
>>> typedef struct {
>>> ...
>>> union {
>>> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
>>> * Flexcomm module ID */
>>> struct {
>>> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
>>> uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
>>> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
>>> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
>>> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
>>> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
>>> uint32_t : 4;
>>> uint32_t ID : 20; /* [31..12] Flexcomm ID */
>>> } PSELID_b;
>>> };
>>
>> Bitfield layout in C isn't portable, so don't generate this kind
>> of union-of-a-integer-and-some-bitfields, please. You can
>> generate FIELD() macro invocations (see include/hw/registerfields.h)
>> which define shift/mask/length macros that can be used with
>> FIELD_EX*/FIELD_DP* to do extract/deposit operations.
>>
>
> I see that C bitfields are already used in a few places in qemu. Could
> you please elaborate on the portability issue?
Bitfields are fine, so long as you're only using them for storage compression and do not
care about the underlying layout.
The moment you put them into a union with an integer, clearly you are expecting the
bitfields to be in some particular order with respect to the integer, and that is the
portability issue.
In particular, big-endian hosts will generally flip the order, layout starting at the most
signifiacnt bit and work down. Other compilers will pad bits for alignment in ways that
you do not expect.
Just Don't Do It.
r~
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 05/23] hw: add register access utility functions
2024-08-12 21:14 ` Octavian Purdila
2024-08-12 22:35 ` Richard Henderson
@ 2024-08-13 8:28 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-13 8:28 UTC (permalink / raw)
To: Octavian Purdila, Peter Maydell
Cc: qemu-devel, qemu-arm, stefanst, pbonzini, alex.bennee, thuth,
marcandre.lureau, alistair, berrange, jsnow, crosa, bleal
On 12/8/24 23:14, Octavian Purdila wrote:
> On Mon, Aug 12, 2024 at 8:33 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> On Mon, 5 Aug 2024 at 21:17, Octavian Purdila <tavip@google.com> wrote:
>>>
>>> Add register access utility functions for device models, like checking
>>> aligned access and reading and writing to a register backstore.
>>
>>
>>> Signed-off-by: Octavian Purdila <tavip@google.com>
>>> ---
>>> include/hw/regs.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 89 insertions(+)
>>> create mode 100644 include/hw/regs.h
>>> +/*
>>> + * reg32_read
>>> + * @base: base address
>>> + * @addr: register offset in bytes
>>> + *
>>> + * Returns: 32bit value from register backstore
>>> + */
>>> +static inline uint32_t reg32_read(void *base, uint32_t addr)
>>> +{
>>> + return *(uint32_t *)(base + addr);
>>> +}
>>
>> Pointer type handling looks suspicious here -- if
>> the thing we're accessing is really a uint32_t* then
>> we should take that; if it isn't then casting it to
>> one and dereferencing might be reading unaligned memory.
>>
>
> It is used for performing generic accesses to generated structs (patch
> 3/23) which should be aligned in the way that are used in the patch
> set. If we decide to keep it, I'll add a note regarding alignment.
Could we use ldl_he_p() instead?
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-09 9:59 ` Daniel P. Berrangé
@ 2024-08-13 8:32 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 56+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-08-13 8:32 UTC (permalink / raw)
To: Daniel P. Berrangé, Paolo Bonzini
Cc: Octavian Purdila, John Snow, qemu-devel, qemu-arm, stefanst,
alex.bennee, thuth, peter.maydell, marcandre.lureau, alistair,
crosa, bleal
On 9/8/24 11:59, Daniel P. Berrangé wrote:
> On Fri, Aug 09, 2024 at 11:42:38AM +0200, Paolo Bonzini wrote:
>> On Fri, Aug 9, 2024 at 11:30 AM Philippe Mathieu-Daudé
>> <philmd@linaro.org> wrote:
>>>
>>> On 9/8/24 00:30, Octavian Purdila wrote:
>>>> On Thu, Aug 8, 2024 at 2:56 PM John Snow <jsnow@redhat.com> wrote:
>>>
>>>
>>>>>> diff --git a/configure b/configure
>>>>>> index 5ad1674ca5..811bfa5d54 100755
>>>>>> --- a/configure
>>>>>> +++ b/configure
>>>>>> @@ -956,7 +956,7 @@ mkvenv="$python ${source_path}/python/scripts/mkvenv.py"
>>>>>> # Finish preparing the virtual environment using vendored .whl files
>>>>>>
>>>>>> $mkvenv ensuregroup --dir "${source_path}/python/wheels" \
>>>>>> - ${source_path}/pythondeps.toml meson || exit 1
>>>>>> + ${source_path}/pythondeps.toml meson svd-gen-header || exit 1
>>>>>
>>>>
>>>> Hi John,
>>>>
>>>> Thanks for reviewing!
>>>>
>>>>>
>>>>> I haven't read the rest of this series; I'm chiming in solely from the build/python maintainer angle. Do we *always* need pysvd, no matter how QEMU was configured? Adding it to the meson line here is a very big hammer.
>>>>>
>>>>
>>>> I think the minimum we can do is to install it only if CONFIG_ARM is
>>>> enabled. That might change in the future if the models we create with
>>>> pysvd are enabled for other architectures.
>>>
>>> Similarly on how we manage libfdt, you can have meson defines
>>> SVDGEN as:
>>>
>>> config_host_data.set('CONFIG_SVDGEN', svd_gen_header.found())
>>>
>>> Then declare SVDGEN in Kconfig.host, and finally use in the Kconfigs:
>>
>> That would force people to install pysvd on the host, which is a pity
>> for such a small and little-known library. We have used submodules
>> in the past for much larger and common dependencies, for example
>> capstone.
>
> As mentioned elsewhere in thsi threads, IMHO we shoud not be running
> this generator during the build at all. It should be run once to
> create the headers needed for a particular device & the output committed
> to QEMU. There after any changes to the header (if any) need reviewing
> to ensure they don't imply an impact on guest ABI. This avoids having
> the 8.6 MB XML file in QEMU git too, as well as the pysvd dep on the
> host. Only the very rare times when we create a new device would need
> to have the pysvd code & XML.
OK, fine by me, as long as we don't end in a similar use of dtc where
we use distrib dtc to rebuild outdated dts and emit a bunch of pointless
warnings cluttering the build output and that nobody agrees how to fix.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files
2024-08-12 22:43 ` Richard Henderson
@ 2024-08-13 15:47 ` Octavian Purdila
0 siblings, 0 replies; 56+ messages in thread
From: Octavian Purdila @ 2024-08-13 15:47 UTC (permalink / raw)
To: Richard Henderson; +Cc: qemu-devel
On Mon, Aug 12, 2024 at 3:43 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 8/13/24 03:56, Octavian Purdila wrote:
> >>> typedef struct {
> >>> ...
> >>> union {
> >>> uint32_t PSELID; /* 0x00000FF8 Peripheral Select and
> >>> * Flexcomm module ID */
> >>> struct {
> >>> uint32_t PERSEL : 3; /* [2..0] Peripheral Select */
> >>> uint32_t LOCK : 1; /* [3..3] Lock the peripheral select */
> >>> uint32_t USARTPRESENT : 1; /* [4..4] USART present indicator */
> >>> uint32_t SPIPRESENT : 1; /* [5..5] SPI present indicator */
> >>> uint32_t I2CPRESENT : 1; /* [6..6] I2C present indicator */
> >>> uint32_t I2SPRESENT : 1; /* [7..7] I2S Present */
> >>> uint32_t : 4;
> >>> uint32_t ID : 20; /* [31..12] Flexcomm ID */
> >>> } PSELID_b;
> >>> };
> >>
> >> Bitfield layout in C isn't portable, so don't generate this kind
> >> of union-of-a-integer-and-some-bitfields, please. You can
> >> generate FIELD() macro invocations (see include/hw/registerfields.h)
> >> which define shift/mask/length macros that can be used with
> >> FIELD_EX*/FIELD_DP* to do extract/deposit operations.
> >>
> >
> > I see that C bitfields are already used in a few places in qemu. Could
> > you please elaborate on the portability issue?
>
> Bitfields are fine, so long as you're only using them for storage compression and do not
> care about the underlying layout.
>
> The moment you put them into a union with an integer, clearly you are expecting the
> bitfields to be in some particular order with respect to the integer, and that is the
> portability issue.
>
> In particular, big-endian hosts will generally flip the order, layout starting at the most
> signifiacnt bit and work down. Other compilers will pad bits for alignment in ways that
> you do not expect.
>
> Just Don't Do It.
>
Thanks Richard, I appreciate the detailed explanation! I will look
into the approach Peter suggested.
>
> r~
^ permalink raw reply [flat|nested] 56+ messages in thread
end of thread, other threads:[~2024-08-13 15:48 UTC | newest]
Thread overview: 56+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-05 20:16 [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 01/23] fifo32: add peek function Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 02/23] tests/unit: add fifo test Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 03/23] scripts: add script to generate C header files from SVD XML files Octavian Purdila
2024-08-08 21:56 ` John Snow
2024-08-08 22:30 ` Octavian Purdila
2024-08-08 23:06 ` John Snow
2024-08-09 9:30 ` Philippe Mathieu-Daudé
2024-08-09 9:42 ` Paolo Bonzini
2024-08-09 9:59 ` Daniel P. Berrangé
2024-08-13 8:32 ` Philippe Mathieu-Daudé
2024-08-09 6:34 ` Paolo Bonzini
2024-08-09 19:28 ` Octavian Purdila
2024-08-12 15:27 ` Peter Maydell
2024-08-12 17:56 ` Octavian Purdila
2024-08-12 22:43 ` Richard Henderson
2024-08-13 15:47 ` Octavian Purdila
2024-08-05 20:16 ` [RFC PATCH 04/23] hw/arm: add SVD file for NXP i.MX RT595 Octavian Purdila
2024-08-06 14:06 ` Alex Bennée
2024-08-06 20:31 ` Octavian Purdila
2024-08-07 11:24 ` Philippe Mathieu-Daudé
2024-08-07 16:36 ` Octavian Purdila
2024-08-09 9:13 ` Daniel P. Berrangé
2024-08-09 22:40 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 05/23] hw: add register access utility functions Octavian Purdila
2024-08-12 15:32 ` Peter Maydell
2024-08-12 21:14 ` Octavian Purdila
2024-08-12 22:35 ` Richard Henderson
2024-08-13 8:28 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 06/23] hw/misc: add basic flexcomm device model Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 07/23] tests/unit: add system bus mock Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 08/23] test/unit: add register access macros and functions Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 09/23] test/unit: add flexcomm unit test Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 10/23] hw/char: add support for flexcomm usart Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 11/23] test/unit: add flexcomm usart unit test Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 12/23] hw/i2c: add support for flexcomm i2c Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 13/23] test/unit: add i2c-tester Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 14/23] test/unit: add unit tests for flexcomm i2c Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 15/23] hw/ssi: add support for flexcomm spi Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 16/23] test/unit: add spi-tester Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 17/23] test/unit: add unit tests for flexcomm spi Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 18/23] hw/misc: add support for RT500's clock controller Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 19/23] test/unit: add unit tests " Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 20/23] hw/ssi: add support for flexspi Octavian Purdila
2024-08-08 5:11 ` Philippe Mathieu-Daudé
2024-08-08 21:31 ` Octavian Purdila
2024-08-09 8:54 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 21/23] hw/misc: add support for RT500 reset controller Octavian Purdila
2024-08-08 5:00 ` Philippe Mathieu-Daudé
2024-08-05 20:17 ` [RFC PATCH 22/23] hw/arm: add basic support for the RT500 SoC Octavian Purdila
2024-08-06 14:51 ` Philippe Mathieu-Daudé
2024-08-07 23:57 ` Octavian Purdila
2024-08-05 20:17 ` [RFC PATCH 23/23] hw/arm: add RT595-EVK board Octavian Purdila
2024-08-12 16:10 ` [RFC PATCH 00/23] NXP i.MX RT595, ARM SVD and device model unit tests Peter Maydell
2024-08-12 16:22 ` Daniel P. Berrangé
2024-08-12 18:39 ` Octavian Purdila
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).