diff --git a/hw/char/esp32_c3_uart.c b/hw/char/esp32_c3_uart.c index 4a8e0b163a42d309ba4a97ee20f8f19419bfbd4b..5689bb3e42774cf9bfb472e92cf7824df3fb768b 100644 --- a/hw/char/esp32_c3_uart.c +++ b/hw/char/esp32_c3_uart.c @@ -24,68 +24,158 @@ #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "trace.h" +#include "trace/trace-hw_char.h" + +static void uart_rx_fifo_push(ESP32C3UARTState* s, uint8_t byte) +{ + s->rx_fifo[s->rx_fifo_head] = byte; + s->rx_fifo_head++; + + if (s->rx_fifo_head >= ESP32_C3_UART_FIFO_LENGTH) { + s->rx_fifo_head = 0; + } + + /* Increment number of bytes in RX FIFO tracked by control register */ + uint32_t bytes = s->regs[R_UART_STATUS] & R_UART_STATUS_UART_RXFIFO_CNT_MASK; + bytes += 1; + s->regs[R_UART_STATUS] |= bytes; +} + +static void uart_tx_fifo_push(ESP32C3UARTState* s, uint8_t byte) +{ + s->tx_fifo[s->tx_fifo_head] = byte; + s->tx_fifo_head++; + + if (s->tx_fifo_head >= ESP32_C3_UART_FIFO_LENGTH) { + s->tx_fifo_head = 0; + } + + /* Increment number of bytes in TX FIFO tracked by control register */ + uint32_t bytes = s->regs[R_UART_STATUS] & R_UART_STATUS_UART_TXFIFO_CNT_MASK; + bytes += 1; + s->regs[R_UART_STATUS] |= bytes; +} + +static bool uart_rx_fifo_pull(ESP32C3UARTState* s, uint8_t *out) +{ + if (s->rx_fifo_tail > s->rx_fifo_head) { + trace_esp32_c3_uart_rx_fifo_underrun(); + return FALSE; + } + + *out = s->rx_fifo[s->rx_fifo_tail]; + + s->rx_fifo_tail++; + + if (s->rx_fifo_tail >= ESP32_C3_UART_FIFO_LENGTH) { + s->rx_fifo_tail = 0; + } + + /* Decrement number of bytes in RX FIFO tracked by control register */ + uint32_t bytes = s->regs[R_UART_STATUS] & R_UART_STATUS_UART_RXFIFO_CNT_MASK; + bytes -= 1; + s->regs[R_UART_STATUS] |= bytes; + + return TRUE; +} + +static bool uart_tx_fifo_pull(ESP32C3UARTState* s, uint8_t *out) +{ + if (s->tx_fifo_tail > s->tx_fifo_head) { + trace_esp32_c3_uart_tx_fifo_underrun(); + return FALSE; + } + + *out = s->tx_fifo[s->tx_fifo_tail]; + + s->tx_fifo_tail++; + + if (s->tx_fifo_tail >= ESP32_C3_UART_FIFO_LENGTH) { + s->tx_fifo_tail = 0; + } + + /* Decrement number of bytes in TX FIFO tracked by control register */ + uint32_t bytes = s->regs[R_UART_STATUS] & R_UART_STATUS_UART_TXFIFO_CNT_MASK; + bytes -= 1; + s->regs[R_UART_STATUS] |= bytes; + + return TRUE; +} static uint64_t uart_read(void *opaque, hwaddr addr, unsigned int size) { - /* Note that this is for reading I/O memory, not FIFO */ - /* + /* Note that this is for reading I/O memory, not FIFO memory */ ESP32C3UARTState *s = ESP32_C3_UART(opaque); - uint64_t r; - TODO: Check if enabled - if (!s->rx_enabled) { - return 0; - } + uint8_t byte; switch (addr) { + case A_UART_FIFO: + if (!uart_rx_fifo_pull(s, &byte)) { + r = 0; + } else { + r = byte; + } + break; default: r = s->regs[addr / 4]; break; } - trace_esp32_c3_uart_read(addr, r, size); + 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) { + + if (s->tx_fifo_head == 0) { + /* Don't have any data */ return FALSE; } - */ + /* TODO: Bounds check / wraparound */ - /* - uint8_t c = s->tx_fifo[s->tx_fifo_pos]; - s->tx_fifo_pos++; + uint8_t c; + if (!uart_tx_fifo_pull(s, &c)) { + /* FIFO underrun */ + return FALSE; + } r = qemu_chr_fe_write(&s->chr, &c, 1); return TRUE; + + + /* TODO: Raise interrupt */ + + /* TODO: Figure out what this watch_tag thing is about */ + 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) { + /* TODO: Figure out what this watch_tag business is about */ /* if (s->watch_tag) { g_source_remove(s->watch_tag); @@ -97,30 +187,29 @@ static void uart_cancel_transmit(ESP32C3UARTState *s) static void uart_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { + uint8_t byte; 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; - } + /* + * FIXME: + * The hardware doesn't allow writing this register. + * We only allow it here until sending is implemented properly. + */ + byte = (value & R_UART_FIFO_UART_RXFIFO_RD_BYTE_MASK) << R_UART_FIFO_UART_RXFIFO_RD_BYTE_SHIFT; + uart_tx_fifo_push(s, byte); uart_transmit(NULL, G_IO_OUT, s); break; default: - /* TODO: Log */ + qemu_log_mask(LOG_UNIMP, "Write to unimplemented UART control register may not have any effect\n"); s->regs[addr / 4] = value; break; } - /* TODO: Update IRQ */ + + /* TODO: Update IRQ (once interrupt controller is implemented) */ } static const MemoryRegionOps uart_ops = { @@ -140,41 +229,38 @@ static void esp32_c3_uart_reset(DeviceState *dev) 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->rx_fifo_head = 0; + s->rx_fifo_tail = 0; + s->tx_fifo_head = 0; + s->tx_fifo_tail = 0; s->pending_tx = false; + s->rx_enabled = false; + s->tx_enabled = 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; - } + ESP32C3UARTState *s = ESP32_C3_UART(opaque); 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++; + uart_rx_fifo_push(s, buf[i]); } - // TODO: Signal RX ready + /* TODO: Raise interrupt */ + } 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; + /* Is receiving enabled? */ + if (!s->rx_enabled) { + return 0; } - return 0; + + return 1; } static void uart_event(void *opaque, QEMUChrEvent event) @@ -208,9 +294,9 @@ static void esp32_c3_uart_init(Object *obj) static int esp32_c3_uart_post_load(void *opaque, int version_id) { - // ESP32C3UARTState *s = ESP32_C3_UART(opaque); - + /* TODO: Send pending bytes */ /* + 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, @@ -229,8 +315,13 @@ static const VMStateDescription esp32_c3_uart_vmstate = { 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_UINT32(rx_fifo_head, ESP32C3UARTState), + VMSTATE_UINT32(rx_fifo_tail, ESP32C3UARTState), + VMSTATE_UINT32(tx_fifo_head, ESP32C3UARTState), + VMSTATE_UINT32(tx_fifo_tail, ESP32C3UARTState), + VMSTATE_BOOL(rx_enabled, ESP32C3UARTState), + VMSTATE_BOOL(tx_enabled, ESP32C3UARTState), + VMSTATE_BOOL(pending_tx, ESP32C3UARTState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/char/trace-events b/hw/char/trace-events index 049b5f0e7fe8b47b03df04fae0f97a0f553899ed..3da84626d4742ba15af5b496e94dfd5a9894c7e6 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -109,3 +109,10 @@ sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size % # 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" +esp32_c3_uart_tx(void) "UART TX" +esp32_c3_uart_rx(void) "UART RX" +esp32_c3_uart_rx_fifo_read(void) "UART RX FIFO read" +esp32_c3_uart_rx_fifo_underrun(void) "UART RX FIFO underrun, no data" +esp32_c3_uart_tx_fifo_underrun(void) "UART TX FIFO underrun, no data" +esp32_c3_uart_read_disabled(void) "UART read despite RX being disabled" +esp32_c3_uart_write_disabled(void) "UART write despite TX being disabled" diff --git a/include/hw/char/esp32_c3_uart.h b/include/hw/char/esp32_c3_uart.h index b06769f2bba84a363918396ec1ac5c9d6bf9e4ac..3b5a48db14199bbe9b4bf5fd62ddfbb370db49c7 100644 --- a/include/hw/char/esp32_c3_uart.h +++ b/include/hw/char/esp32_c3_uart.h @@ -37,9 +37,13 @@ /* These are all relative to the base address */ -REG32(UART_FIFO, 0x0000) /* FIFO read / write operations */ +REG32(UART_FIFO, 0x0000) /* FIFO read via this register */ + FIELD(UART_FIFO, UART_RXFIFO_RD_BYTE, 0, 8); REG32(UART_CLKDIV, 0x0014) REG32(UART_RX_FILT, 0x0018) +REG32(UART_STATUS, 0x001C) + FIELD(UART_STATUS, UART_RXFIFO_CNT, 0, 10) + FIELD(UART_STATUS, UART_TXFIFO_CNT, 16, 10) REG32(UART_CONF0, 0x0020) FIELD(UART_CONF0, PARITY, 0, 1) /* Parity config */ FIELD(UART_CONF0, PARITY_EN, 1, 1) /* Parity enable */ @@ -55,12 +59,16 @@ typedef struct { 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 */ + MemoryRegion fifo; /* UART 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 */ + unsigned int rx_fifo_head; /* Head of RX FIFO (bytes are appended here) */ + unsigned int rx_fifo_tail; /* Tail of RX FIFO (bytes are read from here) */ + unsigned int tx_fifo_head; /* Head of TX FIFO (bytes are appended here) */ + unsigned int tx_fifo_tail; /* Tail of TX FIFO (bytes are read from here) */ + bool rx_enabled; + bool tx_enabled; bool pending_tx; } ESP32C3UARTState;