dvisampler: software controlled phase detector

This commit is contained in:
Sebastien Bourdeauducq 2013-03-21 00:46:29 +01:00
parent 28cb97068c
commit 0a14c3714b
4 changed files with 87 additions and 60 deletions

View File

@ -46,10 +46,10 @@ NET "asfifo*/preset_empty*" TIG;
NET "{dviclk0}" TNM_NET = "GRPdviclk0"; NET "{dviclk0}" TNM_NET = "GRPdviclk0";
NET "{dviclk0}" CLOCK_DEDICATED_ROUTE = FALSE; NET "{dviclk0}" CLOCK_DEDICATED_ROUTE = FALSE;
TIMESPEC "TSdviclk0" = PERIOD "GRPdviclk0" 22 ns HIGH 50%; TIMESPEC "TSdviclk0" = PERIOD "GRPdviclk0" 26.7 ns HIGH 50%;
NET "{dviclk1}" TNM_NET = "GRPdviclk1"; NET "{dviclk1}" TNM_NET = "GRPdviclk1";
NET "{dviclk1}" CLOCK_DEDICATED_ROUTE = FALSE; NET "{dviclk1}" CLOCK_DEDICATED_ROUTE = FALSE;
TIMESPEC "TSdviclk1" = PERIOD "GRPdviclk1" 22 ns HIGH 50%; TIMESPEC "TSdviclk1" = PERIOD "GRPdviclk1" 26.7 ns HIGH 50%;
""", """,
clk50=soc.crg.clk50_pad, clk50=soc.crg.clk50_pad,
phy_rx_clk=soc.crg.eth_rx_clk_pad, phy_rx_clk=soc.crg.eth_rx_clk_pad,

View File

@ -7,7 +7,7 @@ from milkymist.dvisampler.clocking import Clocking
from milkymist.dvisampler.datacapture import DataCapture from milkymist.dvisampler.datacapture import DataCapture
class DVISampler(Module, AutoReg): class DVISampler(Module, AutoReg):
def __init__(self, inversions="", debug_data_capture=True): def __init__(self, inversions=""):
self.submodules.edid = EDID() self.submodules.edid = EDID()
self.sda = self.edid.sda self.sda = self.edid.sda
self.scl = self.edid.scl self.scl = self.edid.scl
@ -17,7 +17,7 @@ class DVISampler(Module, AutoReg):
for datan in "012": for datan in "012":
name = "data" + str(datan) name = "data" + str(datan)
cap = DataCapture(8, debug_data_capture) cap = DataCapture(8)
setattr(self.submodules, name + "_cap", cap) setattr(self.submodules, name + "_cap", cap)
if datan in inversions: if datan in inversions:
name += "_n" name += "_n"

View File

@ -15,6 +15,7 @@ class Clocking(Module, AutoReg):
self.serdesstrobe = Signal() self.serdesstrobe = Signal()
self.clock_domains._cd_pix = ClockDomain() self.clock_domains._cd_pix = ClockDomain()
self.clock_domains._cd_pix5x = ClockDomain() self.clock_domains._cd_pix5x = ClockDomain()
self.clock_domains._cd_pix10x = ClockDomain()
self.clock_domains._cd_pix20x = ClockDomain() self.clock_domains._cd_pix20x = ClockDomain()
### ###
@ -24,18 +25,22 @@ class Clocking(Module, AutoReg):
pll_clk0 = Signal() pll_clk0 = Signal()
pll_clk1 = Signal() pll_clk1 = Signal()
pll_clk2 = Signal() pll_clk2 = Signal()
pll_clk3 = Signal()
self.specials += Instance("PLL_BASE", self.specials += Instance("PLL_BASE",
Instance.Parameter("CLKIN_PERIOD", 22.0), Instance.Parameter("CLKIN_PERIOD", 26.7),
Instance.Parameter("CLKFBOUT_MULT", 20), Instance.Parameter("CLKFBOUT_MULT", 20),
Instance.Parameter("CLKOUT0_DIVIDE", 1), # pix20x Instance.Parameter("CLKOUT0_DIVIDE", 1), # pix20x
Instance.Parameter("CLKOUT1_DIVIDE", 4), # pix5x Instance.Parameter("CLKOUT1_DIVIDE", 4), # pix5x
Instance.Parameter("CLKOUT2_DIVIDE", 20), # pix Instance.Parameter("CLKOUT2_DIVIDE", 20), # pix
Instance.Parameter("CLKOUT3_DIVIDE", 2), # pix10x
Instance.Parameter("COMPENSATION", "INTERNAL"), Instance.Parameter("COMPENSATION", "INTERNAL"),
Instance.Output("CLKFBOUT", clkfbout), Instance.Output("CLKFBOUT", clkfbout),
# WARNING: Do not touch the order of those clocks, or PAR fails.
Instance.Output("CLKOUT0", pll_clk0), Instance.Output("CLKOUT0", pll_clk0),
Instance.Output("CLKOUT1", pll_clk1), Instance.Output("CLKOUT1", pll_clk1),
Instance.Output("CLKOUT2", pll_clk2), Instance.Output("CLKOUT2", pll_clk2),
Instance.Output("CLKOUT3", pll_clk3),
Instance.Output("LOCKED", pll_locked), Instance.Output("LOCKED", pll_locked),
Instance.Input("CLKFBIN", clkfbout), Instance.Input("CLKFBIN", clkfbout),
Instance.Input("CLKIN", self.clkin), Instance.Input("CLKIN", self.clkin),
@ -56,6 +61,8 @@ class Clocking(Module, AutoReg):
Instance.Input("I", pll_clk1), Instance.Output("O", self._cd_pix5x.clk)) Instance.Input("I", pll_clk1), Instance.Output("O", self._cd_pix5x.clk))
self.specials += Instance("BUFG", self.specials += Instance("BUFG",
Instance.Input("I", pll_clk2), Instance.Output("O", self._cd_pix.clk)) Instance.Input("I", pll_clk2), Instance.Output("O", self._cd_pix.clk))
self.specials += Instance("BUFG",
Instance.Input("I", pll_clk3), Instance.Output("O", self._cd_pix10x.clk))
self.specials += MultiReg(locked_async, self.locked, "sys") self.specials += MultiReg(locked_async, self.locked, "sys")
self.comb += self._r_locked.field.w.eq(self.locked) self.comb += self._r_locked.field.w.eq(self.locked)

