[PATCH] STM32F100: support different density lines

Lucas Villa Real posted 1 patch 11 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20230619221819.6882-1-lucas@osdyne.com
Maintainers: Alexandre Iooss <erdnaxe@crans.org>, Paolo Bonzini <pbonzini@redhat.com>, Peter Maydell <peter.maydell@linaro.org>, Alistair Francis <alistair@alistair23.me>
configs/devices/arm-softmmu/default.mak |  1 +
docs/system/arm/stm32.rst               | 14 ++++
hw/arm/Kconfig                          |  6 ++
hw/arm/meson.build                      |  1 +
hw/arm/stm32f100_soc.c                  | 92 +++++++++++++++++++++----
hw/arm/stm32f1_generic.c                | 70 +++++++++++++++++++
hw/arm/stm32vldiscovery.c               |  3 +-
include/hw/arm/stm32f100_soc.h          | 18 ++++-
8 files changed, 189 insertions(+), 16 deletions(-)
create mode 100644 hw/arm/stm32f1_generic.c
[PATCH] STM32F100: support different density lines
Posted by Lucas Villa Real 11 months ago
This patch adds support for the emulation of different density lines
(low, medium, and high). A new class property stm32f100-soc.density=
has been introduced to allow users to state the desired configuration.
That property is recognized by a new machine, stm32f1-generic. The SOC
is configured according to the following:

   density=low       32 KB FLASH, 2 SPIs
   density=medium   128 KB FLASH, 2 SPIs
   density=high     512 KB FLASH, 3 SPIs

With this code change we should be able to introduce richer features
to STM32F100, such as support for FSMC (so that a machine with more
RAM capacity can be properly emulated). FSMC is supported on high
density line devices only.

Signed-off-by: Lucas C. Villa Real <lucas@osdyne.com>
---
 configs/devices/arm-softmmu/default.mak |  1 +
 docs/system/arm/stm32.rst               | 14 ++++
 hw/arm/Kconfig                          |  6 ++
 hw/arm/meson.build                      |  1 +
 hw/arm/stm32f100_soc.c                  | 92 +++++++++++++++++++++----
 hw/arm/stm32f1_generic.c                | 70 +++++++++++++++++++
 hw/arm/stm32vldiscovery.c               |  3 +-
 include/hw/arm/stm32f100_soc.h          | 18 ++++-
 8 files changed, 189 insertions(+), 16 deletions(-)
 create mode 100644 hw/arm/stm32f1_generic.c

diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak
index 980c48a7d9..4f0f2e99c0 100644
--- a/configs/devices/arm-softmmu/default.mak
+++ b/configs/devices/arm-softmmu/default.mak
@@ -19,6 +19,7 @@ CONFIG_ARM_VIRT=y
 # CONFIG_NSERIES=n
 # CONFIG_STELLARIS=n
 # CONFIG_STM32VLDISCOVERY=n
+# CONFIG_STM32F1_GENERIC=n
 # CONFIG_REALVIEW=n
 # CONFIG_VERSATILE=n
 # CONFIG_VEXPRESS=n
diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
index d7265b763d..d0a3b1a7eb 100644
--- a/docs/system/arm/stm32.rst
+++ b/docs/system/arm/stm32.rst
@@ -10,6 +10,12 @@ The STM32F1 series is based on ARM Cortex-M3 core. The following machines are
 based on this chip :
 
 - ``stm32vldiscovery``  STM32VLDISCOVERY board with STM32F100RBT6 microcontroller
+- ``stm32f1-generic``   Generic STM32F1 board supporting low, medium and high
+                        density devices. Low-density emulates a 32KB FLASH;
+                        medium-density emulates a 128KB FLASH; high-density
+                        emulates a 512KB FLASH. The density also affects the
+                        number of peripherals exposed by QEMU for the emulated
+                        device. See ``Boot options`` below for more details.
 
 The STM32F2 series is based on ARM Cortex-M3 core. The following machines are
 based on this chip :
@@ -65,3 +71,11 @@ firmware. Example:
 .. code-block:: bash
 
   $ qemu-system-arm -M stm32vldiscovery -kernel firmware.bin
+
+Additionally, the ``stm32f1-generic`` board supports the ``density`` option
+to select the device density line.  The following values are supported:
+``low``, ``medium``, ``high``. Example:
+
+.. code-block:: bash
+
+  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
\ No newline at end of file
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2159de3ce6..822441945c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -301,6 +301,12 @@ config STM32VLDISCOVERY
     depends on TCG && ARM
     select STM32F100_SOC
 
+config STM32F1_GENERIC
+    bool
+    default y
+    depends on TCG && ARM
+    select STM32F100_SOC
+
 config STRONGARM
     bool
     select PXA2XX
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 870ec67376..f88b5fe3c8 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -23,6 +23,7 @@ arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c'))
 arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c'))
 arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c'))
 arm_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c'))
+arm_ss.add(when: 'CONFIG_STM32F1_GENERIC', if_true: files('stm32f1_generic.c'))
 arm_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c'))
 arm_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
 arm_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c
index f7b344ba9f..c157ffd644 100644
--- a/hw/arm/stm32f100_soc.c
+++ b/hw/arm/stm32f100_soc.c
@@ -38,10 +38,11 @@
 
 static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400,
     0x40004800 };
-static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800 };
+static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
+    0x40003C00 };
 
 static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39};
-static const int spi_irq[STM_NUM_SPIS] = {35, 36};
+static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
 
 static void stm32f100_soc_initfn(Object *obj)
 {
@@ -50,17 +51,21 @@ static void stm32f100_soc_initfn(Object *obj)
 
     object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
 
+    /*
+     * All density lines feature the same number of USARTs, so they can be
+     * initialized in this function. The number of SPIs is density-dependent,
+     * though, so SPIs are initialized in stm32f100_soc_realize().
+     */
     for (i = 0; i < STM_NUM_USARTS; i++) {
         object_initialize_child(obj, "usart[*]", &s->usart[i],
                                 TYPE_STM32F2XX_USART);
     }
 
-    for (i = 0; i < STM_NUM_SPIS; i++) {
-        object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
-    }
-
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
+
+    /* Default density. May be overridden by the machine or cmdline option */
+    s->density = STM32F100_DENSITY_HIGH;
 }
 
 static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -70,6 +75,17 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
     SysBusDevice *busdev;
     int i;
 
+    if (s->density == STM32F100_DENSITY_HIGH) {
+        s->num_spis = 3;
+        s->flash_size = FLASH_SIZE_HD;
+    } else if (s->density == STM32F100_DENSITY_MEDIUM) {
+        s->num_spis = 2;
+        s->flash_size = FLASH_SIZE_MD;
+    } else {
+        s->num_spis = 2;
+        s->flash_size = FLASH_SIZE_LD;
+    }
+
     MemoryRegion *system_memory = get_system_memory();
 
     /*
@@ -101,9 +117,10 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
      * Flash starts at 0x08000000 and then is aliased to boot memory at 0x0
      */
     memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F100.flash",
-                           FLASH_SIZE, &error_fatal);
+                           s->flash_size, &error_fatal);
     memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
-                             "STM32F100.flash.alias", &s->flash, 0, FLASH_SIZE);
+                             "STM32F100.flash.alias", &s->flash, 0,
+                             s->flash_size);
     memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
     memory_region_add_subregion(system_memory, 0, &s->flash_alias);
 
@@ -137,8 +154,11 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
         sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i]));
     }
 
