WRPLL gateware

kasli_soc: add wrpll, and its IRQs for satellite
ddmtd: add ddmtd and deglitcher
ddmtd: add phase and period collector
wrpll: add helper clockdomain
wrpll: add frequency counter
wrpll: add gtx_period, gtx_tag and main_tag csr
wrpll: move collector result from helper CD to sys CD and CSR
wrpll: add phase & period eventmanage for shared peripheral interrupt
morgan 2023-12-20 15:07:51 +08:00
parent e08d942066
commit 0cd1ac9204
3 changed files with 308 additions and 0 deletions

156
src/gateware/ddmtd.py Normal file
View File

@ -0,0 +1,156 @@
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=128):
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)
)
]
class Collector(Module):
"""
A phase and period collector for DDMTD
"""
def __init__(self, N):
self.gtx_stb = Signal()
self.main_stb = Signal()
self.tag_gtx = Signal(N)
self.tag_main = Signal(N)
self.out_period_stb = Signal()
self.out_beating_period = Signal(N)
last_gtx_tag = Signal(N)
beating_period_r = Signal(N)
self.out_phase_stb = Signal()
self.out_tag_gtx = Signal(N)
self.out_tag_main = Signal(N)
tag_gtx_r = Signal(N)
tag_main_r = Signal(N)
# # #
# Period collector
# collect the difference between each gtx tags
self.submodules.period_colr_fsm = period_colr_fsm = FSM(reset_state="IDLE")
period_colr_fsm.act("IDLE",
NextValue(self.out_period_stb, 0),
If(self.gtx_stb,
NextValue(beating_period_r, self.tag_gtx - last_gtx_tag),
NextValue(last_gtx_tag, self.tag_gtx),
NextState("OUTPUT")
)
)
period_colr_fsm.act("OUTPUT",
NextValue(self.out_beating_period, beating_period_r),
NextValue(self.out_period_stb, 1),
NextState("IDLE")
)
# Phase collector
# collect main and gtx tag
self.submodules.phase_colr_fsm = phase_colr_fsm = FSM(reset_state="IDLE")
phase_colr_fsm.act("IDLE",
NextValue(self.out_phase_stb, 0),
If(self.gtx_stb & self.main_stb,
NextValue(tag_gtx_r, self.tag_gtx),
NextValue(tag_main_r, self.tag_main),
NextState("OUTPUT")
).Elif(self.gtx_stb,
NextValue(tag_gtx_r, self.tag_gtx),
NextState("WAITMAIN")
).Elif(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("WAITGTX")
)
)
phase_colr_fsm.act("WAITGTX",
If(self.gtx_stb,
NextValue(tag_gtx_r, self.tag_gtx),
NextState("OUTPUT")
)
)
phase_colr_fsm.act("WAITMAIN",
If(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("OUTPUT")
)
)
phase_colr_fsm.act("OUTPUT",
NextValue(self.out_tag_gtx, tag_gtx_r),
NextValue(self.out_tag_main, tag_main_r),
NextValue(self.out_phase_stb, 1),
NextState("IDLE")
)

View File

@ -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
@ -564,6 +565,17 @@ class GenericSatellite(SoCCore):
else:
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.period_ev.irq), # IRQ_ID = 61
self.ps7.interrupt[1].eq(self.wrpll.phase_ev.irq) # IRQ_ID = 62
]
self.config["HAS_SI549"] = None
gtx0 = self.gt_drtio.gtxs[0]

140
src/gateware/wrpll.py Normal file
View File

@ -0,0 +1,140 @@
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, Collector
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_period = CSRStatus(COUNTER_BIT)
self.gtx_tag = CSRStatus(COUNTER_BIT)
self.main_tag = CSRStatus(COUNTER_BIT)
ddmtd_counter = Signal(COUNTER_BIT)
gtx_period_sys = Signal(COUNTER_BIT)
gtx_tag_sys = Signal(COUNTER_BIT)
main_tag_sys = Signal(COUNTER_BIT)
period_colr_stb_sys = Signal()
phase_colr_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.submodules.collector = ClockDomainsRenamer("helper")(Collector(COUNTER_BIT))
self.comb += [
self.collector.gtx_stb.eq(self.ddmtd_gtx.h_tag_update),
self.collector.main_stb.eq(self.ddmtd_main.h_tag_update),
self.collector.tag_gtx.eq(self.ddmtd_gtx.h_tag),
self.collector.tag_main.eq(self.ddmtd_main.h_tag)
]
period_colr_stb_ps = PulseSynchronizer("helper", "sys")
phase_colr_stb_ps = PulseSynchronizer("helper", "sys")
self.submodules += [
period_colr_stb_ps,
phase_colr_stb_ps
]
self.sync.helper += [
period_colr_stb_ps.i.eq(self.collector.out_period_stb),
phase_colr_stb_ps.i.eq(self.collector.out_phase_stb)
]
self.sync += [
period_colr_stb_sys.eq(period_colr_stb_ps.o),
phase_colr_stb_sys.eq(phase_colr_stb_ps.o)
]
self.specials += [
MultiReg(self.collector.out_beating_period, gtx_period_sys),
MultiReg(self.collector.out_tag_gtx, gtx_tag_sys),
MultiReg(self.collector.out_tag_main, main_tag_sys)
]
self.sync += [
If(period_colr_stb_sys,
self.gtx_period.status.eq(gtx_period_sys),
),
If(phase_colr_stb_sys,
self.gtx_tag.status.eq(gtx_tag_sys),
self.main_tag.status.eq(main_tag_sys)
)
]
# PL-PS shared peripheral interrupt (SPI)
self.submodules.period_ev = EventManager()
self.period_ev.stb = EventSourcePulse()
self.period_ev.finalize()
self.submodules.phase_ev = EventManager()
self.phase_ev.stb = EventSourcePulse()
self.phase_ev.finalize()
self.sync += [
self.period_ev.stb.trigger.eq(period_colr_stb_sys),
self.phase_ev.stb.trigger.eq(phase_colr_stb_sys)
]