* [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine
[not found] <20250510042043.2056265-1-jcksn@duck.com>
@ 2025-05-10 4:20 ` jcksn
2025-06-13 15:01 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This patch adds support for the MAX78000FTHR machine.
The MAX78000FTHR contains a MAX78000 and a RISC-V core. This patch
implements only the MAX78000, which is Cortex-M4 based.
Details can be found at:
https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 10 ++
hw/arm/max78000_soc.c | 184 ++++++++++++++++++++++++++++++++++
hw/arm/max78000fthr.c | 50 +++++++++
hw/arm/meson.build | 2 +
include/hw/arm/max78000_soc.h | 36 +++++++
5 files changed, 282 insertions(+)
create mode 100644 hw/arm/max78000_soc.c
create mode 100644 hw/arm/max78000fthr.c
create mode 100644 include/hw/arm/max78000_soc.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index a55b44d7bd..1c365d1115 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -95,6 +95,12 @@ config INTEGRATOR
select PL181 # display
select SMC91C111
+config MAX78000FTHR
+ bool
+ default y
+ depends on TCG && ARM
+ select MAX78000_SOC
+
config MPS3R
bool
default y
@@ -358,6 +364,10 @@ config ALLWINNER_R40
select USB_EHCI_SYSBUS
select SD
+config MAX78000_SOC
+ bool
+ select ARM_V7M
+
config RASPI
bool
default y
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
new file mode 100644
index 0000000000..64578438bd
--- /dev/null
+++ b/hw/arm/max78000_soc.c
@@ -0,0 +1,184 @@
+/*
+ * MAX78000 SOC
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Implementation based on stm32f205
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "system/address-spaces.h"
+#include "system/system.h"
+#include "hw/arm/max78000_soc.h"
+#include "hw/qdev-clock.h"
+#include "hw/misc/unimp.h"
+
+static void max78000_soc_initfn(Object *obj)
+{
+ MAX78000State *s = MAX78000_SOC(obj);
+
+ object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+
+ s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+ s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
+}
+
+static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
+{
+ MAX78000State *s = MAX78000_SOC(dev_soc);
+ MemoryRegion *system_memory = get_system_memory();
+ DeviceState *armv7m;
+ Error *err = NULL;
+
+ /*
+ * We use s->refclk internally and only define it with qdev_init_clock_in()
+ * so it is correctly parented and not leaked on an init/deinit; it is not
+ * intended as an externally exposed clock.
+ */
+ if (clock_has_source(s->refclk)) {
+ error_setg(errp, "refclk clock must not be wired up by the board code");
+ return;
+ }
+
+ if (!clock_has_source(s->sysclk)) {
+ error_setg(errp, "sysclk clock must be wired up by the board code");
+ return;
+ }
+
+ /*
+ * TODO: ideally we should model the SoC RCC and its ability to
+ * change the sysclk frequency and define different sysclk sources.
+ */
+
+ /* The refclk always runs at frequency HCLK / 8 */
+ clock_set_mul_div(s->refclk, 8, 1);
+ clock_set_source(s->refclk, s->sysclk);
+
+ memory_region_init_rom(&s->flash, OBJECT(dev_soc), "MAX78000.flash",
+ FLASH_SIZE, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
+
+ memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE,
+ &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
+
+ armv7m = DEVICE(&s->armv7m);
+ qdev_prop_set_uint32(armv7m, "num-irq", 134);
+ qdev_prop_set_uint8(armv7m, "num-prio-bits", 3);
+ qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
+ qdev_prop_set_bit(armv7m, "enable-bitband", true);
+ qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
+ qdev_connect_clock_in(armv7m, "refclk", s->refclk);
+ object_property_set_link(OBJECT(&s->armv7m), "memory",
+ OBJECT(system_memory), &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
+ return;
+ }
+
+ create_unimplemented_device("globalControl", 0x40000000, 0x400);
+ create_unimplemented_device("systemInterface", 0x40000400, 0x400);
+ create_unimplemented_device("functionControl", 0x40000800, 0x3400);
+ create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400);
+ create_unimplemented_device("dynamicVoltScale", 0x40003c00, 0x800);
+ create_unimplemented_device("SIMO", 0x40004400, 0x400);
+ create_unimplemented_device("trimSystemInit", 0x40005400, 0x400);
+ create_unimplemented_device("generalCtrlFunc", 0x40005800, 0xc00);
+ create_unimplemented_device("wakeupTimer", 0x40006400, 0x400);
+ create_unimplemented_device("powerSequencer", 0x40006800, 0x400);
+ create_unimplemented_device("miscControl", 0x40006c00, 0x800);
+
+ create_unimplemented_device("aes", 0x40007400, 0x400);
+ create_unimplemented_device("aesKey", 0x40007800, 0x800);
+
+ create_unimplemented_device("gpio0", 0x40008000, 0x1000);
+ create_unimplemented_device("gpio1", 0x40009000, 0x1000);
+
+ create_unimplemented_device("parallelCamInter", 0x4000e000, 0x1000);
+ create_unimplemented_device("CRC", 0x4000f000, 0x1000);
+
+ create_unimplemented_device("timer0", 0x40010000, 0x1000);
+ create_unimplemented_device("timer1", 0x40011000, 0x1000);
+ create_unimplemented_device("timer2", 0x40012000, 0x1000);
+ create_unimplemented_device("timer3", 0x40013000, 0x1000);
+
+ create_unimplemented_device("i2c0", 0x4001d000, 0x1000);
+ create_unimplemented_device("i2c1", 0x4001e000, 0x1000);
+ create_unimplemented_device("i2c2", 0x4001f000, 0x1000);
+
+ create_unimplemented_device("standardDMA", 0x40028000, 0x1000);
+ create_unimplemented_device("flashController0", 0x40029000, 0x400);
+
+ create_unimplemented_device("icc0", 0x4002a000, 0x800);
+ create_unimplemented_device("icc1", 0x4002a800, 0x800);
+
+ create_unimplemented_device("adc", 0x40034000, 0x1000);
+ create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
+ create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
+ create_unimplemented_device("semaphore", 0x4003e000, 0x1000);
+
+ create_unimplemented_device("uart0", 0x40042000, 0x1000);
+ create_unimplemented_device("uart1", 0x40043000, 0x1000);
+ create_unimplemented_device("uart2", 0x40044000, 0x1000);
+
+ create_unimplemented_device("spi1", 0x40046000, 0x2000);
+ create_unimplemented_device("trng", 0x4004d000, 0x1000);
+ create_unimplemented_device("i2s", 0x40060000, 0x1000);
+ create_unimplemented_device("lowPowerControl", 0x40080000, 0x400);
+ create_unimplemented_device("gpio2", 0x40080400, 0x200);
+ create_unimplemented_device("lowPowWatchdogTi", 0x40080800, 0x400);
+
+ create_unimplemented_device("lowPowerTimer5", 0x40081000, 0x400);
+ create_unimplemented_device("lowPowerUART0", 0x40081400, 0x400);
+ create_unimplemented_device("lowPowerCompar", 0x40088000, 0x400);
+
+ create_unimplemented_device("spi0", 0x400be000, 0x400);
+
+ /*
+ * The MAX78000 user guide's base address map lists the CNN TX FIFO as
+ * beginning at 0x400c0400 and ending at 0x400c0400. Given that CNN_FIFO
+ * is listed as having data accessible up to offset 0x1000, the user
+ * guide is likely incorrect.
+ */
+ create_unimplemented_device("cnnTxFIFO", 0x400c0400, 0x2000);
+
+ create_unimplemented_device("cnnGlobalControl", 0x50000000, 0x10000);
+ create_unimplemented_device("cnnx16quad0", 0x50100000, 0x40000);
+ create_unimplemented_device("cnnx16quad1", 0x50500000, 0x40000);
+ create_unimplemented_device("cnnx16quad2", 0x50900000, 0x40000);
+ create_unimplemented_device("cnnx16quad3", 0x50d00000, 0x40000);
+
+}
+
+static void max78000_soc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = max78000_soc_realize;
+}
+
+static const TypeInfo max78000_soc_info = {
+ .name = TYPE_MAX78000_SOC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MAX78000State),
+ .instance_init = max78000_soc_initfn,
+ .class_init = max78000_soc_class_init,
+};
+
+static void max78000_soc_types(void)
+{
+ type_register_static(&max78000_soc_info);
+}
+
+type_init(max78000_soc_types)
diff --git a/hw/arm/max78000fthr.c b/hw/arm/max78000fthr.c
new file mode 100644
index 0000000000..c4f6b5b1b0
--- /dev/null
+++ b/hw/arm/max78000fthr.c
@@ -0,0 +1,50 @@
+/*
+ * MAX78000FTHR Evaluation Board
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
+#include "qemu/error-report.h"
+#include "hw/arm/max78000_soc.h"
+#include "hw/arm/boot.h"
+
+/* 60MHz is the default, but other clocks can be selected. */
+#define SYSCLK_FRQ 60000000ULL
+static void max78000_init(MachineState *machine)
+{
+ DeviceState *dev;
+ Clock *sysclk;
+
+ sysclk = clock_new(OBJECT(machine), "SYSCLK");
+ clock_set_hz(sysclk, SYSCLK_FRQ);
+
+ dev = qdev_new(TYPE_MAX78000_SOC);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
+ qdev_connect_clock_in(dev, "sysclk", sysclk);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ armv7m_load_kernel(ARM_CPU(first_cpu),
+ machine->kernel_filename,
+ 0x00000000, FLASH_SIZE);
+}
+
+static void max78000_machine_init(MachineClass *mc)
+{
+ static const char * const valid_cpu_types[] = {
+ ARM_CPU_TYPE_NAME("cortex-m4"),
+ NULL
+ };
+
+ mc->desc = "MAX78000FTHR Board (Cortex-M4 / (Unimplemented) RISC-V)";
+ mc->init = max78000_init;
+ mc->valid_cpu_types = valid_cpu_types;
+}
+
+DEFINE_MACHINE("max78000fthr", max78000_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 5098795f61..31f40c5bd4 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -27,6 +27,7 @@ arm_common_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c'))
arm_common_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c'))
arm_common_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
arm_common_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
+arm_common_ss.add(when: 'CONFIG_MAX78000_SOC', if_true: files('max78000_soc.c'))
arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
arm_common_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c'))
arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
@@ -71,6 +72,7 @@ arm_ss.add(when: 'CONFIG_XEN', if_true: files(
arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
arm_common_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c'))
arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c'))
+arm_common_ss.add(when: 'CONFIG_MAX78000FTHR', if_true: files('max78000fthr.c'))
arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c'))
arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
new file mode 100644
index 0000000000..0e6def295d
--- /dev/null
+++ b/include/hw/arm/max78000_soc.h
@@ -0,0 +1,36 @@
+/*
+ * MAX78000 SOC
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_ARM_MAX78000_SOC_H
+#define HW_ARM_MAX78000_SOC_H
+
+#include "hw/or-irq.h"
+#include "hw/arm/armv7m.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_SOC "max78000-soc"
+OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
+
+#define FLASH_BASE_ADDRESS 0x10000000
+#define FLASH_SIZE (512 * 1024)
+#define SRAM_BASE_ADDRESS 0x20000000
+#define SRAM_SIZE (128 * 1024)
+
+struct MAX78000State {
+ SysBusDevice parent_obj;
+
+ ARMv7MState armv7m;
+
+ MemoryRegion sram;
+ MemoryRegion flash;
+
+ Clock *sysclk;
+ Clock *refclk;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/6] MAX78000: ICC Implementation
[not found] <20250510042043.2056265-1-jcksn@duck.com>
2025-05-10 4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
@ 2025-05-10 4:20 ` jcksn
2025-06-13 15:26 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This patch implements the Instruction Cache Controller for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/arm/max78000_soc.c | 20 ++++++--
hw/misc/Kconfig | 3 ++
hw/misc/max78000_icc.c | 89 ++++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/arm/max78000_soc.h | 6 +++
include/hw/misc/max78000_icc.h | 34 +++++++++++++
7 files changed, 150 insertions(+), 4 deletions(-)
create mode 100644 hw/misc/max78000_icc.c
create mode 100644 include/hw/misc/max78000_icc.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 1c365d1115..3f23af3244 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -367,6 +367,7 @@ config ALLWINNER_R40
config MAX78000_SOC
bool
select ARM_V7M
+ select MAX78000_ICC
config RASPI
bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 64578438bd..4d598bddd4 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -16,12 +16,19 @@
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
+static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
+
static void max78000_soc_initfn(Object *obj)
{
MAX78000State *s = MAX78000_SOC(obj);
+ int i;
object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+ for (i = 0; i < MAX78000_NUM_ICC; i++) {
+ object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
+ }
+
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
@@ -30,8 +37,9 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
{
MAX78000State *s = MAX78000_SOC(dev_soc);
MemoryRegion *system_memory = get_system_memory();
- DeviceState *armv7m;
+ DeviceState *dev, *armv7m;
Error *err = NULL;
+ int i;
/*
* We use s->refclk internally and only define it with qdev_init_clock_in()
@@ -87,6 +95,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
+ for (i = 0; i < MAX78000_NUM_ICC; i++) {
+ dev = DEVICE(&(s->icc[i]));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
+ }
+
+
create_unimplemented_device("globalControl", 0x40000000, 0x400);
create_unimplemented_device("systemInterface", 0x40000400, 0x400);
create_unimplemented_device("functionControl", 0x40000800, 0x3400);
@@ -120,9 +135,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("standardDMA", 0x40028000, 0x1000);
create_unimplemented_device("flashController0", 0x40029000, 0x400);
- create_unimplemented_device("icc0", 0x4002a000, 0x800);
- create_unimplemented_device("icc1", 0x4002a800, 0x800);
-
create_unimplemented_device("adc", 0x40034000, 0x1000);
create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index ec0fa5aa9f..781bcf74cc 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
config ARM11SCU
bool
+config MAX78000_ICC
+ bool
+
config MOS6522
bool
diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c
new file mode 100644
index 0000000000..3eacf6bd1b
--- /dev/null
+++ b/hw/misc/max78000_icc.c
@@ -0,0 +1,89 @@
+/*
+ * MAX78000 Instruction Cache
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_icc.h"
+
+
+static uint64_t max78000_icc_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000IccState *s = opaque;
+ switch (addr) {
+ case ICC_INFO:{
+ return s->info;
+ }
+ case ICC_SZ:{
+ return s->sz;
+ }
+ case ICC_CTRL:{
+ return s->ctrl;
+ }
+ case ICC_INVALIDATE:{
+ return s->invalidate;
+ }
+ default:{
+ return 0;
+ }
+ }
+}
+
+static void max78000_icc_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000IccState *s = opaque;
+
+ switch (addr) {
+ case ICC_CTRL:{
+ s->ctrl = 0x10000 + (val64 & 1);
+ break;
+ }
+ case ICC_INVALIDATE:{
+ s->ctrl = s->ctrl | 0x80;
+ }
+ }
+}
+
+static const MemoryRegionOps stm32f4xx_exti_ops = {
+ .read = max78000_icc_read,
+ .write = max78000_icc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_icc_init(Object *obj)
+{
+ Max78000IccState *s = MAX78000_ICC(obj);
+ s->info = 0;
+ s->sz = 0x10000010;
+ s->ctrl = 0x10000;
+ s->invalidate = 0;
+
+
+ memory_region_init_io(&s->mmio, obj, &stm32f4xx_exti_ops, s,
+ TYPE_MAX78000_ICC, 0x800);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static const TypeInfo max78000_icc_info = {
+ .name = TYPE_MAX78000_ICC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000IccState),
+ .instance_init = max78000_icc_init,
+};
+
+static void max78000_icc_register_types(void)
+{
+ type_register_static(&max78000_icc_info);
+}
+
+type_init(max78000_icc_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 6d47de482c..a21a994ff8 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
'imx_ccm.c',
'imx_rngc.c',
))
+system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm_clk.c',
'npcm_gcr.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 0e6def295d..207a4da68a 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
#include "hw/or-irq.h"
#include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_icc.h"
#include "qom/object.h"
#define TYPE_MAX78000_SOC "max78000-soc"
@@ -21,6 +22,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
#define SRAM_BASE_ADDRESS 0x20000000
#define SRAM_SIZE (128 * 1024)
+/* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */
+#define MAX78000_NUM_ICC 2
+
struct MAX78000State {
SysBusDevice parent_obj;
@@ -29,6 +33,8 @@ struct MAX78000State {
MemoryRegion sram;
MemoryRegion flash;
+ Max78000IccState icc[MAX78000_NUM_ICC];
+
Clock *sysclk;
Clock *refclk;
};
diff --git a/include/hw/misc/max78000_icc.h b/include/hw/misc/max78000_icc.h
new file mode 100644
index 0000000000..fe9228e25d
--- /dev/null
+++ b/include/hw/misc/max78000_icc.h
@@ -0,0 +1,34 @@
+/*
+ * MAX78000 Instruction Cache
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MAX78000_ICC_H
+#define HW_MAX78000_ICC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_ICC "max78000-icc"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000IccState, MAX78000_ICC)
+
+#define ICC_INFO 0x0
+#define ICC_SZ 0x4
+#define ICC_CTRL 0x100
+#define ICC_INVALIDATE 0x700
+
+struct Max78000IccState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t info;
+ uint32_t sz;
+ uint32_t ctrl;
+ uint32_t invalidate;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/6] MAX78000 UART implementation
[not found] <20250510042043.2056265-1-jcksn@duck.com>
2025-05-10 4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
2025-05-10 4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
@ 2025-05-10 4:20 ` jcksn
2025-06-13 16:03 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This patch implements UART support for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/arm/max78000_soc.c | 27 +++-
hw/char/Kconfig | 3 +
hw/char/max78000_uart.c | 263 ++++++++++++++++++++++++++++++++
hw/char/meson.build | 1 +
include/hw/arm/max78000_soc.h | 3 +
include/hw/char/max78000_uart.h | 77 ++++++++++
7 files changed, 370 insertions(+), 5 deletions(-)
create mode 100644 hw/char/max78000_uart.c
create mode 100644 include/hw/char/max78000_uart.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 3f23af3244..59450dc3cb 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -368,6 +368,7 @@ config MAX78000_SOC
bool
select ARM_V7M
select MAX78000_ICC
+ select MAX78000_UART
config RASPI
bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 4d598bddd4..6334d8b49b 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -16,7 +16,11 @@
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
-static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
+static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
+static const uint32_t max78000_uart_addr[] = {0x40042000, 0x40043000,
+ 0x40044000};
+
+static const int max78000_uart_irq[] = {30, 31, 50};
static void max78000_soc_initfn(Object *obj)
{
@@ -29,6 +33,10 @@ static void max78000_soc_initfn(Object *obj)
object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
}
+ for (i = 0; i < MAX78000_NUM_UART; i++) {
+ object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);
+ }
+
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
@@ -38,6 +46,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
MAX78000State *s = MAX78000_SOC(dev_soc);
MemoryRegion *system_memory = get_system_memory();
DeviceState *dev, *armv7m;
+ SysBusDevice *busdev;
Error *err = NULL;
int i;
@@ -101,6 +110,18 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
}
+ for (i = 0; i < MAX78000_NUM_UART; i++) {
+ dev = DEVICE(&(s->uart[i]));
+ qdev_prop_set_chr(dev, "chardev", serial_hd(i));
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
+ sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
+ max78000_uart_irq[i]));
+ }
+
create_unimplemented_device("globalControl", 0x40000000, 0x400);
create_unimplemented_device("systemInterface", 0x40000400, 0x400);
@@ -140,10 +161,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
create_unimplemented_device("semaphore", 0x4003e000, 0x1000);
- create_unimplemented_device("uart0", 0x40042000, 0x1000);
- create_unimplemented_device("uart1", 0x40043000, 0x1000);
- create_unimplemented_device("uart2", 0x40044000, 0x1000);
-
create_unimplemented_device("spi1", 0x40046000, 0x2000);
create_unimplemented_device("trng", 0x4004d000, 0x1000);
create_unimplemented_device("i2s", 0x40060000, 0x1000);
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 9d517f3e28..020c0a84bb 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -48,6 +48,9 @@ config VIRTIO_SERIAL
default y
depends on VIRTIO
+config MAX78000_UART
+ bool
+
config STM32F2XX_USART
bool
diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c
new file mode 100644
index 0000000000..edd39c5a8b
--- /dev/null
+++ b/hw/char/max78000_uart.c
@@ -0,0 +1,263 @@
+/*
+ * MAX78000 UART
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/max78000_uart.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+static int max78000_uart_can_receive(void *opaque)
+{
+ Max78000UartState *s = opaque;
+ if (!(s->ctrl & UART_BCLKEN)) {
+ return 0;
+ }
+ return fifo8_num_free(&s->rx_fifo);
+}
+
+static void max78000_update_irq(Max78000UartState *s)
+{
+ int interrupt_level = 0;
+ uint32_t rx_threshold = s->ctrl & 0xf;
+
+ /*
+ * Because tx is synchronous and we should have no frame errors, the only
+ * possible interrupt is receive fifo threshold
+ */
+ if ((s->int_en & UART_RX_THD) && fifo8_num_used(&s->rx_fifo) >= rx_threshold) {
+ interrupt_level = 1;
+ s->int_fl = s->int_fl & UART_RX_THD;
+ } else{
+ s->int_fl = s->int_fl & ~UART_RX_THD;
+ }
+ qemu_set_irq(s->irq, interrupt_level);
+}
+
+static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ Max78000UartState *s = opaque;
+
+ if (size <= fifo8_num_free(&s->rx_fifo)) {
+ fifo8_push_all(&s->rx_fifo, buf, size);
+ } else{
+ fifo8_push_all(&s->rx_fifo, buf, fifo8_num_free(&s->rx_fifo));
+ printf("rx_fifo overrun!\n");
+ }
+
+ max78000_update_irq(s);
+}
+
+static void max78000_uart_reset_hold(Object *obj, ResetType type)
+{
+ Max78000UartState *s = MAX78000_UART(obj);
+
+ s->ctrl = 0;
+ s->status = UART_TX_EM | UART_RX_EM;
+ s->int_en = 0;
+ s->int_fl = 0;
+ s->osr = 0;
+ s->txpeek = 0;
+ s->pnr = UART_RTS;
+ s->fifo = 0;
+ s->dma = 0;
+ s->wken = 0;
+ s->wkfl = 0;
+ fifo8_reset(&s->rx_fifo);
+}
+
+static uint64_t max78000_uart_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000UartState *s = opaque;
+ uint64_t retvalue = 0;
+
+ switch (addr) {
+ case UART_CTRL:
+ retvalue = s->ctrl;
+ break;
+ case UART_STATUS:
+ retvalue = (fifo8_num_used(&s->rx_fifo) > 7 ?
+ 8 : (fifo8_num_used(&s->rx_fifo)) << UART_RX_LVL) |
+ UART_TX_EM |
+ (fifo8_is_empty(&s->rx_fifo) ? UART_RX_EM : 0);
+ break;
+ case UART_INT_EN:
+ retvalue = s->int_en;
+ break;
+ case UART_INT_FL:
+ retvalue = s->int_fl;
+ break;
+ case UART_CLKDIV:
+ retvalue = s->clkdiv;
+ break;
+ case UART_OSR:
+ retvalue = s->osr;
+ break;
+ case UART_TXPEEK:
+ if (!fifo8_is_empty(&s->rx_fifo)) {
+ retvalue = fifo8_peek(&s->rx_fifo);
+ }
+ break;
+ case UART_PNR:
+ retvalue = s->pnr;
+ break;
+ case UART_FIFO:
+ if (!fifo8_is_empty(&s->rx_fifo)) {
+ retvalue = fifo8_pop(&s->rx_fifo);
+ max78000_update_irq(s);
+ }
+ break;
+ case UART_DMA:
+ /* DMA not implemented */
+ retvalue = s->dma;
+ break;
+ case UART_WKEN:
+ retvalue = s->wken;
+ break;
+ case UART_WKFL:
+ retvalue = s->wkfl;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+ break;
+ }
+
+ return retvalue;
+}
+
+static void max78000_uart_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000UartState *s = opaque;
+
+ uint32_t value = val64;
+ uint8_t data;
+
+ switch (addr) {
+ case UART_CTRL:
+ if (value & UART_FLUSH_RX) {
+ fifo8_reset(&s->rx_fifo);
+ }
+ if (value & UART_BCLKEN) {
+ value = value | UART_BCLKRDY;
+ }
+ s->ctrl = value & ~(UART_FLUSH_RX | UART_FLUSH_TX);
+
+ /*
+ * Software can manage UART flow control manually by setting hfc_en
+ * in UART_CTRL. This would require emulating uart at a lower level,
+ * and is currently unimplemented.
+ */
+
+ return;
+ case UART_STATUS:
+ /* UART_STATUS is read only */
+ return;
+ case UART_INT_EN:
+ s->int_en = value;
+ return;
+ case UART_INT_FL:
+ s->int_fl = value;
+ return;
+ case UART_CLKDIV:
+ s->clkdiv = value;
+ return;
+ case UART_OSR:
+ s->osr = value;
+ return;
+ case UART_TXPEEK:
+ /* UART_TXPEEK is read only */
+ return;
+ case UART_PNR:
+ s->pnr = value;
+ return;
+ case UART_FIFO:
+ data = value & 0xff;
+ /*
+ * XXX this blocks entire thread. Rewrite to use
+ * qemu_chr_fe_write and background I/O callbacks
+ */
+ qemu_chr_fe_write_all(&s->chr, &data, 1);
+ return;
+ case UART_DMA:
+ /* DMA not implemented */
+ s->dma = value;
+ return;
+ case UART_WKEN:
+ s->wken = value;
+ return;
+ case UART_WKFL:
+ s->wkfl = value;
+ return;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_MAX78000_UART, __func__, addr);
+ }
+}
+
+static const MemoryRegionOps max78000_uart_ops = {
+ .read = max78000_uart_read,
+ .write = max78000_uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const Property max78000_uart_properties[] = {
+ DEFINE_PROP_CHR("chardev", Max78000UartState, chr),
+};
+
+static void max78000_uart_init(Object *obj)
+{
+ Max78000UartState *s = MAX78000_UART(obj);
+ fifo8_create(&s->rx_fifo, 8);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_uart_ops, s,
+ TYPE_MAX78000_UART, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void max78000_uart_realize(DeviceState *dev, Error **errp)
+{
+ Max78000UartState *s = MAX78000_UART(dev);
+
+ qemu_chr_fe_set_handlers(&s->chr, max78000_uart_can_receive,
+ max78000_uart_receive, NULL, NULL,
+ s, NULL, true);
+}
+
+static void max78000_uart_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = max78000_uart_reset_hold;
+
+ device_class_set_props(dc, max78000_uart_properties);
+ dc->realize = max78000_uart_realize;
+}
+
+static const TypeInfo max78000_uart_info = {
+ .name = TYPE_MAX78000_UART,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000UartState),
+ .instance_init = max78000_uart_init,
+ .class_init = max78000_uart_class_init,
+};
+
+static void max78000_uart_register_types(void)
+{
+ type_register_static(&max78000_uart_info);
+}
+
+type_init(max78000_uart_register_types)
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 4e439da8b9..a9e1dc26c0 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -26,6 +26,7 @@ system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c'))
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c'))
+system_ss.add(when: 'CONFIG_MAX78000_UART', if_true: files('max78000_uart.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c'))
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c'))
system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 207a4da68a..1ee280b894 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -12,6 +12,7 @@
#include "hw/or-irq.h"
#include "hw/arm/armv7m.h"
#include "hw/misc/max78000_icc.h"
+#include "hw/char/max78000_uart.h"
#include "qom/object.h"
#define TYPE_MAX78000_SOC "max78000-soc"
@@ -24,6 +25,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
/* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */
#define MAX78000_NUM_ICC 2
+#define MAX78000_NUM_UART 3
struct MAX78000State {
SysBusDevice parent_obj;
@@ -34,6 +36,7 @@ struct MAX78000State {
MemoryRegion flash;
Max78000IccState icc[MAX78000_NUM_ICC];
+ Max78000UartState uart[MAX78000_NUM_UART];
Clock *sysclk;
Clock *refclk;
diff --git a/include/hw/char/max78000_uart.h b/include/hw/char/max78000_uart.h
new file mode 100644
index 0000000000..0d39a2dd59
--- /dev/null
+++ b/include/hw/char/max78000_uart.h
@@ -0,0 +1,77 @@
+/*
+ * MAX78000 UART
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MAX78000_UART_H
+#define HW_MAX78000_UART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "qemu/fifo8.h"
+#include "qom/object.h"
+
+#define UART_CTRL 0x0
+#define UART_STATUS 0x4
+#define UART_INT_EN 0x8
+#define UART_INT_FL 0xc
+#define UART_CLKDIV 0x10
+#define UART_OSR 0x14
+#define UART_TXPEEK 0x18
+#define UART_PNR 0x1c
+#define UART_FIFO 0x20
+#define UART_DMA 0x30
+#define UART_WKEN 0x34
+#define UART_WKFL 0x38
+
+/* CTRL */
+#define UART_CTF_DIS (1 << 7)
+#define UART_FLUSH_TX (1 << 8)
+#define UART_FLUSH_RX (1 << 9)
+#define UART_BCLKEN (1 << 15)
+#define UART_BCLKRDY (1 << 19)
+
+/* STATUS */
+#define UART_RX_LVL 8
+#define UART_TX_EM (1 << 6)
+#define UART_RX_FULL (1 << 5)
+#define UART_RX_EM (1 << 4)
+
+/* PNR (Pin Control Register) */
+#define UART_CTS 1
+#define UART_RTS (1 << 1)
+
+/* INT_EN / INT_FL */
+#define UART_RX_THD (1 << 4)
+
+#define UART_RXBUFLEN 0x100
+#define TYPE_MAX78000_UART "max78000-uart"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000UartState, MAX78000_UART)
+
+struct Max78000UartState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t int_en;
+ uint32_t int_fl;
+ uint32_t clkdiv;
+ uint32_t osr;
+ uint32_t txpeek;
+ uint32_t pnr;
+ uint32_t fifo;
+ uint32_t dma;
+ uint32_t wken;
+ uint32_t wkfl;
+
+ Fifo8 rx_fifo;
+
+ CharBackend chr;
+ qemu_irq irq;
+};
+#endif /* HW_STM32F2XX_USART_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/6] MAX78000 GCR implementation
[not found] <20250510042043.2056265-1-jcksn@duck.com>
` (2 preceding siblings ...)
2025-05-10 4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
@ 2025-05-10 4:20 ` jcksn
2025-06-16 5:13 ` Philippe Mathieu-Daudé
2025-06-16 9:47 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
2025-05-10 4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn
5 siblings, 2 replies; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This patch implements the Global Control Register for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/arm/max78000_soc.c | 9 +-
hw/misc/Kconfig | 3 +
hw/misc/max78000_gcr.c | 285 +++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/arm/max78000_soc.h | 2 +
include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
7 files changed, 422 insertions(+), 1 deletion(-)
create mode 100644 hw/misc/max78000_gcr.c
create mode 100644 include/hw/misc/max78000_gcr.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 59450dc3cb..211b201629 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -369,6 +369,7 @@ config MAX78000_SOC
select ARM_V7M
select MAX78000_ICC
select MAX78000_UART
+ select MAX78000_GCR
config RASPI
bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 6334d8b49b..7a012c6ef7 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+ object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
+
for (i = 0; i < MAX78000_NUM_ICC; i++) {
object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
}
@@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
+ dev = DEVICE(&s->gcr);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
+
for (i = 0; i < MAX78000_NUM_ICC; i++) {
dev = DEVICE(&(s->icc[i]));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
@@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
return;
}
+ dev->id = g_strdup_printf("uart%d", i);
+
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
@@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
}
- create_unimplemented_device("globalControl", 0x40000000, 0x400);
create_unimplemented_device("systemInterface", 0x40000400, 0x400);
create_unimplemented_device("functionControl", 0x40000800, 0x3400);
create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 781bcf74cc..fde2266b8f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
config ARM11SCU
bool
+config MAX78000_GCR
+ bool
+
config MAX78000_ICC
bool
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
new file mode 100644
index 0000000000..657b7fc490
--- /dev/null
+++ b/hw/misc/max78000_gcr.c
@@ -0,0 +1,285 @@
+/*
+ * MAX78000 Global Control Registers
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "system/runstate.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_gcr.h"
+
+static void max78000_gcr_reset(DeviceState *dev)
+{
+ Max78000GcrState *s = MAX78000_GCR(dev);
+ s->sysctrl = 0x21002;
+ s->rst0 = 0;
+ /* All clocks are always ready */
+ s->clkctrl = 0x3e140008;
+ s->pm = 0x3f000;
+ s->pclkdiv = 0;
+ s->pclkdis0 = 0xffffffff;
+ s->memctrl = 0x5;
+ s->memz = 0;
+ s->sysst = 0;
+ s->rst1 = 0;
+ s->pckdis1 = 0xffffffff;
+ s->eventen = 0;
+ s->revision = 0xa1;
+ s->sysie = 0;
+ s->eccerr = 0;
+ s->ecced = 0;
+ s->eccie = 0;
+ s->eccaddr = 0;
+}
+
+static uint64_t max78000_gcr_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000GcrState *s = opaque;
+
+ switch (addr) {
+ case SYSCTRL:{
+ return s->sysctrl;
+ }
+ case RST0:{
+ return s->rst0;
+ }
+ case CLKCTRL:{
+ return s->clkctrl;
+ }
+ case PM:{
+ return s->pm;
+ }
+ case PCLKDIV:{
+ return s->pclkdiv;
+ }
+ case PCLKDIS0:{
+ return s->pclkdis0;
+ }
+ case MEMCTRL:{
+ return s->memctrl;
+ }
+ case MEMZ:{
+ return s->memz;
+ }
+ case SYSST:{
+ return s->sysst;
+ }
+ case RST1:{
+ return s->rst1;
+ }
+ case PCKDIS1:{
+ return s->pckdis1;
+ }
+ case EVENTEN:{
+ return s->eventen;
+ }
+ case REVISION:{
+ return s->revision;
+ }
+ case SYSIE:{
+ return s->sysie;
+ }
+ case ECCERR:{
+ return s->eccerr;
+ }
+ case ECCED:{
+ return s->ecced;
+ }
+ case ECCIE:{
+ return s->eccie;
+ }
+ case ECCADDR:{
+ return s->eccaddr;
+ }
+ default:{
+ return 0;
+ }
+ }
+}
+
+static void max78000_gcr_reset_device(const char *device_name)
+{
+ DeviceState *dev = qdev_find_recursive(sysbus_get_default(), device_name);
+ if (dev) {
+ device_cold_reset(dev);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "no device %s for reset\n", device_name);
+ }
+}
+
+static void max78000_gcr_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000GcrState *s = opaque;
+ uint32_t val = val64;
+ uint8_t zero[0xc000] = {0};
+ switch (addr) {
+ case SYSCTRL:{
+ /* Checksum calculations always pass immediately */
+ s->sysctrl = (val & 0x30000) | 0x1002;
+ break;
+ }
+ case RST0:{
+ if (val & SYSTEM_RESET) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+ if (val & PERIPHERAL_RESET) {
+ /*
+ * Peripheral reset resets all peripherals. The CPU
+ * retains its state. The GPIO, watchdog timers, AoD,
+ * RAM retention, and general control registers (GCR),
+ * including the clock configuration, are unaffected.
+ */
+ val = UART2_RESET | UART1_RESET | UART0_RESET |
+ ADC_RESET | CNN_RESET | TRNG_RESET |
+ RTC_RESET | I2C0_RESET | SPI1_RESET |
+ TMR3_RESET | TMR2_RESET | TMR1_RESET |
+ TMR0_RESET | WDT0_RESET | DMA_RESET;
+ }
+ if (val & SOFT_RESET) {
+ /* Soft reset also resets GPIO */
+ val = UART2_RESET | UART1_RESET | UART0_RESET |
+ ADC_RESET | CNN_RESET | TRNG_RESET |
+ RTC_RESET | I2C0_RESET | SPI1_RESET |
+ TMR3_RESET | TMR2_RESET | TMR1_RESET |
+ TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
+ DMA_RESET;
+ }
+ if (val & UART2_RESET) {
+ max78000_gcr_reset_device("uart2");
+ }
+ if (val & UART1_RESET) {
+ max78000_gcr_reset_device("uart1");
+ }
+ if (val & UART0_RESET) {
+ max78000_gcr_reset_device("uart0");
+ }
+ /* TODO: As other devices are implemented, add them here */
+ break;
+ }
+ case CLKCTRL:{
+ s->clkctrl = val | SYSCLK_RDY;
+ break;
+ }
+ case PM:{
+ s->pm = val;
+ break;
+ }
+ case PCLKDIV:{
+ s->pclkdiv = val;
+ break;
+ }
+ case PCLKDIS0:{
+ s->pclkdis0 = val;
+ break;
+ }
+ case MEMCTRL:{
+ s->memctrl = val;
+ break;
+ }
+ case MEMZ:{
+ if (val & ram0) {
+ cpu_physical_memory_write(SYSRAM0_START, zero, 0x8000);
+ }
+ if (val & ram1) {
+ cpu_physical_memory_write(SYSRAM1_START, zero, 0x8000);
+ }
+ if (val & ram2) {
+ cpu_physical_memory_write(SYSRAM2_START, zero, 0xC000);
+ }
+ if (val & ram3) {
+ cpu_physical_memory_write(SYSRAM3_START, zero, 0x4000);
+ }
+ break;
+ }
+ case SYSST:{
+ s->sysst = val;
+ break;
+ }
+ case RST1:{
+ /* TODO: As other devices are implemented, add them here */
+ s->rst1 = val;
+ break;
+ }
+ case PCKDIS1:{
+ s->pckdis1 = val;
+ break;
+ }
+ case EVENTEN:{
+ s->eventen = val;
+ break;
+ }
+ case REVISION:{
+ s->revision = val;
+ break;
+ }
+ case SYSIE:{
+ s->sysie = val;
+ break;
+ }
+ case ECCERR:{
+ s->eccerr = val;
+ break;
+ }
+ case ECCED:{
+ s->ecced = val;
+ break;
+ }
+ case ECCIE:{
+ s->eccie = val;
+ break;
+ }
+ case ECCADDR:{
+ s->eccaddr = val;
+ break;
+ }
+ default:{
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps max78000_gcr_ops = {
+ .read = max78000_gcr_read,
+ .write = max78000_gcr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_gcr_init(Object *obj)
+{
+ Max78000GcrState *s = MAX78000_GCR(obj);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_gcr_ops, s,
+ TYPE_MAX78000_GCR, 0x400);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_gcr_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_legacy_reset(dc, max78000_gcr_reset);
+}
+
+static const TypeInfo max78000_gcr_info = {
+ .name = TYPE_MAX78000_GCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000GcrState),
+ .instance_init = max78000_gcr_init,
+ .class_init = max78000_gcr_class_init,
+};
+
+static void max78000_gcr_register_types(void)
+{
+ type_register_static(&max78000_gcr_info);
+}
+
+type_init(max78000_gcr_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index a21a994ff8..283d06dad4 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
'imx_ccm.c',
'imx_rngc.c',
))
+system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm_clk.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 1ee280b894..8b9e3052cd 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
#include "hw/or-irq.h"
#include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_gcr.h"
#include "hw/misc/max78000_icc.h"
#include "hw/char/max78000_uart.h"
#include "qom/object.h"
@@ -35,6 +36,7 @@ struct MAX78000State {
MemoryRegion sram;
MemoryRegion flash;
+ Max78000GcrState gcr;
Max78000IccState icc[MAX78000_NUM_ICC];
Max78000UartState uart[MAX78000_NUM_UART];
diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h
new file mode 100644
index 0000000000..128f9b8a99
--- /dev/null
+++ b/include/hw/misc/max78000_gcr.h
@@ -0,0 +1,122 @@
+/*
+ * MAX78000 Global Control Register
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_GCR_H
+#define HW_MAX78000_GCR_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_GCR "max78000-gcr"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR)
+
+#define SYSCTRL 0x0
+#define RST0 0x4
+#define CLKCTRL 0x8
+#define PM 0xc
+#define PCLKDIV 0x18
+#define PCLKDIS0 0x24
+#define MEMCTRL 0x28
+#define MEMZ 0x2c
+#define SYSST 0x40
+#define RST1 0x44
+#define PCKDIS1 0x48
+#define EVENTEN 0x4c
+#define REVISION 0x50
+#define SYSIE 0x54
+#define ECCERR 0x64
+#define ECCED 0x68
+#define ECCIE 0x6c
+#define ECCADDR 0x70
+
+/* RST0 */
+#define SYSTEM_RESET (1 << 31)
+#define PERIPHERAL_RESET (1 << 30)
+#define SOFT_RESET (1 << 29)
+#define UART2_RESET (1 << 28)
+
+#define ADC_RESET (1 << 26)
+#define CNN_RESET (1 << 25)
+#define TRNG_RESET (1 << 24)
+
+#define RTC_RESET (1 << 17)
+#define I2C0_RESET (1 << 16)
+
+#define SPI1_RESET (1 << 13)
+#define UART1_RESET (1 << 12)
+#define UART0_RESET (1 << 11)
+
+#define TMR3_RESET (1 << 8)
+#define TMR2_RESET (1 << 7)
+#define TMR1_RESET (1 << 6)
+#define TMR0_RESET (1 << 5)
+
+#define GPIO1_RESET (1 << 3)
+#define GPIO0_RESET (1 << 2)
+#define WDT0_RESET (1 << 1)
+#define DMA_RESET (1 << 0)
+
+/* CLKCTRL */
+#define SYSCLK_RDY (1 << 13)
+
+/* MEMZ */
+#define ram0 (1 << 0)
+#define ram1 (1 << 1)
+#define ram2 (1 << 2)
+#define ram3 (1 << 3)
+
+/* RST1 */
+#define CPU1_RESET (1 << 31)
+
+#define SIMO_RESET (1 << 25)
+#define DVS_RESET (1 << 24)
+
+#define I2C2_RESET (1 << 20)
+#define I2S_RESET (1 << 19)
+
+#define SMPHR_RESET (1 << 16)
+
+#define SPI0_RESET (1 << 11)
+#define AES_RESET (1 << 10)
+#define CRC_RESET (1 << 9)
+
+#define PT_RESET (1 << 1)
+#define I2C1_RESET (1 << 0)
+
+
+#define SYSRAM0_START 0x20000000
+#define SYSRAM1_START 0x20008000
+#define SYSRAM2_START 0x20010000
+#define SYSRAM3_START 0x2001C000
+
+struct Max78000GcrState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t sysctrl;
+ uint32_t rst0;
+ uint32_t clkctrl;
+ uint32_t pm;
+ uint32_t pclkdiv;
+ uint32_t pclkdis0;
+ uint32_t memctrl;
+ uint32_t memz;
+ uint32_t sysst;
+ uint32_t rst1;
+ uint32_t pckdis1;
+ uint32_t eventen;
+ uint32_t revision;
+ uint32_t sysie;
+ uint32_t eccerr;
+ uint32_t ecced;
+ uint32_t eccie;
+ uint32_t eccaddr;
+
+};
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 5/6] MAX78000 TRNG Implementation
[not found] <20250510042043.2056265-1-jcksn@duck.com>
` (3 preceding siblings ...)
2025-05-10 4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
@ 2025-05-10 4:20 ` jcksn
2025-06-16 9:56 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn
5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This patch implements the True Random Number Generator for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/arm/max78000_soc.c | 9 ++-
hw/misc/Kconfig | 3 +
hw/misc/max78000_gcr.c | 3 +
hw/misc/max78000_trng.c | 115 ++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/arm/max78000_soc.h | 2 +
include/hw/misc/max78000_trng.h | 35 ++++++++++
8 files changed, 168 insertions(+), 1 deletion(-)
create mode 100644 hw/misc/max78000_trng.c
create mode 100644 include/hw/misc/max78000_trng.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 211b201629..c13ddc980a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -370,6 +370,7 @@ config MAX78000_SOC
select MAX78000_ICC
select MAX78000_UART
select MAX78000_GCR
+ select MAX78000_TRNG
config RASPI
bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 7a012c6ef7..6809d4e249 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -39,6 +39,8 @@ static void max78000_soc_initfn(Object *obj)
object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);
}
+ object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG);
+
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
@@ -130,6 +132,11 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
max78000_uart_irq[i]));
}
+ dev = DEVICE(&s->trng);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x4004d000);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4));
+ dev->id = g_strdup("trng");
create_unimplemented_device("systemInterface", 0x40000400, 0x400);
create_unimplemented_device("functionControl", 0x40000800, 0x3400);
@@ -169,7 +176,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("semaphore", 0x4003e000, 0x1000);
create_unimplemented_device("spi1", 0x40046000, 0x2000);
- create_unimplemented_device("trng", 0x4004d000, 0x1000);
+
create_unimplemented_device("i2s", 0x40060000, 0x1000);
create_unimplemented_device("lowPowerControl", 0x40080000, 0x400);
create_unimplemented_device("gpio2", 0x40080400, 0x200);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index fde2266b8f..dd6a6e54da 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -53,6 +53,9 @@ config MAX78000_GCR
config MAX78000_ICC
bool
+config MAX78000_TRNG
+ bool
+
config MOS6522
bool
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
index 657b7fc490..a469d7a489 100644
--- a/hw/misc/max78000_gcr.c
+++ b/hw/misc/max78000_gcr.c
@@ -161,6 +161,9 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
if (val & UART0_RESET) {
max78000_gcr_reset_device("uart0");
}
+ if (val & TRNG_RESET) {
+ max78000_gcr_reset_device("trng");
+ }
/* TODO: As other devices are implemented, add them here */
break;
}
diff --git a/hw/misc/max78000_trng.c b/hw/misc/max78000_trng.c
new file mode 100644
index 0000000000..bdfaa75bd4
--- /dev/null
+++ b/hw/misc/max78000_trng.c
@@ -0,0 +1,115 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_trng.h"
+#include "crypto/random.h"
+
+static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ uint8_t buf[4];
+ Error *errp = NULL;
+
+ Max78000TrngState *s = opaque;
+ switch (addr) {
+ case CTRL:{
+ return s->ctrl;
+ }
+ case STATUS:{
+ return 1;
+ }
+ case DATA:{
+ qcrypto_random_bytes(buf, 4, &errp);
+ return *(uint32_t *)buf;
+ }
+ default:{
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_MAX78000_TRNG, __func__, addr);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void max78000_trng_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000TrngState *s = opaque;
+ uint32_t val = val64;
+ switch (addr) {
+ case CTRL:{
+ /* TODO: implement AES keygen */
+ s->ctrl = val;
+ if (val & RND_IE) {
+ qemu_set_irq(s->irq, 1);
+ } else{
+ qemu_set_irq(s->irq, 0);
+ }
+ break;
+ }
+ default:{
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_MAX78000_TRNG, __func__, addr);
+ break;
+ }
+
+ }
+}
+
+static void max78000_trng_reset_hold(Object *obj, ResetType type)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ s->ctrl = 0;
+ s->status = 0;
+ s->data = 0;
+}
+
+static const MemoryRegionOps max78000_trng_ops = {
+ .read = max78000_trng_read,
+ .write = max78000_trng_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_trng_init(Object *obj)
+{
+ Max78000TrngState *s = MAX78000_TRNG(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_trng_ops, s,
+ TYPE_MAX78000_TRNG, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_trng_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = max78000_trng_reset_hold;
+
+}
+
+static const TypeInfo max78000_trng_info = {
+ .name = TYPE_MAX78000_TRNG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000TrngState),
+ .instance_init = max78000_trng_init,
+ .class_init = max78000_trng_class_init,
+};
+
+static void max78000_trng_register_types(void)
+{
+ type_register_static(&max78000_trng_info);
+}
+
+type_init(max78000_trng_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 283d06dad4..c7c57d924b 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -72,6 +72,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
))
system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
+system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm_clk.c',
'npcm_gcr.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 8b9e3052cd..936825399c 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -14,6 +14,7 @@
#include "hw/misc/max78000_gcr.h"
#include "hw/misc/max78000_icc.h"
#include "hw/char/max78000_uart.h"
+#include "hw/misc/max78000_trng.h"
#include "qom/object.h"
#define TYPE_MAX78000_SOC "max78000-soc"
@@ -39,6 +40,7 @@ struct MAX78000State {
Max78000GcrState gcr;
Max78000IccState icc[MAX78000_NUM_ICC];
Max78000UartState uart[MAX78000_NUM_UART];
+ Max78000TrngState trng;
Clock *sysclk;
Clock *refclk;
diff --git a/include/hw/misc/max78000_trng.h b/include/hw/misc/max78000_trng.h
new file mode 100644
index 0000000000..c5a8129b6a
--- /dev/null
+++ b/include/hw/misc/max78000_trng.h
@@ -0,0 +1,35 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_TRNG_H
+#define HW_MAX78000_TRNG_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_TRNG "max78000-trng"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000TrngState, MAX78000_TRNG)
+
+#define CTRL 0
+#define STATUS 4
+#define DATA 8
+
+#define RND_IE (1 << 1)
+
+struct Max78000TrngState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t data;
+
+ qemu_irq irq;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 6/6] MAX78000 AES implementation
[not found] <20250510042043.2056265-1-jcksn@duck.com>
` (4 preceding siblings ...)
2025-05-10 4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
@ 2025-05-10 4:20 ` jcksn
5 siblings, 0 replies; 12+ messages in thread
From: jcksn @ 2025-05-10 4:20 UTC (permalink / raw)
To: qemu-devel; +Cc: alistair
This commit implements AES encryption/decryption functionality
for the MAX78000 SOC
Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
hw/arm/Kconfig | 1 +
hw/arm/max78000_soc.c | 12 +-
hw/misc/Kconfig | 3 +
hw/misc/max78000_aes.c | 215 +++++++++++++++++++++++++++++++++
hw/misc/max78000_gcr.c | 9 +-
hw/misc/meson.build | 1 +
include/hw/arm/max78000_soc.h | 2 +
include/hw/misc/max78000_aes.h | 68 +++++++++++
include/hw/misc/max78000_gcr.h | 19 +++
9 files changed, 326 insertions(+), 4 deletions(-)
create mode 100644 hw/misc/max78000_aes.c
create mode 100644 include/hw/misc/max78000_aes.h
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index c13ddc980a..5d38aa0dc3 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -371,6 +371,7 @@ config MAX78000_SOC
select MAX78000_UART
select MAX78000_GCR
select MAX78000_TRNG
+ select MAX78000_AES
config RASPI
bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 6809d4e249..4b9682afce 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -41,6 +41,8 @@ static void max78000_soc_initfn(Object *obj)
object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG);
+ object_initialize_child(obj, "aes", &s->aes, TYPE_MAX78000_AES);
+
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
}
@@ -138,6 +140,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4));
dev->id = g_strdup("trng");
+ dev = DEVICE(&s->aes);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40007400);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 5));
+ dev->id = g_strdup("aes");
+
+
create_unimplemented_device("systemInterface", 0x40000400, 0x400);
create_unimplemented_device("functionControl", 0x40000800, 0x3400);
create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400);
@@ -149,9 +158,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("powerSequencer", 0x40006800, 0x400);
create_unimplemented_device("miscControl", 0x40006c00, 0x800);
- create_unimplemented_device("aes", 0x40007400, 0x400);
- create_unimplemented_device("aesKey", 0x40007800, 0x800);
-
create_unimplemented_device("gpio0", 0x40008000, 0x1000);
create_unimplemented_device("gpio1", 0x40009000, 0x1000);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index dd6a6e54da..c27285b47a 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
config ARM11SCU
bool
+config MAX78000_AES
+ bool
+
config MAX78000_GCR
bool
diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c
new file mode 100644
index 0000000000..9de230fb07
--- /dev/null
+++ b/hw/misc/max78000_aes.c
@@ -0,0 +1,215 @@
+/*
+ * MAX78000 AES
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_aes.h"
+#include "crypto/aes.h"
+
+static void max78000_aes_set_status(Max78000AesState *s)
+{
+ s->status = 0;
+ if (s->result_index >= 16) {
+ s->status |= OUTPUT_FULL;
+ }
+ if (s->result_index == 0) {
+ s->status |= OUTPUT_EMPTY;
+ }
+ if (s->data_index >= 16) {
+ s->status |= INPUT_FULL;
+ }
+ if (s->data_index == 0) {
+ s->status |= INPUT_EMPTY;
+ }
+}
+
+static uint64_t max78000_aes_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ Max78000AesState *s = opaque;
+ switch (addr) {
+ case CTRL:{
+ return s->ctrl;
+ }
+ case STATUS:{
+ return s->status;
+ }
+ case INTFL:{
+ return s->intfl;
+ }
+ case INTEN:{
+ return s->inten;
+ }
+ case FIFO:{
+ if (s->result_index >= 4) {
+ s->intfl &= ~DONE;
+ s->result_index -= 4;
+ max78000_aes_set_status(s);
+ return (s->result[s->result_index] << 24) +
+ (s->result[s->result_index + 1] << 16) +
+ (s->result[s->result_index + 2] << 8) +
+ s->result[s->result_index + 3];
+ } else{
+ return 0;
+ }
+ }
+ default:{
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void max78000_aes_do_crypto(Max78000AesState *s)
+{
+ int keylen = 256;
+ uint8_t *keydata = s->key;
+ if ((s->ctrl & KEY_SIZE) == 0) {
+ keylen = 128;
+ keydata += 16;
+ } else if ((s->ctrl & KEY_SIZE) == 1 << 6) {
+ keylen = 192;
+ keydata += 8;
+ }
+
+ AES_KEY key;
+ if ((s->ctrl & TYPE) == 0) {
+ AES_set_encrypt_key(keydata, keylen, &key);
+ AES_set_decrypt_key(keydata, keylen, &s->internal_key);
+ AES_encrypt(s->data, s->result, &key);
+ s->result_index = 16;
+ } else if ((s->ctrl & TYPE) == 1 << 8) {
+ AES_set_decrypt_key(keydata, keylen, &key);
+ AES_set_decrypt_key(keydata, keylen, &s->internal_key);
+ AES_decrypt(s->data, s->result, &key);
+ s->result_index = 16;
+ } else{
+ AES_decrypt(s->data, s->result, &s->internal_key);
+ s->result_index = 16;
+ }
+ s->intfl |= DONE;
+}
+
+static void max78000_aes_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ Max78000AesState *s = opaque;
+ uint32_t val = val64;
+ int i;
+ switch (addr) {
+ case CTRL:{
+ if (val & OUTPUT_FLUSH) {
+ s->result_index = 0;
+ val &= ~OUTPUT_FLUSH;
+ }
+ if (val & INPUT_FLUSH) {
+ s->data_index = 0;
+ val &= ~INPUT_FLUSH;
+ }
+ if (val & START) {
+ max78000_aes_do_crypto(s);
+ }
+
+ /* Hardware appears to stay enabled even if 0 written */
+ s->ctrl = val | (s->ctrl & AES_EN);
+ break;
+ }
+
+ case FIFO:{
+ for (i = 0; i < 4; i++) {
+ s->data[(12 - s->data_index) + i] =
+ (val >> ((3 - i) * 8)) & 0xff;
+ }
+ s->data_index += 4;
+ if (s->data_index >= 16) {
+ s->data_index = 0;
+ max78000_aes_do_crypto(s);
+ }
+ break;
+ }
+ case KEY_BASE ... KEY_END - 4:{
+ for (i = 0; i < 4; i++) {
+ s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE) + i] =
+ (val >> ((3 - i) * 8)) & 0xff;
+ }
+ break;
+ }
+
+ default:{
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr);
+ break;
+ }
+
+ }
+ max78000_aes_set_status(s);
+}
+
+static void max78000_aes_reset_hold(Object *obj, ResetType type)
+{
+ Max78000AesState *s = MAX78000_AES(obj);
+ s->ctrl = 0;
+ s->status = 0;
+ s->intfl = 0;
+ s->inten = 0;
+
+ s->data_index = 0;
+ s->result_index = 0;
+
+ memset(s->data, 0, sizeof(s->data));
+ memset(s->key, 0, sizeof(s->key));
+ memset(s->result, 0, sizeof(s->result));
+ memset(&s->internal_key, 0, sizeof(s->internal_key));
+
+
+}
+
+static const MemoryRegionOps max78000_aes_ops = {
+ .read = max78000_aes_read,
+ .write = max78000_aes_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_aes_init(Object *obj)
+{
+ Max78000AesState *s = MAX78000_AES(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &max78000_aes_ops, s,
+ TYPE_MAX78000_AES, 0xc00);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_aes_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = max78000_aes_reset_hold;
+
+}
+
+static const TypeInfo max78000_aes_info = {
+ .name = TYPE_MAX78000_AES,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Max78000AesState),
+ .instance_init = max78000_aes_init,
+ .class_init = max78000_aes_class_init,
+};
+
+static void max78000_aes_register_types(void)
+{
+ type_register_static(&max78000_aes_info);
+}
+
+type_init(max78000_aes_register_types)
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
index a469d7a489..c8e2279b00 100644
--- a/hw/misc/max78000_gcr.c
+++ b/hw/misc/max78000_gcr.c
@@ -142,6 +142,8 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
RTC_RESET | I2C0_RESET | SPI1_RESET |
TMR3_RESET | TMR2_RESET | TMR1_RESET |
TMR0_RESET | WDT0_RESET | DMA_RESET;
+
+ max78000_gcr_reset_device("aes");
}
if (val & SOFT_RESET) {
/* Soft reset also resets GPIO */
@@ -151,6 +153,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
TMR3_RESET | TMR2_RESET | TMR1_RESET |
TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
DMA_RESET;
+ max78000_gcr_reset_device("aes");
}
if (val & UART2_RESET) {
max78000_gcr_reset_device("uart2");
@@ -164,6 +167,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
if (val & TRNG_RESET) {
max78000_gcr_reset_device("trng");
}
+
/* TODO: As other devices are implemented, add them here */
break;
}
@@ -208,7 +212,10 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
}
case RST1:{
/* TODO: As other devices are implemented, add them here */
- s->rst1 = val;
+ if (val & AES_RESET) {
+ max78000_gcr_reset_device("aes");
+ }
+
break;
}
case PCKDIS1:{
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index c7c57d924b..b1d8d8e5d2 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
'imx_ccm.c',
'imx_rngc.c',
))
+system_ss.add(when: 'CONFIG_MAX78000_AES', if_true: files('max78000_aes.c'))
system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 936825399c..69284abf9f 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
#include "hw/or-irq.h"
#include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_aes.h"
#include "hw/misc/max78000_gcr.h"
#include "hw/misc/max78000_icc.h"
#include "hw/char/max78000_uart.h"
@@ -41,6 +42,7 @@ struct MAX78000State {
Max78000IccState icc[MAX78000_NUM_ICC];
Max78000UartState uart[MAX78000_NUM_UART];
Max78000TrngState trng;
+ Max78000AesState aes;
Clock *sysclk;
Clock *refclk;
diff --git a/include/hw/misc/max78000_aes.h b/include/hw/misc/max78000_aes.h
new file mode 100644
index 0000000000..407c45ef61
--- /dev/null
+++ b/include/hw/misc/max78000_aes.h
@@ -0,0 +1,68 @@
+/*
+ * MAX78000 AES
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_AES_H
+#define HW_MAX78000_AES_H
+
+#include "hw/sysbus.h"
+#include "crypto/aes.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_AES "max78000-aes"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000AesState, MAX78000_AES)
+
+#define CTRL 0
+#define STATUS 4
+#define INTFL 8
+#define INTEN 0xc
+#define FIFO 0x10
+
+#define KEY_BASE 0x400
+#define KEY_END 0x420
+
+/* CTRL */
+#define TYPE (1 << 9 | 1 << 8)
+#define KEY_SIZE (1 << 7 | 1 << 6)
+#define OUTPUT_FLUSH (1 << 5)
+#define INPUT_FLUSH (1 << 4)
+#define START (1 << 3)
+
+#define AES_EN (1 << 0)
+
+/* STATUS */
+#define OUTPUT_FULL (1 << 4)
+#define OUTPUT_EMPTY (1 << 3)
+#define INPUT_FULL (1 << 2)
+#define INPUT_EMPTY (1 << 1)
+#define BUSY (1 << 0)
+
+/* INTFL*/
+#define DONE (1 << 0)
+
+struct Max78000AesState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t intfl;
+ uint32_t inten;
+ uint32_t data_index;
+ uint8_t data[16];
+
+ uint8_t key[32];
+ AES_KEY internal_key;
+
+ uint32_t result_index;
+ uint8_t result[16];
+
+
+ qemu_irq irq;
+};
+
+#endif
diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h
index 128f9b8a99..b6abaa25bc 100644
--- a/include/hw/misc/max78000_gcr.h
+++ b/include/hw/misc/max78000_gcr.h
@@ -88,6 +88,25 @@ OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR)
#define I2C1_RESET (1 << 0)
+/* RST1 */
+#define CPU1_RESET (1 << 31)
+
+#define SIMO_RESET (1 << 25)
+#define DVS_RESET (1 << 24)
+
+#define I2C2_RESET (1 << 20)
+#define I2S_RESET (1 << 19)
+
+#define SMPHR_RESET (1 << 16)
+
+#define SPI0_RESET (1 << 11)
+#define AES_RESET (1 << 10)
+#define CRC_RESET (1 << 9)
+
+#define PT_RESET (1 << 1)
+#define I2C1_RESET (1 << 0)
+
+
#define SYSRAM0_START 0x20000000
#define SYSRAM1_START 0x20008000
#define SYSRAM2_START 0x20010000
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine
2025-05-10 4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
@ 2025-06-13 15:01 ` Peter Maydell
0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 15:01 UTC (permalink / raw)
To: jcksn; +Cc: qemu-devel, alistair
On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch adds support for the MAX78000FTHR machine.
>
> The MAX78000FTHR contains a MAX78000 and a RISC-V core. This patch
> implements only the MAX78000, which is Cortex-M4 based.
> Details can be found at:
> https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
Hi; thanks for this patchset, and sorry it's taken me so
long to get to reviewing it. Mostly it looks in good
shape, but I have some review comments, which I'll give
below (and on some of the other patches).
A minor note on email patchseries formatting: something odd
seems to have happened with your cover letter. Patches in a
series are supposed to be followups to the cover letter, but
in this case each patch email has a header
In-reply-to: 20250510042043.2056265-1-jcksn@duck.com
but that doesn't match the message-id of the cover letter mail
(which was
16B81215-B460-4A49-910E-E5FB479837C8.1@smtp-inbound1.duck.com
).
It looks like something has rewritten the message-ID header
between when git generated it and it got to the mailing list.
I don't know if that's something you can fix at your end.
(Otherwise I guess you could work around it by sending the cover
letter first, finding out what message-ID it got given, and then
generating the patch emails with the --in-reply-to=<identifier>
option of git send-email so they come out as replies to the
cover letter. But that seems a bit of a faff :-/ )
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> new file mode 100644
> index 0000000000..64578438bd
> --- /dev/null
> +++ b/hw/arm/max78000_soc.c
> @@ -0,0 +1,184 @@
> +/*
> + * MAX78000 SOC
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Implementation based on stm32f205
This comment is a good place to add the URL of the user guide.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "system/address-spaces.h"
> +#include "system/system.h"
> +#include "hw/arm/max78000_soc.h"
> +#include "hw/qdev-clock.h"
> +#include "hw/misc/unimp.h"
> +
> +static void max78000_soc_initfn(Object *obj)
> +{
> + MAX78000State *s = MAX78000_SOC(obj);
> +
> + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
> +
> + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
> + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> +}
> +
> +static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> +{
> + MAX78000State *s = MAX78000_SOC(dev_soc);
> + MemoryRegion *system_memory = get_system_memory();
> + DeviceState *armv7m;
> + Error *err = NULL;
> +
> + /*
> + * We use s->refclk internally and only define it with qdev_init_clock_in()
> + * so it is correctly parented and not leaked on an init/deinit; it is not
> + * intended as an externally exposed clock.
> + */
> + if (clock_has_source(s->refclk)) {
> + error_setg(errp, "refclk clock must not be wired up by the board code");
> + return;
> + }
> +
> + if (!clock_has_source(s->sysclk)) {
> + error_setg(errp, "sysclk clock must be wired up by the board code");
> + return;
> + }
> +
> + /*
> + * TODO: ideally we should model the SoC RCC and its ability to
> + * change the sysclk frequency and define different sysclk sources.
> + */
The MAX78000 doesn't have an RCC...
> +
> + /* The refclk always runs at frequency HCLK / 8 */
Is this actually true for the MAX78000? I couldn't figure out
from the datasheet what it does for the systick refclk.
> + clock_set_mul_div(s->refclk, 8, 1);
> + clock_set_source(s->refclk, s->sysclk);
> +
> + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "MAX78000.flash",
> + FLASH_SIZE, &err);
> + if (err != NULL) {
> + error_propagate(errp, err);
> + return;
> + }
> +
> + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
> +
> + memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE,
> + &err);
> + if (err != NULL) {
> + error_propagate(errp, err);
> + return;
> + }
> + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
> +
> + armv7m = DEVICE(&s->armv7m);
> + qdev_prop_set_uint32(armv7m, "num-irq", 134);
The datasheet seems a little confused here, because
it says there are 134 interrupts including the 15 system
exceptions, but the interrupt vector table it lists then
only goes from 0 to 119 despite including the system exceptions.
So if we believe table 5-1 then there are 104 external
interrupts (numbered from 16 to 119 in that table).
If we believe the "total number of entries including the
15 system exceptions in 134" text above the table, then
there are 120 external exceptions, numbered 16 to 135,
of which 120 to 135 don't appear in the table and would
presumably be unused.
The num-irq property is the number of external interrupts
(not including the CPU-internal ones). So 134 seems definitely
the wrong value, and the correct number is probably either
104 or 120.
If you have the real hardware and the enthusiasm then you
can check by writing to the NVIC_ISERn register bank until
you find the point where you can't write 1 bits and read
them back any more (i.e. until you find the RAZ/WI bits
corresponding to "interrupt number not implemented").
Otherwise, it's not a big deal if we don't actually
get this value correct, but it would in that case be worth
a comment noting that the datasheet is unclear and we've
made our best guess at the value.
> + qdev_prop_set_uint8(armv7m, "num-prio-bits", 3);
> + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
> + qdev_prop_set_bit(armv7m, "enable-bitband", true);
> + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
> + qdev_connect_clock_in(armv7m, "refclk", s->refclk);
> + object_property_set_link(OBJECT(&s->armv7m), "memory",
> + OBJECT(system_memory), &error_abort);
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
> + return;
> + }
> +
> + create_unimplemented_device("globalControl", 0x40000000, 0x400);
> + create_unimplemented_device("systemInterface", 0x40000400, 0x400);
> + create_unimplemented_device("functionControl", 0x40000800, 0x3400);
Table 3-3 says the end address is 0x4000_0bff, so the size should be 0x400,
not 0x3400.
> + create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400);
> + create_unimplemented_device("dynamicVoltScale", 0x40003c00, 0x800);
Size doesn't match listed end address in table 3-3 ?
> + create_unimplemented_device("SIMO", 0x40004400, 0x400);
> + create_unimplemented_device("trimSystemInit", 0x40005400, 0x400);
> + create_unimplemented_device("generalCtrlFunc", 0x40005800, 0xc00);
Ditto
> + create_unimplemented_device("wakeupTimer", 0x40006400, 0x400);
> + create_unimplemented_device("powerSequencer", 0x40006800, 0x400);
> + create_unimplemented_device("miscControl", 0x40006c00, 0x800);
Ditto. I stopped checking the sizes here but you should probably
look at the rest.
> +
> + create_unimplemented_device("aes", 0x40007400, 0x400);
> + create_unimplemented_device("aesKey", 0x40007800, 0x800);
> +
> + create_unimplemented_device("gpio0", 0x40008000, 0x1000);
> + create_unimplemented_device("gpio1", 0x40009000, 0x1000);
> +
> + create_unimplemented_device("parallelCamInter", 0x4000e000, 0x1000);
> + create_unimplemented_device("CRC", 0x4000f000, 0x1000);
> +
> + create_unimplemented_device("timer0", 0x40010000, 0x1000);
> + create_unimplemented_device("timer1", 0x40011000, 0x1000);
> + create_unimplemented_device("timer2", 0x40012000, 0x1000);
> + create_unimplemented_device("timer3", 0x40013000, 0x1000);
> +
> + create_unimplemented_device("i2c0", 0x4001d000, 0x1000);
> + create_unimplemented_device("i2c1", 0x4001e000, 0x1000);
> + create_unimplemented_device("i2c2", 0x4001f000, 0x1000);
> +
> + create_unimplemented_device("standardDMA", 0x40028000, 0x1000);
> + create_unimplemented_device("flashController0", 0x40029000, 0x400);
> +
> + create_unimplemented_device("icc0", 0x4002a000, 0x800);
> + create_unimplemented_device("icc1", 0x4002a800, 0x800);
> +
> + create_unimplemented_device("adc", 0x40034000, 0x1000);
> + create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
> + create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
> + create_unimplemented_device("semaphore", 0x4003e000, 0x1000);
> +
> + create_unimplemented_device("uart0", 0x40042000, 0x1000);
> + create_unimplemented_device("uart1", 0x40043000, 0x1000);
> + create_unimplemented_device("uart2", 0x40044000, 0x1000);
> +
> + create_unimplemented_device("spi1", 0x40046000, 0x2000);
> + create_unimplemented_device("trng", 0x4004d000, 0x1000);
> + create_unimplemented_device("i2s", 0x40060000, 0x1000);
> + create_unimplemented_device("lowPowerControl", 0x40080000, 0x400);
> + create_unimplemented_device("gpio2", 0x40080400, 0x200);
> + create_unimplemented_device("lowPowWatchdogTi", 0x40080800, 0x400);
You don't need to just cut off the name string like this, there's
no fixed length to it. "Ti" is rather non-obvious (and the point
of these strings is to show them to the user if the guest tries
to access the missing device). Either write Timer or else just say
"lowPowerWatchdog". Similarly with some of the other names.
> +
> + create_unimplemented_device("lowPowerTimer5", 0x40081000, 0x400);
> + create_unimplemented_device("lowPowerUART0", 0x40081400, 0x400);
> + create_unimplemented_device("lowPowerCompar", 0x40088000, 0x400);
> +
> + create_unimplemented_device("spi0", 0x400be000, 0x400);
> +
> + /*
> + * The MAX78000 user guide's base address map lists the CNN TX FIFO as
> + * beginning at 0x400c0400 and ending at 0x400c0400. Given that CNN_FIFO
> + * is listed as having data accessible up to offset 0x1000, the user
> + * guide is likely incorrect.
> + */
> + create_unimplemented_device("cnnTxFIFO", 0x400c0400, 0x2000);
> +
> + create_unimplemented_device("cnnGlobalControl", 0x50000000, 0x10000);
> + create_unimplemented_device("cnnx16quad0", 0x50100000, 0x40000);
> + create_unimplemented_device("cnnx16quad1", 0x50500000, 0x40000);
> + create_unimplemented_device("cnnx16quad2", 0x50900000, 0x40000);
> + create_unimplemented_device("cnnx16quad3", 0x50d00000, 0x40000);
> +
> +}
thanks
-- PMM
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/6] MAX78000: ICC Implementation
2025-05-10 4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
@ 2025-06-13 15:26 ` Peter Maydell
0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 15:26 UTC (permalink / raw)
To: jcksn; +Cc: qemu-devel, alistair
On Sat, 10 May 2025 at 07:58, <jcksn@duck.com> wrote:
>
> This patch implements the Instruction Cache Controller for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> hw/arm/Kconfig | 1 +
> hw/arm/max78000_soc.c | 20 ++++++--
> hw/misc/Kconfig | 3 ++
> hw/misc/max78000_icc.c | 89 ++++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/arm/max78000_soc.h | 6 +++
> include/hw/misc/max78000_icc.h | 34 +++++++++++++
> 7 files changed, 150 insertions(+), 4 deletions(-)
> create mode 100644 hw/misc/max78000_icc.c
> create mode 100644 include/hw/misc/max78000_icc.h
We generally prefer to split into two patches,
one for "just add the new device" and then a separate
one for "instantiate the new device in the SoC".
(This device is quite small, but for larger ones it
helps to keep the patchsize down.)
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 1c365d1115..3f23af3244 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -367,6 +367,7 @@ config ALLWINNER_R40
> config MAX78000_SOC
> bool
> select ARM_V7M
> + select MAX78000_ICC
>
> config RASPI
> bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 64578438bd..4d598bddd4 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -16,12 +16,19 @@
> #include "hw/qdev-clock.h"
> #include "hw/misc/unimp.h"
>
> +static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
> +
> static void max78000_soc_initfn(Object *obj)
> {
> MAX78000State *s = MAX78000_SOC(obj);
> + int i;
>
> object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> + for (i = 0; i < MAX78000_NUM_ICC; i++) {
> + object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
> + }
> +
> s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
> s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> }
> @@ -30,8 +37,9 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> {
> MAX78000State *s = MAX78000_SOC(dev_soc);
> MemoryRegion *system_memory = get_system_memory();
> - DeviceState *armv7m;
> + DeviceState *dev, *armv7m;
> Error *err = NULL;
> + int i;
>
> /*
> * We use s->refclk internally and only define it with qdev_init_clock_in()
> @@ -87,6 +95,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> return;
> }
>
> + for (i = 0; i < MAX78000_NUM_ICC; i++) {
> + dev = DEVICE(&(s->icc[i]));
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
You want just sysbus_realize() here. There are two patterns:
- when creating a device via qdev_new() or something similar,
realize it with a *_realize_and_unref() function
- when initializing an object in place with object_initialize_child(),
realize it with a *_realize() function
(This is because object_initialize_child() has already arranged
for the only reference to the child device to be held by the
parent via the child<> property, so we don't want to deref it
again.)
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
> + }
> +
> +
> create_unimplemented_device("globalControl", 0x40000000, 0x400);
> create_unimplemented_device("systemInterface", 0x40000400, 0x400);
> create_unimplemented_device("functionControl", 0x40000800, 0x3400);
> @@ -120,9 +135,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> create_unimplemented_device("standardDMA", 0x40028000, 0x1000);
> create_unimplemented_device("flashController0", 0x40029000, 0x400);
>
> - create_unimplemented_device("icc0", 0x4002a000, 0x800);
> - create_unimplemented_device("icc1", 0x4002a800, 0x800);
> -
> create_unimplemented_device("adc", 0x40034000, 0x1000);
> create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
> create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index ec0fa5aa9f..781bcf74cc 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -47,6 +47,9 @@ config A9SCU
> config ARM11SCU
> bool
>
> +config MAX78000_ICC
> + bool
> +
> config MOS6522
> bool
>
> diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c
> new file mode 100644
> index 0000000000..3eacf6bd1b
> --- /dev/null
> +++ b/hw/misc/max78000_icc.c
> @@ -0,0 +1,89 @@
> +/*
> + * MAX78000 Instruction Cache
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "trace.h"
> +#include "hw/irq.h"
> +#include "migration/vmstate.h"
> +#include "hw/misc/max78000_icc.h"
> +
> +
> +static uint64_t max78000_icc_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + Max78000IccState *s = opaque;
> + switch (addr) {
> + case ICC_INFO:{
> + return s->info;
> + }
Your switch case indentation seems a bit odd. Our usual
style puts the "case" at the same indent level as "switch".
Also, you don't need braces on cases unless they are
required to define the scope of a local variable.
So none of these need them.
> + case ICC_SZ:{
> + return s->sz;
> + }
> + case ICC_CTRL:{
> + return s->ctrl;
> + }
> + case ICC_INVALIDATE:{
> + return s->invalidate;
> + }
ICCn_INVALIDATE is write-only; it doesn't need a state
field in the device state struct. It doesn't need a
case in the read function, we can let that drop into
the default case.
> + default:{
> + return 0;
For "there is no register at this offset", use:
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad offset 0x%" HWADDR_PRIx "\n",
__func__, addr);
return 0;
(Similarly for write.)
> + }
> + }
> +}
> +
> +static void max78000_icc_write(void *opaque, hwaddr addr,
> + uint64_t val64, unsigned int size)
> +{
> + Max78000IccState *s = opaque;
> +
> + switch (addr) {
> + case ICC_CTRL:{
> + s->ctrl = 0x10000 + (val64 & 1);
I prefer "|" over "+" for this kind of thing, as we are
assembling bit fields, not doing arithmetic.
> + break;
> + }
> + case ICC_INVALIDATE:{
> + s->ctrl = s->ctrl | 0x80;
What is this doing? The datasheet says bit 7 in ICCn_CTRL
is reserved.
> + }
A default case logging the guest error would be good.
> + }
> +}
> +
> +static const MemoryRegionOps stm32f4xx_exti_ops = {
> + .read = max78000_icc_read,
> + .write = max78000_icc_write,
I think we should also specify
.valid.min_access_size = 4,
.valid.max_access_size = 4,
which will mean the guest gets an exception if it tries
to do a byte read to these 32-bit registers. Otherwise
we would have to put in extra complexity to handle the
byte and halfword access cases. (QEMU can try to
synthesize these for you but its default is not
necessarily what you need for all devices.)
> + .endianness = DEVICE_NATIVE_ENDIAN,
We're trying to reduce the use of DEVICE_NATIVE_ENDIAN,
so use DEVICE_LITTLE_ENDIAN here, please. (For an Arm
machine type, the two are the same thing.)
> +};
> +
> +static void max78000_icc_init(Object *obj)
> +{
> + Max78000IccState *s = MAX78000_ICC(obj);
> + s->info = 0;
> + s->sz = 0x10000010;
> + s->ctrl = 0x10000;
> + s->invalidate = 0;
These look like reset values. Reset of registers needs to
be done in a reset method, not in init.
> +
> +
> + memory_region_init_io(&s->mmio, obj, &stm32f4xx_exti_ops, s,
> + TYPE_MAX78000_ICC, 0x800);
> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +
> +}
You also need to support migration, which means having
a VMStateDescription that defines what the guest-visible
state of your device is, and setting dc->vmsd to point to it
in your device's class init. For more info see
https://www.qemu.org/docs/master/devel/migration/main.html
and look at other devices in the source tree for examples.
thanks
-- PMM
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/6] MAX78000 UART implementation
2025-05-10 4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
@ 2025-06-13 16:03 ` Peter Maydell
0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 16:03 UTC (permalink / raw)
To: jcksn; +Cc: qemu-devel, alistair
On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements UART support for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> hw/arm/Kconfig | 1 +
> hw/arm/max78000_soc.c | 27 +++-
> hw/char/Kconfig | 3 +
> hw/char/max78000_uart.c | 263 ++++++++++++++++++++++++++++++++
> hw/char/meson.build | 1 +
> include/hw/arm/max78000_soc.h | 3 +
> include/hw/char/max78000_uart.h | 77 ++++++++++
> 7 files changed, 370 insertions(+), 5 deletions(-)
> create mode 100644 hw/char/max78000_uart.c
> create mode 100644 include/hw/char/max78000_uart.h
Some of the comments I had on patch 2 apply also to this
and the other devices:
* separate patches for "new device" and "wire up new
device into the SoC"
* missing migration state support
* use DEVICE_LITTLE_ENDIAN
* set .impl.min_access_size and .impl_max_access_size
appropriately for the device
* make sure you've implemented a reset method (this device
has one but some of the later ones don't)
* fix case indent and excess braces if necessary
* make sure you're not using the _realize_and_unref functions
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 3f23af3244..59450dc3cb 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -368,6 +368,7 @@ config MAX78000_SOC
> bool
> select ARM_V7M
> select MAX78000_ICC
> + select MAX78000_UART
>
> config RASPI
> bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 4d598bddd4..6334d8b49b 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -16,7 +16,11 @@
> #include "hw/qdev-clock.h"
> #include "hw/misc/unimp.h"
>
> -static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
> +static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
> +static const uint32_t max78000_uart_addr[] = {0x40042000, 0x40043000,
> + 0x40044000};
> +
> +static const int max78000_uart_irq[] = {30, 31, 50};
The GPIO inputs to the ARM7M object are only the external
interrupt lines, so GPIO 0 is the first external interrupt,
which is 16 in the datasheet's Table 3-3. So you need to
subtract 16 from all the numbers in the table to get
the right GPIO index. The UARTs here are at 14, 15, 34.
>
> static void max78000_soc_initfn(Object *obj)
> {
> @@ -29,6 +33,10 @@ static void max78000_soc_initfn(Object *obj)
> object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
> }
>
> + for (i = 0; i < MAX78000_NUM_UART; i++) {
> + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);
I didn't notice this for the icc patch, but rather than using the
same string for each UART, you can give them unique names with
g_autofree char *name = g_strdup_printf("uart%d", i);
object_initialize_child(obj, name, ...)
See eg hw/arm/stm32l4x5_soc.c for examples.
> + }
> +
> s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
> s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> }
> @@ -38,6 +46,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> MAX78000State *s = MAX78000_SOC(dev_soc);
> MemoryRegion *system_memory = get_system_memory();
> DeviceState *dev, *armv7m;
> + SysBusDevice *busdev;
> Error *err = NULL;
> int i;
>
> @@ -101,6 +110,18 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
> }
>
> + for (i = 0; i < MAX78000_NUM_UART; i++) {
> + dev = DEVICE(&(s->uart[i]));
> + qdev_prop_set_chr(dev, "chardev", serial_hd(i));
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> + return;
> + }
> + busdev = SYS_BUS_DEVICE(dev);
> + sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
> + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> + max78000_uart_irq[i]));
> + }
> +
>
> create_unimplemented_device("globalControl", 0x40000000, 0x400);
> create_unimplemented_device("systemInterface", 0x40000400, 0x400);
> @@ -140,10 +161,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000);
> create_unimplemented_device("semaphore", 0x4003e000, 0x1000);
>
> - create_unimplemented_device("uart0", 0x40042000, 0x1000);
> - create_unimplemented_device("uart1", 0x40043000, 0x1000);
> - create_unimplemented_device("uart2", 0x40044000, 0x1000);
> -
> create_unimplemented_device("spi1", 0x40046000, 0x2000);
> create_unimplemented_device("trng", 0x4004d000, 0x1000);
> create_unimplemented_device("i2s", 0x40060000, 0x1000);
> diff --git a/hw/char/Kconfig b/hw/char/Kconfig
> index 9d517f3e28..020c0a84bb 100644
> --- a/hw/char/Kconfig
> +++ b/hw/char/Kconfig
> @@ -48,6 +48,9 @@ config VIRTIO_SERIAL
> default y
> depends on VIRTIO
>
> +config MAX78000_UART
> + bool
> +
> config STM32F2XX_USART
> bool
>
> diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c
> new file mode 100644
> index 0000000000..edd39c5a8b
> --- /dev/null
> +++ b/hw/char/max78000_uart.c
> @@ -0,0 +1,263 @@
> +/*
> + * MAX78000 UART
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/char/max78000_uart.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "trace.h"
> +
> +static int max78000_uart_can_receive(void *opaque)
> +{
> + Max78000UartState *s = opaque;
> + if (!(s->ctrl & UART_BCLKEN)) {
> + return 0;
> + }
> + return fifo8_num_free(&s->rx_fifo);
> +}
> +
> +static void max78000_update_irq(Max78000UartState *s)
> +{
> + int interrupt_level = 0;
> + uint32_t rx_threshold = s->ctrl & 0xf;
> +
> + /*
> + * Because tx is synchronous and we should have no frame errors, the only
> + * possible interrupt is receive fifo threshold
> + */
> + if ((s->int_en & UART_RX_THD) && fifo8_num_used(&s->rx_fifo) >= rx_threshold) {
> + interrupt_level = 1;
> + s->int_fl = s->int_fl & UART_RX_THD;
> + } else{
> + s->int_fl = s->int_fl & ~UART_RX_THD;
> + }
This looks a little odd. The usual pattern for interrupt
status bits is:
* when we notice the condition (in this case, when we receive
a char and it puts us above the rx threshold), set the
bit in the int_fl register
* option A: devices where the flag bit tracks the underlying
condition:
- when we notice that the condition is no longer true (in this
case, when the guest reads a char from the rx fifo and it
puts us below the rx threshold again), clear the int_fl bit,
* option B: devices where the flag bit latches and the guest must
explicitly clear it:
- no action when the condition is no longer true
The MAX78000 UART is an "option B" device -- see section 12.5.
* in the update_irq() function, set the interrupt if
the int_fl bit and the int_en bit are set, otherwise clear it.
If the bit definitions in the two registers line up, this is
as simple as
bool interrupt_level = s->int_fl & s->int_en;
If they don't line up then it needs a bit more manual work.
The code you have at the moment doesn't implement the "int_fl
bits latch and must be explicitly cleared by software" behaviour.
> + qemu_set_irq(s->irq, interrupt_level);
> +}
> +
> +static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size)
> +{
> + Max78000UartState *s = opaque;
> +
> + if (size <= fifo8_num_free(&s->rx_fifo)) {
> + fifo8_push_all(&s->rx_fifo, buf, size);
> + } else{
> + fifo8_push_all(&s->rx_fifo, buf, fifo8_num_free(&s->rx_fifo));
> + printf("rx_fifo overrun!\n");
This should be a "can't happen" condition -- your receive
method will never be passed more bytes of data than you
said you could handle in can_receive.
Don't printf() in device code. Your options are:
* if it's something that the device spec says is
forbidden but a badly behaved guest can trigger,
use qemu_log(LOG_GUEST_ERROR, ...)
* if it's something that the device should implement but
we don't, use qemu_log(LOG_UNIMP, ...)
* if it's something that can't happen unless there's a bug
in QEMU, use some kind of assert()
* if it's nice-to-have information for debugging or for
a user to see what the device is doing, use a tracepoint
(trace_* functions, see docs/devel/tracing.rst)
From your register write function:
> + case UART_INT_FL:
> + s->int_fl = value;
> + return;
This register's bits are W1C, i.e. write-1-to-clear.
thanks
-- PMM
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 4/6] MAX78000 GCR implementation
2025-05-10 4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
@ 2025-06-16 5:13 ` Philippe Mathieu-Daudé
2025-06-16 9:47 ` Peter Maydell
1 sibling, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-06-16 5:13 UTC (permalink / raw)
To: jcksn, qemu-devel; +Cc: alistair
Hi,
On 10/5/25 06:20, jcksn@duck.com wrote:
> This patch implements the Global Control Register for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> hw/arm/Kconfig | 1 +
> hw/arm/max78000_soc.c | 9 +-
> hw/misc/Kconfig | 3 +
> hw/misc/max78000_gcr.c | 285 +++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/arm/max78000_soc.h | 2 +
> include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
> 7 files changed, 422 insertions(+), 1 deletion(-)
> create mode 100644 hw/misc/max78000_gcr.c
> create mode 100644 include/hw/misc/max78000_gcr.h
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 6334d8b49b..7a012c6ef7 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
>
> object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> + object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
> +
> for (i = 0; i < MAX78000_NUM_ICC; i++) {
> object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
> }
> @@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> return;
> }
>
> + dev = DEVICE(&s->gcr);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
> +
> for (i = 0; i < MAX78000_NUM_ICC; i++) {
> dev = DEVICE(&(s->icc[i]));
> sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> @@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> return;
> }
> + dev->id = g_strdup_printf("uart%d", i);
This line belongs to the previous patch (UART implementation).
> busdev = SYS_BUS_DEVICE(dev);
> sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
> sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> @@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 4/6] MAX78000 GCR implementation
2025-05-10 4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
2025-06-16 5:13 ` Philippe Mathieu-Daudé
@ 2025-06-16 9:47 ` Peter Maydell
1 sibling, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-16 9:47 UTC (permalink / raw)
To: jcksn; +Cc: qemu-devel, Alistair Francis
On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements the Global Control Register for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> hw/arm/Kconfig | 1 +
> hw/arm/max78000_soc.c | 9 +-
> hw/misc/Kconfig | 3 +
> hw/misc/max78000_gcr.c | 285 +++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/arm/max78000_soc.h | 2 +
> include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
> 7 files changed, 422 insertions(+), 1 deletion(-)
> create mode 100644 hw/misc/max78000_gcr.c
> create mode 100644 include/hw/misc/max78000_gcr.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 59450dc3cb..211b201629 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -369,6 +369,7 @@ config MAX78000_SOC
> select ARM_V7M
> select MAX78000_ICC
> select MAX78000_UART
> + select MAX78000_GCR
>
> config RASPI
> bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 6334d8b49b..7a012c6ef7 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
>
> object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> + object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
> +
> for (i = 0; i < MAX78000_NUM_ICC; i++) {
> object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
> }
> @@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> return;
> }
>
> + dev = DEVICE(&s->gcr);
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
> +
> for (i = 0; i < MAX78000_NUM_ICC; i++) {
> dev = DEVICE(&(s->icc[i]));
> sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> @@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> return;
> }
> + dev->id = g_strdup_printf("uart%d", i);
> +
> busdev = SYS_BUS_DEVICE(dev);
> sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
> sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> @@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> }
>
>
> - create_unimplemented_device("globalControl", 0x40000000, 0x400);
> create_unimplemented_device("systemInterface", 0x40000400, 0x400);
> create_unimplemented_device("functionControl", 0x40000800, 0x3400);
> create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 781bcf74cc..fde2266b8f 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -47,6 +47,9 @@ config A9SCU
> config ARM11SCU
> bool
>
> +config MAX78000_GCR
> + bool
> +
> config MAX78000_ICC
> bool
>
> diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
> new file mode 100644
> index 0000000000..657b7fc490
> --- /dev/null
> +++ b/hw/misc/max78000_gcr.c
> @@ -0,0 +1,285 @@
> +/*
> + * MAX78000 Global Control Registers
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "trace.h"
> +#include "hw/irq.h"
> +#include "system/runstate.h"
> +#include "migration/vmstate.h"
> +#include "hw/misc/max78000_gcr.h"
> +
> +static void max78000_gcr_reset(DeviceState *dev)
> +{
> + Max78000GcrState *s = MAX78000_GCR(dev);
> + s->sysctrl = 0x21002;
> + s->rst0 = 0;
> + /* All clocks are always ready */
> + s->clkctrl = 0x3e140008;
> + s->pm = 0x3f000;
> + s->pclkdiv = 0;
> + s->pclkdis0 = 0xffffffff;
> + s->memctrl = 0x5;
> + s->memz = 0;
> + s->sysst = 0;
> + s->rst1 = 0;
> + s->pckdis1 = 0xffffffff;
> + s->eventen = 0;
> + s->revision = 0xa1;
> + s->sysie = 0;
> + s->eccerr = 0;
> + s->ecced = 0;
> + s->eccie = 0;
> + s->eccaddr = 0;
> +}
> +
> +static uint64_t max78000_gcr_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + Max78000GcrState *s = opaque;
> +
> + switch (addr) {
> + case SYSCTRL:{
> + return s->sysctrl;
> + }
> + case RST0:{
> + return s->rst0;
> + }
> + case CLKCTRL:{
> + return s->clkctrl;
> + }
> + case PM:{
> + return s->pm;
> + }
> + case PCLKDIV:{
> + return s->pclkdiv;
> + }
> + case PCLKDIS0:{
> + return s->pclkdis0;
> + }
> + case MEMCTRL:{
> + return s->memctrl;
> + }
> + case MEMZ:{
> + return s->memz;
> + }
> + case SYSST:{
> + return s->sysst;
> + }
> + case RST1:{
> + return s->rst1;
> + }
> + case PCKDIS1:{
> + return s->pckdis1;
> + }
> + case EVENTEN:{
> + return s->eventen;
> + }
> + case REVISION:{
> + return s->revision;
> + }
> + case SYSIE:{
> + return s->sysie;
> + }
> + case ECCERR:{
> + return s->eccerr;
> + }
> + case ECCED:{
> + return s->ecced;
> + }
> + case ECCIE:{
> + return s->eccie;
> + }
> + case ECCADDR:{
> + return s->eccaddr;
> + }
> + default:{
> + return 0;
> + }
> + }
> +}
> +
> +static void max78000_gcr_reset_device(const char *device_name)
> +{
> + DeviceState *dev = qdev_find_recursive(sysbus_get_default(), device_name);
> + if (dev) {
> + device_cold_reset(dev);
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR, "no device %s for reset\n", device_name);
> + }
> +}
This device shouldn't be finding the devices it needs to reset
by looking them up by name. I don't think we really have any
pre-existing examples of reset controllers in the tree, so
here's the off-the-top-of-my-head suggestion:
* have this device define link properties with DEFINE_PROP_LINK
for the devices it needs to reset
* have the SoC set those link properties when it creates this
device
* then in the code that does the reset you have a direct pointer
to the DeviceState* that you want to reset
> +static void max78000_gcr_write(void *opaque, hwaddr addr,
> + uint64_t val64, unsigned int size)
> +{
> + Max78000GcrState *s = opaque;
> + uint32_t val = val64;
> + uint8_t zero[0xc000] = {0};
> + switch (addr) {
> + case SYSCTRL:{
> + /* Checksum calculations always pass immediately */
> + s->sysctrl = (val & 0x30000) | 0x1002;
> + break;
> + }
> + case RST0:{
> + if (val & SYSTEM_RESET) {
> + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> + }
> + if (val & PERIPHERAL_RESET) {
> + /*
> + * Peripheral reset resets all peripherals. The CPU
> + * retains its state. The GPIO, watchdog timers, AoD,
> + * RAM retention, and general control registers (GCR),
> + * including the clock configuration, are unaffected.
> + */
> + val = UART2_RESET | UART1_RESET | UART0_RESET |
> + ADC_RESET | CNN_RESET | TRNG_RESET |
> + RTC_RESET | I2C0_RESET | SPI1_RESET |
> + TMR3_RESET | TMR2_RESET | TMR1_RESET |
> + TMR0_RESET | WDT0_RESET | DMA_RESET;
> + }
> + if (val & SOFT_RESET) {
> + /* Soft reset also resets GPIO */
> + val = UART2_RESET | UART1_RESET | UART0_RESET |
> + ADC_RESET | CNN_RESET | TRNG_RESET |
> + RTC_RESET | I2C0_RESET | SPI1_RESET |
> + TMR3_RESET | TMR2_RESET | TMR1_RESET |
> + TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
> + DMA_RESET;
> + }
> + if (val & UART2_RESET) {
> + max78000_gcr_reset_device("uart2");
> + }
> + if (val & UART1_RESET) {
> + max78000_gcr_reset_device("uart1");
> + }
> + if (val & UART0_RESET) {
> + max78000_gcr_reset_device("uart0");
> + }
> + /* TODO: As other devices are implemented, add them here */
> + break;
> + }
> + case CLKCTRL:{
> + s->clkctrl = val | SYSCLK_RDY;
> + break;
> + }
> + case PM:{
> + s->pm = val;
> + break;
> + }
> + case PCLKDIV:{
> + s->pclkdiv = val;
> + break;
> + }
> + case PCLKDIS0:{
> + s->pclkdis0 = val;
> + break;
> + }
> + case MEMCTRL:{
> + s->memctrl = val;
> + break;
> + }
> + case MEMZ:{
> + if (val & ram0) {
> + cpu_physical_memory_write(SYSRAM0_START, zero, 0x8000);
> + }
> + if (val & ram1) {
> + cpu_physical_memory_write(SYSRAM1_START, zero, 0x8000);
> + }
> + if (val & ram2) {
> + cpu_physical_memory_write(SYSRAM2_START, zero, 0xC000);
> + }
> + if (val & ram3) {
> + cpu_physical_memory_write(SYSRAM3_START, zero, 0x4000);
> + }
New devices shouldn't use cpu_physical_memory_write(), please.
Take a link property to a MemoryRegion, which you then convert
into an AddressSpace you can pass to address_space_write().
> + break;
> + }
thanks
-- PMM
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/6] MAX78000 TRNG Implementation
2025-05-10 4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
@ 2025-06-16 9:56 ` Peter Maydell
0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-16 9:56 UTC (permalink / raw)
To: jcksn; +Cc: qemu-devel, alistair
On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements the True Random Number Generator for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
> hw/arm/Kconfig | 1 +
> hw/arm/max78000_soc.c | 9 ++-
> hw/misc/Kconfig | 3 +
> hw/misc/max78000_gcr.c | 3 +
> hw/misc/max78000_trng.c | 115 ++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/arm/max78000_soc.h | 2 +
> include/hw/misc/max78000_trng.h | 35 ++++++++++
> 8 files changed, 168 insertions(+), 1 deletion(-)
> create mode 100644 hw/misc/max78000_trng.c
> create mode 100644 include/hw/misc/max78000_trng.h
> +static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + uint8_t buf[4];
> + Error *errp = NULL;
> +
> + Max78000TrngState *s = opaque;
> + switch (addr) {
> + case CTRL:{
> + return s->ctrl;
> + }
> + case STATUS:{
> + return 1;
> + }
> + case DATA:{
> + qcrypto_random_bytes(buf, 4, &errp);
> + return *(uint32_t *)buf;
> + }
No other code in hw/ calls qcrypto_random_bytes(). This kind
of "almost no other use of this function like this" is usually
a hint that it's not a good idea in new code. You want
qemu_guest_getrandom_nofail().
Also, instead of using a uint8_t buf[], use a
uint32_t and pass the address of that to the function:
uint32_t data;
qemu_guest_getrandom_nofail(&data, sizeof(data));
return data;
That avoids the need for a cast.
Your interrupt generation code in this device doesn't look
right: the interrupt is supposed to be generated when each
new random number is ready, so in our "generation takes
zero time" model a read from DATA should provoke a new
interrupt immediately (assuming the interrupt is enabled):
you need to simulate the ready status bit going low and
then high again.
See also my comments on an earlier patch about the usual
logic being to have an update function which does
"set interrupt to (condition && enabled)".
-- PMM
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-06-16 9:57 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20250510042043.2056265-1-jcksn@duck.com>
2025-05-10 4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
2025-06-13 15:01 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
2025-06-13 15:26 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
2025-06-13 16:03 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
2025-06-16 5:13 ` Philippe Mathieu-Daudé
2025-06-16 9:47 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
2025-06-16 9:56 ` Peter Maydell
2025-05-10 4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn
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).