-    /* SPI 1 and 2 */
-    for (i = 0; i < STM_NUM_SPIS; i++) {
+    /* Initialize all SPIs supported by the selected density line */
+    for (i = 0; i < s->num_spis; i++) {
+        object_initialize_child(OBJECT(dev_soc), "spi[*]", &s->spi[i],
+                                TYPE_STM32F2XX_SPI);
+
         dev = DEVICE(&(s->spi[i]));
         if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) {
             return;
@@ -153,9 +173,14 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("timer[4]",  0x40000800, 0x400);
     create_unimplemented_device("timer[6]",  0x40001000, 0x400);
     create_unimplemented_device("timer[7]",  0x40001400, 0x400);
+    create_unimplemented_device("timer[12]", 0x40001800, 0x400);
+    create_unimplemented_device("timer[13]", 0x40001C00, 0x400);
+    create_unimplemented_device("timer[14]", 0x40002000, 0x400);
     create_unimplemented_device("RTC",       0x40002800, 0x400);
     create_unimplemented_device("WWDG",      0x40002C00, 0x400);
     create_unimplemented_device("IWDG",      0x40003000, 0x400);
+    create_unimplemented_device("UART4",     0x40004C00, 0x400);
+    create_unimplemented_device("UART5",     0x40005000, 0x400);
     create_unimplemented_device("I2C1",      0x40005400, 0x400);
     create_unimplemented_device("I2C2",      0x40005800, 0x400);
     create_unimplemented_device("BKP",       0x40006C00, 0x400);
@@ -169,12 +194,15 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("GPIOC",     0x40011000, 0x400);
     create_unimplemented_device("GPIOD",     0x40011400, 0x400);
     create_unimplemented_device("GPIOE",     0x40011800, 0x400);
+    create_unimplemented_device("GPIOF",     0x40011C00, 0x400);
+    create_unimplemented_device("GPIOG",     0x40012000, 0x400);
     create_unimplemented_device("ADC1",      0x40012400, 0x400);
     create_unimplemented_device("timer[1]",  0x40012C00, 0x400);
     create_unimplemented_device("timer[15]", 0x40014000, 0x400);
     create_unimplemented_device("timer[16]", 0x40014400, 0x400);
     create_unimplemented_device("timer[17]", 0x40014800, 0x400);
-    create_unimplemented_device("DMA",       0x40020000, 0x400);
+    create_unimplemented_device("DMA1",      0x40020000, 0x400);
+    create_unimplemented_device("DMA2",      0x40020400, 0x400);
     create_unimplemented_device("RCC",       0x40021000, 0x400);
     create_unimplemented_device("Flash Int", 0x40022000, 0x400);
     create_unimplemented_device("CRC",       0x40023000, 0x400);
@@ -185,12 +213,50 @@ static Property stm32f100_soc_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void stm32f100_soc_class_init(ObjectClass *klass, void *data)
+static char *stm32f100_get_density(Object *obj, Error **errp)
 {
-    DeviceClass *dc = DEVICE_CLASS(klass);
+    STM32F100State *s = STM32F100_SOC(obj);
+
+    switch (s->density) {
+    case STM32F100_DENSITY_LOW:
+        return g_strdup("low");
+    case STM32F100_DENSITY_MEDIUM:
+        return g_strdup("medium");
+    case STM32F100_DENSITY_HIGH:
+        return g_strdup("high");
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void stm32f100_set_density(Object *obj, const char *value, Error **errp)
+{
+    STM32F100State *s = STM32F100_SOC(obj);
+
+    if (!strcmp(value, "low")) {
+        s->density = STM32F100_DENSITY_LOW;
+    } else if (!strcmp(value, "medium")) {
+        s->density = STM32F100_DENSITY_MEDIUM;
+    } else if (!strcmp(value, "high")) {
+        s->density = STM32F100_DENSITY_HIGH;
+    } else {
+        error_setg(errp, "Invalid density value '%s'", value);
+        error_append_hint(errp, "Valid values: 'low', 'medium', 'high'\n");
+    }
+}
+
+static void stm32f100_soc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
 
     dc->realize = stm32f100_soc_realize;
     device_class_set_props(dc, stm32f100_soc_properties);
+
+    object_class_property_add_str(oc, "density", stm32f100_get_density,
+        stm32f100_set_density);
+    object_class_property_set_description(oc, "density",
+        "Set the STM32F100 density line device. "
+        "Valid values are 'low', 'medium', and 'high' (default).");
 }
 
 static const TypeInfo stm32f100_soc_info = {
diff --git a/hw/arm/stm32f1_generic.c b/hw/arm/stm32f1_generic.c
new file mode 100644
index 0000000000..63d2a58bdc
--- /dev/null
+++ b/hw/arm/stm32f1_generic.c
@@ -0,0 +1,70 @@
+/*
+ * ST generic STM32F1 board
+ *
+ * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
+ * Copyright (c) 2021 Alexandre Iooss <erdnaxe@crans.org>
+ * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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/stm32f100_soc.h"
+#include "hw/arm/boot.h"
+
+/* Main SYSCLK frequency in Hz (24MHz) */
+#define SYSCLK_FRQ 24000000ULL
+
+static void stm32f1_generic_init(MachineState *machine)
+{
+    STM32F100State *s;
+    DeviceState *dev;
+    Clock *sysclk;
+
+    /* This clock doesn't need migration because it is fixed-frequency */
+    sysclk = clock_new(OBJECT(machine), "SYSCLK");
+    clock_set_hz(sysclk, SYSCLK_FRQ);
+
+    /*
+     * Note that we don't set the "density" property so that the default
+     * value ("high") can be changed via "-global stm32f100-soc.density=..."
+     */
+    dev = qdev_new(TYPE_STM32F100_SOC);
+    qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
+    qdev_connect_clock_in(dev, "sysclk", sysclk);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+    s = STM32F100_SOC(OBJECT(dev));
+    armv7m_load_kernel(ARM_CPU(first_cpu),
+                       machine->kernel_filename,
+                       0, s->flash_size);
+}
+
+static void stm32f1_generic_machine_init(MachineClass *mc)
+{
+    mc->desc = "STM32F1 generic (Cortex-M3)";
+    mc->init = stm32f1_generic_init;
+}
+
+DEFINE_MACHINE("stm32f1-generic", stm32f1_generic_machine_init)
diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c
index 67675e952f..3c4bffe5d4 100644
--- a/hw/arm/stm32vldiscovery.c
+++ b/hw/arm/stm32vldiscovery.c
@@ -47,13 +47,14 @@ static void stm32vldiscovery_init(MachineState *machine)
     clock_set_hz(sysclk, SYSCLK_FRQ);
 
     dev = qdev_new(TYPE_STM32F100_SOC);
+    qdev_prop_set_string(dev, "density", "medium");
     qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
     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,
-                       0, FLASH_SIZE);
+                       0, FLASH_SIZE_MD);
 }
 
 static void stm32vldiscovery_machine_init(MachineClass *mc)
diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h
index 40cd415b28..5305e342e3 100644
--- a/include/hw/arm/stm32f100_soc.h
+++ b/include/hw/arm/stm32f100_soc.h
@@ -34,14 +34,24 @@
 #define TYPE_STM32F100_SOC "stm32f100-soc"
 OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
 
+/* Definitions for high-density value line devices */
 #define STM_NUM_USARTS 3
-#define STM_NUM_SPIS 2
+#define STM_NUM_SPIS 3
 
 #define FLASH_BASE_ADDRESS 0x08000000
-#define FLASH_SIZE (128 * 1024)
+#define FLASH_SIZE_LD (32 * 1024)
+#define FLASH_SIZE_MD (128 * 1024)
+#define FLASH_SIZE_HD (512 * 1024)
 #define SRAM_BASE_ADDRESS 0x20000000
 #define SRAM_SIZE (8 * 1024)
 
+/* Supported density value lines */
+typedef enum {
+    STM32F100_DENSITY_LOW,
+    STM32F100_DENSITY_MEDIUM,
+    STM32F100_DENSITY_HIGH,
+} STM32F100Density;
+
 struct STM32F100State {
     /*< private >*/
     SysBusDevice parent_obj;
@@ -60,6 +70,10 @@ struct STM32F100State {
 
     Clock *sysclk;
     Clock *refclk;
+
+    STM32F100Density density;
+    uint8_t num_spis;
+    uint32_t flash_size;
 };
 
 #endif
-- 
2.39.2 (Apple Git-143)
Re: [PATCH] STM32F100: support different density lines
Posted by Alexandre IOOSS 10 months, 3 weeks ago
On 6/20/23 00:18, Lucas Villa Real wrote:
> This patch adds support for the emulation of different density lines
> (low, medium, and high). A new class property stm32f100-soc.density=
> has been introduced to allow users to state the desired configuration.
> That property is recognized by a new machine, stm32f1-generic. The SOC
> is configured according to the following:
> 
>     density=low       32 KB FLASH, 2 SPIs
>     density=medium   128 KB FLASH, 2 SPIs
>     density=high     512 KB FLASH, 3 SPIs
> 
> With this code change we should be able to introduce richer features
> to STM32F100, such as support for FSMC (so that a machine with more
> RAM capacity can be properly emulated). FSMC is supported on high
> density line devices only.

Thanks a lot for the patches!

STM32 families look very similar to each other, it makes great sense to 
take a generic approach rather than bloating QEMU with many machines.

You patch proposes to create a "stm32f1-generic" machine. I believe we 
should rather name this machine "stm32f100-generic":
   - STM32F101 has a XL-density line, STM32F100 does not have a 
XL-density line.
   - STM32F100 high density line does not have the same maximum SRAM 
size, timers, USART numbers, clock frequencies and CEC peripherals as 
the STM32F101 high density line.

Regarding the stm32vldiscovery machine, I am not against deprecating it 
if we warn users to use stm32f100-generic with density=medium. This 
makes sense as the development board does not add anything more than 
just some buttons and LED.
Maybe "stm32vldiscovery" could become an alias for stm32f100-generic 
machine ?
@Alistair: Do you have an opinion on aliasing the old machine? Is this 
something common in QEMU?

In the long run, we should maybe rename "stm32f100-soc.c" to 
"stm32f1-soc.c" and add another class property to choose the sub-family. 
This would highly reduce potential code duplication.
STM32F1 machines could take this structure:
  - stm32f100-generic machine
    - stm32f1-soc.family=f100 stm32f1-soc.density=low
    - stm32f1-soc.family=f100 stm32f1-soc.density=medium
      (alias stm32vldiscovery)
    - stm32f1-soc.family=f100 stm32f1-soc.density=high
  - stm32f101-generic machine
    - stm32f1-soc.family=f101 stm32f1-soc.density=low
    - stm32f1-soc.family=f101 stm32f1-soc.density=medium
    - stm32f1-soc.family=f101 stm32f1-soc.density=high
    - stm32f1-soc.family=f101 stm32f1-soc.density=xl
  - stm32f102-generic machine
    - stm32f1-soc.family=f102 stm32f1-soc.density=low
    - stm32f1-soc.family=f102 stm32f1-soc.density=medium
  - stm32f103-generic machine
    - stm32f1-soc.family=f103 stm32f1-soc.density=low
    - stm32f1-soc.family=f103 stm32f1-soc.density=medium
      (alias stm32-nucleo-f103rb)
    - stm32f1-soc.family=f103 stm32f1-soc.density=high
    - stm32f1-soc.family=f103 stm32f1-soc.density=xl
@Alistair: Would such modification make also sense regarding stm32f2 and 
stm32f4 families?

Thanks,
-- 
Alexandre
Re: [PATCH] STM32F100: support different density lines
Posted by Alistair Francis 10 months, 2 weeks ago
On Mon, Jun 26, 2023 at 4:24 PM Alexandre IOOSS <erdnaxe@crans.org> wrote:
>
> On 6/20/23 00:18, Lucas Villa Real wrote:
> > This patch adds support for the emulation of different density lines
> > (low, medium, and high). A new class property stm32f100-soc.density=
> > has been introduced to allow users to state the desired configuration.
> > That property is recognized by a new machine, stm32f1-generic. The SOC
> > is configured according to the following:
> >
> >     density=low       32 KB FLASH, 2 SPIs
> >     density=medium   128 KB FLASH, 2 SPIs
> >     density=high     512 KB FLASH, 3 SPIs
> >
> > With this code change we should be able to introduce richer features
> > to STM32F100, such as support for FSMC (so that a machine with more
> > RAM capacity can be properly emulated). FSMC is supported on high
> > density line devices only.
>
> Thanks a lot for the patches!
>
> STM32 families look very similar to each other, it makes great sense to
> take a generic approach rather than bloating QEMU with many machines.
>
> You patch proposes to create a "stm32f1-generic" machine. I believe we
> should rather name this machine "stm32f100-generic":
>    - STM32F101 has a XL-density line, STM32F100 does not have a
> XL-density line.
>    - STM32F100 high density line does not have the same maximum SRAM
> size, timers, USART numbers, clock frequencies and CEC peripherals as
> the STM32F101 high density line.
>
> Regarding the stm32vldiscovery machine, I am not against deprecating it
> if we warn users to use stm32f100-generic with density=medium. This
> makes sense as the development board does not add anything more than
> just some buttons and LED.
> Maybe "stm32vldiscovery" could become an alias for stm32f100-generic
> machine ?
> @Alistair: Do you have an opinion on aliasing the old machine? Is this
> something common in QEMU?

I don't think we have any aliased machines. I think we could do
something like that as well. So the "aliased" machines end up just
being pre-configured generic machines. We just want to do it in a way
to avoid code duplication where we can.

>
> In the long run, we should maybe rename "stm32f100-soc.c" to
> "stm32f1-soc.c" and add another class property to choose the sub-family.
> This would highly reduce potential code duplication.
> STM32F1 machines could take this structure:
>   - stm32f100-generic machine
>     - stm32f1-soc.family=f100 stm32f1-soc.density=low
>     - stm32f1-soc.family=f100 stm32f1-soc.density=medium
>       (alias stm32vldiscovery)
>     - stm32f1-soc.family=f100 stm32f1-soc.density=high
>   - stm32f101-generic machine
>     - stm32f1-soc.family=f101 stm32f1-soc.density=low
>     - stm32f1-soc.family=f101 stm32f1-soc.density=medium
>     - stm32f1-soc.family=f101 stm32f1-soc.density=high
>     - stm32f1-soc.family=f101 stm32f1-soc.density=xl
>   - stm32f102-generic machine
>     - stm32f1-soc.family=f102 stm32f1-soc.density=low
>     - stm32f1-soc.family=f102 stm32f1-soc.density=medium
>   - stm32f103-generic machine
>     - stm32f1-soc.family=f103 stm32f1-soc.density=low
>     - stm32f1-soc.family=f103 stm32f1-soc.density=medium
>       (alias stm32-nucleo-f103rb)
>     - stm32f1-soc.family=f103 stm32f1-soc.density=high
>     - stm32f1-soc.family=f103 stm32f1-soc.density=xl
> @Alistair: Would such modification make also sense regarding stm32f2 and
> stm32f4 families?

I think that's also ok, but it really only makes sense to do when we
have differentiating features implemented in QEMU.

Alistair

>
> Thanks,
> --
> Alexandre
Re: [PATCH] STM32F100: support different density lines
Posted by Alistair Francis 10 months, 4 weeks ago
On Tue, Jun 20, 2023 at 8:20 AM Lucas Villa Real <lucas@osdyne.com> wrote:
>
> This patch adds support for the emulation of different density lines
> (low, medium, and high). A new class property stm32f100-soc.density=
> has been introduced to allow users to state the desired configuration.
> That property is recognized by a new machine, stm32f1-generic. The SOC
> is configured according to the following:
>
>    density=low       32 KB FLASH, 2 SPIs
>    density=medium   128 KB FLASH, 2 SPIs
>    density=high     512 KB FLASH, 3 SPIs
>
> With this code change we should be able to introduce richer features
> to STM32F100, such as support for FSMC (so that a machine with more
> RAM capacity can be properly emulated). FSMC is supported on high
> density line devices only.
>
> Signed-off-by: Lucas C. Villa Real <lucas@osdyne.com>
> ---
>  configs/devices/arm-softmmu/default.mak |  1 +
>  docs/system/arm/stm32.rst               | 14 ++++
>  hw/arm/Kconfig                          |  6 ++
>  hw/arm/meson.build                      |  1 +
>  hw/arm/stm32f100_soc.c                  | 92 +++++++++++++++++++++----
>  hw/arm/stm32f1_generic.c                | 70 +++++++++++++++++++
>  hw/arm/stm32vldiscovery.c               |  3 +-
>  include/hw/arm/stm32f100_soc.h          | 18 ++++-
>  8 files changed, 189 insertions(+), 16 deletions(-)
>  create mode 100644 hw/arm/stm32f1_generic.c
>
> diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak
> index 980c48a7d9..4f0f2e99c0 100644
> --- a/configs/devices/arm-softmmu/default.mak
> +++ b/configs/devices/arm-softmmu/default.mak
> @@ -19,6 +19,7 @@ CONFIG_ARM_VIRT=y
>  # CONFIG_NSERIES=n
>  # CONFIG_STELLARIS=n
>  # CONFIG_STM32VLDISCOVERY=n
> +# CONFIG_STM32F1_GENERIC=n
>  # CONFIG_REALVIEW=n
>  # CONFIG_VERSATILE=n
>  # CONFIG_VEXPRESS=n
> diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
> index d7265b763d..d0a3b1a7eb 100644
> --- a/docs/system/arm/stm32.rst
> +++ b/docs/system/arm/stm32.rst
> @@ -10,6 +10,12 @@ The STM32F1 series is based on ARM Cortex-M3 core. The following machines are
>  based on this chip :
>
>  - ``stm32vldiscovery``  STM32VLDISCOVERY board with STM32F100RBT6 microcontroller
> +- ``stm32f1-generic``   Generic STM32F1 board supporting low, medium and high
> +                        density devices. Low-density emulates a 32KB FLASH;
> +                        medium-density emulates a 128KB FLASH; high-density
> +                        emulates a 512KB FLASH. The density also affects the
> +                        number of peripherals exposed by QEMU for the emulated
> +                        device. See ``Boot options`` below for more details.
>
>  The STM32F2 series is based on ARM Cortex-M3 core. The following machines are
>  based on this chip :
> @@ -65,3 +71,11 @@ firmware. Example:
>  .. code-block:: bash
>
>    $ qemu-system-arm -M stm32vldiscovery -kernel firmware.bin
> +
> +Additionally, the ``stm32f1-generic`` board supports the ``density`` option
> +to select the device density line.  The following values are supported:
> +``low``, ``medium``, ``high``. Example:
> +
> +.. code-block:: bash
> +
> +  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
> \ No newline at end of file

You are missing a new line here

> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 2159de3ce6..822441945c 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -301,6 +301,12 @@ config STM32VLDISCOVERY
>      depends on TCG && ARM
>      select STM32F100_SOC
>
> +config STM32F1_GENERIC
> +    bool
> +    default y
> +    depends on TCG && ARM
> +    select STM32F100_SOC
> +
>  config STRONGARM
>      bool
>      select PXA2XX
> diff --git a/hw/arm/meson.build b/hw/arm/meson.build
> index 870ec67376..f88b5fe3c8 100644
> --- a/hw/arm/meson.build
> +++ b/hw/arm/meson.build
> @@ -23,6 +23,7 @@ arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c'))
>  arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c'))
>  arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c'))
>  arm_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c'))
> +arm_ss.add(when: 'CONFIG_STM32F1_GENERIC', if_true: files('stm32f1_generic.c'))
>  arm_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c'))
>  arm_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
>  arm_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
> diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c
> index f7b344ba9f..c157ffd644 100644
> --- a/hw/arm/stm32f100_soc.c
> +++ b/hw/arm/stm32f100_soc.c
> @@ -38,10 +38,11 @@
>
>  static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400,
>      0x40004800 };
> -static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800 };
> +static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
> +    0x40003C00 };
>
>  static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39};
> -static const int spi_irq[STM_NUM_SPIS] = {35, 36};
> +static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
>
>  static void stm32f100_soc_initfn(Object *obj)
>  {
> @@ -50,17 +51,21 @@ static void stm32f100_soc_initfn(Object *obj)
>
>      object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> +    /*
> +     * All density lines feature the same number of USARTs, so they can be
> +     * initialized in this function. The number of SPIs is density-dependent,
> +     * though, so SPIs are initialized in stm32f100_soc_realize().
> +     */
>      for (i = 0; i < STM_NUM_USARTS; i++) {
>          object_initialize_child(obj, "usart[*]", &s->usart[i],
>                                  TYPE_STM32F2XX_USART);
>      }
>
> -    for (i = 0; i < STM_NUM_SPIS; i++) {
> -        object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
> -    }
> -
>      s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
>      s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> +
> +    /* Default density. May be overridden by the machine or cmdline option */
> +    s->density = STM32F100_DENSITY_HIGH;
>  }
>
>  static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
> @@ -70,6 +75,17 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>      SysBusDevice *busdev;
>      int i;
>
> +    if (s->density == STM32F100_DENSITY_HIGH) {
> +        s->num_spis = 3;
> +        s->flash_size = FLASH_SIZE_HD;
> +    } else if (s->density == STM32F100_DENSITY_MEDIUM) {
> +        s->num_spis = 2;
> +        s->flash_size = FLASH_SIZE_MD;
> +    } else {
> +        s->num_spis = 2;
> +        s->flash_size = FLASH_SIZE_LD;
> +    }
> +
>      MemoryRegion *system_memory = get_system_memory();
>
>      /*
> @@ -101,9 +117,10 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>       * Flash starts at 0x08000000 and then is aliased to boot memory at 0x0
>       */
>      memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F100.flash",
> -                           FLASH_SIZE, &error_fatal);
> +                           s->flash_size, &error_fatal);
>      memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
> -                             "STM32F100.flash.alias", &s->flash, 0, FLASH_SIZE);
> +                             "STM32F100.flash.alias", &s->flash, 0,
> +                             s->flash_size);
>      memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
>      memory_region_add_subregion(system_memory, 0, &s->flash_alias);
>
> @@ -137,8 +154,11 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>          sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i]));
>      }
>
> -    /* SPI 1 and 2 */
> -    for (i = 0; i < STM_NUM_SPIS; i++) {
> +    /* Initialize all SPIs supported by the selected density line */
> +    for (i = 0; i < s->num_spis; i++) {
> +        object_initialize_child(OBJECT(dev_soc), "spi[*]", &s->spi[i],
> +                                TYPE_STM32F2XX_SPI);
> +
>          dev = DEVICE(&(s->spi[i]));
>          if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) {
>              return;
> @@ -153,9 +173,14 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>      create_unimplemented_device("timer[4]",  0x40000800, 0x400);
>      create_unimplemented_device("timer[6]",  0x40001000, 0x400);
>      create_unimplemented_device("timer[7]",  0x40001400, 0x400);
> +    create_unimplemented_device("timer[12]", 0x40001800, 0x400);
> +    create_unimplemented_device("timer[13]", 0x40001C00, 0x400);
> +    create_unimplemented_device("timer[14]", 0x40002000, 0x400);
>      create_unimplemented_device("RTC",       0x40002800, 0x400);
>      create_unimplemented_device("WWDG",      0x40002C00, 0x400);
>      create_unimplemented_device("IWDG",      0x40003000, 0x400);
> +    create_unimplemented_device("UART4",     0x40004C00, 0x400);
> +    create_unimplemented_device("UART5",     0x40005000, 0x400);
>      create_unimplemented_device("I2C1",      0x40005400, 0x400);
>      create_unimplemented_device("I2C2",      0x40005800, 0x400);
>      create_unimplemented_device("BKP",       0x40006C00, 0x400);
> @@ -169,12 +194,15 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>      create_unimplemented_device("GPIOC",     0x40011000, 0x400);
>      create_unimplemented_device("GPIOD",     0x40011400, 0x400);
>      create_unimplemented_device("GPIOE",     0x40011800, 0x400);
> +    create_unimplemented_device("GPIOF",     0x40011C00, 0x400);
> +    create_unimplemented_device("GPIOG",     0x40012000, 0x400);
>      create_unimplemented_device("ADC1",      0x40012400, 0x400);
>      create_unimplemented_device("timer[1]",  0x40012C00, 0x400);
>      create_unimplemented_device("timer[15]", 0x40014000, 0x400);
>      create_unimplemented_device("timer[16]", 0x40014400, 0x400);
>      create_unimplemented_device("timer[17]", 0x40014800, 0x400);
> -    create_unimplemented_device("DMA",       0x40020000, 0x400);
> +    create_unimplemented_device("DMA1",      0x40020000, 0x400);
> +    create_unimplemented_device("DMA2",      0x40020400, 0x400);
>      create_unimplemented_device("RCC",       0x40021000, 0x400);
>      create_unimplemented_device("Flash Int", 0x40022000, 0x400);
>      create_unimplemented_device("CRC",       0x40023000, 0x400);
> @@ -185,12 +213,50 @@ static Property stm32f100_soc_properties[] = {
>      DEFINE_PROP_END_OF_LIST(),
>  };
>
> -static void stm32f100_soc_class_init(ObjectClass *klass, void *data)
> +static char *stm32f100_get_density(Object *obj, Error **errp)
>  {
> -    DeviceClass *dc = DEVICE_CLASS(klass);
> +    STM32F100State *s = STM32F100_SOC(obj);
> +
> +    switch (s->density) {
> +    case STM32F100_DENSITY_LOW:
> +        return g_strdup("low");
> +    case STM32F100_DENSITY_MEDIUM:
> +        return g_strdup("medium");
> +    case STM32F100_DENSITY_HIGH:
> +        return g_strdup("high");
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
> +static void stm32f100_set_density(Object *obj, const char *value, Error **errp)
> +{
> +    STM32F100State *s = STM32F100_SOC(obj);
> +
> +    if (!strcmp(value, "low")) {
> +        s->density = STM32F100_DENSITY_LOW;
> +    } else if (!strcmp(value, "medium")) {
> +        s->density = STM32F100_DENSITY_MEDIUM;
> +    } else if (!strcmp(value, "high")) {
> +        s->density = STM32F100_DENSITY_HIGH;
> +    } else {
> +        error_setg(errp, "Invalid density value '%s'", value);
> +        error_append_hint(errp, "Valid values: 'low', 'medium', 'high'\n");
> +    }
> +}
> +
> +static void stm32f100_soc_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
>
>      dc->realize = stm32f100_soc_realize;
>      device_class_set_props(dc, stm32f100_soc_properties);
> +
> +    object_class_property_add_str(oc, "density", stm32f100_get_density,
> +        stm32f100_set_density);
> +    object_class_property_set_description(oc, "density",
> +        "Set the STM32F100 density line device. "
> +        "Valid values are 'low', 'medium', and 'high' (default).");
>  }

This should be split into a separate commit from adding the machine

>
>  static const TypeInfo stm32f100_soc_info = {
> diff --git a/hw/arm/stm32f1_generic.c b/hw/arm/stm32f1_generic.c
> new file mode 100644
> index 0000000000..63d2a58bdc
> --- /dev/null
> +++ b/hw/arm/stm32f1_generic.c
> @@ -0,0 +1,70 @@
> +/*
> + * ST generic STM32F1 board
> + *
> + * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
> + * Copyright (c) 2021 Alexandre Iooss <erdnaxe@crans.org>
> + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#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/stm32f100_soc.h"
> +#include "hw/arm/boot.h"
> +
> +/* Main SYSCLK frequency in Hz (24MHz) */
> +#define SYSCLK_FRQ 24000000ULL
> +
> +static void stm32f1_generic_init(MachineState *machine)
> +{
> +    STM32F100State *s;
> +    DeviceState *dev;
> +    Clock *sysclk;
> +
> +    /* This clock doesn't need migration because it is fixed-frequency */
> +    sysclk = clock_new(OBJECT(machine), "SYSCLK");
> +    clock_set_hz(sysclk, SYSCLK_FRQ);
> +
> +    /*
> +     * Note that we don't set the "density" property so that the default
> +     * value ("high") can be changed via "-global stm32f100-soc.density=..."
> +     */
> +    dev = qdev_new(TYPE_STM32F100_SOC);
> +    qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
> +    qdev_connect_clock_in(dev, "sysclk", sysclk);
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +
> +    s = STM32F100_SOC(OBJECT(dev));
> +    armv7m_load_kernel(ARM_CPU(first_cpu),
> +                       machine->kernel_filename,
> +                       0, s->flash_size);
> +}

Isn't this exactly the same as the stm32vldiscovery board? Which is
already very similar to the netduino2 machine. I'm not sure we need
another machine.

It could make more sense to deprecate the stm32vldiscovery machine and
replace it with this generic one. At least we could keep everything in
the one file and reuse a lot of the code.

Alistair

> +
> +static void stm32f1_generic_machine_init(MachineClass *mc)
> +{
> +    mc->desc = "STM32F1 generic (Cortex-M3)";
> +    mc->init = stm32f1_generic_init;
> +}
> +
> +DEFINE_MACHINE("stm32f1-generic", stm32f1_generic_machine_init)
> diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c
> index 67675e952f..3c4bffe5d4 100644
> --- a/hw/arm/stm32vldiscovery.c
> +++ b/hw/arm/stm32vldiscovery.c
> @@ -47,13 +47,14 @@ static void stm32vldiscovery_init(MachineState *machine)
>      clock_set_hz(sysclk, SYSCLK_FRQ);
>
>      dev = qdev_new(TYPE_STM32F100_SOC);
> +    qdev_prop_set_string(dev, "density", "medium");
>      qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
>      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,
> -                       0, FLASH_SIZE);
> +                       0, FLASH_SIZE_MD);
>  }
>
>  static void stm32vldiscovery_machine_init(MachineClass *mc)
> diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h
> index 40cd415b28..5305e342e3 100644
> --- a/include/hw/arm/stm32f100_soc.h
> +++ b/include/hw/arm/stm32f100_soc.h
> @@ -34,14 +34,24 @@
>  #define TYPE_STM32F100_SOC "stm32f100-soc"
>  OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
>
> +/* Definitions for high-density value line devices */
>  #define STM_NUM_USARTS 3
> -#define STM_NUM_SPIS 2
> +#define STM_NUM_SPIS 3
>
>  #define FLASH_BASE_ADDRESS 0x08000000
> -#define FLASH_SIZE (128 * 1024)
> +#define FLASH_SIZE_LD (32 * 1024)
> +#define FLASH_SIZE_MD (128 * 1024)
> +#define FLASH_SIZE_HD (512 * 1024)
>  #define SRAM_BASE_ADDRESS 0x20000000
>  #define SRAM_SIZE (8 * 1024)
>
> +/* Supported density value lines */
> +typedef enum {
> +    STM32F100_DENSITY_LOW,
> +    STM32F100_DENSITY_MEDIUM,
> +    STM32F100_DENSITY_HIGH,
> +} STM32F100Density;
> +
>  struct STM32F100State {
>      /*< private >*/
>      SysBusDevice parent_obj;
> @@ -60,6 +70,10 @@ struct STM32F100State {
>
>      Clock *sysclk;
>      Clock *refclk;
> +
> +    STM32F100Density density;
> +    uint8_t num_spis;
> +    uint32_t flash_size;
>  };
>
>  #endif
> --
> 2.39.2 (Apple Git-143)
>
>
Re: [PATCH] STM32F100: support different density lines
Posted by Lucas C. Villa Real 10 months, 3 weeks ago
On Thu, Jun 22, 2023 at 10:30 PM Alistair Francis <alistair23@gmail.com>
wrote:

> > +
> > +    object_class_property_add_str(oc, "density", stm32f100_get_density,
> > +        stm32f100_set_density);
> > +    object_class_property_set_description(oc, "density",
> > +        "Set the STM32F100 density line device. "
> > +        "Valid values are 'low', 'medium', and 'high' (default).");
> >  }
>
> This should be split into a separate commit from adding the machine
>

Ok, I'll do that.


> Isn't this exactly the same as the stm32vldiscovery board? Which is
> already very similar to the netduino2 machine. I'm not sure we need
> another machine.
>
> It could make more sense to deprecate the stm32vldiscovery machine and
> replace it with this generic one. At least we could keep everything in
> the one file and reuse a lot of the code.
>

What is the protocol for deprecating a machine? Should I just submit a
patch that removes it along with the corresponding entry in the MAINTAINERS
file? Should I coordinate that offline with the maintainer of the machine
that's to be retired?

Thanks,
Lucas
Re: [PATCH] STM32F100: support different density lines
Posted by Alistair Francis 10 months, 2 weeks ago
On Sun, Jun 25, 2023 at 1:43 PM Lucas C. Villa Real <lucas@osdyne.com> wrote:
>
> On Thu, Jun 22, 2023 at 10:30 PM Alistair Francis <alistair23@gmail.com> wrote:
>>
>> > +
>> > +    object_class_property_add_str(oc, "density", stm32f100_get_density,
>> > +        stm32f100_set_density);
>> > +    object_class_property_set_description(oc, "density",
>> > +        "Set the STM32F100 density line device. "
>> > +        "Valid values are 'low', 'medium', and 'high' (default).");
>> >  }
>>
>> This should be split into a separate commit from adding the machine
>
>
> Ok, I'll do that.
>
>>
>> Isn't this exactly the same as the stm32vldiscovery board? Which is
>> already very similar to the netduino2 machine. I'm not sure we need
>> another machine.
>>
>> It could make more sense to deprecate the stm32vldiscovery machine and
>> replace it with this generic one. At least we could keep everything in
>> the one file and reuse a lot of the code.
>
>
> What is the protocol for deprecating a machine? Should I just submit a patch that removes it along with the corresponding entry in the MAINTAINERS file? Should I coordinate that offline with the maintainer of the machine that's to be retired?

