Skip to content

Commit

Permalink
phy/ecp5rgmii.py: Add support for dynamic link speeds
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanG077 committed Jul 16, 2023
1 parent 64cceb2 commit b8c7408
Showing 1 changed file with 155 additions and 18 deletions.
173 changes: 155 additions & 18 deletions liteeth/phy/ecp5rgmii.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,76 @@
from liteeth.common import *
from liteeth.phy.common import *

# LiteEth PHY RGMII LINK Status --------------------------------------------------------------------

class LiteEthOneHotLinkSpeed(LiteXModule):
def __init__(self):
# Encode link status as one hot
self.link_10M = Signal()
self.link_100M = Signal()
self.link_1G = Signal()
self.tx_clk_en = Signal()

# LiteEth PHY RGMII TX -----------------------------------------------------------------------------

class LiteEthPHYRGMIITX(LiteXModule):
def __init__(self, pads):
def __init__(self, pads, link_speed=None):
self.sink = sink = stream.Endpoint(eth_phy_description(8))

# # #

tx_ctl_oddrx1f = Signal()
tx_data_oddrx1f = Signal(4)

valid = Signal()
data = Signal(8)
ready = Signal()

if link_speed is not None:
ready_next = Signal()
nibble_select = Signal()

# If link speed is not 1G we need to take care
# that the DDR lines only transfer 4-bit of data at a time
# This means we need to provide back pressure on non-1G link speeds
self.comb += ready_next.eq(~ready & sink.valid)

self.sync += If(link_speed.link_1G,
data.eq(sink.data)
).Elif(nibble_select,
data.eq(Cat(sink.data[0:4], sink.data[0:4]))
).Else(
data.eq(Cat(sink.data[4:8], sink.data[4:8]))
)

self.sync += valid.eq(sink.valid),

self.sync += If(link_speed.link_1G,
ready.eq(1),
).Elif(link_speed.tx_clk_en,
ready.eq(ready_next)
).Else(
ready.eq(0)
)

self.sync += If(link_speed.tx_clk_en & sink.valid,
nibble_select.eq(~nibble_select)
).Else(
nibble_select.eq(nibble_select)
)

else:
self.comb += [
ready.eq(1),
valid.eq(sink.valid),
data.eq(sink.data)
]

self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = sink.valid,
i2 = sink.valid,
i1 = valid,
i2 = valid,
o = tx_ctl_oddrx1f,
),
Instance("DELAYG",
Expand All @@ -46,8 +100,8 @@ def __init__(self, pads):
self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = sink.data[i],
i2 = sink.data[4+i],
i1 = data[i],
i2 = data[4+i],
o = tx_data_oddrx1f[i],
),
Instance("DELAYG",
Expand All @@ -57,12 +111,12 @@ def __init__(self, pads):
o_Z = pads.tx_data[i],
)
]
self.comb += sink.ready.eq(1)
self.comb += sink.ready.eq(ready)

# LiteEth PHY RGMII RX -----------------------------------------------------------------------------

class LiteEthPHYRGMIIRX(LiteXModule):
def __init__(self, pads, rx_delay=2e-9, with_inband_status=True):
def __init__(self, pads, rx_delay=2e-9, with_inband_status=True, link_speed=None):
self.source = source = stream.Endpoint(eth_phy_description(8))

if with_inband_status:
Expand Down Expand Up @@ -123,19 +177,42 @@ def __init__(self, pads, rx_delay=2e-9, with_inband_status=True):
o2 = rx_data[i+4],
)
]
self.sync += rx_data_reg.eq(rx_data)

rx_ctl_reg_d = Signal(2)
self.sync += rx_ctl_reg_d.eq(rx_ctl_reg)
# 1G has DDR, 100m and 10m are SDR on Rising edge only
valid_reg = Signal()

