mirror of
https://github.com/enjoy-digital/liteeth.git
synced 2025-01-03 03:43:37 -05:00
phy: add s7rgmii
This commit is contained in:
parent
6b39b0f674
commit
09e6b3a8d7
1 changed files with 176 additions and 0 deletions
176
liteeth/phy/s7rgmii.py
Normal file
176
liteeth/phy/s7rgmii.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
# RGMII PHY for 7-Series Xilinx FPGA
|
||||
from liteeth.common import *
|
||||
|
||||
from litex.gen.genlib.io import DDROutput
|
||||
from litex.gen.genlib.misc import WaitTimer
|
||||
from litex.gen.genlib.fsm import FSM, NextState
|
||||
|
||||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
|
||||
class LiteEthPHYRGMIITX(Module):
|
||||
def __init__(self, pads, pads_register=True):
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
tx_ctl_obuf = Signal()
|
||||
tx_data_obuf = Signal(4)
|
||||
|
||||
self.specials += [
|
||||
Instance("ODDR",
|
||||
p_DDR_CLK_EDGE="SAME_EDGE",
|
||||
i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
|
||||
i_D1=sink.stb, i_D2=sink.stb, o_Q=tx_ctl_obuf
|
||||
),
|
||||
Instance("OBUF", i_I=tx_ctl_obuf, o_O=pads.tx_ctl)
|
||||
]
|
||||
for i in range(4):
|
||||
self.specials += [
|
||||
Instance("ODDR",
|
||||
p_DDR_CLK_EDGE="SAME_EDGE",
|
||||
i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
|
||||
i_D1=sink.data[i], i_D2=sink.data[4+i], o_Q=tx_data_obuf[i],
|
||||
),
|
||||
Instance("OBUF", i_I=tx_data_obuf[i], o_O=pads.tx_data[i])
|
||||
]
|
||||
self.comb += sink.ack.eq(1)
|
||||
|
||||
|
||||
class LiteEthPHYRGMIIRX(Module):
|
||||
def __init__(self, pads):
|
||||
self.source = source = Source(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
rx_ctl_ibuf = Signal()
|
||||
rx_ctl_idelay = Signal()
|
||||
rx_ctl = Signal()
|
||||
rx_data_ibuf = Signal(4)
|
||||
rx_data_idelay = Signal(4)
|
||||
rx_data = Signal(8)
|
||||
|
||||
self.specials += [
|
||||
Instance("IBUF", i_I=pads.rx_ctl, o_O=rx_ctl_ibuf),
|
||||
Instance("IDELAYE2",
|
||||
p_IDELAY_TYPE="FIXED", #p_IDELAY_VALUE=0,
|
||||
i_C=0, i_LD=0, i_CE=0, i_LDPIPEEN=0, i_INC=0,
|
||||
i_IDATAIN=rx_ctl_ibuf, o_DATAOUT=rx_ctl_idelay
|
||||
),
|
||||
Instance("IDDR",
|
||||
p_DDR_CLK_EDGE="SAME_EDGE_PIPELINED",
|
||||
i_C=ClockSignal("eth_rx"), i_CE=1, i_S=0, i_R=0,
|
||||
i_D=rx_ctl_idelay, o_Q1=rx_ctl, #o_Q2=,
|
||||
)
|
||||
]
|
||||
for i in range(4):
|
||||
self.specials += [
|
||||
Instance("IBUF", i_I=pads.rx_data[i], o_O=rx_data_ibuf[i]),
|
||||
Instance("IDELAYE2",
|
||||
p_IDELAY_TYPE="FIXED", #p_IDELAY_VALUE=0,
|
||||
i_C=0, i_LD=0, i_CE=0, i_LDPIPEEN=0, i_INC=0,
|
||||
i_IDATAIN=rx_data_ibuf[i], o_DATAOUT=rx_data_idelay[i]
|
||||
),
|
||||
Instance("IDDR",
|
||||
p_DDR_CLK_EDGE="SAME_EDGE_PIPELINED",
|
||||
i_C=ClockSignal("eth_rx"), i_CE=1, i_S=0, i_R=0,
|
||||
i_D=rx_data_idelay[i], o_Q1=rx_data[i], o_Q2=rx_data[i+4],
|
||||
)
|
||||
]
|
||||
|
||||
rx_ctl_d = Signal()
|
||||
self.sync += rx_ctl_d.eq(rx_ctl)
|
||||
|
||||
sop = Signal()
|
||||
eop = Signal()
|
||||
self.comb += [
|
||||
sop.eq(rx_ctl & ~rx_ctl_d),
|
||||
eop.eq(~rx_ctl & rx_ctl_d)
|
||||
]
|
||||
self.sync += [
|
||||
source.stb.eq(rx_ctl),
|
||||
source.sop.eq(sop),
|
||||
source.data.eq(rx_data)
|
||||
]
|
||||
self.comb += source.eop.eq(eop)
|
||||
|
||||
|
||||
class LiteEthPHYRGMIICRG(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset):
|
||||
self._reset = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
self.clock_domains.cd_eth_rx = ClockDomain()
|
||||
self.clock_domains.cd_eth_tx = ClockDomain()
|
||||
self.clock_domains.cd_eth_tx90 = ClockDomain(reset_less=True)
|
||||
|
||||
|
||||
# RX
|
||||
eth_rx_clk_ibuf = Signal()
|
||||
self.specials += [
|
||||
Instance("IBUF", i_I=clock_pads.rx, o_O=eth_rx_clk_ibuf),
|
||||
Instance("BUFG", i_I=eth_rx_clk_ibuf, o_O=self.cd_eth_rx.clk)
|
||||
]
|
||||
|
||||
# TX
|
||||
pll_locked = Signal()
|
||||
pll_fb = Signal()
|
||||
pll_clk_tx = Signal()
|
||||
pll_clk_tx90 = Signal()
|
||||
eth_tx_clk_obuf = Signal()
|
||||
self.specials += [
|
||||
Instance("PLLE2_BASE",
|
||||
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
|
||||
|
||||
# VCO @ 1000 MHz
|
||||
p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0,
|
||||
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
|
||||
i_CLKIN1=ClockSignal("eth_rx"), i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
|
||||
|
||||
# 125 MHz
|
||||
p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0,
|
||||
o_CLKOUT0=pll_clk_tx,
|
||||
|
||||
# 125 MHz
|
||||
p_CLKOUT1_DIVIDE=8, p_CLKOUT1_PHASE=90.0,
|
||||
o_CLKOUT1=pll_clk_tx90
|
||||
),
|
||||
Instance("BUFG", i_I=pll_clk_tx, o_O=self.cd_eth_tx.clk),
|
||||
Instance("BUFG", i_I=pll_clk_tx90, o_O=self.cd_eth_tx90.clk),
|
||||
Instance("ODDR",
|
||||
p_DDR_CLK_EDGE="SAME_EDGE",
|
||||
i_C=ClockSignal("eth_tx90"), i_CE=1, i_S=0, i_R=0,
|
||||
i_D1=1, i_D2=0, o_Q=eth_tx_clk_obuf
|
||||
),
|
||||
Instance("OBUF", i_I=eth_tx_clk_obuf, o_O=clock_pads.tx)
|
||||
]
|
||||
|
||||
# Reset
|
||||
if with_hw_init_reset:
|
||||
reset = Signal()
|
||||
counter_done = Signal()
|
||||
counter = Signal(max=512)
|
||||
counter_ce = Signal()
|
||||
self.sync += If(counter_ce, counter.eq(counter + 1))
|
||||
self.comb += [
|
||||
counter_done.eq(counter == 256),
|
||||
counter_ce.eq(~counter_done),
|
||||
reset.eq(~counter_done | self._reset.storage)
|
||||
]
|
||||
else:
|
||||
reset = self._reset.storage
|
||||
self.comb += pads.rst_n.eq(~reset)
|
||||
self.specials += [
|
||||
AsyncResetSynchronizer(self.cd_eth_tx, reset),
|
||||
AsyncResetSynchronizer(self.cd_eth_rx, reset),
|
||||
]
|
||||
|
||||
|
||||
class LiteEthPHYRGMII(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset=True):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset)
|
||||
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads))
|
||||
self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads))
|
||||
self.sink, self.source = self.tx.sink, self.rx.source
|
Loading…
Reference in a new issue