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"]:
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)
+ ]
--- /dev/null
+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)
--- /dev/null
+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)