diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 3be65d70eba3..685120a9e672 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -88,4 +88,5 @@ config SPIKE config RISCV_ESP32C3 bool + select OPENCORES_ETH select UNIMP diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c new file mode 100644 index 000000000000..b8120779f310 --- /dev/null +++ b/hw/riscv/esp32c3.c @@ -0,0 +1,617 @@ +/* + * ESP32-C3 SoC and machine + * + * Copyright (c) 2019-2022 Espressif Systems (Shanghai) Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/qdev-properties.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/riscv/riscv_hart.h" +#include "target/riscv/esp_cpu.h" +#include "hw/riscv/boot.h" +#include "hw/riscv/numa.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/runstate.h" +#include "sysemu/reset.h" +#include "net/net.h" +#include "hw/misc/esp32c3_reg.h" +#include "hw/misc/esp32c3_rtc_cntl.h" +#include "hw/misc/esp32c3_cache.h" +#include "hw/char/esp32c3_uart.h" +#include "hw/gpio/esp32c3_gpio.h" +#include "hw/nvram/esp32c3_efuse.h" +#include "hw/riscv/esp32c3_clk.h" +#include "hw/riscv/esp32c3_intmatrix.h" +#include "hw/misc/esp32c3_sha.h" +#include "hw/timer/esp32c3_timg.h" +#include "hw/timer/esp32c3_systimer.h" +#include "hw/ssi/esp32c3_spi.h" +#include "hw/misc/esp32c3_rtc_cntl.h" +#include "hw/misc/esp32c3_aes.h" +#include "hw/misc/esp32c3_rsa.h" +#include "hw/misc/esp32c3_hmac.h" +#include "hw/misc/esp32c3_ds.h" +#include "hw/misc/esp32c3_jtag.h" +#include "hw/dma/esp32c3_gdma.h" +#include "hw/display/esp_rgb.h" + +#define ESP32C3_IO_WARNING 0 + +#define ESP32C3_RESET_ADDRESS 0x40000000 +#define ESP32C3_RESET_GPIO_NAME "esp32c3.machine.reset_gpio" +#define MB (1024*1024) + + +/* Define a new "class" which derivates from "MachineState" */ +struct Esp32C3MachineState { + MachineState parent; + + /* Attributes specific to our class */ + EspRISCVCPU soc; + BusState periph_bus; + MemoryRegion iomem; + + qemu_irq cpu_reset; + + DeviceState *eth; /* Ethernet controller */ + ESP32C3IntMatrixState intmatrix; + ESP32C3UARTState uart[ESP32C3_UART_COUNT]; + ESP32C3GPIOState gpio; + ESP32C3CacheState cache; + ESP32C3EfuseState efuse; + ESP32C3ClockState clock; + ESP32C3GdmaState gdma; + ESP32C3AesState aes; + ESP32C3ShaState sha; + ESP32C3RsaState rsa; + ESP32C3HmacState hmac; + ESP32C3DsState ds; + ESP32C3TimgState timg[2]; + ESP32C3SysTimerState systimer; + ESP32C3SpiState spi1; + ESP32C3RtcCntlState rtccntl; + ESP32C3UsbJtagState jtag; + ESPRgbState rgb; +}; + +/* Fake register used by ESP-IDF application to determine whether the code is running on real hardware or on QEMU */ +#define A_SYSCON_ORIGIN_REG 0x3F8 +/* Temporary macro for generating a random value from register SYSCON_RND_DATA_REG */ +#define A_SYSCON_RND_DATA_REG 0x0B0 + +/* Temporary macro to mark the CPU as in non-debugging mode */ +#define A_ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG 0x098 + +/* Create a macro which defines the name of our new machine class */ +#define TYPE_ESP32C3_MACHINE MACHINE_TYPE_NAME("esp32c3") + +/* This will create a macro ESP32_MACHINE, which can be used to check and cast a generic MachineClass + * to the specific class we defined above: Esp32C3MachineState. */ +OBJECT_DECLARE_SIMPLE_TYPE(Esp32C3MachineState, ESP32C3_MACHINE) + +/* Memory entries for ESP32-C3 */ +enum MemoryRegions { + ESP32C3_MEMREGION_IROM, + ESP32C3_MEMREGION_DROM, + ESP32C3_MEMREGION_DRAM, + ESP32C3_MEMREGION_IRAM, + ESP32C3_MEMREGION_RTCFAST, + ESP32C3_MEMREGION_DCACHE, + ESP32C3_MEMREGION_ICACHE, + ESP32C3_MEMREGION_FRAMEBUF, +}; + +#define ESP32C3_INTERNAL_SRAM0_SIZE (16*1024) + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} esp32c3_memmap[] = { + [ESP32C3_MEMREGION_IROM] = { 0x40000000, 0x60000 }, + [ESP32C3_MEMREGION_DROM] = { 0x3ff00000, 0x20000 }, + [ESP32C3_MEMREGION_DRAM] = { 0x3fc80000, 0x60000 }, + /* Merge SRAM0 and SRAM1 into a single entry */ + [ESP32C3_MEMREGION_IRAM] = { 0x4037c000, 0x60000 + ESP32C3_INTERNAL_SRAM0_SIZE }, + [ESP32C3_MEMREGION_RTCFAST] = { 0x50000000, 0x2000 }, + [ESP32C3_MEMREGION_DCACHE] = { 0x3c000000, 0x800000 }, + [ESP32C3_MEMREGION_ICACHE] = { 0x42000000, 0x800000 }, + /* Virtual Framebuffer, used for the graphical interface */ + [ESP32C3_MEMREGION_FRAMEBUF] = { 0x20000000, ESP_RGB_MAX_VRAM_SIZE } +}; + + +static bool addr_in_range(hwaddr addr, hwaddr start, hwaddr end) +{ + return addr >= start && addr < end; +} + +static uint64_t esp32c3_io_read(void *opaque, hwaddr addr, unsigned int size) +{ + if (addr_in_range(addr + ESP32C3_IO_START_ADDR, DR_REG_RTC_I2C_BASE, DR_REG_RTC_I2C_BASE + 0x100)) { + return (uint32_t) 0xffffff; + } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_SYSCON_BASE + A_SYSCON_ORIGIN_REG) { + /* Return "QEMU" as a 32-bit value */ + return 0x51454d55; + } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_SYSCON_BASE + A_SYSCON_RND_DATA_REG) { + /* Return a random 32-bit value */ + static bool init = false; + if (!init) { + srand(time(NULL)); + init = true; + } + return rand(); + } else if (addr + ESP32C3_IO_START_ADDR == DR_REG_ASSIST_DEBUG_BASE + A_ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG) { + return 0; + } else { +#if ESP32C3_IO_WARNING + warn_report("[ESP32-C3] Unsupported read to $%08lx\n", ESP32C3_IO_START_ADDR + addr); +#endif + } + return 0; +} + +static void esp32c3_io_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) +{ +#if ESP32C3_IO_WARNING + warn_report("[ESP32-C3] Unsupported write $%08lx = %08lx\n", ESP32C3_IO_START_ADDR + addr, value); +#endif +} + + +/* Define operations for I/OS */ +static const MemoryRegionOps esp32c3_io_ops = { + .read = esp32c3_io_read, + .write = esp32c3_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + + +/** + * @brief Callback invoked when SoC's ESP32C3_RESET_GPIO_NAME pin is toggled + */ +static void esp32c3_reset_request(void* opaque, int n, int level) +{ + if (level) { + ShutdownCause cause = SHUTDOWN_CAUSE_GUEST_RESET; + qemu_system_reset_request(cause); + } +} + + +static void esp32c3_init_spi_flash(Esp32C3MachineState *ms, BlockBackend* blk) +{ + DeviceState *spi_master = DEVICE(&ms->spi1); + BusState* spi_bus = qdev_get_child_bus(spi_master, "spi"); + const char* flash_model = NULL; + int64_t image_size = blk_getlength(blk); + + switch (image_size) { + case 2 * MB: + flash_model = "w25x16"; + break; + case 4 * MB: + flash_model = "gd25q32"; + break; + case 8 * MB: + flash_model = "gd25q64"; + break; + case 16 * MB: + flash_model = "is25lp128"; + break; + default: + error_report("Drive size error: only 2, 4, 8, and 16MB images are supported"); + return; + } + + /* Create the SPI flash model */ + DeviceState *flash_dev = qdev_new(flash_model); + qdev_prop_set_drive(flash_dev, "drive", blk); + + /* Realize the SPI flash, its "drive" (blk) property must already be set! */ + qdev_realize(flash_dev, spi_bus, &error_fatal); + qdev_connect_gpio_out_named(spi_master, SSI_GPIO_CS, 0, + qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0)); +} + + +static void esp32c3_init_openeth(Esp32C3MachineState *ms) +{ + const char* type_openeth = "open_eth"; + MemoryRegion* mr = NULL; + SysBusDevice* sbd = NULL; + + NICInfo *nd = &nd_table[0]; + MemoryRegion* sys_mem = get_system_memory(); + + if (nd->used && nd->model && strcmp(nd->model, type_openeth) == 0) { + /* Create a new OpenCores Ethernet component */ + DeviceState* open_eth_dev = qdev_new(type_openeth); + ms->eth = open_eth_dev; + qdev_set_nic_properties(open_eth_dev, nd); + sbd = SYS_BUS_DEVICE(open_eth_dev); + sysbus_realize(sbd, &error_fatal); + + /* OpenCores Ethernet has two memory regions: one for registers and one for descriptors, + * we need to provide one I/O range for each of them */ + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_EMAC_BASE, mr, 0); + mr = sysbus_mmio_get_region(sbd, 1); + memory_region_add_subregion_overlap(sys_mem, DR_REG_EMAC_BASE + 0x400, mr, 0); + + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(DEVICE(&ms->intmatrix), ETS_ETH_MAC_INTR_SOURCE)); + } +} + + +static void esp32c3_machine_init(MachineState *machine) +{ + /* First thing to do is to check if a drive format and a file ahve been passed through the command line. + * In fact, we will emulate the SPI flash if `if=mtd` was given. To know this, we will need to use the + * Global API's function `driver_get`. */ + BlockBackend* blk = NULL; + DriveInfo *dinfo = drive_get(IF_MTD, 0, 0); + if (dinfo) { + /* MTD was given! We need to initialize and emulate SPI flash */ + qemu_log("Adding SPI flash device\n"); + blk = blk_by_legacy_dinfo(dinfo); + } else { + qemu_log("Not initializing SPI Flash\n"); + } + + /* Re-use the macro that checks and casts any generic/parent class to the real child instance */ + Esp32C3MachineState *ms = ESP32C3_MACHINE(machine); + + /* Initialize SoC */ + object_initialize_child(OBJECT(ms), "soc", &ms->soc, TYPE_ESP_RISCV_CPU); + qdev_prop_set_uint64(DEVICE(&ms->soc), "resetvec", ESP32C3_RESET_ADDRESS); + + /* Initialize the memory mapping */ + const struct MemmapEntry *memmap = esp32c3_memmap; + MemoryRegion *sys_mem = get_system_memory(); + + /* Initialize the IROM */ + MemoryRegion *irom = g_new(MemoryRegion, 1); + memory_region_init_rom(irom, NULL, "esp32c3.irom", memmap[ESP32C3_MEMREGION_IROM].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[ESP32C3_MEMREGION_IROM].base, irom); + + /* Initialize the DROM as an alias to IROM. */ + MemoryRegion *drom = g_new(MemoryRegion, 1); + const hwaddr offset_in_orig = 0x40000; + memory_region_init_alias(drom, NULL, "esp32c3.drom", irom, offset_in_orig, memmap[ESP32C3_MEMREGION_DROM].size); + memory_region_add_subregion(sys_mem, memmap[ESP32C3_MEMREGION_DROM].base, drom); + + /* Initialize the IRAM */ + MemoryRegion *iram = g_new(MemoryRegion, 1); + memory_region_init_ram(iram, NULL, "esp32c3.iram", memmap[ESP32C3_MEMREGION_IRAM].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[ESP32C3_MEMREGION_IRAM].base, iram); + + /* Initialize DRAM as an alias to IRAM (not including Internal SRAM 0) */ + MemoryRegion *dram = g_new(MemoryRegion, 1); + /* DRAM mirrors IRAM for SRAM 1, skip the SRAM 0 area */ + memory_region_init_alias(dram, NULL, "esp32c3.dram", iram, + ESP32C3_INTERNAL_SRAM0_SIZE, memmap[ESP32C3_MEMREGION_DRAM].size); + memory_region_add_subregion(sys_mem, memmap[ESP32C3_MEMREGION_DRAM].base, dram); + + /* Initialize RTC Fast Memory as regular RAM */ + MemoryRegion *rtcram = g_new(MemoryRegion, 1); + memory_region_init_ram(rtcram, NULL, "esp32c3.rtcram", memmap[ESP32C3_MEMREGION_RTCFAST].size, &error_fatal); + memory_region_add_subregion(sys_mem, memmap[ESP32C3_MEMREGION_RTCFAST].base, rtcram); + + qdev_realize(DEVICE(&ms->soc), NULL, &error_fatal); + + memory_region_init_io(&ms->iomem, OBJECT(&ms->soc), &esp32c3_io_ops, + NULL, "esp32c3.iomem", 0xd1000); + memory_region_add_subregion(sys_mem, ESP32C3_IO_START_ADDR, &ms->iomem); + + + /* Initialize the peripheral bus */ + qbus_init(&ms->periph_bus, sizeof(ms->periph_bus), + TYPE_SYSTEM_BUS, DEVICE(&ms->soc), "esp32c3-periph-bus"); + + /* Initialize the main I/O of the CPU that waits for "reset" requests */ + qdev_init_gpio_in_named(DEVICE(&ms->soc), esp32c3_reset_request, ESP32C3_RESET_GPIO_NAME, 1); + + /* Initialize the I/O peripherals */ + for (int i = 0; i < ESP32C3_UART_COUNT; ++i) { + char name[16]; + snprintf(name, sizeof(name), "uart%d", i); + object_initialize_child(OBJECT(machine), name, &ms->uart[i], TYPE_ESP32C3_UART); + + snprintf(name, sizeof(name), "serial%d", i); + object_property_add_alias(OBJECT(machine), name, OBJECT(&ms->uart[i]), "chardev"); + qdev_prop_set_chr(DEVICE(&ms->uart[i]), "chardev", serial_hd(i)); + } + + object_initialize_child(OBJECT(machine), "intmatrix", &ms->intmatrix, TYPE_ESP32C3_INTMATRIX); + object_initialize_child(OBJECT(machine), "gpio", &ms->gpio, TYPE_ESP32C3_GPIO); + object_initialize_child(OBJECT(machine), "extmem", &ms->cache, TYPE_ESP32C3_CACHE); + object_initialize_child(OBJECT(machine), "efuse", &ms->efuse, TYPE_ESP32C3_EFUSE); + object_initialize_child(OBJECT(machine), "clock", &ms->clock, TYPE_ESP32C3_CLOCK); + object_initialize_child(OBJECT(machine), "sha", &ms->sha, TYPE_ESP32C3_SHA); + object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES); + object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA); + object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA); + object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC); + object_initialize_child(OBJECT(machine), "ds", &ms->ds, TYPE_ESP32C3_DS); + object_initialize_child(OBJECT(machine), "timg0", &ms->timg[0], TYPE_ESP32C3_TIMG); + object_initialize_child(OBJECT(machine), "timg1", &ms->timg[1], TYPE_ESP32C3_TIMG); + object_initialize_child(OBJECT(machine), "systimer", &ms->systimer, TYPE_ESP32C3_SYSTIMER); + object_initialize_child(OBJECT(machine), "spi1", &ms->spi1, TYPE_ESP32C3_SPI); + object_initialize_child(OBJECT(machine), "rtccntl", &ms->rtccntl, TYPE_ESP32C3_RTC_CNTL); + object_initialize_child(OBJECT(machine), "jtag", &ms->jtag, TYPE_ESP32C3_JTAG); + object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB); + + /* Realize all the I/O peripherals we depend on */ + + /* Interrupt matrix realization */ + DeviceState* intmatrix_dev = DEVICE(&ms->intmatrix); + { + /* Store the current Machine CPU in the interrupt matrix */ + object_property_set_link(OBJECT(&ms->intmatrix), "cpu", OBJECT(&ms->soc), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&ms->intmatrix), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->intmatrix), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_INTERRUPT_BASE, mr, 0); + + /* Connect all the interrupt matrix 31 output lines to the CPU 31 input IRQ lines. + * The lines are indexed starting at 1. + */ + for (int i = 0; i <= ESP32C3_CPU_INT_COUNT; i++) { + qemu_irq cpu_input = qdev_get_gpio_in_named(DEVICE(&ms->soc), ESP_CPU_IRQ_LINES_NAME, i); + qdev_connect_gpio_out_named(intmatrix_dev, ESP32C3_INT_MATRIX_OUTPUT_NAME, i, cpu_input); + } + } + + /* Initialize OpenCores Ethernet controller now sicne it requires the interrupt matrix */ + esp32c3_init_openeth(ms); + + /* USB Serial JTAG realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->jtag), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->jtag), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_USB_SERIAL_JTAG_BASE, mr, 0); + } + + /* RTC CNTL realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->rtccntl), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->rtccntl), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_RTCCNTL_BASE, mr, 0); + /* Connect CNTL's reset-request GPIO to the SoC's reset GPIO */ + qdev_connect_gpio_out_named(DEVICE(&ms->rtccntl), ESP32C3_RTC_CPU_RESET_GPIO, 0, + qdev_get_gpio_in_named(DEVICE(&ms->soc), ESP32C3_RESET_GPIO_NAME, 0)); + } + + /* SPI1 controller (SPI Flash) */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->spi1), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi1), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI1_BASE, mr, 0); + if (blk) { + esp32c3_init_spi_flash(ms, blk); + } + } + + for (int i = 0; i < ESP32C3_UART_COUNT; ++i) { + const hwaddr uart_base[] = { DR_REG_UART_BASE, DR_REG_UART1_BASE }; + sysbus_realize(SYS_BUS_DEVICE(&ms->uart[i]), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->uart[i]), 0); + memory_region_add_subregion_overlap(sys_mem, uart_base[i], mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->uart[i]), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_UART0_INTR_SOURCE + i)); + } + + /* GPIO realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->gpio), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->gpio), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_GPIO_BASE, mr, 0); + } + + /* (Extmem) Cache realization */ + { + if (blk) { + ms->cache.flash_blk = blk; + } + sysbus_realize(SYS_BUS_DEVICE(&ms->cache), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->cache), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_EXTMEM_BASE, mr, 0); + + memory_region_add_subregion_overlap(sys_mem, ms->cache.dcache_base, &ms->cache.dcache, 0); + memory_region_add_subregion_overlap(sys_mem, ms->cache.icache_base, &ms->cache.icache, 0); + } + + /* eFuses realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->efuse), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->efuse), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_EFUSE_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->efuse), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_EFUSE_INTR_SOURCE)); + } + + /* System clock realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->clock), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->clock), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SYSTEM_BASE, mr, 0); + /* Connect the IRQ lines to the interrupt matrix */ + for (int i = 0; i < ESP32C3_SYSTEM_CPU_INTR_COUNT; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->clock), i, + qdev_get_gpio_in(intmatrix_dev, ETS_FROM_CPU_INTR0_SOURCE + i)); + } + } + + /* Timer Groups realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->timg[0]), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->timg[0]), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_TIMERGROUP0_BASE, mr, 0); + /* Connect the T0 interrupt line to the interrupt matrix */ + qdev_connect_gpio_out_named(DEVICE(&ms->timg[0]), ESP32C3_T0_IRQ_INTERRUPT, 0, + qdev_get_gpio_in(intmatrix_dev, ETS_TG0_T0_LEVEL_INTR_SOURCE)); + /* Connect the Watchdog interrupt line to the interrupt matrix */ + qdev_connect_gpio_out_named(DEVICE(&ms->timg[0]), ESP32C3_WDT_IRQ_INTERRUPT, 0, + qdev_get_gpio_in(intmatrix_dev, ETS_TG0_WDT_LEVEL_INTR_SOURCE)); + /* Connect the Watchdog reset request to the CNTL's WDT0 line */ + qdev_connect_gpio_out_named(DEVICE(&ms->timg[0]), ESP32C3_WDT_IRQ_RESET, 0, + qdev_get_gpio_in(DEVICE(&ms->rtccntl), ESP32C3_TG0WDT_SYS_RESET)); + + } + { + sysbus_realize(SYS_BUS_DEVICE(&ms->timg[1]), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->timg[1]), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_TIMERGROUP1_BASE, mr, 0); + /* Connect the T0 interrupt line to the interrupt matrix */ + qdev_connect_gpio_out_named(DEVICE(&ms->timg[1]), ESP32C3_T0_IRQ_INTERRUPT, 0, + qdev_get_gpio_in(intmatrix_dev, ETS_TG1_T0_LEVEL_INTR_SOURCE)); + qdev_connect_gpio_out_named(DEVICE(&ms->timg[1]), ESP32C3_WDT_IRQ_INTERRUPT, 0, + qdev_get_gpio_in(intmatrix_dev, ETS_TG1_WDT_LEVEL_INTR_SOURCE)); + qdev_connect_gpio_out_named(DEVICE(&ms->timg[1]), ESP32C3_WDT_IRQ_RESET, 0, + qdev_get_gpio_in(DEVICE(&ms->rtccntl), ESP32C3_TG1WDT_SYS_RESET)); + } + + /* System timer */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->systimer), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->systimer), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SYSTIMER_BASE, mr, 0); + for (int i = 0; i < ESP32C3_SYSTIMER_IRQ_COUNT; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->systimer), i, + qdev_get_gpio_in(intmatrix_dev, ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE + i)); + } + } + + /* GDMA Realization */ + { + object_property_set_link(OBJECT(&ms->gdma), "soc_mr", OBJECT(dram), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&ms->gdma), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->gdma), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_GDMA_BASE, mr, 0); + /* Connect the IRQs to the Interrupt Matrix */ + for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->gdma), i, + qdev_get_gpio_in(intmatrix_dev, ETS_DMA_CH0_INTR_SOURCE + i)); + } + + } + + /* SHA realization */ + { + ms->sha.gdma = &ms->gdma; + sysbus_realize(SYS_BUS_DEVICE(&ms->sha), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->sha), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_SHA_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->sha), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_SHA_INTR_SOURCE)); + } + + /* AES realization */ + { + ms->aes.gdma = &ms->gdma; + sysbus_realize(SYS_BUS_DEVICE(&ms->aes), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->aes), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->aes), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_AES_INTR_SOURCE)); + } + + /* RSA realization */ + { + sysbus_realize(SYS_BUS_DEVICE(&ms->rsa), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->rsa), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_RSA_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&ms->rsa), 0, + qdev_get_gpio_in(intmatrix_dev, ETS_RSA_INTR_SOURCE)); + } + + /* HMAC realization */ + { + ms->hmac.efuse = &ms->efuse; + qdev_realize(DEVICE(&ms->hmac), &ms->periph_bus, &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->hmac), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_HMAC_BASE, mr, 0); + } + + /* Digital Signature realization */ + { + ms->ds.hmac = &ms->hmac; + ms->ds.aes = &ms->aes; + ms->ds.rsa = &ms->rsa; + ms->ds.sha = &ms->sha; + qdev_realize(DEVICE(&ms->ds), &ms->periph_bus, &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->ds), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_DIGITAL_SIGNATURE_BASE, mr, 0); + } + + /* RGB display realization */ + { + /* Give the internal RAM memory region to the display */ + ms->rgb.intram = dram; + sysbus_realize(SYS_BUS_DEVICE(&ms->rgb), &error_fatal); + MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->rgb), 0); + memory_region_add_subregion_overlap(sys_mem, DR_REG_FRAMEBUF_BASE, mr, 0); + memory_region_add_subregion_overlap(sys_mem, esp32c3_memmap[ESP32C3_MEMREGION_FRAMEBUF].base, &ms->rgb.vram, 0); + } + + /* Open and load the "bios", which is the ROM binary, also named "first stage bootloader" */ + char *rom_binary = qemu_find_file(QEMU_FILE_TYPE_BIOS, "esp32c3-rom.bin"); + if (rom_binary == NULL) { + error_report("Error: -bios argument not set, and ROM code binary not found (1)"); + exit(1); + } + + /* Load ROM file at the reset address */ + int size = load_image_targphys_as(rom_binary, ESP32C3_RESET_ADDRESS, 0x60000, CPU(&ms->soc)->as); + if (size < 0) { + error_report("Error: could not load ROM binary '%s'", rom_binary); + exit(1); + } + + g_free(rom_binary); +} + + +/* Initialize machine type */ +static void esp32c3_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Espressif ESP32-C3 machine"; + mc->default_cpu_type = TYPE_ESP_RISCV_CPU; + mc->init = esp32c3_machine_init; + mc->max_cpus = 1; + mc->default_cpus = 1; + // 0x4f600 + mc->default_ram_size = 400 * 1024; +} + +/* Create a new type of machine ("child class") */ +static const TypeInfo esp32c3_info = { + .name = TYPE_ESP32C3_MACHINE, + /* Specify the parent class, i.e. the class we derivate from */ + .parent = TYPE_MACHINE, + /* Real size in bytes of our machine instance */ + .instance_size = sizeof(Esp32C3MachineState), + /* Override the init function to one we defined above */ + .class_init = esp32c3_machine_class_init, +}; + +static void esp32c3_machine_type_init(void) +{ + type_register_static(&esp32c3_info); +} + +type_init(esp32c3_machine_type_init); diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 7b59b8b25ff7..be0858cf1391 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -10,6 +10,6 @@ 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_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) -riscv_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3_clk.c', 'esp32c3_intmatrix.c')) +riscv_ss.add(when: 'CONFIG_RISCV_ESP32C3', if_true: files('esp32c3.c', 'esp32c3_clk.c', 'esp32c3_intmatrix.c')) hw_arch += {'riscv': riscv_ss} diff --git a/include/hw/misc/esp32c3_reg.h b/include/hw/misc/esp32c3_reg.h new file mode 100644 index 000000000000..9029327afd7c --- /dev/null +++ b/include/hw/misc/esp32c3_reg.h @@ -0,0 +1,52 @@ +#pragma once + +#define DR_REG_FRAMEBUF_BASE 0x21000000 + +#define DR_REG_SYSTEM_BASE 0x600c0000 +#define DR_REG_SENSITIVE_BASE 0x600c1000 +#define DR_REG_INTERRUPT_BASE 0x600c2000 +#define DR_REG_EXTMEM_BASE 0x600c4000 +#define DR_REG_MMU_TABLE 0x600c5000 +#define DR_REG_AES_BASE 0x6003a000 +#define DR_REG_SHA_BASE 0x6003b000 +#define DR_REG_RSA_BASE 0x6003c000 +#define DR_REG_HMAC_BASE 0x6003e000 +#define DR_REG_DIGITAL_SIGNATURE_BASE 0x6003d000 +#define DR_REG_GDMA_BASE 0x6003f000 +#define DR_REG_ASSIST_DEBUG_BASE 0x600ce000 +#define DR_REG_DEDICATED_GPIO_BASE 0x600cf000 +#define DR_REG_WORLD_CNTL_BASE 0x600d0000 +#define DR_REG_DPORT_END 0x600d3FFC +#define DR_REG_UART_BASE 0x60000000 +#define DR_REG_SPI1_BASE 0x60002000 +#define DR_REG_SPI0_BASE 0x60003000 +#define DR_REG_GPIO_BASE 0x60004000 +#define DR_REG_FE2_BASE 0x60005000 +#define DR_REG_FE_BASE 0x60006000 +#define DR_REG_RTCCNTL_BASE 0x60008000 +#define DR_REG_IO_MUX_BASE 0x60009000 +#define DR_REG_RTC_I2C_BASE 0x6000e000 +#define DR_REG_UART1_BASE 0x60010000 +#define DR_REG_I2C_EXT_BASE 0x60013000 +#define DR_REG_UHCI0_BASE 0x60014000 +#define DR_REG_RMT_BASE 0x60016000 +#define DR_REG_LEDC_BASE 0x60019000 +#define DR_REG_EFUSE_BASE 0x60008800 +#define DR_REG_NRX_BASE 0x6001CC00 +#define DR_REG_BB_BASE 0x6001D000 +#define DR_REG_TIMERGROUP0_BASE 0x6001F000 +#define DR_REG_TIMERGROUP1_BASE 0x60020000 +#define DR_REG_SYSTIMER_BASE 0x60023000 +#define DR_REG_SPI2_BASE 0x60024000 +#define DR_REG_SYSCON_BASE 0x60026000 +#define DR_REG_APB_CTRL_BASE 0x60026000 /* Old name for SYSCON, to be removed */ +#define DR_REG_TWAI_BASE 0x6002B000 +#define DR_REG_I2S0_BASE 0x6002D000 +#define DR_REG_APB_SARADC_BASE 0x60040000 +#define DR_REG_USB_SERIAL_JTAG_BASE 0x60043000 +#define DR_REG_AES_XTS_BASE 0x600CC000 +#define DR_REG_EMAC_BASE 0x600CD000 + + +#define ESP32C3_IO_START_ADDR (DR_REG_UART_BASE) +#define ESP32C3_UART_COUNT 2 diff --git a/pc-bios/esp32c3-rom.bin b/pc-bios/esp32c3-rom.bin new file mode 100755 index 000000000000..3f59017cbe39 Binary files /dev/null and b/pc-bios/esp32c3-rom.bin differ diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 54729278e0b0..dc699be1609a 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -58,6 +58,7 @@ blobs = [ 'efi-vmxnet3.rom', 'esp32-v3-rom.bin', 'esp32-v3-rom-app.bin', + 'esp32c3-rom.bin', 'qemu-nsis.bmp', 'bamboo.dtb', 'canyonlands.dtb',