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

namespace Livt.IO.Tests

using Livt.IO

/**
 * Exercises LoopbackUart with a shared fixture instance. Each scenario establishes the
 * inputs and state it needs.
 */
@Test
@Context(ClockFrequency = 100MHz)
component LoopbackUartTest
{
	// Independent 100 MHz/1 Mbaud timing keeps behavioral regressions short.
	const TICKS_PER_BIT: int = 100

	// Shared fixture instances, signals, and observation state.
	loopback: LoopbackUart<64, 64, UartDataBits.Eight, UartParity.None, UartStopBits.One, 1MHz>

	/**
	 * Creates the shared fixture instances (LoopbackUart). Tests initialize the state required
	 * by their scenario.
	 */
	new()
	{
		this.loopback = new LoopbackUart<64, 64, UartDataBits.Eight, UartParity.None, UartStopBits.One, 1MHz>()
	}

	/**
	 * Checks the initial state case.
	 */
	@Test
	fn TestInitialState()
	{
		// Verify initial state
		assert this.loopback.GetReceiveCount() == 0
		assert this.loopback.GetTransmitSpace() == 64
		assert this.loopback.GetReceiveCount() == 0
		assert this.loopback.IsTransmitting() == false
	}

	/**
	 * Checks the loopback single byte case.
	 */
	@Test
	fn TestLoopbackSingleByte()
	{
		// Transmit a byte
		var result = this.loopback.TryTransmit(0x42)
		assert result == true

		// Initially no data available (transmission in progress)
		assert this.loopback.GetReceiveCount() == 0

		// Wait for transmission and reception to complete
		// Full transmission takes 10 bit periods, plus some for reception
		Simulation.Wait(TICKS_PER_BIT * 12)

		// Data should now be available via loopback
		assert this.loopback.GetReceiveCount() > 0
		assert this.loopback.GetReceiveCount() == 1

		// Receive the looped-back byte
		var received: byte
		assert this.loopback.TryReceive(received) == true
		assert received == 0x42

		// No more data
		assert this.loopback.GetReceiveCount() == 0
	}

	/**
	 * Checks the loopback multiple bytes case.
	 */
	@Test
	fn TestLoopbackMultipleBytes()
	{
		// Transmit three bytes in sequence
		assert this.loopback.TryTransmit(0xAA) == true
		assert this.loopback.TryTransmit(0x55) == true
		assert this.loopback.TryTransmit(0xFF) == true

		// Wait for all to transmit and loop back
		// 3 bytes × 10 bit periods + margin
		Simulation.Wait(TICKS_PER_BIT * 35)

		// Should have 3 bytes available
		assert this.loopback.GetReceiveCount() == 3

		// Receive in order
		var byte1: byte
		assert this.loopback.TryReceive(byte1) == true
		assert byte1 == 0xAA

		var byte2: byte

		assert this.loopback.TryReceive(byte2) == true
		assert byte2 == 0x55

		var byte3: byte

		assert this.loopback.TryReceive(byte3) == true
		assert byte3 == 0xFF

		// All received
		assert this.loopback.GetReceiveCount() == 0
	}

	/**
	 * Checks the delegated send case.
	 */
	@Test
	fn TestDelegatedSend()
	{
		this.loopback.ClearReceiveBuffer()
		this.loopback.ClearTransmitBuffer()

		var message = "Hi".Encode()
		assert this.loopback.Send(message) == 2

		Simulation.Wait(TICKS_PER_BIT * 25)

		assert this.loopback.GetReceiveCount() == 2
		var first: byte
		assert this.loopback.TryReceive(first) == true
		assert first == 0x48
		var second: byte
		assert this.loopback.TryReceive(second) == true
		assert second == 0x69
	}

	/**
	 * Checks the loopback all zeros case.
	 */
	@Test
	fn TestLoopbackAllZeros()
	{
		// Test edge case: all zeros
		this.loopback.TryTransmit(0x00)

		Simulation.Wait(TICKS_PER_BIT * 12)

		assert this.loopback.GetReceiveCount() > 0
		var received: byte
		assert this.loopback.TryReceive(received) == true
		assert received == 0x00
	}

	/**
	 * Checks the loopback all ones case.
	 */
	@Test
	fn TestLoopbackAllOnes()
	{
		// Test edge case: all ones
		this.loopback.TryTransmit(0xFF)

		Simulation.Wait(TICKS_PER_BIT * 12)

		assert this.loopback.GetReceiveCount() > 0
		var received: byte
		assert this.loopback.TryReceive(received) == true
		assert received == 0xFF
	}

	/**
	 * Checks the loopback pattern case.
	 */
	@Test
	fn TestLoopbackPattern()
	{
		// Test alternating bit pattern
		this.loopback.TryTransmit(0xAA) // 10101010

		Simulation.Wait(TICKS_PER_BIT * 12)

		assert this.loopback.GetReceiveCount() > 0
		var received: byte
		assert this.loopback.TryReceive(received) == true
		assert received == 0xAA
	}

	/**
	 * Checks the transmit buffer space case.
	 */
	@Test
	fn TestTransmitBufferSpace()
	{
		// Verify transmit buffer management
		var initial_space = this.loopback.GetTransmitSpace()
		assert initial_space == 64

		// Add a few bytes
		this.loopback.TryTransmit(0x11)
		this.loopback.TryTransmit(0x22)
		this.loopback.TryTransmit(0x33)

		// Wait for transmission to complete
		Simulation.Wait(TICKS_PER_BIT * 35)

		// Buffer should be drained
		assert this.loopback.GetTransmitSpace() == 64
	}

	/**
	 * Checks the receive buffer filling case.
	 */
	@Test
	fn TestReceiveBufferFilling()
	{
		this.loopback.ClearReceiveBuffer()
		this.loopback.ClearTransmitBuffer()

		// Send multiple bytes and verify receive buffer fills
		this.loopback.TryTransmit(0x10)
		this.loopback.TryTransmit(0x11)
		this.loopback.TryTransmit(0x12)
		this.loopback.TryTransmit(0x13)
		this.loopback.TryTransmit(0x14)

		// Wait for all to complete
		Simulation.Wait(TICKS_PER_BIT * 60)

		// Should have 5 bytes available
		assert this.loopback.GetReceiveCount() == 5

		// Receive all in order
		var receivedByte1: byte
		assert this.loopback.TryReceive(receivedByte1) == true
		assert receivedByte1 == 0x10
		var receivedByte2: byte
		assert this.loopback.TryReceive(receivedByte2) == true
		assert receivedByte2 == 0x11
		var receivedByte3: byte
		assert this.loopback.TryReceive(receivedByte3) == true
		assert receivedByte3 == 0x12
		var receivedByte4: byte
		assert this.loopback.TryReceive(receivedByte4) == true
		assert receivedByte4 == 0x13
		var receivedByte5: byte
		assert this.loopback.TryReceive(receivedByte5) == true
		assert receivedByte5 == 0x14

		assert this.loopback.GetReceiveCount() == 0
	}

	/**
	 * Checks the clear receive buffer case.
	 */
	@Test
	fn TestClearReceiveBuffer()
	{
		// Send a byte
		this.loopback.TryTransmit(0x99)

		// Wait for loopback
		Simulation.Wait(TICKS_PER_BIT * 12)

		// Should have data
		assert this.loopback.GetReceiveCount() == 1

		// Clear receive buffer
		this.loopback.ClearReceiveBuffer()

		// Should be empty
		assert this.loopback.GetReceiveCount() == 0
		assert this.loopback.GetReceiveCount() == 0
	}

	/**
	 * Checks the clear transmit buffer case.
	 */
	@Test
	fn TestClearTransmitBuffer()
	{
		// Queue some bytes
		this.loopback.TryTransmit(0x12)
		this.loopback.TryTransmit(0x34)

		// Clear immediately (before transmission completes)
		Simulation.Wait(5)
		this.loopback.ClearTransmitBuffer()

		// Buffer should be cleared
		assert this.loopback.GetTransmitSpace() == 64

		// Wait to see if anything was received
		Simulation.Wait(TICKS_PER_BIT * 25)

		// Should have received at most 1 byte (one that was already transmitting)
		// but likely 0 if cleared fast enough
		var received_count = this.loopback.GetReceiveCount()
		assert received_count <= 1
	}

	/**
	 * Checks the busy signal case.
	 */
	@Test
	fn TestBusySignal()
	{
		// Initially not busy
		assert this.loopback.IsTransmitting() == 0b0

		// Transmit a byte
		this.loopback.TryTransmit(0x77)

		// Wait a tiny bit for transmission to start
		Simulation.Wait(TICKS_PER_BIT * 2)

		// Should be busy during transmission
		assert this.loopback.IsTransmitting() == 0b1

		// Wait for completion
		Simulation.Wait(TICKS_PER_BIT * 12)

		// Should no longer be busy
		assert this.loopback.IsTransmitting() == 0b0
	}

	/**
	 * Checks the large data transfer case.
	 */
	@Test
	fn TestLargeDataTransfer()
	{
		this.loopback.ClearReceiveBuffer()
		this.loopback.ClearTransmitBuffer()

		// Send a sequence of bytes
		this.loopback.TryTransmit(0x00)
		this.loopback.TryTransmit(0x01)
		this.loopback.TryTransmit(0x02)
		this.loopback.TryTransmit(0x03)
		this.loopback.TryTransmit(0x04)
		this.loopback.TryTransmit(0x05)
		this.loopback.TryTransmit(0x06)
		this.loopback.TryTransmit(0x07)
		this.loopback.TryTransmit(0x08)
		this.loopback.TryTransmit(0x09)

		// Wait for all to complete
		Simulation.Wait(TICKS_PER_BIT * 120)

		// Should have all 10 bytes
		assert this.loopback.GetReceiveCount() == 10

		// Verify all received correctly
		var receivedByte6: byte
		assert this.loopback.TryReceive(receivedByte6) == true
		assert receivedByte6 == 0x00
		var receivedByte7: byte
		assert this.loopback.TryReceive(receivedByte7) == true
		assert receivedByte7 == 0x01
		var receivedByte8: byte
		assert this.loopback.TryReceive(receivedByte8) == true
		assert receivedByte8 == 0x02
		var receivedByte9: byte
		assert this.loopback.TryReceive(receivedByte9) == true
		assert receivedByte9 == 0x03
		var receivedByte10: byte
		assert this.loopback.TryReceive(receivedByte10) == true
		assert receivedByte10 == 0x04
		var receivedByte11: byte
		assert this.loopback.TryReceive(receivedByte11) == true
		assert receivedByte11 == 0x05
		var receivedByte12: byte
		assert this.loopback.TryReceive(receivedByte12) == true
		assert receivedByte12 == 0x06
		var receivedByte13: byte
		assert this.loopback.TryReceive(receivedByte13) == true
		assert receivedByte13 == 0x07
		var receivedByte14: byte
		assert this.loopback.TryReceive(receivedByte14) == true
		assert receivedByte14 == 0x08
		var receivedByte15: byte
		assert this.loopback.TryReceive(receivedByte15) == true
		assert receivedByte15 == 0x09

		assert this.loopback.GetReceiveCount() == 0
	}

	/**
	 * Checks the receive empty buffer case.
	 */
	@Test
	fn TestReceiveEmptyBuffer()
	{
		// Try to receive from empty buffer
		assert this.loopback.GetReceiveCount() == 0

		var data: byte

		assert this.loopback.TryReceive(data) == false

		// Should return 0x00 when empty
		assert data == 0x00

		// Should still be empty
		assert this.loopback.GetReceiveCount() == 0
	}
}