[PATCH qemu v2 3/7] ot_uart: update register defs, switch to Fifo8 for tx/rx buffers

~lexbaileylowrisc posted 7 patches 3 days, 7 hours ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, Alistair Francis <Alistair.Francis@wdc.com>, Palmer Dabbelt <palmer@dabbelt.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Chao Liu <chao.liu.zevorn@gmail.com>
[PATCH qemu v2 3/7] ot_uart: update register defs, switch to Fifo8 for tx/rx buffers
Posted by ~lexbaileylowrisc 4 days, 5 hours ago
From: Lex Bailey <lex.bailey@lowrisc.org>

The register definitions for the UART device need to be updated to match
the documentation at https://opentitan.org/book/hw/ip/uart/doc/registers.html

This commit does that, and also switches to using Fifo8 for managing the
transmit and receive buffers.

Signed-off-by: Lex Bailey <lex.bailey@lowrisc.org>
---
 hw/char/ot_uart.c         | 332 ++++++++++++++++++++++----------------
 include/hw/char/ot_uart.h |  18 +--
 2 files changed, 196 insertions(+), 154 deletions(-)

diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
index 2cf0d73cf5..3bf3295b1b 100644
--- a/hw/char/ot_uart.c
+++ b/hw/char/ot_uart.c
@@ -27,6 +27,7 @@
 
 #include "qemu/osdep.h"
 #include "hw/char/ot_uart.h"
+#include "qemu/fifo8.h"
 #include "hw/core/irq.h"
 #include "hw/core/qdev-clock.h"
 #include "hw/core/qdev-properties.h"
@@ -36,17 +37,24 @@
 #include "qemu/log.h"
 #include "qemu/module.h"
 
+/* clang-format off */
 REG32(INTR_STATE, 0x00)
-    FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
-    FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
-    FIELD(INTR_STATE, TX_EMPTY, 2, 1)
-    FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
+    SHARED_FIELD(INTR_TX_WATERMARK, 0, 1)
+    SHARED_FIELD(INTR_RX_WATERMARK, 1, 1)
+    SHARED_FIELD(INTR_TX_DONE, 2, 1)
+    SHARED_FIELD(INTR_RX_OVERFLOW, 3, 1)
+    SHARED_FIELD(INTR_RX_FRAME_ERR, 4, 1)
+    SHARED_FIELD(INTR_RX_BREAK_ERR, 5, 1)
+    SHARED_FIELD(INTR_RX_TIMEOUT, 6, 1)
+    SHARED_FIELD(INTR_RX_PARITY_ERR, 7, 1)
+    SHARED_FIELD(INTR_TX_EMPTY, 8, 1)
 REG32(INTR_ENABLE, 0x04)
 REG32(INTR_TEST, 0x08)
 REG32(ALERT_TEST, 0x0C)
+    FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
 REG32(CTRL, 0x10)
-    FIELD(CTRL, TX_ENABLE, 0, 1)
-    FIELD(CTRL, RX_ENABLE, 1, 1)
+    FIELD(CTRL, TX, 0, 1)
+    FIELD(CTRL, RX, 1, 1)
     FIELD(CTRL, NF, 2, 1)
     FIELD(CTRL, SLPBK, 4, 1)
     FIELD(CTRL, LLPBK, 5, 1)
@@ -58,43 +66,78 @@ REG32(STATUS, 0x14)
     FIELD(STATUS, TXFULL, 0, 1)
     FIELD(STATUS, RXFULL, 1, 1)
     FIELD(STATUS, TXEMPTY, 2, 1)
+    FIELD(STATUS, TXIDLE, 3, 1)
     FIELD(STATUS, RXIDLE, 4, 1)
     FIELD(STATUS, RXEMPTY, 5, 1)
 REG32(RDATA, 0x18)
+    FIELD(RDATA, RDATA, 0, 8)
 REG32(WDATA, 0x1C)
+    FIELD(WDATA, WDATA, 0, 8)
 REG32(FIFO_CTRL, 0x20)
     FIELD(FIFO_CTRL, RXRST, 0, 1)
     FIELD(FIFO_CTRL, TXRST, 1, 1)
     FIELD(FIFO_CTRL, RXILVL, 2, 3)
-    FIELD(FIFO_CTRL, TXILVL, 5, 2)
+    FIELD(FIFO_CTRL, TXILVL, 5, 3)
 REG32(FIFO_STATUS, 0x24)
-    FIELD(FIFO_STATUS, TXLVL, 0, 5)
-    FIELD(FIFO_STATUS, RXLVL, 16, 5)
+    FIELD(FIFO_STATUS, TXLVL, 0, 8)
+    FIELD(FIFO_STATUS, RXLVL, 16, 8)
 REG32(OVRD, 0x28)
+    FIELD(OVRD, TXEN, 0, 1)
+    FIELD(OVRD, TXVAL, 1, 1)
 REG32(VAL, 0x2C)
+    FIELD(VAL, RX, 0, 16)
 REG32(TIMEOUT_CTRL, 0x30)
+    FIELD(TIMEOUT_CTRL, VAL, 0, 24)
+    FIELD(TIMEOUT_CTRL, EN, 31, 1)
+/* clang-format on */
+
+#define INTR_MASK \
+    (INTR_TX_WATERMARK_MASK | INTR_RX_WATERMARK_MASK | INTR_TX_DONE_MASK | \
+     INTR_RX_OVERFLOW_MASK | INTR_RX_FRAME_ERR_MASK | INTR_RX_BREAK_ERR_MASK | \
+     INTR_RX_TIMEOUT_MASK | INTR_RX_PARITY_ERR_MASK | INTR_TX_EMPTY_MASK)
+
+#define CTRL_MASK \
+    (R_CTRL_TX_MASK | R_CTRL_RX_MASK | R_CTRL_NF_MASK | R_CTRL_SLPBK_MASK | \
+     R_CTRL_LLPBK_MASK | R_CTRL_PARITY_EN_MASK | R_CTRL_PARITY_ODD_MASK | \
+     R_CTRL_RXBLVL_MASK | R_CTRL_NCO_MASK)
+
+#define CTRL_SUP_MASK \
+    (R_CTRL_RX_MASK | R_CTRL_TX_MASK | R_CTRL_SLPBK_MASK | R_CTRL_NCO_MASK)
+
+#define OT_UART_NCO_BITS     16
+#define OT_UART_TX_FIFO_SIZE 128
+#define OT_UART_RX_FIFO_SIZE 128
+
+#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t))
+
+#define R_LAST_REG (R_TIMEOUT_CTRL)
+#define REGS_COUNT (R_LAST_REG + 1u)
+#define REGS_SIZE  (REGS_COUNT * sizeof(uint32_t))
 
 static void ot_uart_update_irqs(OtUARTState *s)
 {
-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+        & INTR_TX_WATERMARK_MASK) {
         qemu_set_irq(s->tx_watermark, 1);
     } else {
         qemu_set_irq(s->tx_watermark, 0);
     }
 
