Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions docs/opentitan/darjeeling.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 1 addition & 7 deletions docs/opentitan/earlgrey.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
43 changes: 43 additions & 0 deletions docs/opentitan/uart.md
Original file line number Diff line number Diff line change
@@ -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.
66 changes: 57 additions & 9 deletions hw/opentitan/ot_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 {
Expand Down Expand Up @@ -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]);
}
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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(),
};

Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down
Loading