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/i2c/I2cMasterTest.lvt

namespace Livt.IO.Tests

using Livt.IO

/**
 * Test bus adapter that exposes observed line levels and low-drive requests without
 * physical board pins.
 */
component I2CMasterTestBus : I2CBus
{
	// Shared fixture instances, signals, and observation state.
	observed_scl: logic
	observed_sda: logic

	/**
	 * Initializes fixture state and constructor bindings. Test cases share the fixture
	 * instance rather than constructing a fresh subject for each method.
	 */
	new()
	{
		this.observed_scl = 0b1
		this.observed_sda = 0b1
	}

	/**
	 * Publishes the fixture SCL/SDA levels in the test clock context.
	 */
	process ApplyObservedLines()
	{
		state {
			this.scl = this.observed_scl
			this.sda = this.observed_sda
		}
	}

	/**
	 * Sets the SCL/SDA levels that the fixture will present to the component under test.
	 */
	public fn SetObservedLines(sclValue: logic, sdaValue: logic)
	{
		state {
			this.observed_scl = sclValue
			this.observed_sda = sdaValue
		}
	}
}

/**
 * Exercises I2CMaster with a shared fixture instance. Each scenario establishes the inputs
 * and state it needs.
 */
@Test
component I2CMasterTest
{
	// Shared fixture instances, signals, and observation state.
	bus: I2CMasterTestBus
	master: I2CMaster

	/**
	 * Creates the shared fixture instances (I2CMasterTestBus, I2CMaster). Tests initialize the
	 * state required by their scenario.
	 */
	new()
	{
		this.bus = new I2CMasterTestBus()
		this.master = new I2CMaster(this.bus)
	}

	/**
	 * Waits until the master is no longer busy; the test runner timeout bounds a stalled
	 * command.
	 */
	fn WaitForCommand()
	{
		while (this.master.IsBusy()) {
			state {
			}
		}
	}

	/**
	 * Checks the busy rejects second command case.
	 */
	@Test
	fn TestBusyRejectsSecondCommand()
	{
		this.bus.SetObservedLines(0b1, 0b1)

		var started: bool = this.master.BeginStart()
		var rejected: bool = this.master.BeginStop()

		assert started == true
		assert rejected == false
		assert this.master.IsBusy() == true

		this.WaitForCommand()

		assert this.master.IsBusy() == false
		assert this.master.HasResult() == true

		this.master.ClearResult()
		assert this.master.HasResult() == false
	}

	/**
	 * Checks that a byte write without a responding target completes with NACK.
	 */
	@Test
	fn TestWriteWithoutTargetReportsNack()
	{
		this.bus.SetObservedLines(0b1, 0b1)

		var accepted: bool = this.master.BeginWriteByte(0x55)
		assert accepted == true

		this.WaitForCommand()

		assert this.master.HasResult() == true
		assert this.master.WasAckReceived() == false
		assert this.master.WasNackReceived() == true
	}

	/**
	 * Checks that an external target ACK is retained after the byte write.
	 */
	@Test
	fn TestWriteWithExternalAckReportsAck()
	{
		this.bus.SetObservedLines(0b1, 0b0)

		var accepted: bool = this.master.BeginWriteByte(0x55)
		assert accepted == true

		this.WaitForCommand()

		assert this.master.HasResult() == true
		assert this.master.WasAckReceived() == true
		assert this.master.WasNackReceived() == false
	}

	/**
	 * Checks that BeginWriteAddress sends shifted write address.
	 */
	@Test
	fn TestBeginWriteAddressSendsShiftedWriteAddress()
	{
		this.bus.SetObservedLines(0b1, 0b0)

		var accepted: bool = this.master.BeginWriteAddress(0x42)
		assert accepted == true

		this.WaitForCommand()

		assert this.master.HasResult() == true
		assert this.master.WasAckReceived() == true
	}

	/**
	 * Checks that BeginReadAddress sends shifted read address.
	 */
	@Test
	fn TestBeginReadAddressSendsShiftedReadAddress()
	{
		this.bus.SetObservedLines(0b1, 0b0)

		var accepted: bool = this.master.BeginReadAddress(0x42)
		assert accepted == true

		this.WaitForCommand()

		assert this.master.HasResult() == true
		assert this.master.WasAckReceived() == true
	}

	/**
	 * Checks the address helpers reject invalid seven bit address case.
	 */
	@Test
	fn TestAddressHelpersRejectInvalidSevenBitAddress()
	{
		this.bus.SetObservedLines(0b1, 0b1)
		this.master.ClearResult()

		assert this.master.BeginWriteAddress(0x80) == false
		assert this.master.BeginReadAddress(0x80) == false
		assert this.master.IsBusy() == false
		assert this.master.HasResult() == false
	}

	/**
	 * Checks that Read released line returns all ones.
	 */
	@Test
	fn TestReadReleasedLineReturnsAllOnes()
	{
		this.bus.SetObservedLines(0b1, 0b1)

		var accepted: bool = this.master.BeginReadByte(false)
		assert accepted == true

		this.WaitForCommand()

		assert this.master.HasResult() == true
		assert this.master.GetReadByte() == 0xFF
	}

	/**
	 * Checks the clock stretch keeps master busy until scl released case.
	 */
	@Test
	fn TestClockStretchKeepsMasterBusyUntilSclReleased()
	{
		this.bus.SetObservedLines(0b0, 0b1)

		var accepted: bool = this.master.BeginStop()
		assert accepted == true

		Simulation.Wait(this.master.HalfPeriodTicks() * 2)
		assert this.master.IsBusy() == true

		this.bus.SetObservedLines(0b1, 0b1)
		this.WaitForCommand()

		assert this.master.IsBusy() == false
		assert this.master.HasResult() == true
	}
}