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/PacketReceiver.lvt
namespace Livt.Usb.Packets
using Livt.IO
using Livt.Usb
using Livt.Utils.Hashing
/**
* Unstalled PHY packet parser with one retained, validated packet slot.
* CAPACITY counts payload bytes. B must implement the exact one-edge synchronous
* RAM contract and geometry. Default storage requests block RAM; callers may
* select another memory style or substitute a compatible signal-level backend.
* completed/accepted are one-edge events. Only available authorizes payload reads.
* A packet arriving while occupied is discarded without changing the held packet.
*/
component PacketReceiver<CAPACITY: int = 64, STYLE: MemoryStyle = MemoryStyle.Block,
B: ISynchronousRam<byte, uint, RamGeometry<CAPACITY>> & new() = SynchronousRam<byte, uint, CAPACITY, STYLE>>
{
static assert CAPACITY > 0 && CAPACITY <= 1023, "Packet capacity must be 1 through 1023"
// Owned storage/checksum logic and primitive PHY bindings.
storage: B
crc5: Crc<5, 0x05, true>
crc16: Crc<16, 0x8005, true>
public speed: in UsbSpeed
public cancel: in bool
public active: in bool
public valid: in bool
public data: in byte
public physicalError: in bool
// Unpublished packet state; two trailing bytes never enter payload storage.
wasActive: bool
blocked: bool
byteCount: logic[11]
receivePid: byte
oldest: byte
newest: byte
tokenWord: logic[16]
checksum: logic[16] = PacketIntegrity.DATA_INITIAL
failure: PacketError
sampledSpeed: UsbSpeed
// Coherent publication registers; the output process exposes all on the same edge.
published: bool
publishedPid: byte
publishedLength: uint
publishedToken: logic[11]
completionPulse: bool
acceptPulse: bool
completionError: PacketError
// Consumer-owned release/read controls; reads respond after one accepting edge.
public release: in bool
public readRequest: in bool
public readAddress: in uint
public readReady: out bool
public readValid: out bool
public readData: out byte
// Published metadata is retained until release/cancel; errors describe completion.
public available: out bool
public pid: out byte
public length: out uint
public token: out logic[11]
public completed: out bool
public accepted: out bool
public error: out PacketError
/**
* All bindings share the PHY clock. cancel must accompany PHY cancellation,
* including reset/disconnect or a speed change. Release wins over a new start.
*/
new()
{
storage = new B()
crc5 = new Crc<5, 0x05, true>()
crc16 = new Crc<16, 0x8005, true>()
}
/**
* Captures payload at the receiving edge; published reads cannot collide with RX.
*/
process Memory[]()
{
available = published
pid = publishedPid
length = publishedLength
token = publishedToken
completed = completionPulse
accepted = acceptPulse
error = completionError
var enabled = !cancel && this.context.rst == 0b0
readReady = enabled && published && !release && readAddress < publishedLength
readData = 0
if (readValid && published && enabled) {
readData = storage.readData
}
storage.enable = readRequest && readReady
storage.writeEnable = false
storage.address = readAddress
storage.writeData = oldest
if (enabled && active && wasActive && valid && !blocked
&& !published && failure == PacketError.None && PacketPid.IsData(receivePid)
&& (byteCount as uint) >= PacketIntegrity.DATA_OVERHEAD && (byteCount as uint) < (CAPACITY as uint) + PacketIntegrity.DATA_OVERHEAD) {
storage.enable = true
storage.writeEnable = true
storage.address = (byteCount as uint) - PacketIntegrity.DATA_OVERHEAD
}
}
/**
* Validates an entire packet before publication; no automatic ACK policy.
*/
process Receive()
{
state {
// EOP cannot accept a byte; use the state before active-byte updates.
var packetCount = byteCount
var packetPid = receivePid
var packetToken = tokenWord
var packetChecksum = checksum
completionPulse = false
acceptPulse = false
readValid = readRequest && readReady
if (release) {
published = false
publishedLength = 0
}
if (cancel) {
wasActive = false
published = false
readValid = false
publishedLength = 0
publishedPid = 0
publishedToken = 0
completionError = PacketError.None
failure = PacketError.None
} else {
if (active && !wasActive) {
blocked = published
byteCount = 0
receivePid = 0
checksum = PacketIntegrity.DATA_INITIAL
tokenWord = 0
failure = PacketError.None
sampledSpeed = speed
if (published) {
failure = PacketError.Occupied
}
}
if (active && valid) {
if (byteCount == 0) {
receivePid = data
if (failure == PacketError.None) {
if (!PacketPid.IsValid(data)) {
failure = PacketError.InvalidPid
} elif (!(PacketPid.IsToken(data) || PacketPid.IsData(data)
|| PacketPid.IsHandshake(data)) || (speed != UsbSpeed.Low && speed != UsbSpeed.Full)
|| (speed == UsbSpeed.Low && data == PacketPid.SOF)) {
failure = PacketError.Unsupported
}
}
} else {
checksum = crc16.UpdateByte(checksum, data)
oldest = newest
newest = data
if (byteCount == 1) {
tokenWord = data as logic[16]
} elif (byteCount == 2) {
tokenWord = tokenWord | ((data as logic[16]) << 8)
}
}
if ((byteCount as uint) < (CAPACITY as uint) + PacketIntegrity.DATA_OVERHEAD + 1) {
byteCount++
}
if ((byteCount as uint) > (CAPACITY as uint) + PacketIntegrity.DATA_OVERHEAD && failure == PacketError.None) {
failure = PacketError.Overflow
}
}
if ((active || wasActive) && (physicalError || speed != sampledSpeed)) {
failure = PacketError.Physical
}
if (wasActive && !active) {
var result = failure
var fields = (packetToken & 0x07FF) as logic[11]
var expected = crc5.UpdateBits(PacketIntegrity.TOKEN_INITIAL, fields as uint, PacketIntegrity.TOKEN_BITS)
^ PacketIntegrity.TOKEN_FINAL_XOR
if (result == PacketError.None) {
result = PacketIntegrity.Validate(packetPid, packetCount as uint, speed,
packetChecksum, packetToken, expected)
}
completionPulse = true
completionError = result
if (result == PacketError.None && !blocked) {
published = true
acceptPulse = true
publishedPid = packetPid
publishedLength = 0
if (PacketPid.IsData(packetPid)) {
publishedLength = (packetCount as uint) - PacketIntegrity.DATA_OVERHEAD
}
publishedToken = fields
}
}
wasActive = active
}
}
}
}