Livt packages
Livt.IO
Version 1.8.1
Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.
Published files (126)
src/uart/BufferedUart.lvt
namespace Livt.IO
using Livt.Collections
/**
* FIFO-backed application UART. The owner of each FIFO is its only mutator.
* Launch permission gates a new frame, never an active or committed frame.
*/
component BufferedUart<RX_CAPACITY: int = 64,
TX_CAPACITY: int = 64,
DATA_BITS: UartDataBits = UartDataBits.Eight,
PARITY: UartParity = UartParity.None,
STOP_BITS: UartStopBits = UartStopBits.One,
BAUD: Frequency = 115200Hz> : IBufferedUart
{
// Signal-level UART and the two independently owned byte stores.
uart: Uart<DATA_BITS, PARITY, STOP_BITS, BAUD>
receiver: Fifo<byte, RX_CAPACITY>
transmitter: Fifo<byte, TX_CAPACITY>
launchPermission: bool
// Scheduled transmit and receive transactions with captured results.
txRequest: bool
txCompleted: bool
txAccepted: bool
txPayload: byte
rxRequest: bool
rxCompleted: bool
rxAccepted: bool
rxResult: byte
// Independently serialized clear transactions.
txClearRequest: bool
txClearCompleted: bool
rxClearRequest: bool
rxClearCompleted: bool
// Combinational requests presented to the two FIFO owners.
txClear: bool
rxClear: bool
txPush: bool
txPop: bool
rxPush: bool
rxPop: bool
// FIFO acceptance, payload and occupancy observations.
txPushAccepted: bool
txPopAccepted: bool
rxPushAccepted: bool
rxPopAccepted: bool
txHead: byte
rxHead: byte
rxPayload: byte
txCount: int
txSpace: int
rxCount: int
public receiveCount: out int
rxSpace: int
// Physical UART handshakes and committed-launch tracking.
txValid: logic
txData: logic[8]
txActive: logic
txDone: logic
rxValid: logic
rxData: logic[8]
rxError: logic
launchPending: bool
// Sticky receive diagnostics and their serialized clear transaction.
frameErrors: int
overflows: int
errorClearRequest: bool
errorClearCompleted: bool
/**
* Uses the inherited context. Optional launch permission must already be
* synchronized; ordinary UARTs leave it at its default value of true.
*/
new(
rx: in logic,
tx: out logic,
launchPermission: bool = true
)
{
this.launchPermission = launchPermission
this.uart = new Uart<DATA_BITS, PARITY, STOP_BITS, BAUD>(rx, tx, this.txValid, this.txData,
this.txActive, this.txDone, this.rxValid, this.rxData, this.rxError)
this.receiver = new Fifo<byte, RX_CAPACITY>(this.rxClear, this.rxPush,
this.rxPayload, this.rxPop, this.rxPushAccepted, this.rxPopAccepted,
this.rxHead, this.rxCount, this.rxSpace)
this.transmitter = new Fifo<byte, TX_CAPACITY>(this.txClear, this.txPush,
this.txPayload, this.txPop, this.txPushAccepted, this.txPopAccepted,
this.txHead, this.txCount, this.txSpace)
}
/**
* Returns acceptance into the transmit FIFO, not wire completion.
*/
override fn TryTransmit(data: byte) bool
{
txPayload = data
txRequest = !txRequest
while (txCompleted != txRequest) {
state {
}
}
return txAccepted
}
/**
* Enqueues a prefix, stopping at the first rejected byte. Other producers
* may interleave; the returned count is the number accepted by this call.
*/
override fn Send(data: byte[]) int
{
var accepted = 0
for (var index = 0; index < data.Length(); index++) {
if (!TryTransmit(data[index])) {
return accepted
}
accepted++
}
return accepted
}
/**
* Removes one received byte atomically; zero on failure is not valid data.
*/
override fn TryReceive(data: out byte) bool
{
rxRequest = !rxRequest
while (rxCompleted != rxRequest) {
state {
}
}
data = rxResult
return rxAccepted
}
/**
* Discards queued data but not a frame or byte already committed to launch.
*/
override fn ClearTransmitBuffer()
{
txClearRequest = !txClearRequest
while (txClearCompleted != txClearRequest) {
state {
}
}
}
/**
* Clear wins over reception and consumption at its transaction edge.
*/
override fn ClearReceiveBuffer()
{
rxClearRequest = !rxClearRequest
while (rxClearCompleted != rxClearRequest) {
state {
}
}
}
/**
* Reports an active physical transmit frame.
*/
override fn IsTransmitting() bool
{
return txActive == 0b1
}
/**
* Includes queued, pending API, committed-launch and physically active work.
*/
override fn HasPendingTransmit() bool
{
return txCount > 0 || launchPending || txActive == 0b1 || txRequest != txCompleted
}
/**
* Reports that no transmit work remains.
*/
override fn IsTransmitIdle() bool
{
return txCount == 0 && !launchPending && txActive == 0b0 && txRequest == txCompleted
}
/**
* Returns a receive occupancy snapshot, not a reservation.
*/
override fn GetReceiveCount() int
{
return rxCount
}
/**
* Returns a transmit free-space snapshot, not a reservation.
*/
override fn GetTransmitSpace() int
{
return txSpace
}
/**
* Returns the number of invalid parity or stop bits observed since error clear.
*/
override fn GetFrameErrorCount() int
{
return frameErrors
}
/**
* Returns valid received frames rejected because the FIFO was full.
*/
override fn GetReceiveOverflowCount() int
{
return overflows
}
/**
* Clears both error counters; clear wins over events on its edge.
*/
override fn ClearErrors()
{
errorClearRequest = !errorClearRequest
while (errorClearCompleted != errorClearRequest) {
state {
}
}
}
/**
* Presents combinational FIFO requests; clear suppresses other operations.
*/
process Requests[]()
{
this.receiveCount = rxCount
txClear = txClearRequest != txClearCompleted
rxClear = rxClearRequest != rxClearCompleted
txPush = !txClear && txRequest != txCompleted
rxPop = !rxClear && rxRequest != rxCompleted
rxPush = !rxClear && rxValid == 0b1
rxPayload = rxData as byte
txPop = !txClear && launchPermission && !launchPending && txActive == 0b0 && txCount > 0
}
/**
* Captures API outcomes and launch bytes exactly at the FIFO transfer edge.
*/
process Transfers()
{
state Tick {
if (txClear) {
txClearCompleted = txClearRequest
} elif (txPush) {
txAccepted = txPushAccepted
txCompleted = txRequest
}
if (rxClear) {
rxClearCompleted = rxClearRequest
} elif (rxPop) {
rxAccepted = rxPopAccepted
rxResult = rxHead
rxCompleted = rxRequest
}
txValid = 0b0
if (txPopAccepted) {
txData = txHead as logic[8]
txValid = 0b1
launchPending = true
} elif (txActive == 0b1) {
launchPending = false
}
goto Tick
}
}
/**
* Owns both counters; rejected reception during explicit clear is intentional.
*/
process Errors()
{
state Tick {
if (errorClearRequest != errorClearCompleted) {
frameErrors = 0
overflows = 0
errorClearCompleted = errorClearRequest
} else {
if (rxError == 0b1) {
frameErrors++
}
if (rxPush && !rxPushAccepted) {
overflows++
}
}
goto Tick
}
}
}