235 lines
6.1 KiB
Python
235 lines
6.1 KiB
Python
from math import ceil
|
|
|
|
from migen.fhdl.std import *
|
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
from migen.genlib.fsm import FSM, NextState
|
|
|
|
from lib.sata.k7sataphy.std import *
|
|
from lib.sata.k7sataphy.gtx import GTXE2_COMMON
|
|
|
|
class K7SATAPHYReconfig(Module):
|
|
def __init__(self, channel_drp, mmcm_drp):
|
|
self.speed = Signal(3)
|
|
|
|
###
|
|
|
|
drp_sel = Signal()
|
|
drp = DRPBus()
|
|
self.comb += \
|
|
If(drp_sel,
|
|
drp.connect(mmcm_drp)
|
|
).Else(
|
|
drp.connect(channel_drp)
|
|
)
|
|
# Todo
|
|
|
|
class K7SATAPHYCRG(Module):
|
|
def __init__(self, pads, gtx, clk_freq, default_speed):
|
|
self.reset = Signal()
|
|
self.ready = Signal()
|
|
|
|
self.clock_domains.cd_sata_tx = ClockDomain()
|
|
self.clock_domains.cd_sata_rx = ClockDomain()
|
|
|
|
# CPLL
|
|
# (SATA3) 150MHz / VCO @ 3GHz / Line rate @ 6Gbps
|
|
# (SATA2 & SATA1) VCO still @ 3 GHz, Line rate is decreased with output dividers.
|
|
# When changing rate, reconfiguration of the CPLL over DRP is needed to:
|
|
# - update the output divider
|
|
# - update the equalizer configuration (specific for each line rate).
|
|
refclk = Signal()
|
|
self.specials += Instance("IBUFDS_GTE2",
|
|
i_CEB=0,
|
|
i_I=pads.refclk_p,
|
|
i_IB=pads.refclk_n,
|
|
o_O=refclk
|
|
)
|
|
self.comb += gtx.gtrefclk0.eq(refclk)
|
|
|
|
# QPLL
|
|
# not used be need to be there... see AR43339...
|
|
gtx_common = GTXE2_COMMON()
|
|
self.comb += [
|
|
gtx_common.refclk0.eq(refclk),
|
|
gtx.qpllclk.eq(gtx_common.qpllclk),
|
|
gtx.qpllrefclk.eq(gtx_common.qpllrefclk),
|
|
]
|
|
|
|
# TX clocking
|
|
# (SATA3) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 300MHz (16-bits)
|
|
# (SATA2) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 150MHz (16-bits)
|
|
# (SATA1) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 75MHz (16-bits)
|
|
# When changing rate, reconfiguration of the MMCM is needed to update the output divider.
|
|
mmcm_reset = Signal()
|
|
mmcm_locked = Signal()
|
|
mmcm_drp = DRPBus()
|
|
mmcm_fb = Signal()
|
|
mmcm_clk_i = Signal()
|
|
mmcm_clk0_o = Signal()
|
|
mmcm_clk1_o = Signal()
|
|
mmcm_div_config = {
|
|
"SATA1" : 16,
|
|
"SATA2" : 8,
|
|
"SATA3" : 4
|
|
}
|
|
mmcm_div = mmcm_div_config[default_speed]
|
|
self.specials += [
|
|
Instance("BUFG", i_I=gtx.txoutclk, o_O=mmcm_clk_i),
|
|
Instance("MMCME2_ADV",
|
|
p_BANDWIDTH="HIGH", p_COMPENSATION="ZHOLD", i_RST=mmcm_reset, o_LOCKED=mmcm_locked,
|
|
|
|
# DRP
|
|
i_DCLK=mmcm_drp.clk, i_DEN=mmcm_drp.en, o_DRDY=mmcm_drp.rdy, i_DWE=mmcm_drp.we,
|
|
i_DADDR=mmcm_drp.addr, i_DI=mmcm_drp.di, o_DO=mmcm_drp.do,
|
|
|
|
# VCO
|
|
p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=6.666,
|
|
p_CLKFBOUT_MULT_F=8.000, p_CLKFBOUT_PHASE=0.000, p_DIVCLK_DIVIDE=1,
|
|
i_CLKIN1=mmcm_clk_i, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb,
|
|
|
|
# CLK0
|
|
p_CLKOUT0_DIVIDE_F=mmcm_div, p_CLKOUT0_PHASE=0.000, o_CLKOUT0=mmcm_clk0_o,
|
|
|
|
# CLK1
|
|
p_CLKOUT1_DIVIDE=mmcm_div*2, p_CLKOUT1_PHASE=0.000, o_CLKOUT1=mmcm_clk1_o,
|
|
),
|
|
Instance("BUFG", i_I=mmcm_clk0_o, o_O=self.cd_sata_tx.clk),
|
|
]
|
|
self.comb += [
|
|
gtx.txusrclk.eq(self.cd_sata_tx.clk),
|
|
gtx.txusrclk2.eq(self.cd_sata_tx.clk)
|
|
]
|
|
|
|
# RX clocking
|
|
# (SATA3) sata_rx recovered clk @ 300MHz from GTX RXOUTCLK
|
|
# (SATA2) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK
|
|
# (SATA1) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK
|
|
self.specials += [
|
|
Instance("BUFG", i_I=gtx.rxoutclk, o_O=self.cd_sata_rx.clk),
|
|
]
|
|
self.comb += [
|
|
gtx.rxusrclk.eq(self.cd_sata_rx.clk),
|
|
gtx.rxusrclk2.eq(self.cd_sata_rx.clk)
|
|
]
|
|
|
|
# Configuration Reset
|
|
# After configuration, GTX's resets have to stay low for at least 500ns
|
|
# See AR43482
|
|
reset_en = Signal()
|
|
clk_period_ns = 1000000000/clk_freq
|
|
reset_en_cnt_max = ceil(500/clk_period_ns)
|
|
reset_en_cnt = Signal(max=reset_en_cnt_max, reset=reset_en_cnt_max-1)
|
|
self.sync += If(~reset_en, reset_en_cnt.eq(reset_en_cnt-1))
|
|
self.comb += reset_en.eq(reset_en_cnt == 0)
|
|
|
|
# TX Reset FSM
|
|
tx_reset_fsm = FSM(reset_state="IDLE")
|
|
self.submodules += tx_reset_fsm
|
|
tx_reset_fsm.act("IDLE",
|
|
gtx.txuserrdy.eq(0),
|
|
gtx.gttxreset.eq(0),
|
|
gtx.txdlysreset.eq(0),
|
|
If(reset_en,
|
|
NextState("RESET_ALL"),
|
|
)
|
|
)
|
|
tx_reset_fsm.act("RESET_ALL",
|
|
gtx.txuserrdy.eq(0),
|
|
gtx.gttxreset.eq(1),
|
|
gtx.txdlysreset.eq(1),
|
|
If(gtx.cplllock & mmcm_locked,
|
|
NextState("RELEASE_GTXRESET")
|
|
)
|
|
)
|
|
tx_reset_fsm.act("RELEASE_GTXRESET",
|
|
gtx.txuserrdy.eq(1),
|
|
gtx.gttxreset.eq(0),
|
|
gtx.txdlysreset.eq(1),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
).Elif(gtx.txresetdone,
|
|
NextState("RELEASE_DLYRESET")
|
|
)
|
|
)
|
|
tx_reset_fsm.act("RELEASE_DLYRESET",
|
|
gtx.txuserrdy.eq(1),
|
|
gtx.gttxreset.eq(0),
|
|
gtx.txdlysreset.eq(0),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
).Elif(gtx.txdlysresetdone,
|
|
NextState("READY")
|
|
)
|
|
)
|
|
tx_reset_fsm.act("READY",
|
|
gtx.txuserrdy.eq(1),
|
|
gtx.gttxreset.eq(0),
|
|
gtx.txdlysreset.eq(0),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
)
|
|
)
|
|
|
|
# RX Reset FSM
|
|
rx_reset_fsm = FSM(reset_state="IDLE")
|
|
self.submodules += rx_reset_fsm
|
|
rx_reset_fsm.act("IDLE",
|
|
gtx.rxuserrdy.eq(0),
|
|
gtx.gtrxreset.eq(0),
|
|
gtx.rxdlysreset.eq(0),
|
|
If(reset_en,
|
|
NextState("RESET_ALL"),
|
|
)
|
|
)
|
|
rx_reset_fsm.act("RESET_ALL",
|
|
gtx.rxuserrdy.eq(0),
|
|
gtx.gtrxreset.eq(1),
|
|
gtx.rxdlysreset.eq(1),
|
|
If(gtx.cplllock & mmcm_locked,
|
|
NextState("RELEASE_GTXRESET")
|
|
)
|
|
)
|
|
rx_reset_fsm.act("RELEASE_GTXRESET",
|
|
gtx.rxuserrdy.eq(1),
|
|
gtx.gtrxreset.eq(0),
|
|
gtx.rxdlysreset.eq(1),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
).Elif(gtx.rxresetdone,
|
|
NextState("RELEASE_DLYRESET")
|
|
)
|
|
)
|
|
rx_reset_fsm.act("RELEASE_DLYRESET",
|
|
gtx.rxuserrdy.eq(1),
|
|
gtx.gtrxreset.eq(0),
|
|
gtx.rxdlysreset.eq(0),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
).Elif(gtx.rxdlysresetdone,
|
|
NextState("READY")
|
|
)
|
|
)
|
|
rx_reset_fsm.act("READY",
|
|
gtx.rxuserrdy.eq(1),
|
|
gtx.gtrxreset.eq(0),
|
|
gtx.rxdlysreset.eq(0),
|
|
If(self.reset,
|
|
NextState("RESET_ALL")
|
|
)
|
|
)
|
|
|
|
# Ready
|
|
self.comb += self.ready.eq(tx_reset_fsm.ongoing("READY") & rx_reset_fsm.ongoing("READY"))
|
|
|
|
# Reset PLL
|
|
self.comb += gtx.cpllreset.eq(self.reset | ~reset_en)
|
|
|
|
# Reset for SATA TX/RX clock domains
|
|
self.specials += [
|
|
AsyncResetSynchronizer(self.cd_sata_tx, ~self.ready),
|
|
AsyncResetSynchronizer(self.cd_sata_rx, ~self.ready),
|
|
]
|
|
|
|
# Dynamic Reconfiguration
|
|
self.submodules.reconfig = K7SATAPHYReconfig(mmcm_drp, gtx.drp)
|