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/Usb2LineReceiver.lvt
namespace Livt.Usb.IO
using Livt.Usb
/**
* Samples the USB pair in the inherited context and recovers a low/full-speed bit phase.
* Two input registers contain metastability; protocol qualification ignores short
* single-ended crossover intervals. GPIO sampling is not an analog differential PHY.
* The owner changes speed only while disabled or idle. No scheduled operations run
* in this path; public events describe the preceding clock's sampled pair.
*/
component Usb2LineReceiver
{
// Exact USB bit periods; TicksFor currently rounds fractional-ns clock periods.
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 PHASE_BITS: int = Bits.RequiredFor(LOW_BIT_TICKS - 1)
const IDLE_BITS: int = Bits.RequiredFor(LOW_BIT_TICKS * 2)
const ZERO_BITS: int = Bits.RequiredFor(LOW_BIT_TICKS * 3 + 1)
const INVALID_TICKS: uint = (FULL_BIT_TICKS + 2) / 3
const INVALID_BITS: int = Bits.RequiredFor(INVALID_TICKS)
// Borrowed raw pad samples, selected speed and receive ownership.
dp: logic
dm: logic
speed: UsbSpeed
enabled: bool
// Pair synchronizer and edge/qualification history.
meta: logic[2]
sampledSpeed: UsbSpeed = UsbSpeed.Unknown
previous: logic[2]
phase: logic[PHASE_BITS]
idleCount: logic[IDLE_BITS]
zeroCount: logic[ZERO_BITS]
oneCount: logic[INVALID_BITS]
// Raw synchronized pair and qualified observations; bit 1 is D+.
public lineState: out logic[2]
public bitTick: out bool
public bitState: out logic[2]
public modeReady: out bool
public idle: out bool
public se0: out bool
public longSe0: out bool
public invalid: out bool
/**
* Binds pad inputs in the inherited context, with integral samples per USB bit.
* At least eight samples per full-speed bit are required.
*/
new(dp: logic, dm: logic, speed: UsbSpeed, enabled: 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.dp = dp
this.dm = dm
this.speed = speed
this.enabled = enabled
}
/**
* Prevents admission while a new speed is being initialized.
*/
process Mode[]()
{
modeReady = CLOCK_HZ % 12000000 == 0 && FULL_BIT_TICKS >= 8
&& enabled && speed == sampledSpeed && this.context.rst == 0b0
}
/**
* Rephases on differential transitions and samples close to each bit center.
* Short SE0 crossover intervals do not replace the last differential sample.
*/
process Sample()
{
state {
var observed = lineState
var oldMeta = meta
var raw: logic[2] = 0
raw[1] = dp
raw[0] = dm
meta = raw
lineState = oldMeta
bitTick = false
var j: logic[2]
j = 0b10
var idleLimit: logic[IDLE_BITS]
idleLimit = ((FULL_BIT_TICKS * 2) as int) as logic[IDLE_BITS]
var zeroLimit: logic[ZERO_BITS]
zeroLimit = ((FULL_BIT_TICKS * 3 + 1) as int) as logic[ZERO_BITS]
var zeroThreshold: logic[ZERO_BITS]
zeroThreshold = (((FULL_BIT_TICKS + 2) / 3) as int) as logic[ZERO_BITS]
var longThreshold: logic[ZERO_BITS]
longThreshold = ((FULL_BIT_TICKS * 3) as int) as logic[ZERO_BITS]
var centerPhase: logic[PHASE_BITS]
centerPhase = ((FULL_BIT_TICKS / 2 - 2) as int) as logic[PHASE_BITS]
var fullPhase: logic[PHASE_BITS]
fullPhase = ((FULL_BIT_TICKS - 1) as int) as logic[PHASE_BITS]
if (speed == UsbSpeed.Low) {
j = 0b01
idleLimit = ((LOW_BIT_TICKS * 2) as int) as logic[IDLE_BITS]
zeroLimit = ((LOW_BIT_TICKS * 3 + 1) as int) as logic[ZERO_BITS]
zeroThreshold = (((LOW_BIT_TICKS + 2) / 3) as int) as logic[ZERO_BITS]
longThreshold = ((LOW_BIT_TICKS * 3) as int) as logic[ZERO_BITS]
centerPhase = ((LOW_BIT_TICKS / 2 - 2) as int) as logic[PHASE_BITS]
fullPhase = ((LOW_BIT_TICKS - 1) as int) as logic[PHASE_BITS]
}
if (!enabled || speed != sampledSpeed || CLOCK_HZ % 12000000 != 0 || FULL_BIT_TICKS < 8) {
sampledSpeed = speed
phase = 0
previous = j
idleCount = 0
zeroCount = 0
oneCount = 0
idle = false
se0 = false
longSe0 = false
invalid = false
} else {
if (observed == j) {
if (idleCount < idleLimit) {
idleCount++
}
} else {
idleCount = 0
}
if (observed == 0) {
if (zeroCount < zeroLimit) {
zeroCount++
}
} else {
zeroCount = 0
}
if (observed == 3) {
if ((oneCount as uint) < INVALID_TICKS) {
oneCount++
}
} else {
oneCount = 0
}
idle = idleCount >= idleLimit
se0 = zeroCount >= zeroThreshold
longSe0 = zeroCount >= longThreshold
invalid = (oneCount as uint) >= INVALID_TICKS
var differential = observed == 1 || observed == 2
if (differential && observed != previous) {
phase = centerPhase
previous = observed
} elif (phase == 0) {
bitTick = true
bitState = previous
phase = fullPhase
} else {
phase--
}
}
}
}
}