dvisampler: add clocking and phase detector
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 17 Mar 2013 13:43:10 +0000 (14:43 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 17 Mar 2013 13:43:10 +0000 (14:43 +0100)
build.py
milkymist/dvisampler/__init__.py
milkymist/dvisampler/clocking.py [new file with mode: 0644]
milkymist/dvisampler/datacapture.py [new file with mode: 0644]

index a4e332fe856e6e5f42138e25043275c6959fab98..21f8fd128157a0a4bab41f0749879ea74fce82e5 100755 (executable)
--- 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"]:
index 0d1c4d768a8b7cda5e5ab3f016943294024a7307..e0f2fda5f5436cdf0f20f1bc8ae5c27f38b183e9 100644 (file)
@@ -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 (file)
index 0000000..b326ce6
--- /dev/null
@@ -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 (file)
index 0000000..630af4c
--- /dev/null
@@ -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)