Merge pull request #143 from Icenowy/gw5rgmii

phy: add initial GW5RGMII (RGMII for Gowin Arora V series)
This commit is contained in:
enjoy-digital 2023-09-01 12:13:57 +02:00 committed by GitHub
commit 42c7e0eea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 197 additions and 0 deletions

197
liteeth/phy/gw5rgmii.py Normal file
View File

@ -0,0 +1,197 @@
#
# This file is part of LiteEth.
#
# Copyright (c) 2023 Icenowy Zheng <uwu@icenowy.me>
# Based on ecp5rgmii.py, which is:
# Copyright (c) 2019-2023 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020 Shawn Hoffman <godisgovernment@gmail.com>
#
# SPDX-License-Identifier: BSD-2-Clause
# RGMII PHY for Gowin GW5A FPGA
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen import *
from litex.build.io import DDROutput, DDRInput
from liteeth.common import *
from liteeth.phy.common import *
# LiteEth PHY RGMII TX -----------------------------------------------------------------------------
class LiteEthPHYRGMIITX(LiteXModule):
def __init__(self, pads):
self.sink = sink = stream.Endpoint(eth_phy_description(8))
# # #
tx_ctl_oddrx1f = Signal()
tx_data_oddrx1f = Signal(4)
self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = sink.valid,
i2 = sink.valid,
o = tx_ctl_oddrx1f,
),
Instance("IODELAY",
p_DYN_DLY_EN = "FALSE",
p_ADAPT_EN = "FALSE",
p_C_STATIC_DLY = 0,
i_DI = tx_ctl_oddrx1f,
o_DO = pads.tx_ctl,
)
]
for i in range(4):
self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = sink.data[i],
i2 = sink.data[4+i],
o = tx_data_oddrx1f[i],
),
Instance("IODELAY",
p_DYN_DLY_EN = "FALSE",
p_ADAPT_EN = "FALSE",
p_C_STATIC_DLY = 0,
i_DI = tx_data_oddrx1f[i],
o_DO = pads.tx_data[i],
)
]
self.comb += sink.ready.eq(1)
# LiteEth PHY RGMII RX -----------------------------------------------------------------------------
class LiteEthPHYRGMIIRX(LiteXModule):
def __init__(self, pads, rx_delay=2e-9):
self.source = source = stream.Endpoint(eth_phy_description(8))
# # #
rx_delay_taps = int(rx_delay/12.5e-12) # 12.5ps per tap
assert rx_delay_taps < 256
rx_ctl_delayf = Signal()
rx_ctl = Signal()
rx_data_delayf = Signal(4)
rx_data = Signal(8)
self.specials += [
Instance("IODELAY",
p_DYN_DLY_EN = "FALSE",
p_ADAPT_EN = "FALSE",
p_C_STATIC_DLY = rx_delay_taps,
i_DI = pads.rx_ctl,
o_DO = rx_ctl_delayf,
),
DDRInput(
clk = ClockSignal("eth_rx"),
i = rx_ctl_delayf,
o1 = rx_ctl,
o2 = Open()
)
]
for i in range(4):
self.specials += [
Instance("IODELAY",
p_DYN_DLY_EN = "FALSE",
p_ADAPT_EN = "FALSE",
p_C_STATIC_DLY = rx_delay_taps,
i_DI = pads.rx_data[i],
o_DO = rx_data_delayf[i]),
DDRInput(
clk = ClockSignal("eth_rx"),
i = rx_data_delayf[i],
o1 = rx_data[i],
o2 = rx_data[i+4],
)
]
rx_ctl_d = Signal()
self.sync += rx_ctl_d.eq(rx_ctl)
last = Signal()
self.comb += last.eq(~rx_ctl & rx_ctl_d)
self.sync += [
source.valid.eq(rx_ctl),
source.data.eq(rx_data)
]
self.comb += source.last.eq(last)
# LiteEth PHY RGMII CRG ----------------------------------------------------------------------------
class LiteEthPHYRGMIICRG(LiteXModule):
def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, tx_clk=None):
self._reset = CSRStorage()
# # #
# RX Clock
self.cd_eth_rx = ClockDomain()
self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
# TX Clock
self.cd_eth_tx = ClockDomain()
if isinstance(tx_clk, Signal):
self.comb += self.cd_eth_tx.clk.eq(tx_clk)
else:
self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk)
tx_delay_taps = int(tx_delay/12.5e-12) # 12.5ps per tap
assert tx_delay_taps < 256
self._txdelay_taps = CSRStorage(8, reset=tx_delay_taps)
eth_tx_clk_o = Signal()
self.specials += [
DDROutput(
clk = ClockSignal("eth_tx"),
i1 = 1,
i2 = 0,
o = eth_tx_clk_o,
),
Instance("IODELAY",
p_DYN_DLY_EN = "TRUE",
p_ADAPT_EN = "FALSE",
p_C_STATIC_DLY = tx_delay_taps,
i_DI = eth_tx_clk_o,
i_DLYSTEP = self._txdelay_taps.storage,
i_SDTAP = 1,
o_DO = clock_pads.tx,
)
]
# Reset
self.reset = reset = Signal()
if with_hw_init_reset:
self.hw_reset = LiteEthPHYHWReset()
self.comb += reset.eq(self._reset.storage | self.hw_reset.reset)
else:
self.comb += reset.eq(self._reset.storage)
if hasattr(pads, "rst_n"):
self.comb += pads.rst_n.eq(~reset)
self.specials += [
AsyncResetSynchronizer(self.cd_eth_tx, reset),
AsyncResetSynchronizer(self.cd_eth_rx, reset),
]
class LiteEthPHYRGMII(LiteXModule):
dw = 8
tx_clk_freq = 125e6
rx_clk_freq = 125e6
def __init__(self, clock_pads, pads, with_hw_init_reset=True,
tx_delay = 2e-9,
rx_delay = 2e-9,
tx_clk = None,
):
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))
self.sink, self.source = self.tx.sink, self.rx.source
if hasattr(pads, "mdc"):
self.mdio = LiteEthPHYMDIO(pads)