238 lines
7.8 KiB
Python
238 lines
7.8 KiB
Python
|
from migen import *
|
||
|
from migen.genlib.cdc import MultiReg, AsyncResetSynchronizer, PulseSynchronizer
|
||
|
from misoc.interconnect.csr import *
|
||
|
from misoc.interconnect.csr_eventmanager import *
|
||
|
|
||
|
from ddmtd import DDMTDSampler, DDMTD
|
||
|
from si549 import Si549
|
||
|
|
||
|
class FrequencyCounter(Module, AutoCSR):
|
||
|
def __init__(self, domains, counter_width=24):
|
||
|
self.update = CSR()
|
||
|
self.busy = CSRStatus()
|
||
|
|
||
|
counter_reset = Signal()
|
||
|
counter_stb = Signal()
|
||
|
timer = Signal(counter_width)
|
||
|
|
||
|
# # #
|
||
|
|
||
|
fsm = FSM()
|
||
|
self.submodules += fsm
|
||
|
|
||
|
fsm.act("IDLE",
|
||
|
counter_reset.eq(1),
|
||
|
If(self.update.re,
|
||
|
NextValue(timer, 2**counter_width - 1),
|
||
|
NextState("COUNTING")
|
||
|
)
|
||
|
)
|
||
|
fsm.act("COUNTING",
|
||
|
self.busy.status.eq(1),
|
||
|
If(timer != 0,
|
||
|
NextValue(timer, timer - 1)
|
||
|
).Else(
|
||
|
counter_stb.eq(1),
|
||
|
NextState("IDLE")
|
||
|
)
|
||
|
)
|
||
|
|
||
|
for domain in domains:
|
||
|
name = "counter_" + domain
|
||
|
counter_csr = CSRStatus(counter_width, name=name)
|
||
|
setattr(self, name, counter_csr)
|
||
|
|
||
|
divider = Signal(2)
|
||
|
divided = Signal()
|
||
|
divided_sys = Signal()
|
||
|
divided_sys_r = Signal()
|
||
|
divided_tick = Signal()
|
||
|
counter = Signal(counter_width)
|
||
|
|
||
|
# # #
|
||
|
|
||
|
sync_domain = getattr(self.sync, domain)
|
||
|
sync_domain +=[
|
||
|
divider.eq(divider + 1),
|
||
|
divided.eq(divider[-1])
|
||
|
]
|
||
|
self.specials += MultiReg(divided, divided_sys)
|
||
|
self.sync += divided_sys_r.eq(divided_sys)
|
||
|
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
|
||
|
|
||
|
self.sync += [
|
||
|
If(counter_stb, counter_csr.status.eq(counter)),
|
||
|
If(divided_tick, counter.eq(counter + 1)),
|
||
|
If(counter_reset, counter.eq(0))
|
||
|
]
|
||
|
|
||
|
class SkewTester(Module, AutoCSR):
|
||
|
def __init__(self, rx_synchronizer):
|
||
|
self.error = CSR()
|
||
|
|
||
|
# # #
|
||
|
|
||
|
# The RX synchronizer is tested for setup/hold violations by feeding it a
|
||
|
# toggling pattern and checking that the same toggling pattern comes out.
|
||
|
toggle_in = Signal()
|
||
|
self.sync.rtio_rx0 += toggle_in.eq(~toggle_in)
|
||
|
toggle_out = rx_synchronizer.resync(toggle_in)
|
||
|
|
||
|
toggle_out_expected = Signal()
|
||
|
self.sync += toggle_out_expected.eq(~toggle_out)
|
||
|
|
||
|
error = Signal()
|
||
|
self.sync += [
|
||
|
If(toggle_out != toggle_out_expected, error.eq(1)),
|
||
|
If(self.error.re, error.eq(0))
|
||
|
]
|
||
|
self.specials += MultiReg(error, self.error.w)
|
||
|
|
||
|
|
||
|
class WRPLL(Module, AutoCSR):
|
||
|
def __init__(self, platform, cd_ref, main_clk_se, COUNTER_BIT=32):
|
||
|
self.helper_reset = CSRStorage(reset=1)
|
||
|
self.ref_tag = CSRStatus(COUNTER_BIT)
|
||
|
self.main_tag = CSRStatus(COUNTER_BIT)
|
||
|
|
||
|
ddmtd_counter = Signal(COUNTER_BIT)
|
||
|
|
||
|
ref_tag_sys = Signal(COUNTER_BIT)
|
||
|
main_tag_sys = Signal(COUNTER_BIT)
|
||
|
ref_tag_stb_sys = Signal()
|
||
|
main_tag_stb_sys = Signal()
|
||
|
|
||
|
# # #
|
||
|
|
||
|
self.submodules.main_dcxo = Si549(platform.request("ddmtd_main_dcxo_i2c"))
|
||
|
self.submodules.helper_dcxo = Si549(platform.request("ddmtd_helper_dcxo_i2c"))
|
||
|
|
||
|
helper_dcxo_pads = platform.request("ddmtd_helper_clk")
|
||
|
self.clock_domains.cd_helper = ClockDomain()
|
||
|
self.specials += [
|
||
|
Instance("IBUFGDS",
|
||
|
i_I=helper_dcxo_pads.p, i_IB=helper_dcxo_pads.n,
|
||
|
o_O=self.cd_helper.clk),
|
||
|
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage)
|
||
|
]
|
||
|
|
||
|
self.submodules.frequency_counter = FrequencyCounter(["sys", cd_ref.name])
|
||
|
|
||
|
self.submodules.ddmtd_sampler = DDMTDSampler(cd_ref, main_clk_se)
|
||
|
|
||
|
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
|
||
|
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, self.ddmtd_sampler.ref_beating)
|
||
|
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, self.ddmtd_sampler.main_beating)
|
||
|
|
||
|
# DDMTD tags collection
|
||
|
|
||
|
self.specials += [
|
||
|
MultiReg(self.ddmtd_ref.h_tag, ref_tag_sys),
|
||
|
MultiReg(self.ddmtd_main.h_tag, main_tag_sys)
|
||
|
]
|
||
|
|
||
|
ref_tag_stb_ps = PulseSynchronizer("helper", "sys")
|
||
|
main_tag_stb_ps = PulseSynchronizer("helper", "sys")
|
||
|
self.submodules += [
|
||
|
ref_tag_stb_ps,
|
||
|
main_tag_stb_ps
|
||
|
]
|
||
|
self.sync.helper += [
|
||
|
ref_tag_stb_ps.i.eq(self.ddmtd_ref.h_tag_update),
|
||
|
main_tag_stb_ps.i.eq(self.ddmtd_main.h_tag_update)
|
||
|
]
|
||
|
self.sync += [
|
||
|
ref_tag_stb_sys.eq(ref_tag_stb_ps.o),
|
||
|
main_tag_stb_sys.eq(main_tag_stb_ps.o)
|
||
|
]
|
||
|
|
||
|
self.sync += [
|
||
|
If(ref_tag_stb_sys,
|
||
|
self.ref_tag.status.eq(ref_tag_sys),
|
||
|
),
|
||
|
If(main_tag_stb_sys,
|
||
|
self.main_tag.status.eq(main_tag_sys)
|
||
|
)
|
||
|
]
|
||
|
|
||
|
# EventMangers for firmware interrupt
|
||
|
|
||
|
self.submodules.ref_tag_ev = EventManager()
|
||
|
self.ref_tag_ev.stb = EventSourcePulse()
|
||
|
self.ref_tag_ev.finalize()
|
||
|
|
||
|
self.submodules.main_tag_ev = EventManager()
|
||
|
self.main_tag_ev.stb = EventSourcePulse()
|
||
|
self.main_tag_ev.finalize()
|
||
|
|
||
|
self.sync += [
|
||
|
self.ref_tag_ev.stb.trigger.eq(ref_tag_stb_sys),
|
||
|
self.main_tag_ev.stb.trigger.eq(main_tag_stb_sys)
|
||
|
]
|
||
|
|
||
|
self.submodules.ev = SharedIRQ(self.ref_tag_ev, self.main_tag_ev)
|
||
|
|
||
|
|
||
|
class SMAFrequencyMultiplier(Module, AutoCSR):
|
||
|
def __init__(self, sma_clkin):
|
||
|
sma_clkin_se = Signal()
|
||
|
mmcm_locked = Signal()
|
||
|
mmcm_fb_clk = Signal()
|
||
|
ref_clk = Signal()
|
||
|
self.clock_domains.cd_ref = ClockDomain()
|
||
|
self.refclk_reset = CSRStorage(reset=1)
|
||
|
|
||
|
self.mmcm_bypass = CSRStorage()
|
||
|
self.mmcm_locked = CSRStatus()
|
||
|
self.mmcm_reset = CSRStorage(reset=1)
|
||
|
|
||
|
self.mmcm_daddr = CSRStorage(7)
|
||
|
self.mmcm_din = CSRStorage(16)
|
||
|
self.mmcm_dwen = CSRStorage()
|
||
|
self.mmcm_den = CSRStorage()
|
||
|
self.mmcm_dclk = CSRStorage()
|
||
|
self.mmcm_dout = CSRStatus(16)
|
||
|
self.mmcm_dready = CSRStatus()
|
||
|
|
||
|
# # #
|
||
|
|
||
|
self.specials += [
|
||
|
Instance("IBUFDS",
|
||
|
i_I=sma_clkin.p, i_IB=sma_clkin.n,
|
||
|
o_O=sma_clkin_se),
|
||
|
# MMCME2 is capable to accept 10MHz input while PLLE2 only support down to 19MHz input (DS191)
|
||
|
# The MMCME2 can be reconfiged during runtime using the Dynamic Reconfiguration Ports
|
||
|
Instance("MMCME2_ADV",
|
||
|
p_BANDWIDTH="LOW", # lower jitter
|
||
|
o_LOCKED=self.mmcm_locked.status,
|
||
|
i_RST=self.mmcm_reset.storage,
|
||
|
|
||
|
p_CLKIN1_PERIOD=8, # ns
|
||
|
i_CLKIN1=sma_clkin_se,
|
||
|
i_CLKINSEL=1, # 1=CLKIN1 0=CLKIN2
|
||
|
|
||
|
# VCO @ 1.25GHz
|
||
|
p_CLKFBOUT_MULT_F=10, p_DIVCLK_DIVIDE=1,
|
||
|
i_CLKFBIN=mmcm_fb_clk, o_CLKFBOUT=mmcm_fb_clk,
|
||
|
|
||
|
# 125MHz for WRPLL
|
||
|
p_CLKOUT0_DIVIDE_F=10, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=ref_clk,
|
||
|
|
||
|
# Dynamic Reconfiguration Ports
|
||
|
i_DADDR = self.mmcm_daddr.storage,
|
||
|
i_DI = self.mmcm_din.storage,
|
||
|
i_DWE = self.mmcm_dwen.storage,
|
||
|
i_DEN = self.mmcm_den.storage,
|
||
|
i_DCLK = self.mmcm_dclk.storage,
|
||
|
o_DO = self.mmcm_dout.status,
|
||
|
o_DRDY = self.mmcm_dready.status
|
||
|
),
|
||
|
Instance("BUFGMUX",
|
||
|
i_I0=ref_clk,
|
||
|
i_I1=sma_clkin_se,
|
||
|
i_S=self.mmcm_bypass.storage,
|
||
|
o_O=self.cd_ref.clk
|
||
|
),
|
||
|
AsyncResetSynchronizer(self.cd_ref, self.refclk_reset.storage),
|
||
|
]
|