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/I2cBusCombinerTest.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 TestI2CBusProvider : 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
		}
	}

	/**
	 * Returns the observed SCL low-drive request without changing the bus.
	 */
	public fn ReadSclDriveLow() logic
	{
		return this.scl_drive_low
	}

	/**
	 * Returns the observed SDA low-drive request without changing the bus.
	 */
	public fn ReadSdaDriveLow() logic
	{
		return this.sda_drive_low
	}
}

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

	/**
	 * Initializes fixture state and constructor bindings. Test cases share the fixture
	 * instance rather than constructing a fresh subject for each method.
	 */
	new(bus: flip I2CBus)
	{
		this.bus = bus
		this.scl_drive_low = 0b0
		this.sda_drive_low = 0b0
	}

	/**
	 * Copies the requested low-drive flags to the borrowed bus attachment in the test clock
	 * context.
	 */
	process ApplyDrive()
	{
		state {
			this.bus.scl_drive_low = this.scl_drive_low
			this.bus.sda_drive_low = this.sda_drive_low
		}
	}

	/**
	 * Sets both fixture low-drive requests together; the drive process forwards them to the
	 * attachment.
	 */
	public fn SetDrive(sclLow: logic, sdaLow: logic)
	{
		state {
			this.scl_drive_low = sclLow
			this.sda_drive_low = sdaLow
		}
	}

	/**
	 * Returns the logical SCL level observed at the borrowed attachment.
	 */
	public fn ReadScl() logic
	{
		return this.bus.scl
	}

	/**
	 * Returns the logical SDA level observed at the borrowed attachment.
	 */
	public fn ReadSda() logic
	{
		return this.bus.sda
	}
}

/**
 * Exercises I2CBusCombiner with a shared fixture instance. Each scenario establishes the
 * inputs and state it needs.
 */
@Test
component I2CBusCombinerTest
{
	// Shared fixture instances, signals, and observation state.
	root: TestI2CBusProvider
	controller: AttachmentProbe
	target: AttachmentProbe
	combiner: I2CBusCombiner

	/**
	 * Creates the shared fixture instances (TestI2CBusProvider, I2CBusCombiner,
	 * AttachmentProbe). Tests initialize the state required by their scenario.
	 */
	new()
	{
		this.root = new TestI2CBusProvider()
		this.combiner = new I2CBusCombiner(this.root)
		this.controller = new AttachmentProbe(this.combiner.controller)
		this.target = new AttachmentProbe(this.combiner.target)
	}

	/**
	 * Checks the released devices release upstream case.
	 */
	@Test
	fn TestReleasedDevicesReleaseUpstream()
	{
		this.root.SetObservedLines(0b1, 0b1)
		this.controller.SetDrive(0b0, 0b0)
		this.target.SetDrive(0b0, 0b0)
		Simulation.Wait(80)

		assert this.root.ReadSclDriveLow() == 0b0
		assert this.root.ReadSdaDriveLow() == 0b0
	}

	/**
	 * Checks the either device can drive low case.
	 */
	@Test
	fn TestEitherDeviceCanDriveLow()
	{
		this.root.SetObservedLines(0b1, 0b1)
		this.controller.SetDrive(0b1, 0b0)
		this.target.SetDrive(0b0, 0b1)
		Simulation.Wait(80)

		assert this.root.ReadSclDriveLow() == 0b1
		assert this.root.ReadSdaDriveLow() == 0b1
	}

	/**
	 * Checks the observed lines propagate to both attachments case.
	 */
	@Test
	fn TestObservedLinesPropagateToBothAttachments()
	{
		this.controller.SetDrive(0b0, 0b0)
		this.target.SetDrive(0b0, 0b0)
		Simulation.Wait(400)
		this.root.SetObservedLines(0b0, 0b1)
		Simulation.Wait(400)

		assert this.controller.ReadScl() == 0b0
		assert this.controller.ReadSda() == 0b1
		assert this.target.ReadScl() == 0b0
		assert this.target.ReadSda() == 0b1
	}
}