[sw, dif_uart] Add API useful for debugging
There could be many use-cases for the loopback functionality, one of
which is dif_uart_sanitytest.
`dif_uart_fifo_reset` API is added for flexibility, which is
particularily useful together with a loopback functionality.
Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/sw/device/lib/dif/dif_uart.c b/sw/device/lib/dif/dif_uart.c
index f7480b6..3771a08 100644
--- a/sw/device/lib/dif/dif_uart.c
+++ b/sw/device/lib/dif/dif_uart.c
@@ -472,3 +472,50 @@
return kDifUartOk;
}
+
+dif_uart_result_t dif_uart_fifo_reset(const dif_uart_t *uart,
+ dif_uart_fifo_reset_t reset) {
+ if (uart == NULL) {
+ return kDifUartBadArg;
+ }
+
+ uint32_t reg = mmio_region_read32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET);
+
+ if (reset == kDifUartFifoResetRx || reset == kDifUartFifoResetAll) {
+ reg = bitfield_set_field32(
+ reg, (bitfield_field32_t){
+ .mask = 0x1, .value = 1, .index = UART_FIFO_CTRL_RXRST,
+ });
+ }
+
+ if (reset == kDifUartFifoResetTx || reset == kDifUartFifoResetAll) {
+ reg = bitfield_set_field32(
+ reg, (bitfield_field32_t){
+ .mask = 0x1, .value = 1, .index = UART_FIFO_CTRL_TXRST,
+ });
+ }
+
+ mmio_region_write32(uart->base_addr, UART_FIFO_CTRL_REG_OFFSET, reg);
+
+ return kDifUartOk;
+}
+
+dif_uart_result_t dif_uart_loopback_set(const dif_uart_t *uart,
+ dif_uart_loopback_t loopback,
+ dif_uart_enable_t enable) {
+ if (uart == NULL) {
+ return kDifUartBadArg;
+ }
+
+ bitfield_field32_t field = {
+ .mask = 0x1,
+ .index = loopback ? UART_CTRL_LLPBK : UART_CTRL_SLPBK,
+ .value = enable ? 1 : 0,
+ };
+
+ uint32_t reg = mmio_region_read32(uart->base_addr, UART_CTRL_REG_OFFSET);
+ reg = bitfield_set_field32(reg, field);
+ mmio_region_write32(uart->base_addr, UART_CTRL_REG_OFFSET, reg);
+
+ return kDifUartOk;
+}
diff --git a/sw/device/lib/dif/dif_uart.h b/sw/device/lib/dif/dif_uart.h
index 0f6857c..a8d93d6 100644
--- a/sw/device/lib/dif/dif_uart.h
+++ b/sw/device/lib/dif/dif_uart.h
@@ -60,13 +60,30 @@
} dif_uart_watermark_t;
/**
+ * UART TX/RX FIFO reset enumeration.
+ */
+typedef enum dif_uart_fifo_reset {
+ kDifUartFifoResetRx = 0, /**< Reset RX FIFO. */
+ kDifUartFifoResetTx, /**< Reset TX FIFO. */
+ kDifUartFifoResetAll, /**< All above. */
+} dif_uart_fifo_reset_t;
+
+/**
+ * UART System/Line loopback enumeration.
+ */
+typedef enum dif_uart_loopback {
+ kDifUartLoopbackSystem = 0, /**< Outgoing TX bits received through RX. */
+ kDifUartLoopbackLine, /**< Incoming RX bits are forwarded to TX. */
+} dif_uart_loopback_t;
+
+/**
* Generic enable/disable enumeration.
*
* Enumeration used to enable/disable bits, flags, ...
*/
typedef enum dif_uart_enable {
- kDifUartEnable = 0, /**< enable. */
- kDifUartDisable, /**< disable. */
+ kDifUartDisable = 0, /**< disable. */
+ kDifUartEnable, /**< enable. */
} dif_uart_enable_t;
/**
@@ -365,6 +382,41 @@
DIF_WARN_UNUSED_RESULT
dif_uart_result_t dif_uart_tx_bytes_available(const dif_uart_t *uart,
size_t *num_bytes);
+/**
+ * UART TX reset RX/TX FIFO.
+ *
+ * Reset both FIFOs, or the requested one.
+ *
+ * @param uart UART state data.
+ * @param reset FIFO to reset (RX or TX).
+ * @return `dif_uart_result_t`.
+ */
+dif_uart_result_t dif_uart_fifo_reset(const dif_uart_t *uart,
+ dif_uart_fifo_reset_t reset);
+
+/**
+ * UART enable/disable transmit/receive loopback.
+ *
+ * This API can be used for testing purpose. For example, to validate transmit
+ * and receive routines.
+ *
+ * Loopback should only be enabled when device is in the IDLE state to prevent
+ * data loss/coruption. Behaviour depends on the `loopback` parameter:
+ * - `kDifUartLoopbackSystem`:
+ * Receives the data that is being transmitted. No external data can be
+ * received (from the RX line). When enabled the TX line goes high.
+ * - `kDifUartLoopbackLine`:
+ * Transmits the data that is being received. No internal data can be
+ * sent out (from the TX FIFO). When enabled the RX line goes high.
+ *
+ * @param uart UART state data.
+ * @param loopback Loopback type (transmit/receive).
+ * @param enable Enable/disable control flag.
+ * @return `dif_uart_result_t`.
+ */
+dif_uart_result_t dif_uart_loopback_set(const dif_uart_t *uart,
+ dif_uart_loopback_t loopback,
+ dif_uart_enable_t enable);
#ifdef __cplusplus
} // extern "C"
diff --git a/sw/device/tests/dif/dif_uart_unittest.cc b/sw/device/tests/dif/dif_uart_unittest.cc
index a2581e4..d63863d 100644
--- a/sw/device/tests/dif/dif_uart_unittest.cc
+++ b/sw/device/tests/dif/dif_uart_unittest.cc
@@ -736,5 +736,62 @@
EXPECT_EQ(num_bytes, kDifUartFifoSizeBytes);
}
+class FifoResetTest : public UartTest {};
+
+TEST_F(FifoResetTest, NullArgs) {
+ dif_uart_result_t result = dif_uart_fifo_reset(nullptr, kDifUartFifoResetRx);
+ EXPECT_EQ(result, kDifUartBadArg);
+}
+
+TEST_F(FifoResetTest, Success) {
+ EXPECT_MASK32(UART_FIFO_CTRL_REG_OFFSET, {{UART_FIFO_CTRL_RXRST, 0x1, true}});
+ dif_uart_result_t result =
+ dif_uart_fifo_reset(&dif_uart_, kDifUartFifoResetRx);
+ EXPECT_EQ(result, kDifUartOk);
+
+ EXPECT_MASK32(UART_FIFO_CTRL_REG_OFFSET, {{UART_FIFO_CTRL_TXRST, 0x1, true}});
+ result = dif_uart_fifo_reset(&dif_uart_, kDifUartFifoResetTx);
+ EXPECT_EQ(result, kDifUartOk);
+
+ EXPECT_MASK32(
+ UART_FIFO_CTRL_REG_OFFSET,
+ {
+ {UART_FIFO_CTRL_RXRST, 0x1, true}, {UART_FIFO_CTRL_TXRST, 0x1, true},
+ });
+
+ result = dif_uart_fifo_reset(&dif_uart_, kDifUartFifoResetAll);
+ EXPECT_EQ(result, kDifUartOk);
+}
+
+class LoopbackSetTest : public UartTest {};
+
+TEST_F(LoopbackSetTest, NullArgs) {
+ dif_uart_result_t result =
+ dif_uart_loopback_set(nullptr, kDifUartLoopbackSystem, kDifUartEnable);
+ EXPECT_EQ(result, kDifUartBadArg);
+}
+
+TEST_F(LoopbackSetTest, Success) {
+ EXPECT_MASK32(UART_CTRL_REG_OFFSET, {{UART_CTRL_SLPBK, 0x1, true}});
+ dif_uart_result_t result =
+ dif_uart_loopback_set(&dif_uart_, kDifUartLoopbackSystem, kDifUartEnable);
+ EXPECT_EQ(result, kDifUartOk);
+
+ EXPECT_MASK32(UART_CTRL_REG_OFFSET, {{UART_CTRL_SLPBK, 0x1, false}});
+ result = dif_uart_loopback_set(&dif_uart_, kDifUartLoopbackSystem,
+ kDifUartDisable);
+ EXPECT_EQ(result, kDifUartOk);
+
+ EXPECT_MASK32(UART_CTRL_REG_OFFSET, {{UART_CTRL_LLPBK, 0x1, true}});
+ result =
+ dif_uart_loopback_set(&dif_uart_, kDifUartLoopbackLine, kDifUartEnable);
+ EXPECT_EQ(result, kDifUartOk);
+
+ EXPECT_MASK32(UART_CTRL_REG_OFFSET, {{UART_CTRL_LLPBK, 0x1, false}});
+ result =
+ dif_uart_loopback_set(&dif_uart_, kDifUartLoopbackLine, kDifUartDisable);
+ EXPECT_EQ(result, kDifUartOk);
+}
+
} // namespace
} // namespace dif_uart_unittest