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/I2cIntegrationTest.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 ResolvedI2CTestBus : I2CBus
{
	/**
	 * Initializes fixture state and constructor bindings. Test cases share the fixture
	 * instance rather than constructing a fresh subject for each method.
	 */
	new()
	{
		this.scl = 0b1
		this.sda = 0b1
	}

	/**
	 * Models pull-ups: a low-drive request resolves low and a released line resolves high.
	 */
	process ResolveLines()
	{
		state {
			if (this.scl_drive_low == 0b1) {
				this.scl = 0b0
			} else {
				this.scl = 0b1
			}

			if (this.sda_drive_low == 0b1) {
				this.sda = 0b0
			} else {
				this.sda = 0b1
			}
		}
	}
}

/**
 * Exercises I2CIntegration with a shared fixture instance. Each scenario establishes the
 * inputs and state it needs.
 */
@Test
component I2CIntegrationTest
{
	// Shared fixture instances, signals, and observation state.
	bus: ResolvedI2CTestBus
	combiner: I2CBusCombiner
	master: I2CMaster
	slave: I2CSlave

	/**
	 * Creates the shared fixture instances (ResolvedI2CTestBus, I2CBusCombiner, I2CMaster,
	 * I2CSlave). Tests initialize the state required by their scenario.
	 */
	new()
	{
		this.bus = new ResolvedI2CTestBus()
		this.combiner = new I2CBusCombiner(this.bus)
		this.master = new I2CMaster(this.combiner.controller)
		this.slave = new I2CSlave(this.combiner.target, 0x42)
	}

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

	/**
	 * Asserts START acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStart()
	{
		var accepted: bool = this.master.BeginStart()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Asserts STOP acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStop()
	{
		var accepted: bool = this.master.BeginStop()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Sends a seven-bit write address, asserts acceptance/completion, and returns the ACK flag
	 * before clearing status.
	 */
	fn RunWriteAddress(address: byte) bool
	{
		var accepted: bool = this.master.BeginWriteAddress(address)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Sends a seven-bit read address, asserts acceptance/completion, and returns the ACK flag
	 * before clearing status.
	 */
	fn RunReadAddress(address: byte) bool
	{
		var accepted: bool = this.master.BeginReadAddress(address)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Sends one data byte, asserts acceptance/completion, and returns the ACK flag before
	 * clearing status.
	 */
	fn RunWriteByte(value: byte) bool
	{
		var accepted: bool = this.master.BeginWriteByte(value)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Receives one byte with the requested controller ACK/NACK, checks completion, and clears
	 * status before returning the byte.
	 */
	fn RunReadByte(sendAck: bool) byte
	{
		var accepted: bool = this.master.BeginReadByte(sendAck)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var value: byte = this.master.GetReadByte()
		this.master.ClearResult()
		return value
	}

	/**
	 * Checks the master writes byte to slave case.
	 */
	@Test
	fn TestMasterWritesByteToSlave()
	{
		this.slave.ClearAddressMatch()
		this.slave.ClearReceivedByte()

		this.RunStart()
		var addressAck: bool = this.RunWriteAddress(0x42)
		assert this.slave.HasAddressMatch() == true
		var dataAck: bool = this.RunWriteByte(0xA5)
		this.RunStop()

		assert addressAck == true
		assert dataAck == true
		assert this.slave.HasAddressMatch() == true
		assert this.slave.WasWriteAddressed() == true
		assert this.slave.HasReceivedByte() == true
		assert this.slave.GetReceivedByte() == 0xA5
	}
}

/**
 * Exercises I2CReadIntegration with a shared fixture instance. Each scenario establishes
 * the inputs and state it needs.
 */
@Test
component I2CReadIntegrationTest
{
	// Shared fixture instances, signals, and observation state.
	bus: ResolvedI2CTestBus
	combiner: I2CBusCombiner
	master: I2CMaster
	slave: I2CSlave

	/**
	 * Creates the shared fixture instances (ResolvedI2CTestBus, I2CBusCombiner, I2CMaster,
	 * I2CSlave). Tests initialize the state required by their scenario.
	 */
	new()
	{
		this.bus = new ResolvedI2CTestBus()
		this.combiner = new I2CBusCombiner(this.bus)
		this.master = new I2CMaster(this.combiner.controller)
		this.slave = new I2CSlave(this.combiner.target, 0x42)
	}

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

	/**
	 * Asserts START acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStart()
	{
		var accepted: bool = this.master.BeginStart()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Asserts STOP acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStop()
	{
		var accepted: bool = this.master.BeginStop()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Sends a seven-bit read address, asserts acceptance/completion, and returns the ACK flag
	 * before clearing status.
	 */
	fn RunReadAddress(address: byte) bool
	{
		var accepted: bool = this.master.BeginReadAddress(address)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Receives one byte with the requested controller ACK/NACK, checks completion, and clears
	 * status before returning the byte.
	 */
	fn RunReadByte(sendAck: bool) byte
	{
		var accepted: bool = this.master.BeginReadByte(sendAck)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var value: byte = this.master.GetReadByte()
		this.master.ClearResult()
		return value
	}

	/**
	 * Checks the master reads byte from slave case.
	 */
	@Test
	fn TestMasterReadsByteFromSlave()
	{
		this.slave.ClearAddressMatch()
		this.slave.ClearReadRequested()
		this.slave.ClearTransmittedByte()
		this.slave.SetTransmitByte(0x5A)

		this.RunStart()
		var addressAck: bool = this.RunReadAddress(0x42)
		var value: byte = this.RunReadByte(false)
		this.RunStop()

		assert addressAck == true
		assert this.slave.HasAddressMatch() == true
		assert this.slave.WasReadAddressed() == true
		assert value == 0x5A
		assert this.slave.HasTransmittedByte() == true
		assert this.slave.WasTransmitAcked() == false
	}
}

/**
 * Exercises I2CRegisterIntegration with a shared fixture instance. Each scenario
 * establishes the inputs and state it needs.
 */
@Test
component I2CRegisterIntegrationTest
{
	// Shared fixture instances, signals, and observation state.
	bus: ResolvedI2CTestBus
	combiner: I2CBusCombiner
	master: I2CMaster
	registers: I2CRegisterSlave

	/**
	 * Creates the shared fixture instances (ResolvedI2CTestBus, I2CBusCombiner, I2CMaster,
	 * I2CRegisterSlave). Tests initialize the state required by their scenario.
	 */
	new()
	{
		this.bus = new ResolvedI2CTestBus()
		this.combiner = new I2CBusCombiner(this.bus)
		this.master = new I2CMaster(this.combiner.controller)
		this.registers = new I2CRegisterSlave(this.combiner.target, 0x42)
	}

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

	/**
	 * Asserts START acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStart()
	{
		var accepted: bool = this.master.BeginStart()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Asserts STOP acceptance, waits for completion, and clears the retained master result.
	 */
	fn RunStop()
	{
		var accepted: bool = this.master.BeginStop()
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		this.master.ClearResult()
	}

	/**
	 * Sends a seven-bit write address, asserts acceptance/completion, and returns the ACK flag
	 * before clearing status.
	 */
	fn RunWriteAddress(address: byte) bool
	{
		var accepted: bool = this.master.BeginWriteAddress(address)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Sends a seven-bit read address, asserts acceptance/completion, and returns the ACK flag
	 * before clearing status.
	 */
	fn RunReadAddress(address: byte) bool
	{
		var accepted: bool = this.master.BeginReadAddress(address)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Sends one data byte, asserts acceptance/completion, and returns the ACK flag before
	 * clearing status.
	 */
	fn RunWriteByte(value: byte) bool
	{
		var accepted: bool = this.master.BeginWriteByte(value)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var acked: bool = this.master.WasAckReceived()
		this.master.ClearResult()
		return acked
	}

	/**
	 * Receives one byte with the requested controller ACK/NACK, checks completion, and clears
	 * status before returning the byte.
	 */
	fn RunReadByte(sendAck: bool) byte
	{
		var accepted: bool = this.master.BeginReadByte(sendAck)
		assert accepted == true
		this.WaitForCommand()
		assert this.master.HasResult() == true
		var value: byte = this.master.GetReadByte()
		this.master.ClearResult()
		return value
	}

	/**
	 * Checks the master writes then reads register case.
	 */
	@Test
	fn TestMasterWritesThenReadsRegister()
	{
		this.RunStart()
		assert this.RunWriteAddress(0x42) == true
		assert this.RunWriteByte(0x10) == true
		assert this.RunWriteByte(0x5A) == true
		assert this.RunWriteByte(0xC3) == true
		this.RunStop()

		assert this.registers.GetRegister(0x10) == 0x5A
		assert this.registers.GetRegister(0x11) == 0xC3

		this.RunStart()
		assert this.RunWriteAddress(0x42) == true
		assert this.RunWriteByte(0x10) == true
		assert this.registers.GetPointer() == 0x10
		this.RunStart()
		assert this.RunReadAddress(0x42) == true
		var first: byte = this.RunReadByte(true)
		var second: byte = this.RunReadByte(false)
		this.RunStop()

		assert first == 0x5A
		assert second == 0xC3
		assert this.registers.GetPointer() == 0x11
	}
}