Livt packages

Livt.Net

Version 1.4.1

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

Published files (163)

src/ethernet/EthernetFrame.lvt

namespace Livt.Net

/**
 * Ethernet II envelope over a prepared payload; FCS is supplied by the MAC.
 * Prepare the child first, then this envelope. Keep the child unchanged during
 * emission. Use one serialized application owner in the same clock context.
 */
component EthernetFrame<P: IPacketData & IEthernetPayload, MAX_PAYLOAD_LENGTH: int = 1500> : IPacketData
{
	static assert MAX_PAYLOAD_LENGTH > 0 && MAX_PAYLOAD_LENGTH <= 65535, "Invalid Ethernet payload limit"

	// Borrowed prepared payload; must remain stable until emission finishes.
	payload: P

	// Owned Ethernet header and prepared payload/frame lengths.
	const PAYLOAD_BITS: int = Bits.RequiredFor(MAX_PAYLOAD_LENGTH)
	const FRAME_BITS: int = Bits.RequiredFor(MAX_PAYLOAD_LENGTH + 60)
	payloadLength: logic[PAYLOAD_BITS]
	length: logic[FRAME_BITS]
	source0: logic[16]
	source1: logic[16]
	source2: logic[16]
	destination0: logic[16]
	destination1: logic[16]
	destination2: logic[16]
	typeWord: logic[16]
	ready: bool

	/**
	 * Borrows the fixed payload; starts unprepared and performs no payload reads.
	 */
	new(payload: P)
	{
		this.payload = payload
	}

	/**
	 * Invalidates this envelope without changing the child.
	 */
	public fn Invalidate()
	{
		ready = false;
		length = 0;
		payloadLength = 0
	}

	/**
	 * Addresses use network byte order. Failure invalidates any previous frame.
	 */
	public fn TryPrepare(sourceMac: byte[6], destinationMac: byte[6]) PacketDataResult
	{
		this.Invalidate()

		if (!payload.IsReady()) {
			return PacketDataResult.NotReady
		}

		var size = payload.GetAvailableLength()
		var etherType = payload.GetEtherType()

		if (size <= 0 || size > MAX_PAYLOAD_LENGTH) {
			return PacketDataResult.Invalid
		}

		if (etherType < 1536 || etherType > 65535) {
			return PacketDataResult.Invalid
		}

		source0 = NetworkOrder.Word(sourceMac[0], sourceMac[1]) as logic[16]
		source1 = NetworkOrder.Word(sourceMac[2], sourceMac[3]) as logic[16]
		source2 = NetworkOrder.Word(sourceMac[4], sourceMac[5]) as logic[16]
		destination0 = NetworkOrder.Word(destinationMac[0], destinationMac[1]) as logic[16]
		destination1 = NetworkOrder.Word(destinationMac[2], destinationMac[3]) as logic[16]
		destination2 = NetworkOrder.Word(destinationMac[4], destinationMac[5]) as logic[16]
		typeWord = etherType as logic[16]
		payloadLength = size as logic[PAYLOAD_BITS]
		var frameLength = 14 + size
		if (frameLength < 60) { frameLength = 60 }
		length = frameLength as logic[FRAME_BITS]

		ready = true
		return PacketDataResult.Success
	}

	/**
	 * Reports prepared metadata; the owner must still keep all borrowed payloads stable.
	 */
	override fn IsReady() bool
	{
		return ready
	}

	/**
	 * Returns the prepared packet length in bytes, or zero while unprepared.
	 */
	override fn GetAvailableLength() int
	{
		if (!ready) {
			return 0
		}

		return (length as uint) as int
	}

	/**
	 * Reads a packet-relative byte; returns NotReady while unprepared and Invalid
	 * outside the prepared length. Delegated payload errors propagate; the output
	 * is data only on Success. Keep the prepared graph stable throughout the call.
	 * Bytes 0..13 are the Ethernet header; payload follows, then zero padding to
	 * the prepared frame length. Preamble and FCS are excluded.
	 */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

		if (!ready) {
			return PacketDataResult.NotReady
		}

		if (index < 0 || index >= ((length as uint) as int)) {
			return PacketDataResult.Invalid
		}

		if (index < 14) {
			value = this.EthernetHeaderByte(index, source0, source1, source2, destination0, destination1, destination2, typeWord);
			return PacketDataResult.Success
		}

		if (index < 14 + ((payloadLength as uint) as int)) {
			return payload.TryRead(index - 14, value)
		}

		return PacketDataResult.Success
	}

	/** Pure byte selection; no additional scheduled transaction. */
	inline fn EthernetHeaderByte[](index: int, snapshotsource0: logic[16], snapshotsource1: logic[16], snapshotsource2: logic[16], snapshotdestination0: logic[16], snapshotdestination1: logic[16], snapshotdestination2: logic[16], snapshottypeWord: logic[16]) byte
	{
		if (index == 0) { return (snapshotdestination0 >> 8) as byte }
		if (index == 1) { return snapshotdestination0 as byte }
		if (index == 2) { return (snapshotdestination1 >> 8) as byte }
		if (index == 3) { return snapshotdestination1 as byte }
		if (index == 4) { return (snapshotdestination2 >> 8) as byte }
		if (index == 5) { return snapshotdestination2 as byte }
		if (index == 6) { return (snapshotsource0 >> 8) as byte }
		if (index == 7) { return snapshotsource0 as byte }
		if (index == 8) { return (snapshotsource1 >> 8) as byte }
		if (index == 9) { return snapshotsource1 as byte }
		if (index == 10) { return (snapshotsource2 >> 8) as byte }
		if (index == 11) { return snapshotsource2 as byte }
		if (index == 12) { return (snapshottypeWord >> 8) as byte }
		if (index == 13) { return snapshottypeWord as byte }
		return 0x00
	}
}