From 3dc0ad24df9709565c52eae5e421cef074767eb6 Mon Sep 17 00:00:00 2001 From: Davids Paskevics <davip00@mi.fu-berlin.de> Date: Tue, 20 Jun 2023 13:26:30 +0200 Subject: [PATCH] Initial work on ESP32-C3 UART --- hw/char/esp32_c3_uart.c | 267 ++++++++++++++++++++++++++++++++ hw/char/meson.build | 1 + hw/char/trace-events | 4 + hw/riscv/esp32_c3.c | 17 +- include/hw/char/esp32_c3_uart.h | 67 ++++++++ include/hw/riscv/esp32_c3.h | 3 + 6 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 hw/char/esp32_c3_uart.c create mode 100644 include/hw/char/esp32_c3_uart.h diff --git a/hw/char/esp32_c3_uart.c b/hw/char/esp32_c3_uart.c new file mode 100644 index 00000000000..4a8e0b163a4 --- /dev/null +++ b/hw/char/esp32_c3_uart.c @@ -0,0 +1,267 @@ +/* + * ESP32-C3 SoC UART 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 + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/char/esp32_c3_uart.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "migration/vmstate.h" +#include "trace.h" + +static uint64_t uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + /* Note that this is for reading I/O memory, not FIFO */ + /* + ESP32C3UARTState *s = ESP32_C3_UART(opaque); + + uint64_t r; + TODO: Check if enabled + if (!s->rx_enabled) { + return 0; + } + + switch (addr) { + default: + r = s->regs[addr / 4]; + break; + } + + trace_esp32_c3_uart_read(addr, r, size); + + return r; + */ + return 0; +} + +static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque) +{ + /* + ESP32C3UARTState *s = ESP32_C3_UART(opaque); + int r; + if (s->tx_fifo_pos == 0) { + return FALSE; + } + */ + /* TODO: Bounds check / wraparound */ + /* + uint8_t c = s->tx_fifo[s->tx_fifo_pos]; + s->tx_fifo_pos++; + + r = qemu_chr_fe_write(&s->chr, &c, 1); + return TRUE; + if (r <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + if (!s->watch_tag) { + goto buffer_drained; + } + return FALSE; + } + +buffer_drained: + s->reg[R_UART_TXDRDY] = 1; + s->pending_tx_byte = false; + return FALSE; + */ + return FALSE; + +} + +static void uart_cancel_transmit(ESP32C3UARTState *s) +{ + /* + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } + */ +} + +static void uart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + ESP32C3UARTState *s = ESP32_C3_UART(opaque); + + trace_esp32_c3_uart_write(addr, value, size); + + /* TODO: Check if enabled */ + + switch (addr) { + case A_UART_FIFO: + if (s->tx_fifo_pos < ESP32_C3_UART_FIFO_LENGTH) { + s->tx_fifo[s->tx_fifo_pos] = value; + s->tx_fifo_pos++; + s->pending_tx = true; + } else { + /* TODO: Log */ + return; + } + uart_transmit(NULL, G_IO_OUT, s); + break; + default: + /* TODO: Log */ + s->regs[addr / 4] = value; + break; + } + /* TODO: Update IRQ */ +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32_c3_uart_reset(DeviceState *dev) +{ + ESP32C3UARTState *s = ESP32_C3_UART(dev); + + uart_cancel_transmit(s); + + /* TODO: Fill regs w/ defaults per TRM */ + memset(s->regs, 0, sizeof(s->regs)); + memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); + memset(s->tx_fifo, 0, sizeof(s->tx_fifo)); + + s->rx_fifo_pos = 0; + s->tx_fifo_pos = 0; + s->pending_tx = false; +} + +static void uart_receive(void *opaque, const uint8_t *buf, int size) +{ + + ESP32C3UARTState *s = ESP32_C3_UART(opaque); + int i; + + if (size == 0 || s->rx_fifo_pos >= ESP32_C3_UART_FIFO_LENGTH) { + // TODO: Log + return; + } + + for (i = 0; i < size; i++) { + uint32_t pos = s->rx_fifo_pos % ESP32_C3_UART_FIFO_LENGTH; + s->rx_fifo[pos] = buf[i]; + s->rx_fifo_pos++; + } + + // TODO: Signal RX ready +} + +static int uart_can_receive(void *opaque) +{ + ESP32C3UARTState *s = ESP32_C3_UART(opaque); + + /* Does FIFO have space? */ + // TODO: Check whether receiver is enabled + if (s->rx_fifo_pos < ESP32_C3_UART_FIFO_LENGTH) { + return ESP32_C3_UART_FIFO_LENGTH - s->rx_fifo_pos; + } + return 0; +} + +static void uart_event(void *opaque, QEMUChrEvent event) +{ + // ESP32C3UARTState *s = ESP32_C3_UART(opaque); + + if (event == CHR_EVENT_BREAK) { + /* TODO: Register as error */ + /* TODO: Trigger error IRQ */ + } +} + +static void esp32_c3_uart_realize(DeviceState *dev, Error **errp) +{ + ESP32C3UARTState *s = ESP32_C3_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, + uart_event, NULL, s, NULL, true); +} + +static void esp32_c3_uart_init(Object *obj) +{ + ESP32C3UARTState *s = ESP32_C3_UART(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &uart_ops, s, + TYPE_ESP32_C3_UART, ESP32_C3_UART_IOMEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + /* sysbus_init_irq(sbd, &s->irq); */ +} + +static int esp32_c3_uart_post_load(void *opaque, int version_id) +{ + // ESP32C3UARTState *s = ESP32_C3_UART(opaque); + + /* + + if (s->pending_tx_byte) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + } + + */ + + return 0; +} + +static const VMStateDescription esp32_c3_uart_vmstate = { + .name = TYPE_ESP32_C3_UART, + .post_load = esp32_c3_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, ESP32C3UARTState, ESP32_C3_UART_IOMEM_SIZE_WORDS), + VMSTATE_UINT8_ARRAY(rx_fifo, ESP32C3UARTState, ESP32_C3_UART_FIFO_LENGTH), + VMSTATE_UINT8_ARRAY(tx_fifo, ESP32C3UARTState, ESP32_C3_UART_FIFO_LENGTH), + VMSTATE_UINT32(rx_fifo_pos, ESP32C3UARTState), + VMSTATE_UINT32(tx_fifo_pos, ESP32C3UARTState), + VMSTATE_END_OF_LIST() + } +}; + +static Property esp32_c3_uart_properties[] = { + DEFINE_PROP_CHR("chardev", ESP32C3UARTState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void esp32_c3_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = esp32_c3_uart_reset; + dc->realize = esp32_c3_uart_realize; + device_class_set_props(dc, esp32_c3_uart_properties); + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->vmsd = &esp32_c3_uart_vmstate; +} + +static const TypeInfo esp32_c3_uart_info = { + .name = TYPE_ESP32_C3_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ESP32C3UARTState), + .instance_init = esp32_c3_uart_init, + .class_init = esp32_c3_uart_class_init +}; + +static void esp32_c3_uart_register_types(void) +{ + type_register_static(&esp32_c3_uart_info); +} + +type_init(esp32_c3_uart_register_types) diff --git a/hw/char/meson.build b/hw/char/meson.build index 006d20f1e25..f5c51d51f00 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -24,6 +24,7 @@ system_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c')) system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) +system_ss.add(when: 'CONFIG_ESP32_C3', if_true: files('esp32_c3_uart.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) diff --git a/hw/char/trace-events b/hw/char/trace-events index 2ecb36232e9..049b5f0e7fe 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -105,3 +105,7 @@ cadence_uart_baudrate(unsigned baudrate) "baudrate %u" # sh_serial.c sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64 sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64 + +# esp32_c3_uart.c +esp32_c3_uart_read(uint64_t addr, uint64_t r, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" +esp32_c3_uart_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" diff --git a/hw/riscv/esp32_c3.c b/hw/riscv/esp32_c3.c index 6d7f425da3a..21d1bb3f2b3 100644 --- a/hw/riscv/esp32_c3.c +++ b/hw/riscv/esp32_c3.c @@ -16,12 +16,13 @@ */ #include "qemu/osdep.h" + +#include "hw/char/esp32_c3_uart.h" #include "qemu/log.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" @@ -154,6 +155,8 @@ type_init(esp32_c3_machine_type_info_register) static void esp32_c3_soc_state_realize(DeviceState *dev, Error **errp) { + MemoryRegion* uart_mem; + MachineState *ms = MACHINE(qdev_get_machine()); (void)ms; Esp32C3SoCState *sss = RISCV_ESP32_SOC(dev); @@ -195,6 +198,14 @@ static void esp32_c3_soc_state_realize(DeviceState *dev, Error **errp) esp32_c3_clkcfg, &sss->clock, -1); create_unimplemented_device("riscv.esp32.c.sysregs", esp32_c3_sysregs, esp32_c3_sysregs_size); + /* Register implemented peripherals */ + qdev_prop_set_chr(DEVICE(&(sss->uart)), "chardev", serial_hd(0)); + if (!sysbus_realize(SYS_BUS_DEVICE(&sss->uart), errp)) { + return; + } + uart_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(&sss->uart), 0); + memory_region_add_subregion_overlap(&sss->container, ESP32_C3_UART0_BASE, uart_mem, 0); + /* Load boot ROM dump into emulated ROM */ riscv_load_firmware(ms->firmware, esp32_c3_memmap[ESP32_C3_IROM].base, NULL); } @@ -227,6 +238,10 @@ static void esp32_c3_soc_instance_init(Object *obj) TYPE_RISCV_CPU_ESP32_C3, &error_abort); object_property_set_int(OBJECT(&sss->cpus), "num-harts", 1, &error_abort); + + /* Init peripherals */ + object_initialize_child(obj, "uart", &sss->uart, TYPE_ESP32_C3_UART); + //object_property_add_alias(obj, "serial0", &sss->uart, "chardev"); } static const TypeInfo esp32_c3_type_info = { diff --git a/include/hw/char/esp32_c3_uart.h b/include/hw/char/esp32_c3_uart.h new file mode 100644 index 00000000000..b06769f2bba --- /dev/null +++ b/include/hw/char/esp32_c3_uart.h @@ -0,0 +1,67 @@ +/* + * ESP32-C3 SoC UART 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 + */ + +#ifndef ESP32_C3_UART_H +#define ESP32_C3_UART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/registerfields.h" +#include "qom/object.h" + + +#define TYPE_ESP32_C3_UART "esp32c3-uart" +#define ESP32_C3_UART(obj) \ + OBJECT_CHECK(ESP32C3UARTState, (obj), TYPE_ESP32_C3_UART) + +#define ESP32_C3_UART0_BASE 0x60000000 +#define ESP32_C3_UART1_BASE 0x60001000 +#define ESP32_C3_UART_IOMEM_SIZE 0x1000 /* 4K */ +#define ESP32_C3_UART_IOMEM_SIZE_WORDS (ESP32_C3_UART_IOMEM_SIZE / 4) + +#define ESP32_C3_UART_FIFO_LENGTH 128 + +/* These are all relative to the base address */ + +REG32(UART_FIFO, 0x0000) /* FIFO read / write operations */ +REG32(UART_CLKDIV, 0x0014) +REG32(UART_RX_FILT, 0x0018) +REG32(UART_CONF0, 0x0020) + FIELD(UART_CONF0, PARITY, 0, 1) /* Parity config */ + FIELD(UART_CONF0, PARITY_EN, 1, 1) /* Parity enable */ + FIELD(UART_CONF0, BIT_NUM, 2, 1) /* Number of bits per transmission */ + FIELD(UART_CONF0, STOP_BIT_NUM, 4, 1) /* Number of stop bits per transmission */ +REG32(UART_CLK_CONF, 0x0078) +REG32(UART_ID, 0x0080) + +typedef struct { + SysBusDevice parent_obj; + CharBackend chr; /* Tell qemu this is a character device */ + + MemoryRegion iomem; /* Config registers memory region */ + uint32_t regs[ESP32_C3_UART_IOMEM_SIZE_WORDS]; /* Actual contents of config registers */ + + MemoryRegion fifo; /* FIFO RAM memory region */ + uint8_t rx_fifo[ESP32_C3_UART_FIFO_LENGTH]; /* RX FIFO RAM */ + uint8_t tx_fifo[ESP32_C3_UART_FIFO_LENGTH]; /* TX FIFO RAM */ + + unsigned int rx_fifo_pos; /* How many bytes are waiting in RX FIFO */ + unsigned int tx_fifo_pos; /* How many bytes are waiting in TX FIFO */ + bool pending_tx; +} ESP32C3UARTState; + +#endif diff --git a/include/hw/riscv/esp32_c3.h b/include/hw/riscv/esp32_c3.h index cb799764f56..80f2559e67d 100644 --- a/include/hw/riscv/esp32_c3.h +++ b/include/hw/riscv/esp32_c3.h @@ -25,6 +25,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/boards.h" #include "qemu/typedefs.h" +#include "hw/char/esp32_c3_uart.h" @@ -48,6 +49,8 @@ typedef struct Esp32C3SoCState { /* Memory mapped I/O devices */ MemoryRegion container; MemoryRegion clock; + /* Misc. devices */ + ESP32C3UARTState uart; } Esp32C3SoCState; -- GitLab