diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak
index d847bd5692ec5364e4f6608c58073f75cf615bfe..65c65cc2cc69b1575a1e0b9098e5f62e5756607f 100644
--- a/configs/devices/riscv32-softmmu/default.mak
+++ b/configs/devices/riscv32-softmmu/default.mak
@@ -13,3 +13,4 @@ CONFIG_SIFIVE_E=y
 CONFIG_SIFIVE_U=y
 CONFIG_RISCV_VIRT=y
 CONFIG_OPENTITAN=y
+CONFIG_ESP32_C3=y
diff --git a/docs/system/riscv/esp32-c3.rst b/docs/system/riscv/esp32-c3.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6376d77b5f86d0d906faa6bc6793ab5c2622d39c
--- /dev/null
+++ b/docs/system/riscv/esp32-c3.rst
@@ -0,0 +1,2 @@
+esp32/c3 Reference Platform (``esp32/c3``)
+==========================================
diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 89a866e4f4f192242efc11bece2478c6f0850ab6..f1c024efed7c6340218265ba69b13f4edf80ec92 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -66,6 +66,7 @@ undocumented; you can get a complete list by running
 .. toctree::
    :maxdepth: 1
 
+   riscv/esp32-c3
    riscv/microchip-icicle-kit
    riscv/shakti-c
    riscv/sifive_u
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 6528ebfa3a3bbd7408a46b485f606a30e295c172..1e25b075169f1d798f42c36d503b69c654b4be28 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -6,6 +6,9 @@ config IBEX
 
 # RISC-V machines in alphabetical order
 
+config ESP32_C3
+    bool
+
 config MICROCHIP_PFSOC
     bool
     select CADENCE_SDHCI
