Skip to content
Snippets Groups Projects
Commit 3dc0ad24 authored by davip00's avatar davip00
Browse files

Initial work on ESP32-C3 UART

parent 01a96326
Branches
No related tags found
No related merge requests found
/*
* 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)
......@@ -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'))
......
......@@ -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"
......@@ -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 = {
......
/*
* 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
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment