diff --git a/src/gateware/ddmtd.py b/src/gateware/ddmtd.py new file mode 100644 index 0000000..3e49ba3 --- /dev/null +++ b/src/gateware/ddmtd.py @@ -0,0 +1,72 @@ +from migen import * +from migen.genlib.cdc import PulseSynchronizer, MultiReg +from migen.genlib.fsm import FSM +from misoc.interconnect.csr import * + + +class DDMTDSampler(Module): + def __init__(self, cd_ref, main_dcxo_pads): + self.ref_beating = Signal() + self.main_beating = Signal() + + # # # + + main_clk_se = Signal() + ref_beating_FF = Signal() + main_beating_FF = Signal() + self.specials += [ + Instance("IBUFDS", + i_I=main_dcxo_pads.p, i_IB=main_dcxo_pads.n, + o_O=main_clk_se), + # Two back to back FFs are used to prevent metastability + Instance("FD", i_C=ClockSignal("helper"), + i_D=cd_ref.clk, o_Q=ref_beating_FF), + Instance("FD", i_C=ClockSignal("helper"), + i_D=ref_beating_FF, o_Q=self.ref_beating), + Instance("FD", i_C=ClockSignal("helper"), + i_D=main_clk_se, o_Q=main_beating_FF), + Instance("FD", i_C=ClockSignal("helper"), + i_D=main_beating_FF, o_Q=self.main_beating) + ] + + +class DDMTDDeglitcherFirstEdge(Module): + def __init__(self, input_signal, blind_period=300): + self.detect = Signal() + rising = Signal() + input_signal_r = Signal() + + # # # + + self.sync.helper += [ + input_signal_r.eq(input_signal), + rising.eq(input_signal & ~input_signal_r) + ] + + blind_counter = Signal(max=blind_period) + self.sync.helper += [ + If(blind_counter != 0, blind_counter.eq(blind_counter - 1)), + If(input_signal_r, blind_counter.eq(blind_period - 1)), + self.detect.eq(rising & (blind_counter == 0)) + ] + + +class DDMTD(Module): + def __init__(self, counter, input_signal): + + # in helper clock domain + self.h_tag = Signal(len(counter)) + self.h_tag_update = Signal() + + # # # + + deglitcher = DDMTDDeglitcherFirstEdge(input_signal) + self.submodules += deglitcher + + self.sync.helper += [ + self.h_tag_update.eq(0), + If(deglitcher.detect, + self.h_tag_update.eq(1), + self.h_tag.eq(counter) + ) + ] diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index ff1f268..ba2f0ca 100755 --- a/src/gateware/kasli_soc.py +++ b/src/gateware/kasli_soc.py @@ -26,6 +26,7 @@ import analyzer import acpki import drtio_aux_controller import zynq_clocking +import wrpll import si549 from config import write_csr_file, write_mem_file, write_rustc_cfg_file @@ -555,6 +556,14 @@ class GenericSatellite(SoCCore): if with_wrpll: self.submodules.main_dcxo = si549.Si549(platform.request("ddmtd_main_dcxo_i2c")) self.submodules.helper_dcxo = si549.Si549(platform.request("ddmtd_helper_dcxo_i2c")) + self.submodules.wrpll = wrpll.WRPLL( + cd_ref=self.gt_drtio.cd_rtio_rx0, + main_dcxo_pads=platform.request("cdr_clk_clean_fabric"), + helper_dcxo_pads=platform.request("ddmtd_helper_clk")) + self.csr_devices.append("main_dcxo") + self.csr_devices.append("helper_dcxo") + self.csr_devices.append("wrpll") + self.comb += self.ps7.core.core0.nfiq.eq(self.wrpll.ev.irq) self.config["HAS_SI549"] = None else: self.submodules.siphaser = SiPhaser7Series( diff --git a/src/gateware/wrpll.py b/src/gateware/wrpll.py new file mode 100644 index 0000000..995c91f --- /dev/null +++ b/src/gateware/wrpll.py @@ -0,0 +1,126 @@ +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 + + +class FrequencyCounter(Module, AutoCSR): + def __init__(self, domains, counter_width=24): + for domain in domains: + name = "counter_" + domain + counter = CSRStatus(counter_width, name=name) + setattr(self, name, counter) + self.update_en = CSRStorage() + + timer = Signal(counter_width) + timer_tick = Signal() + self.sync += Cat(timer, timer_tick).eq(timer + 1) + + for domain in domains: + sync_domain = getattr(self.sync, domain) + divider = Signal(2) + sync_domain += divider.eq(divider + 1) + + divided = Signal() + sync_domain += divided.eq(divider[-1]) + divided_sys = Signal() + self.specials += MultiReg(divided, divided_sys) + + divided_sys_r = Signal() + divided_tick = Signal() + self.sync += divided_sys_r.eq(divided_sys) + self.comb += divided_tick.eq(divided_sys & ~divided_sys_r) + + counter = Signal(counter_width) + counter_csr = getattr(self, "counter_" + domain) + self.sync += [ + If(timer_tick, + If(self.update_en.storage, counter_csr.status.eq(counter)), + counter.eq(0), + ).Else( + If(divided_tick, counter.eq(counter + 1)) + ) + ] + + +class WRPLL(Module, AutoCSR): + def __init__(self, cd_ref, main_dcxo_pads, helper_dcxo_pads, COUNTER_BIT=32): + + 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.helper_reset = CSRStorage(reset=1) + 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_dcxo_pads) + + 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) + ) + ] + + # PL->PS 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) \ No newline at end of file