diff --git a/hw/riscv/esp32_c3.c b/hw/riscv/esp32_c3.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5f1d43aaea08489497f845c35220e66ca28226b
--- /dev/null
+++ b/hw/riscv/esp32_c3.c
@@ -0,0 +1,166 @@
+/*
+ * ESP32-C3-class SoC emulation
+ *
+ * Copyright (c) 2023
+ * Davids Paskevics (davip00@zedat.fu-berlin.de)
+ * Nicolas Patzelt (nicolas.patzelt@fu-berlin.de)
+ * Jakob Knitter (jakok07@zedat.fu-berlin.de)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.The 0x00001000 address is mapped to ROM with a trampoline code to 0x80000000. AUIPC instruction moves its immediate value 12 bits to the left and adds to the current PC , so t0 = 0(x7ffff<<12)+ 0x1000 = 0x80000000ww.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "hw/riscv/esp32_c3.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/intc/sifive_plic.h"
+#include "hw/intc/riscv_aclint.h"
+#include "sysemu/sysemu.h"
+#include "hw/qdev-properties.h"
+#include "exec/address-spaces.h"
+#include "hw/riscv/boot.h"
+
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} esp32_c3_memmap[] = {
+    [ESP32_C3_DROM]   =  {  0x3FF00000,  0x1FFFF   },
+    [ESP32_C3_IROM]   =  {  0x40000000,  0x5FFFF   },
+    [ESP32_C3_IRAM]   =  {  0x4037C000,  0x63FFF   },
+    [ESP32_C3_DRAM]   =  {  0x3FC80000,  0x5FFFF   },
+    [ESP32_C3_DM]     =  {  0x20000000,  0x7FFFFFF }
+};
+
+static void esp32_c3_machine_state_init(MachineState *mstate)
+{
+    Esp32C3MachineState *sms = RISCV_ESP32_MACHINE(mstate);
+    MemoryRegion *system_memory = get_system_memory();
+
+    /* Allow only ESP32 C3 CPU for this platform */
+    if (strcmp(mstate->cpu_type, TYPE_RISCV_CPU_ESP32_C3) != 0) {
+        error_report("This board can only be used with ESP32 C3 CPU");
+        exit(1);
+    }
+
+    /* Initialize SoC */
+    object_initialize_child(OBJECT(mstate), "soc", &sms->soc,
+                            TYPE_RISCV_ESP32_SOC);
+    qdev_realize(DEVICE(&sms->soc), NULL, &error_abort);
+
+    /* register RAM */
+    // TODO: Deal with IRAM vs DRAM
+    memory_region_add_subregion(system_memory,
+                                esp32_c3_memmap[ESP32_C3_IRAM].base,
+                                mstate->ram);
+
+    /* ROM reset vector */
+    // TODO: Check if this is correct
+    // TODO: Does this make sense for our board? Code runs from ROM directly.
+    riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus,
+                              esp32_c3_memmap[ESP32_C3_IRAM].base,
+                              esp32_c3_memmap[ESP32_C3_IROM].base,
+                              esp32_c3_memmap[ESP32_C3_IROM].size, 0x0400, 0);
+    if (mstate->firmware) {
+        // We don't have any (separate) firmware to load, as this is an MCU
+        error_report("This board can't load firmware");
+        exit(1);
+    }
+}
+
+static void esp32_c3_machine_instance_init(Object *obj)
+{
+}
+
+static void esp32_c3_machine_class_init(ObjectClass *klass, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(klass);
+    mc->desc = "RISC-V Board compatible with ES32-C3 SDK";
+    mc->init = esp32_c3_machine_state_init;
+    mc->default_cpu_type = TYPE_RISCV_CPU_ESP32_C3;
+    mc->default_ram_id = "riscv.esp32.c.ram";
+}
+
+static const TypeInfo esp32_c3_machine_type_info = {
+    .name = TYPE_RISCV_ESP32_MACHINE,
+    .parent = TYPE_MACHINE,
+    .class_init = esp32_c3_machine_class_init,
+    .instance_init = esp32_c3_machine_instance_init,
+    .instance_size = sizeof(Esp32C3MachineState),
+};
+
+static void esp32_c3_machine_type_info_register(void)
+{
+    type_register_static(&esp32_c3_machine_type_info);
+}
+type_init(esp32_c3_machine_type_info_register)
+
+static void esp32_c3_soc_state_realize(DeviceState *dev, Error **errp)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+    (void)ms;
+    Esp32C3SoCState *sss = RISCV_ESP32_SOC(dev);
+    MemoryRegion *system_memory = get_system_memory();
+
+    sysbus_realize(SYS_BUS_DEVICE(&sss->cpus), &error_abort);
+
+    /* ROM */
+    // TODO: Deal with IROM vs DROM
+    memory_region_init_rom(&sss->rom, OBJECT(dev), "riscv.esp32.c.rom",
+                           esp32_c3_memmap[ESP32_C3_IROM].size, &error_fatal);
+    memory_region_add_subregion(system_memory,
+        esp32_c3_memmap[ESP32_C3_IROM].base, &sss->rom);
+}
+
+static void esp32_c3_soc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->realize = esp32_c3_soc_state_realize;
+    /*
+     * Reasons:
+     *     - Creates CPUS in riscv_hart_realize(), and can create unintended
+     *       CPUs
+     *     - Uses serial_hds in realize function, thus can't be used twice
+     */
+    dc->user_creatable = false;
+}
+
+static void esp32_c3_soc_instance_init(Object *obj)
+{
+    Esp32C3SoCState *sss = RISCV_ESP32_SOC(obj);
+
+    object_initialize_child(obj, "cpus", &sss->cpus, TYPE_RISCV_HART_ARRAY);
+
+    /*
+     * CPU type is fixed and we are not supporting passing from commandline yet.
+     * So let it be in instance_init. When supported should use ms->cpu_type
+     * instead of TYPE_RISCV_CPU_ESP32_C3
+     */
+    object_property_set_str(OBJECT(&sss->cpus), "cpu-type",
+                            TYPE_RISCV_CPU_ESP32_C3, &error_abort);
+    object_property_set_int(OBJECT(&sss->cpus), "num-harts", 1,
+                            &error_abort);
+}
+
+static const TypeInfo esp32_c3_type_info = {
+    .name = TYPE_RISCV_ESP32_SOC,
+    .parent = TYPE_DEVICE,
+    .class_init = esp32_c3_soc_class_init,
+    .instance_init = esp32_c3_soc_instance_init,
+    .instance_size = sizeof(Esp32C3SoCState),
+};
+
+static void esp32_c3_type_info_register(void)
+{
+    type_register_static(&esp32_c3_type_info);
+}
+
+type_init(esp32_c3_type_info_register)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 2f7ee81be3ca916185c00d9ae5a19e45928f871e..2bdcc914eb9885b93d7f16b2565f569a2a3f5f9d 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -8,6 +8,7 @@ riscv_ss.add(when: 'CONFIG_SHAKTI_C', if_true: files('shakti_c.c'))
 riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
 riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
 riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
