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/io/Usb2PacketReceiver.lvt
namespace Livt.Usb.IO
using Livt.Usb
/**
* Decodes sampled low/full-speed SYNC, NRZI and stuffing into an unstalled stream.
* PID/CRC bytes are published without interpretation. Error is retained through
* packet completion until the next start or cancellation. Up to six trailing
* decoded ones may be EOP dribble; packet integrity remains the link's responsibility.
*/
component Usb2PacketReceiver
{
// Framing phases; recovery requires qualified idle before another SYNC.
const IDLE: logic[3] = 0
const SYNC: logic[3] = 1
const DATA: logic[3] = 2
const END: logic[3] = 3
const RECOVER: logic[3] = 4
// Borrowed sampler signals in the same clock context; no aggregate copy.
speed: UsbSpeed
enabled: bool
lineState: logic[2]
bitTick: bool
bitState: logic[2]
idle: bool
se0: bool
longSe0: bool
invalid: bool
// Packet history and byte assembly.
sampledSpeed: UsbSpeed = UsbSpeed.Unknown
phase: logic[3]
armed: bool
previous: logic[2]
syncIndex: logic[3]
bitIndex: logic[3]
ones: logic[3]
payload: logic[8]
seenByte: bool
// Receiver-owned packet event stream.
public active: out bool
public valid: out bool
public data: out byte
public error: out bool
/**
* Binds sampled signal ports directly. Disabling cancels RX.
* Scalar bindings avoid a compiler-added register on borrowed component outputs.
*/
new(speed: UsbSpeed, enabled: bool, lineState: logic[2], bitTick: bool,
bitState: logic[2], idle: bool, se0: bool, longSe0: bool, invalid: bool)
{
this.speed = speed
this.enabled = enabled
this.lineState = lineState
this.bitTick = bitTick
this.bitState = bitState
this.idle = idle
this.se0 = se0
this.longSe0 = longSe0
this.invalid = invalid
}
/**
* Decodes one sampled bit at a time while independently qualifying EOP/error.
*/
process Decode()
{
state {
valid = false
var j: logic[2]
j = 0b10
if (speed == UsbSpeed.Low) {
j = 0b01
}
var k = j ^ 0b11
if (!enabled || speed != sampledSpeed) {
sampledSpeed = speed
phase = IDLE
armed = false
active = false
error = false
} elif (phase == RECOVER) {
if (idle) {
phase = IDLE
armed = true
}
} elif (phase == END) {
if (longSe0 || invalid || lineState == k) {
error = true
active = false
phase = RECOVER
} elif (lineState == j) {
active = false
phase = IDLE
armed = false
}
} elif (phase != IDLE && invalid) {
error = true
active = false
phase = RECOVER
} elif (phase != IDLE && se0) {
var tailMask = ((1 as uint) << (bitIndex as uint)) - 1
if (phase != DATA || !seenByte || (bitIndex == 0 && ones == 6)
|| (bitIndex != 0 && ((payload as uint) != tailMask || bitIndex > 6))) {
error = true
}
phase = END
} elif (phase == IDLE) {
if (idle) {
armed = true
}
if (armed && bitTick && bitState == k && !se0 && !invalid) {
active = true
error = false
phase = SYNC
syncIndex = 1
previous = k
payload = 0
bitIndex = 0
seenByte = false
ones = 0
armed = false
}
} elif (bitTick) {
if (phase == SYNC) {
var expected = k
if ((syncIndex & 1) != 0 && syncIndex != 7) {
expected = j
}
if (bitState != expected) {
error = true
active = false
phase = RECOVER
} elif (syncIndex == 7) {
phase = DATA
ones = 1
} else {
syncIndex++
}
} else {
var bit = bitState == previous
previous = bitState
if (ones == 6) {
if (bit) {
error = true
active = false
phase = RECOVER
} else {
ones = 0
}
} else {
var nextPayload = payload
nextPayload[bitIndex as uint] = 0b0
if (bit) {
nextPayload[bitIndex as uint] = 0b1
ones++
} else {
ones = 0
}
if (bitIndex == 7) {
data = nextPayload as byte
valid = true
seenByte = true
bitIndex = 0
payload = 0
} else {
payload = nextPayload
bitIndex++
}
}
}
}
}
}
}