-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+        & INTR_RX_WATERMARK_MASK) {
         qemu_set_irq(s->rx_watermark, 1);
     } else {
         qemu_set_irq(s->rx_watermark, 0);
     }
 
-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE] & INTR_TX_EMPTY_MASK) {
         qemu_set_irq(s->tx_empty, 1);
     } else {
         qemu_set_irq(s->tx_empty, 0);
     }
 
-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+        & INTR_RX_OVERFLOW_MASK) {
         qemu_set_irq(s->rx_overflow, 1);
     } else {
         qemu_set_irq(s->rx_overflow, 0);
@@ -105,126 +148,133 @@ static int ot_uart_can_receive(void *opaque)
 {
     OtUARTState *s = opaque;
 
-    if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
-           && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
-        return 1;
+    if (s->regs[R_CTRL] & R_CTRL_RX_MASK) {
+        return (int)fifo8_num_free(&s->rx_fifo);
     }
 
     return 0;
 }
 
+static uint32_t ot_uart_get_rx_watermark_level(const OtUARTState *s)
+{
+    uint32_t rx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, RXILVL);
+
+    return rx_ilvl < 7 ? (1 << rx_ilvl) : 126;
+}
+
 static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
 {
     OtUARTState *s = opaque;
-    uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
-                            >> R_FIFO_CTRL_RXILVL_SHIFT;
-
-    s->uart_rdata = *buf;
+    uint32_t rx_watermark_level;
+    size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);
 
-    s->uart_status &= ~R_STATUS_RXIDLE_MASK;
-    s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
-    /* The RXFULL is set after receiving a single byte
-     * as the FIFO buffers are not yet implemented.
-     */
-    s->uart_status |= R_STATUS_RXFULL_MASK;
-    s->rx_level += 1;
+    for (int index = 0; index < size; index++) {
+        fifo8_push(&s->rx_fifo, buf[index]);
+    }
 
-    if (size > rx_fifo_level) {
-        s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
+    /* update INTR_STATE */
+    if (count != size) {
+        s->regs[R_INTR_STATE] |= INTR_RX_OVERFLOW_MASK;
+    }
+    rx_watermark_level = ot_uart_get_rx_watermark_level(s);
+    if (rx_watermark_level && size >= rx_watermark_level) {
+        s->regs[R_INTR_STATE] |= INTR_RX_WATERMARK_MASK;
     }
 
     ot_uart_update_irqs(s);
 }
 
