/* * 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/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "sysemu/reset.h" #include "net/net.h" #include "elf.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/ssi/esp32c3_spi2.h" #include "hw/adc/esp32c3_adc.h" #include "hw/i2c/esp32_i2c.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_xts_aes.h" #include "hw/misc/esp32c3_jtag.h" #include "hw/misc/esp32c3_wifi.h" #include "hw/misc/esp32c3_ana.h" #include "hw/misc/esp32_phya.h" #include "hw/misc/esp32_fe.h" #include "hw/misc/esp32c3_pwrmng.h" #include "hw/dma/esp32c3_gdma.h" #include "hw/display/esp_rgb.h" #include "hw/display/xteink_x3_eink.h" #include "hw/sd/sd.h" #include "hw/net/can/esp32c3_twai.h" #include "ui/console.h" #ifdef __EMSCRIPTEN__ #include #endif #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; uint32_t syscon[0x1000 / sizeof(uint32_t)]; bool xteink; bool x4; 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; ESP32C3XtsAesState xts_aes; ESP32C3TimgState timg[2]; ESP32C3SysTimerState systimer; ESP32C3SpiState spi1; ESP32C3Spi2State spi2; ESP32C3AdcState adc; Esp32I2CState i2c; ESP32C3RtcCntlState rtccntl; ESP32C3UsbJtagState jtag; ESPRgbState rgb; Esp32C3TWAIState twai; }; #ifdef __EMSCRIPTEN__ static struct Esp32C3MachineState *xteink_wasm_machine; EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_adc(int channel, uint32_t value) { if (xteink_wasm_machine && channel >= 0 && channel < ESP32C3_ADC_CHANNELS) { fprintf(stderr, "[button] adc channel=%d value=%u\n", channel, value); qatomic_set(&xteink_wasm_machine->adc.input[channel], MIN(value, UINT32_C(0xfff))); } } EMSCRIPTEN_KEEPALIVE void xteink_wasm_set_gpio(int pin, int level) { if (xteink_wasm_machine && pin >= 0 && pin < ESP32_GPIO_COUNT) { fprintf(stderr, "[button] gpio pin=%d level=%d\n", pin, level); esp32_gpio_set_input_level(&xteink_wasm_machine->gpio.parent, pin, level); } } #endif /* 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) { Esp32C3MachineState *ms = opaque; hwaddr absolute = addr + ESP32C3_IO_START_ADDR; if (addr_in_range(absolute, DR_REG_NRX_BASE - 0xc00, DR_REG_BB_BASE + 0x1000)) { return UINT32_MAX; } else if (addr_in_range(absolute, DR_REG_RTC_I2C_BASE, DR_REG_RTC_I2C_BASE + 0x100)) { return 0xffffff; } else if (absolute == DR_REG_SYSCON_BASE + A_SYSCON_ORIGIN_REG) { /* Return "QEMU" as a 32-bit value */ return 0x51454d55; } else if (absolute == 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 (absolute == DR_REG_ASSIST_DEBUG_BASE + A_ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG) { return 0; } else if (addr_in_range(absolute, DR_REG_SYSCON_BASE, DR_REG_SYSCON_BASE + sizeof(ms->syscon))) { return ms->syscon[(absolute - DR_REG_SYSCON_BASE) / sizeof(uint32_t)]; } 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) { Esp32C3MachineState *ms = opaque; hwaddr absolute = addr + ESP32C3_IO_START_ADDR; if (addr_in_range(absolute, DR_REG_SYSCON_BASE, DR_REG_SYSCON_BASE + sizeof(ms->syscon))) { ms->syscon[(absolute - DR_REG_SYSCON_BASE) / sizeof(uint32_t)] = value; return; } #if ESP32C3_IO_WARNING warn_report("[ESP32-C3] Unsupported write $%08lx = %08lx\n", absolute, 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); qdev_prop_set_uint8(flash_dev, "cs", 1); /* 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_map_radio_device(const char *type, hwaddr address) { DeviceState *device = qdev_new(type); SysBusDevice *sbd = SYS_BUS_DEVICE(device); sysbus_realize_and_unref(sbd, &error_fatal); memory_region_add_subregion_overlap(get_system_memory(), address, sysbus_mmio_get_region(sbd, 0), 0); } static bool esp32c3_init_wifi(Esp32C3MachineState *ms) { DeviceState *wifi = qemu_create_nic_device(TYPE_ESP32C3_WIFI, false, NULL); SysBusDevice *sbd; if (!wifi) { return false; } esp32c3_map_radio_device(TYPE_ESP32C3_ANA, DR_REG_RTC_I2C_BASE); esp32c3_map_radio_device(TYPE_ESP32_PHYA, DR_REG_PHYA_BASE); esp32c3_map_radio_device(TYPE_ESP32_FE, DR_REG_FE_BASE); esp32c3_map_radio_device(TYPE_ESP32C3_PWR_MANAGER, DR_REG_PWR_MANAGER_BASE); sbd = SYS_BUS_DEVICE(wifi); sysbus_realize_and_unref(sbd, &error_fatal); memory_region_add_subregion_overlap(get_system_memory(), DR_REG_WIFI_BASE, sysbus_mmio_get_region(sbd, 0), 0); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&ms->intmatrix), ETS_WIFI_MAC_INTR_SOURCE)); return true; } static void esp32c3_init_openeth(Esp32C3MachineState *ms) { DeviceState *open_eth_dev = qemu_create_nic_device("open_eth", true, NULL); SysBusDevice *sbd; if (!open_eth_dev) { return; } ms->eth = open_eth_dev; sbd = SYS_BUS_DEVICE(open_eth_dev); sysbus_realize(sbd, &error_fatal); memory_region_add_subregion_overlap(get_system_memory(), DR_REG_EMAC_BASE, sysbus_mmio_get_region(sbd, 0), 0); memory_region_add_subregion_overlap(get_system_memory(), DR_REG_EMAC_BASE + 0x400, sysbus_mmio_get_region(sbd, 1), 0); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(&ms->intmatrix), ETS_ETH_MAC_INTR_SOURCE)); } static void esp32c3_load_firmware(MachineState *machine) { Esp32C3MachineState *ms = ESP32C3_MACHINE(machine); const char *bios_filename = NULL; if (machine->firmware) { bios_filename = machine->firmware; } if (machine->kernel_filename) { if (bios_filename) { qemu_log("Warning: both -bios and -kernel arguments specified. Only loading the the -kernel file.\n"); } bios_filename = machine->kernel_filename; } if (bios_filename) { /* Since EspRISCVCPU doens't have a RISCVHartArrayState field, let's bake one on the stack. It will only be * used to get the type of the RISC-V CPU (32 or 64 bits) in `riscv_load_kernel` */ RISCVHartArrayState hart = { .harts = &ms->soc.parent_obj, .num_harts = 1, }; /* The function `riscv_load_kernel` won't load the ELF file at its entry point, so we have to look * for the ELF entry point manually here */ uint64_t elf_entry = ESP32C3_RESET_ADDRESS; /* The entry point address should be populated regardless of the return value */ load_elf_ram_sym(bios_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, NULL, 0, EM_RISCV, 1, 0, NULL, false, NULL); /* On failure, riscv_load_kernel exits the program */ qemu_log("Loading kernel at address 0x%08" PRIx64 "\n", elf_entry); riscv_load_kernel(machine, &hart, elf_entry, false, NULL); if (elf_entry != ESP32C3_RESET_ADDRESS) { qdev_prop_set_uint64(DEVICE(&ms->soc), "resetvec", elf_entry); } } else { /* 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); } } 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); esp32c3_load_firmware(machine); qdev_realize(DEVICE(&ms->soc), NULL, &error_fatal); memory_region_init_io(&ms->iomem, OBJECT(&ms->soc), &esp32c3_io_ops, ms, "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); #ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "aes", &ms->aes, TYPE_ESP32C3_AES); #endif object_initialize_child(OBJECT(machine), "gdma", &ms->gdma, TYPE_ESP32C3_GDMA); #ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "rsa", &ms->rsa, TYPE_ESP32C3_RSA); #endif object_initialize_child(OBJECT(machine), "hmac", &ms->hmac, TYPE_ESP32C3_HMAC); #ifdef CONFIG_GCRYPT object_initialize_child(OBJECT(machine), "ds", &ms->ds, TYPE_ESP32C3_DS); object_initialize_child(OBJECT(machine), "xts_aes", &ms->xts_aes, TYPE_ESP32C3_XTS_AES); #endif 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), "spi2", &ms->spi2, TYPE_ESP32C3_SPI2); object_initialize_child(OBJECT(machine), "adc", &ms->adc, TYPE_ESP32C3_ADC); object_initialize_child(OBJECT(machine), "i2c", &ms->i2c, TYPE_ESP32_I2C); qdev_prop_set_bit(DEVICE(&ms->i2c), "c3", true); object_initialize_child(OBJECT(machine), "rtccntl", &ms->rtccntl, TYPE_ESP32C3_RTC_CNTL); object_initialize_child(OBJECT(machine), "jtag", &ms->jtag, TYPE_ESP32C3_JTAG); if (!ms->xteink) { object_initialize_child(OBJECT(machine), "rgb", &ms->rgb, TYPE_ESP_RGB); } object_initialize_child(OBJECT(machine), "twai", &ms->twai, TYPE_ESP32C3_TWAI); /* 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); } } if (!esp32c3_init_wifi(ms)) { 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); sysbus_connect_irq(SYS_BUS_DEVICE(&ms->jtag), 0, qdev_get_gpio_in(intmatrix_dev, ETS_USB_SERIAL_JTAG_INTR_SOURCE)); } /* 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) */ { #ifdef CONFIG_GCRYPT ms->spi1.xts_aes = &ms->xts_aes; #endif 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); } } /* SPI2 controller (display and SD card) */ { sysbus_realize(SYS_BUS_DEVICE(&ms->spi2), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->spi2), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_SPI2_BASE, mr, 0); } /* SAR ADC (battery and resistor-ladder buttons) */ { sysbus_realize(SYS_BUS_DEVICE(&ms->adc), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->adc), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_APB_SARADC_BASE, mr, 0); } /* I2C controller (X3/X4 fingerprint chips live on SDA20/SCL0). The * interrupt must reach the matrix or the ESP-IDF driver blocks on its * completion semaphore and every probe times out. */ { sysbus_realize(SYS_BUS_DEVICE(&ms->i2c), &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->i2c), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_I2C_EXT_BASE, mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&ms->i2c), 0, qdev_get_gpio_in(intmatrix_dev, ETS_I2C_EXT0_INTR_SOURCE)); } 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; } #ifdef CONFIG_GCRYPT ms->cache.xts_aes = &ms->xts_aes; #endif 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 < ESP_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); /* On the ESP32-C3, both IN and OUT channels are connected to the same Connect the IRQs to the Interrupt Matrix */ for (int i = 0; i < ESP32C3_GDMA_CHANNEL_COUNT; i++) { qdev_connect_gpio_out_named(DEVICE(&ms->gdma), ESP_GDMA_IRQ_IN_NAME, i, qdev_get_gpio_in(intmatrix_dev, ETS_DMA_CH0_INTR_SOURCE + i)); qdev_connect_gpio_out_named(DEVICE(&ms->gdma), ESP_GDMA_IRQ_OUT_NAME, i, qdev_get_gpio_in(intmatrix_dev, ETS_DMA_CH0_INTR_SOURCE + i)); } } /* SHA realization */ { ms->sha.parent.gdma = ESP_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)); } #ifdef CONFIG_GCRYPT /* AES realization */ { ms->aes.parent.gdma = ESP_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)); } #endif /* HMAC realization */ { ms->hmac.parent.efuse = ESP_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); } #ifdef CONFIG_GCRYPT /* Digital Signature realization */ { ms->ds.parent.hmac = ESP_HMAC(&ms->hmac); ms->ds.parent.aes = ESP_AES(&ms->aes); ms->ds.parent.rsa = ESP_RSA(&ms->rsa); ms->ds.parent.sha = ESP_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); } /* XTS-AES realization */ { ms->xts_aes.efuse = ESP_EFUSE(&ms->efuse); ms->xts_aes.clock = &ms->clock; qdev_realize(DEVICE(&ms->xts_aes), &ms->periph_bus, &error_fatal); MemoryRegion *mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->xts_aes), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_AES_XTS_BASE, mr, 0); } #endif /* RGB display realization */ if (!ms->xteink) { /* 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); } /* TWAI peripheral realization */ sysbus_realize(SYS_BUS_DEVICE(&ms->twai), &error_fatal); MemoryRegion *twai_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&ms->twai), 0); memory_region_add_subregion_overlap(sys_mem, DR_REG_TWAI_BASE, twai_mr, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&ms->twai), 0, qdev_get_gpio_in(DEVICE(&ms->intmatrix), ETS_TWAI_INTR_SOURCE)); } static void xteink_key_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { Esp32C3MachineState *ms = ESP32C3_MACHINE(qdev_get_machine()); InputKeyEvent *key = evt->u.key.data; int qcode = qemu_input_key_value_to_qcode(key->key); int channel = -1; uint32_t pressed = 0; switch (qcode) { case Q_KEY_CODE_LEFT: channel = 2; pressed = 2242; break; case Q_KEY_CODE_RIGHT: channel = 2; pressed = 5; break; case Q_KEY_CODE_1: channel = 1; pressed = 3512; break; case Q_KEY_CODE_2: channel = 1; pressed = 2694; break; case Q_KEY_CODE_3: channel = 1; pressed = 1493; break; case Q_KEY_CODE_4: channel = 1; pressed = 5; break; case Q_KEY_CODE_P: esp32_gpio_set_input_level(&ms->gpio.parent, 3, !key->down); return; default: return; } qatomic_set(&ms->adc.input[channel], key->down ? pressed : 4095); } static const QemuInputHandler xteink_key_handler = { .name = "xteink buttons", .mask = INPUT_EVENT_MASK_KEY, .event = xteink_key_event, }; static void xteink_machine_init(MachineState *machine) { Esp32C3MachineState *ms = ESP32C3_MACHINE(machine); ms->xteink = true; esp32c3_machine_init(machine); #ifdef __EMSCRIPTEN__ xteink_wasm_machine = ms; #endif /* X3 exposes the fingerprint chips; X4 omits them so stock firmware's * all-NAK probe selects X4. The X4 panel is a blank protocol stub. */ if (!ms->x4) { i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x55); i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x68); i2c_slave_create_simple(ms->i2c.bus, "xteink-fpchip", 0x6B); } DeviceState *panel = ssi_create_peripheral(ms->spi2.bus, ms->x4 ? TYPE_XTEINK_X4_EINK : TYPE_XTEINK_X3_EINK); qemu_input_handler_register(panel, &xteink_key_handler); qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 21, qdev_get_gpio_in_named(panel, SSI_GPIO_CS, 0)); qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 4, qdev_get_gpio_in_named(panel, XTEINK_X3_EINK_DC, 0)); qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 5, qdev_get_gpio_in_named(panel, XTEINK_X3_EINK_RESET, 0)); qdev_connect_gpio_out_named(panel, XTEINK_X3_EINK_BUSY, 0, qdev_get_gpio_in_named(DEVICE(&ms->gpio), ESP32_GPIO_INPUT, 6)); device_cold_reset(panel); qemu_set_irq(qdev_get_gpio_in_named(panel, SSI_GPIO_CS, 0), 1); DeviceState *sd_adapter = qdev_new("ssi-sd"); qdev_prop_set_uint8(sd_adapter, "cs", 1); qdev_realize_and_unref(sd_adapter, BUS(ms->spi2.bus), &error_fatal); qdev_connect_gpio_out_named(DEVICE(&ms->gpio), ESP32_GPIO_OUTPUT, 12, qdev_get_gpio_in_named(sd_adapter, SSI_GPIO_CS, 0)); qemu_set_irq(qdev_get_gpio_in_named(sd_adapter, SSI_GPIO_CS, 0), 1); DriveInfo *dinfo = drive_get(IF_SD, 0, 0); DeviceState *sd_card = qdev_new(TYPE_SD_CARD_SPI); qdev_prop_set_drive_err(sd_card, "drive", dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, &error_fatal); qdev_realize_and_unref(sd_card, qdev_get_child_bus(sd_adapter, "sd-bus"), &error_fatal); } /* 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 char *xteink_get_variant(Object *obj, Error **errp) { return g_strdup(ESP32C3_MACHINE(obj)->x4 ? "x4" : "x3"); } static void xteink_set_variant(Object *obj, const char *value, Error **errp) { Esp32C3MachineState *ms = ESP32C3_MACHINE(obj); if (g_str_equal(value, "x3")) { ms->x4 = false; } else if (g_str_equal(value, "x4")) { ms->x4 = true; } else { error_setg(errp, "invalid variant '%s' (expected x3 or x4)", value); } } static void xteink_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "xteink X3/X4 (ESP32-C3); variant=x3|x4"; mc->init = xteink_machine_init; object_class_property_add_str(oc, "variant", xteink_get_variant, xteink_set_variant); object_class_property_set_description(oc, "variant", "xteink model: x3 (default) or x4"); } static const TypeInfo xteink_info = { .name = MACHINE_TYPE_NAME("xteink"), .parent = TYPE_ESP32C3_MACHINE, .class_init = xteink_machine_class_init, }; static void esp32c3_machine_type_init(void) { type_register_static(&esp32c3_info); type_register_static(&xteink_info); } type_init(esp32c3_machine_type_init);