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

namespace Livt.IO.Tests

using Livt.IO

/**
 * Exercises UartTransmitter with a shared fixture instance. Each scenario establishes the
 * inputs and state it needs.
 */
@Test
@Context(ClockFrequency = 100MHz)
component UartTransmitterTest
{
	// 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.
	transmitter: UartTransmitter

	// 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

	/**
	 * Creates the shared fixture instances (UartTransmitter). Tests initialize the state
	 * required by their scenario.
	 */
	new()
	{
		this.transmitter = new UartTransmitter(
			this.tx,
			this.tx_dv,
			this.tx_byte,
			this.tx_active,
			this.tx_done
		)
	}

	/**
	 * Checks the transmitter case.
	 */
	@Test
	fn TestTransmitter()
	{
		this.tx_byte = 0xAA
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 10)

		assert this.tx == 0b1 // Idle state

		// Wait for idle state
		Simulation.Wait(10)
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the idle state case.
	 */
	@Test
	fn TestIdleState()
	{
		// Verify TX line is high when idle
		assert this.tx == 0b1
		assert this.tx_active == 0b0
		assert this.tx_done == 0b0

		Simulation.Wait(100)

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

	/**
	 * Checks the start bit case.
	 */
	@Test
	fn TestStartBit()
	{
		this.tx_byte = 0xAA
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// After triggering, should enter start bit state
		Simulation.Wait(5)
		assert this.tx_active == 0b1
		assert this.tx == 0b0 // Start bit is low

		// Should stay low for TICKS_PER_BIT cycles
		Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		assert this.tx == 0b0 // Still in start bit

		// Wait for transmission to complete and return to idle
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 10)
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the data bits transmission case.
	 */
	@Test
	fn TestDataBitsTransmission()
	{
		// Test transmitting 0xAA (10101010 in binary)
		this.tx_byte = 0xAA
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Skip start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// Check each data bit (LSB first)
		// 0xAA = 10101010, so LSB first: 0,1,0,1,0,1,0,1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		assert this.tx == 0b0 // Bit 0

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1 // Bit 1

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b0 // Bit 2

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1 // Bit 3

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b0 // Bit 4

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1 // Bit 5

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b0 // Bit 6

		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1 // Bit 7

		// Wait for stop bit and return to idle
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 2)
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the stop bit case.
	 */
	@Test
	fn TestStopBit()
	{
		this.tx_byte = 0x00
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Skip start bit + 8 data bits
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 9)

		// Should be in stop bit now
		Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		assert this.tx == 0b1 // Stop bit is high
		assert this.tx_active == 0b1 // Still active during stop bit

		// Wait for transmission to complete and return to idle
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the tx done signal case.
	 */
	@Test
	fn TestTxDoneSignal()
	{
		this.tx_byte = 0x55
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Before transmission completes
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 5)
		assert this.tx_done == 0b0

		// Poll for tx_done signal in a window around expected completion
		// Transmission takes: 1 start + 8 data + 1 stop = 10 bit periods
		var done_seen = 0b0

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

		// Verify we saw the tx_done pulse
		assert done_seen == 0b1

		// Verify tx_done returns to 0
		assert this.tx_done == 0b0

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

	/**
	 * Checks the tx active signal case.
	 */
	@Test
	fn TestTxActiveSignal()
	{
		// Initially inactive
		assert this.tx_active == 0b0

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

		// Should be active during transmission
		Simulation.Wait(5)
		assert this.tx_active == 0b1

		// Should remain active throughout
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 5)
		assert this.tx_active == 0b1

		// Complete transmission
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 5)

		// Should go inactive after cleanup
		Simulation.Wait(5)
		assert this.tx_active == 0b0
	}

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

		// Wait for transmission to complete
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 10 + 5)
		assert this.tx == 0b1 // Back to idle
		assert this.tx_active == 0b0

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

		// Should start transmitting again
		Simulation.Wait(5)
		assert this.tx_active == 0b1
		assert this.tx == 0b0 // Start bit

		// Wait for completion
		Simulation.Wait(DEFAULT_TICKS_PER_BIT * 10)
		assert this.tx == 0b1 // Back to idle
	}

	/**
	 * Checks the all zeros case.
	 */
	@Test
	fn TestAllZeros()
	{
		this.tx_byte = 0x00
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Skip start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// All data bits should be 0
		for (var i = 0; i < 8; i = i + 1) {
			Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
			assert this.tx == 0b0
			Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		}

		// Stop bit should be 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		assert this.tx == 0b1

		// Wait for return to idle
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}

	/**
	 * Checks the all ones case.
	 */
	@Test
	fn TestAllOnes()
	{
		this.tx_byte = 0xFF
		this.tx_dv = 0b1
		this.tx_dv = 0b0

		// Skip start bit
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)

		// All data bits should be 1
		for (var i = 0; i < 8; i = i + 1) {
			Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
			assert this.tx == 0b1
			Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		}

		// Stop bit should be 1
		Simulation.Wait(DEFAULT_TICKS_PER_BIT / 2)
		assert this.tx == 0b1

		// Wait for return to idle
		Simulation.Wait(DEFAULT_TICKS_PER_BIT)
		assert this.tx == 0b1
		assert this.tx_active == 0b0
	}
}