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/norm/LayerNorm64Stream.lvt
namespace Livt.ML.Norm
using Livt.Math.Arithmetic
/**
* Runtime division is scheduled; completion latency increases with work.
* Serialize operations and keep inputs stable until the call or handshake completes.
*
* Toggle-stream implementation of the LayerNorm64 numeric contract.
*
* Sixty-four Q8.8 gamma/beta records configure the component. Each input frame
* then contains exactly 64 signed integer values. Outputs use Q8.8 normalized
* and affine scaling. Internal arrays never cross a public-function boundary.
*/
component LayerNorm64Stream
{
// Owned divider; calls complete before this component advances.
divider: IntegerDivision<32>
// Compile-time dimensions, bounds, and numeric constants.
public const VECTOR_SIZE: int = 64
public const AFFINE_SCALE: int = 256
// Parameter-loading payload and toggle acknowledgment.
affineRequest: logic
affineGamma: int
affineBeta: int
affineEpsilon: int
affineAcknowledge: logic
// Input payload and toggle acknowledgment from the producer.
inputRequest: logic
inputData: int
inputLast: logic
inputAcknowledge: logic
// Output payload and toggle acknowledgment; payload stays stable while pending.
outputRequest: logic
outputData: int
outputLast: logic
outputAcknowledge: logic
// Configuration readiness and frame-boundary error status.
configured: logic
frameError: logic
// Transfer progress and retained request/acknowledgment state.
affineSeen: logic
inputSeen: logic
// Current position and bounds within the configured frame.
affineIndex: int
inputIndex: int
outputIndex: int
// Configured coefficients and numeric scaling parameters.
epsilon: int
// Input tensor storage; populate the required span before computation.
inputs: int[64]
// Configured coefficients and numeric scaling parameters.
gamma: int[64]
beta: int[64]
// Latest computed result, retained for the caller.
outputs: int[64]
// Transfer progress and retained request/acknowledgment state.
configurationComplete: bool
computing: bool
emitting: bool
pending: bool
/**
* Binds configuration, input, and output toggle-handshake ports. Callers hold each payload
* until its acknowledgment matches the request; configure the worker before sending
* frames.
*/
new(
affineRequest: in logic, affineGamma: in int, affineBeta: in int,
affineEpsilon: in int, affineAcknowledge: out logic,
inputRequest: in logic, inputData: in int, inputLast: in logic,
inputAcknowledge: out logic, outputRequest: out logic,
outputData: out int, outputLast: out logic, outputAcknowledge: in logic,
configured: out logic, frameError: out logic)
{
divider = new IntegerDivision<32>()
this.affineRequest = affineRequest
this.affineGamma = affineGamma
this.affineBeta = affineBeta
this.affineEpsilon = affineEpsilon
affineAcknowledge = this.affineAcknowledge
this.inputRequest = inputRequest
this.inputData = inputData
this.inputLast = inputLast
inputAcknowledge = this.inputAcknowledge
outputRequest = this.outputRequest
outputData = this.outputData
outputLast = this.outputLast
this.outputAcknowledge = outputAcknowledge
configured = this.configured
frameError = this.frameError
}
/**
* Accepts configuration and frame values through the toggle handshakes, evaluates the
* configured operator, and holds each output until acknowledged. All connected endpoints
* must share compatible clock/reset sequencing.
*/
process Main()
{
affineAcknowledge = affineSeen
inputAcknowledge = inputSeen
configured = 0b0
if (configurationComplete) {
configured = 0b1
}
if (!configurationComplete && affineRequest != affineSeen) {
gamma[affineIndex] = affineGamma
beta[affineIndex] = affineBeta
epsilon = affineEpsilon
affineSeen = affineRequest
if (affineIndex == 63) {
affineIndex = 0
configurationComplete = true
} else {
affineIndex++
}
}
if (pending) {
if (outputAcknowledge == outputRequest) {
pending = false
}
} else {
if (emitting) {
outputData = outputs[outputIndex]
outputLast = 0b0
if (outputIndex == 63) {
outputLast = 0b1
}
outputRequest = !outputRequest
pending = true
if (outputIndex == 63) {
outputIndex = 0
emitting = false
} else {
outputIndex++
}
} else {
if (computing) {
var sum = 0
for (var index = 0; index < VECTOR_SIZE; index++) {
sum = sum + inputs[index]
}
var mean = sum / VECTOR_SIZE
var squaredSum = 0
for (var index = 0; index < VECTOR_SIZE; index++) {
var centered = inputs[index] - mean
squaredSum = squaredSum + centered * centered
}
var variance = squaredSum / VECTOR_SIZE
var normValue = variance + epsilon
if (normValue < 0) {
normValue = 0
}
var inverseStddev = 0
if (normValue > 0) {
var root = normValue
if (normValue > 1) {
root = normValue / 2 + 1
}
for (var iteration = 0; iteration < 31; iteration++) {
var quotient = divider.Divide(normValue, root)
if (root > quotient) {
root = (root + quotient) / 2
}
}
if (root > divider.Divide(normValue, root)) {
root--
}
inverseStddev = divider.Divide(32768 + root / 2, root)
}
for (var index = 0; index < VECTOR_SIZE; index++) {
var centered = inputs[index] - mean
var normalized = centered * inverseStddev / 128
outputs[index] = normalized * gamma[index] / AFFINE_SCALE + beta[index]
}
computing = false
emitting = true
} else {
if (configurationComplete && inputRequest != inputSeen) {
if (inputIndex == 0) {
frameError = 0b0
}
if (inputLast == 0b1 && inputIndex != 63) {
frameError = 0b1
}
if (inputIndex == 63 && inputLast != 0b1) {
frameError = 0b1
}
inputs[inputIndex] = inputData
inputSeen = inputRequest
if (inputIndex == 63) {
inputIndex = 0
computing = true
} else {
inputIndex++
}
}
}
}
}
}
}