| 
 | 1 | +#  | 
 | 2 | +# This file is part of LiteEth, backported to MiSoC.  | 
 | 3 | +#  | 
 | 4 | +# Copyright (c) 2015-2020 Florent Kermarrec <[email protected]>  | 
 | 5 | +# Copyright (c) 2022-2022 Mikolaj Sowinski <[email protected]>  | 
 | 6 | +# SPDX-License-Identifier: BSD-2-Clause  | 
 | 7 | + | 
 | 8 | +# RGMII PHY for 7-Series Xilinx FPGA  | 
 | 9 | + | 
 | 10 | +from migen import *  | 
 | 11 | +from migen.genlib.resetsync import AsyncResetSynchronizer  | 
 | 12 | + | 
 | 13 | +from misoc.interconnect.csr import *  | 
 | 14 | +from misoc.interconnect import stream  | 
 | 15 | +from misoc.cores.liteeth_mini.common import *  | 
 | 16 | + | 
 | 17 | + | 
 | 18 | +class LiteEthPHYHWReset(Module):  | 
 | 19 | +    def __init__(self, cycles=256):  | 
 | 20 | +        self.reset = Signal()  | 
 | 21 | + | 
 | 22 | +        # # #  | 
 | 23 | + | 
 | 24 | +        counter      = Signal(max=cycles + 1)  | 
 | 25 | +        counter_done = Signal()  | 
 | 26 | +        counter_ce   = Signal()  | 
 | 27 | +        self.sync += If(counter_ce, counter.eq(counter + 1))  | 
 | 28 | +        self.comb += [  | 
 | 29 | +            counter_done.eq(counter == cycles),  | 
 | 30 | +            counter_ce.eq(~counter_done),  | 
 | 31 | +            self.reset.eq(~counter_done)  | 
 | 32 | +        ]  | 
 | 33 | + | 
 | 34 | + | 
 | 35 | +class LiteEthPHYRGMIITX(Module):  | 
 | 36 | +    def __init__(self, pads):  | 
 | 37 | +        self.sink = sink = stream.Endpoint(eth_phy_layout(8))  | 
 | 38 | + | 
 | 39 | +        # # #  | 
 | 40 | + | 
 | 41 | +        tx_ctl_obuf  = Signal()  | 
 | 42 | +        tx_data_obuf = Signal(4)  | 
 | 43 | + | 
 | 44 | +        self.specials += [  | 
 | 45 | +            Instance("ODDR",  | 
 | 46 | +                p_DDR_CLK_EDGE = "SAME_EDGE",  | 
 | 47 | +                i_C  = ClockSignal("eth_tx"),  | 
 | 48 | +                i_CE = 1,  | 
 | 49 | +                i_S  = 0,  | 
 | 50 | +                i_R  = 0,  | 
 | 51 | +                i_D1 = sink.stb,  | 
 | 52 | +                i_D2 = sink.stb,  | 
 | 53 | +                o_Q  = tx_ctl_obuf,  | 
 | 54 | +            ),  | 
 | 55 | +            Instance("OBUF",  | 
 | 56 | +                i_I = tx_ctl_obuf,  | 
 | 57 | +                o_O = pads.tx_ctl,  | 
 | 58 | +            ),  | 
 | 59 | +        ]  | 
 | 60 | +        for i in range(4):  | 
 | 61 | +            self.specials += [  | 
 | 62 | +                Instance("ODDR",  | 
 | 63 | +                    p_DDR_CLK_EDGE = "SAME_EDGE",  | 
 | 64 | +                    i_C  = ClockSignal("eth_tx"),  | 
 | 65 | +                    i_CE = 1,  | 
 | 66 | +                    i_S  = 0,  | 
 | 67 | +                    i_R  = 0,  | 
 | 68 | +                    i_D1 = sink.data[i],  | 
 | 69 | +                    i_D2 = sink.data[4+i],  | 
 | 70 | +                    o_Q  = tx_data_obuf[i],  | 
 | 71 | +                ),  | 
 | 72 | +                Instance("OBUF",  | 
 | 73 | +                    i_I = tx_data_obuf[i],  | 
 | 74 | +                    o_O = pads.tx_data[i],  | 
 | 75 | +                )  | 
 | 76 | +            ]  | 
 | 77 | +        self.comb += sink.ack.eq(1)  | 
 | 78 | + | 
 | 79 | + | 
 | 80 | +class LiteEthPHYRGMIIRX(Module):  | 
 | 81 | +    def __init__(self, pads, rx_delay=2e-9, iodelay_clk_freq=200e6):  | 
 | 82 | +        self.source = source = stream.Endpoint(eth_phy_layout(8))  | 
 | 83 | + | 
 | 84 | +        # # #  | 
 | 85 | + | 
 | 86 | +        assert iodelay_clk_freq in [200e6, 300e6, 400e6]  | 
 | 87 | +        iodelay_tap_average = 1 / (2*32 * iodelay_clk_freq)  | 
 | 88 | +        rx_delay_taps = round(rx_delay / iodelay_tap_average)  | 
 | 89 | +        assert rx_delay_taps < 32, "Exceeded ODELAYE2 max value: {} >= 32".format(rx_delay_taps)  | 
 | 90 | + | 
 | 91 | +        rx_ctl_ibuf    = Signal()  | 
 | 92 | +        rx_ctl_idelay  = Signal()  | 
 | 93 | +        rx_ctl         = Signal()  | 
 | 94 | +        rx_data_ibuf   = Signal(4)  | 
 | 95 | +        rx_data_idelay = Signal(4)  | 
 | 96 | +        rx_data        = Signal(8)  | 
 | 97 | + | 
 | 98 | +        self.specials += [  | 
 | 99 | +            Instance("IBUF", i_I=pads.rx_ctl, o_O=rx_ctl_ibuf),  | 
 | 100 | +            Instance("IDELAYE2",  | 
 | 101 | +                p_IDELAY_TYPE  = "FIXED",  | 
 | 102 | +                p_IDELAY_VALUE = rx_delay_taps,  | 
 | 103 | +                p_REFCLK_FREQUENCY = iodelay_clk_freq/1e6,  | 
 | 104 | +                i_C        = 0,  | 
 | 105 | +                i_LD       = 0,  | 
 | 106 | +                i_CE       = 0,  | 
 | 107 | +                i_LDPIPEEN = 0,  | 
 | 108 | +                i_INC      = 0,  | 
 | 109 | +                i_IDATAIN  = rx_ctl_ibuf,  | 
 | 110 | +                o_DATAOUT  = rx_ctl_idelay,  | 
 | 111 | +            ),  | 
 | 112 | +            Instance("IDDR",  | 
 | 113 | +                p_DDR_CLK_EDGE = "SAME_EDGE_PIPELINED",  | 
 | 114 | +                i_C  = ClockSignal("eth_rx"),  | 
 | 115 | +                i_CE = 1,  | 
 | 116 | +                i_S  = 0,  | 
 | 117 | +                i_R  = 0,  | 
 | 118 | +                i_D  = rx_ctl_idelay,  | 
 | 119 | +                o_Q1 = rx_ctl,  | 
 | 120 | +                o_Q2 = Signal(),  | 
 | 121 | +            )  | 
 | 122 | +        ]  | 
 | 123 | +        for i in range(4):  | 
 | 124 | +            self.specials += [  | 
 | 125 | +                Instance("IBUF",  | 
 | 126 | +                    i_I = pads.rx_data[i],  | 
 | 127 | +                    o_O = rx_data_ibuf[i],  | 
 | 128 | +                ),  | 
 | 129 | +                Instance("IDELAYE2",  | 
 | 130 | +                    p_IDELAY_TYPE  = "FIXED",  | 
 | 131 | +                    p_IDELAY_VALUE = rx_delay_taps,  | 
 | 132 | +                    p_REFCLK_FREQUENCY = iodelay_clk_freq/1e6,  | 
 | 133 | +                    i_C        = 0,  | 
 | 134 | +                    i_LD       = 0,  | 
 | 135 | +                    i_CE       = 0,  | 
 | 136 | +                    i_LDPIPEEN = 0,  | 
 | 137 | +                    i_INC      = 0,  | 
 | 138 | +                    i_IDATAIN  = rx_data_ibuf[i],  | 
 | 139 | +                    o_DATAOUT  = rx_data_idelay[i],  | 
 | 140 | +                ),  | 
 | 141 | +                Instance("IDDR",  | 
 | 142 | +                    p_DDR_CLK_EDGE = "SAME_EDGE_PIPELINED",  | 
 | 143 | +                    i_C  = ClockSignal("eth_rx"),  | 
 | 144 | +                    i_CE = 1,  | 
 | 145 | +                    i_S  = 0,  | 
 | 146 | +                    i_R  = 0,  | 
 | 147 | +                    i_D  = rx_data_idelay[i],  | 
 | 148 | +                    o_Q1 = rx_data[i],  | 
 | 149 | +                    o_Q2 = rx_data[i+4],  | 
 | 150 | +                )  | 
 | 151 | +            ]  | 
 | 152 | + | 
 | 153 | +        rx_ctl_d = Signal()  | 
 | 154 | +        self.sync += rx_ctl_d.eq(rx_ctl)  | 
 | 155 | + | 
 | 156 | +        last = Signal()  | 
 | 157 | +        self.comb += last.eq(~rx_ctl & rx_ctl_d)  | 
 | 158 | +        self.sync += [  | 
 | 159 | +            source.stb.eq(rx_ctl),  | 
 | 160 | +            source.data.eq(rx_data)  | 
 | 161 | +        ]  | 
 | 162 | +        self.comb += source.eop.eq(last)  | 
 | 163 | + | 
 | 164 | + | 
 | 165 | +class LiteEthPHYRGMIICRG(Module, AutoCSR):  | 
 | 166 | +    def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, hw_reset_cycles=256):  | 
 | 167 | +        self._reset = CSRStorage()  | 
 | 168 | + | 
 | 169 | +        # # #  | 
 | 170 | + | 
 | 171 | +        # RX clock  | 
 | 172 | +        self.clock_domains.cd_eth_rx = ClockDomain()  | 
 | 173 | +        eth_rx_clk_ibuf = Signal()  | 
 | 174 | +        self.specials += [  | 
 | 175 | +            Instance("IBUF",  | 
 | 176 | +                i_I = clock_pads.rx,  | 
 | 177 | +                o_O = eth_rx_clk_ibuf,  | 
 | 178 | +            ),  | 
 | 179 | +            Instance("BUFG",  | 
 | 180 | +                i_I = eth_rx_clk_ibuf,  | 
 | 181 | +                o_O = self.cd_eth_rx.clk,  | 
 | 182 | +            ),  | 
 | 183 | +        ]  | 
 | 184 | + | 
 | 185 | +        # TX clock  | 
 | 186 | +        self.clock_domains.cd_eth_tx         = ClockDomain()  | 
 | 187 | +        self.clock_domains.cd_eth_tx_delayed = ClockDomain(reset_less=True)  | 
 | 188 | +        tx_phase = 125e6*tx_delay*360  | 
 | 189 | +        assert tx_phase < 360  | 
 | 190 | +        pll_fb = Signal()  | 
 | 191 | +        eth_tx_clk = Signal()  | 
 | 192 | +        eth_tx_delayed_clk = Signal()  | 
 | 193 | +        self.specials += [  | 
 | 194 | +            Instance("PLLE2_BASE",  | 
 | 195 | +                     p_STARTUP_WAIT="FALSE", o_LOCKED=Signal(),  | 
 | 196 | + | 
 | 197 | +                     # VCO @ 1GHz  | 
 | 198 | +                     p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0,  | 
 | 199 | +                     p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,  | 
 | 200 | +                     i_CLKIN1=ClockSignal("eth_rx"), i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,  | 
 | 201 | + | 
 | 202 | +                     # 125MHz  | 
 | 203 | +                     p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=eth_tx_clk,  | 
 | 204 | + | 
 | 205 | +                     # 500MHz  | 
 | 206 | +                     p_CLKOUT1_DIVIDE=8, p_CLKOUT1_PHASE=tx_phase, o_CLKOUT1=eth_tx_delayed_clk,  | 
 | 207 | + | 
 | 208 | +                     p_CLKOUT2_DIVIDE=5, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=,  | 
 | 209 | +                     p_CLKOUT3_DIVIDE=5, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=,  | 
 | 210 | +                     p_CLKOUT4_DIVIDE=5, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4=  | 
 | 211 | +            ),  | 
 | 212 | +            Instance("BUFG", i_I=eth_tx_clk, o_O=self.cd_eth_tx.clk),  | 
 | 213 | +            Instance("BUFG", i_I=eth_tx_delayed_clk, o_O=self.cd_eth_tx_delayed.clk),  | 
 | 214 | +        ]  | 
 | 215 | + | 
 | 216 | +        eth_tx_clk_obuf = Signal()  | 
 | 217 | +        self.specials += [  | 
 | 218 | +            Instance("ODDR",  | 
 | 219 | +                p_DDR_CLK_EDGE = "SAME_EDGE",  | 
 | 220 | +                i_C  = ClockSignal("eth_tx_delayed"),  | 
 | 221 | +                i_CE = 1,  | 
 | 222 | +                i_S  = 0,  | 
 | 223 | +                i_R  = 0,  | 
 | 224 | +                i_D1 = 1,  | 
 | 225 | +                i_D2 = 0,  | 
 | 226 | +                o_Q  = eth_tx_clk_obuf,  | 
 | 227 | +            ),  | 
 | 228 | +            Instance("OBUF",  | 
 | 229 | +                i_I = eth_tx_clk_obuf,  | 
 | 230 | +                o_O = clock_pads.tx,  | 
 | 231 | +            )  | 
 | 232 | +        ]  | 
 | 233 | + | 
 | 234 | +        # Reset  | 
 | 235 | +        self.reset = reset = Signal()  | 
 | 236 | +        if with_hw_init_reset:  | 
 | 237 | +            self.submodules.hw_reset = LiteEthPHYHWReset(cycles=hw_reset_cycles)  | 
 | 238 | +            self.comb += reset.eq(self._reset.storage | self.hw_reset.reset)  | 
 | 239 | +        else:  | 
 | 240 | +            self.comb += reset.eq(self._reset.storage)  | 
 | 241 | +        if hasattr(pads, "rst_n"):  | 
 | 242 | +            self.comb += pads.rst_n.eq(~reset)  | 
 | 243 | +        self.specials += [  | 
 | 244 | +            AsyncResetSynchronizer(self.cd_eth_tx, reset),  | 
 | 245 | +            AsyncResetSynchronizer(self.cd_eth_rx, reset),  | 
 | 246 | +        ]  | 
 | 247 | + | 
 | 248 | + | 
 | 249 | +class LiteEthPHYRGMII(Module, AutoCSR):  | 
 | 250 | +    dw          = 8  | 
 | 251 | +    tx_clk_freq = 125e6  | 
 | 252 | +    rx_clk_freq = 125e6  | 
 | 253 | +    def __init__(self, clock_pads, pads, with_hw_init_reset=True, tx_delay=2e-9, rx_delay=2e-9,  | 
 | 254 | +            iodelay_clk_freq=200e6, hw_reset_cycles=256):  | 
 | 255 | +        self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset, tx_delay, hw_reset_cycles)  | 
 | 256 | +        self.submodules.tx  = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads))  | 
 | 257 | +        self.submodules.rx  = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads, rx_delay, iodelay_clk_freq))  | 
 | 258 | +        self.sink, self.source = self.tx.sink, self.rx.source  | 
0 commit comments