From: Sebastien Bourdeauducq Date: Sun, 17 Mar 2013 13:43:10 +0000 (+0100) Subject: dvisampler: add clocking and phase detector X-Git-Tag: 24jan2021_ls180~3026 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9f02ced39e89b829504744f5f03558b6933ff1ea;p=litex.git dvisampler: add clocking and phase detector --- diff --git a/build.py b/build.py index a4e332fe..21f8fd12 100755 --- a/build.py +++ b/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"]: diff --git a/milkymist/dvisampler/__init__.py b/milkymist/dvisampler/__init__.py index 0d1c4d76..e0f2fda5 100644 --- a/milkymist/dvisampler/__init__.py +++ b/milkymist/dvisampler/__init__.py @@ -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() + 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" - setattr(self, name, Signal(name=name)) - - self.submodules.edid = EDID() - self.sda = self.edid.sda - self.scl = self.edid.scl + 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) + ] diff --git a/milkymist/dvisampler/clocking.py b/milkymist/dvisampler/clocking.py new file mode 100644 index 00000000..b326ce60 --- /dev/null +++ b/milkymist/dvisampler/clocking.py @@ -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) diff --git a/milkymist/dvisampler/datacapture.py b/milkymist/dvisampler/datacapture.py new file mode 100644 index 00000000..630af4cf --- /dev/null +++ b/milkymist/dvisampler/datacapture.py @@ -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)