View File

@ -5,14 +5,16 @@ from migen.genlib.cdc import MultiReg, PulseSynchronizer
from migen.bank.description import * from migen.bank.description import *
class DataCapture(Module, AutoReg): class DataCapture(Module, AutoReg):
def __init__(self, ntbits, debug=False): def __init__(self, ntbits):
self.pad = Signal() self.pad = Signal()
self.serdesstrobe = Signal() self.serdesstrobe = Signal()
self.d0 = Signal() # pix5x clock domain self.d0 = Signal() # pix5x clock domain
self.d1 = Signal() # pix5x clock domain self.d1 = Signal() # pix5x clock domain
if debug: self._r_dly_ctl = RegisterRaw(4)
self._r_current_tap = RegisterField(8, READ_ONLY, WRITE_ONLY) self._r_dly_busy = RegisterField(1, READ_ONLY, WRITE_ONLY)
self._r_phase = RegisterField(2, READ_ONLY, WRITE_ONLY)
self._r_phase_reset = RegisterRaw()
### ###
@ -20,19 +22,23 @@ class DataCapture(Module, AutoReg):
pad_delayed = Signal() pad_delayed = Signal()
delay_inc = Signal() delay_inc = Signal()
delay_ce = Signal() delay_ce = Signal()
delay_cal = Signal()
delay_rst = Signal()
delay_busy = Signal()
self.specials += Instance("IODELAY2", self.specials += Instance("IODELAY2",
Instance.Parameter("DELAY_SRC", "IDATAIN"), Instance.Parameter("DELAY_SRC", "IDATAIN"),
Instance.Parameter("IDELAY_TYPE", "VARIABLE_FROM_ZERO"), Instance.Parameter("IDELAY_TYPE", "VARIABLE_FROM_HALF_MAX"),
Instance.Parameter("COUNTER_WRAPAROUND", "STAY_AT_LIMIT"), Instance.Parameter("COUNTER_WRAPAROUND", "STAY_AT_LIMIT"),
Instance.Parameter("DATA_RATE", "SDR"), Instance.Parameter("DATA_RATE", "SDR"),
Instance.Input("IDATAIN", self.pad), Instance.Input("IDATAIN", self.pad),
Instance.Output("DATAOUT", pad_delayed), Instance.Output("DATAOUT", pad_delayed),
Instance.Input("CLK", ClockSignal("pix5x")),
Instance.Input("IOCLK0", ClockSignal("pix10x")),
Instance.Input("INC", delay_inc), Instance.Input("INC", delay_inc),
Instance.Input("CE", delay_ce), Instance.Input("CE", delay_ce),
Instance.Input("RST", ResetSignal("pix5x")), Instance.Input("CAL", delay_cal),
Instance.Input("CLK", ClockSignal("pix5x")), Instance.Input("RST", delay_rst),
Instance.Input("IOCLK0", ClockSignal("pix20x")), Instance.Output("BUSY", delay_busy),
Instance.Input("CAL", 0),
Instance.Input("T", 1) Instance.Input("T", 1)
) )
@ -57,23 +63,19 @@ class DataCapture(Module, AutoReg):
Instance.Input("RST", 0) Instance.Input("RST", 0)
) )
# Transition counter # Phase detector
transitions = Signal(ntbits) lateness = Signal(ntbits, reset=2**(ntbits - 1))
lateness = Signal((ntbits + 1, True)) too_late = Signal()
pulse_inc = Signal() too_early = Signal()
pulse_dec = Signal() reset_lateness = Signal()
self.comb += [
too_late.eq(lateness == (2**ntbits - 1)),
too_early.eq(lateness == 0)
]
self.sync.pix5x += [ self.sync.pix5x += [
pulse_inc.eq(0), If(reset_lateness,
pulse_dec.eq(0), lateness.eq(2**(ntbits - 1))
If(transitions == 2**ntbits - 1, ).Elif(~delay_busy & ~too_late & ~too_early & (self.d0 != self.d1),
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, If(self.d0,
# 1 -----> 0 # 1 -----> 0
# d0p # d0p
@ -90,39 +92,57 @@ class DataCapture(Module, AutoReg):
).Else( ).Else(
lateness.eq(lateness - 1) lateness.eq(lateness - 1)
) )
), )
transitions.eq(transitions + 1)
) )
] ]
# Drive IODELAY controls # Delay control
delay_init = Signal() self.submodules.delay_done = PulseSynchronizer("pix5x", "sys")
delay_init_count = Signal(7, reset=127) delay_pending = Signal()
self.comb += delay_init.eq(delay_init_count != 0) self.sync.pix5x += [
self.sync.pix5x += If(delay_init, delay_init_count.eq(delay_init_count - 1)) self.delay_done.i.eq(0),
self.comb += [ If(~delay_pending,
delay_ce.eq(delay_init | pulse_inc | pulse_dec), If(delay_cal | delay_ce, delay_pending.eq(1))
delay_inc.eq(delay_init | pulse_inc) ).Else(
If(~delay_busy,
self.delay_done.i.eq(1),
delay_pending.eq(0)
)
)
] ]
# Debug self.submodules.do_delay_cal = PulseSynchronizer("sys", "pix5x")
if debug: self.submodules.do_delay_rst = PulseSynchronizer("sys", "pix5x")
# Transfer delay update commands to system clock domain self.submodules.do_delay_inc = PulseSynchronizer("sys", "pix5x")
pix5x_reset_sys = Signal() self.submodules.do_delay_dec = PulseSynchronizer("sys", "pix5x")
self.specials += MultiReg(ResetSignal("pix5x"), pix5x_reset_sys, "sys") self.comb += [
self.submodules.xf_inc = PulseSynchronizer("pix5x", "sys") delay_cal.eq(self.do_delay_cal.o),
self.submodules.xf_dec = PulseSynchronizer("pix5x", "sys") delay_rst.eq(self.do_delay_rst.o),
self.comb += [ delay_inc.eq(self.do_delay_inc.o),
self.xf_inc.i.eq(pulse_inc), delay_ce.eq(self.do_delay_inc.o | self.do_delay_dec.o),
self.xf_dec.i.eq(pulse_dec) ]
]
# Update tap count in system clock domain sys_delay_pending = Signal()
current_tap = Signal(8, reset=127) self.sync += [
self.comb += self._r_current_tap.field.w.eq(current_tap) If(self.do_delay_cal.i | self.do_delay_inc.i | self.do_delay_dec.i,
self.sync += If(pix5x_reset_sys, sys_delay_pending.eq(1)
current_tap.eq(127) ).Elif(self.delay_done.o,
).Elif(self.xf_inc.o & (current_tap != 0xff), sys_delay_pending.eq(0)
current_tap.eq(current_tap + 1) )
).Elif(self.xf_dec.o & (current_tap != 0), ]
current_tap.eq(current_tap - 1)
) self.comb += [
self.do_delay_cal.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[0]),
self.do_delay_rst.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[1]),
self.do_delay_inc.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[2]),
self.do_delay_dec.i.eq(self._r_dly_ctl.re & self._r_dly_ctl.r[3]),
self._r_dly_busy.field.w.eq(sys_delay_pending)
]
# Phase detector control
self.specials += MultiReg(Cat(too_late, too_early), self._r_phase.field.w)
self.submodules.do_reset_lateness = PulseSynchronizer("sys", "pix5x")
self.comb += [
reset_lateness.eq(self.do_reset_lateness.o),
self.do_reset_lateness.i.eq(self._r_phase_reset.re)
]