Livt packages

Livt.Usb

Version 0.0.5

USB 2.0 low/full-speed PHY, host transfers, device discovery, HID keyboard, and read-only mass storage for Livt.

Published files (57)

src/packets/PacketBuffer.lvt

namespace Livt.Usb.Packets

using Livt.IO

/**
 * Owns a bounded packet payload with an initialized prefix and publication state.
 * One caller serializes scheduled operations in the inherited clock/reset context.
 * Readers borrow the published bytes until Clear; finish reads before clearing.
 * Reset cancels work and invalidates metadata, regardless of backend payload reset.
 * Calls may take multiple cycles; this is not a wire-rate PHY storage interface.
 */
component PacketBuffer<PAYLOAD_CAPACITY: int = 64, STYLE: MemoryStyle = MemoryStyle.Block,
	B: IRam<byte> & new() = Ram<byte, PAYLOAD_CAPACITY, STYLE>>
{
	static assert PAYLOAD_CAPACITY > 0, "Packet buffer capacity must be positive"

	// Configured payload capacity in bytes, excluding PID and CRC.
	public const BYTE_CAPACITY: int = PAYLOAD_CAPACITY

	// Owned backend; style applies only to the default RAM implementation.
	storage: B

	// Only bytes below length are initialized; publication freezes the prefix.
	const LENGTH_BITS: int = Bits.RequiredFor(PAYLOAD_CAPACITY)
	length: logic[LENGTH_BITS]
	ready: bool

	/**
	 * Creates empty storage. B must provide byte addresses 0 through PAYLOAD_CAPACITY-1.
	 * Unwritten backend bytes are never exposed; no payload initialization is needed.
	 */
	new()
	{
		storage = new B()
	}

	/**
	 * Returns the initialized prefix length in bytes, including before publication.
	 * This snapshot does not reserve storage or authorize a read before publication.
	 */
	public fn GetLength() int
	{
		return (length as uint) as int
	}

	/**
	 * Reports whether the initialized prefix is published, including an empty payload.
	 */
	public fn IsReady() bool
	{
		return ready
	}

	/**
	 * Appends one byte and returns true after its write commits. Returns false without
	 * changing state when published, full, or the backend cannot address the next byte.
	 */
	public fn TryAppend(value: byte) bool
	{
		var index = (length as uint) as int

		if (ready || index >= PAYLOAD_CAPACITY) {
			return false
		}

		if (!storage.IsValidAddress(index)) {
			return false
		}

		storage.Write(index, value)
		length++
		return true
	}

	/**
	 * Publishes the initialized prefix, including zero bytes. Returns false if already
	 * published. The owner must validate packet integrity before publishing RX data.
	 */
	public fn TryPublish() bool
	{
		if (ready) {
			return false
		}

		ready = true
		return true
	}

	/**
	 * Reads a published byte at index in 0..GetLength()-1 and returns true on completion.
	 * Returns false with value zero for unpublished data or an out-of-prefix index.
	 * The caller must retain publication until the scheduled read finishes.
	 */
	public fn TryRead(index: int, value: out byte) bool
	{
		value = 0

		if (!ready || index < 0 || index >= ((length as uint) as int)) {
			return false
		}

		value = storage.Read(index)
		return true
	}

	/**
	 * Discards publication and the initialized prefix without erasing payload storage.
	 * The owner must finish all borrowed reads first. The next append starts at zero.
	 */
	public fn Clear()
	{
		ready = false
		length = 0
	}
}