There is an official deprecation policy:
https://www.qemu.org/docs/master/about/deprecated.html#deprecated-features

The board will be listed as deprecated for two releases and then removed.

Alistair

>
> Thanks,
> Lucas
[PATCH] STM32F100: add support for external memory via FSMC
Posted by Lucas Villa Real 11 months ago
Add support for FSMC on high-density STM32F100 devices and enable
mapping of additional memory via the `-m SIZE` command-line option.
FSMC Bank1 can address up to 4x64MB of PSRAM memory at 0x60000000.

RCC is needed to enable peripheral clock for FSMC; this commit
implements support for RCC through the MMIO interface.

Last, high-density devices support up to 32KB of static SRAM, so
adjust SRAM_SIZE accordingly.

Signed-off-by: Lucas C. Villa Real <lucas@odsyne.com>
---
 docs/system/arm/stm32.rst        |  12 ++-
 hw/arm/Kconfig                   |   1 +
 hw/arm/stm32f100_soc.c           | 102 +++++++++++++++++++-
 hw/arm/stm32f1_generic.c         |  12 +++
 hw/misc/Kconfig                  |   3 +
 hw/misc/meson.build              |   1 +
 hw/misc/stm32f1xx_fsmc.c         | 155 +++++++++++++++++++++++++++++++
 include/hw/arm/stm32f100_soc.h   |  24 ++++-
 include/hw/misc/stm32f1xx_fsmc.h |  62 +++++++++++++
 9 files changed, 368 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/stm32f1xx_fsmc.c
 create mode 100644 include/hw/misc/stm32f1xx_fsmc.h

diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
index d0a3b1a7eb..40de58ed04 100644
--- a/docs/system/arm/stm32.rst
+++ b/docs/system/arm/stm32.rst
@@ -40,6 +40,8 @@ Supported devices
  * SPI controller
  * System configuration (SYSCFG)
  * Timer controller (TIMER)
+ * Reset and Clock Controller (RCC)
+ * Flexible static memory controller (FSMC)
 
 Missing devices
 ---------------
@@ -57,7 +59,6 @@ Missing devices
  * Power supply configuration (PWR)
  * Random Number Generator (RNG)
  * Real-Time Clock (RTC) controller
- * Reset and Clock Controller (RCC)
  * Secure Digital Input/Output (SDIO) interface
  * USB OTG
  * Watchdog controller (IWDG, WWDG)
@@ -78,4 +79,11 @@ to select the device density line.  The following values are supported:
 
 .. code-block:: bash
 
-  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
\ No newline at end of file
+  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
+
+High-density devices can also enable up to 256 MB of external memory using
+the `-m SIZE` option. The memory is mapped at address 0x60000000. Example:
+ 
+.. code-block:: bash
+
+  $ qemu-system-arm -M stm32f1-generic -m 64M ...
\ No newline at end of file
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 822441945c..dd48068108 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -433,6 +433,7 @@ config RASPI
 config STM32F100_SOC
     bool
     select ARM_V7M
+    select STM32F1XX_FSMC
     select STM32F2XX_USART
     select STM32F2XX_SPI
 
diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c
index c157ffd644..a2b863d309 100644
--- a/hw/arm/stm32f100_soc.c
+++ b/hw/arm/stm32f100_soc.c
@@ -26,6 +26,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#include "qemu/log.h"
 #include "hw/arm/boot.h"
 #include "exec/address-spaces.h"
 #include "hw/arm/stm32f100_soc.h"
@@ -40,9 +41,85 @@ static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400,
     0x40004800 };
 static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
     0x40003C00 };
+static const uint32_t fsmc_addr = 0xA0000000;
 
 static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39};
 static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
+static const int fsmc_irq = 48;
+
+static uint64_t stm32f100_rcc_read(void *h, hwaddr offset, unsigned size)
+{
+    STM32F100State *s = (STM32F100State *) h;
+    switch (offset) {
+    case 0x00:
+        return s->rcc.cr;
+    case 0x04:
+        return s->rcc.cfgr;
+    case 0x08:
+        return s->rcc.cir;
+    case 0x0C:
+        return s->rcc.apb2rstr;
+    case 0x10:
+        return s->rcc.apb1rstr;
+    case 0x14:
+        return s->rcc.ahbenr;
+    case 0x18:
+        return s->rcc.apb2enr;
+    case 0x1C:
+        return s->rcc.apb1enr;
+    case 0x20:
+        return s->rcc.bdcr;
+    case 0x24:
+        return s->rcc.csr;
+    case 0x2C:
+        return s->rcc.cfgr2;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
+    }
+    return 0;
+}
+
+static void stm32f100_rcc_write(void *h, hwaddr offset, uint64_t value64,
+                                unsigned size)
+{
+    STM32F100State *s = (STM32F100State *) h;
+    uint32_t value = value64 & 0xffffffff;
+
+    switch (offset) {
+    case 0x00:
+        s->rcc.cr = value;
+    case 0x04:
+        s->rcc.cfgr = value;
+    case 0x08:
+        s->rcc.cir = value;
+    case 0x0C:
+        s->rcc.apb2rstr = value;
+    case 0x10:
+        s->rcc.apb1rstr = value;
+    case 0x14:
+        s->rcc.ahbenr = value;
+    case 0x18:
+        s->rcc.apb2enr = value;
+    case 0x1C:
+        s->rcc.apb1enr = value;
+    case 0x20:
+        s->rcc.bdcr = value;
+    case 0x24:
+        s->rcc.csr = value;
+    case 0x2C:
+        s->rcc.cfgr2 = value;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
+    }
+}
+
+static const MemoryRegionOps stm32f100_rcc_ops = {
+    .read = stm32f100_rcc_read,
+    .write = stm32f100_rcc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
 
 static void stm32f100_soc_initfn(Object *obj)
 {
@@ -66,6 +143,11 @@ static void stm32f100_soc_initfn(Object *obj)
 
     /* Default density. May be overridden by the machine or cmdline option */
     s->density = STM32F100_DENSITY_HIGH;
+
+    memset(&s->rcc, 0, sizeof(s->rcc));
+    s->rcc.cr = 0x00000083;
+    s->rcc.ahbenr = 0x00000014;
+    s->rcc.csr = 0x0C000000;
 }
 
 static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -168,6 +250,25 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
         sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i]));
     }
 
+    /* Declare a simple memory-mapped I/O region for RCC */
+    memory_region_init_io(&s->iomem, OBJECT(dev_soc), &stm32f100_rcc_ops, s,
+                          "STM32F100.mmio.rcc", 0x400);
+    memory_region_add_subregion(system_memory, 0x40021000, &s->iomem);
+
+    /* Declare an I/O region for FSMC */
+    if (s->density == STM32F100_DENSITY_HIGH) {
+        object_initialize_child(OBJECT(dev_soc), "fsmc", &s->fsmc,
+                                TYPE_STM32F1XX_FSMC);
+
+        dev = DEVICE(&s->fsmc);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsmc), errp)) {
+            return;
+        }
+        busdev = SYS_BUS_DEVICE(dev);
+        sysbus_mmio_map(busdev, 0, fsmc_addr);
+        sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, fsmc_irq));
+    }
+
     create_unimplemented_device("timer[2]",  0x40000000, 0x400);
     create_unimplemented_device("timer[3]",  0x40000400, 0x400);
     create_unimplemented_device("timer[4]",  0x40000800, 0x400);
@@ -203,7 +304,6 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("timer[17]", 0x40014800, 0x400);
     create_unimplemented_device("DMA1",      0x40020000, 0x400);
     create_unimplemented_device("DMA2",      0x40020400, 0x400);
-    create_unimplemented_device("RCC",       0x40021000, 0x400);
     create_unimplemented_device("Flash Int", 0x40022000, 0x400);
     create_unimplemented_device("CRC",       0x40023000, 0x400);
 }
diff --git a/hw/arm/stm32f1_generic.c b/hw/arm/stm32f1_generic.c
index 63d2a58bdc..6420542f1b 100644
--- a/hw/arm/stm32f1_generic.c
+++ b/hw/arm/stm32f1_generic.c
@@ -30,6 +30,7 @@
 #include "hw/qdev-properties.h"
 #include "hw/qdev-clock.h"
 #include "qemu/error-report.h"
