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

namespace Livt.IO.Tests

using Livt.IO

/**
 * Exercises BufferedUart with a shared fixture instance. Each scenario establishes the
 * inputs and state it needs.
 */
@Test
@Context(ClockFrequency = 100MHz)
component BufferedUartTest
{
	// Independent default-baud timing tolerates scheduled serial-stimulus overhead.
	const TICKS_PER_BIT: int = 868

	// Shared fixture instances, signals, and observation state.
	controller: BufferedUart<3, 3>

	// Shared fixture instances, signals, and observation state.
	loopback_uart: BufferedUart<3, 3>

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

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

	// Shared fixture instances, signals, and observation state.
	loopback_rx: logic = 0b1

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

	/**
	 * Creates the shared fixture instances (BufferedUart). Tests initialize the state required
	 * by their scenario.
	 */
	new()
	{
		this.controller = new BufferedUart<3, 3>(this.rx, this.tx)

		this.loopback_rx = this.loopback_tx
		this.loopback_uart = new BufferedUart<3, 3>(this.loopback_rx, this.loopback_tx)
	}

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

	/**
	 * Checks the transmit single byte case.
	 */
	@Test
	fn TestTransmitSingleByte()
	{
		// Queue a byte for transmission
		var result = this.controller.TryTransmit(0xAA)
		assert result == true // Should succeed

		// Wait for automatic transmission to start and complete
		Simulation.Wait(TICKS_PER_BIT * 15)

		// Check transmission occurred (tx line should be back to idle)
		assert this.tx == 0b1

		// Buffer should be empty again
		Simulation.Wait(10)
		assert this.controller.GetTransmitSpace() == 3
	}

	/**
	 * Checks the transmit multiple bytes case.
	 */
	@Test
	fn TestTransmitMultipleBytes()
	{
		// Queue multiple bytes
		assert this.controller.TryTransmit(0x12) == 0b1
		assert this.controller.TryTransmit(0x34) == 0b1
		assert this.controller.TryTransmit(0x56) == 0b1

		// Check buffer usage
		assert this.controller.GetTransmitSpace() > 0

		// Wait for all transmissions to complete
		// Each byte takes ~10 bit periods
		Simulation.Wait(TICKS_PER_BIT * 35)

		// All should be transmitted
		assert this.tx == 0b1
		Simulation.Wait(10)
		assert this.controller.GetTransmitSpace() == 3
	}

	/**
	 * Checks the transmit buffer full case.
	 */
	@Test
	fn TestTransmitBufferFull()
	{
		// Fill the buffer in a tight loop (faster than transmission)
		for (var i = 0; i < 3 + 1; i++) {
			var result = this.controller.TryTransmit(0x55)
			assert result == 0b1
		}

		// Buffer should be full
		assert this.controller.GetTransmitSpace() == 0

		// Buffer should be full NOW (first byte still transmitting)
		// Try immediately without waiting
		var overflow_result = this.controller.TryTransmit(0xFF)
		assert overflow_result == 0b0

		// Wait for buffer to drain completely
		Simulation.Wait(TICKS_PER_BIT * ((3 + 2) * 12))
		assert this.controller.GetTransmitSpace() == 3
	}

	/**
	 * Checks the receive single byte case.
	 */
	@Test
	fn TestReceiveSingleByte()
	{
		// Initially no data available
		assert this.controller.GetReceiveCount() == 0

		// Send a byte on RX line (0x55)
		this.rx = 0b1
		Simulation.Wait(100)

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

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

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

		// Data should be available
		assert this.controller.GetReceiveCount() > 0
		assert this.controller.GetReceiveCount() == 1

		// Receive the byte
		var data: byte
		assert this.controller.TryReceive(data) == true
		assert data == 0x55

		// No more data available
		assert this.controller.GetReceiveCount() == 0
		assert this.controller.GetReceiveCount() == 0

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

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

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

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

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

		// Send second byte (0x55)
		this.rx = 0b0 // Start bit
		Simulation.Wait(TICKS_PER_BIT)

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

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

		// Should have 2 bytes available
		assert this.controller.GetReceiveCount() == 2

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

		var byte2: byte

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

		// Buffer should be empty
		assert this.controller.GetReceiveCount() == 0

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

	/**
	 * Checks the receive buffer full case.
	 */
	@Test
	fn TestReceiveBufferFull()
	{
		// Fill receive buffer by sending BUFFER_SIZE bytes
		this.rx = 0b1
		Simulation.Wait(100)

		for (var i = 0; i < 3; i = i + 1) {
			// Send a byte (value = i)
			this.rx = 0b0 // Start bit
			Simulation.Wait(TICKS_PER_BIT)

			// Send 8 data bits (all zeros for simplicity)
			for (var j = 0; j < 8; j = j + 1) {
				this.rx = 0b0
				Simulation.Wait(TICKS_PER_BIT)
			}

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

		// Buffer should be full
		assert this.controller.GetReceiveCount() == 3

		// Send one more byte - should be dropped
		this.rx = 0b0 // Start bit
		Simulation.Wait(TICKS_PER_BIT)

		for (var j = 0; j < 8; j = j + 1) {
			this.rx = 0b1 // Send 0xFF
			Simulation.Wait(TICKS_PER_BIT)
		}

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

		// Still should have only BUFFER_SIZE bytes
		assert this.controller.GetReceiveCount() == 3

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

	/**
	 * Checks the frame error does not queue data case.
	 */
	@Test
	fn TestFrameErrorDoesNotQueueData()
	{
		this.controller.ClearReceiveBuffer()
		this.controller.ClearErrors()

		this.rx = 0b1
		Simulation.Wait(100)

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

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

		this.rx = 0b0 // Invalid stop bit
		Simulation.Wait(TICKS_PER_BIT * 2)

		assert this.controller.GetReceiveCount() == 0
		assert this.controller.GetFrameErrorCount() > 0
		assert this.controller.GetFrameErrorCount() == 1

		this.rx = 0b1
		Simulation.Wait(TICKS_PER_BIT * 2)

		// Send a valid frame to confirm recovery.
		this.rx = 0b0 // Start bit
		Simulation.Wait(TICKS_PER_BIT)

		// Send 0x3C = 00111100, LSB first: 0,0,1,1,1,1,0,0
		this.rx = 0b0 // Bit 0
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b0 // Bit 1
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b1 // Bit 2
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b1 // Bit 3
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b1 // Bit 4
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b1 // Bit 5
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b0 // Bit 6
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b0 // Bit 7
		Simulation.Wait(TICKS_PER_BIT)
		this.rx = 0b1 // Stop bit
		Simulation.Wait(TICKS_PER_BIT * 2)

		assert this.controller.GetReceiveCount() == 1
		var receivedByte1: byte
		assert this.controller.TryReceive(receivedByte1) == true
		assert receivedByte1 == 0x3C
		assert this.controller.GetFrameErrorCount() == 1
	}

	/**
	 * Checks the clear receive buffer case.
	 */
	@Test
	fn TestClearReceiveBuffer()
	{
		this.controller.ClearReceiveBuffer()

		// Send a byte
		this.rx = 0b1
		Simulation.Wait(100)

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

		for (var i = 0; i < 8; i = i + 1) {
			this.rx = 0b1
			Simulation.Wait(TICKS_PER_BIT)
		}

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

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

		// Clear buffer
		this.controller.ClearReceiveBuffer()

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

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

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

		// Wait a tiny bit (not enough to transmit)
		Simulation.Wait(10)

		// Clear transmit buffer
		this.controller.ClearTransmitBuffer()

		// Buffer should be empty
		assert this.controller.GetTransmitSpace() == 3

		// Wait to ensure nothing transmits
		Simulation.Wait(TICKS_PER_BIT * 25)

		// Should still be idle
		assert this.tx == 0b1
	}

	/**
	 * Checks the receive empty buffer case.
	 */
	@Test
	fn TestReceiveEmptyBuffer()
	{
		this.controller.ClearReceiveBuffer()

		// Try to receive from empty buffer
		assert this.controller.GetReceiveCount() == 0

		var data: byte

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

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

		// Buffer should still be empty
		assert this.controller.GetReceiveCount() == 0
	}

	/**
	 * Checks the clear frame errors case.
	 */
	@Test
	fn TestClearFrameErrors()
	{
		this.controller.ClearErrors()

		// Create a framing error.
		this.rx = 0b1
		Simulation.Wait(100)
		this.rx = 0b0 // Start bit
		Simulation.Wait(TICKS_PER_BIT)

		for (var i = 0; i < 8; i = i + 1) {
			this.rx = 0b0
			Simulation.Wait(TICKS_PER_BIT)
		}

		this.rx = 0b0 // Invalid stop bit
		Simulation.Wait(TICKS_PER_BIT * 2)

		assert this.controller.GetFrameErrorCount() > 0
		assert this.controller.GetFrameErrorCount() == 1

		this.controller.ClearErrors()

		assert this.controller.GetFrameErrorCount() == 0
		assert this.controller.GetFrameErrorCount() == 0

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

	/**
	 * Checks the loopback transmit receive case.
	 */
	@Test
	fn TestLoopbackTransmitReceive()
	{
		this.loopback_uart.ClearReceiveBuffer()
		this.loopback_uart.ClearTransmitBuffer()

		// Transmit a byte
		this.loopback_uart.TryTransmit(0x42)

		// Wait for transmission to start
		Simulation.Wait(TICKS_PER_BIT * 9)

		// Wait a bit more for reception to complete
		Simulation.Wait(TICKS_PER_BIT * 2)

		// Should have received the byte
		assert this.loopback_uart.GetReceiveCount() > 0
		var received: byte
		assert this.loopback_uart.TryReceive(received) == true
		assert received == 0x42
	}
}