On Fri, May 1, 2026 at 2:04 AM ~lexbaileylowrisc
<[email protected]> wrote:
>
> From: Lex Bailey <[email protected]>
>
> What is a break event:
> Normally, a UART wire is held high by the transmitting device any time the
> communication channel is idle (nothing to transmit). If a receiver does not 
> see
> that its receive line is high, this means one of two things:
>     1. there is a transmission happening now
>     2. there is no transmitter connected, and the line is floating or pulled
>        low, or the transmitting device is in a bad state or reset state and so
>        it has not yet asserted its TX output.
>
> In case 1, the line will go high again very soon.
> In case 2, it is sometimes useful to detect that this is the case and signal 
> to
> the software that this has happened (normally by means of an interrupt)
>
> The ot_uart device does exactly this in case 2. It detects that this is
> happening by detecting when the RX pin stays low for an extended period. This
> period can be configured to be 2, 4, 8, or 16 times a character period. The
> character period itself depends on the configuration of the device (baud rate,
> and parity bit)
>
> for more details about how this works, there is documentation about the UART
> break detection on this page:
> https://opentitan.org/book/hw/ip/uart/doc/theory_of_operation.html#rx_break_err
>
> This commit adds a handler for the UART break event, and logic to emulate a
> break when oversampling is enabled and there is a break event.
>
> Signed-off-by: Lex Bailey <[email protected]>
> ---
>  docs/system/riscv/opentitan-uart.rst | 43 +++++++++++++
>  hw/char/ot_uart.c                    | 92 +++++++++++++++++++++++++++-
>  include/hw/char/ot_uart.h            |  4 ++
>  3 files changed, 136 insertions(+), 3 deletions(-)
>  create mode 100644 docs/system/riscv/opentitan-uart.rst
>
> diff --git a/docs/system/riscv/opentitan-uart.rst 
> b/docs/system/riscv/opentitan-uart.rst
> new file mode 100644
> index 0000000000..7ca596712d
> --- /dev/null
> +++ b/docs/system/riscv/opentitan-uart.rst

We should just add an OpenTitan Machine documentation here and include
the UART there.

> @@ -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.
> diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
> index 3511a42fbe..cdf02da62b 100644
> --- a/hw/char/ot_uart.c
> +++ b/hw/char/ot_uart.c
> @@ -179,6 +179,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]);
>      }
> @@ -331,9 +336,40 @@ static void ot_uart_reset_enter(Object *obj, ResetType 
> type)
>
>      s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10;
>
> +    /*
> +     * 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);
>  }
>
> +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;
> @@ -355,6 +391,17 @@ static uint8_t ot_uart_read_rx_fifo(OtUARTState *s)
>      return val;
>  }
>
> +static gboolean ot_uart_watch_cb(void *do_not_use, GIOCondition cond,
> +                                 void *opaque)
> +{
> +    OtUARTState *s = opaque;
> +
> +    s->watch_tag = 0;
> +    ot_uart_xmit(s);
> +
> +    return FALSE;
> +}
> +
>  static uint64_t ot_uart_get_baud(OtUARTState *s)
>  {
>      uint64_t baud;
> @@ -424,6 +471,26 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, 
> unsigned int size)
>          break;
>
>      case R_VAL:
> +        /*
> +         * This is not trivially implemented due to the QEMU UART
> +         * interface. There is no way to reliably sample or oversample
> +         * given our emulated interface, but some software might poll the
> +         * value of this register to determine break conditions.
> +         *
> +         * 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.
> +         */
> +        retvalue = (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:
>          retvalue = s->regs[reg];
> @@ -607,8 +674,27 @@ static const VMStateDescription vmstate_ot_uart = {
>
>  static const Property ot_uart_properties[] = {
>      DEFINE_PROP_CHR("chardev", OtUARTState, chr),
> +    DEFINE_PROP_BOOL("oversample-break", OtUARTState, oversample_break, 
> false),
> +    DEFINE_PROP_BOOL("toggle-break", OtUARTState, toggle_break, false),

Why have these disabled by default?

Alistair

Reply via email to