mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
dvisampler: add clocking and phase detector
This commit is contained in:
parent
0168f83523
commit
9f02ced39e
4 changed files with 226 additions and 9 deletions
11
build.py
11
build.py
|
@ -43,10 +43,19 @@ TIMESPEC "TSphy_rx_clk_io" = FROM "PADS" TO "GRPphy_rx_clk" 10 ns;
|
|||
NET "asfifo*/counter_read/gray_count*" TIG;
|
||||
NET "asfifo*/counter_write/gray_count*" TIG;
|
||||
NET "asfifo*/preset_empty*" TIG;
|
||||
|
||||
NET "{dviclk0}" TNM_NET = "GRPdviclk0";
|
||||
NET "{dviclk0}" CLOCK_DEDICATED_ROUTE = FALSE;
|
||||
TIMESPEC "TSdviclk0" = PERIOD "GRPdviclk0" 22 ns HIGH 50%;
|
||||
NET "{dviclk1}" TNM_NET = "GRPdviclk1";
|
||||
NET "{dviclk1}" CLOCK_DEDICATED_ROUTE = FALSE;
|
||||
TIMESPEC "TSdviclk1" = PERIOD "GRPdviclk1" 22 ns HIGH 50%;
|
||||
""",
|
||||
clk50=soc.crg.clk50_pad,
|
||||
phy_rx_clk=soc.crg.eth_rx_clk_pad,
|
||||
phy_tx_clk=soc.crg.eth_tx_clk_pad)
|
||||
phy_tx_clk=soc.crg.eth_tx_clk_pad,
|
||||
dviclk0=soc.dvisampler0.clk,
|
||||
dviclk1=soc.dvisampler1.clk)
|
||||
|
||||
# add Verilog sources
|
||||
for d in ["generic", "m1crg", "s6ddrphy", "minimac3"]:
|
||||
|
|
|
@ -3,16 +3,28 @@ from migen.fhdl.module import Module
|
|||
from migen.bank.description import *
|
||||
|
||||
from milkymist.dvisampler.edid import EDID
|
||||
from milkymist.dvisampler.clocking import Clocking
|
||||
from milkymist.dvisampler.datacapture import DataCapture
|
||||
|
||||
class DVISampler(Module, AutoReg):
|
||||
def __init__(self, inversions=""):
|
||||
self.clk = Signal()
|
||||
for datan in "012":
|
||||
name = "data" + str(datan)
|
||||
if datan in inversions:
|
||||
name += "_n"
|
||||
setattr(self, name, Signal(name=name))
|
||||
|
||||
def __init__(self, inversions="", debug_data_capture=True):
|
||||
self.submodules.edid = EDID()
|
||||
self.sda = self.edid.sda
|
||||
self.scl = self.edid.scl
|
||||
|
||||
self.submodules.clocking = Clocking()
|
||||
self.clk = self.clocking.clkin
|
||||
|
||||
for datan in "012":
|
||||
name = "data" + str(datan)
|
||||
cap = DataCapture(8, debug_data_capture)
|
||||
setattr(self.submodules, name + "_cap", cap)
|
||||
if datan in inversions:
|
||||
name += "_n"
|
||||
s = Signal(name=name)
|
||||
setattr(self, name, s)
|
||||
self.comb += [
|
||||
cap.pad.eq(s),
|
||||
cap.serdesstrobe.eq(self.clocking.serdesstrobe),
|
||||
cap.delay_rst.eq(~self.clocking.locked)
|
||||
]
|
||||
|
|
60
milkymist/dvisampler/clocking.py
Normal file
60
milkymist/dvisampler/clocking.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.module import Module
|
||||
from migen.fhdl.specials import Instance
|
||||
from migen.genlib.cdc import MultiReg
|
||||
from migen.bank.description import *
|
||||
|
||||
class Clocking(Module, AutoReg):
|
||||
def __init__(self):
|
||||
self.clkin = Signal()
|
||||
|
||||
self._r_pll_reset = RegisterField(reset=1)
|
||||
self._r_locked = RegisterField(1, READ_ONLY, WRITE_ONLY)
|
||||
|
||||
self.locked = Signal()
|
||||
self.serdesstrobe = Signal()
|
||||
self._cd_pix = ClockDomain()
|
||||
self._cd_pix5x = ClockDomain()
|
||||
self._cd_pix20x = ClockDomain()
|
||||
|
||||
###
|
||||
|
||||
clkfbout = Signal()
|
||||
pll_locked = Signal()
|
||||
pll_clk0 = Signal()
|
||||
pll_clk1 = Signal()
|
||||
pll_clk2 = Signal()
|
||||
self.specials += Instance("PLL_BASE",
|
||||
Instance.Parameter("CLKIN_PERIOD", 22.0),
|
||||
Instance.Parameter("CLKFBOUT_MULT", 20),
|
||||
Instance.Parameter("CLKOUT0_DIVIDE", 20), # pix
|
||||
Instance.Parameter("CLKOUT1_DIVIDE", 4), # pix5x
|
||||
Instance.Parameter("CLKOUT2_DIVIDE", 1), # pix20x
|
||||
Instance.Parameter("COMPENSATION", "INTERNAL"),
|
||||
|
||||
Instance.Output("CLKFBOUT", clkfbout),
|
||||
Instance.Output("CLKOUT0", pll_clk0),
|
||||
Instance.Output("CLKOUT1", pll_clk1),
|
||||
Instance.Output("CLKOUT2", pll_clk2),
|
||||
Instance.Output("LOCKED", pll_locked),
|
||||
Instance.Input("CLKFBIN", clkfbout),
|
||||
Instance.Input("CLKIN", self.clkin),
|
||||
Instance.Input("RST", self._r_pll_reset.field.r)
|
||||
)
|
||||
|
||||
self.specials += Instance("BUFG",
|
||||
Instance.Input("I", pll_clk0), Instance.Output("O", self._cd_pix.clk))
|
||||
self.specials += Instance("BUFG",
|
||||
Instance.Input("I", pll_clk1), Instance.Output("O", self._cd_pix5x.clk))
|
||||
locked_async = Signal()
|
||||
self.specials += Instance("BUFPLL",
|
||||
Instance.Parameter("DIVIDE", 4),
|
||||
Instance.Input("PLLIN", pll_clk2),
|
||||
Instance.ClockPort("GCLK", "pix5x"),
|
||||
Instance.Input("LOCKED", pll_locked),
|
||||
Instance.Output("IOCLK", self._cd_pix20x.clk),
|
||||
Instance.Output("LOCK", locked_async),
|
||||
Instance.Output("SERDESSTROBE", self.serdesstrobe)
|
||||
)
|
||||
self.specials += MultiReg(locked_async, self.locked, "sys")
|
||||
self.comb += self._r_locked.field.w.eq(self.locked)
|
136
milkymist/dvisampler/datacapture.py
Normal file
136
milkymist/dvisampler/datacapture.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
from migen.fhdl.structure import *
|
||||
from migen.fhdl.module import Module
|
||||
from migen.fhdl.specials import Instance
|
||||
from migen.genlib.cdc import PulseSynchronizer
|
||||
from migen.bank.description import *
|
||||
|
||||
class DataCapture(Module, AutoReg):
|
||||
def __init__(self, ntbits, debug=False):
|
||||
self.pad = Signal()
|
||||
self.serdesstrobe = Signal()
|
||||
self.delay_rst = Signal() # system clock domain
|
||||
self.d0 = Signal() # pix5x clock domain
|
||||
self.d1 = Signal() # pix5x clock domain
|
||||
|
||||
if debug:
|
||||
self._r_delay_rst = RegisterRaw()
|
||||
self._r_current_tap = RegisterField(8, READ_ONLY, WRITE_ONLY)
|
||||
|
||||
###
|
||||
|
||||
# IO
|
||||
pad_delayed = Signal()
|
||||
delay_inc = Signal()
|
||||
delay_ce = Signal()
|
||||
delay_rst = Signal()
|
||||
delay_init = Signal()
|
||||
self.specials += Instance("IDELAY2",
|
||||
Instance.Parameter("DELAY_SRC", "IDATAIN"),
|
||||
Instance.Parameter("IDELAY_TYPE", "VARIABLE_FROM_ZERO"),
|
||||
Instance.Parameter("COUNTER_WRAP_AROUND", "STAY_AT_LIMIT"),
|
||||
Instance.Input("IDATAIN", self.pad),
|
||||
Instance.Output("DATAOUT", pad_delayed),
|
||||
Instance.Input("INC", delay_inc | delay_init),
|
||||
Instance.Input("CE", delay_ce | delay_init),
|
||||
Instance.Input("RST", delay_rst),
|
||||
Instance.ClockPort("CLK"),
|
||||
Instance.Input("CAL", 0),
|
||||
Instance.Input("T", 1)
|
||||
)
|
||||
# initialize delay to 127 taps
|
||||
delay_init_count = Signal(7, reset=127)
|
||||
self.comb += delay_init.eq(delay_init_count != 0)
|
||||
self.sync += If(delay_rst,
|
||||
delay_init_count.eq(127)
|
||||
).Elif(delay_init,
|
||||
delay_init_count.eq(delay_init_count - 1)
|
||||
)
|
||||
|
||||
d0p = Signal()
|
||||
d1p = Signal()
|
||||
self.specials += Instance("ISERDES2",
|
||||
Instance.Parameter("BITSLIP_ENABLE", "FALSE"),
|
||||
Instance.Parameter("DATA_RATE", "SDR"),
|
||||
Instance.Parameter("DATA_WIDTH", 4),
|
||||
Instance.Parameter("INTERFACE_TYPE", "RETIMED"),
|
||||
Instance.Parameter("SERDES_MODE", "NONE"),
|
||||
Instance.Output("Q4", self.d0),
|
||||
Instance.Output("Q3", d0p),
|
||||
Instance.Output("Q2", self.d1),
|
||||
Instance.Output("Q1", d1p),
|
||||
Instance.Input("BITSLIP", 0),
|
||||
Instance.Input("CE0", 1),
|
||||
Instance.ClockPort("CLK0", "pix20x"),
|
||||
Instance.ClockPort("CLKDIV", "pix5x"),
|
||||
Instance.Input("D", pad_delayed),
|
||||
Instance.Input("IOCE", self.serdesstrobe),
|
||||
Instance.Input("RST", 0)
|
||||
)
|
||||
|
||||
# Transition counter
|
||||
transitions = Signal(ntbits)
|
||||
lateness = Signal((ntbits + 1, True))
|
||||
pulse_inc = Signal()
|
||||
pulse_dec = Signal()
|
||||
self.sync.pix5x += [
|
||||
pulse_inc.eq(0),
|
||||
pulse_dec.eq(0),
|
||||
If(transitions == 2**ntbits - 1,
|
||||
If(lateness[ntbits],
|
||||
pulse_inc.eq(1)
|
||||
).Else(
|
||||
pulse_dec.eq(1)
|
||||
),
|
||||
lateness.eq(0),
|
||||
transitions.eq(0)
|
||||
).Elif(self.d0 != self.d1,
|
||||
If(self.d0,
|
||||
# 1 -----> 0
|
||||
# d0p
|
||||
If(d0p,
|
||||
lateness.eq(lateness - 1)
|
||||
).Else(
|
||||
lateness.eq(lateness + 1)
|
||||
)
|
||||
).Else(
|
||||
# 0 -----> 1
|
||||
# d0p
|
||||
If(d0p,
|
||||
lateness.eq(lateness + 1)
|
||||
).Else(
|
||||
lateness.eq(lateness - 1)
|
||||
)
|
||||
),
|
||||
transitions.eq(transitions + 1)
|
||||
)
|
||||
]
|
||||
|
||||
# Send delay update commands to system (IDELAY) clock domain
|
||||
self.submodules.xf_inc = PulseSynchronizer("pix5x", "sys")
|
||||
self.submodules.xf_dec = PulseSynchronizer("pix5x", "sys")
|
||||
self.comb += [
|
||||
self.xf_inc.i.eq(pulse_inc),
|
||||
delay_inc.eq(self.xf_inc.o),
|
||||
self.xf_dec.i.eq(pulse_dec),
|
||||
delay_ce.eq(self.xf_inc.o | self.xf_dec.o)
|
||||
]
|
||||
|
||||
# Debug
|
||||
if debug:
|
||||
self.comb += delay_rst.eq(self.delay_rst | self._r_delay_rst.re)
|
||||
current_tap = self._r_current_tap.field.w
|
||||
If(delay_rst,
|
||||
current_tap.eq(0)
|
||||
).Elif(delay_ce,
|
||||
If(delay_inc,
|
||||
If(current_tap != 0xff,
|
||||
current_tap.eq(current_tap + 1)
|
||||
)
|
||||
).Else(
|
||||
If(current_tap != 0,
|
||||
current_tap.eq(current_tap - 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.comb += delay_rst.eq(self.delay_rst)
|
Loading…
Reference in a new issue