Skip to content

Commit

Permalink
liteeth/phy/rmii: Add 10Mbps/100MBps dynamic speed support.
Browse files Browse the repository at this point in the history
Speed still needs to be changed manually, we could try to add automatic detection in the future.
  • Loading branch information
enjoy-digital committed Sep 26, 2024
1 parent 7f91ebb commit 880bdf4
Showing 1 changed file with 75 additions and 18 deletions.
93 changes: 75 additions & 18 deletions liteeth/phy/rmii.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,43 @@
from liteeth.common import *
from liteeth.phy.common import *

# LiteEth PHY RMII Timer ---------------------------------------------------------------------------

class LiteEthPHYRMIITimer(LiteXModule):
def __init__(self, speed):
self.rst = Signal() # i.
self.ce = Signal() # o.

# # #

timer = Signal(4)
self.comb += self.ce.eq(timer == 0)
self.sync += [
# Decrement timer.
timer.eq(timer - 1),
# Reload Timer.
If(self.ce | self.rst,
Case(speed, {
0b0: timer.eq(9), # 10Mbps.
0b1: timer.eq(0), # 100Mbps.
})
)
]

# LiteEth PHY RMII TX ------------------------------------------------------------------------------

class LiteEthPHYRMIITX(LiteXModule):
def __init__(self, pads, clk_signal):
self.sink = sink = stream.Endpoint(eth_phy_description(8))
self.sink = sink = stream.Endpoint(eth_phy_description(8))
self.speed = Signal() # 0: 10Mbps / 1: 100Mbps.

# # #

# Speed Timer for 10Mbps/100Mbps.
# -------------------------------
self.timer = timer = LiteEthPHYRMIITimer(speed=self.speed)
self.comb += timer.rst.eq(~sink.valid)

# Converter: 8-bit to 2-bit.
# --------------------------
self.converter = converter = stream.Converter(8, 2)
Expand All @@ -31,7 +60,7 @@ def __init__(self, pads, clk_signal):
# ----------------------------
self.comb += [
sink.connect(converter.sink, keep={"valid", "ready", "data"}),
converter.source.ready.eq(1),
converter.source.ready.eq(timer.ce),
]

# Output (Sync).
Expand All @@ -46,16 +75,30 @@ def __init__(self, pads, clk_signal):
class LiteEthPHYRMIIRX(LiteXModule):
def __init__(self, pads, clk_signal):
self.source = source = stream.Endpoint(eth_phy_description(8))
self.speed = Signal() # 0: 10Mbps / 1: 100Mbps.

# # #

# Input (Sync).
# -------------
crs_dv = Signal()
rx_data = Signal(2)
self.specials += SDRInput(i=pads.crs_dv, o=crs_dv, clk=clk_signal)
crs_dv_i = Signal()
rx_data_i = Signal(2)
self.specials += SDRInput(i=pads.crs_dv, o=crs_dv_i, clk=clk_signal)
for i in range(2):
self.specials += SDRInput(i=pads.rx_data[i], o=rx_data[i], clk=clk_signal)
self.specials += SDRInput(i=pads.rx_data[i], o=rx_data_i[i], clk=clk_signal)

# Speed Timer for 10Mbps/100Mbps.
# -------------------------------
self.timer = timer = LiteEthPHYRMIITimer(speed=self.speed)

# Latch Input.
# ------------
crs_dv = Signal()
rx_data = Signal(2)
self.sync += If(timer.ce,
crs_dv.eq(crs_dv_i),
rx_data.eq(rx_data_i),
)

# Converter: 2-bit to 8-bit.
# --------------------------
Expand All @@ -65,37 +108,37 @@ def __init__(self, pads, clk_signal):
# ------
# Add a delay to align the data with the frame boundaries since the end-of-frame condition
# (2 consecutive `crs_dv` signals low) is detected with a few cycles delay.
self.delay = delay = stream.Delay(layout=[("data", 8)], n=2)
self.delay = delay = stream.Delay(layout=[("data", 2)], n=2)

# Frame Delimitation.
# -------------------
crs_dv_d = Signal()
crs_first = Signal()
crs_last = Signal()
crs_run = Signal()
self.sync += crs_dv_d.eq(crs_dv)
self.comb += [
crs_dv_d = Signal()
self.comb += If(timer.ce,
crs_first.eq(crs_dv & (rx_data != 0b00)), # Start of frame on crs_dv high and non-null data.
crs_last.eq(~crs_dv & ~crs_dv_d), # End of frame on 2 consecutive crs_dv low.
]
)
self.sync += [
If(timer.ce, crs_dv_d.eq(crs_dv)),
If(crs_first, crs_run.eq(1)),
If(crs_last, crs_run.eq(0)),
]

# Datapath: Input -> Delay -> Converter -> Source.
# ------------------------------------------------
self.comb += [
delay.source.ready.eq(1), # Ready by default to flush pipeline.
delay.sink.valid.eq(crs_first | crs_run),
delay.sink.valid.eq(crs_first | (crs_run & timer.ce)),
delay.sink.data.eq(rx_data),
If(crs_run,
delay.source.ready.eq(~crs_run), # Flush pipeline when in idle.
delay.source.connect(converter.sink, keep={"data"}),
If(crs_run & timer.ce,
delay.source.connect(converter.sink, keep={"valid", "ready"}),
converter.sink.last.eq(crs_last),
delay.source.connect(converter.sink, keep={"valid", "ready", "data"})
),
converter.source.connect(source),
]

]

# LiteEth PHY RMII CRG -----------------------------------------------------------------------------

Expand Down Expand Up @@ -152,7 +195,7 @@ class LiteEthPHYRMII(LiteXModule):
dw = 8
tx_clk_freq = 50e6
rx_clk_freq = 50e6
def __init__(self, clock_pads, pads, refclk_cd="eth",
def __init__(self, clock_pads, pads, refclk_cd="eth", default_speed=1,
with_hw_init_reset = True,
with_refclk_ddr_output = True):

Expand All @@ -163,10 +206,24 @@ def __init__(self, clock_pads, pads, refclk_cd="eth",
with_refclk_ddr_output = with_refclk_ddr_output,
)

# Control/Status.
self._control = CSRStorage(fields=[
CSRField("speed", size=1, values=[
("``0b0``", "10Mbps."),
("``0b1``", "100Mbps."),
], reset=default_speed)
])
speed = Signal()
self.specials += MultiReg(self._control.fields.speed, speed, n=2)

# TX/RX.
# ------
self.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRMIITX(pads, self.crg.clk_signal))
self.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRMIIRX(pads, self.crg.clk_signal))
self.comb += [
self.tx.speed.eq(speed),
self.rx.speed.eq(speed),
]
self.sink, self.source = self.tx.sink, self.rx.source

# MDIO.
Expand Down

0 comments on commit 880bdf4

Please sign in to comment.