Livt packages

Livt.IO

Version 1.8.1

Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.

Published files (126)

src/uart/RtsCtsBufferedUart.lvt

namespace Livt.IO

/**
 * Active-low RTS/CTS UART with compile-time receive and transmit capacities.
 * Scheduled methods report transaction results; they are not single-cycle ports.
 */
component RtsCtsBufferedUart<RX_CAPACITY: int = 64,
	TX_CAPACITY: int = 64,
	DATA_BITS: UartDataBits = UartDataBits.Eight,
	PARITY: UartParity = UartParity.None,
	STOP_BITS: UartStopBits = UartStopBits.One,
	BAUD: Frequency = 115200Hz> : IBufferedUart by buffered
{
	// Reserve at least one receive slot; small FIFOs require prompt peer response.
	const RTS_HEADROOM: int = (RX_CAPACITY + 7) / 8
	const RTS_STOP_LEVEL: int = RX_CAPACITY - RTS_HEADROOM
	const RTS_RESUME_LEVEL: int = RTS_STOP_LEVEL - RTS_HEADROOM

	static assert RX_CAPACITY >= 2, "RTS/CTS receive capacity must be at least two"

	// Scheduled UART implementation delegated through the common contract.
	buffered: BufferedUart<RX_CAPACITY, TX_CAPACITY, DATA_BITS, PARITY, STOP_BITS, BAUD>

	// Asynchronous CTS input and its two-stage synchronized representation.
	ctsN: logic
	ctsMeta: logic
	ctsSync: logic
	permission: bool

	// Registered RTS output and the receive occupancy used by its hysteresis.
	rtsN: logic
	receiveCount: int

	/**
	 * Inherits clock/reset from the selected context and specializes the baud rate.
	 */
	new(rx: in logic, tx: out logic, ctsN: in logic, rtsN: out logic)
	{
		this.ctsN = ctsN
		this.ctsMeta = 0b1
		this.ctsSync = 0b1
		rtsN = this.rtsN
		this.buffered = new BufferedUart<RX_CAPACITY, TX_CAPACITY, DATA_BITS, PARITY, STOP_BITS, BAUD>(
			rx, tx, launchPermission = permission)
	}

	/**
	 * Two clocked stages synchronize active-low CTS before launch permission.
	 */
	process SynchronizeCts()
	{
		state Tick {
			ctsSync = ctsMeta
			ctsMeta = ctsN
			goto Tick
		}
	}

	/**
	 * Read the FIFO occupancy without a scheduled status call.
	 */
	process Status[]()
	{
		permission = ctsSync == 0b0
		receiveCount = this.buffered.receiveCount
	}

	/**
	 * Stop with reserved headroom and resume only below the lower watermark.
	 */
	process UpdateRts()
	{
		state Tick {
			if (receiveCount >= RTS_STOP_LEVEL) {
				rtsN = 0b1
			} elif (receiveCount <= RTS_RESUME_LEVEL) {
				rtsN = 0b0
			}

			goto Tick
		}
	}
}