Skip to content
Snippets Groups Projects
Commit 31c5762c authored by davip00's avatar davip00
Browse files

Flesh out ESP32-C3 UART FIFO implementation

parent 3dc0ad24
No related branches found
No related tags found
No related merge requests found
...@@ -24,68 +24,158 @@ ...@@ -24,68 +24,158 @@
#include "hw/qdev-properties-system.h" #include "hw/qdev-properties-system.h"
#include "migration/vmstate.h" #include "migration/vmstate.h"
#include "trace.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) 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); ESP32C3UARTState *s = ESP32_C3_UART(opaque);
uint64_t r; uint64_t r;
TODO: Check if enabled uint8_t byte;
if (!s->rx_enabled) {
return 0;
}
switch (addr) { switch (addr) {
case A_UART_FIFO:
if (!uart_rx_fifo_pull(s, &byte)) {
r = 0;
} else {
r = byte;
}
break;
default: default:
r = s->regs[addr / 4]; r = s->regs[addr / 4];
break; break;
} }
trace_esp32_c3_uart_read(addr, r, size);
trace_esp32_c3_uart_read(addr, r, size);
return r; return r;
*/
return 0;
} }
static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque) static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque)
{ {
/*
ESP32C3UARTState *s = ESP32_C3_UART(opaque); ESP32C3UARTState *s = ESP32_C3_UART(opaque);
int r; int r;
if (s->tx_fifo_pos == 0) {
if (s->tx_fifo_head == 0) {
/* Don't have any data */
return FALSE; return FALSE;
} }
*/
/* TODO: Bounds check / wraparound */ /* TODO: Bounds check / wraparound */
/* uint8_t c;
uint8_t c = s->tx_fifo[s->tx_fifo_pos]; if (!uart_tx_fifo_pull(s, &c)) {
s->tx_fifo_pos++; /* FIFO underrun */
return FALSE;
}
r = qemu_chr_fe_write(&s->chr, &c, 1); r = qemu_chr_fe_write(&s->chr, &c, 1);
return TRUE; return TRUE;
/* TODO: Raise interrupt */
/* TODO: Figure out what this watch_tag thing is about */
if (r <= 0) { if (r <= 0) {
/*
s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
uart_transmit, s); uart_transmit, s);
if (!s->watch_tag) { if (!s->watch_tag) {
goto buffer_drained; goto buffer_drained;
} }
*/
return FALSE; return FALSE;
} }
/*
buffer_drained: buffer_drained:
s->reg[R_UART_TXDRDY] = 1; s->reg[R_UART_TXDRDY] = 1;
s->pending_tx_byte = false; s->pending_tx_byte = false;
return FALSE; return FALSE;
*/
return FALSE; return FALSE;
*/
} }
static void uart_cancel_transmit(ESP32C3UARTState *s) static void uart_cancel_transmit(ESP32C3UARTState *s)
{ {
/* TODO: Figure out what this watch_tag business is about */
/* /*
if (s->watch_tag) { if (s->watch_tag) {
g_source_remove(s->watch_tag); g_source_remove(s->watch_tag);
...@@ -97,30 +187,29 @@ static void uart_cancel_transmit(ESP32C3UARTState *s) ...@@ -97,30 +187,29 @@ static void uart_cancel_transmit(ESP32C3UARTState *s)
static void uart_write(void *opaque, hwaddr addr, static void uart_write(void *opaque, hwaddr addr,
uint64_t value, unsigned int size) uint64_t value, unsigned int size)
{ {
uint8_t byte;
ESP32C3UARTState *s = ESP32_C3_UART(opaque); ESP32C3UARTState *s = ESP32_C3_UART(opaque);
trace_esp32_c3_uart_write(addr, value, size); trace_esp32_c3_uart_write(addr, value, size);
/* TODO: Check if enabled */
switch (addr) { switch (addr) {
case A_UART_FIFO: case A_UART_FIFO:
if (s->tx_fifo_pos < ESP32_C3_UART_FIFO_LENGTH) { /*
s->tx_fifo[s->tx_fifo_pos] = value; * FIXME:
s->tx_fifo_pos++; * The hardware doesn't allow writing this register.
s->pending_tx = true; * We only allow it here until sending is implemented properly.
} else { */
/* TODO: Log */ byte = (value & R_UART_FIFO_UART_RXFIFO_RD_BYTE_MASK) << R_UART_FIFO_UART_RXFIFO_RD_BYTE_SHIFT;
return; uart_tx_fifo_push(s, byte);
}
uart_transmit(NULL, G_IO_OUT, s); uart_transmit(NULL, G_IO_OUT, s);
break; break;
default: 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; s->regs[addr / 4] = value;
break; break;
} }
/* TODO: Update IRQ */
/* TODO: Update IRQ (once interrupt controller is implemented) */
} }
static const MemoryRegionOps uart_ops = { static const MemoryRegionOps uart_ops = {
...@@ -140,43 +229,40 @@ static void esp32_c3_uart_reset(DeviceState *dev) ...@@ -140,43 +229,40 @@ static void esp32_c3_uart_reset(DeviceState *dev)
memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); memset(s->rx_fifo, 0, sizeof(s->rx_fifo));
memset(s->tx_fifo, 0, sizeof(s->tx_fifo)); memset(s->tx_fifo, 0, sizeof(s->tx_fifo));
s->rx_fifo_pos = 0; s->rx_fifo_head = 0;
s->tx_fifo_pos = 0; s->rx_fifo_tail = 0;
s->tx_fifo_head = 0;
s->tx_fifo_tail = 0;
s->pending_tx = false; s->pending_tx = false;
s->rx_enabled = false;
s->tx_enabled = false;
} }
static void uart_receive(void *opaque, const uint8_t *buf, int size) static void uart_receive(void *opaque, const uint8_t *buf, int size)
{ {
ESP32C3UARTState *s = ESP32_C3_UART(opaque);
int i; int i;
ESP32C3UARTState *s = ESP32_C3_UART(opaque);
if (size == 0 || s->rx_fifo_pos >= ESP32_C3_UART_FIFO_LENGTH) {
// TODO: Log
return;
}
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
uint32_t pos = s->rx_fifo_pos % ESP32_C3_UART_FIFO_LENGTH; uart_rx_fifo_push(s, buf[i]);
s->rx_fifo[pos] = buf[i];
s->rx_fifo_pos++;
} }
// TODO: Signal RX ready /* TODO: Raise interrupt */
} }
static int uart_can_receive(void *opaque) static int uart_can_receive(void *opaque)
{ {
ESP32C3UARTState *s = ESP32_C3_UART(opaque); ESP32C3UARTState *s = ESP32_C3_UART(opaque);
/* Does FIFO have space? */ /* Is receiving enabled? */
// TODO: Check whether receiver is enabled if (!s->rx_enabled) {
if (s->rx_fifo_pos < ESP32_C3_UART_FIFO_LENGTH) {
return ESP32_C3_UART_FIFO_LENGTH - s->rx_fifo_pos;
}
return 0; return 0;
} }
return 1;
}
static void uart_event(void *opaque, QEMUChrEvent event) static void uart_event(void *opaque, QEMUChrEvent event)
{ {
// ESP32C3UARTState *s = ESP32_C3_UART(opaque); // ESP32C3UARTState *s = ESP32_C3_UART(opaque);
...@@ -208,9 +294,9 @@ static void esp32_c3_uart_init(Object *obj) ...@@ -208,9 +294,9 @@ static void esp32_c3_uart_init(Object *obj)
static int esp32_c3_uart_post_load(void *opaque, int version_id) 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) { if (s->pending_tx_byte) {
s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, 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 = { ...@@ -229,8 +315,13 @@ static const VMStateDescription esp32_c3_uart_vmstate = {
VMSTATE_UINT32_ARRAY(regs, ESP32C3UARTState, ESP32_C3_UART_IOMEM_SIZE_WORDS), 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(rx_fifo, ESP32C3UARTState, ESP32_C3_UART_FIFO_LENGTH),
VMSTATE_UINT8_ARRAY(tx_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(rx_fifo_head, ESP32C3UARTState),
VMSTATE_UINT32(tx_fifo_pos, 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() VMSTATE_END_OF_LIST()
} }
}; };
......
...@@ -109,3 +109,10 @@ sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size % ...@@ -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.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_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_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"
...@@ -37,9 +37,13 @@ ...@@ -37,9 +37,13 @@
/* These are all relative to the base address */ /* 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_CLKDIV, 0x0014)
REG32(UART_RX_FILT, 0x0018) 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) REG32(UART_CONF0, 0x0020)
FIELD(UART_CONF0, PARITY, 0, 1) /* Parity config */ FIELD(UART_CONF0, PARITY, 0, 1) /* Parity config */
FIELD(UART_CONF0, PARITY_EN, 1, 1) /* Parity enable */ FIELD(UART_CONF0, PARITY_EN, 1, 1) /* Parity enable */
...@@ -55,12 +59,16 @@ typedef struct { ...@@ -55,12 +59,16 @@ typedef struct {
MemoryRegion iomem; /* Config registers memory region */ MemoryRegion iomem; /* Config registers memory region */
uint32_t regs[ESP32_C3_UART_IOMEM_SIZE_WORDS]; /* Actual contents of config registers */ 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 rx_fifo[ESP32_C3_UART_FIFO_LENGTH]; /* RX FIFO RAM */
uint8_t tx_fifo[ESP32_C3_UART_FIFO_LENGTH]; /* TX 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 rx_fifo_head; /* Head of RX FIFO (bytes are appended here) */
unsigned int tx_fifo_pos; /* How many bytes are waiting in TX FIFO */ 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; bool pending_tx;
} ESP32C3UARTState; } ESP32C3UARTState;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment