From: Lex Bailey <lex.bailey@lowrisc.org>
Added some tracing to the OpenTitan UART for transparency when debugging
Signed-off-by: Lex Bailey <lex.bailey@lowrisc.org>
---
hw/char/ot_uart.c | 30 ++++++++++++++++++++++++++++++
hw/char/trace-events | 8 ++++++++
hw/riscv/opentitan.c | 1 +
include/hw/char/ot_uart.h | 1 +
4 files changed, 40 insertions(+)
diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
index 1dda771724..6f7b8946e8 100644
--- a/hw/char/ot_uart.c
+++ b/hw/char/ot_uart.c
@@ -30,6 +30,7 @@
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
+#include "trace.h"
/* clang-format off */
REG32(INTR_STATE, 0x00)
@@ -135,6 +136,9 @@ static void ot_uart_update_irqs(OtUARTState *s)
{
uint32_t state_masked = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE];
+ trace_ot_uart_irqs(s->ot_id, s->regs[R_INTR_STATE], s->regs[R_INTR_ENABLE],
+ state_masked);
+
for (int index = 0; index < OT_UART_IRQ_NUM; index++) {
bool level = (state_masked & (1U << index)) != 0;
qemu_set_irq(s->irqs[index], level);
@@ -156,6 +160,18 @@ static bool ot_uart_is_rx_enabled(const OtUARTState *s)
return FIELD_EX32(s->regs[R_CTRL], CTRL, RX);
}
+static void ot_uart_check_baudrate(const OtUARTState *s)
+{
+ uint32_t nco = FIELD_EX32(s->regs[R_CTRL], CTRL, NCO);
+
+ unsigned baudrate = (unsigned)(((uint64_t)nco * (uint64_t)s->pclk) >>
+ (R_CTRL_NCO_LENGTH + 4));
+
+ if (baudrate) {
+ trace_ot_uart_check_baudrate(s->ot_id, s->pclk, baudrate);
+ }
+}
+
static int ot_uart_can_receive(void *opaque)
{
OtUARTState *s = opaque;
@@ -403,6 +419,7 @@ static void ot_uart_clock_input(void *opaque, int irq, int level)
s->pclk = (unsigned)level;
/* TODO: disable UART transfer when PCLK is 0 */
+ ot_uart_check_baudrate(s);
}
static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
@@ -500,6 +517,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
break;
}
+ uint32_t pc = current_cpu->cc->get_pc(current_cpu);
+ trace_ot_uart_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32,
+ pc);
+
return (uint64_t)val32;
}
@@ -511,6 +532,9 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
hwaddr reg = R32_OFF(addr);
+ uint32_t pc = current_cpu->cc->get_pc(current_cpu);
+ trace_ot_uart_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, pc);
+
switch (reg) {
case R_INTR_STATE:
val32 &= INTR_MASK;
@@ -541,6 +565,9 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
uint32_t prev = s->regs[R_CTRL];
s->regs[R_CTRL] = val32 & CTRL_MASK;
uint32_t change = prev ^ s->regs[R_CTRL];
+ if (change & R_CTRL_NCO_MASK) {
+ ot_uart_check_baudrate(s);
+ }
if ((change & R_CTRL_RX_MASK) && ot_uart_is_rx_enabled(s) &&
!ot_uart_is_sys_loopack_enabled(s)) {
qemu_chr_fe_accept_input(&s->chr);
@@ -621,6 +648,7 @@ static const VMStateDescription vmstate_ot_uart = {
};
static const Property ot_uart_properties[] = {
+ DEFINE_PROP_STRING("ot-id", OtUARTState, ot_id),
DEFINE_PROP_CHR("chardev", OtUARTState, chr),
DEFINE_PROP_BOOL("oversample-break", OtUARTState, oversample_break, false),
DEFINE_PROP_BOOL("toggle-break", OtUARTState, toggle_break, false),
@@ -669,6 +697,8 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
{
OtUARTState *s = OT_UART(dev);
+ g_assert(s->ot_id);
+
qdev_init_gpio_in_named(DEVICE(s), &ot_uart_clock_input, "clock-in", 1);
fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
diff --git a/hw/char/trace-events b/hw/char/trace-events
index a3fcc77287..c859d8af4e 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -141,3 +141,11 @@ stm32f2xx_usart_receive(char *id, uint8_t chr) " %s receiving '%c'"
# riscv_htif.c
htif_uart_write_to_host(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64
htif_uart_unknown_device_command(uint8_t device, uint8_t cmd, uint64_t payload) "device: %u cmd: %02u payload: %016" PRIx64
+
+# ot_uart.c
+ot_uart_check_baudrate(const char *id, unsigned pclk, unsigned baud) "%s: @ %u Hz: %u bps"
+ot_uart_connect_input_clock(const char *id, const char * srcname) "%s: %s"
+ot_uart_debug(const char *id, const char *msg) "%s: %s"
+ot_uart_io_read_out(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x"
+ot_uart_io_write(const char *id, uint32_t addr, const char *regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x"
+ot_uart_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%08x msk:0x%08x eff:0x%08x"
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index 97c33d1b53..163d3ac3d3 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -133,6 +133,7 @@ static void lowrisc_ibex_soc_init(Object *obj)
object_initialize_child(obj, "plic", &s->plic, TYPE_SIFIVE_PLIC);
object_initialize_child(obj, "uart", &s->uart, TYPE_OT_UART);
+ object_property_set_str(OBJECT(&s->uart), "ot-id", "uart0", &error_fatal);
object_initialize_child(obj, "timer", &s->timer, TYPE_IBEX_TIMER);
diff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h
index 221c581e52..7c2b5f3457 100644
--- a/include/hw/char/ot_uart.h
+++ b/include/hw/char/ot_uart.h
@@ -46,6 +46,7 @@ struct OtUARTState {
unsigned pclk; /* Current input clock */
const char *clock_src_name; /* IRQ name once connected */
+ char *ot_id;
DeviceState *clock_src;
CharFrontend chr;
bool oversample_break; /* Should mock break in the oversampled VAL reg? */
--
2.49.1