Livt packages
Livt.IO
Version 1.8.1
Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.
Published files (126)
- phy
- EthernetCrc.lvt
- EthernetReceiver.lvt
- EthernetResult.lvt
- EthernetTransmitter.lvt
- IEthernetLinkStatus.lvt
- IEthernetMedia.lvt
- IEthernetReceiver.lvt
- IEthernetTransmitter.lvt
- IMdioController.lvt
- IMdioPins.lvt
- IMiiPins.lvt
- IRmiiPins.lvt
- MdioController.lvt
- MiiAdapter.lvt
- MiiClearEndpoint.lvt
- MiiClockBridge.lvt
- MiiCompletionReceiver.lvt
- MiiFifoStorage.lvt
- MiiReadBuffer.lvt
- MiiReceiver.lvt
- MiiTransmitter.lvt
- RmiiAdapter.lvt
src/ethernet/RmiiAdapter.lvt
namespace Livt.IO
/**
* RMII serializer and receive-valid reconstruction in the 50 MHz REF_CLK context.
* Standard CRS_DV is decoded per nibble, including alternating tail indications.
* ENABLE_10 is experimental until ETH-011 qualification; default accepts 100M only.
*/
component RmiiAdapter<ENABLE_10: bool = false>
{
txDone: bool
txFault: bool
rxData: byte
rxValid: bool
rxStart: bool
rxEnd: bool
rxError: bool
media: IEthernetMedia
pins: IRmiiPins
link: IEthernetLinkStatus
enabled: bool
txActive: bool
txFinal: bool
txShift: logic[8]
txSymbol: int
txTicks: int
txSpeed: EthernetSpeed
new(pins: IRmiiPins, media: IEthernetMedia, link: IEthernetLinkStatus)
{
assert this.context.ticksPerSecond == 50000000, "Ethernet requires a 50 MHz context"
this.media = media
this.pins = pins
this.link = link
}
process Outputs[]()
{
media.txDone = txDone
media.txFault = txFault
media.rxData = rxData
media.rxValid = rxValid
media.rxStart = rxStart
media.rxEnd = rxEnd
media.rxError = rxError
enabled = link.ready && (link.speed == EthernetSpeed.Mbps100
|| (ENABLE_10 && link.speed == EthernetSpeed.Mbps10))
media.txReady = enabled && !media.txAbort && (!txActive
|| (txSymbol == 3 && txTicks == 0 && !txFinal && txSpeed == link.speed))
}
process Transmit()
{
state Tick {
txDone = false
txFault = false
if (!enabled || media.txAbort || (txActive && txSpeed != link.speed)) {
txFault = txActive
txActive = false
pins.txEnable = 0
pins.txData = 0
} elif (txActive && txTicks > 0) {
txTicks--
} elif (txActive && txSymbol < 3) {
txShift = txShift >> 2
pins.txData = (txShift & 0x03) as logic[2]
txSymbol++
txTicks = 0
if (txSpeed == EthernetSpeed.Mbps10) { txTicks = 9 }
} elif (txActive && txFinal) {
txActive = false
pins.txEnable = 0
pins.txData = 0
txDone = true
} elif (media.txValid) {
if ((!txActive && !media.txFirst) || (txActive && media.txFirst)) {
txFault = true
txActive = false
pins.txEnable = 0
} else {
txActive = true
txFinal = media.txLast
txShift = media.txData as logic[8]
pins.txData = (txShift & 0x03) as logic[2]
pins.txEnable = 1
txSymbol = 0
txSpeed = link.speed
txTicks = 0
if (txSpeed == EthernetSpeed.Mbps10) { txTicks = 9 }
}
} elif (txActive) {
// The wire cannot pause between bytes.
txActive = false
pins.txEnable = 0
txFault = true
}
goto Tick
}
}
process Receive()
{
var active = false
var started = false
var aligned = false
var dropping = false
var ones = 0
var ticks = 0
var rate: EthernetSpeed = EthernetSpeed.Unknown
var firstDv = false
var firstSymbol: logic[2] = 0
var second = false
var highNibble = false
var assembled: logic[8] = 0
var bad = false
state Tick {
rxValid = false
rxStart = false
rxEnd = false
rxError = false
if (!enabled || (active && rate != link.speed)) {
if (active && !dropping) {
rxStart = !started
rxEnd = true
rxError = true
}
active = false
dropping = false
ticks = 0
} elif (ticks > 0) {
ticks--
} else {
if (!active && pins.crsDv == 1) {
active = true
started = false
aligned = false
dropping = false
ones = 0
second = false
highNibble = false
bad = false
rate = link.speed
}
if (active) {
ticks = 0
if (rate == EthernetSpeed.Mbps10) { ticks = 9 }
bad = bad || pins.rxError != 0
if (dropping) {
if (pins.crsDv == 0) { active = false }
} elif (!aligned) {
if (pins.crsDv == 0 || bad || pins.rxData == 0b10) {
rxStart = !started
rxEnd = true
rxError = true
dropping = true
} elif (pins.rxData == 0b01) {
ones++
if (ones == 4) {
rxData = 0x55
rxValid = true
rxStart = !started
started = true
ones = 0
}
} elif (pins.rxData == 0b11 && (started || ones >= 3)) {
// SFD establishes octet/nibble phase even with shortened preamble.
rxData = 0xD5
rxValid = true
rxStart = !started
started = true
aligned = true
second = false
highNibble = false
} elif (pins.rxData != 0b00 || started || ones != 0) {
rxStart = !started
rxEnd = true
rxError = true
dropping = true
}
} elif (!second) {
firstSymbol = pins.rxData
firstDv = pins.crsDv == 1
second = true
} else {
second = false
if (!firstDv && pins.crsDv == 0) {
rxEnd = true
rxError = bad || highNibble
active = false
} else {
var nibble = (firstSymbol as uint) | ((pins.rxData as uint) << 2)
if (!highNibble) {
assembled = nibble as logic[8]
highNibble = true
} else {
assembled = assembled | ((nibble << 4) as logic[8])
rxData = assembled as byte
rxValid = true
rxError = bad
highNibble = false
}
}
}
}
}
goto Tick
}
}
}