+riscv_ss.add(when: 'CONFIG_ESP32_C3', if_true: files('esp32_c3.c'))
 riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
 riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
 
diff --git a/include/hw/riscv/esp32_c3.h b/include/hw/riscv/esp32_c3.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ef80b3f758055ec57ffa11ea21a3179ed854dba
--- /dev/null
+++ b/include/hw/riscv/esp32_c3.h
@@ -0,0 +1,64 @@
+/*
+ * ESP32-C3-class SoC emulation
+ *
+ * Copyright (c) 2023
+ * Davids Paskevics (davip00@zedat.fu-berlin.de)
+ * Nicolas Patzelt (nicolas.patzelt@fu-berlin.de)
+ * Jakob Knitter (jakok07@zedat.fu-berlin.de)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_ESP32_C3_H
+#define HW_ESP32_C3_H
+
+#include "hw/riscv/riscv_hart.h"
+#include "hw/boards.h"
+
+
+
+#define TYPE_RISCV_ESP32_SOC "riscv.esp32.cclass.soc"
+#define RISCV_ESP32_SOC(obj) \
+    OBJECT_CHECK(Esp32C3SoCState, (obj), TYPE_RISCV_ESP32_SOC)
+
+typedef struct Esp32C3SoCState {
+    /*< private >*/
+    DeviceState parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState cpus;
+    DeviceState *plic;
+    MemoryRegion rom;
+
+} Esp32C3SoCState;
+
+#define TYPE_RISCV_ESP32_MACHINE MACHINE_TYPE_NAME("esp32_c3")
+#define RISCV_ESP32_MACHINE(obj) \
+    OBJECT_CHECK(Esp32C3MachineState, (obj), TYPE_RISCV_ESP32_MACHINE)
+typedef struct Esp32C3MachineState {
+    /*< private >*/
+    MachineState parent_obj;
+
+    /*< public >*/
+    Esp32C3SoCState soc;
+} Esp32C3MachineState;
+
+enum {
+    ESP32_C3_DROM,
+    ESP32_C3_IROM,
+    ESP32_C3_IRAM,
+    ESP32_C3_DRAM,
+    ESP32_C3_DM
+};
+
+#endif
diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
index 04af50983e61ebdd66ca647dbe3ba6e0f83a7f3f..af05762b7126a01410d5ce0ef55b942100692d7a 100644
--- a/target/riscv/cpu-qom.h
+++ b/target/riscv/cpu-qom.h
@@ -35,6 +35,7 @@
 #define TYPE_RISCV_CPU_BASE128          RISCV_CPU_TYPE_NAME("x-rv128")
 #define TYPE_RISCV_CPU_IBEX             RISCV_CPU_TYPE_NAME("lowrisc-ibex")
 #define TYPE_RISCV_CPU_SHAKTI_C         RISCV_CPU_TYPE_NAME("shakti-c")
+#define TYPE_RISCV_CPU_ESP32_C3         RISCV_CPU_TYPE_NAME("esp32-c3")
 #define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e31")
 #define TYPE_RISCV_CPU_SIFIVE_E34       RISCV_CPU_TYPE_NAME("sifive-e34")
 #define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e51")
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 881bddf393f9fe00d50156311be09ce48d5f50c4..d04b5c88f98279079e883b86c7081423033c0798 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -520,6 +520,15 @@ static void rv32_sifive_u_cpu_init(Object *obj)
     cpu->cfg.pmp = true;
 }
 
+static void rv32_esp32_c3_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, MXL_RV32, RVI | RVM | RVC);
+#ifndef CONFIG_USER_ONLY
+    set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32);
+#endif
+}
+
 static void rv32_sifive_e_cpu_init(Object *obj)
 {
     CPURISCVState *env = &RISCV_CPU(obj)->env;
@@ -1938,6 +1947,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
     DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31,       rv32_sifive_e_cpu_init),
     DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34,       rv32_imafcu_nommu_cpu_init),
     DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34,       rv32_sifive_u_cpu_init),
+    DEFINE_CPU(TYPE_RISCV_CPU_ESP32_C3,         rv32_esp32_c3_cpu_init),
 #elif defined(TARGET_RISCV64)
     DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE64,   rv64_base_cpu_init),
     DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51,       rv64_sifive_e_cpu_init),