diff --git a/src/gateware/ddmtd.py b/src/gateware/ddmtd.py new file mode 100644 index 0000000..7b2e315 --- /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 DDMTDSamplerGTX(Module): + def __init__(self, gtx, main_xo_pads): + self.gtx_beating = Signal() + self.main_beating = Signal() + + # # # + + main_clk_se = Signal() + gtx_beating_FF = Signal() + main_beating_FF = Signal() + self.specials += [ + Instance("IBUFDS", + i_I=main_xo_pads.p, i_IB=main_xo_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=gtx.cd_rtio_rx0.clk, o_Q=gtx_beating_FF), + Instance("FD", i_C=ClockSignal("helper"), + i_D=gtx_beating_FF, o_Q=self.gtx_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=200): + 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..7d9f6f1 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,17 @@ 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( + gtx=self.gt_drtio, + 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.interrupt[0].eq(self.wrpll.gtx_tag_ev.irq), # IRQ_ID = 61 + self.ps7.interrupt[1].eq(self.wrpll.main_tag_ev.irq) # IRQ_ID = 62 + ] 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..2dca3de --- /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 DDMTDSamplerGTX, DDMTD + + +class FrequencyCounter(Module, AutoCSR): + def __init__(self, counter_width=24, domains=["gtx0_rtio_rx", "sys", "helper"]): + 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() + divided.attr.add("no_retiming") + 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, gtx, main_dcxo_pads, helper_dcxo_pads, COUNTER_BIT=32): + + self.gtx_tag = CSRStatus(COUNTER_BIT) + self.main_tag = CSRStatus(COUNTER_BIT) + + ddmtd_counter = Signal(COUNTER_BIT) + + gtx_tag_sys = Signal(COUNTER_BIT) + main_tag_sys = Signal(COUNTER_BIT) + gtx_tag_stb_sys = Signal() + main_tag_stb_sys = Signal() + + # # # + + self.helper_reset = CSRStorage(reset=1) + self.clock_domains.cd_helper = ClockDomain() + self.helper_reset.storage.attr.add("no_retiming") + 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() + + self.submodules.ddmtd_sampler = DDMTDSamplerGTX(gtx, main_dcxo_pads) + + self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1) + self.submodules.ddmtd_gtx = DDMTD(ddmtd_counter, self.ddmtd_sampler.gtx_beating) + self.submodules.ddmtd_main = DDMTD(ddmtd_counter, self.ddmtd_sampler.main_beating) + + # DDMTD tags collection + + self.specials += [ + MultiReg(self.ddmtd_gtx.h_tag, gtx_tag_sys), + MultiReg(self.ddmtd_main.h_tag, main_tag_sys) + ] + + gtx_tag_stb_ps = PulseSynchronizer("helper", "sys") + main_tag_stb_ps = PulseSynchronizer("helper", "sys") + self.submodules += [ + gtx_tag_stb_ps, + main_tag_stb_ps + ] + self.sync.helper += [ + gtx_tag_stb_ps.i.eq(self.ddmtd_gtx.h_tag_update), + main_tag_stb_ps.i.eq(self.ddmtd_main.h_tag_update) + ] + self.sync += [ + gtx_tag_stb_sys.eq(gtx_tag_stb_ps.o), + main_tag_stb_sys.eq(main_tag_stb_ps.o) + ] + + self.sync += [ + If(gtx_tag_stb_sys, + self.gtx_tag.status.eq(gtx_tag_sys), + ), + If(main_tag_stb_sys, + self.main_tag.status.eq(main_tag_sys) + ) + ] + + # PL-PS shared peripheral interrupt (SPI) + + self.submodules.gtx_tag_ev = EventManager() + self.gtx_tag_ev.stb = EventSourcePulse() + self.gtx_tag_ev.finalize() + + self.submodules.main_tag_ev = EventManager() + self.main_tag_ev.stb = EventSourcePulse() + self.main_tag_ev.finalize() + + self.sync += [ + self.gtx_tag_ev.stb.trigger.eq(gtx_tag_stb_sys), + self.main_tag_ev.stb.trigger.eq(main_tag_stb_sys) + ]