Livt packages

Livt.Net

Version 1.4.1

Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.

Published files (163)

tests/EthernetLiteDeviceFixture.lvt

namespace Livt.Net.Tests

using Livt.Net.Drivers.EthernetLite

/**
 * Livt-only register model with independently stalled AXI channels.
 * RX memory deliberately retains bytes after shorter frames. The fixture accepts
 * externally computed FCS values rather than sharing the driver's CRC algorithm.
 */
component EthernetLiteDeviceFixture
{
	public awready: out logic
	public wready: out logic
	public bresp: out logic[2]
	public bvalid: out logic
	public arready: out logic
	public rdata: out logic[32]
	public rresp: out logic[2]
	public rvalid: out logic
	public awaddr: in logic[13]
	public awvalid: in logic
	public wdata: in logic[32]
	public wstrb: in logic[4]
	public wvalid: in logic
	public bready: in logic
	public araddr: in logic[13]
	public arvalid: in logic
	public rready: in logic

	// Borrowed bus and model-visible slave outputs.
	m_axi_arvalid: logic
	m_axi_araddr: logic[13]
	m_axi_rready: logic
	m_axi_awvalid: logic
	m_axi_awaddr: logic[13]
	m_axi_wvalid: logic
	m_axi_wdata: logic[32]
	m_axi_wstrb: logic[4]
	m_axi_bready: logic

	driveArready: logic
	driveRdata: logic[32]
	driveRresp: logic[2]
	driveRvalid: logic
	driveAwready: logic
	driveWready: logic
	driveBresp: logic[2]
	driveBvalid: logic

	// Register model, error injection and immutable completed write trace.
	memory: byte[256]
	ping: bool
	pong: bool
	reads: int
	writes: int
	addresses: int[512]
	values: logic[512, 32]
	readError: int
	writeError: int
	addressFirst: bool
	holdResponses: bool
	holdWriteResponses: bool
	programming: int

	/**
	 * Starts idle with no errors; choose write-channel order before transactions.
	 */
	new()
	{
		this.readError = -1
		this.writeError = -1
	}

	/**
	 * Drives and samples live pins in the same clock domain as the transaction core.
	 */
	process Pins[]()
	{
		this.awready = this.driveAwready
		this.wready = this.driveWready
		this.bresp = this.driveBresp
		this.bvalid = this.driveBvalid
		this.arready = this.driveArready
		this.rdata = this.driveRdata
		this.rresp = this.driveRresp
		this.rvalid = this.driveRvalid
		this.m_axi_awaddr = this.awaddr
		this.m_axi_awvalid = this.awvalid
		this.m_axi_wdata = this.wdata
		this.m_axi_wstrb = this.wstrb
		this.m_axi_wvalid = this.wvalid
		this.m_axi_bready = this.bready
		this.m_axi_araddr = this.araddr
		this.m_axi_arvalid = this.arvalid
		this.m_axi_rready = this.rready
	}

	/**
	 * Returns a register value in the fixture, not a protocol parser result.
	 */
	fn ReadWord(address: int) logic[32]
	{
		if (address == 0x07FC) {
			if (this.programming > 0) {
				this.programming = this.programming - 1
				return 0x00000003
			}

			return 0x00000000
		}

		if (address == 0x17FC) {
			if (this.ping) {
				return 0x00000001
			}

			return 0x00000000
		}

		if (address == 0x1FFC) {
			if (this.pong) {
				return 0x00000001
			}

			return 0x00000000
		}

		var offset = address - 0x1000

		if (address >= 0x1800) {
			offset = address - 0x1800
		}

		assert offset >= 0 && offset + 3 < 256
		var word: logic[32] = 0x00000000

		for (var lane = 0; lane < 4; lane++) {
			var octet = this.memory[offset + lane] as logic[8]

			for (var bit = 0; bit < 8; bit++) {
				word[lane * 8 + bit] = octet[bit]
			}
		}

		return word
	}

	/**
	 * Delays AR and R independently and holds response fields until acceptance.
	 */
	process ReadChannel()
	{
		var address = 0
		var delay = 0

		state Idle {
			if (this.m_axi_arvalid == 0b1) {
				delay = 3
				address = (this.m_axi_araddr as uint) as int
				goto DelayAddress
			} else { goto Idle }
		}

		state DelayAddress {
			assert this.m_axi_arvalid == 0b1
			assert this.m_axi_araddr == (address as logic[13])
			delay = delay - 1

			if (delay == 0) {
				this.driveArready = 0b1;
				goto AcceptAddress
			}
			else {
				goto DelayAddress
			}
		}

		state AcceptAddress {
			this.driveArready = 0b0
			this.reads = this.reads + 1
			goto PrepareResponse
		}

		state PrepareResponse {
			this.driveRdata = this.ReadWord(address)
			this.driveRresp = 0b00

			if (address == this.readError) {
				this.driveRresp = 0b10
			}

			delay = 4
			goto DelayResponse
		}

		state DelayResponse {
			if (this.holdResponses) {
				goto DelayResponse
			}
			else {
				delay = delay - 1

				if (delay == 0) {
					this.driveRvalid = 0b1;
					goto Response
				}
				else {
					goto DelayResponse
				}
			}
		}

		state Response {
			if (this.m_axi_rready == 0b1) {
				this.driveRvalid = 0b0;
				goto Idle
			}
			else {
				goto Response
			}
		}
	}

	/**
	 * Accepts AW/W in either order, delays B and checks stability during stalls.
	 */
	process WriteChannels()
	{
		var address = 0
		var word: logic[32] = 0x00000000
		var delay = 0

		state Idle {
			if (this.m_axi_awvalid == 0b1 && this.m_axi_wvalid == 0b1) {
				address = (this.m_axi_awaddr as uint) as int
				word = this.m_axi_wdata
				delay = 3
				goto DelayFirst
			} else { goto Idle }
		}

		state DelayFirst {
			assert this.m_axi_awaddr == (address as logic[13])
			assert this.m_axi_wdata == word
			assert this.m_axi_awvalid == 0b1 && this.m_axi_wvalid == 0b1
			assert this.m_axi_wstrb == 0b1111
			delay = delay - 1

			if (delay == 0) {
				if (this.addressFirst) {
					this.driveAwready = 0b1
				}
				else {
					this.driveWready = 0b1
				}

				goto AcceptFirst
			} else { goto DelayFirst }
		}

		state AcceptFirst {
			this.driveAwready = 0b0
			this.driveWready = 0b0
			delay = 5
			goto DelaySecond
		}

		state DelaySecond {
			if (this.addressFirst) {
				assert this.m_axi_awvalid == 0b0
				assert this.m_axi_wvalid == 0b1
				assert this.m_axi_wdata == word
			} else {
				assert this.m_axi_wvalid == 0b0
				assert this.m_axi_awvalid == 0b1
				assert this.m_axi_awaddr == (address as logic[13])
			}
			delay = delay - 1

			if (delay == 0) {
				if (this.addressFirst) {
					this.driveWready = 0b1
				}
				else {
					this.driveAwready = 0b1
				}

				goto AcceptSecond
			} else { goto DelaySecond }
		}

		state AcceptSecond {
			this.driveAwready = 0b0
			this.driveWready = 0b0
			assert this.writes < 512
			this.addresses[this.writes] = address
			this.values[this.writes] = word
			this.writes = this.writes + 1
			this.driveBresp = 0b00

			if (address == this.writeError) {
				this.driveBresp = 0b11
			}
			else {
				if (address == 0x17FC) {
					this.ping = false
				}

				if (address == 0x1FFC) {
					this.pong = false
				}

				if (address == 0x07FC && word == 0x00000003) {
					this.programming = 2
				}
			}

			delay = 7
			goto DelayResponse
		}

		state DelayResponse {
			if (this.holdResponses || this.holdWriteResponses) {
				goto DelayResponse
			}
			else {
				delay = delay - 1

				if (delay == 0) {
					this.driveBvalid = 0b1;
					goto Response
				}
				else {
					goto DelayResponse
				}
			}
		}

		state Response {
			if (this.m_axi_bready == 0b1) {
				this.driveBvalid = 0b0;
				goto Idle
			}
			else {
				goto Response
			}
		}
	}

	/**
	 * Selects the independent write-channel acceptance order.
	 */
	public fn SetAddressFirst(value: bool)
	{
		this.addressFirst = value
	}

	/**
	 * Holds future responses while addresses/data may already have been accepted.
	 */
	public fn HoldResponses(value: bool)
	{
		this.holdResponses = value
	}

	/**
	 * Holds B responses independently so a submitted frame can enter the write path.
	 */
	public fn HoldWriteResponses(value: bool)
	{
		this.holdWriteResponses = value
	}

	/**
	 * Injects SLVERR at a read address; -1 disables injection.
	 */
	public fn SetReadError(address: int)
	{
		this.readError = address
	}

	/**
	 * Injects DECERR at a write address; -1 disables injection.
	 */
	public fn SetWriteError(address: int)
	{
		this.writeError = address
	}

	/**
	 * Reports accepted write count, including injected failures.
	 */
	public fn GetWriteCount() int
	{
		return this.writes
	}

	/**
	 * Reads a completed trace address.
	 */
	public fn GetWriteAddress(index: int) int
	{
		return this.addresses[index]
	}

	/**
	 * Reads a completed trace value.
	 */
	public fn GetWriteValue(index: int) logic[32]
	{
		return this.values[index]
	}

	/**
	 * Prepares increasing octets plus an independent precomputed FCS; retains tail RAM.
	 */
	public fn QueueFrame(length: int, fcs: byte[4], pong: bool)
	{
		assert !this.ping && !this.pong

		for (var i = 0; i < length; i++) {
			this.memory[i] = i as byte
		}

		for (var i = 0; i < 4; i++) {
			this.memory[length + i] = fcs[i]
		}

		if (pong) {
			this.pong = true
		}
		else {
			this.ping = true
		}
	}
}