+#include "exec/address-spaces.h"
 #include "hw/arm/stm32f100_soc.h"
 #include "hw/arm/boot.h"
 
@@ -38,6 +39,7 @@
 
 static void stm32f1_generic_init(MachineState *machine)
 {
+    MemoryRegion *psram1;
     STM32F100State *s;
     DeviceState *dev;
     Clock *sysclk;
@@ -59,6 +61,16 @@ static void stm32f1_generic_init(MachineState *machine)
     armv7m_load_kernel(ARM_CPU(first_cpu),
                        machine->kernel_filename,
                        0, s->flash_size);
+
+    /* Allow assigning more RAM via FSMC on high-density devices */
+    if (s->density == STM32F100_DENSITY_HIGH) {
+        assert(machine->ram_size <= PSRAM1_SIZE);
+        psram1 = g_new(MemoryRegion, 1);
+        memory_region_init_ram(psram1, NULL, "STM32F1-generic.psram1",
+                               machine->ram_size, &error_fatal);
+        memory_region_add_subregion(get_system_memory(),
+                                    PSRAM1_BASE_ADDRESS, psram1);
+    }
 }
 
 static void stm32f1_generic_machine_init(MachineClass *mc)
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index e4c2149175..002d34a123 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -73,6 +73,9 @@ config IMX
     select SSI
     select USB_EHCI_SYSBUS
 
+config STM32F1XX_FSMC
+    bool
+
 config STM32F2XX_SYSCFG
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 78ca857c9d..a0b0786fd3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -97,6 +97,7 @@ softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
   'xlnx-versal-xramc.c',
   'xlnx-versal-pmc-iou-slcr.c',
 ))
