From fb7b49e7bd9c9907d381fa861cfd25da670dcd95 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Fri, 3 Oct 2025 12:37:16 +0100 Subject: [PATCH 1/2] [ot] hw/opentitan: ot_uart: Add break interrupt & mocked oversampling This commit hooks up the QEMU UART RX break event to the `ot_uart` so that the break receive triggers an interrupt. This completely ignores the configured `CTRL.RXBLVL` as we have no notion for "how long" break is being held for. Breaks can be sent to a given CharDev via the QEMU monitor. With the goal of mocking the UART oversampling `VAL` register enough that it can support differentiation between break conditions and not (essentially treating the UART RX like a GPIO strap), we implement logic for outputting either 0x0 or 0xFFFF depending on whether we are "in a break" or not. That is, the `VAL` register will report 16 samples equivalent to the last UART bit received (which is 0 when in a break, and 1 for a stop bit on any valid transmission). We make the assumption here that break is being "held", rather than just being a transient event. This logic is enabled through an `oversample-break` property to make it an opt-in behaviour. To better support flows that use break as a GPIO signal that can be continually asserted, add an additional property that treats incoming break events as a "toggle", such that one break event will cause the UART to enter break until another event tells it to exit break. This is again an opt-in property intended for testing edge-case software that uses UART Break in this way. Signed-off-by: Alex Jones --- hw/opentitan/ot_uart.c | 66 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_uart.c b/hw/opentitan/ot_uart.c index bc47d9789a198..e33f874ebe350 100644 --- a/hw/opentitan/ot_uart.c +++ b/hw/opentitan/ot_uart.c @@ -158,6 +158,7 @@ struct OtUARTState { Fifo8 tx_fifo; Fifo8 rx_fifo; uint32_t tx_watermark_level; + bool in_break; guint watch_tag; unsigned pclk; /* Current input clock */ const char *clock_src_name; /* IRQ name once connected */ @@ -166,6 +167,8 @@ struct OtUARTState { char *clock_name; DeviceState *clock_src; CharBackend chr; + bool oversample_break; /* Should mock break in the oversampled VAL reg? */ + bool toggle_break; /* Are incoming breaks temporary or toggled? */ }; struct OtUARTClass { @@ -256,6 +259,11 @@ static void ot_uart_receive(void *opaque, const uint8_t *buf, int size) uint32_t rx_watermark_level; size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size); + if (size && !s->toggle_break) { + /* no longer breaking, so emulate idle in oversampled VAL register */ + s->in_break = false; + } + for (int index = 0; index < size; index++) { fifo8_push(&s->rx_fifo, buf[index]); } @@ -272,6 +280,24 @@ static void ot_uart_receive(void *opaque, const uint8_t *buf, int size) ot_uart_update_irqs(s); } +static void ot_uart_event_handler(void *opaque, QEMUChrEvent event) +{ + OtUARTState *s = opaque; + + if (event == CHR_EVENT_BREAK) { + if (!s->in_break || !s->oversample_break) { + /* ignore CTRL.RXBLVL as we have no notion of break "time" */ + s->regs[R_INTR_STATE] |= INTR_RX_BREAK_ERR_MASK; + ot_uart_update_irqs(s); + /* emulate break in the oversampled VAL register */ + s->in_break = true; + } else if (s->toggle_break) { + /* emulate toggling break off in the oversampled VAL register */ + s->in_break = false; + } + } +} + static uint8_t ot_uart_read_rx_fifo(OtUARTState *s) { uint8_t val; @@ -472,14 +498,19 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned size) * given our emulated interface, but some software might poll the * value of this register to determine break conditions. * - * As such, and given that we always support RXIDLE, default - * to reporting 16 idle high samples (i.e. 0xFFFF) instead. - * This is still not ideal, but at least defaults to showing - * nothing (similar to SW waiting too long before polling) rather - * than essentially showing a break condition. + * As such, default to reporting 16 of the last sample received + * instead. This defaults to 16 idle high samples (as a stop bit is + * always the last received), except for when the `oversample-break` + * property is set and a break condition is received over UART RX, + * where we then show 16 low samples until the next valid UART + * transmission is received (or break is toggled off with the + * `toggle-break` property enabled). This will not be accurate, but + * should be sufficient to support basic software flows that + * essentially use UART break as a strapping mechanism. */ - val32 = R_VAL_RX_MASK; - qemu_log_mask(LOG_UNIMP, "%s: UART VAL always shows idle\n", __func__); + val32 = (s->in_break && s->oversample_break) ? 0u : UINT16_MAX; + qemu_log_mask(LOG_UNIMP, "%s: VAL only shows idle%s\n", __func__, + (s->oversample_break ? "/break" : "")); break; case R_OVRD: case R_TIMEOUT_CTRL: @@ -618,6 +649,8 @@ static Property ot_uart_properties[] = { DEFINE_PROP_STRING("clock-name", OtUARTState, clock_name), DEFINE_PROP_LINK("clock-src", OtUARTState, clock_src, TYPE_DEVICE, DeviceState *), + DEFINE_PROP_BOOL("oversample-break", OtUARTState, oversample_break, false), + DEFINE_PROP_BOOL("toggle-break", OtUARTState, toggle_break, false), DEFINE_PROP_END_OF_LIST(), }; @@ -626,7 +659,8 @@ static int ot_uart_be_change(void *opaque) OtUARTState *s = opaque; qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive, ot_uart_receive, - NULL, ot_uart_be_change, s, NULL, true); + ot_uart_event_handler, ot_uart_be_change, s, NULL, + true); if (s->watch_tag > 0) { g_source_remove(s->watch_tag); @@ -656,6 +690,19 @@ static void ot_uart_reset_enter(Object *obj, ResetType type) ot_uart_reset_tx_fifo(s); ot_uart_reset_rx_fifo(s); + /* + * do not reset `s->in_break`, as that tracks whether we are currently + * receiving a break condition over UART RX from some device talking + * to OpenTitan, which should survive resets. The QEMU CharDev only + * supports transient break events and not the notion of holding the + * UART in break, so remembering breaks like this is required to + * support mocking of break conditions in the oversampled `VAL` reg. + */ + if (s->in_break) { + /* ignore CTRL.RXBLVL as we have no notion of break "time" */ + s->regs[R_INTR_STATE] |= INTR_RX_BREAK_ERR_MASK; + } + ot_uart_update_irqs(s); ibex_irq_set(&s->alert, 0); @@ -687,7 +734,8 @@ static void ot_uart_realize(DeviceState *dev, Error **errp) 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, ot_uart_be_change, s, NULL, true); + ot_uart_event_handler, ot_uart_be_change, s, NULL, + true); } static void ot_uart_init(Object *obj) From 7a2d21b4bd2996296ac8e4d34762299f0702f3bb Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Fri, 3 Oct 2025 13:50:20 +0100 Subject: [PATCH 2/2] [ot] docs/opentitan: uart.md: Add UART documentation Separate out the UART documentation from the Earlgrey and Darjeeling machine documentation files into its own seperate documentation file (like the `spi_device.md`) and describe in more detail sending break conditions via the QEMU monitor and using the properties to mock break conditions in the oversampled `VAL` register for SW edge cases. Signed-off-by: Alex Jones --- docs/opentitan/darjeeling.md | 7 +----- docs/opentitan/earlgrey.md | 8 +------ docs/opentitan/uart.md | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 docs/opentitan/uart.md diff --git a/docs/opentitan/darjeeling.md b/docs/opentitan/darjeeling.md index 71019e381491c..59f48319ba710 100644 --- a/docs/opentitan/darjeeling.md +++ b/docs/opentitan/darjeeling.md @@ -307,13 +307,8 @@ a reset request, rather than rebooting the whole machine endlessly as the defaul ### UART -* `-serial mon:stdio`, used as the first `-serial` option, redirects the virtual UART0 to the - current console/shell. +See documentation in [`uart.md`](./uart.md). -* `-chardev socket,id=serial1,host=localhost,port=8001,server=on,wait=off` and - `-serial chardev:serial1` can be used to redirect UART1 (in this example) to a TCP socket. These - options are not specific to OpenTitan emulation, but are useful to communicate over a UART. - Note that QEMU offers many `chardev` backends, please check QEMU documentation for details. ## Useful debugging options diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index 1ba0b974738bd..2249506c0229f 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -256,13 +256,7 @@ There are two modes to handle address remapping, with different limitations: ### UART -* `-serial mon:stdio`, used as the first `-serial` option, redirects the virtual UART0 to the - current console/shell. - -* `-chardev socket,id=serial1,host=localhost,port=8001,server=on,wait=off` and - `-serial chardev:serial1` can be used to redirect UART1 (in this example) to a TCP socket. These - options are not specific to OpenTitan emulation, but are useful to communicate over a UART. - Note that QEMU offers many `chardev` backends, please check QEMU documentation for details. +See documentation in [`uart.md`](./uart.md). ### I2C diff --git a/docs/opentitan/uart.md b/docs/opentitan/uart.md new file mode 100644 index 0000000000000..7ca596712d011 --- /dev/null +++ b/docs/opentitan/uart.md @@ -0,0 +1,43 @@ +# OpenTitan UART Support + +## Connecting to the UART + +* `-serial mon:stdio`, used as the first `-serial` option, redirects the virtual UART0 to the + current console/shell. + +* `-chardev socket,id=serial1,host=localhost,port=8001,server=on,wait=off` and + `-serial chardev:serial1` can be used to redirect UART1 (in this example) to a TCP socket. These + options are not specific to OpenTitan emulation, but are useful to communicate over a UART. + Note that QEMU offers many `chardev` backends, please check QEMU documentation for details. + +## Sending Break Conditions + +Break conditions can be sent to the UART on select supported CharDev backends (telnet, mux) +or by sending the `chardev-send-break` command with the CharDev ID via the QEMU Monitor. +Break conditions are treated as transient events and the length of time of a break condition +is not considered. + +## Oversampling + +OpenTitan's UART has a `VAL` register which oversamples the RX pin 16 times per bit. +This cannot be emulated by QEMU which uses a CharDev backend and does not have a notion of +accurate sampling times. + +If software wishes to poll the `VAL` register to determine break conditions, there are +some properties available to help with emulating this use case: + +* `-global ot-uart.oversample-break=true` is used to enable UART break oversampling. + This will attempt to display 16 samples of the last bit received in the `VAL` register, + which will be 16 high bits after any UART frame is transmitted (as these end with a stop + bit, which is high), or 16 low bits if the UART previously received a break condition + and has not received any frames since. That is, enabling this property assumes that + transmitted break conditions are "held" until the next UART transfer in terms of what + is being shown in the oversampled `VAL` register. + +* `-global ot-uart.toggle-break=true` is used to provide more control over "holding" + the UART RX break condition like a GPIO strap, and changes the behavior of a UART + such that received break condition events now *toggle* the break condition state + rather than keeping it asserted until the next transfer. This allows any device talking + to OpenTitan via UART to have more precise control over when the UART VAL register + displays idle and when it displays a break condition, as it can precisely toggle the + break condition on or off like a GPIO strapping being held down.