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/UartTransmitter.lvt

namespace Livt.IO

/**
 * UartTransmitter
 *
 * Transmits compile-time-configured UART frames on `tx`. A pulse on `tx_dv` starts
 * transmission of `tx_byte`, `tx_active` remains high while the frame
 * is in flight, and `tx_done` pulses for one cycle on completion.
 */
component UartTransmitter<DATA_BITS: UartDataBits = UartDataBits.Eight,
	PARITY: UartParity = UartParity.None,
	STOP_BITS: UartStopBits = UartStopBits.One,
	BAUD: Frequency = 115200Hz>
{
	// Compile-time bit timing and the minimum width of the bit-period counter.
	const TICKS_PER_BIT: uint = this.context.TicksPer(BAUD)
	const CLOCK_COUNT_BITS: int = Bits.RequiredFor(TICKS_PER_BIT - 1)

	// Launch request and payload captured when the transmitter is idle.
	tx_dv: logic
	tx_byte: logic[8]

	// Physical line and frame-progress observations.
	tx: logic
	tx_active: logic
	tx_done: logic

	// Zero-initialized state represents the idle-high physical line.
	txLow: logic

	/**
	 * Configures transmission at a compile-time baud rate and frame format.
	 */
	new(
		tx: out logic,
		tx_dv: in logic,
		tx_byte: in logic[8],
		tx_active: out logic,
		tx_done: out logic
	)
	{
		this.tx_dv = tx_dv
		this.tx_byte = tx_byte

		tx_active = this.tx_active
		tx = this.tx
		tx_done = this.tx_done
	}

	/**
	 * Converts the reset-low internal state to the idle-high UART wire.
	 */
	process DriveTx[]()
	{
		this.tx = ~this.txLow
	}

	/**
	 * Serializes the captured byte with compile-time parity and stop-bit selection.
	 */
	process Transmitter()
	{
		var clock_count: logic[CLOCK_COUNT_BITS] = (0 as uint) as logic[CLOCK_COUNT_BITS]
		var bit_index: logic[3] = 0b000
		var tx_data = 0x00
		var parity: logic = 0b0

		state Idle {
			this.tx_active = 0
			this.txLow = 0
			this.tx_done = 0
			clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]
			bit_index = 0
			parity = 0b0

			if (this.tx_dv) {
				tx_data = this.tx_byte
				goto StartBit
			} else {
				goto Idle
			}
		}

		state StartBit {
			this.tx_active = 1
			this.txLow = 1

			if ((clock_count as uint) < TICKS_PER_BIT - 1) {
				clock_count = (((clock_count as uint) + 1) as int) as logic[CLOCK_COUNT_BITS]
				goto StartBit
			} else {
				clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]
				goto DataBits
			}
		}

		state DataBits {
			this.txLow = ~tx_data[bit_index as uint]

			if ((clock_count as uint) < TICKS_PER_BIT - 1) {
				clock_count = (((clock_count as uint) + 1) as int) as logic[CLOCK_COUNT_BITS]
				goto DataBits
			} else {
				clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]

				static if (PARITY != UartParity.None) {
					parity = parity ^ tx_data[bit_index as uint]
				}

				static if (DATA_BITS == UartDataBits.Five) {
					if ((bit_index as uint) < 4) {
						bit_index = ((bit_index as uint) + 1) as logic[3];
						goto DataBits
					} else {
						bit_index = 0;

						static if (PARITY == UartParity.None) {
							goto StopBit
						} else {
							goto ParityBit
						}
					}
				} elif (DATA_BITS == UartDataBits.Six) {
					if ((bit_index as uint) < 5) {
						bit_index = ((bit_index as uint) + 1) as logic[3];
						goto DataBits
					} else {
						bit_index = 0;

						static if (PARITY == UartParity.None) {
							goto StopBit
						} else {
							goto ParityBit
						}
					}
				} elif (DATA_BITS == UartDataBits.Seven) {
					if ((bit_index as uint) < 6) {
						bit_index = ((bit_index as uint) + 1) as logic[3];
						goto DataBits
					} else {
						bit_index = 0;

						static if (PARITY == UartParity.None) {
							goto StopBit
						} else {
							goto ParityBit
						}
					}
				} else {
					if ((bit_index as uint) < 7) {
						bit_index = ((bit_index as uint) + 1) as logic[3];
						goto DataBits
					} else {
						bit_index = 0;

						static if (PARITY == UartParity.None) {
							goto StopBit
						} else {
							goto ParityBit
						}
					}
				}
			}
		}

		static if (PARITY != UartParity.None) {
			state ParityBit {
				static if (PARITY == UartParity.Even) {
					this.txLow = ~parity
				} else {
					this.txLow = parity
				}

				if ((clock_count as uint) < TICKS_PER_BIT - 1) {
					clock_count = (((clock_count as uint) + 1) as int) as logic[CLOCK_COUNT_BITS]
					goto ParityBit
				} else {
					clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]
					goto StopBit
				}
			}
		}

		state StopBit {
			this.txLow = 0

			if ((clock_count as uint) < TICKS_PER_BIT - 1) {
				clock_count = (((clock_count as uint) + 1) as int) as logic[CLOCK_COUNT_BITS]
				goto StopBit
			} else {
				clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]

				static if (STOP_BITS == UartStopBits.Two) {
					goto SecondStopBit
				} else {
					this.tx_done = 1
					goto Cleanup
				}
			}
		}

		static if (STOP_BITS == UartStopBits.Two) {
			state SecondStopBit {
				this.txLow = 0

				if ((clock_count as uint) < TICKS_PER_BIT - 1) {
					clock_count = (((clock_count as uint) + 1) as int) as logic[CLOCK_COUNT_BITS]
					goto SecondStopBit
				} else {
					clock_count = (0 as uint) as logic[CLOCK_COUNT_BITS]
					this.tx_done = 1
					goto Cleanup
				}
			}
		}

		state Cleanup {
			goto Idle
		}
	}
}