+softmmu_ss.add(when: 'CONFIG_STM32F1XX_FSMC', if_true: files('stm32f1xx_fsmc.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
diff --git a/hw/misc/stm32f1xx_fsmc.c b/hw/misc/stm32f1xx_fsmc.c
new file mode 100644
index 0000000000..8a9618bb31
--- /dev/null
+++ b/hw/misc/stm32f1xx_fsmc.c
@@ -0,0 +1,155 @@
+/*
+ * STM32F1XX FSMC
+ *
+ * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/stm32f1xx_fsmc.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static void stm32f1xx_fsmc_reset(DeviceState *dev)
+{
+    STM32F1XXFsmcState *s = STM32F1XX_FSMC(dev);
+
+    s->fsmc_bcr[0] = 0x000030DB;
+    for (int i=1; i<4; ++i)
+        s->fsmc_bcr[i] = 0x000030D2;
+    for (int i=0; i<4; ++i) {
+        s->fsmc_btr[i] = 0xffffffff;
+        s->fsmc_bwtr[i] = 0xffffffff;
+    }
+}
+
+static uint64_t stm32f1xx_fsmc_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    STM32F1XXFsmcState *s = opaque;
+
+    switch (addr) {
+    case FSMC_BCR1:
+        return s->fsmc_bcr[0];
+    case FSMC_BCR2:
+        return s->fsmc_bcr[1];
+    case FSMC_BCR3:
+        return s->fsmc_bcr[2];
+    case FSMC_BCR4:
+        return s->fsmc_bcr[3];
+    case FSMC_BTR1:
+        return s->fsmc_btr[0];
+    case FSMC_BTR2:
+        return s->fsmc_btr[1];
+    case FSMC_BTR3:
+        return s->fsmc_btr[2];
+    case FSMC_BTR4:
+        return s->fsmc_btr[3];
+    case FSMC_BWTR1:
+        return s->fsmc_bwtr[0];
+    case FSMC_BWTR2:
+        return s->fsmc_bwtr[1];
+    case FSMC_BWTR3:
+        return s->fsmc_bwtr[2];
+    case FSMC_BWTR4:
+        return s->fsmc_bwtr[3];
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+
+    return 0;
+}
+
+static void stm32f1xx_fsmc_write(void *opaque, hwaddr addr,
+                       uint64_t val64, unsigned int size)
+{
+    STM32F1XXFsmcState *s = opaque;
+    uint32_t value = val64 & 0xffffffff;
+
+    switch (addr) {
+    case FSMC_BCR1:
+        s->fsmc_bcr[0] = value;
+    case FSMC_BCR2:
+        s->fsmc_bcr[1] = value;
+    case FSMC_BCR3:
+        s->fsmc_bcr[2] = value;
+    case FSMC_BCR4:
+        s->fsmc_bcr[3] = value;
+    case FSMC_BTR1:
+        s->fsmc_btr[0] = value;
+    case FSMC_BTR2:
+        s->fsmc_btr[1] = value;
+    case FSMC_BTR3:
+        s->fsmc_btr[2] = value;
+    case FSMC_BTR4:
+        s->fsmc_btr[3] = value;
+    case FSMC_BWTR1:
+        s->fsmc_bwtr[0] = value;
+    case FSMC_BWTR2:
+        s->fsmc_bwtr[1] = value;
+    case FSMC_BWTR3:
+        s->fsmc_bwtr[2] = value;
+    case FSMC_BWTR4:
+        s->fsmc_bwtr[3] = value;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+}
+
+static const MemoryRegionOps stm32f1xx_fsmc_ops = {
+    .read = stm32f1xx_fsmc_read,
+    .write = stm32f1xx_fsmc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32f1xx_fsmc_init(Object *obj)
+{
+    STM32F1XXFsmcState *s = STM32F1XX_FSMC(obj);
+
+    memory_region_init_io(&s->mmio, obj, &stm32f1xx_fsmc_ops, s,
+                          TYPE_STM32F1XX_FSMC, 0x400);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static void stm32f1xx_fsmc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->reset = stm32f1xx_fsmc_reset;
+}
+
+static const TypeInfo stm32f1xx_fsmc_info = {
+    .name          = TYPE_STM32F1XX_FSMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32F1XXFsmcState),
+    .instance_init = stm32f1xx_fsmc_init,
+    .class_init    = stm32f1xx_fsmc_class_init,
+};
+
+static void stm32f1xx_fsmc_register_types(void)
+{
+    type_register_static(&stm32f1xx_fsmc_info);
+}
+
+type_init(stm32f1xx_fsmc_register_types)
diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h
index 5305e342e3..0ce2439e38 100644
--- a/include/hw/arm/stm32f100_soc.h
+++ b/include/hw/arm/stm32f100_soc.h
@@ -26,6 +26,7 @@
 #define HW_ARM_STM32F100_SOC_H
 
 #include "hw/char/stm32f2xx_usart.h"
+#include "hw/misc/stm32f1xx_fsmc.h"
 #include "hw/ssi/stm32f2xx_spi.h"
 #include "hw/arm/armv7m.h"
 #include "qom/object.h"
@@ -42,8 +43,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
 #define FLASH_SIZE_LD (32 * 1024)
 #define FLASH_SIZE_MD (128 * 1024)
 #define FLASH_SIZE_HD (512 * 1024)
+
 #define SRAM_BASE_ADDRESS 0x20000000
-#define SRAM_SIZE (8 * 1024)
+#define SRAM_SIZE (32 * 1024)
+
+#define PSRAM1_BASE_ADDRESS 0x60000000
+#define PSRAM1_SIZE (256 * 1024 * 1024)
 
 /* Supported density value lines */
 typedef enum {
@@ -52,6 +57,20 @@ typedef enum {
     STM32F100_DENSITY_HIGH,
 } STM32F100Density;
 
+typedef struct {
+    uint32_t cr;
+    uint32_t cfgr;
+    uint32_t cir;
+    uint32_t apb2rstr;
+    uint32_t apb1rstr;
+    uint32_t ahbenr;
+    uint32_t apb2enr;
+    uint32_t apb1enr;
+    uint32_t bdcr;
+    uint32_t csr;
+    uint32_t cfgr2;
+} STM32F1XXRccState;
+
 struct STM32F100State {
     /*< private >*/
     SysBusDevice parent_obj;
@@ -63,10 +82,13 @@ struct STM32F100State {
 
     STM32F2XXUsartState usart[STM_NUM_USARTS];
     STM32F2XXSPIState spi[STM_NUM_SPIS];
+    STM32F1XXFsmcState fsmc;
+    STM32F1XXRccState rcc;
 
     MemoryRegion sram;
     MemoryRegion flash;
     MemoryRegion flash_alias;
+    MemoryRegion iomem;
 
     Clock *sysclk;
     Clock *refclk;
diff --git a/include/hw/misc/stm32f1xx_fsmc.h b/include/hw/misc/stm32f1xx_fsmc.h
new file mode 100644
index 0000000000..1432d2da9e
--- /dev/null
+++ b/include/hw/misc/stm32f1xx_fsmc.h
@@ -0,0 +1,62 @@
+/*
+ * STM32F1xx FSMC
+ *
+ * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_STM32F1XX_FSMC_H
+#define HW_STM32F1XX_FSMC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define FSMC_BCR1  0x000
+#define FSMC_BCR2  0x008
+#define FSMC_BCR3  0x010
+#define FSMC_BCR4  0x018
+#define FSMC_BTR1  0x004
+#define FSMC_BTR2  0x00C
+#define FSMC_BTR3  0x014
+#define FSMC_BTR4  0x01C
+#define FSMC_BWTR1 0x104
+#define FSMC_BWTR2 0x10C
+#define FSMC_BWTR3 0x114
+#define FSMC_BWTR4 0x11C
+#define NUM_BANKS  4
+
+#define TYPE_STM32F1XX_FSMC "stm32f1xx-fsmc"
+OBJECT_DECLARE_SIMPLE_TYPE(STM32F1XXFsmcState, STM32F1XX_FSMC)
+
+struct STM32F1XXFsmcState {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion mmio;
+
+    uint32_t fsmc_bcr[NUM_BANKS];
+    uint32_t fsmc_btr[NUM_BANKS];
+    uint32_t fsmc_bwtr[NUM_BANKS];
+
+    qemu_irq irq;
+};
+
+#endif
-- 
2.39.2 (Apple Git-143)
Re: [PATCH] STM32F100: add support for external memory via FSMC
Posted by Alistair Francis 10 months, 4 weeks ago
On Wed, Jun 21, 2023 at 5:44 AM Lucas Villa Real <lucas@osdyne.com> wrote:
>
> Add support for FSMC on high-density STM32F100 devices and enable
> mapping of additional memory via the `-m SIZE` command-line option.
> FSMC Bank1 can address up to 4x64MB of PSRAM memory at 0x60000000.

Thanks for the patches!

>
> RCC is needed to enable peripheral clock for FSMC; this commit
> implements support for RCC through the MMIO interface.

This should be a separate commit. The idea is to break commits up as
small as possible and send a patch series, this makes review much
easier. Each new feature should be its own commit.

>
> Last, high-density devices support up to 32KB of static SRAM, so
> adjust SRAM_SIZE accordingly.

Also, can you include a link to the documentation in the commit message?

>
> Signed-off-by: Lucas C. Villa Real <lucas@odsyne.com>
> ---
>  docs/system/arm/stm32.rst        |  12 ++-
>  hw/arm/Kconfig                   |   1 +
>  hw/arm/stm32f100_soc.c           | 102 +++++++++++++++++++-
>  hw/arm/stm32f1_generic.c         |  12 +++
>  hw/misc/Kconfig                  |   3 +
>  hw/misc/meson.build              |   1 +
>  hw/misc/stm32f1xx_fsmc.c         | 155 +++++++++++++++++++++++++++++++
>  include/hw/arm/stm32f100_soc.h   |  24 ++++-
>  include/hw/misc/stm32f1xx_fsmc.h |  62 +++++++++++++
>  9 files changed, 368 insertions(+), 4 deletions(-)
>  create mode 100644 hw/misc/stm32f1xx_fsmc.c
>  create mode 100644 include/hw/misc/stm32f1xx_fsmc.h
>
> diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
> index d0a3b1a7eb..40de58ed04 100644
> --- a/docs/system/arm/stm32.rst
> +++ b/docs/system/arm/stm32.rst
> @@ -40,6 +40,8 @@ Supported devices
>   * SPI controller
>   * System configuration (SYSCFG)
>   * Timer controller (TIMER)
> + * Reset and Clock Controller (RCC)
> + * Flexible static memory controller (FSMC)
>
>  Missing devices
>  ---------------
> @@ -57,7 +59,6 @@ Missing devices
>   * Power supply configuration (PWR)
>   * Random Number Generator (RNG)
>   * Real-Time Clock (RTC) controller
> - * Reset and Clock Controller (RCC)
>   * Secure Digital Input/Output (SDIO) interface
>   * USB OTG
>   * Watchdog controller (IWDG, WWDG)
> @@ -78,4 +79,11 @@ to select the device density line.  The following values are supported:
>
>  .. code-block:: bash
>
> -  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
> \ No newline at end of file
> +  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
> +
> +High-density devices can also enable up to 256 MB of external memory using
> +the `-m SIZE` option. The memory is mapped at address 0x60000000. Example:
> +
> +.. code-block:: bash
> +
> +  $ qemu-system-arm -M stm32f1-generic -m 64M ...
> \ No newline at end of file
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 822441945c..dd48068108 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -433,6 +433,7 @@ config RASPI
>  config STM32F100_SOC
>      bool
>      select ARM_V7M
> +    select STM32F1XX_FSMC
>      select STM32F2XX_USART
>      select STM32F2XX_SPI
>
> diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c
> index c157ffd644..a2b863d309 100644
> --- a/hw/arm/stm32f100_soc.c
> +++ b/hw/arm/stm32f100_soc.c
> @@ -26,6 +26,7 @@
>  #include "qemu/osdep.h"
>  #include "qapi/error.h"
>  #include "qemu/module.h"
> +#include "qemu/log.h"
>  #include "hw/arm/boot.h"
>  #include "exec/address-spaces.h"
>  #include "hw/arm/stm32f100_soc.h"
> @@ -40,9 +41,85 @@ static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400,
>      0x40004800 };
>  static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
>      0x40003C00 };
> +static const uint32_t fsmc_addr = 0xA0000000;
>
>  static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39};
>  static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
> +static const int fsmc_irq = 48;
> +
> +static uint64_t stm32f100_rcc_read(void *h, hwaddr offset, unsigned size)
> +{
> +    STM32F100State *s = (STM32F100State *) h;
> +    switch (offset) {
> +    case 0x00:
> +        return s->rcc.cr;
> +    case 0x04:
> +        return s->rcc.cfgr;
> +    case 0x08:
> +        return s->rcc.cir;
> +    case 0x0C:
> +        return s->rcc.apb2rstr;
> +    case 0x10:
> +        return s->rcc.apb1rstr;
> +    case 0x14:
> +        return s->rcc.ahbenr;
> +    case 0x18:
> +        return s->rcc.apb2enr;
> +    case 0x1C:
> +        return s->rcc.apb1enr;
> +    case 0x20:
> +        return s->rcc.bdcr;
> +    case 0x24:
> +        return s->rcc.csr;
> +    case 0x2C:
> +        return s->rcc.cfgr2;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
> +    }
> +    return 0;
> +}
> +
> +static void stm32f100_rcc_write(void *h, hwaddr offset, uint64_t value64,
> +                                unsigned size)
> +{
> +    STM32F100State *s = (STM32F100State *) h;
> +    uint32_t value = value64 & 0xffffffff;
> +
> +    switch (offset) {
> +    case 0x00:
> +        s->rcc.cr = value;
> +    case 0x04:
> +        s->rcc.cfgr = value;
> +    case 0x08:
> +        s->rcc.cir = value;
> +    case 0x0C:
> +        s->rcc.apb2rstr = value;
> +    case 0x10:
> +        s->rcc.apb1rstr = value;
> +    case 0x14:
> +        s->rcc.ahbenr = value;
> +    case 0x18:
> +        s->rcc.apb2enr = value;
> +    case 0x1C:
> +        s->rcc.apb1enr = value;
> +    case 0x20:
> +        s->rcc.bdcr = value;
> +    case 0x24:
> +        s->rcc.csr = value;
> +    case 0x2C:
> +        s->rcc.cfgr2 = value;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
> +    }
> +}
> +
> +static const MemoryRegionOps stm32f100_rcc_ops = {
> +    .read = stm32f100_rcc_read,
> +    .write = stm32f100_rcc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};

This should be its own file and device that is included

Alistair

>
>  static void stm32f100_soc_initfn(Object *obj)
>  {
> @@ -66,6 +143,11 @@ static void stm32f100_soc_initfn(Object *obj)
>
>      /* Default density. May be overridden by the machine or cmdline option */
>      s->density = STM32F100_DENSITY_HIGH;
> +
> +    memset(&s->rcc, 0, sizeof(s->rcc));
> +    s->rcc.cr = 0x00000083;
> +    s->rcc.ahbenr = 0x00000014;
> +    s->rcc.csr = 0x0C000000;
>  }
>
>  static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
> @@ -168,6 +250,25 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>          sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i]));
>      }
>
> +    /* Declare a simple memory-mapped I/O region for RCC */
> +    memory_region_init_io(&s->iomem, OBJECT(dev_soc), &stm32f100_rcc_ops, s,
> +                          "STM32F100.mmio.rcc", 0x400);
> +    memory_region_add_subregion(system_memory, 0x40021000, &s->iomem);
> +
> +    /* Declare an I/O region for FSMC */
> +    if (s->density == STM32F100_DENSITY_HIGH) {
> +        object_initialize_child(OBJECT(dev_soc), "fsmc", &s->fsmc,
> +                                TYPE_STM32F1XX_FSMC);
> +
> +        dev = DEVICE(&s->fsmc);
> +        if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsmc), errp)) {
> +            return;
> +        }
> +        busdev = SYS_BUS_DEVICE(dev);
> +        sysbus_mmio_map(busdev, 0, fsmc_addr);
> +        sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, fsmc_irq));
> +    }
> +
>      create_unimplemented_device("timer[2]",  0x40000000, 0x400);
>      create_unimplemented_device("timer[3]",  0x40000400, 0x400);
>      create_unimplemented_device("timer[4]",  0x40000800, 0x400);
> @@ -203,7 +304,6 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
>      create_unimplemented_device("timer[17]", 0x40014800, 0x400);
>      create_unimplemented_device("DMA1",      0x40020000, 0x400);
>      create_unimplemented_device("DMA2",      0x40020400, 0x400);
> -    create_unimplemented_device("RCC",       0x40021000, 0x400);
>      create_unimplemented_device("Flash Int", 0x40022000, 0x400);
>      create_unimplemented_device("CRC",       0x40023000, 0x400);
>  }
> diff --git a/hw/arm/stm32f1_generic.c b/hw/arm/stm32f1_generic.c
> index 63d2a58bdc..6420542f1b 100644
> --- a/hw/arm/stm32f1_generic.c
> +++ b/hw/arm/stm32f1_generic.c
> @@ -30,6 +30,7 @@
>  #include "hw/qdev-properties.h"
>  #include "hw/qdev-clock.h"
>  #include "qemu/error-report.h"
> +#include "exec/address-spaces.h"
>  #include "hw/arm/stm32f100_soc.h"
>  #include "hw/arm/boot.h"
>
> @@ -38,6 +39,7 @@
>
>  static void stm32f1_generic_init(MachineState *machine)
>  {
> +    MemoryRegion *psram1;
>      STM32F100State *s;
>      DeviceState *dev;
>      Clock *sysclk;
> @@ -59,6 +61,16 @@ static void stm32f1_generic_init(MachineState *machine)
>      armv7m_load_kernel(ARM_CPU(first_cpu),
>                         machine->kernel_filename,
>                         0, s->flash_size);
> +
> +    /* Allow assigning more RAM via FSMC on high-density devices */
> +    if (s->density == STM32F100_DENSITY_HIGH) {
> +        assert(machine->ram_size <= PSRAM1_SIZE);
> +        psram1 = g_new(MemoryRegion, 1);
> +        memory_region_init_ram(psram1, NULL, "STM32F1-generic.psram1",
> +                               machine->ram_size, &error_fatal);
> +        memory_region_add_subregion(get_system_memory(),
> +                                    PSRAM1_BASE_ADDRESS, psram1);
> +    }
>  }
>
>  static void stm32f1_generic_machine_init(MachineClass *mc)
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index e4c2149175..002d34a123 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -73,6 +73,9 @@ config IMX
>      select SSI
>      select USB_EHCI_SYSBUS
>
> +config STM32F1XX_FSMC
> +    bool
> +
>  config STM32F2XX_SYSCFG
>      bool
>
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 78ca857c9d..a0b0786fd3 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -97,6 +97,7 @@ softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
>    'xlnx-versal-xramc.c',
>    'xlnx-versal-pmc-iou-slcr.c',
>  ))
> +softmmu_ss.add(when: 'CONFIG_STM32F1XX_FSMC', if_true: files('stm32f1xx_fsmc.c'))
>  softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
>  softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
>  softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
> diff --git a/hw/misc/stm32f1xx_fsmc.c b/hw/misc/stm32f1xx_fsmc.c
> new file mode 100644
> index 0000000000..8a9618bb31
> --- /dev/null
> +++ b/hw/misc/stm32f1xx_fsmc.c
> @@ -0,0 +1,155 @@
> +/*
> + * STM32F1XX FSMC
> + *
> + * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/misc/stm32f1xx_fsmc.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +
> +static void stm32f1xx_fsmc_reset(DeviceState *dev)
> +{
> +    STM32F1XXFsmcState *s = STM32F1XX_FSMC(dev);
> +
> +    s->fsmc_bcr[0] = 0x000030DB;
> +    for (int i=1; i<4; ++i)
> +        s->fsmc_bcr[i] = 0x000030D2;
> +    for (int i=0; i<4; ++i) {
> +        s->fsmc_btr[i] = 0xffffffff;
> +        s->fsmc_bwtr[i] = 0xffffffff;
> +    }
> +}
> +
> +static uint64_t stm32f1xx_fsmc_read(void *opaque, hwaddr addr,
> +                                     unsigned int size)
> +{
> +    STM32F1XXFsmcState *s = opaque;
> +
> +    switch (addr) {
> +    case FSMC_BCR1:
> +        return s->fsmc_bcr[0];
> +    case FSMC_BCR2:
> +        return s->fsmc_bcr[1];
> +    case FSMC_BCR3:
> +        return s->fsmc_bcr[2];
> +    case FSMC_BCR4:
> +        return s->fsmc_bcr[3];
> +    case FSMC_BTR1:
> +        return s->fsmc_btr[0];
> +    case FSMC_BTR2:
> +        return s->fsmc_btr[1];
> +    case FSMC_BTR3:
> +        return s->fsmc_btr[2];
> +    case FSMC_BTR4:
> +        return s->fsmc_btr[3];
> +    case FSMC_BWTR1:
> +        return s->fsmc_bwtr[0];
> +    case FSMC_BWTR2:
> +        return s->fsmc_bwtr[1];
> +    case FSMC_BWTR3:
> +        return s->fsmc_bwtr[2];
> +    case FSMC_BWTR4:
> +        return s->fsmc_bwtr[3];
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
> +    }
> +
> +    return 0;
> +}
> +
> +static void stm32f1xx_fsmc_write(void *opaque, hwaddr addr,
> +                       uint64_t val64, unsigned int size)
> +{
> +    STM32F1XXFsmcState *s = opaque;
> +    uint32_t value = val64 & 0xffffffff;
> +
> +    switch (addr) {
> +    case FSMC_BCR1:
> +        s->fsmc_bcr[0] = value;
> +    case FSMC_BCR2:
> +        s->fsmc_bcr[1] = value;
> +    case FSMC_BCR3:
> +        s->fsmc_bcr[2] = value;
> +    case FSMC_BCR4:
> +        s->fsmc_bcr[3] = value;
> +    case FSMC_BTR1:
> +        s->fsmc_btr[0] = value;
> +    case FSMC_BTR2:
> +        s->fsmc_btr[1] = value;
> +    case FSMC_BTR3:
> +        s->fsmc_btr[2] = value;
> +    case FSMC_BTR4:
> +        s->fsmc_btr[3] = value;
> +    case FSMC_BWTR1:
> +        s->fsmc_bwtr[0] = value;
> +    case FSMC_BWTR2:
> +        s->fsmc_bwtr[1] = value;
> +    case FSMC_BWTR3:
> +        s->fsmc_bwtr[2] = value;
> +    case FSMC_BWTR4:
> +        s->fsmc_bwtr[3] = value;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
> +    }
> +}
> +
> +static const MemoryRegionOps stm32f1xx_fsmc_ops = {
> +    .read = stm32f1xx_fsmc_read,
> +    .write = stm32f1xx_fsmc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void stm32f1xx_fsmc_init(Object *obj)
> +{
> +    STM32F1XXFsmcState *s = STM32F1XX_FSMC(obj);
> +
> +    memory_region_init_io(&s->mmio, obj, &stm32f1xx_fsmc_ops, s,
> +                          TYPE_STM32F1XX_FSMC, 0x400);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +
> +    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
> +}
> +
> +static void stm32f1xx_fsmc_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->reset = stm32f1xx_fsmc_reset;
> +}
> +
> +static const TypeInfo stm32f1xx_fsmc_info = {
> +    .name          = TYPE_STM32F1XX_FSMC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(STM32F1XXFsmcState),
> +    .instance_init = stm32f1xx_fsmc_init,
> +    .class_init    = stm32f1xx_fsmc_class_init,
> +};
> +
> +static void stm32f1xx_fsmc_register_types(void)
> +{
> +    type_register_static(&stm32f1xx_fsmc_info);
> +}
> +
> +type_init(stm32f1xx_fsmc_register_types)
> diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h
> index 5305e342e3..0ce2439e38 100644
> --- a/include/hw/arm/stm32f100_soc.h
> +++ b/include/hw/arm/stm32f100_soc.h
> @@ -26,6 +26,7 @@
>  #define HW_ARM_STM32F100_SOC_H
>
>  #include "hw/char/stm32f2xx_usart.h"
> +#include "hw/misc/stm32f1xx_fsmc.h"
>  #include "hw/ssi/stm32f2xx_spi.h"
>  #include "hw/arm/armv7m.h"
>  #include "qom/object.h"
> @@ -42,8 +43,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
>  #define FLASH_SIZE_LD (32 * 1024)
>  #define FLASH_SIZE_MD (128 * 1024)
>  #define FLASH_SIZE_HD (512 * 1024)
> +
>  #define SRAM_BASE_ADDRESS 0x20000000
> -#define SRAM_SIZE (8 * 1024)
> +#define SRAM_SIZE (32 * 1024)
> +
> +#define PSRAM1_BASE_ADDRESS 0x60000000
> +#define PSRAM1_SIZE (256 * 1024 * 1024)
>
>  /* Supported density value lines */
>  typedef enum {
> @@ -52,6 +57,20 @@ typedef enum {
>      STM32F100_DENSITY_HIGH,
>  } STM32F100Density;
>
> +typedef struct {
> +    uint32_t cr;
> +    uint32_t cfgr;
> +    uint32_t cir;
> +    uint32_t apb2rstr;
> +    uint32_t apb1rstr;
> +    uint32_t ahbenr;
> +    uint32_t apb2enr;
> +    uint32_t apb1enr;
> +    uint32_t bdcr;
> +    uint32_t csr;
> +    uint32_t cfgr2;
> +} STM32F1XXRccState;
> +
>  struct STM32F100State {
>      /*< private >*/
>      SysBusDevice parent_obj;
> @@ -63,10 +82,13 @@ struct STM32F100State {
>
>      STM32F2XXUsartState usart[STM_NUM_USARTS];
>      STM32F2XXSPIState spi[STM_NUM_SPIS];
> +    STM32F1XXFsmcState fsmc;
> +    STM32F1XXRccState rcc;
>
>      MemoryRegion sram;
>      MemoryRegion flash;
>      MemoryRegion flash_alias;
> +    MemoryRegion iomem;
>
>      Clock *sysclk;
>      Clock *refclk;
> diff --git a/include/hw/misc/stm32f1xx_fsmc.h b/include/hw/misc/stm32f1xx_fsmc.h
> new file mode 100644
> index 0000000000..1432d2da9e
> --- /dev/null
> +++ b/include/hw/misc/stm32f1xx_fsmc.h
> @@ -0,0 +1,62 @@
> +/*
> + * STM32F1xx FSMC
> + *
> + * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#ifndef HW_STM32F1XX_FSMC_H
> +#define HW_STM32F1XX_FSMC_H
> +
> +#include "hw/sysbus.h"
> +#include "qom/object.h"
> +
> +#define FSMC_BCR1  0x000
> +#define FSMC_BCR2  0x008
> +#define FSMC_BCR3  0x010
> +#define FSMC_BCR4  0x018
> +#define FSMC_BTR1  0x004
> +#define FSMC_BTR2  0x00C
> +#define FSMC_BTR3  0x014
> +#define FSMC_BTR4  0x01C
> +#define FSMC_BWTR1 0x104
> +#define FSMC_BWTR2 0x10C
> +#define FSMC_BWTR3 0x114
> +#define FSMC_BWTR4 0x11C
> +#define NUM_BANKS  4
> +
> +#define TYPE_STM32F1XX_FSMC "stm32f1xx-fsmc"
> +OBJECT_DECLARE_SIMPLE_TYPE(STM32F1XXFsmcState, STM32F1XX_FSMC)
> +
> +struct STM32F1XXFsmcState {
> +    /* <private> */
> +    SysBusDevice parent_obj;
> +
> +    /* <public> */
> +    MemoryRegion mmio;
> +
> +    uint32_t fsmc_bcr[NUM_BANKS];
> +    uint32_t fsmc_btr[NUM_BANKS];
> +    uint32_t fsmc_bwtr[NUM_BANKS];
> +
> +    qemu_irq irq;
> +};
> +
> +#endif
> --
> 2.39.2 (Apple Git-143)
>
>
Re: [PATCH] STM32F100: add support for external memory via FSMC
Posted by Lucas C. Villa Real 10 months, 3 weeks ago
On Thu, Jun 22, 2023 at 10:37 PM Alistair Francis <alistair23@gmail.com>
wrote:

> On Wed, Jun 21, 2023 at 5:44 AM Lucas Villa Real <lucas@osdyne.com> wrote:
> >
> > Add support for FSMC on high-density STM32F100 devices and enable
> > mapping of additional memory via the `-m SIZE` command-line option.
> > FSMC Bank1 can address up to 4x64MB of PSRAM memory at 0x60000000.
>
> Thanks for the patches!
>

You're welcome!


> >
> > RCC is needed to enable peripheral clock for FSMC; this commit
> > implements support for RCC through the MMIO interface.
>
> This should be a separate commit. The idea is to break commits up as
> small as possible and send a patch series, this makes review much
> easier. Each new feature should be its own commit.
>

Thanks, I'll submit a new patchset as recommended.


> >
> > Last, high-density devices support up to 32KB of static SRAM, so
> > adjust SRAM_SIZE accordingly.
>
> Also, can you include a link to the documentation in the commit message?
>

Absolutely.


> > +static const MemoryRegionOps stm32f100_rcc_ops = {
> > +    .read = stm32f100_rcc_read,
> > +    .write = stm32f100_rcc_write,
> > +    .endianness = DEVICE_NATIVE_ENDIAN,
> > +};
>
> This should be its own file and device that is included
>

Sounds good, thanks for the guidance. I'll work on this next week.

Best regards,
Lucas
[PATCH] STM32F100: add support for external memory via FSMC
Posted by Lucas Villa Real 11 months ago
Add support for FSMC on high-density STM32F100 devices and enable
mapping of additional memory via the `-m SIZE` command-line option.
FSMC Bank1 can address up to 4x64MB of PSRAM memory at 0x60000000.

RCC is needed to enable peripheral clock for FSMC; this commit
implements support for RCC through the MMIO interface.

Last, high-density devices support up to 32KB of static SRAM, so
adjust SRAM_SIZE accordingly.

Signed-off-by: Lucas C. Villa Real <lucas@odsyne.com>
---
 docs/system/arm/stm32.rst        |  12 ++-
 hw/arm/Kconfig                   |   1 +
 hw/arm/stm32f100_soc.c           | 102 +++++++++++++++++++-
 hw/arm/stm32f1_generic.c         |  12 +++
 hw/misc/Kconfig                  |   3 +
 hw/misc/meson.build              |   1 +
 hw/misc/stm32f1xx_fsmc.c         | 155 +++++++++++++++++++++++++++++++
 include/hw/arm/stm32f100_soc.h   |  24 ++++-
 include/hw/misc/stm32f1xx_fsmc.h |  62 +++++++++++++
 9 files changed, 368 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/stm32f1xx_fsmc.c
 create mode 100644 include/hw/misc/stm32f1xx_fsmc.h

diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
index d0a3b1a7eb..40de58ed04 100644
--- a/docs/system/arm/stm32.rst
+++ b/docs/system/arm/stm32.rst
@@ -40,6 +40,8 @@ Supported devices
  * SPI controller
  * System configuration (SYSCFG)
  * Timer controller (TIMER)
+ * Reset and Clock Controller (RCC)
+ * Flexible static memory controller (FSMC)
 
 Missing devices
 ---------------
@@ -57,7 +59,6 @@ Missing devices
  * Power supply configuration (PWR)
  * Random Number Generator (RNG)
  * Real-Time Clock (RTC) controller
- * Reset and Clock Controller (RCC)
  * Secure Digital Input/Output (SDIO) interface
  * USB OTG
  * Watchdog controller (IWDG, WWDG)
@@ -78,4 +79,11 @@ to select the device density line.  The following values are supported:
 
 .. code-block:: bash
 
-  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
\ No newline at end of file
+  $ qemu-system-arm -M stm32f1-generic -global stm32f100-soc.density=medium ...
+
+High-density devices can also enable up to 256 MB of external memory using
+the `-m SIZE` option. The memory is mapped at address 0x60000000. Example:
+ 
+.. code-block:: bash
+
+  $ qemu-system-arm -M stm32f1-generic -m 64M ...
\ No newline at end of file
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 822441945c..dd48068108 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -433,6 +433,7 @@ config RASPI
 config STM32F100_SOC
     bool
     select ARM_V7M
+    select STM32F1XX_FSMC
     select STM32F2XX_USART
     select STM32F2XX_SPI
 
diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c
index c157ffd644..a2b863d309 100644
--- a/hw/arm/stm32f100_soc.c
+++ b/hw/arm/stm32f100_soc.c
@@ -26,6 +26,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#include "qemu/log.h"
 #include "hw/arm/boot.h"
 #include "exec/address-spaces.h"
 #include "hw/arm/stm32f100_soc.h"
@@ -40,9 +41,85 @@ static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400,
     0x40004800 };
 static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800,
     0x40003C00 };
