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/Usb2PacketTransmitter.lvt
namespace Livt.Usb.IO
using Livt.Usb
/**
* Serializes an uninterrupted byte stream into low/full-speed USB signaling.
* Ready is an edge handshake; valid dropping at a byte boundary requests EOP.
* Stuffing includes SYNC's last one and a required stuffed bit before EOP.
* Busy remains true for two SE0 bits and one driven J bit before pad release.
*/
component Usb2PacketTransmitter
{
// Constant bit periods and EOP counter geometry follow the inherited context.
const CLOCK_HZ: uint = this.context.TicksPer(1Hz)
const FULL_BIT_TICKS: uint = this.context.TicksPer(12MHz)
const LOW_BIT_TICKS: uint = this.context.TicksPer(1500KHz)
const COUNT_BITS: int = Bits.RequiredFor(LOW_BIT_TICKS * 2 - 1)
// Wire phases; this component is the single owner of its serializer state.
const IDLE: logic[3] = 0
const SYNC: logic[3] = 1
const DATA: logic[3] = 2
const END_ZERO: logic[3] = 3
const END_J: logic[3] = 4
// Borrowed stream and admission/cancellation signals.
speed: UsbSpeed
allowStart: bool
cancel: bool
txValid: bool
txData: byte
keepAlive: bool
// Selected packet timing, serialization and continuity across byte boundaries.
phase: logic[3]
count: logic[COUNT_BITS]
ticks: logic[COUNT_BITS]
j: logic[2] = 0b10
first: logic[8]
shift: logic[8]
bitIndex: logic[3]
ones: logic[3]
needByte: bool
level: logic[2]
// Combinational edge admission and registered-state wire outputs.
public ready: out bool
public keepAliveReady: out bool
public busy: out bool
public output: out logic[2]
public enable: out bool
/**
* Binds a same-context producer with no pauses inside a packet; cancel aborts locally.
*/
new(speed: UsbSpeed, allowStart: bool, cancel: bool,
txValid: bool, txData: byte, keepAlive: bool)
{
assert CLOCK_HZ % 12000000 == 0, "USB PHY clock must be an exact multiple of 12 MHz"
assert FULL_BIT_TICKS >= 8, "USB PHY requires at least eight samples per full-speed bit"
this.speed = speed
this.allowStart = allowStart
this.cancel = cancel
this.txValid = txValid
this.txData = txData
this.keepAlive = keepAlive
}
/**
* Presents edge-level handshakes and suppresses all drive during context reset.
*/
process Ports[]()
{
var available = CLOCK_HZ % 12000000 == 0 && FULL_BIT_TICKS >= 8
&& !cancel && this.context.rst == 0b0
ready = available && ((phase == IDLE && allowStart)
|| (phase == DATA && needByte && count == 0 && ones != 6))
keepAliveReady = available && phase == IDLE && allowStart
&& speed == UsbSpeed.Low && !txValid
busy = phase != IDLE && available
output = level
enable = phase != IDLE && available
}
/**
* Advances only on bit boundaries; no scheduled calls can alter wire timing.
*/
process Serialize()
{
state {
if (cancel) {
phase = IDLE
count = 0
needByte = false
} elif (phase == IDLE) {
if ((ready && txValid) || (keepAliveReady && keepAlive)) {
ticks = (FULL_BIT_TICKS as int) as logic[COUNT_BITS]
j = 0b10
if (speed == UsbSpeed.Low) {
ticks = (LOW_BIT_TICKS as int) as logic[COUNT_BITS]
j = 0b01
}
level = j
count = 0
ones = 0
bitIndex = 0
needByte = false
first = txData as logic[8]
shift = 0x80
phase = SYNC
if (!txValid) {
phase = END_ZERO
level = 0
count = (((ticks as uint) * 2 - 1) as int) as logic[COUNT_BITS]
}
}
} elif (count != 0) {
count--
} elif (phase == END_ZERO) {
phase = END_J
level = j
count = (((ticks as uint) - 1) as int) as logic[COUNT_BITS]
} elif (phase == END_J) {
phase = IDLE
} else {
count = (((ticks as uint) - 1) as int) as logic[COUNT_BITS]
if (ones == 6) {
level = level ^ 0b11
ones = 0
} elif (phase == DATA && needByte && !txValid) {
phase = END_ZERO
level = 0
count = (((ticks as uint) * 2 - 1) as int) as logic[COUNT_BITS]
} else {
if (phase == DATA && needByte) {
shift = txData as logic[8]
bitIndex = 0
needByte = false
}
if ((shift & 1) == 0) {
level = level ^ 0b11
ones = 0
} else {
ones++
}
shift = shift >> 1
if (bitIndex == 7) {
bitIndex = 0
if (phase == SYNC) {
phase = DATA
shift = first
} else {
needByte = true
}
} else {
bitIndex++
}
}
}
}
}
}