-static gboolean ot_uart_xmit(void *do_not_use, GIOCondition cond,
-                             void *opaque)
+static void ot_uart_reset_tx_fifo(OtUARTState *s)
 {
-    OtUARTState *s = opaque;
-    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
-                            >> R_FIFO_CTRL_TXILVL_SHIFT;
-    int ret;
-
-    /* instant drain the fifo when there's no back-end */
-    if (!qemu_chr_fe_backend_connected(&s->chr)) {
-        s->tx_level = 0;
-        return G_SOURCE_REMOVE;
+    fifo8_reset(&s->tx_fifo);
+    s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
+    s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
+    if (s->tx_watermark_level) {
+        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
+        s->tx_watermark_level = 0;
     }
+}
 
-    if (!s->tx_level) {
-        s->uart_status &= ~R_STATUS_TXFULL_MASK;
-        s->uart_status |= R_STATUS_TXEMPTY_MASK;
-        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
-        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
-        ot_uart_update_irqs(s);
-        return G_SOURCE_REMOVE;
+static void ot_uart_reset_rx_fifo(OtUARTState *s)
+{
+    fifo8_reset(&s->rx_fifo);
+    s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;
+    s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;
+    if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {
+        qemu_chr_fe_accept_input(&s->chr);
     }
+}
 
-    ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
+static uint32_t ot_uart_get_tx_watermark_level(const OtUARTState *s)
+{
+    uint32_t tx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, TXILVL);
 
-    if (ret >= 0) {
-        s->tx_level -= ret;
-        memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
-    }
+    return tx_ilvl < 7 ? (1 << tx_ilvl) : 64;
+}
 
-    if (s->tx_level) {
-        guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
-                                        ot_uart_xmit, s);
-        if (!r) {
-            s->tx_level = 0;
-            return G_SOURCE_REMOVE;
-        }
+static void ot_uart_xmit(OtUARTState *s)
+{
+    const uint8_t *buf;
+    uint32_t size;
+    int ret;
+
+    if (fifo8_is_empty(&s->tx_fifo)) {
+        return;
     }
 
-    /* Clear the TX Full bit */
-    if (s->tx_level != OT_UART_TX_FIFO_SIZE) {
-        s->uart_status &= ~R_STATUS_TXFULL_MASK;
+    /* instant drain the fifo when there's no back-end */
+    if (!qemu_chr_fe_backend_connected(&s->chr)) {
+        ot_uart_reset_tx_fifo(s);
+        ot_uart_update_irqs(s);
+        return;
     }
 
-    /* Disable the TX_WATERMARK IRQ */
-    if (s->tx_level < tx_fifo_level) {
-        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
+    /* get a continuous buffer from the FIFO */
+    buf =
+        fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);
+    /* send as much as possible */
+    ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
+    /* if some characters where sent, remove them from the FIFO */
+    if (ret >= 0) {
+        fifo8_drop(&s->tx_fifo, ret);
     }
 
-    /* Set TX empty */
-    if (s->tx_level == 0) {
-        s->uart_status |= R_STATUS_TXEMPTY_MASK;
-        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
+    /* update INTR_STATE */
+    if (fifo8_is_empty(&s->tx_fifo)) {
+        s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
+        s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
+    }
+    if (s->tx_watermark_level &&
+        fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
+        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
+        s->tx_watermark_level = 0;
     }
 
     ot_uart_update_irqs(s);
-    return G_SOURCE_REMOVE;
 }
 
-static void uart_write_tx_fifo(OtUARTState *s, const uint8_t *buf,
-                               int size)
+static void uart_write_tx_fifo(OtUARTState *s, uint8_t val)
 {
-    uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
-                            >> R_FIFO_CTRL_TXILVL_SHIFT;
-
-    if (size > OT_UART_TX_FIFO_SIZE - s->tx_level) {
-        size = OT_UART_TX_FIFO_SIZE - s->tx_level;
+    if (fifo8_is_full(&s->tx_fifo)) {
         qemu_log_mask(LOG_GUEST_ERROR, "ot_uart: TX FIFO overflow");
+        return;
     }
 
-    memcpy(s->tx_fifo + s->tx_level, buf, size);
-    s->tx_level += size;
-
-    if (s->tx_level > 0) {
-        s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
-    }
+    fifo8_push(&s->tx_fifo, val);
 
-    if (s->tx_level >= tx_fifo_level) {
-        s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
-        ot_uart_update_irqs(s);
+    s->tx_watermark_level = ot_uart_get_tx_watermark_level(s);
+    if (fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
+        /*
+         * TX watermark interrupt is raised when FIFO depth goes from above
+         * watermark to below. If we haven't reached watermark, reset cached
+         * watermark level
+         */
+        s->tx_watermark_level = 0;
     }
 
-    if (s->tx_level == OT_UART_TX_FIFO_SIZE) {
-        s->uart_status |= R_STATUS_TXFULL_MASK;
+    if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {
+        ot_uart_xmit(s);
     }
-
-    timer_mod(s->fifo_trigger_handle, current_time +
-              (s->char_tx_time * 4));
 }
 
 static void ot_uart_reset_enter(Object *obj, ResetType type)
@@ -236,17 +286,13 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)
         c->parent_phases.enter(obj, type);
     }
 
-    s->uart_intr_state = 0x00000000;
-    s->uart_intr_state = 0x00000000;
-    s->uart_intr_enable = 0x00000000;
-    s->uart_ctrl = 0x00000000;
-    s->uart_status = 0x0000003c;
-    s->uart_rdata = 0x00000000;
-    s->uart_fifo_ctrl = 0x00000000;
-    s->uart_fifo_status = 0x00000000;
-    s->uart_ovrd = 0x00000000;
-    s->uart_val = 0x00000000;
-    s->uart_timeout_ctrl = 0x00000000;
+    memset(&s->regs[0], 0, sizeof(s->regs));
+
+    s->regs[R_STATUS] = 0x0000003c;
+
+    s->tx_watermark_level = 0;
+    ot_uart_reset_tx_fifo(s);
+    ot_uart_reset_rx_fifo(s);
 
     s->tx_level = 0;
     s->rx_level = 0;
@@ -260,7 +306,7 @@ static uint64_t ot_uart_get_baud(OtUARTState *s)
 {
     uint64_t baud;
 
-    baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
+    baud = ((s->regs[R_CTRL] & R_CTRL_NCO_MASK) >> 16);
     baud *= clock_get_hz(s->f_clk);
     baud >>= 20;
 
@@ -274,10 +320,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
 
     switch (addr >> 2) {
     case R_INTR_STATE:
-        retvalue = s->uart_intr_state;
+        retvalue = s->regs[R_INTR_STATE];
         break;
     case R_INTR_ENABLE:
-        retvalue = s->uart_intr_enable;
+        retvalue = s->regs[R_INTR_ENABLE];
         break;
     case R_INTR_TEST:
         qemu_log_mask(LOG_GUEST_ERROR,
@@ -285,22 +331,22 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
         break;
 
     case R_CTRL:
-        retvalue = s->uart_ctrl;
+        retvalue = s->regs[R_CTRL];
         break;
     case R_STATUS:
-        retvalue = s->uart_status;
+        retvalue = s->regs[R_STATUS];
         break;
 
     case R_RDATA:
-        retvalue = s->uart_rdata;
-        if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
+        retvalue = s->regs[R_RDATA];
+        if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {
             qemu_chr_fe_accept_input(&s->chr);
 
             s->rx_level -= 1;
-            s->uart_status &= ~R_STATUS_RXFULL_MASK;
+            s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;
             if (s->rx_level == 0) {
-                s->uart_status |= R_STATUS_RXIDLE_MASK;
-                s->uart_status |= R_STATUS_RXEMPTY_MASK;
+                s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;
+                s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;
             }
         }
         break;
@@ -310,10 +356,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
         break;
 
     case R_FIFO_CTRL:
-        retvalue = s->uart_fifo_ctrl;
+        retvalue = s->regs[R_FIFO_CTRL];
         break;
     case R_FIFO_STATUS:
-        retvalue = s->uart_fifo_status;
+        retvalue = s->regs[R_FIFO_STATUS];
 
         retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
         retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
@@ -323,17 +369,17 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
         break;
 
     case R_OVRD:
-        retvalue = s->uart_ovrd;
+        retvalue = s->regs[R_OVRD];
         qemu_log_mask(LOG_UNIMP,
                       "%s: ovrd is not supported\n", __func__);
         break;
     case R_VAL:
-        retvalue = s->uart_val;
+        retvalue = s->regs[R_VAL];
         qemu_log_mask(LOG_UNIMP,
                       "%s: val is not supported\n", __func__);
         break;
     case R_TIMEOUT_CTRL:
-        retvalue = s->uart_timeout_ctrl;
+        retvalue = s->regs[R_TIMEOUT_CTRL];
         qemu_log_mask(LOG_UNIMP,
                       "%s: timeout_ctrl is not supported\n", __func__);
         break;
@@ -355,20 +401,20 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
     switch (addr >> 2) {
     case R_INTR_STATE:
         /* Write 1 clear */
-        s->uart_intr_state &= ~value;
+        s->regs[R_INTR_STATE] &= ~value;
         ot_uart_update_irqs(s);
         break;
     case R_INTR_ENABLE:
-        s->uart_intr_enable = value;
+        s->regs[R_INTR_ENABLE] = value;
         ot_uart_update_irqs(s);
         break;
     case R_INTR_TEST:
-        s->uart_intr_state |= value;
+        s->regs[R_INTR_STATE] |= value;
         ot_uart_update_irqs(s);
         break;
 
     case R_CTRL:
-        s->uart_ctrl = value;
+        s->regs[R_CTRL] = value;
 
         if (value & R_CTRL_NF_MASK) {
             qemu_log_mask(LOG_UNIMP,
@@ -412,11 +458,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
                       "%s: rdata is read only\n", __func__);
         break;
     case R_WDATA:
-        uart_write_tx_fifo(s, (uint8_t *) &value, 1);
+        uart_write_tx_fifo(s, value);
         break;
 
     case R_FIFO_CTRL:
-        s->uart_fifo_ctrl = value;
+        s->regs[R_FIFO_CTRL] = value;
 
         if (value & R_FIFO_CTRL_RXRST_MASK) {
             s->rx_level = 0;
@@ -433,7 +479,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
         break;
 
     case R_OVRD:
-        s->uart_ovrd = value;
+        s->regs[R_OVRD] = value;
         qemu_log_mask(LOG_UNIMP,
                       "%s: ovrd is not supported\n", __func__);
         break;
@@ -442,7 +488,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
                       "%s: val is read only\n", __func__);
         break;
     case R_TIMEOUT_CTRL:
-        s->uart_timeout_ctrl = value;
+        s->regs[R_TIMEOUT_CTRL] = value;
         qemu_log_mask(LOG_UNIMP,
                       "%s: timeout_ctrl is not supported\n", __func__);
         break;
@@ -466,8 +512,8 @@ static void fifo_trigger_update(void *opaque)
 {
     OtUARTState *s = opaque;
 
-    if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
-        ot_uart_xmit(NULL, G_IO_OUT, s);
+    if (s->regs[R_CTRL] & R_CTRL_TX_MASK) {
+        ot_uart_xmit(s);
     }
 }
 
@@ -489,25 +535,17 @@ static int ot_uart_post_load(void *opaque, int version_id)
 
 static const VMStateDescription vmstate_ot_uart = {
     .name = TYPE_OT_UART,
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .post_load = ot_uart_post_load,
     .fields = (const VMStateField[]) {
-        VMSTATE_UINT8_ARRAY(tx_fifo, OtUARTState,
-                            OT_UART_TX_FIFO_SIZE),
+        VMSTATE_STRUCT(tx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
+        VMSTATE_STRUCT(rx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
         VMSTATE_UINT32(tx_level, OtUARTState),
         VMSTATE_UINT64(char_tx_time, OtUARTState),
         VMSTATE_TIMER_PTR(fifo_trigger_handle, OtUARTState),
-        VMSTATE_UINT32(uart_intr_state, OtUARTState),
-        VMSTATE_UINT32(uart_intr_enable, OtUARTState),
-        VMSTATE_UINT32(uart_ctrl, OtUARTState),
-        VMSTATE_UINT32(uart_status, OtUARTState),
-        VMSTATE_UINT32(uart_rdata, OtUARTState),
-        VMSTATE_UINT32(uart_fifo_ctrl, OtUARTState),
-        VMSTATE_UINT32(uart_fifo_status, OtUARTState),
-        VMSTATE_UINT32(uart_ovrd, OtUARTState),
-        VMSTATE_UINT32(uart_val, OtUARTState),
-        VMSTATE_UINT32(uart_timeout_ctrl, OtUARTState),
+        VMSTATE_ARRAY(regs, OtUARTState, REGS_COUNT, 1, vmstate_info_uint32,
+                      uint32_t),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -532,6 +570,13 @@ static void ot_uart_init(Object *obj)
     memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s,
                           TYPE_OT_UART, 0x400);
     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+    /*
+     * This array has a fixed size in the header. This assertion is used to
+     * check that it is consistent with the definition in this file. This is
+     * ostensibly a runtime check, but may be optimised away by the compiler.
+     */
+    assert(REGS_SIZE == sizeof(s->regs));
 }
 
 static void ot_uart_realize(DeviceState *dev, Error **errp)
@@ -541,6 +586,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                           fifo_trigger_update, s);
 
+    fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
+    fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);
+
     qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive,
                              ot_uart_receive, NULL, NULL,
                              s, NULL, true);
diff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h
index fee2128f90..f489612700 100644
--- a/include/hw/char/ot_uart.h
+++ b/include/hw/char/ot_uart.h
@@ -26,11 +26,11 @@
 #define HW_OT_UART_H
 
 #include "hw/core/sysbus.h"
+#include "qemu/fifo8.h"
 #include "chardev/char-fe.h"
 #include "qemu/timer.h"
 #include "qom/object.h"
 
-#define OT_UART_TX_FIFO_SIZE 16
 #define OT_UART_CLOCK 50000000 /* 50MHz clock */
 
 #define TYPE_OT_UART "ot-uart"
@@ -43,7 +43,6 @@ struct OtUARTState {
     /* <public> */
     MemoryRegion mmio;
 
-    uint8_t tx_fifo[OT_UART_TX_FIFO_SIZE];
     uint32_t tx_level;
 
     uint32_t rx_level;
@@ -51,16 +50,11 @@ struct OtUARTState {
     QEMUTimer *fifo_trigger_handle;
     uint64_t char_tx_time;
 
-    uint32_t uart_intr_state;
-    uint32_t uart_intr_enable;
-    uint32_t uart_ctrl;
-    uint32_t uart_status;
-    uint32_t uart_rdata;
-    uint32_t uart_fifo_ctrl;
-    uint32_t uart_fifo_status;
-    uint32_t uart_ovrd;
-    uint32_t uart_val;
-    uint32_t uart_timeout_ctrl;
+    uint32_t regs[13]; /* Length must be updated if regs added or removed */
+
+    Fifo8 tx_fifo;
+    Fifo8 rx_fifo;
+    uint32_t tx_watermark_level;
 
     Clock *f_clk;
 
-- 
2.49.1
Re: [PATCH qemu v2 3/7] ot_uart: update register defs, switch to Fifo8 for tx/rx buffers
Posted by Alistair Francis 1 day, 15 hours ago
On Thu, Apr 9, 2026 at 5:52 AM ~lexbaileylowrisc
<lexbaileylowrisc@git.sr.ht> wrote:
>
> From: Lex Bailey <lex.bailey@lowrisc.org>
>
> The register definitions for the UART device need to be updated to match
> the documentation at https://opentitan.org/book/hw/ip/uart/doc/registers.html
>
> This commit does that, and also switches to using Fifo8 for managing the
> transmit and receive buffers.
>
> Signed-off-by: Lex Bailey <lex.bailey@lowrisc.org>
> ---
>  hw/char/ot_uart.c         | 332 ++++++++++++++++++++++----------------
>  include/hw/char/ot_uart.h |  18 +--
>  2 files changed, 196 insertions(+), 154 deletions(-)
>
> diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
> index 2cf0d73cf5..3bf3295b1b 100644
> --- a/hw/char/ot_uart.c
> +++ b/hw/char/ot_uart.c
> @@ -27,6 +27,7 @@
>
>  #include "qemu/osdep.h"
>  #include "hw/char/ot_uart.h"
> +#include "qemu/fifo8.h"
>  #include "hw/core/irq.h"
>  #include "hw/core/qdev-clock.h"
>  #include "hw/core/qdev-properties.h"
> @@ -36,17 +37,24 @@
>  #include "qemu/log.h"
>  #include "qemu/module.h"
>
> +/* clang-format off */

Let's remove these

>  REG32(INTR_STATE, 0x00)
> -    FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
> -    FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
> -    FIELD(INTR_STATE, TX_EMPTY, 2, 1)
> -    FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
> +    SHARED_FIELD(INTR_TX_WATERMARK, 0, 1)
> +    SHARED_FIELD(INTR_RX_WATERMARK, 1, 1)
> +    SHARED_FIELD(INTR_TX_DONE, 2, 1)
> +    SHARED_FIELD(INTR_RX_OVERFLOW, 3, 1)
> +    SHARED_FIELD(INTR_RX_FRAME_ERR, 4, 1)
> +    SHARED_FIELD(INTR_RX_BREAK_ERR, 5, 1)
> +    SHARED_FIELD(INTR_RX_TIMEOUT, 6, 1)
> +    SHARED_FIELD(INTR_RX_PARITY_ERR, 7, 1)
> +    SHARED_FIELD(INTR_TX_EMPTY, 8, 1)
>  REG32(INTR_ENABLE, 0x04)
>  REG32(INTR_TEST, 0x08)
>  REG32(ALERT_TEST, 0x0C)
> +    FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
>  REG32(CTRL, 0x10)
> -    FIELD(CTRL, TX_ENABLE, 0, 1)
> -    FIELD(CTRL, RX_ENABLE, 1, 1)
> +    FIELD(CTRL, TX, 0, 1)
> +    FIELD(CTRL, RX, 1, 1)
>      FIELD(CTRL, NF, 2, 1)
>      FIELD(CTRL, SLPBK, 4, 1)
>      FIELD(CTRL, LLPBK, 5, 1)
> @@ -58,43 +66,78 @@ REG32(STATUS, 0x14)
>      FIELD(STATUS, TXFULL, 0, 1)
>      FIELD(STATUS, RXFULL, 1, 1)
>      FIELD(STATUS, TXEMPTY, 2, 1)
> +    FIELD(STATUS, TXIDLE, 3, 1)
>      FIELD(STATUS, RXIDLE, 4, 1)
>      FIELD(STATUS, RXEMPTY, 5, 1)
>  REG32(RDATA, 0x18)
> +    FIELD(RDATA, RDATA, 0, 8)
>  REG32(WDATA, 0x1C)
> +    FIELD(WDATA, WDATA, 0, 8)
>  REG32(FIFO_CTRL, 0x20)
>      FIELD(FIFO_CTRL, RXRST, 0, 1)
>      FIELD(FIFO_CTRL, TXRST, 1, 1)
>      FIELD(FIFO_CTRL, RXILVL, 2, 3)
> -    FIELD(FIFO_CTRL, TXILVL, 5, 2)
> +    FIELD(FIFO_CTRL, TXILVL, 5, 3)
>  REG32(FIFO_STATUS, 0x24)
> -    FIELD(FIFO_STATUS, TXLVL, 0, 5)
> -    FIELD(FIFO_STATUS, RXLVL, 16, 5)
> +    FIELD(FIFO_STATUS, TXLVL, 0, 8)
> +    FIELD(FIFO_STATUS, RXLVL, 16, 8)
>  REG32(OVRD, 0x28)
> +    FIELD(OVRD, TXEN, 0, 1)
> +    FIELD(OVRD, TXVAL, 1, 1)
>  REG32(VAL, 0x2C)
> +    FIELD(VAL, RX, 0, 16)
>  REG32(TIMEOUT_CTRL, 0x30)
> +    FIELD(TIMEOUT_CTRL, VAL, 0, 24)
> +    FIELD(TIMEOUT_CTRL, EN, 31, 1)
> +/* clang-format on */
> +
> +#define INTR_MASK \
> +    (INTR_TX_WATERMARK_MASK | INTR_RX_WATERMARK_MASK | INTR_TX_DONE_MASK | \
> +     INTR_RX_OVERFLOW_MASK | INTR_RX_FRAME_ERR_MASK | INTR_RX_BREAK_ERR_MASK | \
> +     INTR_RX_TIMEOUT_MASK | INTR_RX_PARITY_ERR_MASK | INTR_TX_EMPTY_MASK)
> +
> +#define CTRL_MASK \
> +    (R_CTRL_TX_MASK | R_CTRL_RX_MASK | R_CTRL_NF_MASK | R_CTRL_SLPBK_MASK | \
> +     R_CTRL_LLPBK_MASK | R_CTRL_PARITY_EN_MASK | R_CTRL_PARITY_ODD_MASK | \
> +     R_CTRL_RXBLVL_MASK | R_CTRL_NCO_MASK)
> +
> +#define CTRL_SUP_MASK \
> +    (R_CTRL_RX_MASK | R_CTRL_TX_MASK | R_CTRL_SLPBK_MASK | R_CTRL_NCO_MASK)
> +
> +#define OT_UART_NCO_BITS     16
> +#define OT_UART_TX_FIFO_SIZE 128
> +#define OT_UART_RX_FIFO_SIZE 128
> +
> +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t))
> +
> +#define R_LAST_REG (R_TIMEOUT_CTRL)
> +#define REGS_COUNT (R_LAST_REG + 1u)
> +#define REGS_SIZE  (REGS_COUNT * sizeof(uint32_t))
>
>  static void ot_uart_update_irqs(OtUARTState *s)
>  {
> -    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
> +    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> +        & INTR_TX_WATERMARK_MASK) {
>          qemu_set_irq(s->tx_watermark, 1);
>      } else {
>          qemu_set_irq(s->tx_watermark, 0);
>      }
>
> -    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
> +    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> +        & INTR_RX_WATERMARK_MASK) {
>          qemu_set_irq(s->rx_watermark, 1);
>      } else {
>          qemu_set_irq(s->rx_watermark, 0);
>      }
>
> -    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
> +    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE] & INTR_TX_EMPTY_MASK) {
>          qemu_set_irq(s->tx_empty, 1);
>      } else {
>          qemu_set_irq(s->tx_empty, 0);
>      }
>
> -    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
> +    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> +        & INTR_RX_OVERFLOW_MASK) {
>          qemu_set_irq(s->rx_overflow, 1);
>      } else {
>          qemu_set_irq(s->rx_overflow, 0);
> @@ -105,126 +148,133 @@ static int ot_uart_can_receive(void *opaque)
>  {
>      OtUARTState *s = opaque;
>
> -    if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
> -           && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
> -        return 1;
> +    if (s->regs[R_CTRL] & R_CTRL_RX_MASK) {
> +        return (int)fifo8_num_free(&s->rx_fifo);
>      }
>
>      return 0;
>  }
>
> +static uint32_t ot_uart_get_rx_watermark_level(const OtUARTState *s)
> +{
> +    uint32_t rx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, RXILVL);
> +
> +    return rx_ilvl < 7 ? (1 << rx_ilvl) : 126;
> +}
> +
>  static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
>  {
>      OtUARTState *s = opaque;
> -    uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
> -                            >> R_FIFO_CTRL_RXILVL_SHIFT;
> -
> -    s->uart_rdata = *buf;
> +    uint32_t rx_watermark_level;
> +    size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);
>
> -    s->uart_status &= ~R_STATUS_RXIDLE_MASK;
> -    s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
> -    /* The RXFULL is set after receiving a single byte
> -     * as the FIFO buffers are not yet implemented.
> -     */
> -    s->uart_status |= R_STATUS_RXFULL_MASK;
> -    s->rx_level += 1;
> +    for (int index = 0; index < size; index++) {
> +        fifo8_push(&s->rx_fifo, buf[index]);
> +    }
>
> -    if (size > rx_fifo_level) {
> -        s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
> +    /* update INTR_STATE */
> +    if (count != size) {
> +        s->regs[R_INTR_STATE] |= INTR_RX_OVERFLOW_MASK;
> +    }
> +    rx_watermark_level = ot_uart_get_rx_watermark_level(s);
> +    if (rx_watermark_level && size >= rx_watermark_level) {
> +        s->regs[R_INTR_STATE] |= INTR_RX_WATERMARK_MASK;
>      }
>
>      ot_uart_update_irqs(s);
>  }
>
> -static gboolean ot_uart_xmit(void *do_not_use, GIOCondition cond,
> -                             void *opaque)
> +static void ot_uart_reset_tx_fifo(OtUARTState *s)
>  {
> -    OtUARTState *s = opaque;
> -    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
> -                            >> R_FIFO_CTRL_TXILVL_SHIFT;
> -    int ret;
> -
> -    /* instant drain the fifo when there's no back-end */
> -    if (!qemu_chr_fe_backend_connected(&s->chr)) {
> -        s->tx_level = 0;
> -        return G_SOURCE_REMOVE;
> +    fifo8_reset(&s->tx_fifo);
> +    s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
> +    s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
> +    if (s->tx_watermark_level) {
> +        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
> +        s->tx_watermark_level = 0;
>      }
> +}
>
> -    if (!s->tx_level) {
> -        s->uart_status &= ~R_STATUS_TXFULL_MASK;
> -        s->uart_status |= R_STATUS_TXEMPTY_MASK;
> -        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
> -        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
> -        ot_uart_update_irqs(s);
> -        return G_SOURCE_REMOVE;
> +static void ot_uart_reset_rx_fifo(OtUARTState *s)
> +{
> +    fifo8_reset(&s->rx_fifo);
> +    s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;
> +    s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;
> +    if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {
> +        qemu_chr_fe_accept_input(&s->chr);
>      }
> +}
>
> -    ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
> +static uint32_t ot_uart_get_tx_watermark_level(const OtUARTState *s)
> +{
> +    uint32_t tx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, TXILVL);
>
> -    if (ret >= 0) {
> -        s->tx_level -= ret;
> -        memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
> -    }
> +    return tx_ilvl < 7 ? (1 << tx_ilvl) : 64;
> +}
>
> -    if (s->tx_level) {
> -        guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
> -                                        ot_uart_xmit, s);
> -        if (!r) {
> -            s->tx_level = 0;
> -            return G_SOURCE_REMOVE;
> -        }
> +static void ot_uart_xmit(OtUARTState *s)
> +{
> +    const uint8_t *buf;
> +    uint32_t size;
> +    int ret;
> +
> +    if (fifo8_is_empty(&s->tx_fifo)) {
> +        return;
>      }
>
> -    /* Clear the TX Full bit */
> -    if (s->tx_level != OT_UART_TX_FIFO_SIZE) {
> -        s->uart_status &= ~R_STATUS_TXFULL_MASK;
> +    /* instant drain the fifo when there's no back-end */
> +    if (!qemu_chr_fe_backend_connected(&s->chr)) {
> +        ot_uart_reset_tx_fifo(s);
> +        ot_uart_update_irqs(s);
> +        return;
>      }
>
> -    /* Disable the TX_WATERMARK IRQ */
> -    if (s->tx_level < tx_fifo_level) {
> -        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
> +    /* get a continuous buffer from the FIFO */
> +    buf =
> +        fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);
> +    /* send as much as possible */
> +    ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
> +    /* if some characters where sent, remove them from the FIFO */
> +    if (ret >= 0) {
> +        fifo8_drop(&s->tx_fifo, ret);
>      }
>
> -    /* Set TX empty */
> -    if (s->tx_level == 0) {
> -        s->uart_status |= R_STATUS_TXEMPTY_MASK;
> -        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
> +    /* update INTR_STATE */
> +    if (fifo8_is_empty(&s->tx_fifo)) {
> +        s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
> +        s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
> +    }
> +    if (s->tx_watermark_level &&
> +        fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
> +        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
> +        s->tx_watermark_level = 0;
>      }
>
>      ot_uart_update_irqs(s);
> -    return G_SOURCE_REMOVE;
>  }
>
> -static void uart_write_tx_fifo(OtUARTState *s, const uint8_t *buf,
> -                               int size)
> +static void uart_write_tx_fifo(OtUARTState *s, uint8_t val)
>  {
> -    uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> -    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
> -                            >> R_FIFO_CTRL_TXILVL_SHIFT;
> -
> -    if (size > OT_UART_TX_FIFO_SIZE - s->tx_level) {
> -        size = OT_UART_TX_FIFO_SIZE - s->tx_level;
> +    if (fifo8_is_full(&s->tx_fifo)) {
>          qemu_log_mask(LOG_GUEST_ERROR, "ot_uart: TX FIFO overflow");
> +        return;
>      }
>
> -    memcpy(s->tx_fifo + s->tx_level, buf, size);
> -    s->tx_level += size;
> -
> -    if (s->tx_level > 0) {
> -        s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
> -    }
> +    fifo8_push(&s->tx_fifo, val);
>
> -    if (s->tx_level >= tx_fifo_level) {
> -        s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
> -        ot_uart_update_irqs(s);
> +    s->tx_watermark_level = ot_uart_get_tx_watermark_level(s);
> +    if (fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
> +        /*
> +         * TX watermark interrupt is raised when FIFO depth goes from above
> +         * watermark to below. If we haven't reached watermark, reset cached
> +         * watermark level
> +         */
> +        s->tx_watermark_level = 0;
>      }
>
> -    if (s->tx_level == OT_UART_TX_FIFO_SIZE) {
> -        s->uart_status |= R_STATUS_TXFULL_MASK;
> +    if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {
> +        ot_uart_xmit(s);
>      }
> -
> -    timer_mod(s->fifo_trigger_handle, current_time +
> -              (s->char_tx_time * 4));

What about the timer?

Alistair

>  }
>
>  static void ot_uart_reset_enter(Object *obj, ResetType type)
> @@ -236,17 +286,13 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)
>          c->parent_phases.enter(obj, type);
>      }
>
> -    s->uart_intr_state = 0x00000000;
> -    s->uart_intr_state = 0x00000000;
> -    s->uart_intr_enable = 0x00000000;
> -    s->uart_ctrl = 0x00000000;
> -    s->uart_status = 0x0000003c;
> -    s->uart_rdata = 0x00000000;
> -    s->uart_fifo_ctrl = 0x00000000;
> -    s->uart_fifo_status = 0x00000000;
> -    s->uart_ovrd = 0x00000000;
> -    s->uart_val = 0x00000000;
> -    s->uart_timeout_ctrl = 0x00000000;
> +    memset(&s->regs[0], 0, sizeof(s->regs));
> +
> +    s->regs[R_STATUS] = 0x0000003c;
> +
> +    s->tx_watermark_level = 0;
> +    ot_uart_reset_tx_fifo(s);
> +    ot_uart_reset_rx_fifo(s);
>
>      s->tx_level = 0;
>      s->rx_level = 0;
> @@ -260,7 +306,7 @@ static uint64_t ot_uart_get_baud(OtUARTState *s)
>  {
>      uint64_t baud;
>
> -    baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
> +    baud = ((s->regs[R_CTRL] & R_CTRL_NCO_MASK) >> 16);
>      baud *= clock_get_hz(s->f_clk);
>      baud >>= 20;
>
> @@ -274,10 +320,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
>
>      switch (addr >> 2) {
>      case R_INTR_STATE:
> -        retvalue = s->uart_intr_state;
> +        retvalue = s->regs[R_INTR_STATE];
>          break;
>      case R_INTR_ENABLE:
> -        retvalue = s->uart_intr_enable;
> +        retvalue = s->regs[R_INTR_ENABLE];
>          break;
>      case R_INTR_TEST:
>          qemu_log_mask(LOG_GUEST_ERROR,
> @@ -285,22 +331,22 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
>          break;
>
>      case R_CTRL:
> -        retvalue = s->uart_ctrl;
> +        retvalue = s->regs[R_CTRL];
>          break;
>      case R_STATUS:
> -        retvalue = s->uart_status;
> +        retvalue = s->regs[R_STATUS];
>          break;
>
>      case R_RDATA:
> -        retvalue = s->uart_rdata;
> -        if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
> +        retvalue = s->regs[R_RDATA];
> +        if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {
>              qemu_chr_fe_accept_input(&s->chr);
>
>              s->rx_level -= 1;
> -            s->uart_status &= ~R_STATUS_RXFULL_MASK;
> +            s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;
>              if (s->rx_level == 0) {
> -                s->uart_status |= R_STATUS_RXIDLE_MASK;
> -                s->uart_status |= R_STATUS_RXEMPTY_MASK;
> +                s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;
> +                s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;
>              }
>          }
>          break;
> @@ -310,10 +356,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
>          break;
>
>      case R_FIFO_CTRL:
> -        retvalue = s->uart_fifo_ctrl;
> +        retvalue = s->regs[R_FIFO_CTRL];
>          break;
>      case R_FIFO_STATUS:
> -        retvalue = s->uart_fifo_status;
> +        retvalue = s->regs[R_FIFO_STATUS];
>
>          retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
>          retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
> @@ -323,17 +369,17 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
>          break;
>
>      case R_OVRD:
> -        retvalue = s->uart_ovrd;
> +        retvalue = s->regs[R_OVRD];
>          qemu_log_mask(LOG_UNIMP,
>                        "%s: ovrd is not supported\n", __func__);
>          break;
>      case R_VAL:
> -        retvalue = s->uart_val;
> +        retvalue = s->regs[R_VAL];
>          qemu_log_mask(LOG_UNIMP,
>                        "%s: val is not supported\n", __func__);
>          break;
>      case R_TIMEOUT_CTRL:
> -        retvalue = s->uart_timeout_ctrl;
> +        retvalue = s->regs[R_TIMEOUT_CTRL];
>          qemu_log_mask(LOG_UNIMP,
>                        "%s: timeout_ctrl is not supported\n", __func__);
>          break;
> @@ -355,20 +401,20 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
>      switch (addr >> 2) {
>      case R_INTR_STATE:
>          /* Write 1 clear */
> -        s->uart_intr_state &= ~value;
> +        s->regs[R_INTR_STATE] &= ~value;
>          ot_uart_update_irqs(s);
>          break;
>      case R_INTR_ENABLE:
> -        s->uart_intr_enable = value;
> +        s->regs[R_INTR_ENABLE] = value;
>          ot_uart_update_irqs(s);
>          break;
>      case R_INTR_TEST:
> -        s->uart_intr_state |= value;
> +        s->regs[R_INTR_STATE] |= value;
>          ot_uart_update_irqs(s);
>          break;
>
>      case R_CTRL:
> -        s->uart_ctrl = value;
> +        s->regs[R_CTRL] = value;
>
>          if (value & R_CTRL_NF_MASK) {
>              qemu_log_mask(LOG_UNIMP,
> @@ -412,11 +458,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
>                        "%s: rdata is read only\n", __func__);
>          break;
>      case R_WDATA:
> -        uart_write_tx_fifo(s, (uint8_t *) &value, 1);
> +        uart_write_tx_fifo(s, value);
>          break;
>
>      case R_FIFO_CTRL:
> -        s->uart_fifo_ctrl = value;
> +        s->regs[R_FIFO_CTRL] = value;
>
>          if (value & R_FIFO_CTRL_RXRST_MASK) {
>              s->rx_level = 0;
> @@ -433,7 +479,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
>          break;
>
>      case R_OVRD:
> -        s->uart_ovrd = value;
> +        s->regs[R_OVRD] = value;
>          qemu_log_mask(LOG_UNIMP,
>                        "%s: ovrd is not supported\n", __func__);
>          break;
> @@ -442,7 +488,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
>                        "%s: val is read only\n", __func__);
>          break;
>      case R_TIMEOUT_CTRL:
> -        s->uart_timeout_ctrl = value;
> +        s->regs[R_TIMEOUT_CTRL] = value;
>          qemu_log_mask(LOG_UNIMP,
>                        "%s: timeout_ctrl is not supported\n", __func__);
>          break;
> @@ -466,8 +512,8 @@ static void fifo_trigger_update(void *opaque)
>  {
>      OtUARTState *s = opaque;
>
> -    if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
> -        ot_uart_xmit(NULL, G_IO_OUT, s);
> +    if (s->regs[R_CTRL] & R_CTRL_TX_MASK) {
> +        ot_uart_xmit(s);
>      }
>  }
>
> @@ -489,25 +535,17 @@ static int ot_uart_post_load(void *opaque, int version_id)
>
>  static const VMStateDescription vmstate_ot_uart = {
>      .name = TYPE_OT_UART,
> -    .version_id = 1,
> -    .minimum_version_id = 1,
> +    .version_id = 2,
> +    .minimum_version_id = 2,
>      .post_load = ot_uart_post_load,
>      .fields = (const VMStateField[]) {
> -        VMSTATE_UINT8_ARRAY(tx_fifo, OtUARTState,
> -                            OT_UART_TX_FIFO_SIZE),
> +        VMSTATE_STRUCT(tx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
> +        VMSTATE_STRUCT(rx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
>          VMSTATE_UINT32(tx_level, OtUARTState),
>          VMSTATE_UINT64(char_tx_time, OtUARTState),
>          VMSTATE_TIMER_PTR(fifo_trigger_handle, OtUARTState),
> -        VMSTATE_UINT32(uart_intr_state, OtUARTState),
> -        VMSTATE_UINT32(uart_intr_enable, OtUARTState),
> -        VMSTATE_UINT32(uart_ctrl, OtUARTState),
> -        VMSTATE_UINT32(uart_status, OtUARTState),
> -        VMSTATE_UINT32(uart_rdata, OtUARTState),
> -        VMSTATE_UINT32(uart_fifo_ctrl, OtUARTState),
> -        VMSTATE_UINT32(uart_fifo_status, OtUARTState),
> -        VMSTATE_UINT32(uart_ovrd, OtUARTState),
> -        VMSTATE_UINT32(uart_val, OtUARTState),
> -        VMSTATE_UINT32(uart_timeout_ctrl, OtUARTState),
> +        VMSTATE_ARRAY(regs, OtUARTState, REGS_COUNT, 1, vmstate_info_uint32,
> +                      uint32_t),
>          VMSTATE_END_OF_LIST()
>      }
>  };
> @@ -532,6 +570,13 @@ static void ot_uart_init(Object *obj)
>      memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s,
>                            TYPE_OT_UART, 0x400);
>      sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +
> +    /*
> +     * This array has a fixed size in the header. This assertion is used to
> +     * check that it is consistent with the definition in this file. This is
> +     * ostensibly a runtime check, but may be optimised away by the compiler.
> +     */
> +    assert(REGS_SIZE == sizeof(s->regs));
>  }
>
>  static void ot_uart_realize(DeviceState *dev, Error **errp)
> @@ -541,6 +586,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
>      s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
>                                            fifo_trigger_update, s);
>
> +    fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
> +    fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);
> +
>      qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive,
>                               ot_uart_receive, NULL, NULL,
>                               s, NULL, true);
> diff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h
> index fee2128f90..f489612700 100644
> --- a/include/hw/char/ot_uart.h
> +++ b/include/hw/char/ot_uart.h
> @@ -26,11 +26,11 @@
>  #define HW_OT_UART_H
>
>  #include "hw/core/sysbus.h"
> +#include "qemu/fifo8.h"
>  #include "chardev/char-fe.h"
>  #include "qemu/timer.h"
>  #include "qom/object.h"
>
> -#define OT_UART_TX_FIFO_SIZE 16
>  #define OT_UART_CLOCK 50000000 /* 50MHz clock */
>
>  #define TYPE_OT_UART "ot-uart"
> @@ -43,7 +43,6 @@ struct OtUARTState {
>      /* <public> */
>      MemoryRegion mmio;
>
> -    uint8_t tx_fifo[OT_UART_TX_FIFO_SIZE];
>      uint32_t tx_level;
>
>      uint32_t rx_level;
> @@ -51,16 +50,11 @@ struct OtUARTState {
>      QEMUTimer *fifo_trigger_handle;
>      uint64_t char_tx_time;
>
> -    uint32_t uart_intr_state;
> -    uint32_t uart_intr_enable;
> -    uint32_t uart_ctrl;
> -    uint32_t uart_status;
> -    uint32_t uart_rdata;
> -    uint32_t uart_fifo_ctrl;
> -    uint32_t uart_fifo_status;
> -    uint32_t uart_ovrd;
> -    uint32_t uart_val;
> -    uint32_t uart_timeout_ctrl;
> +    uint32_t regs[13]; /* Length must be updated if regs added or removed */
> +
> +    Fifo8 tx_fifo;
> +    Fifo8 rx_fifo;
> +    uint32_t tx_watermark_level;
>
>      Clock *f_clk;
>
> --
> 2.49.1
>
>