+static const uint32_t fsmc_addr = 0xA0000000;
 
 static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39};
 static const int spi_irq[STM_NUM_SPIS] = {35, 36, 51};
+static const int fsmc_irq = 48;
+
+static uint64_t stm32f100_rcc_read(void *h, hwaddr offset, unsigned size)
+{
+    STM32F100State *s = (STM32F100State *) h;
+    switch (offset) {
+    case 0x00:
+        return s->rcc.cr;
+    case 0x04:
+        return s->rcc.cfgr;
+    case 0x08:
+        return s->rcc.cir;
+    case 0x0C:
+        return s->rcc.apb2rstr;
+    case 0x10:
+        return s->rcc.apb1rstr;
+    case 0x14:
+        return s->rcc.ahbenr;
+    case 0x18:
+        return s->rcc.apb2enr;
+    case 0x1C:
+        return s->rcc.apb1enr;
+    case 0x20:
+        return s->rcc.bdcr;
+    case 0x24:
+        return s->rcc.csr;
+    case 0x2C:
+        return s->rcc.cfgr2;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
+    }
+    return 0;
+}
+
+static void stm32f100_rcc_write(void *h, hwaddr offset, uint64_t value64,
+                                unsigned size)
+{
+    STM32F100State *s = (STM32F100State *) h;
+    uint32_t value = value64 & 0xffffffff;
+
+    switch (offset) {
+    case 0x00:
+        s->rcc.cr = value;
+    case 0x04:
+        s->rcc.cfgr = value;
+    case 0x08:
+        s->rcc.cir = value;
+    case 0x0C:
+        s->rcc.apb2rstr = value;
+    case 0x10:
+        s->rcc.apb1rstr = value;
+    case 0x14:
+        s->rcc.ahbenr = value;
+    case 0x18:
+        s->rcc.apb2enr = value;
+    case 0x1C:
+        s->rcc.apb1enr = value;
+    case 0x20:
+        s->rcc.bdcr = value;
+    case 0x24:
+        s->rcc.csr = value;
+    case 0x2C:
+        s->rcc.cfgr2 = value;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset);
+    }
+}
+
+static const MemoryRegionOps stm32f100_rcc_ops = {
+    .read = stm32f100_rcc_read,
+    .write = stm32f100_rcc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
 
 static void stm32f100_soc_initfn(Object *obj)
 {
@@ -66,6 +143,11 @@ static void stm32f100_soc_initfn(Object *obj)
 
     /* Default density. May be overridden by the machine or cmdline option */
     s->density = STM32F100_DENSITY_HIGH;
+
+    memset(&s->rcc, 0, sizeof(s->rcc));
+    s->rcc.cr = 0x00000083;
+    s->rcc.ahbenr = 0x00000014;
+    s->rcc.csr = 0x0C000000;
 }
 
 static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -168,6 +250,25 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
         sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i]));
     }
 
+    /* Declare a simple memory-mapped I/O region for RCC */
+    memory_region_init_io(&s->iomem, OBJECT(dev_soc), &stm32f100_rcc_ops, s,
+                          "STM32F100.mmio.rcc", 0x400);
+    memory_region_add_subregion(system_memory, 0x40021000, &s->iomem);
+
+    /* Declare an I/O region for FSMC */
+    if (s->density == STM32F100_DENSITY_HIGH) {
+        object_initialize_child(OBJECT(dev_soc), "fsmc", &s->fsmc,
+                                TYPE_STM32F1XX_FSMC);
+
+        dev = DEVICE(&s->fsmc);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->fsmc), errp)) {
+            return;
+        }
+        busdev = SYS_BUS_DEVICE(dev);
+        sysbus_mmio_map(busdev, 0, fsmc_addr);
+        sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, fsmc_irq));
+    }
+
     create_unimplemented_device("timer[2]",  0x40000000, 0x400);
     create_unimplemented_device("timer[3]",  0x40000400, 0x400);
     create_unimplemented_device("timer[4]",  0x40000800, 0x400);
@@ -203,7 +304,6 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("timer[17]", 0x40014800, 0x400);
     create_unimplemented_device("DMA1",      0x40020000, 0x400);
     create_unimplemented_device("DMA2",      0x40020400, 0x400);
-    create_unimplemented_device("RCC",       0x40021000, 0x400);
     create_unimplemented_device("Flash Int", 0x40022000, 0x400);
     create_unimplemented_device("CRC",       0x40023000, 0x400);
 }
diff --git a/hw/arm/stm32f1_generic.c b/hw/arm/stm32f1_generic.c
index 63d2a58bdc..6420542f1b 100644
--- a/hw/arm/stm32f1_generic.c
+++ b/hw/arm/stm32f1_generic.c
@@ -30,6 +30,7 @@
 #include "hw/qdev-properties.h"
 #include "hw/qdev-clock.h"
 #include "qemu/error-report.h"
+#include "exec/address-spaces.h"
 #include "hw/arm/stm32f100_soc.h"
 #include "hw/arm/boot.h"
 
@@ -38,6 +39,7 @@
 
 static void stm32f1_generic_init(MachineState *machine)
 {
+    MemoryRegion *psram1;
     STM32F100State *s;
     DeviceState *dev;
     Clock *sysclk;
@@ -59,6 +61,16 @@ static void stm32f1_generic_init(MachineState *machine)
     armv7m_load_kernel(ARM_CPU(first_cpu),
                        machine->kernel_filename,
                        0, s->flash_size);
+
+    /* Allow assigning more RAM via FSMC on high-density devices */
+    if (s->density == STM32F100_DENSITY_HIGH) {
+        assert(machine->ram_size <= PSRAM1_SIZE);
+        psram1 = g_new(MemoryRegion, 1);
+        memory_region_init_ram(psram1, NULL, "STM32F1-generic.psram1",
+                               machine->ram_size, &error_fatal);
+        memory_region_add_subregion(get_system_memory(),
+                                    PSRAM1_BASE_ADDRESS, psram1);
+    }
 }
 
 static void stm32f1_generic_machine_init(MachineClass *mc)
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index e4c2149175..002d34a123 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -73,6 +73,9 @@ config IMX
     select SSI
     select USB_EHCI_SYSBUS
 
+config STM32F1XX_FSMC
+    bool
+
 config STM32F2XX_SYSCFG
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 78ca857c9d..a0b0786fd3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -97,6 +97,7 @@ softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
   'xlnx-versal-xramc.c',
   'xlnx-versal-pmc-iou-slcr.c',
 ))
+softmmu_ss.add(when: 'CONFIG_STM32F1XX_FSMC', if_true: files('stm32f1xx_fsmc.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
diff --git a/hw/misc/stm32f1xx_fsmc.c b/hw/misc/stm32f1xx_fsmc.c
new file mode 100644
index 0000000000..8a9618bb31
--- /dev/null
+++ b/hw/misc/stm32f1xx_fsmc.c
@@ -0,0 +1,155 @@
+/*
+ * STM32F1XX FSMC
+ *
+ * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/stm32f1xx_fsmc.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static void stm32f1xx_fsmc_reset(DeviceState *dev)
+{
+    STM32F1XXFsmcState *s = STM32F1XX_FSMC(dev);
+
+    s->fsmc_bcr[0] = 0x000030DB;
+    for (int i=1; i<4; ++i)
+        s->fsmc_bcr[i] = 0x000030D2;
+    for (int i=0; i<4; ++i) {
+        s->fsmc_btr[i] = 0xffffffff;
+        s->fsmc_bwtr[i] = 0xffffffff;
+    }
+}
+
+static uint64_t stm32f1xx_fsmc_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    STM32F1XXFsmcState *s = opaque;
+
+    switch (addr) {
+    case FSMC_BCR1:
+        return s->fsmc_bcr[0];
+    case FSMC_BCR2:
+        return s->fsmc_bcr[1];
+    case FSMC_BCR3:
+        return s->fsmc_bcr[2];
+    case FSMC_BCR4:
+        return s->fsmc_bcr[3];
+    case FSMC_BTR1:
+        return s->fsmc_btr[0];
+    case FSMC_BTR2:
+        return s->fsmc_btr[1];
+    case FSMC_BTR3:
+        return s->fsmc_btr[2];
+    case FSMC_BTR4:
+        return s->fsmc_btr[3];
+    case FSMC_BWTR1:
+        return s->fsmc_bwtr[0];
+    case FSMC_BWTR2:
+        return s->fsmc_bwtr[1];
+    case FSMC_BWTR3:
+        return s->fsmc_bwtr[2];
+    case FSMC_BWTR4:
+        return s->fsmc_bwtr[3];
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+
+    return 0;
+}
+
+static void stm32f1xx_fsmc_write(void *opaque, hwaddr addr,
+                       uint64_t val64, unsigned int size)
+{
+    STM32F1XXFsmcState *s = opaque;
+    uint32_t value = val64 & 0xffffffff;
+
+    switch (addr) {
+    case FSMC_BCR1:
+        s->fsmc_bcr[0] = value;
+    case FSMC_BCR2:
+        s->fsmc_bcr[1] = value;
+    case FSMC_BCR3:
+        s->fsmc_bcr[2] = value;
+    case FSMC_BCR4:
+        s->fsmc_bcr[3] = value;
+    case FSMC_BTR1:
+        s->fsmc_btr[0] = value;
+    case FSMC_BTR2:
+        s->fsmc_btr[1] = value;
+    case FSMC_BTR3:
+        s->fsmc_btr[2] = value;
+    case FSMC_BTR4:
+        s->fsmc_btr[3] = value;
+    case FSMC_BWTR1:
+        s->fsmc_bwtr[0] = value;
+    case FSMC_BWTR2:
+        s->fsmc_bwtr[1] = value;
+    case FSMC_BWTR3:
+        s->fsmc_bwtr[2] = value;
+    case FSMC_BWTR4:
+        s->fsmc_bwtr[3] = value;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+}
+
+static const MemoryRegionOps stm32f1xx_fsmc_ops = {
+    .read = stm32f1xx_fsmc_read,
+    .write = stm32f1xx_fsmc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stm32f1xx_fsmc_init(Object *obj)
+{
+    STM32F1XXFsmcState *s = STM32F1XX_FSMC(obj);
+
+    memory_region_init_io(&s->mmio, obj, &stm32f1xx_fsmc_ops, s,
+                          TYPE_STM32F1XX_FSMC, 0x400);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static void stm32f1xx_fsmc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->reset = stm32f1xx_fsmc_reset;
+}
+
+static const TypeInfo stm32f1xx_fsmc_info = {
+    .name          = TYPE_STM32F1XX_FSMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32F1XXFsmcState),
+    .instance_init = stm32f1xx_fsmc_init,
+    .class_init    = stm32f1xx_fsmc_class_init,
+};
+
+static void stm32f1xx_fsmc_register_types(void)
+{
+    type_register_static(&stm32f1xx_fsmc_info);
+}
+
+type_init(stm32f1xx_fsmc_register_types)
diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h
index 5305e342e3..0ce2439e38 100644
--- a/include/hw/arm/stm32f100_soc.h
+++ b/include/hw/arm/stm32f100_soc.h
@@ -26,6 +26,7 @@
 #define HW_ARM_STM32F100_SOC_H
 
 #include "hw/char/stm32f2xx_usart.h"
+#include "hw/misc/stm32f1xx_fsmc.h"
 #include "hw/ssi/stm32f2xx_spi.h"
 #include "hw/arm/armv7m.h"
 #include "qom/object.h"
@@ -42,8 +43,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
 #define FLASH_SIZE_LD (32 * 1024)
 #define FLASH_SIZE_MD (128 * 1024)
 #define FLASH_SIZE_HD (512 * 1024)
+
 #define SRAM_BASE_ADDRESS 0x20000000
-#define SRAM_SIZE (8 * 1024)
+#define SRAM_SIZE (32 * 1024)
+
+#define PSRAM1_BASE_ADDRESS 0x60000000
+#define PSRAM1_SIZE (256 * 1024 * 1024)
 
 /* Supported density value lines */
 typedef enum {
@@ -52,6 +57,20 @@ typedef enum {
     STM32F100_DENSITY_HIGH,
 } STM32F100Density;
 
+typedef struct {
+    uint32_t cr;
+    uint32_t cfgr;
+    uint32_t cir;
+    uint32_t apb2rstr;
+    uint32_t apb1rstr;
+    uint32_t ahbenr;
+    uint32_t apb2enr;
+    uint32_t apb1enr;
+    uint32_t bdcr;
+    uint32_t csr;
+    uint32_t cfgr2;
+} STM32F1XXRccState;
+
 struct STM32F100State {
     /*< private >*/
     SysBusDevice parent_obj;
@@ -63,10 +82,13 @@ struct STM32F100State {
 
     STM32F2XXUsartState usart[STM_NUM_USARTS];
     STM32F2XXSPIState spi[STM_NUM_SPIS];
+    STM32F1XXFsmcState fsmc;
+    STM32F1XXRccState rcc;
 
     MemoryRegion sram;
     MemoryRegion flash;
     MemoryRegion flash_alias;
+    MemoryRegion iomem;
 
     Clock *sysclk;
     Clock *refclk;
diff --git a/include/hw/misc/stm32f1xx_fsmc.h b/include/hw/misc/stm32f1xx_fsmc.h
new file mode 100644
index 0000000000..1432d2da9e
--- /dev/null
+++ b/include/hw/misc/stm32f1xx_fsmc.h
@@ -0,0 +1,62 @@
+/*
+ * STM32F1xx FSMC
+ *
+ * Copyright (c) 2023 Lucas C. Villa Real <lucas@osdyne.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_STM32F1XX_FSMC_H
+#define HW_STM32F1XX_FSMC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define FSMC_BCR1  0x000
+#define FSMC_BCR2  0x008
+#define FSMC_BCR3  0x010
+#define FSMC_BCR4  0x018
+#define FSMC_BTR1  0x004
+#define FSMC_BTR2  0x00C
+#define FSMC_BTR3  0x014
+#define FSMC_BTR4  0x01C
+#define FSMC_BWTR1 0x104
+#define FSMC_BWTR2 0x10C
+#define FSMC_BWTR3 0x114
+#define FSMC_BWTR4 0x11C
+#define NUM_BANKS  4
+
+#define TYPE_STM32F1XX_FSMC "stm32f1xx-fsmc"
+OBJECT_DECLARE_SIMPLE_TYPE(STM32F1XXFsmcState, STM32F1XX_FSMC)
+
+struct STM32F1XXFsmcState {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion mmio;
+
+    uint32_t fsmc_bcr[NUM_BANKS];
+    uint32_t fsmc_btr[NUM_BANKS];
+    uint32_t fsmc_bwtr[NUM_BANKS];
+
+    qemu_irq irq;
+};
+
+#endif
-- 
2.39.2 (Apple Git-143)