Livt packages

Livt.IO

Version 1.8.1

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

Published files (126)

tests/uart/UartTest.lvt

namespace Livt.IO.Tests

using Livt.IO

/**
 * Exercises Uart with a shared fixture instance. Each scenario establishes the inputs and
 * state it needs.
 */
@Test
@Context(ClockFrequency = 100MHz)
component UartTest
{
	// Independent reference timing for the default 100 MHz/115200-baud regression.
	const DEFAULT_TICKS_PER_BIT: int = 868

	// Shared fixture instances, signals, and observation state.
	uart: Uart

	// Shared fixture instances, signals, and observation state.
	rx: logic

	// Shared fixture instances, signals, and observation state.
	tx: logic

	// Shared fixture instances, signals, and observation state.
	tx_dv: logic

	// Shared fixture instances, signals, and observation state.
	tx_byte: byte

	// Shared fixture instances, signals, and observation state.
	tx_active: logic

	// Shared fixture instances, signals, and observation state.
	tx_done: logic

	// Shared fixture instances, signals, and observation state.
	rx_dv: logic

	// Shared fixture instances, signals, and observation state.
	rx_byte: byte

	// Shared fixture instances, signals, and observation state.
	rx_frame_error: logic

	/**
	 * Creates the shared fixture instances (Uart). Tests initialize the state required by
	 * their scenario.
	 */
	new()
	{
		this.rx = 0b1
		this.uart = new Uart(
			this.rx,
			this.tx,
			this.tx_dv,
			this.tx_byte,
			this.tx_active,
			this.tx_done,
			this.rx_dv,
			this.rx_byte,
			this.rx_frame_error
		)
	}

	/**
	 * Checks the transmit data case.
	 */
	@Test
	fn TestTransmitData()
	{
		// Test transmitting a byte
		this.tx_byte = 0xAA
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Should be transmitting
		Simulation.Wait(10)
		assert this.tx_active == 0b1

		// Wait for transmission to complete
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 9)

		// Check for tx_done pulse
		var done_seen = 0b0

		state {
			for (var i = 0; i < DEFAULT_TICKS_PER_BIT; i = i++) {
				if (this.tx_done == 0b1) {
					done_seen = 0b1
				}
			}
		}

		assert done_seen == 0b1

		// Should be back to idle
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the receive data case.
	 */
	@Test
	fn TestReceiveData()
	{
		// Test receiving a byte (0x55)
		this.rx = 0b1
		Simulation.Wait(100)

		this.rx = 0b0 // Start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// Send 0x55 = 01010101, LSB first: 1,0,1,0,1,0,1,0
		this.rx = 0b1 // Bit 0
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 2
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 3
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 4
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 5
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 6
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 7
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		this.rx = 0b1 // Stop bit

		// Check for rx_dv pulse and received data
		var dv_seen = 0b0

		state {
			for (var i = 0; i < DEFAULT_TICKS_PER_BIT; i = i + 1) {
				if (this.rx_dv == 0b1) {
					dv_seen = 0b1
				}
			}
		}

		assert dv_seen == 0b1
		assert this.rx_byte == 0x55

		// Should remain idle
		assert this.rx == 0b1
		assert this.rx_dv == 0b0
	}

	/**
	 * Checks the invalid stop bit raises frame error case.
	 */
	@Test
	fn TestInvalidStopBitRaisesFrameError()
	{
		this.rx = 0b1
		Simulation.Wait(100)

		this.rx = 0b0 // Start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// Send 0x5A = 01011010, LSB first: 0,1,0,1,1,0,1,0
		this.rx = 0b0 // Bit 0
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 2
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 3
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 4
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 5
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 6
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 7
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		this.rx = 0b0 // Invalid stop bit

		var frame_error_seen = 0b0
		var dv_seen = 0b0

		state {
			for (var i = 0; i < DEFAULT_TICKS_PER_BIT * 2; i = i + 1) {
				if (this.rx_frame_error == 0b1) {
					frame_error_seen = 0b1
				}

				if (this.rx_dv == 0b1) {
					dv_seen = 0b1
				}
			}
		}

		assert frame_error_seen == 0b1
		assert dv_seen == 0b0

		this.rx = 0b1
		Simulation.Wait(10)
		assert this.rx_frame_error == 0b0
	}

	/**
	 * Checks the loopback case.
	 */
	@Test
	fn TestLoopback()
	{
		// Connect TX to RX for loopback test
		// We'll manually set rx to follow tx

		// Transmit 0x42
		this.tx_byte = 0x42
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Wait a bit for start bit
		Simulation.Wait(10)

		// Now set rx to follow tx for the rest of transmission
		// This simulates a loopback connection
		// Start bit
		this.rx = this.tx
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// Data bits
		for (var i = 0; i < 8; i = i + 1) {
			this.rx = this.tx
			Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		}

		// Stop bit
		this.rx = this.tx
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 2)

		// Check that receiver got the data
		assert this.rx_byte == 0x42

		// Check tx completed
		assert this.tx == 0b1
		assert this.tx_active == 0b0

		// Return to idle
		this.rx = 0b1
		Simulation.Wait(10)
	}

	/**
	 * Checks the transmit multiple bytes case.
	 */
	@Test
	fn TestTransmitMultipleBytes()
	{
		// First byte
		this.tx_byte = 0x12
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 11)

		// Should be idle
		assert this.tx == 0b1
		assert this.tx_active == 0b0

		// Second byte
		this.tx_byte = 0x34
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 11)

		// Should be idle again
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the receive multiple bytes case.
	 */
	@Test
	fn TestReceiveMultipleBytes()
	{
		// First byte: 0xAB
		this.rx = 0b1
		Simulation.Wait(100)

		this.rx = 0b0 // Start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// 0xAB = 10101011, LSB first: 1,1,0,1,0,1,0,1
		this.rx = 0b1 // Bit 0
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 2
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 3
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 4
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 5
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 6
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 7
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		this.rx = 0b1 // Stop bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 2)

		assert this.rx_byte == 0xAB

		// Second byte: 0xCD
		this.rx = 0b0 // Start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// 0xCD = 11001101, LSB first: 1,0,1,1,0,0,1,1
		this.rx = 0b1 // Bit 0
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 2
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 3
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 4
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b0 // Bit 5
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 6
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		this.rx = 0b1 // Bit 7
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		this.rx = 0b1 // Stop bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 2)

		assert this.rx_byte == 0xCD

		// Return to idle
		this.rx = 0b1
		Simulation.Wait(10)
	}

	/**
	 * Checks the idle state case.
	 */
	@Test
	fn TestIdleState()
	{
		// Verify both TX and RX are idle initially
		this.rx = 0b1
		Simulation.Wait(100)

		assert this.tx == 0b1
		assert this.tx_active == 0b0
		assert this.rx_dv == 0b0
	}
}