if link_speed is not None:
self.sync += If(link_speed.link_1G,
rx_data_reg.eq(rx_data),
valid_reg.eq(rx_ctl[0])
).Elif(rx_ctl[0],
rx_data_reg.eq(Cat(rx_data[0:4], rx_data_reg[0:4])),
valid_reg.eq(~valid_reg)
)
else:
self.sync += [
rx_data_reg.eq(rx_data),
valid_reg.eq(rx_ctl[0])
]

valid_reg_d = Signal()
self.sync += valid_reg_d.eq(valid_reg)

last = Signal()
self.comb += last.eq(~rx_ctl_reg[0] & rx_ctl_reg_d[0])
self.comb += last.eq(~valid_reg & valid_reg_d)
self.sync += [
source.valid.eq(rx_ctl_reg[0]),
source.valid.eq(valid_reg),
source.data.eq(rx_data_reg)
]
self.comb += source.last.eq(last)

if link_speed is not None:
self.sync += If(rx_ctl == 0b00,
link_speed.link_10M.eq(rx_data[1:3] == 0b00),
link_speed.link_100M.eq(rx_data[1:3] == 0b01),
link_speed.link_1G.eq(rx_data[1:3] == 0b10),
)

if with_inband_status:
self.sync += [
If(rx_ctl == 0b00,
Expand All @@ -148,7 +225,7 @@ def __init__(self, pads, rx_delay=2e-9, with_inband_status=True):
# LiteEth PHY RGMII CRG ----------------------------------------------------------------------------

class LiteEthPHYRGMIICRG(LiteXModule):
def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, tx_clk=None):
def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, tx_clk=None, link_speed=None):
self._reset = CSRStorage()

# # #
Expand All @@ -167,12 +244,64 @@ def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, tx_clk=N
tx_delay_taps = int(tx_delay/25e-12) # 25ps per tap
assert tx_delay_taps < 128

rising_edge = Signal()
falling_edge = Signal()

if link_speed is not None:
with_switch = Signal()
counter = Signal(6)
counter_switch = Signal(5)
counter_max = Signal(6)

# clock divider for 100M and 10M
self.sync += If(link_speed.link_10M,
with_switch.eq(0),
counter_switch.eq(25),
counter_max.eq(49)
).Elif(link_speed.link_100M,
with_switch.eq(1),
counter_switch.eq(2),
counter_max.eq(4)
).Else(
with_switch.eq(1),
counter_switch.eq(0),
counter_max.eq(0)
)

self.sync += If(counter >= counter_max,
counter.eq(0)
).Else(
counter.eq(counter + 1)
)

at_switch = Signal()
self.comb += at_switch.eq(counter == counter_switch)

self.sync += If(with_switch & at_switch,
rising_edge.eq(1),
falling_edge.eq(0),
).Elif(counter < counter_switch,
rising_edge.eq(1),
falling_edge.eq(1),
).Else(
rising_edge.eq(0),
falling_edge.eq(0)
)

self.sync += link_speed.tx_clk_en.eq(at_switch)

else:
self.comb += [
rising_edge.eq(1),
falling_edge.eq(0)
]

eth_tx_clk_o = Signal()
self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = 1,
i2 = 0,
i1 = rising_edge,
i2 = falling_edge,
o = eth_tx_clk_o,
),
Instance("DELAYG",
Expand Down Expand Up @@ -207,10 +336,18 @@ def __init__(self, clock_pads, pads, with_hw_init_reset=True,
rx_delay = 2e-9,
with_inband_status = True,
tx_clk = None,
dyn_link_speed = False
):
self.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset, tx_delay, tx_clk)
self.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads))
self.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads, rx_delay, with_inband_status))

assert not dyn_link_speed or tx_clk is not None

link_speed = None
if dyn_link_speed:
link_speed = LiteEthOneHotLinkSpeed()

self.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads, rx_delay, with_inband_status, link_speed))
self.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset, tx_delay, tx_clk, link_speed)
self.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads, link_speed))
self.sink, self.source = self.tx.sink, self.rx.source

if hasattr(pads, "mdc"):
Expand Down

0 comments on commit b8c7408

Please sign in to comment.