Livt packages
Livt.Net
Version 1.4.1
Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.
Published files (163)
src/drivers/ethernetlite/EthernetLiteDriver.lvt
namespace Livt.Net.Drivers.EthernetLite
using Livt.Net
/**
* Polled ping/pong RX and ping TX EthernetLite implementation.
* Owns transaction sequencing, register addresses and byte packing; borrowed
* receiver/transmitter components own packet and submission lifetimes.
* All components and the external slave share coordinated reset. AXI stalls
* have no timeout: reset the whole graph to recover from a nonresponsive slave.
*/
component EthernetLiteDriver<B: IEthernetLiteRegisterAccess, S: IPacketData, RX_CAPACITY: int = 128, MAX_TX_LENGTH: int = 1514>
{
// Device register map; standard untagged frames fit below all control registers.
const TX_LENGTH: logic[13] = 0x07F4
const TX_CONTROL: logic[13] = 0x07FC
const RX_PING: int = 0x1000
const RX_PONG: int = 0x1800
const RX_CONTROL_OFFSET: int = 0x07FC
// Borrowed register access and owned receive-prefix scanner.
bus: B
window: EthernetLiteReceiveWindow<RX_CAPACITY>
mac: byte[6]
// Borrowed capabilities and driver initialization/recovery state.
receiver: EthernetLiteReceiver<RX_CAPACITY>
transmitter: EthernetLiteTransmitter<S, MAX_TX_LENGTH>
initialized: bool
failed: bool
recover: bool
pollPong: bool
/**
* Binds independent RX/TX ownership components at the application boundary.
* MAC programming must complete before transmission admission is enabled.
*/
new(bus: B, receiver: EthernetLiteReceiver<RX_CAPACITY>, transmitter: EthernetLiteTransmitter<S, MAX_TX_LENGTH>, localMac: byte[6])
{
this.bus = bus
this.window = new EthernetLiteReceiveWindow<RX_CAPACITY>()
this.receiver = receiver
this.transmitter = transmitter
this.mac = localMac
}
/**
* Reports completed MAC programming and absence of a latched bus failure.
*/
public fn IsInitialized() bool
{
return this.initialized && !this.failed
}
/**
* Reports a latched device/transaction failure; source failures only affect TX.
*/
public fn HasDeviceError() bool
{
return this.failed
}
/**
* Requests reinitialization after a completed error response, not an AXI abort.
* The owner separately acknowledges RX errors and TX completion before reuse.
*/
public fn TryRecover() FrameLinkResult
{
if (!this.failed) {
return FrameLinkResult.NotReady
}
if (this.recover) {
return FrameLinkResult.Busy
}
this.recover = true
return FrameLinkResult.Accepted
}
/**
* Programs the MAC only while TX is idle, then waits for both command bits clear.
*/
fn Initialize() bool
{
var status: logic[32] = 0x00000000
if (!this.bus.Read(TX_CONTROL, status)) {
return false
}
if (status[0] == 0b1 || status[1] == 0b1) {
return true
}
for (var index = 0; index < 2; index++) {
var word: logic[32] = 0x00000000
for (var lane = 0; lane < 4; lane++) {
var offset = (index << 2) + lane
if (offset < 6) {
var octet = this.mac[offset] as logic[8]
word = word | (((octet as uint) as logic[32]) << (lane << 3))
}
}
if (!this.bus.Write((index << 2) as logic[13], word)) {
return false
}
}
if (!this.bus.Write(TX_CONTROL, 0x00000003)) {
return false
}
while (true) {
if (!this.bus.Read(TX_CONTROL, status)) {
return false
}
if (status[0] == 0b0 && status[1] == 0b0) {
break
}
}
this.transmitter.SetDeviceReady(true, false)
this.initialized = true
return true
}
/**
* Writes initialized source bytes with zero-filled final lanes. Delivered means
* the start-command write received OKAY, not physical transmission or peer receipt.
*/
fn Transmit() bool
{
var status: logic[32] = 0x00000000
if (!this.bus.Read(TX_CONTROL, status)) {
return false
}
if (status[0] == 0b1 || status[1] == 0b1) {
return true
}
var length = this.transmitter.GetPendingLength()
for (var index = 0; index < length; index = index + 4) {
var word: logic[32] = 0x00000000
for (var lane = 0; lane < 4; lane++) {
if (index + lane < length) {
var value: byte = 0x00
var result = this.transmitter.TryReadPending(index + lane, value)
if (result != PacketDataResult.Success) {
this.transmitter.Complete(FrameTransmitOutcome.DeviceFailure)
return true
}
word = word | (((value as uint) as logic[32]) << (lane << 3))
}
}
if (!this.bus.Write(index as logic[13], word)) {
return false
}
}
if (!this.bus.Write(TX_LENGTH, length as logic[32])) {
return false
}
if (!this.bus.Write(TX_CONTROL, 0x00000001)) {
return false
}
this.transmitter.Complete(FrameTransmitOutcome.Delivered)
return true
}
/**
* Alternates RX banks and copies only before publication. CRC lookahead excludes
* short-frame FCS/stale RAM without interpreting untrusted protocol lengths.
*/
fn Receive() bool
{
if (!this.receiver.TryBeginCapture()) {
return true
}
var base = RX_PING
if (this.pollPong) {
base = RX_PONG
}
this.pollPong = !this.pollPong
var control = (base + RX_CONTROL_OFFSET) as logic[13]
var status: logic[32] = 0x00000000
if (!this.bus.Read(control, status)) {
return false
}
if (status[0] == 0b0) {
this.receiver.AbandonCapture()
return true
}
this.window.Reset()
var index = 0
while (!this.window.IsFinished()) {
var word: logic[32] = 0x00000000
if (!this.bus.Read((base + index) as logic[13], word)) {
return false
}
for (var lane = 0; lane < 4; lane++) {
if (!this.window.IsFinished()) {
var value = (word >> (lane << 3)) as logic[8]
if (index + lane < RX_CAPACITY) {
if (!this.receiver.TryAppend(value as byte)) {
return false
}
}
this.window.AddByte(value as byte)
}
}
index = index + 4
}
if (!this.bus.Write(control, 0x00000000)) {
return false
}
var length = this.window.GetLength()
return this.receiver.TryPublish(length)
}
/**
* Serializes hardware access while RX ownership and TX completion remain independent.
* A device failure stops new work until explicit recovery or coordinated reset.
*/
process Service()
{
if (this.failed) {
if (!this.recover) {
continue
}
this.recover = false
this.failed = false
this.initialized = false
this.transmitter.SetDeviceReady(false, false)
}
var success = true
if (!this.initialized) {
success = this.Initialize()
} elif (this.transmitter.IsSourceBorrowed()) {
success = this.Transmit()
} else {
success = this.Receive()
}
if (!success) {
// Publish capability errors before the driver flag, and the TX terminal
// result last. Observers must never see completion before error admission.
this.transmitter.SetDeviceReady(false, true)
this.receiver.Fail()
this.failed = true
this.transmitter.Complete(FrameTransmitOutcome.DeviceFailure)
}
}
}