Livt packages

Livt.ML

Version 1.6.1

Fixed-size quantized inference, tensor storage, attention, and transformer building blocks for Livt.

Published files (132)

src/linear/Int8MatrixAccumulator32.lvt

namespace Livt.ML.Linear

/**
 * Retained INT8 matrix sums with signed int32 activations and checked int64 updates.
 * One owner serializes calls and holds the live row stable until each returns.
 * The native core uses synchronous active-high reset; reset all participants
 * across a rising edge. Call Begin before use after construction or reset.
 * Numeric scaling, weight layout and memory transport belong to the caller.
 */
component Int8MatrixAccumulator32<ACTIVATION_BITS: int = 32, PRODUCT_STAGES: int = 1>
{
	static assert ACTIVATION_BITS >= 2 && ACTIVATION_BITS <= 32, "activation range must be 2..32 bits"
	static assert PRODUCT_STAGES == 1 || PRODUCT_STAGES == 2, "product stages must be 1 or 2"

	// Physical bank capacity; Begin selects the number of accessible output rows.
	public const MAX_ROWS: int = 1024

	// Commands shared with the native worker.
	const OP_BEGIN: int = 0
	const OP_LOAD: int = 1
	const OP_ACCUMULATE: int = 2
	const OP_READ: int = 3
	const OP_SEED: int = 4

	// Four sixteen-int32 blocks load activations; a weight row holds 64 signed bytes.
	public values: in logic[512]
	public busy: out logic

	// Owned native arithmetic, storage and producer completion phase.
	primitive: Int8MatrixAccumulator32Primitive
	request: logic

	/**
	 * Owns one worker in the caller's clock/reset domain; Begin initializes its bank.
	 */
	new()
	{
		primitive = new Int8MatrixAccumulator32Primitive()
	}

	/**
	 * Waits for completion without copying a wide result through the dispatcher.
	 */
	fn Submit(operation: int, index: int)
	{
		state {
			primitive.operation = operation
			primitive.index = index
		}

		request = !request
		primitive.request = request

		while (primitive.acknowledge != request) {
			state {
			}
		}
	}

	/**
	 * Clears 1..MAX_ROWS sums, invalidates activations and recovers from failure.
	 * Invalid row counts invalidate the bank. Completion includes all RAM writes.
	 */
	public fn Begin(rows: int) bool
	{
		Submit(OP_BEGIN, rows)
		return (primitive.result >> 64) == 0
	}

	/**
	 * Loads block 0..3 from values; every lane must fit ACTIVATION_BITS.
	 * Rejected loads invalidate the selected block. Load all four before updates.
	 */
	public fn LoadActivations(block: int) bool
	{
		Submit(OP_LOAD, block)
		return (primitive.result >> 64) == 0
	}

	/**
	 * Adds one exact 64-element dot product to row 0..rows-1 of the active bank.
	 * Requires four valid activation blocks; true means the write has committed.
	 * Overflow is checked at the block-update boundary. Failure poisons the bank
	 * until Begin/reset, so a later cancelling update cannot hide an overflow.
	 */
	public fn Accumulate(row: int) bool
	{
		Submit(OP_ACCUMULATE, row)
		return (primitive.result >> 64) == 0
	}

	/**
	 * Waits for row 0..rows-1 of the active bank and returns its signed int64 sum.
	 * Bit 64 reports failure; failed payloads are zero.
	 * An invalid read fails without poisoning an otherwise valid bank.
	 */
	public fn Read(row: int) logic[65]
	{
		Submit(OP_READ, row)
		return primitive.result
	}

	/**
	 * Sets row 0..rows-1 of the active bank to a signed int64 bias bit pattern.
	 * True means the write has committed; invalid writes poison the bank.
	 */
	public fn Seed(row: int, value: logic[64]) bool
	{
		primitive.seed = value
		Submit(OP_SEED, row)
		return (primitive.result >> 64) == 0
	}

	/**
	 * Shares a retained caller row and immutable numeric configuration.
	 */
	process Connect[]()
	{
		primitive.count = 0
		primitive.rowValid = 0b0
		primitive.streamError = 0b0
		primitive.values = values
		primitive.activationBits = ACTIVATION_BITS
		primitive.productStages = PRODUCT_STAGES
		busy = primitive.busy
	}
}