From 9314b5950fd49466a626d3679dfcad6aefbc2dad Mon Sep 17 00:00:00 2001 From: maksyuki Date: Tue, 11 Aug 2026 14:17:33 +0800 Subject: [PATCH] feat: refactor UART implementation to support flow control and enhance functionality - Removed UART files from clusterip and updated file lists accordingly. - Added new UART flow control module and interface to manage RTS/CTS signals. - Updated UART formal verification files to include new flow control signals. - Modified UART testbench to validate flow control behavior and updated test cases. - Adjusted top-level design to integrate new UART interface and signal connections. - Enhanced GPIO pin mapping to accommodate new UART control signals. - Updated linting and formal generation scripts to reflect changes in UART structure. --- .github/workflows/_regression.yml | 1 - .github/workflows/release.yml | 1 - app/apps/ci_smoke/main.c | 12 +- config/dependencies.lock.json | 1 - crt/include/retrosoc/hal/uart.h | 31 +- crt/src/hal/uart.c | 122 +++- docs/README.md | 4 +- docs/ip/README.md | 2 +- docs/ip/uart.md | 78 ++- docs/mini-soc-block-diagram.dot | 3 +- docs/mini-soc-block-diagram.svg | 635 ++++++++++---------- docs/soc-integration-wiring.md | 18 +- rtl/ip/ribp/serial/ribp_uart.sv | 134 +++-- rtl/ip/ribp/serial/ribp_uart_define.svh | 12 + rtl/ip/ribp/serial/ribp_uart_tx.sv | 4 +- rtl/ip/ribp/serial/uart_core.sv | 30 +- rtl/ip/ribp/serial/uart_flow_ctrl.sv | 70 +++ rtl/ip/ribp/serial/uart_if.sv | 14 + rtl/ip/ribp/serial/uart_reg.sv | 242 +++++--- rtl/mini/dv/model/uart_model.sv | 71 +++ rtl/mini/dv/tb/retrosoc_tb.sv | 17 +- rtl/mini/filelist/clusterip.fl | 1 - rtl/mini/filelist/inc.fl | 1 - rtl/mini/filelist/ip.fl | 2 + rtl/mini/filelist/tb.fl | 2 +- rtl/mini/formal/generate_formal_filelist.py | 5 +- rtl/mini/formal/uart_formal.sv | 68 ++- rtl/mini/formal/uart_formal_props.sv | 76 ++- rtl/mini/integration/soc_topology.json | 4 +- rtl/mini/lint.msg | 2 +- rtl/mini/mk/formal.mk | 3 +- rtl/mini/pin_map/pin_map.json | 4 +- rtl/mini/top/retrosoc.sv | 8 +- rtl/mini/top/retrosoc_asic.sv | 10 +- tests/rtl/uart_tb.sv | 96 ++- tests/test_soc_topology.py | 5 +- tests/test_uart.py | 13 +- 37 files changed, 1182 insertions(+), 620 deletions(-) create mode 100644 rtl/ip/ribp/serial/uart_flow_ctrl.sv create mode 100644 rtl/ip/ribp/serial/uart_if.sv create mode 100644 rtl/mini/dv/model/uart_model.sv diff --git a/.github/workflows/_regression.yml b/.github/workflows/_regression.yml index 2c67ad70..946c97ce 100644 --- a/.github/workflows/_regression.yml +++ b/.github/workflows/_regression.yml @@ -76,7 +76,6 @@ jobs: rtl/managed/clusterip/rng rtl/managed/clusterip/rtc rtl/managed/clusterip/spi - rtl/managed/clusterip/uart rtl/managed/clusterip/wdg rtl/managed/third_party rtl/managed/mpw diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed116875..4eb30d8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,6 @@ jobs: rtl/managed/clusterip/rng rtl/managed/clusterip/rtc rtl/managed/clusterip/spi - rtl/managed/clusterip/uart rtl/managed/clusterip/wdg rtl/managed/third_party rtl/managed/mpw diff --git a/app/apps/ci_smoke/main.c b/app/apps/ci_smoke/main.c index c9f1725c..be6a35cd 100644 --- a/app/apps/ci_smoke/main.c +++ b/app/apps/ci_smoke/main.c @@ -160,7 +160,7 @@ static bool rs_ci_smoke_gpio_v2(void) { return rs_gpio_write(31U, false) == RS_OK; } -static bool rs_ci_smoke_uart_v2(void) { +static bool rs_ci_smoke_uart_v3(void) { const rs_uart_config_t config = { .source_clock_hz = CPU_FREQ * UINT32_C(1000000), .baud_rate = UART_BPS, @@ -173,12 +173,16 @@ static bool rs_ci_smoke_uart_v2(void) { .tx_enable = true, .rx_enable = true, .loopback_enable = true, + .auto_cts_enable = false, + .auto_rts_enable = false, + .rts_assert_level = 32U, + .rts_deassert_level = 48U, }; const uint8_t transmitted = UINT8_C(0xA5); rs_uart_rx_data_t received; - if ((RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0xF8)) != UINT32_C(0x00020000)) || - (RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0xFC)) != UINT32_C(0x00FF4040)) || + if ((RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0xF8)) != UINT32_C(0x00030000)) || + (RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, UINT32_C(0xFC)) != UINT32_C(0x03FF4040)) || (rs_uart_configure(&config, RS_TIMEOUT_DEFAULT) != RS_OK) || (rs_uart_write(&transmitted, 1U, RS_TIMEOUT_DEFAULT) != RS_OK) || (rs_uart_read(&received, 1U, RS_TIMEOUT_DEFAULT) != RS_OK) || @@ -193,7 +197,7 @@ int main(void) { rs_test_finish(RS_TEST_FAILED, 6U); } - if (!rs_ci_smoke_uart_v2()) { + if (!rs_ci_smoke_uart_v3()) { rs_test_finish(RS_TEST_FAILED, 7U); } diff --git a/config/dependencies.lock.json b/config/dependencies.lock.json index c196e720..dc3a54bc 100644 --- a/config/dependencies.lock.json +++ b/config/dependencies.lock.json @@ -72,7 +72,6 @@ "cluster_rng": {"url": "https://github.com/retroSoC/rng.git", "revision": "36f3e0774d8f3c4b597d3db8a96ca3992532b61e", "destination": "rtl/managed/clusterip/rng", "license": "MulanPSL-2.0"}, "cluster_rtc": {"url": "https://github.com/retroSoC/rtc.git", "revision": "32548c5cbcdf39e09d0880fb611f9dd47940ea88", "destination": "rtl/managed/clusterip/rtc", "license": "NOASSERTION"}, "cluster_spi": {"url": "https://github.com/retroSoC/spi.git", "revision": "515cb408806584525605b4747db5e890dbdf1928", "destination": "rtl/managed/clusterip/spi", "license": "NOASSERTION"}, - "cluster_uart": {"url": "https://github.com/retroSoC/uart.git", "revision": "eca12213ba06f42e0a6dd0f27907fd4c447e2774", "destination": "rtl/managed/clusterip/uart", "license": "NOASSERTION"}, "cluster_wdg": {"url": "https://github.com/retroSoC/wdg.git", "revision": "134be4b884efa8683b1617ceb41f17399b4bb83f", "destination": "rtl/managed/clusterip/wdg", "license": "MulanPSL-2.0"} }, "archives": { diff --git a/crt/include/retrosoc/hal/uart.h b/crt/include/retrosoc/hal/uart.h index 6b69a863..4ac6bf47 100644 --- a/crt/include/retrosoc/hal/uart.h +++ b/crt/include/retrosoc/hal/uart.h @@ -7,18 +7,23 @@ #include -#define RS_UART_ERROR_PARITY UINT32_C(0x01) -#define RS_UART_ERROR_FRAME UINT32_C(0x02) -#define RS_UART_ERROR_BREAK UINT32_C(0x04) -#define RS_UART_ERROR_NOISE UINT32_C(0x08) +#define RS_UART_ERROR_PARITY UINT32_C(0x01) +#define RS_UART_ERROR_FRAME UINT32_C(0x02) +#define RS_UART_ERROR_BREAK UINT32_C(0x04) +#define RS_UART_ERROR_NOISE UINT32_C(0x08) -#define RS_UART_INTR_RX_WATERMARK UINT32_C(0x01) -#define RS_UART_INTR_RX_TIMEOUT UINT32_C(0x02) -#define RS_UART_INTR_TX_WATERMARK UINT32_C(0x04) -#define RS_UART_INTR_TX_DONE UINT32_C(0x08) -#define RS_UART_INTR_RX_ERROR UINT32_C(0x10) -#define RS_UART_INTR_BREAK UINT32_C(0x20) -#define RS_UART_INTR_ALL UINT32_C(0x3F) +#define RS_UART_INTR_RX_WATERMARK UINT32_C(0x01) +#define RS_UART_INTR_RX_TIMEOUT UINT32_C(0x02) +#define RS_UART_INTR_TX_WATERMARK UINT32_C(0x04) +#define RS_UART_INTR_TX_DONE UINT32_C(0x08) +#define RS_UART_INTR_RX_ERROR UINT32_C(0x10) +#define RS_UART_INTR_BREAK UINT32_C(0x20) +#define RS_UART_INTR_CTS_CHANGE UINT32_C(0x40) +#define RS_UART_INTR_ALL UINT32_C(0x7F) + +#define RS_UART_STATUS_CTS_ASSERTED (UINT32_C(1) << 12U) +#define RS_UART_STATUS_RTS_ASSERTED (UINT32_C(1) << 13U) +#define RS_UART_STATUS_TX_FLOW_BLOCKED (UINT32_C(1) << 14U) typedef enum { RS_UART_PARITY_NONE = 0, @@ -38,6 +43,10 @@ typedef struct { bool tx_enable; bool rx_enable; bool loopback_enable; + bool auto_cts_enable; + bool auto_rts_enable; + uint8_t rts_assert_level; + uint8_t rts_deassert_level; } rs_uart_config_t; typedef struct { diff --git a/crt/src/hal/uart.c b/crt/src/hal/uart.c index 3d9c9ed8..94e88a28 100644 --- a/crt/src/hal/uart.c +++ b/crt/src/hal/uart.c @@ -2,41 +2,50 @@ #include #include +#include #include -#define RS_UART_REG(offset) RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, (offset)) +#define RS_UART_REG(offset) RS_SOC_REG32(RS_SOC_RIBP_UART0_BASE, (offset)) -#define RS_UART_BAUD_INT RS_UART_REG(UINT32_C(0x00)) -#define RS_UART_BAUD_FRAC RS_UART_REG(UINT32_C(0x04)) -#define RS_UART_LINE_CTRL RS_UART_REG(UINT32_C(0x08)) -#define RS_UART_CTRL RS_UART_REG(UINT32_C(0x0C)) -#define RS_UART_TXDATA RS_UART_REG(UINT32_C(0x10)) -#define RS_UART_RXDATA RS_UART_REG(UINT32_C(0x14)) -#define RS_UART_STATUS RS_UART_REG(UINT32_C(0x18)) -#define RS_UART_FIFO_LEVEL RS_UART_REG(UINT32_C(0x1C)) -#define RS_UART_FIFO_CTRL RS_UART_REG(UINT32_C(0x20)) -#define RS_UART_TX_WATERMARK RS_UART_REG(UINT32_C(0x24)) -#define RS_UART_RX_WATERMARK RS_UART_REG(UINT32_C(0x28)) -#define RS_UART_RX_TIMEOUT RS_UART_REG(UINT32_C(0x2C)) -#define RS_UART_ERROR_STATUS RS_UART_REG(UINT32_C(0x30)) -#define RS_UART_INTR_STATE RS_UART_REG(UINT32_C(0x34)) -#define RS_UART_INTR_ENABLE RS_UART_REG(UINT32_C(0x38)) -#define RS_UART_INTR_TEST RS_UART_REG(UINT32_C(0x40)) -#define RS_UART_DMA_CTRL RS_UART_REG(UINT32_C(0x44)) +#define RS_UART_BAUD_INT RS_UART_REG(UINT32_C(0x00)) +#define RS_UART_BAUD_FRAC RS_UART_REG(UINT32_C(0x04)) +#define RS_UART_LINE_CTRL RS_UART_REG(UINT32_C(0x08)) +#define RS_UART_CTRL RS_UART_REG(UINT32_C(0x0C)) +#define RS_UART_TXDATA RS_UART_REG(UINT32_C(0x10)) +#define RS_UART_RXDATA RS_UART_REG(UINT32_C(0x14)) +#define RS_UART_STATUS RS_UART_REG(UINT32_C(0x18)) +#define RS_UART_FIFO_LEVEL RS_UART_REG(UINT32_C(0x1C)) +#define RS_UART_FIFO_CTRL RS_UART_REG(UINT32_C(0x20)) +#define RS_UART_TX_WATERMARK RS_UART_REG(UINT32_C(0x24)) +#define RS_UART_RX_WATERMARK RS_UART_REG(UINT32_C(0x28)) +#define RS_UART_RX_TIMEOUT RS_UART_REG(UINT32_C(0x2C)) +#define RS_UART_ERROR_STATUS RS_UART_REG(UINT32_C(0x30)) +#define RS_UART_INTR_STATE RS_UART_REG(UINT32_C(0x34)) +#define RS_UART_INTR_ENABLE RS_UART_REG(UINT32_C(0x38)) +#define RS_UART_INTR_TEST RS_UART_REG(UINT32_C(0x40)) +#define RS_UART_DMA_CTRL RS_UART_REG(UINT32_C(0x44)) +#define RS_UART_FLOW_CTRL RS_UART_REG(UINT32_C(0x48)) +#define RS_UART_RTS_WATERMARK RS_UART_REG(UINT32_C(0x4C)) -#define RS_UART_CTRL_TX_ENABLE UINT32_C(0x01) -#define RS_UART_CTRL_RX_ENABLE UINT32_C(0x02) -#define RS_UART_CTRL_LOOPBACK UINT32_C(0x04) -#define RS_UART_STATUS_TX_BUSY UINT32_C(0x04) -#define RS_UART_STATUS_RX_BUSY UINT32_C(0x08) -#define RS_UART_STATUS_TX_FULL UINT32_C(0x20) -#define RS_UART_STATUS_RX_EMPTY UINT32_C(0x40) -#define RS_UART_STATUS_CONFIG_VALID UINT32_C(0x100) -#define RS_UART_FIFO_TX_FLUSH UINT32_C(0x01) -#define RS_UART_FIFO_RX_FLUSH UINT32_C(0x02) -#define RS_UART_ERROR_ALL UINT32_C(0x7F) -#define RS_UART_DMA_TX_ENABLE UINT32_C(0x01) -#define RS_UART_DMA_RX_ENABLE UINT32_C(0x02) +#define RS_UART_CTRL_TX_ENABLE UINT32_C(0x01) +#define RS_UART_CTRL_RX_ENABLE UINT32_C(0x02) +#define RS_UART_CTRL_LOOPBACK UINT32_C(0x04) +#define RS_UART_STATUS_TX_BUSY UINT32_C(0x04) +#define RS_UART_STATUS_RX_BUSY UINT32_C(0x08) +#define RS_UART_STATUS_TX_FULL UINT32_C(0x20) +#define RS_UART_STATUS_RX_EMPTY UINT32_C(0x40) +#define RS_UART_STATUS_CONFIG_VALID UINT32_C(0x100) +#define RS_UART_FIFO_TX_FLUSH UINT32_C(0x01) +#define RS_UART_FIFO_RX_FLUSH UINT32_C(0x02) +#define RS_UART_ERROR_ALL UINT32_C(0x7F) +#define RS_UART_DMA_TX_ENABLE UINT32_C(0x01) +#define RS_UART_DMA_RX_ENABLE UINT32_C(0x02) +#define RS_UART_FLOW_AUTO_CTS UINT32_C(0x01) +#define RS_UART_FLOW_AUTO_RTS UINT32_C(0x02) +#define RS_UART_CTS_GPIO UINT32_C(0) +#define RS_UART_RTS_GPIO UINT32_C(1) +#define RS_UART_RTS_ASSERT_DEFAULT UINT32_C(32) +#define RS_UART_RTS_DEASSERT_DEFAULT UINT32_C(48) static uint32_t rs_uart_control_from_config(const rs_uart_config_t *config) { uint32_t control = 0U; @@ -57,12 +66,40 @@ static bool rs_uart_config_valid(const rs_uart_config_t *config) { return (config != NULL) && (config->data_bits >= 5U) && (config->data_bits <= 8U) && ((config->stop_bits == 1U) || (config->stop_bits == 2U)) && (config->parity <= RS_UART_PARITY_ODD) && (config->tx_watermark < 64U) && - (config->rx_watermark > 0U) && (config->rx_watermark <= 64U); + (config->rx_watermark > 0U) && (config->rx_watermark <= 64U) && + (!config->auto_rts_enable || ((config->rts_assert_level < config->rts_deassert_level) && + (config->rts_deassert_level <= 64U))); +} + +static rs_status_t rs_uart_flow_gpio_configure(const rs_uart_config_t *config) { + const rs_gpio_config_t pin_config = { + .mode = RS_GPIO_MODE_ALT0, + .pull = RS_GPIO_PULL_NONE, + .trigger = RS_GPIO_TRIGGER_NONE, + .output_high = true, + .open_drain = false, + .input_cmos = false, + .filter_enable = false, + .interrupt_enable = false, + }; + rs_status_t status = RS_OK; + + if (config->auto_cts_enable) { + status = rs_gpio_configure(RS_UART_CTS_GPIO, &pin_config); + } + if ((status == RS_OK) && config->auto_rts_enable) { + status = rs_gpio_configure(RS_UART_RTS_GPIO, &pin_config); + } + return status; } rs_status_t rs_uart_configure(const rs_uart_config_t *config, rs_timeout_t timeout) { rs_uart_timing_t timing; + rs_status_t status; + uint32_t flow_control = 0U; uint32_t line_control; + uint32_t rts_assert_level; + uint32_t rts_deassert_level; if (!rs_uart_config_valid(config) || (rs_uart_timing_calculate(config->source_clock_hz, config->baud_rate, &timing) != RS_OK)) { @@ -78,6 +115,11 @@ rs_status_t rs_uart_configure(const rs_uart_config_t *config, rs_timeout_t timeo return RS_ETIMEOUT; } + status = rs_uart_flow_gpio_configure(config); + if (status != RS_OK) { + return status; + } + RS_UART_FIFO_CTRL = RS_UART_FIFO_TX_FLUSH | RS_UART_FIFO_RX_FLUSH; RS_UART_ERROR_STATUS = RS_UART_ERROR_ALL; RS_UART_INTR_STATE = RS_UART_INTR_ALL; @@ -90,6 +132,18 @@ rs_status_t rs_uart_configure(const rs_uart_config_t *config, rs_timeout_t timeo RS_UART_RX_WATERMARK = config->rx_watermark; RS_UART_RX_TIMEOUT = config->rx_timeout_bits; RS_UART_DMA_CTRL = 0U; + if (config->auto_cts_enable) { + flow_control |= RS_UART_FLOW_AUTO_CTS; + } + if (config->auto_rts_enable) { + flow_control |= RS_UART_FLOW_AUTO_RTS; + } + rts_assert_level = + config->auto_rts_enable ? config->rts_assert_level : RS_UART_RTS_ASSERT_DEFAULT; + rts_deassert_level = + config->auto_rts_enable ? config->rts_deassert_level : RS_UART_RTS_DEASSERT_DEFAULT; + RS_UART_RTS_WATERMARK = rts_assert_level | (rts_deassert_level << 16U); + RS_UART_FLOW_CTRL = flow_control; RS_UART_CTRL = rs_uart_control_from_config(config); return ((RS_UART_STATUS & RS_UART_STATUS_CONFIG_VALID) != 0U) ? RS_OK : RS_EIO; @@ -108,6 +162,10 @@ rs_status_t rs_uart_init(uint32_t source_clock_hz, uint32_t baud_rate) { .tx_enable = true, .rx_enable = true, .loopback_enable = false, + .auto_cts_enable = false, + .auto_rts_enable = false, + .rts_assert_level = (uint8_t)RS_UART_RTS_ASSERT_DEFAULT, + .rts_deassert_level = (uint8_t)RS_UART_RTS_DEASSERT_DEFAULT, }; return rs_uart_configure(&config, RS_TIMEOUT_DEFAULT); } diff --git a/docs/README.md b/docs/README.md index 3ca4b97f..8ab282f6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,8 +28,8 @@ the root README and subsystem guides. timebase, RV32 access rules, interrupt behavior, and verification contract. - [ip/gpio.md](ip/gpio.md) defines the GPIO V2 dual-window ABI, pad ownership, filtering, interrupts, PDK capabilities, HAL, and verification contract. -- [ip/uart.md](ip/uart.md) defines the UART V2 framing, FIFO, error, interrupt, - DMA, HAL, and verification contracts. +- [ip/uart.md](ip/uart.md) defines the UART V3 framing, FIFO, RTS/CTS flow + control, error, interrupt, DMA, HAL, and verification contracts. - [ip/i2c.md](ip/i2c.md) defines the dual I2C V2 command, timing, error, recovery, DMA, HAL, and verification contracts. - [ip/ps2.md](ip/ps2.md) defines the Mini SoC PS/2 V2 APB, GPIO pad, diff --git a/docs/ip/README.md b/docs/ip/README.md index 2d86fe9e..ebaa7b30 100644 --- a/docs/ip/README.md +++ b/docs/ip/README.md @@ -7,7 +7,7 @@ verification documentation for self-owned retroSoC peripheral IP. - [ws2812.md](ws2812.md) defines the WS2812 transmitter. - [clint.md](clint.md) defines the management-hart software and timer interrupt block. - [gpio.md](gpio.md) defines the dual-window GPIO controller. -- [uart.md](uart.md) defines the RIBP UART V2 controller. +- [uart.md](uart.md) defines the RIBP UART V3 controller. - [i2c.md](i2c.md) defines the dual RIBP I2C V2 controllers. The corresponding RTL and HAL implementations remain the executable sources diff --git a/docs/ip/uart.md b/docs/ip/uart.md index 175d4987..672f2499 100644 --- a/docs/ip/uart.md +++ b/docs/ip/uart.md @@ -1,10 +1,10 @@ -# RIBP UART V2 +# RIBP UART V3 UART0 is the Mini SoC management console and a general-purpose full-duplex -serial port. UART V2 provides independent 64-byte transmit and receive FIFOs, +serial port. UART V3 provides independent 64-byte transmit and receive FIFOs, fractional baud-rate generation, configurable framing, receive diagnostics, -watermark and timeout interrupts, internal loopback, break generation, and -generic-DMA request pacing. +watermark and timeout interrupts, internal loopback, break generation, +generic-DMA request pacing, and automatic active-low RTS/CTS flow control. ## Integration @@ -15,11 +15,13 @@ generic-DMA request pacing. | RIBP interrupt group bit | 2 | | Management-core interrupt | 2 | | DMA modes | 5 UART TX, 6 UART RX | -| External signals | `uart0_tx`, `uart0_rx` | +| Dedicated signals | `uart0_tx`, `uart0_rx` | +| Flow-control signals | GPIO0 ALT0 `uart0_cts_n`, GPIO1 ALT0 `uart0_rts_n` | -UART0 and the generic DMA share the SoC clock domain. The existing `uart_if` -continues to own the physical RX, TX, and IRQ signals; DMA readiness is a -separate internal connection. +UART0 and the generic DMA share the SoC clock domain. The self-owned `uart_if` +contains RX, TX, active-low CTS/RTS, and IRQ. RX/TX retain their dedicated +package pads. CTS/RTS reuse GPIO0/1 ALT0, while GPIO0/1 ALT1 remains the PS/2 +clock/data route. DMA readiness is a separate internal connection. ## Register ABI @@ -43,13 +45,15 @@ require the low byte lane; selected upper lanes must contain zero. | `0x028` | `RX_WATERMARK` | RW | High-water threshold, 1 through 64; reset value 32. | | `0x02C` | `RX_TIMEOUT_BITS` | RW | Idle bit periods before timeout; zero disables, reset value 32. | | `0x030` | `ERROR_STATUS` | RW1C | Overrun, parity, frame, break, noise, config, command. | -| `0x034` | `INTR_STATE` | RW1C | RX water/timeout, TX water/done, RX error, break. | +| `0x034` | `INTR_STATE` | RW1C | RX water/timeout, TX water/done, RX error, break, CTS change. | | `0x038` | `INTR_ENABLE` | RW | Interrupt enable mask. | | `0x03C` | `INTR_STATUS` | RO | `INTR_STATE & INTR_ENABLE`. | | `0x040` | `INTR_TEST` | WO | Write-one interrupt injection. | | `0x044` | `DMA_CTRL` | RW | TX and RX DMA request enables. | -| `0x0F8` | `IP_VERSION` | RO | `0x00020000`, ABI 2.0. | -| `0x0FC` | `CAPABILITY` | RO | `0x00FF4040`, depths and implemented features. | +| `0x048` | `FLOW_CTRL` | RW | Bit 0 enables automatic CTS; bit 1 enables automatic RTS. | +| `0x04C` | `RTS_WATERMARK` | RW | Assert/ready level at bits 6:0 and deassert/halt level at bits 22:16. | +| `0x0F8` | `IP_VERSION` | RO | `0x00030000`, ABI 3.0. | +| `0x0FC` | `CAPABILITY` | RO | `0x03FF4040`, depths and implemented features. | `LINE_CTRL.DATA_BITS` values 0 through 3 select 5 through 8 bits. Bit 2 selects two stop bits. Bits 4:3 select none, even, or odd parity; value 3 is @@ -60,6 +64,18 @@ break. error, break, and inconsistent majority samples. A full RX FIFO drops the new character and sets global overrun. An empty `RXDATA` read returns `resp_err`. +`STATUS[12]` reports synchronized CTS asserted, `STATUS[13]` reports RTS +asserted, and `STATUS[14]` reports that queued TX data is waiting for CTS. +Interrupt bit 6 is sticky on either synchronized CTS edge while automatic CTS +is enabled. All interrupt state, enable, status, test, and clear masks are +seven bits in V3. + +`RTS_WATERMARK` resets to assert/ready level 32 and deassert/halt level 48. +Software must program `assert < deassert <= 64`. UART configuration registers, +including both flow-control registers, can change only while TX/RX are disabled +and both engines are idle. Illegal thresholds or an active-engine update return +`resp_err` and set the configuration error bit. + ## Timing and transfer behavior The programmed bit period is `BAUD_INT + BAUD_FRAC / 256`. A phase accumulator @@ -74,6 +90,20 @@ but lets the current frame finish; clearing RX enable aborts a partial frame. TX break is accepted only while the transmitter is idle. FIFO contents are preserved by enable changes and are discarded only by an accepted flush. +With automatic CTS enabled, synchronized low `cts_n` permits the transmitter +to remove a character from the TX FIFO and start a frame. CTS deassertion never +truncates an active frame; it prevents only the next frame. Internal loopback +bypasses CTS so production test cannot deadlock on an unconnected pad. The CTS +synchronizer resets to not-clear-to-send, which is fail-safe when flow control +is enabled before a valid external level arrives. + +With automatic RTS enabled and RX enabled, `rts_n` is low while the receiver can +accept data. At or above the deassert watermark it rises to request that the +peer stop. It remains high until software drains the FIFO to or below the +assert watermark, providing hysteresis. The output releases high immediately +when automatic RTS or RX is disabled. FIFO push/pop changes affect the +registered hysteresis state at the following SoC-clock boundary. + A full `TXDATA` write applies RIBP backpressure while the enabled transmitter can make progress. A full write while disabled is rejected, avoiding an unbounded bus wait. TX-done is raised when a transmitted character leaves both @@ -94,16 +124,24 @@ status and interrupt control, FIFO flush, and UART-paced DMA transfers. `putch()` remains the internal formatted-output sink, while applications use `rs_uart_init()` or `rs_uart_configure()` instead of raw registers. +Enabling automatic CTS or RTS through `rs_uart_configure()` selects GPIO0 or +GPIO1 ALT0 respectively. Disabling flow control leaves that GPIO mux selection +unchanged so another driver is never enabled implicitly; software must +explicitly reconfigure a pin before reusing it. Default initialization leaves +both automatic functions disabled, preserving console behavior on boards that +do not route flow-control signals. + ## Verification and current scope The self-checking RTL test covers the versioned ABI, access errors, FIFO depth, -8N1 loopback, external frame errors, W1C interrupt behavior, and DMA request -gating. The SBY target proves FIFO bounds, IRQ composition, disabled-DMA -gating, and TX idle/break levels, and covers bus error, TX/RX activity, error, -and IRQ paths. Host tests cover exact fractional timing and invalid ranges; -the CI smoke firmware verifies capability registers and loopback through the -public HAL. +8N1 loopback, external frame errors, CTS frame-boundary gating, loopback bypass, +RTS hysteresis, CTS-change interrupt behavior, and DMA request gating. The SBY +target proves FIFO bounds, IRQ composition, disabled-DMA gating, RTS release, +flow-block consistency, forbidden TX FIFO pops, and TX idle/break levels, and +covers bus error, TX/RX activity, flow blocking, RTS assertion, error, and IRQ +paths. Host tests cover exact fractional timing and invalid ranges; the CI smoke +firmware verifies V3 capability registers and loopback through the public HAL. -Hardware RTS/CTS, IrDA, RS485 direction control, automatic baud detection, and -low-power wakeup are outside UART V2. Adding one requires a versioned ABI and, -where applicable, explicit pad and clock/reset integration. +IrDA, RS485 direction control, automatic baud detection, modem-status inputs, +and low-power wakeup are outside UART V3. Adding one requires a versioned ABI +and, where applicable, explicit pad and clock/reset integration. diff --git a/docs/mini-soc-block-diagram.dot b/docs/mini-soc-block-diagram.dot index 393012ca..7e2755f1 100644 --- a/docs/mini-soc-block-diagram.dot +++ b/docs/mini-soc-block-diagram.dot @@ -132,7 +132,7 @@ digraph mini_soc_block_diagram { ]; uart0 [ fillcolor="#F9E1E7", - label="UART0 V2\n64-byte TX/RX FIFOs, fractional baud\nframing/errors, IRQ2, DMA-paced" + label="UART0 V3\n64-byte TX/RX FIFOs, fractional baud\nauto RTS/CTS, framing/errors, IRQ2, DMA-paced" ]; i2c_controllers [ fillcolor="#F9E1E7", @@ -245,6 +245,7 @@ digraph mini_soc_block_diagram { dma -> ws2812 [label="TXDATA writes", style=dashed]; ws2812 -> irq [label="IRQ17"]; dma -> uart0 [label="TX/RX data", style=dashed]; + uart0 -> gpio_controller [dir=both, label="GPIO0/1 ALT0\nCTS_n / RTS_n"]; uart0 -> irq [label="IRQ2"]; i2c_controllers -> gpio_controller [label="GPIO3/4 ALT1\nGPIO7/8 ALT0"]; dma -> i2c_controllers [label="DATA_CMD / RXDATA", style=dashed]; diff --git a/docs/mini-soc-block-diagram.svg b/docs/mini-soc-block-diagram.svg index cf64ff8c..20b2f92f 100644 --- a/docs/mini-soc-block-diagram.svg +++ b/docs/mini-soc-block-diagram.svg @@ -4,19 +4,19 @@ - + mini_soc_block_diagram retroSoC Mini SoC Architecture cluster_asic - + retrosoc_asic cluster_soc - + retrosoc @@ -44,374 +44,374 @@ ext_io - -External I/O and memory -GPIO, UART0, PS/2, XPI Flash, SDRAM, PSRAM -SD card, I2C, I2S, DVP, WS2812 and PWM V2 + +External I/O and memory +GPIO, UART0, PS/2, XPI Flash, SDRAM, PSRAM +SD card, I2C, I2S, DVP, WS2812 and PWM V2 ext_jtag - -Management JTAG -TCK, TMS, TDI, TRST_n, TDO + +Management JTAG +TCK, TMS, TDI, TRST_n, TDO jtag_dtm - -Hazard3 JTAG DTM -10 MHz asynchronous TCK domain + +Hazard3 JTAG DTM +10 MHz asynchronous TCK domain ext_jtag->jtag_dtm - - -JTAG pads + + +JTAG pads fabric - -RIB fabric -3-master round-robin arbitration -INCR1 / INCR4, access policy, fault reporting + +RIB fabric +3-master round-robin arbitration +INCR1 / INCR4, access policy, fault reporting rcu->fabric - - -system clock / reset + + +system clock / reset clint - -CLINT -standard 64 KiB register map -mtime, mtimecmp and MSIP + +CLINT +standard 64 KiB register map +mtime, mtimecmp and MSIP rcu->clint - - -1 MHz tick + + +1 MHz tick wdg - -WDG V2 -independent-clock window watchdog -early IRQ14, retained cause/count + +WDG V2 +independent-clock window watchdog +early IRQ14, retained cause/count - + rcu->wdg - - -independent 18.432 MHz clock + + +independent 18.432 MHz clock rtc - -RTC V2 -64-bit Epoch + 1/256-second time -dual alarm, periodic wake, calibration, IRQ13 + +RTC V2 +64-bit Epoch + 1/256-second time +dual alarm, periodic wake, calibration, IRQ13 - + rcu->rtc - - -18.432 MHz audio clock + + +18.432 MHz audio clock mgmt_core - -Management core -Fixed Hazard3, branch predictor -JTAG Debug Module always present + +Management core +Fixed Hazard3, branch predictor +JTAG Debug Module always present mgmt_adapter - -RIBP2RIB + +RIBP2RIB mgmt_core->mgmt_adapter - - -RIBP + + +RIBP debug_module - -Hazard3 Debug Module -abstract commands, no SBA -management-hart-only reset + +Hazard3 Debug Module +abstract commands, no SBA +management-hart-only reset jtag_dtm->debug_module - - -DMI, asynchronous CDC + + +DMI, asynchronous CDC debug_module->mgmt_core - - -halt, resume, abstract commands + + +halt, resume, abstract commands debug_module->mgmt_adapter - - -reset after bridge idle + + +reset after bridge idle timers - -General timers 0/1 -32-bit counter, 16-bit prescaler -periodic / one-shot, compare, debug freeze + +General timers 0/1 +32-bit counter, 16-bit prescaler +periodic / one-shot, compare, debug freeze debug_module->timers - - -halted + + +halted mgmt_adapter->fabric - - -RIB + + +RIB user_core - -Selected user core -C0-C5, one active -PicoRV32 at C5 + +Selected user core +C0-C5, one active +PicoRV32 at C5 user_core->fabric - - -RIB + + +RIB dma - -DMA -RIB master and RIBP target + +DMA +RIB master and RIBP target dma->fabric - - -RIB + + +RIB uart0 - -UART0 V2 -64-byte TX/RX FIFOs, fractional baud -framing/errors, IRQ2, DMA-paced + +UART0 V3 +64-byte TX/RX FIFOs, fractional baud +auto RTS/CTS, framing/errors, IRQ2, DMA-paced dma->uart0 - - -TX/RX data + + +TX/RX data i2c_controllers - -I2C0 / I2C1 V2 -16-entry command/RX FIFOs, Sm/Fm/Fm+ -7/10-bit, IRQ7/19, DMA-paced + +I2C0 / I2C1 V2 +16-entry command/RX FIFOs, Sm/Fm/Fm+ +7/10-bit, IRQ7/19, DMA-paced - + dma->i2c_controllers - - -DATA_CMD / RXDATA + + +DATA_CMD / RXDATA ws2812 - -WS2812 TX -24-bit MSB-first serializer -16-word FIFO, IRQ17, DMA-fed + +WS2812 TX +24-bit MSB-first serializer +16-word FIFO, IRQ17, DMA-fed dma->ws2812 - - -TXDATA writes + + +TXDATA writes fault - -Error slave -access and protocol faults + +Error slave +access and protocol faults fabric->fault - - - -invalid / denied + + + +invalid / denied sram - -On-chip SRAM -optional RIB2RAM path + +On-chip SRAM +optional RIB2RAM path fabric->sram - - -RIB + + +RIB ribp_adapter - -RIB2RIBP + +RIB2RIBP fabric->ribp_adapter - - -RIB + + +RIB apb_adapter - -RIB2APB -direct APB bridge + +RIB2APB +direct APB bridge fabric->apb_adapter - - -RIB + + +RIB sysctrl - -SYSCTRL -PLL, user-core select/reset/bus enable -fault and performance control + +SYSCTRL +PLL, user-core select/reset/bus enable +fault and performance control ribp_adapter->sysctrl - - -RIBP + + +RIBP ribp_adapter->clint - - -RIBP + + +RIBP ribp_targets - -RIBP target wrapper -XPI/Flash, SDRAM, PSRAM, SPI-SD and DVP -I2S + +RIBP target wrapper +XPI/Flash, SDRAM, PSRAM, SPI-SD and DVP +I2S ribp_adapter->ribp_targets - - -RIBP + + +RIBP ribp_adapter->uart0 - - -RIBP + + +RIBP ribp_adapter->i2c_controllers - - -RIBP + + +RIBP gpio_controller - -GPIO V2 -user + management RIBP windows -atomic I/O, filter, locks, IRQ18 + +GPIO V2 +user + management RIBP windows +atomic I/O, filter, locks, IRQ18 ribp_adapter->gpio_controller - - -RIBP -user/admin + + +RIBP +user/admin ribp_adapter->timers - - -RIBP + + +RIBP ribp_adapter->ws2812 - - -RIBP + + +RIBP @@ -423,53 +423,53 @@ apb_adapter->apb_targets - + -APB +APB pwm - -PWM V2 -2 timers, 4 action channels, 2 operators -fades, dead time, carrier, fault and capture, IRQ11 + +PWM V2 +2 timers, 4 action channels, 2 operators +fades, dead time, carrier, fault and capture, IRQ11 apb_adapter->pwm - - -APB + + +APB apb_adapter->wdg - - -APB + + +APB crc - -CRC V2 -programmable 7/8/16/32-bit streaming CRC -byte, halfword and word APB feed + +CRC V2 +programmable 7/8/16/32-bit streaming CRC +byte, halfword and word APB feed apb_adapter->crc - - -APB + + +APB apb_adapter->rtc - - -APB + + +APB @@ -482,222 +482,231 @@ apb_adapter->archinfo - - -APB + + +APB rng - -RNG V2 entropy controller -8-word FIFO, fail-closed monitoring, IRQ16 -management-only, deterministic source unqualified + +RNG V2 entropy controller +8-word FIFO, fail-closed monitoring, IRQ16 +management-only, deterministic source unqualified apb_adapter->rng - - -APB + + +APB ps2 - -PS/2 host V2 -bidirectional open-drain transport -16-byte RX/TX FIFOs, errors, IRQ12 + +PS/2 host V2 +bidirectional open-drain transport +16-byte RX/TX FIFOs, errors, IRQ12 apb_adapter->ps2 - - -APB + + +APB irq - -Interrupt aggregation -RIBP and APB target IRQs to management core + +Interrupt aggregation +RIBP and APB target IRQs to management core clint->irq - - -machine IRQ0 / IRQ1 + + +machine IRQ0 / IRQ1 - + ribp_targets->ext_io - - - -device interfaces + + + +device interfaces ribp_targets->gpio_controller - - -alternate functions + + +alternate functions - + uart0->ext_io - - - -UART pads + + + +UART pads + + + +uart0->gpio_controller + + + +GPIO0/1 ALT0 +CTS_n / RTS_n - + uart0->irq - - -IRQ2 + + +IRQ2 - + i2c_controllers->gpio_controller - - -GPIO3/4 ALT1 -GPIO7/8 ALT0 + + +GPIO3/4 ALT1 +GPIO7/8 ALT0 - + i2c_controllers->irq - - -IRQ7 / IRQ19 + + +IRQ7 / IRQ19 gpio_bridge - -GPIO pad bridge -PDK capability-adapted controls + +GPIO pad bridge +PDK capability-adapted controls gpio_controller->gpio_bridge - - -pad control + + +pad control gpio_controller->irq - - -IRQ18 + + +IRQ18 timers->irq - - -IRQ3 / IRQ4 + + +IRQ3 / IRQ4 ws2812->gpio_bridge - - -GPIO2 ALT1 + + +GPIO2 ALT1 ws2812->irq - - -IRQ17 + + +IRQ17 - + apb_targets->gpio_controller - - -user_gpio + + +user_gpio - + pwm->gpio_controller - - - -GPIO3-6 output -GPIO2 sync, GPIO9 fault -GPIO30/31 capture + + + +GPIO3-6 output +GPIO2 sync, GPIO9 fault +GPIO30/31 capture - + pwm->irq - - -IRQ11 + + +IRQ11 - + wdg->rcu - - -system reset request + + +system reset request - + wdg->irq - - -early warning IRQ14 + + +early warning IRQ14 - + rtc->sysctrl - - -wake live / sticky + + +wake live / sticky - + rtc->irq - - -IRQ13 + + +IRQ13 - + rng->irq - - -IRQ16 + + +IRQ16 - + ps2->gpio_controller - - -GPIO0/1 ALT1 -open drain + + +GPIO0/1 ALT1 +open drain - + ps2->irq - - -IRQ12 + + +IRQ12 - + gpio_bridge->ext_io - - - -pad control + + + +pad control diff --git a/docs/soc-integration-wiring.md b/docs/soc-integration-wiring.md index fdccdb3c..a07d064e 100644 --- a/docs/soc-integration-wiring.md +++ b/docs/soc-integration-wiring.md @@ -22,10 +22,26 @@ feature, and every profile binding in the JSON map. Use `null` for an open binding. The generator rejects duplicate pad names, unknown profile bindings, unsupported features, and unapproved connection syntax. -SoC wrappers use the existing clusterIP interfaces for protocol boundaries. +SoC wrappers use the existing clusterIP interfaces for protocol boundaries, +except for the self-owned UART0 `uart_if` under `rtl/ip/ribp/serial`. `apb4_if_bridge` only adapts `apb4_pure_if` to `apb4_if`, and `gpio_pad_bridge` only exposes the pad-side subset of `gpio_if`; neither module contains state. +## UART flow-control alternate functions + +UART0 keeps dedicated RX and TX package pads. Optional active-low hardware flow +control uses GPIO0 ALT0 for the CTS input and GPIO1 ALT0 for the RTS output. +The UART owns synchronization and fail-safe CTS behavior; GPIO supplies only +the pad mux and resolved pad input. GPIO0/1 ALT1 remains assigned to the PS/2 +clock/data pair, so software cannot use PS/2 and UART0 flow control on the same +pins simultaneously. + +`rs_uart_configure()` selects each ALT0 route when the corresponding automatic +flow-control function is enabled. Disabling UART flow control does not reclaim +the GPIO pin. Software must explicitly choose a new GPIO mode before assigning +that physical pad to another function. See [uart.md](ip/uart.md) for the V3 +register and timing contract. + ## I2C alternate functions The two I2C V2 controllers reuse GPIO pads rather than dedicated package pins. diff --git a/rtl/ip/ribp/serial/ribp_uart.sv b/rtl/ip/ribp/serial/ribp_uart.sv index bb73da55..124050d4 100644 --- a/rtl/ip/ribp/serial/ribp_uart.sv +++ b/rtl/ip/ribp/serial/ribp_uart.sv @@ -24,8 +24,13 @@ module ribp_uart #( logic s_rx_enable; logic s_loopback; logic s_break; + logic s_auto_cts_enable; + logic s_auto_rts_enable; + logic [ 6:0] s_rts_assert_level; + logic [ 6:0] s_rts_deassert_level; logic s_tx_data_valid; logic [ 7:0] s_tx_data; + logic [ 6:0] s_rx_level; logic s_tx_data_pop; logic s_tx_busy; logic s_tx_done; @@ -35,6 +40,11 @@ module ribp_uart #( logic s_bit_tick; logic s_irq; logic s_tx; + logic s_cts_asserted; + logic s_rts_asserted; + logic s_tx_start_allowed; + logic s_tx_flow_blocked; + logic s_cts_change; assign uart.tx_o = s_tx; assign uart.irq_o = s_irq; @@ -43,55 +53,87 @@ module ribp_uart #( .TX_FIFO_DEPTH(TX_FIFO_DEPTH), .RX_FIFO_DEPTH(RX_FIFO_DEPTH) ) u_uart_reg ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .ribp (ribp), - .baud_int_o (s_baud_int), - .baud_frac_o (s_baud_frac), - .data_bits_o (s_data_bits), - .stop2_o (s_stop2), - .parity_o (s_parity), - .tx_enable_o (s_tx_enable), - .rx_enable_o (s_rx_enable), - .loopback_o (s_loopback), - .break_o (s_break), - .tx_data_valid_o(s_tx_data_valid), - .tx_data_o (s_tx_data), - .tx_data_pop_i (s_tx_data_pop), - .tx_busy_i (s_tx_busy), - .tx_done_i (s_tx_done), - .rx_active_i (s_rx_active), - .rx_data_valid_i(s_rx_data_valid), - .rx_data_i (s_rx_data), - .bit_tick_i (s_bit_tick), - .dma_tx_stall_o (dma_tx_stall_o), - .dma_rx_stall_o (dma_rx_stall_o), - .irq_o (s_irq) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .ribp (ribp), + .baud_int_o (s_baud_int), + .baud_frac_o (s_baud_frac), + .data_bits_o (s_data_bits), + .stop2_o (s_stop2), + .parity_o (s_parity), + .tx_enable_o (s_tx_enable), + .rx_enable_o (s_rx_enable), + .loopback_o (s_loopback), + .break_o (s_break), + .auto_cts_enable_o (s_auto_cts_enable), + .auto_rts_enable_o (s_auto_rts_enable), + .rts_assert_level_o (s_rts_assert_level), + .rts_deassert_level_o(s_rts_deassert_level), + .tx_data_valid_o (s_tx_data_valid), + .tx_data_o (s_tx_data), + .rx_level_o (s_rx_level), + .tx_data_pop_i (s_tx_data_pop), + .tx_busy_i (s_tx_busy), + .tx_done_i (s_tx_done), + .rx_active_i (s_rx_active), + .rx_data_valid_i (s_rx_data_valid), + .rx_data_i (s_rx_data), + .bit_tick_i (s_bit_tick), + .cts_asserted_i (s_cts_asserted), + .rts_asserted_i (s_rts_asserted), + .tx_flow_blocked_i (s_tx_flow_blocked), + .cts_change_i (s_cts_change), + .dma_tx_stall_o (dma_tx_stall_o), + .dma_rx_stall_o (dma_rx_stall_o), + .irq_o (s_irq) ); uart_core u_uart_core ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .baud_int_i (s_baud_int), - .baud_frac_i (s_baud_frac), - .data_bits_i (s_data_bits), - .stop2_i (s_stop2), - .parity_i (s_parity), - .tx_enable_i (s_tx_enable), - .rx_enable_i (s_rx_enable), - .loopback_i (s_loopback), - .break_i (s_break), - .tx_data_valid_i(s_tx_data_valid), - .tx_data_i (s_tx_data), - .tx_data_pop_o (s_tx_data_pop), - .tx_busy_o (s_tx_busy), - .tx_done_o (s_tx_done), - .rx_active_o (s_rx_active), - .rx_data_valid_o(s_rx_data_valid), - .rx_data_o (s_rx_data), - .bit_tick_o (s_bit_tick), - .rx_i (uart.rx_i), - .tx_o (s_tx) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .baud_int_i (s_baud_int), + .baud_frac_i (s_baud_frac), + .data_bits_i (s_data_bits), + .stop2_i (s_stop2), + .parity_i (s_parity), + .tx_enable_i (s_tx_enable), + .rx_enable_i (s_rx_enable), + .loopback_i (s_loopback), + .tx_start_allowed_i(s_tx_start_allowed), + .break_i (s_break), + .tx_data_valid_i (s_tx_data_valid), + .tx_data_i (s_tx_data), + .tx_data_pop_o (s_tx_data_pop), + .tx_busy_o (s_tx_busy), + .tx_done_o (s_tx_done), + .rx_active_o (s_rx_active), + .rx_data_valid_o (s_rx_data_valid), + .rx_data_o (s_rx_data), + .bit_tick_o (s_bit_tick), + .rx_i (uart.rx_i), + .tx_o (s_tx) + ); + + uart_flow_ctrl u_uart_flow_ctrl ( + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .cts_n_async_i (uart.cts_n_i), + .auto_cts_enable_i (s_auto_cts_enable), + .auto_rts_enable_i (s_auto_rts_enable), + .tx_enable_i (s_tx_enable), + .rx_enable_i (s_rx_enable), + .loopback_i (s_loopback), + .tx_data_valid_i (s_tx_data_valid), + .tx_busy_i (s_tx_busy), + .rx_level_i (s_rx_level), + .rts_assert_level_i (s_rts_assert_level), + .rts_deassert_level_i(s_rts_deassert_level), + .cts_asserted_o (s_cts_asserted), + .rts_asserted_o (s_rts_asserted), + .tx_start_allowed_o (s_tx_start_allowed), + .tx_flow_blocked_o (s_tx_flow_blocked), + .cts_change_o (s_cts_change), + .rts_n_o (uart.rts_n_o) ); endmodule diff --git a/rtl/ip/ribp/serial/ribp_uart_define.svh b/rtl/ip/ribp/serial/ribp_uart_define.svh index 3bb11643..a1bd672b 100644 --- a/rtl/ip/ribp/serial/ribp_uart_define.svh +++ b/rtl/ip/ribp/serial/ribp_uart_define.svh @@ -23,6 +23,8 @@ `define RIBP_UART_INTR_STATUS 8'h3C `define RIBP_UART_INTR_TEST 8'h40 `define RIBP_UART_DMA_CTRL 8'h44 +`define RIBP_UART_FLOW_CTRL 8'h48 +`define RIBP_UART_RTS_WATERMARK 8'h4C `define RIBP_UART_IP_VERSION 8'hF8 `define RIBP_UART_CAPABILITY 8'hFC @@ -50,6 +52,9 @@ `define UART_STATUS_BREAK_ACTIVE 9 `define UART_STATUS_TX_DMA_REQ 10 `define UART_STATUS_RX_DMA_REQ 11 +`define UART_STATUS_CTS_ASSERTED 12 +`define UART_STATUS_RTS_ASSERTED 13 +`define UART_STATUS_TX_FLOW_BLOCKED 14 `define UART_RXDATA_PARITY_ERROR 8 `define UART_RXDATA_FRAME_ERROR 9 @@ -70,9 +75,16 @@ `define UART_INTR_TX_DONE 3 `define UART_INTR_RX_ERROR 4 `define UART_INTR_BREAK 5 +`define UART_INTR_CTS_CHANGE 6 `define UART_DMA_TX_ENABLE 0 `define UART_DMA_RX_ENABLE 1 + +`define UART_FLOW_AUTO_CTS_ENABLE 0 +`define UART_FLOW_AUTO_RTS_ENABLE 1 + +`define UART_RTS_ASSERT_LEVEL 0 +`define UART_RTS_DEASSERT_LEVEL 16 // verilog_format: on `endif diff --git a/rtl/ip/ribp/serial/ribp_uart_tx.sv b/rtl/ip/ribp/serial/ribp_uart_tx.sv index 57d2a469..90594cac 100644 --- a/rtl/ip/ribp/serial/ribp_uart_tx.sv +++ b/rtl/ip/ribp/serial/ribp_uart_tx.sv @@ -7,6 +7,7 @@ module ribp_uart_tx ( input logic rst_n_i, input logic sample_tick_i, input logic enable_i, + input logic start_allowed_i, input logic break_i, input logic [1:0] data_bits_i, input logic stop2_i, @@ -68,7 +69,8 @@ module ribp_uart_tx ( data_pop_o = 1'b0; done_o = 1'b0; - if ((s_state_q == STATE_IDLE) && enable_i && !break_i && data_valid_i && sample_tick_i) begin + if ((s_state_q == STATE_IDLE) && enable_i && start_allowed_i && !break_i && data_valid_i && + sample_tick_i) begin s_state_d = STATE_START; s_sample_d = '0; s_bit_d = '0; diff --git a/rtl/ip/ribp/serial/uart_core.sv b/rtl/ip/ribp/serial/uart_core.sv index 7f9e1c97..f926f7de 100644 --- a/rtl/ip/ribp/serial/uart_core.sv +++ b/rtl/ip/ribp/serial/uart_core.sv @@ -13,6 +13,7 @@ module uart_core ( input logic tx_enable_i, input logic rx_enable_i, input logic loopback_i, + input logic tx_start_allowed_i, input logic break_i, input logic tx_data_valid_i, input logic [ 7:0] tx_data_i, @@ -46,20 +47,21 @@ module uart_core ( ); ribp_uart_tx u_ribp_uart_tx ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .sample_tick_i(s_sample_tick), - .enable_i (tx_enable_i), - .break_i (break_i), - .data_bits_i (data_bits_i), - .stop2_i (stop2_i), - .parity_i (parity_i), - .data_valid_i (tx_data_valid_i), - .data_i (tx_data_i), - .data_pop_o (tx_data_pop_o), - .busy_o (tx_busy_o), - .done_o (tx_done_o), - .tx_o (s_tx) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .sample_tick_i (s_sample_tick), + .enable_i (tx_enable_i), + .start_allowed_i(tx_start_allowed_i), + .break_i (break_i), + .data_bits_i (data_bits_i), + .stop2_i (stop2_i), + .parity_i (parity_i), + .data_valid_i (tx_data_valid_i), + .data_i (tx_data_i), + .data_pop_o (tx_data_pop_o), + .busy_o (tx_busy_o), + .done_o (tx_done_o), + .tx_o (s_tx) ); ribp_uart_rx u_ribp_uart_rx ( diff --git a/rtl/ip/ribp/serial/uart_flow_ctrl.sv b/rtl/ip/ribp/serial/uart_flow_ctrl.sv new file mode 100644 index 00000000..b07a1fbc --- /dev/null +++ b/rtl/ip/ribp/serial/uart_flow_ctrl.sv @@ -0,0 +1,70 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +module uart_flow_ctrl ( + // verilog_format: off + input logic clk_i, + input logic rst_n_i, + input logic cts_n_async_i, + input logic auto_cts_enable_i, + input logic auto_rts_enable_i, + input logic tx_enable_i, + input logic rx_enable_i, + input logic loopback_i, + input logic tx_data_valid_i, + input logic tx_busy_i, + input logic [6:0] rx_level_i, + input logic [6:0] rts_assert_level_i, + input logic [6:0] rts_deassert_level_i, + output logic cts_asserted_o, + output logic rts_asserted_o, + output logic tx_start_allowed_o, + output logic tx_flow_blocked_o, + output logic cts_change_o, + output logic rts_n_o + // verilog_format: on +); + + logic s_cts_rise; + logic s_cts_fall; + logic s_rts_asserted_d, s_rts_asserted_q; + + // Synchronize the logical asserted state so reset is fail-safe: not clear to send. + edge_det #( + .STAGE (2), + .DATA_WIDTH(1) + ) u_cts_edge_det ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (~cts_n_async_i), + .dat_o (cts_asserted_o), + .re_o (s_cts_rise), + .fe_o (s_cts_fall) + ); + + always_comb begin + s_rts_asserted_d = s_rts_asserted_q; + if (!auto_rts_enable_i || !rx_enable_i) begin + s_rts_asserted_d = 1'b0; + end else if (rx_level_i >= rts_deassert_level_i) begin + s_rts_asserted_d = 1'b0; + end else if (rx_level_i <= rts_assert_level_i) begin + s_rts_asserted_d = 1'b1; + end + end + + dffr #(1) u_rts_asserted_dffr ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .dat_i (s_rts_asserted_d), + .dat_o (s_rts_asserted_q) + ); + + assign rts_asserted_o = auto_rts_enable_i && rx_enable_i && s_rts_asserted_q; + assign rts_n_o = ~rts_asserted_o; + assign tx_start_allowed_o = loopback_i || !auto_cts_enable_i || cts_asserted_o; + assign tx_flow_blocked_o = auto_cts_enable_i && tx_enable_i && tx_data_valid_i && + !tx_busy_i && !cts_asserted_o && !loopback_i; + assign cts_change_o = auto_cts_enable_i && (s_cts_rise || s_cts_fall); + +endmodule diff --git a/rtl/ip/ribp/serial/uart_if.sv b/rtl/ip/ribp/serial/uart_if.sv new file mode 100644 index 00000000..b57aa9b9 --- /dev/null +++ b/rtl/ip/ribp/serial/uart_if.sv @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +interface uart_if (); + logic rx_i; + logic cts_n_i; + logic tx_o; + logic rts_n_o; + logic irq_o; + + modport dut(input rx_i, input cts_n_i, output tx_o, output rts_n_o, output irq_o); + + modport tb(output rx_i, output cts_n_i, input tx_o, input rts_n_o, input irq_o); +endinterface diff --git a/rtl/ip/ribp/serial/uart_reg.sv b/rtl/ip/ribp/serial/uart_reg.sv index 2043f748..4f82f3db 100644 --- a/rtl/ip/ribp/serial/uart_reg.sv +++ b/rtl/ip/ribp/serial/uart_reg.sv @@ -22,8 +22,13 @@ module uart_reg #( output logic rx_enable_o, output logic loopback_o, output logic break_o, + output logic auto_cts_enable_o, + output logic auto_rts_enable_o, + output logic [ 6:0] rts_assert_level_o, + output logic [ 6:0] rts_deassert_level_o, output logic tx_data_valid_o, output logic [ 7:0] tx_data_o, + output logic [ 6:0] rx_level_o, input logic tx_data_pop_i, input logic tx_busy_i, input logic tx_done_i, @@ -31,6 +36,10 @@ module uart_reg #( input logic rx_data_valid_i, input logic [11:0] rx_data_i, input logic bit_tick_i, + input logic cts_asserted_i, + input logic rts_asserted_i, + input logic tx_flow_blocked_i, + input logic cts_change_i, output logic dma_tx_stall_o, output logic dma_rx_stall_o, output logic irq_o @@ -41,8 +50,10 @@ module uart_reg #( localparam logic [31:0] RX_WATERMARK_RESET = 32'd32; localparam logic [31:0] RX_TIMEOUT_RESET = 32'd32; localparam logic [31:0] LINE_CTRL_RESET = 32'd3; - localparam logic [31:0] IP_VERSION = 32'h0002_0000; - localparam logic [31:0] CAPABILITY = 32'h00FF_4040; + localparam logic [31:0] RTS_ASSERT_LEVEL_RESET = 32'd32; + localparam logic [31:0] RTS_DEASSERT_LEVEL_RESET = 32'd48; + localparam logic [31:0] IP_VERSION = 32'h0003_0000; + localparam logic [31:0] CAPABILITY = 32'h03FF_4040; logic s_req; logic s_write; @@ -67,9 +78,14 @@ module uart_reg #( logic s_rx_timeout_en; logic [15:0] s_rx_timeout_d, s_rx_timeout_q; logic s_intr_enable_en; - logic [5:0] s_intr_enable_d, s_intr_enable_q; + logic [6:0] s_intr_enable_d, s_intr_enable_q; logic s_dma_ctrl_en; logic [1:0] s_dma_ctrl_d, s_dma_ctrl_q; + logic s_flow_ctrl_en; + logic [1:0] s_flow_ctrl_d, s_flow_ctrl_q; + logic s_rts_watermark_en; + logic [6:0] s_rts_assert_level_d, s_rts_assert_level_q; + logic [6:0] s_rts_deassert_level_d, s_rts_deassert_level_q; logic s_tx_flush; logic s_tx_push; @@ -86,10 +102,10 @@ module uart_reg #( logic [RX_FIFO_LOG_DEPTH:0] s_rx_count; logic [6:0] s_error_status_d, s_error_status_q; - logic [5:0] s_intr_state_d, s_intr_state_q; + logic [6:0] s_intr_state_d, s_intr_state_q; logic [6:0] s_error_clear; - logic [5:0] s_intr_clear; - logic [5:0] s_intr_test; + logic [6:0] s_intr_clear; + logic [6:0] s_intr_test; logic s_config_error_event; logic s_command_error_event; logic s_overrun_event; @@ -123,7 +139,7 @@ module uart_reg #( initial begin if ((TX_FIFO_DEPTH != 64) || (RX_FIFO_DEPTH != 64)) begin - $fatal(1, "uart_reg: UART V2 requires 64-entry FIFOs"); + $fatal(1, "uart_reg: UART V3 requires 64-entry FIFOs"); end end @@ -142,8 +158,13 @@ module uart_reg #( assign rx_enable_o = s_ctrl_q[`UART_CTRL_RX_ENABLE]; assign loopback_o = s_ctrl_q[`UART_CTRL_LOOPBACK]; assign break_o = s_ctrl_q[`UART_CTRL_TX_BREAK]; + assign auto_cts_enable_o = s_flow_ctrl_q[`UART_FLOW_AUTO_CTS_ENABLE]; + assign auto_rts_enable_o = s_flow_ctrl_q[`UART_FLOW_AUTO_RTS_ENABLE]; + assign rts_assert_level_o = s_rts_assert_level_q; + assign rts_deassert_level_o = s_rts_deassert_level_q; assign tx_data_valid_o = !s_tx_empty; assign tx_data_o = s_tx_pop_data; + assign rx_level_o = 7'(s_rx_count); assign s_config_valid = (s_baud_int_q >= 24'd16) && (s_line_ctrl_q[4:3] != 2'd3); assign s_tx_dma_req = s_dma_ctrl_q[`UART_DMA_TX_ENABLE] && tx_enable_o && @@ -156,56 +177,64 @@ module uart_reg #( assign irq_o = |(s_intr_state_q & s_intr_enable_q); always_comb begin - s_status = '0; - s_status[`UART_STATUS_TX_ENABLED] = tx_enable_o; - s_status[`UART_STATUS_RX_ENABLED] = rx_enable_o; - s_status[`UART_STATUS_TX_BUSY] = tx_busy_i; - s_status[`UART_STATUS_RX_ACTIVE] = rx_active_i; - s_status[`UART_STATUS_TX_EMPTY] = s_tx_empty; - s_status[`UART_STATUS_TX_FULL] = s_tx_full; - s_status[`UART_STATUS_RX_EMPTY] = s_rx_empty; - s_status[`UART_STATUS_RX_FULL] = s_rx_full; - s_status[`UART_STATUS_CONFIG_VALID] = s_config_valid; - s_status[`UART_STATUS_BREAK_ACTIVE] = break_o; - s_status[`UART_STATUS_TX_DMA_REQ] = s_tx_dma_req; - s_status[`UART_STATUS_RX_DMA_REQ] = s_rx_dma_req; - s_fifo_level = '0; - s_fifo_level[6:0] = 7'(s_tx_count); - s_fifo_level[22:16] = 7'(s_rx_count); + s_status = '0; + s_status[`UART_STATUS_TX_ENABLED] = tx_enable_o; + s_status[`UART_STATUS_RX_ENABLED] = rx_enable_o; + s_status[`UART_STATUS_TX_BUSY] = tx_busy_i; + s_status[`UART_STATUS_RX_ACTIVE] = rx_active_i; + s_status[`UART_STATUS_TX_EMPTY] = s_tx_empty; + s_status[`UART_STATUS_TX_FULL] = s_tx_full; + s_status[`UART_STATUS_RX_EMPTY] = s_rx_empty; + s_status[`UART_STATUS_RX_FULL] = s_rx_full; + s_status[`UART_STATUS_CONFIG_VALID] = s_config_valid; + s_status[`UART_STATUS_BREAK_ACTIVE] = break_o; + s_status[`UART_STATUS_TX_DMA_REQ] = s_tx_dma_req; + s_status[`UART_STATUS_RX_DMA_REQ] = s_rx_dma_req; + s_status[`UART_STATUS_CTS_ASSERTED] = cts_asserted_i; + s_status[`UART_STATUS_RTS_ASSERTED] = rts_asserted_i; + s_status[`UART_STATUS_TX_FLOW_BLOCKED] = tx_flow_blocked_i; + s_fifo_level = '0; + s_fifo_level[6:0] = 7'(s_tx_count); + s_fifo_level[22:16] = 7'(s_rx_count); end always_comb begin - s_req_accept = s_req; - s_access_error = 1'b0; - s_baud_int_en = 1'b0; - s_baud_frac_en = 1'b0; - s_line_ctrl_en = 1'b0; - s_ctrl_en = 1'b0; - s_tx_watermark_en = 1'b0; - s_rx_watermark_en = 1'b0; - s_rx_timeout_en = 1'b0; - s_intr_enable_en = 1'b0; - s_dma_ctrl_en = 1'b0; - s_baud_int_d = s_baud_int_q; - s_baud_frac_d = s_baud_frac_q; - s_line_ctrl_d = s_line_ctrl_q; - s_ctrl_d = s_ctrl_q; - s_tx_watermark_d = s_tx_watermark_q; - s_rx_watermark_d = s_rx_watermark_q; - s_rx_timeout_d = s_rx_timeout_q; - s_intr_enable_d = s_intr_enable_q; - s_dma_ctrl_d = s_dma_ctrl_q; - s_tx_flush = 1'b0; - s_rx_flush = 1'b0; - s_tx_push = 1'b0; - s_rx_pop = 1'b0; - s_error_clear = '0; - s_intr_clear = '0; - s_intr_test = '0; - s_config_error_event = 1'b0; - s_command_error_event = 1'b0; - s_ribp_rdata_d = '0; - s_merge_value = '0; + s_req_accept = s_req; + s_access_error = 1'b0; + s_baud_int_en = 1'b0; + s_baud_frac_en = 1'b0; + s_line_ctrl_en = 1'b0; + s_ctrl_en = 1'b0; + s_tx_watermark_en = 1'b0; + s_rx_watermark_en = 1'b0; + s_rx_timeout_en = 1'b0; + s_intr_enable_en = 1'b0; + s_dma_ctrl_en = 1'b0; + s_flow_ctrl_en = 1'b0; + s_rts_watermark_en = 1'b0; + s_baud_int_d = s_baud_int_q; + s_baud_frac_d = s_baud_frac_q; + s_line_ctrl_d = s_line_ctrl_q; + s_ctrl_d = s_ctrl_q; + s_tx_watermark_d = s_tx_watermark_q; + s_rx_watermark_d = s_rx_watermark_q; + s_rx_timeout_d = s_rx_timeout_q; + s_intr_enable_d = s_intr_enable_q; + s_dma_ctrl_d = s_dma_ctrl_q; + s_flow_ctrl_d = s_flow_ctrl_q; + s_rts_assert_level_d = s_rts_assert_level_q; + s_rts_deassert_level_d = s_rts_deassert_level_q; + s_tx_flush = 1'b0; + s_rx_flush = 1'b0; + s_tx_push = 1'b0; + s_rx_pop = 1'b0; + s_error_clear = '0; + s_intr_clear = '0; + s_intr_test = '0; + s_config_error_event = 1'b0; + s_command_error_event = 1'b0; + s_ribp_rdata_d = '0; + s_merge_value = '0; if (s_req) begin if ((ribp.addr[11:8] != 4'd0) || (ribp.addr[1:0] != 2'b00)) begin @@ -325,26 +354,26 @@ module uart_reg #( end end `RIBP_UART_INTR_STATE: begin - if (!ribp.wstrb[0] || (ribp.wdata[31:6] != 26'd0)) begin + if (!ribp.wstrb[0] || (ribp.wdata[31:7] != 25'd0)) begin s_access_error = 1'b1; end else begin - s_intr_clear = ribp.wdata[5:0]; + s_intr_clear = ribp.wdata[6:0]; end end `RIBP_UART_INTR_ENABLE: begin - s_merge_value = merge_wstrb({26'd0, s_intr_enable_q}, ribp.wdata, ribp.wstrb); - if (s_merge_value[31:6] != 26'd0) begin + s_merge_value = merge_wstrb({25'd0, s_intr_enable_q}, ribp.wdata, ribp.wstrb); + if (s_merge_value[31:7] != 25'd0) begin s_access_error = 1'b1; end else begin s_intr_enable_en = 1'b1; - s_intr_enable_d = s_merge_value[5:0]; + s_intr_enable_d = s_merge_value[6:0]; end end `RIBP_UART_INTR_TEST: begin - if (!ribp.wstrb[0] || (ribp.wdata[31:6] != 26'd0)) begin + if (!ribp.wstrb[0] || (ribp.wdata[31:7] != 25'd0)) begin s_access_error = 1'b1; end else begin - s_intr_test = ribp.wdata[5:0]; + s_intr_test = ribp.wdata[6:0]; end end `RIBP_UART_DMA_CTRL: begin @@ -357,14 +386,40 @@ module uart_reg #( s_dma_ctrl_d = s_merge_value[1:0]; end end + `RIBP_UART_FLOW_CTRL: begin + s_merge_value = merge_wstrb({30'd0, s_flow_ctrl_q}, ribp.wdata, ribp.wstrb); + if (tx_enable_o || rx_enable_o || tx_busy_i || rx_active_i || + (s_merge_value[31:2] != 30'd0)) begin + s_access_error = 1'b1; + s_config_error_event = 1'b1; + end else begin + s_flow_ctrl_en = 1'b1; + s_flow_ctrl_d = s_merge_value[1:0]; + end + end + `RIBP_UART_RTS_WATERMARK: begin + s_merge_value = merge_wstrb({9'd0, s_rts_deassert_level_q, 9'd0, s_rts_assert_level_q}, + ribp.wdata, ribp.wstrb); + if (tx_enable_o || rx_enable_o || tx_busy_i || rx_active_i || + (s_merge_value[31:23] != 9'd0) || (s_merge_value[15:7] != 9'd0) || + (s_merge_value[22:16] > 7'(RX_FIFO_DEPTH)) || + (s_merge_value[6:0] >= s_merge_value[22:16])) begin + s_access_error = 1'b1; + s_config_error_event = 1'b1; + end else begin + s_rts_watermark_en = 1'b1; + s_rts_assert_level_d = s_merge_value[6:0]; + s_rts_deassert_level_d = s_merge_value[22:16]; + end + end default: s_access_error = 1'b1; endcase end else begin unique case (ribp.addr[7:0]) - `RIBP_UART_BAUD_INT: s_ribp_rdata_d = {8'd0, s_baud_int_q}; - `RIBP_UART_BAUD_FRAC: s_ribp_rdata_d = {24'd0, s_baud_frac_q}; - `RIBP_UART_LINE_CTRL: s_ribp_rdata_d = {27'd0, s_line_ctrl_q}; - `RIBP_UART_CTRL: s_ribp_rdata_d = {28'd0, s_ctrl_q}; + `RIBP_UART_BAUD_INT: s_ribp_rdata_d = {8'd0, s_baud_int_q}; + `RIBP_UART_BAUD_FRAC: s_ribp_rdata_d = {24'd0, s_baud_frac_q}; + `RIBP_UART_LINE_CTRL: s_ribp_rdata_d = {27'd0, s_line_ctrl_q}; + `RIBP_UART_CTRL: s_ribp_rdata_d = {28'd0, s_ctrl_q}; `RIBP_UART_RXDATA: begin if (s_rx_empty) begin s_access_error = 1'b1; @@ -374,18 +429,21 @@ module uart_reg #( s_rx_pop = 1'b1; end end - `RIBP_UART_STATUS: s_ribp_rdata_d = s_status; - `RIBP_UART_FIFO_LEVEL: s_ribp_rdata_d = s_fifo_level; - `RIBP_UART_TX_WATERMARK: s_ribp_rdata_d = {25'd0, s_tx_watermark_q}; - `RIBP_UART_RX_WATERMARK: s_ribp_rdata_d = {25'd0, s_rx_watermark_q}; + `RIBP_UART_STATUS: s_ribp_rdata_d = s_status; + `RIBP_UART_FIFO_LEVEL: s_ribp_rdata_d = s_fifo_level; + `RIBP_UART_TX_WATERMARK: s_ribp_rdata_d = {25'd0, s_tx_watermark_q}; + `RIBP_UART_RX_WATERMARK: s_ribp_rdata_d = {25'd0, s_rx_watermark_q}; `RIBP_UART_RX_TIMEOUT_BITS: s_ribp_rdata_d = {16'd0, s_rx_timeout_q}; - `RIBP_UART_ERROR_STATUS: s_ribp_rdata_d = {25'd0, s_error_status_q}; - `RIBP_UART_INTR_STATE: s_ribp_rdata_d = {26'd0, s_intr_state_q}; - `RIBP_UART_INTR_ENABLE: s_ribp_rdata_d = {26'd0, s_intr_enable_q}; - `RIBP_UART_INTR_STATUS: s_ribp_rdata_d = {26'd0, (s_intr_state_q & s_intr_enable_q)}; - `RIBP_UART_DMA_CTRL: s_ribp_rdata_d = {30'd0, s_dma_ctrl_q}; - `RIBP_UART_IP_VERSION: s_ribp_rdata_d = IP_VERSION; - `RIBP_UART_CAPABILITY: s_ribp_rdata_d = CAPABILITY; + `RIBP_UART_ERROR_STATUS: s_ribp_rdata_d = {25'd0, s_error_status_q}; + `RIBP_UART_INTR_STATE: s_ribp_rdata_d = {25'd0, s_intr_state_q}; + `RIBP_UART_INTR_ENABLE: s_ribp_rdata_d = {25'd0, s_intr_enable_q}; + `RIBP_UART_INTR_STATUS: s_ribp_rdata_d = {25'd0, (s_intr_state_q & s_intr_enable_q)}; + `RIBP_UART_DMA_CTRL: s_ribp_rdata_d = {30'd0, s_dma_ctrl_q}; + `RIBP_UART_FLOW_CTRL: s_ribp_rdata_d = {30'd0, s_flow_ctrl_q}; + `RIBP_UART_RTS_WATERMARK: + s_ribp_rdata_d = {9'd0, s_rts_deassert_level_q, 9'd0, s_rts_assert_level_q}; + `RIBP_UART_IP_VERSION: s_ribp_rdata_d = IP_VERSION; + `RIBP_UART_CAPABILITY: s_ribp_rdata_d = CAPABILITY; default: begin s_access_error = 1'b1; s_ribp_rdata_d = '0; @@ -483,7 +541,7 @@ module uart_reg #( .dat_i (s_rx_timeout_d), .dat_o (s_rx_timeout_q) ); - dffer #(6) u_intr_enable_dffer ( + dffer #(7) u_intr_enable_dffer ( .clk_i (clk_i), .rst_n_i(rst_n_i), .en_i (s_intr_enable_en), @@ -497,6 +555,33 @@ module uart_reg #( .dat_i (s_dma_ctrl_d), .dat_o (s_dma_ctrl_q) ); + dffer #(2) u_flow_ctrl_dffer ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .en_i (s_flow_ctrl_en), + .dat_i (s_flow_ctrl_d), + .dat_o (s_flow_ctrl_q) + ); + dfferc #( + .DATA_WIDTH(7), + .RESET_VAL (7'(RTS_ASSERT_LEVEL_RESET)) + ) u_rts_assert_level_dfferc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .en_i (s_rts_watermark_en), + .dat_i (s_rts_assert_level_d), + .dat_o (s_rts_assert_level_q) + ); + dfferc #( + .DATA_WIDTH(7), + .RESET_VAL (7'(RTS_DEASSERT_LEVEL_RESET)) + ) u_rts_deassert_level_dfferc ( + .clk_i (clk_i), + .rst_n_i(rst_n_i), + .en_i (s_rts_watermark_en), + .dat_i (s_rts_deassert_level_d), + .dat_o (s_rts_deassert_level_q) + ); fifo #( .DATA_WIDTH (8), @@ -580,8 +665,9 @@ module uart_reg #( if (tx_done_i && s_tx_empty) s_intr_state_d[`UART_INTR_TX_DONE] = 1'b1; if (s_rx_error_event || s_overrun_event) s_intr_state_d[`UART_INTR_RX_ERROR] = 1'b1; if (s_rx_push && rx_data_i[`UART_RXDATA_BREAK]) s_intr_state_d[`UART_INTR_BREAK] = 1'b1; + if (cts_change_i) s_intr_state_d[`UART_INTR_CTS_CHANGE] = 1'b1; end - dffr #(6) u_intr_state_dffr ( + dffr #(7) u_intr_state_dffr ( .clk_i (clk_i), .rst_n_i(rst_n_i), .dat_i (s_intr_state_d), diff --git a/rtl/mini/dv/model/uart_model.sv b/rtl/mini/dv/model/uart_model.sv new file mode 100644 index 00000000..5244d139 --- /dev/null +++ b/rtl/mini/dv/model/uart_model.sv @@ -0,0 +1,71 @@ +// Copyright (c) 2026 Yuchi Miao +// retroSoC is licensed under Mulan PSL v2. + +`timescale 1ns / 1ps + +module uart_model #( + parameter int BAUD_RATE = 115200, + parameter bit LOOPBACK = 1'b0, + parameter bit FLOW_CONTROL = 1'b0 +) ( + // verilog_format: off + input logic uart_tx_i, + input logic uart_rts_n_i, + output logic uart_rx_o, + output logic uart_cts_n_o + // verilog_format: on +); + + localparam time BIT_PERIOD_NS = 1_000_000_000 / BAUD_RATE; + + logic [7:0] s_data; + + initial begin + uart_rx_o = 1'b1; + uart_cts_n_o = FLOW_CONTROL ? 1'b0 : 1'bz; + end + + always @(negedge uart_tx_i) begin + receive(s_data); + $write("%c", s_data); + if (LOOPBACK) begin + send(s_data); + end + end + + task automatic receive(output logic [7:0] value); + begin + value = '0; + #(BIT_PERIOD_NS + (BIT_PERIOD_NS / 2)); + for (int bit_index = 0; bit_index < 8; bit_index++) begin + value[bit_index] = uart_tx_i; + #(BIT_PERIOD_NS); + end + end + endtask + + task automatic send(input logic [7:0] value); + begin + if (FLOW_CONTROL) begin + wait (uart_rts_n_i === 1'b0); + end + uart_rx_o = 1'b0; + #(BIT_PERIOD_NS); + for (int bit_index = 0; bit_index < 8; bit_index++) begin + uart_rx_o = value[bit_index]; + #(BIT_PERIOD_NS); + end + uart_rx_o = 1'b1; + #(BIT_PERIOD_NS); + end + endtask + + task automatic set_cts(input logic asserted); + begin + if (FLOW_CONTROL) begin + uart_cts_n_o = ~asserted; + end + end + endtask + +endmodule diff --git a/rtl/mini/dv/tb/retrosoc_tb.sv b/rtl/mini/dv/tb/retrosoc_tb.sv index cbda6f05..fd6fd5b6 100644 --- a/rtl/mini/dv/tb/retrosoc_tb.sv +++ b/rtl/mini/dv/tb/retrosoc_tb.sv @@ -33,8 +33,7 @@ module retrosoc_tb; wire s_jtag_tdi; wire s_jtag_trst_n; wire s_uart0_tx; - // for handle x-prop issue - wire s_uart0_rx = 1'b1; + wire s_uart0_rx; tri1 s_gpio_0_io; tri1 s_gpio_1_io; wire s_psram_sck; @@ -135,11 +134,15 @@ module retrosoc_tb; ); - rs232 #( - .BAUD_RATE(921600) - ) u_rs232_0 ( - .rs232_rx_i(s_uart0_tx), - .rs232_tx_o() + uart_model #( + .BAUD_RATE (921600), + .LOOPBACK (1'b0), + .FLOW_CONTROL(1'b0) + ) u_uart_model_0 ( + .uart_tx_i (s_uart0_tx), + .uart_rts_n_i(s_gpio_1_io), + .uart_rx_o (s_uart0_rx), + .uart_cts_n_o(s_gpio_0_io) ); diff --git a/rtl/mini/filelist/clusterip.fl b/rtl/mini/filelist/clusterip.fl index 9e0775f3..e081f1f6 100644 --- a/rtl/mini/filelist/clusterip.fl +++ b/rtl/mini/filelist/clusterip.fl @@ -4,7 +4,6 @@ /clusterip/rng/rtl/rng_reg.sv /clusterip/rng/rtl/rng_deterministic_source.sv /clusterip/rng/rtl/apb4_rng.sv -/clusterip/uart/rtl/uart_if.sv /clusterip/pwm/rtl/pwm_pkg.sv /clusterip/pwm/rtl/pwm_tickgen.sv /clusterip/pwm/rtl/pwm_timer.sv diff --git a/rtl/mini/filelist/inc.fl b/rtl/mini/filelist/inc.fl index 7ae4b7ec..8ae1163d 100644 --- a/rtl/mini/filelist/inc.fl +++ b/rtl/mini/filelist/inc.fl @@ -7,7 +7,6 @@ +incdir+/clusterip/common/rtl/utils +incdir+/clusterip/archinfo/rtl +incdir+/clusterip/rng/rtl -+incdir+/clusterip/uart/rtl +incdir+/clusterip/pwm/rtl +incdir+/clusterip/ps2/rtl +incdir+/clusterip/i2c/rtl diff --git a/rtl/mini/filelist/ip.fl b/rtl/mini/filelist/ip.fl index f2110855..1603c049 100644 --- a/rtl/mini/filelist/ip.fl +++ b/rtl/mini/filelist/ip.fl @@ -25,9 +25,11 @@ /ip/ribp/serial/ws2812_reg.sv /ip/ribp/serial/ws2812_core.sv /ip/ribp/serial/ribp_ws2812.sv +/ip/ribp/serial/uart_if.sv /ip/ribp/serial/uart_baudgen.sv /ip/ribp/serial/ribp_uart_tx.sv /ip/ribp/serial/ribp_uart_rx.sv +/ip/ribp/serial/uart_flow_ctrl.sv /ip/ribp/serial/uart_core.sv /ip/ribp/serial/uart_reg.sv /ip/ribp/serial/ribp_uart.sv diff --git a/rtl/mini/filelist/tb.fl b/rtl/mini/filelist/tb.fl index 07733b13..6d275d3f 100644 --- a/rtl/mini/filelist/tb.fl +++ b/rtl/mini/filelist/tb.fl @@ -1,6 +1,6 @@ /clusterip/ps2/dv/model/ps2_device_model.sv /clusterip/i2c/model/at24cxxx.sv -/clusterip/uart/model/rs232.sv +/model/uart_model.sv /clusterip/i2s/model/mic.sv /clusterip/spi/model/w25q128jvxim.sv /model/ESP_PSRAM64H.sv diff --git a/rtl/mini/formal/generate_formal_filelist.py b/rtl/mini/formal/generate_formal_filelist.py index 63f8bca2..c67692a7 100644 --- a/rtl/mini/formal/generate_formal_filelist.py +++ b/rtl/mini/formal/generate_formal_filelist.py @@ -111,12 +111,15 @@ def source_files(target: str) -> list[Path]: if target == "uart": return [ COMMON_RTL / "interface/ribp_if.sv", + COMMON_RTL / "cdc/cdc_sync.sv", COMMON_RTL / "utils/register.sv", + COMMON_RTL / "utils/edge_det.sv", COMMON_RTL / "utils/fifo.sv", - ROOT / "rtl/managed/clusterip/uart/rtl/uart_if.sv", + SERIAL / "uart_if.sv", SERIAL / "uart_baudgen.sv", SERIAL / "ribp_uart_tx.sv", SERIAL / "ribp_uart_rx.sv", + SERIAL / "uart_flow_ctrl.sv", SERIAL / "uart_core.sv", SERIAL / "uart_reg.sv", SERIAL / "ribp_uart.sv", diff --git a/rtl/mini/formal/uart_formal.sv b/rtl/mini/formal/uart_formal.sv index 251c36b1..f1c3af1f 100644 --- a/rtl/mini/formal/uart_formal.sv +++ b/rtl/mini/formal/uart_formal.sv @@ -15,16 +15,24 @@ module uart_formal_design ( output logic [ 6:0] tx_count, output logic [ 6:0] rx_count, output logic [ 6:0] error_status, - output logic [ 5:0] intr_state, - output logic [ 5:0] intr_enable, + output logic [ 6:0] intr_state, + output logic [ 6:0] intr_enable, output logic tx_enable, output logic rx_enable, output logic tx_busy, output logic rx_active, output logic break_active, + output logic auto_cts_enable, + output logic auto_rts_enable, + output logic cts_asserted, + output logic rts_asserted, + output logic tx_start_allowed, + output logic tx_flow_blocked, + output logic tx_data_pop, output logic tx_dma_stall, output logic rx_dma_stall, output logic tx, + output logic rts_n, output logic irq // verilog_format: on ); @@ -37,31 +45,41 @@ module uart_formal_design ( (* anyseq *)logic [31:0] f_rib_wdata; (* anyseq *)logic [ 3:0] f_rib_wstrb; (* anyseq *)logic f_uart_rx; + (* anyseq *)logic f_uart_cts_n; - assign ribp.valid = f_rib_valid; - assign ribp.addr = f_rib_addr; - assign ribp.wdata = f_rib_wdata; - assign ribp.wstrb = f_rib_wstrb; - assign uart.rx_i = f_uart_rx; + assign ribp.valid = f_rib_valid; + assign ribp.addr = f_rib_addr; + assign ribp.wdata = f_rib_wdata; + assign ribp.wstrb = f_rib_wstrb; + assign uart.rx_i = f_uart_rx; + assign uart.cts_n_i = f_uart_cts_n; - assign rib_valid = ribp.valid; - assign rib_addr = ribp.addr; - assign rib_wdata = ribp.wdata; - assign rib_wstrb = ribp.wstrb; - assign rib_ready = ribp.ready; - assign rib_resp_err = ribp.resp_err; - assign tx_count = u_dut.u_uart_reg.s_tx_count; - assign rx_count = u_dut.u_uart_reg.s_rx_count; - assign error_status = u_dut.u_uart_reg.s_error_status_q; - assign intr_state = u_dut.u_uart_reg.s_intr_state_q; - assign intr_enable = u_dut.u_uart_reg.s_intr_enable_q; - assign tx_enable = u_dut.s_tx_enable; - assign rx_enable = u_dut.s_rx_enable; - assign tx_busy = u_dut.s_tx_busy; - assign rx_active = u_dut.s_rx_active; - assign break_active = u_dut.s_break; - assign tx = uart.tx_o; - assign irq = uart.irq_o; + assign rib_valid = ribp.valid; + assign rib_addr = ribp.addr; + assign rib_wdata = ribp.wdata; + assign rib_wstrb = ribp.wstrb; + assign rib_ready = ribp.ready; + assign rib_resp_err = ribp.resp_err; + assign tx_count = u_dut.u_uart_reg.s_tx_count; + assign rx_count = u_dut.u_uart_reg.s_rx_count; + assign error_status = u_dut.u_uart_reg.s_error_status_q; + assign intr_state = u_dut.u_uart_reg.s_intr_state_q; + assign intr_enable = u_dut.u_uart_reg.s_intr_enable_q; + assign tx_enable = u_dut.s_tx_enable; + assign rx_enable = u_dut.s_rx_enable; + assign tx_busy = u_dut.s_tx_busy; + assign rx_active = u_dut.s_rx_active; + assign break_active = u_dut.s_break; + assign auto_cts_enable = u_dut.s_auto_cts_enable; + assign auto_rts_enable = u_dut.s_auto_rts_enable; + assign cts_asserted = u_dut.s_cts_asserted; + assign rts_asserted = u_dut.s_rts_asserted; + assign tx_start_allowed = u_dut.s_tx_start_allowed; + assign tx_flow_blocked = u_dut.s_tx_flow_blocked; + assign tx_data_pop = u_dut.s_tx_data_pop; + assign tx = uart.tx_o; + assign rts_n = uart.rts_n_o; + assign irq = uart.irq_o; ribp_uart u_dut ( .clk_i (clk_i), diff --git a/rtl/mini/formal/uart_formal_props.sv b/rtl/mini/formal/uart_formal_props.sv index a0a947b7..0d5cba62 100644 --- a/rtl/mini/formal/uart_formal_props.sv +++ b/rtl/mini/formal/uart_formal_props.sv @@ -14,42 +14,58 @@ module uart_formal; wire [ 6:0] tx_count; wire [ 6:0] rx_count; wire [ 6:0] error_status; - wire [ 5:0] intr_state; - wire [ 5:0] intr_enable; + wire [ 6:0] intr_state; + wire [ 6:0] intr_enable; wire tx_enable; wire rx_enable; wire tx_busy; wire rx_active; wire break_active; + wire auto_cts_enable; + wire auto_rts_enable; + wire cts_asserted; + wire rts_asserted; + wire tx_start_allowed; + wire tx_flow_blocked; + wire tx_data_pop; wire tx_dma_stall; wire rx_dma_stall; wire tx; + wire rts_n; wire irq; uart_formal_design u_design ( - .clk_i (clk_i), - .rst_n_i (rst_n_i), - .f_past_valid(f_past_valid), - .rib_valid (rib_valid), - .rib_addr (rib_addr), - .rib_wdata (rib_wdata), - .rib_wstrb (rib_wstrb), - .rib_ready (rib_ready), - .rib_resp_err(rib_resp_err), - .tx_count (tx_count), - .rx_count (rx_count), - .error_status(error_status), - .intr_state (intr_state), - .intr_enable (intr_enable), - .tx_enable (tx_enable), - .rx_enable (rx_enable), - .tx_busy (tx_busy), - .rx_active (rx_active), - .break_active(break_active), - .tx_dma_stall(tx_dma_stall), - .rx_dma_stall(rx_dma_stall), - .tx (tx), - .irq (irq) + .clk_i (clk_i), + .rst_n_i (rst_n_i), + .f_past_valid (f_past_valid), + .rib_valid (rib_valid), + .rib_addr (rib_addr), + .rib_wdata (rib_wdata), + .rib_wstrb (rib_wstrb), + .rib_ready (rib_ready), + .rib_resp_err (rib_resp_err), + .tx_count (tx_count), + .rx_count (rx_count), + .error_status (error_status), + .intr_state (intr_state), + .intr_enable (intr_enable), + .tx_enable (tx_enable), + .rx_enable (rx_enable), + .tx_busy (tx_busy), + .rx_active (rx_active), + .break_active (break_active), + .auto_cts_enable (auto_cts_enable), + .auto_rts_enable (auto_rts_enable), + .cts_asserted (cts_asserted), + .rts_asserted (rts_asserted), + .tx_start_allowed(tx_start_allowed), + .tx_flow_blocked (tx_flow_blocked), + .tx_data_pop (tx_data_pop), + .tx_dma_stall (tx_dma_stall), + .rx_dma_stall (rx_dma_stall), + .tx (tx), + .rts_n (rts_n), + .irq (irq) ); always @(posedge clk_i) begin @@ -68,11 +84,21 @@ module uart_formal; if (!break_active && !tx_busy) assert (tx); if (!tx_enable) assert (tx_dma_stall); if (!rx_enable) assert (rx_dma_stall); + assert (rts_n == !rts_asserted); + if (!auto_rts_enable || !rx_enable) assert (!rts_asserted); + if (tx_data_pop) assert (tx_start_allowed); + if (tx_flow_blocked) begin + assert (auto_cts_enable); + assert (!cts_asserted); + assert (!tx_busy); + end cover (rib_ready && rib_resp_err); cover (tx_busy); cover (rx_active); cover (irq); cover (error_status != 7'd0); + cover (tx_flow_blocked); + cover (rts_asserted); end end diff --git a/rtl/mini/integration/soc_topology.json b/rtl/mini/integration/soc_topology.json index 1b9483f5..a35e45a9 100644 --- a/rtl/mini/integration/soc_topology.json +++ b/rtl/mini/integration/soc_topology.json @@ -67,8 +67,8 @@ {"slot": 7, "name": "user_ip", "timed_interface": "u_user_ip_apb_if", "pure_interface": "u_user_ip_apb_pure_if", "region": "APB_USER_IP"} ], "gpio_alt_functions": [ - {"pin": 0, "alt0": {"inputs": [], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_clk_i"], "do": "u_ps2_if.ps2_clk_o", "oe": "u_ps2_if.ps2_clk_oe_o"}}, - {"pin": 1, "alt0": {"inputs": [], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_dat_i"], "do": "u_ps2_if.ps2_dat_o", "oe": "u_ps2_if.ps2_dat_oe_o"}}, + {"pin": 0, "alt0": {"inputs": ["u_uart0_if.cts_n_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": ["u_ps2_if.ps2_clk_i"], "do": "u_ps2_if.ps2_clk_o", "oe": "u_ps2_if.ps2_clk_oe_o"}}, + {"pin": 1, "alt0": {"inputs": [], "do": "u_uart0_if.rts_n_o", "oe": "1'b1"}, "alt1": {"inputs": ["u_ps2_if.ps2_dat_i"], "do": "u_ps2_if.ps2_dat_o", "oe": "u_ps2_if.ps2_dat_oe_o"}}, {"pin": 2, "alt0": {"inputs": ["u_pwm_if.sync_i"], "do": "1'b0", "oe": "1'b0"}, "alt1": {"inputs": [], "do": "u_ws2812_if.dat_o", "oe": "1'b1"}}, {"pin": 3, "alt0": {"inputs": [], "do": "u_pwm_if.pwm_o[0]", "oe": "u_pwm_if.oe_o[0]"}, "alt1": {"inputs": ["u_i2c1_if.scl_i"], "do": "u_i2c1_if.scl_o", "oe": "u_i2c1_if.scl_oe_o"}}, {"pin": 4, "alt0": {"inputs": [], "do": "u_pwm_if.pwm_o[1]", "oe": "u_pwm_if.oe_o[1]"}, "alt1": {"inputs": ["u_i2c1_if.sda_i"], "do": "u_i2c1_if.sda_o", "oe": "u_i2c1_if.sda_oe_o"}}, diff --git a/rtl/mini/lint.msg b/rtl/mini/lint.msg index 8ae0fb85..08540928 100644 --- a/rtl/mini/lint.msg +++ b/rtl/mini/lint.msg @@ -1,5 +1,5 @@ { +lint=all; - -file=/hazard3/arith/hazard3_muldiv_seq.v,/hazard3/hazard3_frontend.v,/hazard3/arith/hazard3_onehot_encode.v,/hazard3/hazard3_instr_decompress.v,/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CL/verilog/ics55_LLSC_H7CL.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CH/verilog/ics55_LLSC_H7CH.v,/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v,/clusterip/i2c/model/at24cxxx.sv,/clusterip/spi/model/w25q128jvxim.sv,/clusterip/common/rtl/cdc/cdc_2phase.sv,/clusterip/common/rtl/utils/register.sv,/clusterip/uart/model/rs232.sv,/tb/retrosoc_tb.sv,/clusterip/i2s/model/mic.sv,/model/ESP_PSRAM64H.sv,/third_party/model/sdram/sdr.v; + -file=/hazard3/arith/hazard3_muldiv_seq.v,/hazard3/hazard3_frontend.v,/hazard3/arith/hazard3_onehot_encode.v,/hazard3/hazard3_instr_decompress.v,/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CL/verilog/ics55_LLSC_H7CL.v,/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CH/verilog/ics55_LLSC_H7CH.v,/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v,/clusterip/i2c/model/at24cxxx.sv,/clusterip/spi/model/w25q128jvxim.sv,/clusterip/common/rtl/cdc/cdc_2phase.sv,/clusterip/common/rtl/utils/register.sv,/model/uart_model.sv,/tb/retrosoc_tb.sv,/clusterip/i2s/model/mic.sv,/model/ESP_PSRAM64H.sv,/third_party/model/sdram/sdr.v; } { +warn=all; } diff --git a/rtl/mini/mk/formal.mk b/rtl/mini/mk/formal.mk index 8c00fd97..89e033e8 100644 --- a/rtl/mini/mk/formal.mk +++ b/rtl/mini/mk/formal.mk @@ -56,13 +56,14 @@ FORMAL_SOURCE_FILES := $(RTL_PATH)/formal/bus_formal.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ws2812_reg.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ws2812_core.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ribp_ws2812.sv \ + $(ROOT_PATH)/rtl/ip/ribp/serial/uart_if.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/uart_baudgen.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ribp_uart_tx.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ribp_uart_rx.sv \ + $(ROOT_PATH)/rtl/ip/ribp/serial/uart_flow_ctrl.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/uart_core.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/uart_reg.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/ribp_uart.sv \ - $(ROOT_PATH)/rtl/managed/clusterip/uart/rtl/uart_if.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/i2c_filter.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/i2c_core.sv \ $(ROOT_PATH)/rtl/ip/ribp/serial/i2c_reg.sv \ diff --git a/rtl/mini/pin_map/pin_map.json b/rtl/mini/pin_map/pin_map.json index 5bac2892..c1f2e1c6 100644 --- a/rtl/mini/pin_map/pin_map.json +++ b/rtl/mini/pin_map/pin_map.json @@ -59,14 +59,14 @@ "kind": "tri_output", "direction": "output", "ports": [ - {"name": "uart0_tx_o_pad", "signal": "u_uart0_if.tx_o"} + {"name": "uart0_tx_o_pad", "signal": "s_uart0_tx"} ] }, { "kind": "tri_input", "direction": "inout", "ports": [ - {"name": "uart0_rx_i_pad", "signal": "u_uart0_if.rx_i"} + {"name": "uart0_rx_i_pad", "signal": "s_uart0_rx"} ] }, { diff --git a/rtl/mini/top/retrosoc.sv b/rtl/mini/top/retrosoc.sv index 8269685d..1f828040 100644 --- a/rtl/mini/top/retrosoc.sv +++ b/rtl/mini/top/retrosoc.sv @@ -25,7 +25,8 @@ module retrosoc ( ram_if.master ram, `endif gpio_if.soc_pad gpio, - uart_if.dut uart0, + input logic uart_rx_i, + output logic uart_tx_o, xpi_if.dut xpi, sdram_if.dut sdram, input logic jtag_tck_i, @@ -46,6 +47,7 @@ module retrosoc ( user_gpio_if u_user_gpio_if (); // ip interface gpio_if u_gpio_if (); + uart_if u_uart0_if (); psram_if u_psram_if (); spi_if u_spisd_if (); i2c_if u_i2c0_if (); @@ -101,6 +103,8 @@ module retrosoc ( assign u_sysctrl_if.perf_psram_wait_i = s_perf_psram_wait; assign u_sysctrl_if.perf_flash_wait_i = s_perf_flash_wait; assign u_sysctrl_if.rtc_wake_i = s_rtc_wake; + assign u_uart0_if.rx_i = uart_rx_i; + assign uart_tx_o = u_uart0_if.tx_o; gpio_pad_bridge u_gpio_pad_bridge ( .inner(u_gpio_if), @@ -174,7 +178,7 @@ core_wrapper u_core_wrapper ( `include "ip_ribp_wrapper_fabric.svh" .gpio (u_gpio_if), .user_gpio (u_user_gpio_if), - .uart (uart0), + .uart (u_uart0_if), .psram (u_psram_if), .spisd (u_spisd_if), .i2c0 (u_i2c0_if), diff --git a/rtl/mini/top/retrosoc_asic.sv b/rtl/mini/top/retrosoc_asic.sv index a445fe59..68062dee 100644 --- a/rtl/mini/top/retrosoc_asic.sv +++ b/rtl/mini/top/retrosoc_asic.sv @@ -18,8 +18,8 @@ // // GPIO ALT0 DIR ALT1 DIR // ------ --------------------------- ------ ---------------------- ------ -// GPIO00 TIE_OFF - PS2_CLK BIDI -// GPIO01 TIE_OFF - PS2_DATA BIDI +// GPIO00 UART0_CTS_N IN PS2_CLK BIDI +// GPIO01 UART0_RTS_N OUT PS2_DATA BIDI // GPIO02 PWM_SYNC IN WS2812_DATA OUT // GPIO03 PWM0 OUT I2C1_SCL BIDI // GPIO04 PWM1 OUT I2C1_SDA BIDI @@ -71,6 +71,8 @@ module retrosoc_asic ( logic s_jtag_tdi; logic s_jtag_trst_n; logic s_jtag_tdo; + logic s_uart0_rx; + logic s_uart0_tx; (* keep = "true" *)logic s_test_done; (* keep = "true" *)logic s_test_pass; (* keep = "true" *)logic [7:0] s_test_code; @@ -80,7 +82,6 @@ module retrosoc_asic ( `endif gpio_if u_gpio_if (); - uart_if u_uart0_if (); xpi_if u_xpi_if (); sdram_if u_sdram_if (); pll_ctrl_if u_pll_ctrl_if (); @@ -125,7 +126,8 @@ rcu #( .ram (u_ram_if), `endif .gpio (u_gpio_if), - .uart0 (u_uart0_if), + .uart_rx_i (s_uart0_rx), + .uart_tx_o (s_uart0_tx), .xpi (u_xpi_if), .sdram (u_sdram_if), .jtag_tck_i (s_jtag_tck), diff --git a/tests/rtl/uart_tb.sv b/tests/rtl/uart_tb.sv index 7d789e44..e0aeeb6b 100644 --- a/tests/rtl/uart_tb.sv +++ b/tests/rtl/uart_tb.sv @@ -69,7 +69,10 @@ module uart_tb; end endtask - task automatic configure_uart(input logic [4:0] line_control, input logic [3:0] control); + task automatic configure_uart_flow(input logic [4:0] line_control, input logic [1:0] flow_control, + input logic [6:0] rts_assert_level, + input logic [6:0] rts_deassert_level, + input logic [3:0] control); begin write_ok(`RIBP_UART_CTRL, 0); repeat (4) @(posedge clk_i); @@ -77,10 +80,18 @@ module uart_tb; write_ok(`RIBP_UART_BAUD_INT, 16); write_ok(`RIBP_UART_BAUD_FRAC, 0); write_ok(`RIBP_UART_LINE_CTRL, line_control); + write_ok(`RIBP_UART_RTS_WATERMARK, {9'd0, rts_deassert_level, 9'd0, rts_assert_level}); + write_ok(`RIBP_UART_FLOW_CTRL, flow_control); write_ok(`RIBP_UART_CTRL, control); end endtask + task automatic configure_uart(input logic [4:0] line_control, input logic [3:0] control); + begin + configure_uart_flow(line_control, 2'd0, 7'd32, 7'd48, control); + end + endtask + task automatic send_external_8n1(input logic [7:0] data, input logic bad_stop); integer bit_index; begin @@ -101,23 +112,30 @@ module uart_tb; logic [31:0] value; integer index; - ribp.valid = 1'b0; - ribp.addr = '0; - ribp.wdata = '0; - ribp.wstrb = '0; - uart.rx_i = 1'b1; + ribp.valid = 1'b0; + ribp.addr = '0; + ribp.wdata = '0; + ribp.wstrb = '0; + uart.rx_i = 1'b1; + uart.cts_n_i = 1'b1; repeat (4) @(posedge clk_i); rst_n_i = 1'b1; repeat (4) @(posedge clk_i); + if (uart.rts_n_o !== 1'b1) $fatal(1, "UART RTS_n was not released after reset"); ribp_read(`RIBP_UART_IP_VERSION, 1'b0, value); - if (value != 32'h0002_0000) $fatal(1, "UART IP version mismatch"); + if (value != 32'h0003_0000) $fatal(1, "UART IP version mismatch"); ribp_read(`RIBP_UART_CAPABILITY, 1'b0, value); - if (value != 32'h00FF_4040) $fatal(1, "UART capability mismatch"); + if (value != 32'h03FF_4040) $fatal(1, "UART capability mismatch"); ribp_read(32'h0000_0002, 1'b1, value); ribp_read(`RIBP_UART_RXDATA, 1'b1, value); - configure_uart(5'd3, 4'b0111); + ribp_write(`RIBP_UART_RTS_WATERMARK, 32'h0002_0002, 4'hF, 1'b1); + ribp_read(`RIBP_UART_ERROR_STATUS, 1'b0, value); + if (!value[`UART_ERROR_CONFIG]) $fatal(1, "UART invalid RTS watermark was not reported"); + write_ok(`RIBP_UART_ERROR_STATUS, 7'h7F); + + configure_uart_flow(5'd3, 2'b01, 7'd32, 7'd48, 4'b0111); write_ok(`RIBP_UART_TXDATA, 8'hA5); repeat (220) @(posedge clk_i); ribp_read(`RIBP_UART_RXDATA, 1'b0, value); @@ -135,11 +153,48 @@ module uart_tb; $fatal(1, "UART TX did not finish its active frame after disable: %h", value); end - write_ok(`RIBP_UART_INTR_ENABLE, 6'h3F); - write_ok(`RIBP_UART_INTR_TEST, 6'h20); + write_ok(`RIBP_UART_INTR_ENABLE, 7'h7F); + write_ok(`RIBP_UART_INTR_TEST, 7'h20); ribp_read(`RIBP_UART_INTR_STATUS, 1'b0, value); if (!value[`UART_INTR_BREAK] || !uart.irq_o) $fatal(1, "UART interrupt test failed"); - write_ok(`RIBP_UART_INTR_STATE, 6'h3F); + write_ok(`RIBP_UART_INTR_STATE, 7'h7F); + + configure_uart_flow(5'd3, 2'b01, 7'd32, 7'd48, 4'b0001); + write_ok(`RIBP_UART_INTR_ENABLE, 7'h40); + write_ok(`RIBP_UART_TXDATA, 8'hC3); + write_ok(`RIBP_UART_TXDATA, 8'h5C); + repeat (32) @(posedge clk_i); + ribp_read(`RIBP_UART_STATUS, 1'b0, value); + if (!value[`UART_STATUS_TX_FLOW_BLOCKED] || value[`UART_STATUS_TX_BUSY] || + value[`UART_STATUS_CTS_ASSERTED]) begin + $fatal(1, "UART CTS did not block a new frame: %h", value); + end + ribp_read(`RIBP_UART_FIFO_LEVEL, 1'b0, value); + if (value[6:0] != 7'd2) $fatal(1, "UART CTS-blocked FIFO count mismatch: %h", value); + + uart.cts_n_i = 1'b0; + repeat (32) @(posedge clk_i); + ribp_read(`RIBP_UART_STATUS, 1'b0, value); + if (!value[`UART_STATUS_CTS_ASSERTED] || !value[`UART_STATUS_TX_BUSY]) begin + $fatal(1, "UART did not start after CTS assertion: %h", value); + end + uart.cts_n_i = 1'b1; + repeat (200) @(posedge clk_i); + ribp_read(`RIBP_UART_STATUS, 1'b0, value); + if (!value[`UART_STATUS_TX_FLOW_BLOCKED] || value[`UART_STATUS_TX_BUSY]) begin + $fatal(1, "UART CTS did not stop at the frame boundary: %h", value); + end + ribp_read(`RIBP_UART_FIFO_LEVEL, 1'b0, value); + if (value[6:0] != 7'd1) $fatal(1, "UART frame-boundary FIFO count mismatch: %h", value); + ribp_read(`RIBP_UART_INTR_STATUS, 1'b0, value); + if (!value[`UART_INTR_CTS_CHANGE] || !uart.irq_o) begin + $fatal(1, "UART CTS change interrupt failed: %h", value); + end + write_ok(`RIBP_UART_INTR_STATE, 7'h7F); + uart.cts_n_i = 1'b0; + repeat (220) @(posedge clk_i); + ribp_read(`RIBP_UART_FIFO_LEVEL, 1'b0, value); + if (value[6:0] != 7'd0) $fatal(1, "UART did not resume after CTS assertion: %h", value); configure_uart(5'd3, 4'b0010); fork @@ -150,6 +205,21 @@ module uart_tb; $fatal(1, "UART frame error was not retained: %h", value); end + configure_uart_flow(5'd3, 2'b10, 7'd1, 7'd2, 4'b0010); + repeat (4) @(posedge clk_i); + if (uart.rts_n_o !== 1'b0) $fatal(1, "UART RTS_n was not asserted for an empty RX FIFO"); + send_external_8n1(8'h12, 1'b0); + if (uart.rts_n_o !== 1'b0) $fatal(1, "UART RTS_n asserted state did not hold below watermark"); + send_external_8n1(8'h34, 1'b0); + if (uart.rts_n_o !== 1'b1) $fatal(1, "UART RTS_n was not released at high watermark"); + ribp_read(`RIBP_UART_RXDATA, 1'b0, value); + repeat (2) @(posedge clk_i); + if ((value[7:0] != 8'h12) || (uart.rts_n_o !== 1'b0)) begin + $fatal(1, "UART RTS hysteresis did not reassert below low watermark: %h", value); + end + ribp_read(`RIBP_UART_RXDATA, 1'b0, value); + if (value[7:0] != 8'h34) $fatal(1, "UART RTS test RX ordering mismatch: %h", value); + write_ok(`RIBP_UART_CTRL, 0); repeat (4) @(posedge clk_i); write_ok(`RIBP_UART_FIFO_CTRL, 3); @@ -164,7 +234,7 @@ module uart_tb; write_ok(`RIBP_UART_DMA_CTRL, 3); if (!dma_tx_stall || !dma_rx_stall) $fatal(1, "Disabled UART asserted a DMA request"); - $display("UART V2 register, FIFO, loopback, error, interrupt, and DMA test passed"); + $display("UART V3 register, FIFO, loopback, flow-control, interrupt, and DMA test passed"); $finish; end diff --git a/tests/test_soc_topology.py b/tests/test_soc_topology.py index b834b35c..bd32ebd8 100644 --- a/tests/test_soc_topology.py +++ b/tests/test_soc_topology.py @@ -83,10 +83,11 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) - assert "assign u_sdio_ribp_if.valid = 1'b0;" in routes assert gpio.count("// GPIO") == 64 assert "u_uart1_if" not in gpio + assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio assert "assign u_gpio_if.alt0_do_i[0] = 1'b0;" in gpio assert "assign u_gpio_if.alt0_oe_i[0] = 1'b0;" in gpio - assert "assign u_gpio_if.alt0_do_i[1] = 1'b0;" in gpio - assert "assign u_gpio_if.alt0_oe_i[1] = 1'b0;" in gpio + assert "assign u_gpio_if.alt0_do_i[1] = u_uart0_if.rts_n_o;" in gpio + assert "assign u_gpio_if.alt0_oe_i[1] = 1'b1;" in gpio assert "assign u_gpio_if.alt1_do_i[0] = u_ps2_if.ps2_clk_o;" in gpio assert "assign u_gpio_if.alt1_oe_i[0] = u_ps2_if.ps2_clk_oe_o;" in gpio assert "assign u_gpio_if.alt1_do_i[1] = u_ps2_if.ps2_dat_o;" in gpio diff --git a/tests/test_uart.py b/tests/test_uart.py index 8426c597..837cffcf 100644 --- a/tests/test_uart.py +++ b/tests/test_uart.py @@ -1,4 +1,4 @@ -"""UART V2 register, FIFO, framing, interrupt, and DMA request tests.""" +"""UART V3 register, FIFO, framing, flow-control, interrupt, and DMA tests.""" from __future__ import annotations @@ -11,7 +11,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -def test_uart_v2_register_fifo_loopback_and_errors(tmp_path: Path) -> None: +def test_uart_v3_register_fifo_loopback_flow_control_and_errors(tmp_path: Path) -> None: iverilog = shutil.which("iverilog") vvp = shutil.which("vvp") if iverilog is None or vvp is None: @@ -19,7 +19,6 @@ def test_uart_v2_register_fifo_loopback_and_errors(tmp_path: Path) -> None: common = ROOT / "rtl/managed/clusterip/common/rtl" serial = ROOT / "rtl/ip/ribp/serial" - cluster_uart = ROOT / "rtl/managed/clusterip/uart/rtl" source_list = tmp_path / "uart.fl" source_list.write_text( "\n".join( @@ -27,15 +26,19 @@ def test_uart_v2_register_fifo_loopback_and_errors(tmp_path: Path) -> None: "+define+SV_ASSRT_DISABLE", f"+incdir+{common}", f"+incdir+{common / 'interface'}", + f"+incdir+{common / 'cdc'}", f"+incdir+{common / 'utils'}", f"+incdir+{serial}", str(common / "interface/ribp_if.sv"), + str(common / "cdc/cdc_sync.sv"), str(common / "utils/register.sv"), + str(common / "utils/edge_det.sv"), str(common / "utils/fifo.sv"), - str(cluster_uart / "uart_if.sv"), + str(serial / "uart_if.sv"), str(serial / "uart_baudgen.sv"), str(serial / "ribp_uart_tx.sv"), str(serial / "ribp_uart_rx.sv"), + str(serial / "uart_flow_ctrl.sv"), str(serial / "uart_core.sv"), str(serial / "uart_reg.sv"), str(serial / "ribp_uart.sv"), @@ -63,4 +66,4 @@ def test_uart_v2_register_fifo_loopback_and_errors(tmp_path: Path) -> None: check=True, ) result = subprocess.run([vvp, str(simulation)], text=True, capture_output=True, check=True) - assert "UART V2 register, FIFO, loopback, error, interrupt, and DMA test passed" in result.stdout + assert "UART V3 register, FIFO, loopback, flow-control, interrupt, and DMA test passed" in result.stdout