Compare commits

...

13 Commits

Author SHA1 Message Date
morgan adaecf87a8 FIQ_issue 2024-03-04 12:03:53 +08:00
morgan f8f6dcef14 sma frequency multipler: fix freq input error 2024-03-01 14:50:10 +08:00
morgan 30392c9ca8 master wrpll: default vco to 1.25Ghz 2024-03-01 14:50:10 +08:00
morgan 963a4822c4 master wrpll: rename sma_pll to wrpll_refclk
gw& fw: add mmcm prefix to drp csr
2024-03-01 14:50:10 +08:00
morgan 5aade6abb2 master wrpll: remove nested mmcm module 2024-03-01 14:50:10 +08:00
morgan 7300ecce25 master WRPLL: add mmcm setup, reset and mmcmsetting
gw: allow cpu mmcm reset
2024-03-01 14:50:10 +08:00
morgan 0c8ec61527 master WRPLL: add mmcm drp write operation 2024-03-01 14:50:10 +08:00
morgan c7df09b6a3 master WRPLL: init
gw: add rtio_frequency
gw: add wrpll ref clock config
gw: add mmcm and expose mmcm config port
fw: add mmcm read
fw: add CLK_SEL to expander
2024-03-01 14:50:10 +08:00
morgan 9d5d883b1b cleanup: remove unused fsm import 2024-03-01 14:50:10 +08:00
morgan d00a9afdcc WRPLL firmware
satman main & si549: add WRPLL select_recovered_clock
satman main & si549: add helper si549 setup
si549: add tag collector to process gtx & main tags
si549: add frequency counter to set BASE_ADPLL
si549: add set_adpll for main & helper PLL
si549: add main & helper PLL
FIQ & si549: add handler for gtx & main tags FIQ
2024-03-01 14:50:10 +08:00
morgan f1a4a2f97f Si549 firmware
io_expander: set CLK_SEL pin to output when si549 is used
satman main: drive CLK_SEL to true when si549 is used
satman main & si549: add main DCXO setup
si549: add bit bang i2c
si549: add si549 programming & main setup
2024-03-01 14:46:43 +08:00
morgan 90890e39e0 WRPLL gateware
kasli_soc satellite: add wrpll
kasli_soc satellite: add gtx & main tag nFIQ for satellite
ddmtd: add DDMTD and deglitcher
wrpll: add helper clockdomain
wrpll: add frequency counter
wrpll: add gtx & main tag collection
wrpll: add gtx & main tag eventmanager for shared peripheral interrupt
2024-03-01 10:43:27 +08:00
morgan 3972efc61e si549 gateware
kasli_soc: add --with-wrpll arg to switch from si5324 to si549
kasli_soc: default to use si5435
kasli_soc: add main and helper si549 for satellite
si549: add i2c and adpll programmer
2024-02-14 11:02:08 +08:00
12 changed files with 1603 additions and 18 deletions

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

@ -0,0 +1,71 @@
from migen import *
from migen.genlib.cdc import PulseSynchronizer, MultiReg
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)
)
]

View File

@ -26,6 +26,8 @@ import analyzer
import acpki import acpki
import drtio_aux_controller import drtio_aux_controller
import zynq_clocking import zynq_clocking
import wrpll
import si549
from config import write_csr_file, write_mem_file, write_rustc_cfg_file from config import write_csr_file, write_mem_file, write_rustc_cfg_file
eem_iostandard_dict = { eem_iostandard_dict = {
@ -105,7 +107,7 @@ class GTPBootstrapClock(Module):
class GenericStandalone(SoCCore): class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False, with_wrpll=False):
self.acpki = acpki self.acpki = acpki
clk_freq = description["rtio_frequency"] clk_freq = description["rtio_frequency"]
@ -205,7 +207,7 @@ class GenericStandalone(SoCCore):
class GenericMaster(SoCCore): class GenericMaster(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False, with_wrpll=False):
clk_freq = description["rtio_frequency"] clk_freq = description["rtio_frequency"]
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"]) has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
@ -222,8 +224,6 @@ class GenericMaster(SoCCore):
self.config["HW_REV"] = description["hw_rev"] self.config["HW_REV"] = description["hw_rev"]
self.submodules += SMAClkinForward(self.platform)
data_pads = [platform.request("sfp", i) for i in range(4)] data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.gt_drtio = gtx_7series.GTX( self.submodules.gt_drtio = gtx_7series.GTX(
@ -231,6 +231,7 @@ class GenericMaster(SoCCore):
pads=data_pads, pads=data_pads,
clk_freq=clk_freq) clk_freq=clk_freq)
self.csr_devices.append("gt_drtio") self.csr_devices.append("gt_drtio")
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
self.config["CLOCK_FREQUENCY"] = int(clk_freq) self.config["CLOCK_FREQUENCY"] = int(clk_freq)
txout_buf = Signal() txout_buf = Signal()
@ -257,6 +258,23 @@ class GenericMaster(SoCCore):
self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done) self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done)
self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap") self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap")
if with_wrpll:
self.submodules.wrpll_refclk = wrpll.SMAFrequencyMultiplier(platform.request("sma_clkin"))
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.wrpll_refclk.cd_ref,
main_dcxo_pads=platform.request("cdr_clk_clean_fabric"),
helper_dcxo_pads=platform.request("ddmtd_helper_clk"))
self.csr_devices.append("wrpll_refclk")
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
self.config["WRPLL_REF_CLK"] = "SMA_CLKIN"
else:
self.submodules += SMAClkinForward(self.platform)
self.config["HAS_SI5324"] = None self.config["HAS_SI5324"] = None
self.config["SI5324_SOFT_RESET"] = None self.config["SI5324_SOFT_RESET"] = None
@ -398,7 +416,7 @@ class GenericMaster(SoCCore):
class GenericSatellite(SoCCore): class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False, with_wrpll=False):
clk_freq = description["rtio_frequency"] clk_freq = description["rtio_frequency"]
self.acpki = acpki self.acpki = acpki
@ -551,6 +569,20 @@ class GenericSatellite(SoCCore):
self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6)
self.config["CLOCK_FREQUENCY"] = int(clk_freq) self.config["CLOCK_FREQUENCY"] = int(clk_freq)
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
self.config["WRPLL_REF_CLK"] = "GTX_CDR"
else:
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk"), si5324_clkin=platform.request("cdr_clk"),
rx_synchronizer=self.rx_synchronizer, rx_synchronizer=self.rx_synchronizer,
@ -588,6 +620,8 @@ def main():
help="build gateware into the specified directory") help="build gateware into the specified directory")
parser.add_argument("--acpki", default=False, action="store_true", parser.add_argument("--acpki", default=False, action="store_true",
help="enable ACPKI") help="enable ACPKI")
parser.add_argument("--with-wrpll", default=True, action="store_true",
help="enable WRPLL")
parser.add_argument("description", metavar="DESCRIPTION", parser.add_argument("description", metavar="DESCRIPTION",
help="JSON system description file") help="JSON system description file")
args = parser.parse_args() args = parser.parse_args()
@ -605,7 +639,7 @@ def main():
else: else:
raise ValueError("Invalid DRTIO role") raise ValueError("Invalid DRTIO role")
soc = cls(description, acpki=args.acpki) soc = cls(description, acpki=args.acpki, with_wrpll=args.with_wrpll)
soc.finalize() soc.finalize()
if args.r is not None: if args.r is not None:

278
src/gateware/si549.py Normal file
View File

@ -0,0 +1,278 @@
from migen import *
from migen.genlib.fsm import *
from misoc.interconnect.csr import *
class I2CClockGen(Module):
def __init__(self, width):
self.load = Signal(width)
self.clk2x = Signal()
cnt = Signal.like(self.load)
self.comb += [
self.clk2x.eq(cnt == 0),
]
self.sync += [
If(self.clk2x,
cnt.eq(self.load),
).Else(
cnt.eq(cnt - 1),
)
]
class I2CMasterMachine(Module):
def __init__(self, clock_width):
self.scl = Signal(reset=1)
self.sda_o = Signal(reset=1)
self.sda_i = Signal()
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
self.start = Signal()
self.stop = Signal()
self.write = Signal()
self.ack = Signal()
self.data = Signal(8)
self.ready = Signal()
# # #
bits = Signal(4)
data = Signal(8)
fsm = CEInserter()(FSM("IDLE"))
self.submodules += fsm
fsm.act("IDLE",
self.ready.eq(1),
If(self.start,
NextState("START0"),
).Elif(self.stop,
NextState("STOP0"),
).Elif(self.write,
NextValue(bits, 8),
NextValue(data, self.data),
NextState("WRITE0")
)
)
fsm.act("START0",
NextValue(self.scl, 1),
NextState("START1")
)
fsm.act("START1",
NextValue(self.sda_o, 0),
NextState("IDLE")
)
fsm.act("STOP0",
NextValue(self.scl, 0),
NextState("STOP1")
)
fsm.act("STOP1",
NextValue(self.sda_o, 0),
NextState("STOP2")
)
fsm.act("STOP2",
NextValue(self.scl, 1),
NextState("STOP3")
)
fsm.act("STOP3",
NextValue(self.sda_o, 1),
NextState("IDLE")
)
fsm.act("WRITE0",
NextValue(self.scl, 0),
NextState("WRITE1")
)
fsm.act("WRITE1",
If(bits == 0,
NextValue(self.sda_o, 1),
NextState("READACK0"),
).Else(
NextValue(self.sda_o, data[7]),
NextState("WRITE2"),
)
)
fsm.act("WRITE2",
NextValue(self.scl, 1),
NextValue(data[1:], data[:-1]),
NextValue(bits, bits - 1),
NextState("WRITE0"),
)
fsm.act("READACK0",
NextValue(self.scl, 1),
NextState("READACK1"),
)
fsm.act("READACK1",
NextValue(self.ack, ~self.sda_i),
NextState("IDLE")
)
run = Signal()
idle = Signal()
self.comb += [
run.eq((self.start | self.stop | self.write) & self.ready),
idle.eq(~run & fsm.ongoing("IDLE")),
self.cg.ce.eq(~idle),
fsm.ce.eq(run | self.cg.clk2x),
]
class ADPLLProgrammer(Module):
def __init__(self):
self.i2c_divider = Signal(16)
self.i2c_address = Signal(7)
self.adpll = Signal(24)
self.stb = Signal()
self.busy = Signal()
self.nack = Signal()
self.scl = Signal()
self.sda_i = Signal()
self.sda_o = Signal()
# # #
master = I2CMasterMachine(16)
self.submodules += master
self.comb += [
master.cg.load.eq(self.i2c_divider),
self.scl.eq(master.scl),
master.sda_i.eq(self.sda_i),
self.sda_o.eq(master.sda_o)
]
fsm = FSM()
self.submodules += fsm
adpll = Signal.like(self.adpll)
fsm.act("IDLE",
If(self.stb,
NextValue(adpll, self.adpll),
NextState("START")
)
)
fsm.act("START",
master.start.eq(1),
If(master.ready, NextState("DEVADDRESS"))
)
fsm.act("DEVADDRESS",
master.data.eq(self.i2c_address << 1),
master.write.eq(1),
If(master.ready, NextState("REGADRESS"))
)
fsm.act("REGADRESS",
master.data.eq(231),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA0")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA0",
master.data.eq(adpll[0:8]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA1")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA1",
master.data.eq(adpll[8:16]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA2")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA2",
master.data.eq(adpll[16:24]),
master.write.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("STOP")
)
)
fsm.act("STOP",
master.stop.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("IDLE")
)
)
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
class Si549(Module, AutoCSR):
def __init__(self, pads):
self.i2c_divider = CSRStorage(16, reset=75)
self.i2c_address = CSRStorage(7)
self.adpll = CSRStorage(24)
self.adpll_stb = CSRStorage()
self.adpll_busy = CSRStatus()
self.nack = CSRStatus()
self.bitbang_enable = CSRStorage()
self.sda_oe = CSRStorage()
self.sda_out = CSRStorage()
self.sda_in = CSRStatus()
self.scl_oe = CSRStorage()
self.scl_out = CSRStorage()
# # #
self.submodules.programmer = ADPLLProgrammer()
self.comb += [
self.programmer.i2c_divider.eq(self.i2c_divider.storage),
self.programmer.i2c_address.eq(self.i2c_address.storage),
self.programmer.adpll.eq(self.adpll.storage),
self.programmer.stb.eq(self.adpll_stb.storage),
self.adpll_busy.status.eq(self.programmer.busy),
self.nack.status.eq(self.programmer.nack)
]
# I2C with bitbang/gateware mode select
sda_t = TSTriple(1)
scl_t = TSTriple(1)
self.specials += [
sda_t.get_tristate(pads.sda),
scl_t.get_tristate(pads.scl)
]
self.comb += [
If(self.bitbang_enable.storage,
sda_t.oe.eq(self.sda_oe.storage),
sda_t.o.eq(self.sda_out.storage),
self.sda_in.status.eq(sda_t.i),
scl_t.oe.eq(self.scl_oe.storage),
scl_t.o.eq(self.scl_out.storage)
).Else(
sda_t.oe.eq(~self.programmer.sda_o),
sda_t.o.eq(0),
self.programmer.sda_i.eq(sda_t.i),
scl_t.oe.eq(~self.programmer.scl),
scl_t.o.eq(0),
)
]

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

@ -0,0 +1,185 @@
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()
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)
class SMAFrequencyMultiplier(Module, AutoCSR):
def __init__(self, sma_clkin):
freq = 125e6
period = 1e9/freq # ns
sma_clkin_se = Signal()
mmcm_locked = Signal()
mmcm_fb_clk = Signal()
ref_clk = Signal()
self.clock_domains.cd_ref = ClockDomain()
self.mmcm_locked = CSRStatus()
self.mmcm_reset = CSRStorage()
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)
Instance("MMCME2_ADV",
p_BANDWIDTH="LOW", # lower jitter
o_LOCKED=self.mmcm_locked.status,
i_RST=self.mmcm_reset.storage,
p_CLKIN1_PERIOD=period,
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 Port
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("BUFG", i_I=ref_clk, o_O=self.cd_ref.clk),
AsyncResetSynchronizer(self.cd_ref, ~self.mmcm_locked.status),
]

View File

@ -19,11 +19,15 @@ const IODIR_OUT_SFP_LED: u8 = 0x40;
const IODIR_OUT_SFP0_LED: u8 = 0x40; const IODIR_OUT_SFP0_LED: u8 = 0x40;
#[cfg(hw_rev = "v1.1")] #[cfg(hw_rev = "v1.1")]
const IODIR_OUT_SFP0_LED: u8 = 0x80; const IODIR_OUT_SFP0_LED: u8 = 0x80;
#[cfg(has_si549)]
const IODIR_CLK_SEL: u8 = 0x80; // out
#[cfg(has_si5324)]
const IODIR_CLK_SEL: u8 = 0x00; // in
//IO expander port direction //IO expander port direction
const IODIR0: [u8; 2] = [ const IODIR0: [u8; 2] = [
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED, 0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP0_LED,
0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED, 0xFF & !IODIR_OUT_SFP_TX_DISABLE & !IODIR_OUT_SFP_LED & !IODIR_CLK_SEL,
]; ];
const IODIR1: [u8; 2] = [ const IODIR1: [u8; 2] = [

View File

@ -35,7 +35,8 @@ pub mod drtio_eem;
pub mod grabber; pub mod grabber;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_si549)]
pub mod si549;
use core::{cmp, str}; use core::{cmp, str};
pub fn identifier_read(buf: &mut [u8]) -> &str { pub fn identifier_read(buf: &mut [u8]) -> &str {

View File

@ -0,0 +1,778 @@
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use libboard_zynq::timer::GlobalTimer;
use log::info;
use crate::pl::csr;
#[cfg(feature = "target_kasli_soc")]
const ADDRESS: u8 = 0x67;
#[derive(Clone, Copy)]
pub struct DividerConfig {
pub hsdiv: u16,
pub lsdiv: u8,
pub fbdiv: u64,
}
#[derive(Clone, Copy)]
pub struct FrequencySetting {
pub main: DividerConfig,
#[cfg(has_wrpll)]
pub helper: DividerConfig,
}
mod i2c {
use super::*;
#[derive(Clone, Copy)]
pub enum DCXO {
Main,
#[cfg(has_wrpll)]
Helper,
}
fn half_period(timer: &mut GlobalTimer) {
timer.delay_us(1)
}
fn sda_i(dcxo: DCXO) -> bool {
match dcxo {
DCXO::Main => unsafe { csr::main_dcxo::sda_in_read() == 1 },
#[cfg(has_wrpll)]
DCXO::Helper => unsafe { csr::helper_dcxo::sda_in_read() == 1 },
}
}
fn sda_oe(dcxo: DCXO, oe: bool) {
let val = if oe { 1 } else { 0 };
match dcxo {
DCXO::Main => unsafe { csr::main_dcxo::sda_oe_write(val) },
#[cfg(has_wrpll)]
DCXO::Helper => unsafe { csr::helper_dcxo::sda_oe_write(val) },
};
}
fn sda_o(dcxo: DCXO, o: bool) {
let val = if o { 1 } else { 0 };
match dcxo {
DCXO::Main => unsafe { csr::main_dcxo::sda_out_write(val) },
#[cfg(has_wrpll)]
DCXO::Helper => unsafe { csr::helper_dcxo::sda_out_write(val) },
};
}
fn scl_oe(dcxo: DCXO, oe: bool) {
let val = if oe { 1 } else { 0 };
match dcxo {
DCXO::Main => unsafe { csr::main_dcxo::scl_oe_write(val) },
#[cfg(has_wrpll)]
DCXO::Helper => unsafe { csr::helper_dcxo::scl_oe_write(val) },
};
}
fn scl_o(dcxo: DCXO, o: bool) {
let val = if o { 1 } else { 0 };
match dcxo {
DCXO::Main => unsafe { csr::main_dcxo::scl_out_write(val) },
#[cfg(has_wrpll)]
DCXO::Helper => unsafe { csr::helper_dcxo::scl_out_write(val) },
};
}
pub fn init(dcxo: DCXO, timer: &mut GlobalTimer) -> Result<(), &'static str> {
// Set SCL as output, and high level
scl_o(dcxo, true);
scl_oe(dcxo, true);
// Prepare a zero level on SDA so that sda_oe pulls it down
sda_o(dcxo, false);
// Release SDA
sda_oe(dcxo, false);
// Check the I2C bus is ready
half_period(timer);
half_period(timer);
if !sda_i(dcxo) {
// Try toggling SCL a few times
for _bit in 0..8 {
scl_o(dcxo, false);
half_period(timer);
scl_o(dcxo, true);
half_period(timer);
}
}
if !sda_i(dcxo) {
return Err("SDA is stuck low and doesn't get unstuck");
}
Ok(())
}
pub fn start(dcxo: DCXO, timer: &mut GlobalTimer) {
// Set SCL high then SDA low
scl_o(dcxo, true);
half_period(timer);
sda_oe(dcxo, true);
half_period(timer);
}
pub fn stop(dcxo: DCXO, timer: &mut GlobalTimer) {
// First, make sure SCL is low, so that the target releases the SDA line
scl_o(dcxo, false);
half_period(timer);
// Set SCL high then SDA high
sda_oe(dcxo, true);
scl_o(dcxo, true);
half_period(timer);
sda_oe(dcxo, false);
half_period(timer);
}
pub fn write(dcxo: DCXO, data: u8, timer: &mut GlobalTimer) -> bool {
// MSB first
for bit in (0..8).rev() {
// Set SCL low and set our bit on SDA
scl_o(dcxo, false);
sda_oe(dcxo, data & (1 << bit) == 0);
half_period(timer);
// Set SCL high ; data is shifted on the rising edge of SCL
scl_o(dcxo, true);
half_period(timer);
}
// Check ack
// Set SCL low, then release SDA so that the I2C target can respond
scl_o(dcxo, false);
half_period(timer);
sda_oe(dcxo, false);
// Set SCL high and check for ack
scl_o(dcxo, true);
half_period(timer);
// returns true if acked (I2C target pulled SDA low)
!sda_i(dcxo)
}
pub fn read(dcxo: DCXO, ack: bool, timer: &mut GlobalTimer) -> u8 {
// Set SCL low first, otherwise setting SDA as input may cause a transition
// on SDA with SCL high which will be interpreted as START/STOP condition.
scl_o(dcxo, false);
half_period(timer); // make sure SCL has settled low
sda_oe(dcxo, false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
scl_o(dcxo, false);
half_period(timer);
// Set SCL high and shift data
scl_o(dcxo, true);
half_period(timer);
if sda_i(dcxo) {
data |= 1 << bit
}
}
// Send ack
// Set SCL low and pull SDA low when acking
scl_o(dcxo, false);
if ack {
sda_oe(dcxo, true)
}
half_period(timer);
// then set SCL high
scl_o(dcxo, true);
half_period(timer);
data
}
}
fn write(dcxo: i2c::DCXO, reg: u8, val: u8, timer: &mut GlobalTimer) -> Result<(), &'static str> {
i2c::start(dcxo, timer);
if !i2c::write(dcxo, ADDRESS << 1, timer) {
return Err("Si549 failed to ack write address");
}
if !i2c::write(dcxo, reg, timer) {
return Err("Si549 failed to ack register");
}
if !i2c::write(dcxo, val, timer) {
return Err("Si549 failed to ack value");
}
i2c::stop(dcxo, timer);
Ok(())
}
fn read(dcxo: i2c::DCXO, reg: u8, timer: &mut GlobalTimer) -> Result<u8, &'static str> {
i2c::start(dcxo, timer);
if !i2c::write(dcxo, ADDRESS << 1, timer) {
return Err("Si549 failed to ack write address");
}
if !i2c::write(dcxo, reg, timer) {
return Err("Si549 failed to ack register");
}
i2c::stop(dcxo, timer);
i2c::start(dcxo, timer);
if !i2c::write(dcxo, (ADDRESS << 1) | 1, timer) {
return Err("Si549 failed to ack read address");
}
let val = i2c::read(dcxo, false, timer);
i2c::stop(dcxo, timer);
Ok(val)
}
fn setup(dcxo: i2c::DCXO, config: DividerConfig, timer: &mut GlobalTimer) -> Result<(), &'static str> {
i2c::init(dcxo, timer)?;
write(dcxo, 255, 0x00, timer)?; // PAGE
write(dcxo, 69, 0x00, timer)?; // Disable FCAL override.
write(dcxo, 17, 0x00, timer)?; // Synchronously disable output
// The Si549 has no ID register, so we check that it responds correctly
// by writing values to a RAM-like register and reading them back.
for test_value in 0..255 {
write(dcxo, 23, test_value, timer)?;
let readback = read(dcxo, 23, timer)?;
if readback != test_value {
return Err("Si549 detection failed");
}
}
write(dcxo, 23, config.hsdiv as u8, timer)?;
write(dcxo, 24, (config.hsdiv >> 8) as u8 | (config.lsdiv << 4), timer)?;
write(dcxo, 26, config.fbdiv as u8, timer)?;
write(dcxo, 27, (config.fbdiv >> 8) as u8, timer)?;
write(dcxo, 28, (config.fbdiv >> 16) as u8, timer)?;
write(dcxo, 29, (config.fbdiv >> 24) as u8, timer)?;
write(dcxo, 30, (config.fbdiv >> 32) as u8, timer)?;
write(dcxo, 31, (config.fbdiv >> 40) as u8, timer)?;
write(dcxo, 7, 0x08, timer)?; // Start FCAL
timer.delay_us(30_000); // Internal FCAL VCO calibration
write(dcxo, 17, 0x01, timer)?; // Synchronously enable output
Ok(())
}
pub fn main_setup(timer: &mut GlobalTimer, settings: FrequencySetting) -> Result<(), &'static str> {
unsafe {
csr::main_dcxo::bitbang_enable_write(1);
csr::main_dcxo::i2c_address_write(ADDRESS);
}
setup(i2c::DCXO::Main, settings.main, timer)?;
// Si549 maximum settling time for large frequency change.
timer.delay_us(40_000);
unsafe {
csr::main_dcxo::bitbang_enable_write(0);
}
info!("Main Si549 started");
Ok(())
}
#[cfg(has_wrpll)]
pub mod wrpll {
use embedded_hal::blocking::delay::DelayMs;
use libcortex_a9::mutex::Mutex;
use super::*;
const TIMER_WIDTH: u32 = 24;
const COUNTER_DIV: u32 = 2;
const ADPLL_MAX: i32 = (950.0 / 0.0001164) as i32;
const KP: i32 = 6;
const KI: i32 = 2;
static BASE_ADPLL: Mutex<i32> = Mutex::new(0);
static H_INTEGRATOR: Mutex<i32> = Mutex::new(0);
static M_INTEGRATOR: Mutex<i32> = Mutex::new(0);
#[derive(Clone, Copy)]
pub enum FIQ {
RefTag,
MainTag,
}
mod tag_collector {
use super::*;
const BEATING_PERIOD: i32 = 0x8000;
const BEATING_HALFPERIOD: i32 = 0x4000;
static REF_TAG: Mutex<u32> = Mutex::new(0);
static REF_TAG_READY: Mutex<bool> = Mutex::new(false);
static MAIN_TAG: Mutex<u32> = Mutex::new(0);
static MAIN_TAG_READY: Mutex<bool> = Mutex::new(false);
pub fn reset() {
clear_phase_diff_ready();
*REF_TAG.lock() = 0;
*MAIN_TAG.lock() = 0;
}
pub fn clear_phase_diff_ready() {
*REF_TAG_READY.lock() = false;
*MAIN_TAG_READY.lock() = false;
}
pub fn collect_tags(interrupt: FIQ) {
match interrupt {
FIQ::RefTag => {
*REF_TAG.lock() = unsafe { csr::wrpll::ref_tag_read() };
*REF_TAG_READY.lock() = true;
}
FIQ::MainTag => {
*MAIN_TAG.lock() = unsafe { csr::wrpll::main_tag_read() };
*MAIN_TAG_READY.lock() = true;
}
}
}
pub fn phase_diff_ready() -> bool {
*REF_TAG_READY.lock() && *MAIN_TAG_READY.lock()
}
pub fn get_period_error() -> i32 {
// n * BEATING_PERIOD - REF_TAG(n) mod BEATING_PERIOD
let mut period_error = (*REF_TAG.lock()).overflowing_neg().0.rem_euclid(BEATING_PERIOD as u32) as i32;
// mapping tags from [0, 2π] -> [-π, π]
if period_error > BEATING_HALFPERIOD {
period_error -= BEATING_PERIOD
}
period_error
}
pub fn get_phase_error() -> i32 {
// MAIN_TAG(n) - REF_TAG(n) mod BEATING_PERIOD
let mut phase_error = (*MAIN_TAG.lock())
.overflowing_sub(*REF_TAG.lock())
.0
.rem_euclid(BEATING_PERIOD as u32) as i32;
// mapping tags from [0, 2π] -> [-π, π]
if phase_error > BEATING_HALFPERIOD {
phase_error -= BEATING_PERIOD
}
phase_error
}
}
pub fn helper_setup(timer: &mut GlobalTimer, settings: FrequencySetting) -> Result<(), &'static str> {
unsafe {
csr::wrpll::helper_reset_write(1);
csr::helper_dcxo::bitbang_enable_write(1);
csr::helper_dcxo::i2c_address_write(ADDRESS);
}
setup(i2c::DCXO::Helper, settings.helper, timer)?;
// Si549 maximum settling time for large frequency change.
timer.delay_us(40_000);
unsafe {
csr::wrpll::helper_reset_write(0);
csr::helper_dcxo::bitbang_enable_write(0);
}
info!("Helper Si549 started");
timer.delay_ms(5_000);
Ok(())
}
fn set_fiq(en: bool) {
let val = if en { 1 } else { 0 };
unsafe {
csr::wrpll::ref_tag_ev_enable_write(val);
csr::wrpll::main_tag_ev_enable_write(val);
}
}
/// set adpll using gateware i2c
/// Note: disable main/helper i2c bitbang before using this function
fn set_adpll(dcxo: i2c::DCXO, adpll: i32) -> Result<(), &'static str> {
if adpll.abs() > ADPLL_MAX {
return Err("adpll is too large");
}
match dcxo {
i2c::DCXO::Main => unsafe {
if csr::main_dcxo::bitbang_enable_read() == 1 {
return Err("Main si549 bitbang mode is active when using gateware i2c");
}
while csr::main_dcxo::adpll_busy_read() == 1 {}
csr::main_dcxo::i2c_address_write(ADDRESS);
csr::main_dcxo::adpll_write(adpll as u32);
csr::main_dcxo::adpll_stb_write(1);
csr::main_dcxo::adpll_stb_write(0);
if csr::main_dcxo::nack_read() == 1 {
return Err("Main si549 failed to ack adpll write");
}
},
i2c::DCXO::Helper => unsafe {
if csr::helper_dcxo::bitbang_enable_read() == 1 {
return Err("Helper si549 bitbang mode is active when using gateware i2c");
}
while csr::helper_dcxo::adpll_busy_read() == 1 {}
csr::helper_dcxo::i2c_address_write(ADDRESS);
csr::helper_dcxo::adpll_write(adpll as u32);
csr::helper_dcxo::adpll_stb_write(1);
csr::helper_dcxo::adpll_stb_write(0);
if csr::helper_dcxo::nack_read() == 1 {
return Err("Helper si549 failed to ack adpll write");
}
},
};
Ok(())
}
/// To get within capture range
fn set_base_adpll(timer: &mut GlobalTimer) -> Result<(), &'static str> {
let count2adpll =
|error: i32| (((error) as f64 * 1e6) / (0.0001164 * (1 << (TIMER_WIDTH - COUNTER_DIV)) as f64)) as i32;
let (ref_count, main_count) = get_freq_counts(timer);
let mut base_adpll_lock = BASE_ADPLL.lock();
*base_adpll_lock = count2adpll(ref_count as i32 - main_count as i32);
set_adpll(i2c::DCXO::Main, *base_adpll_lock)?;
set_adpll(i2c::DCXO::Helper, *base_adpll_lock)?;
Ok(())
}
pub fn get_freq_counts(timer: &mut GlobalTimer) -> (u32, u32) {
unsafe {
csr::wrpll::frequency_counter_update_en_write(1);
timer.delay_us(150_000); // 8ns << TIMER_WIDTH
csr::wrpll::frequency_counter_update_en_write(0);
#[cfg(wrpll_ref_clk = "GTX_CDR")]
let ref_count = csr::wrpll::frequency_counter_counter_rtio_rx0_read();
#[cfg(wrpll_ref_clk = "SMA_CLKIN")]
let ref_count = csr::wrpll::frequency_counter_counter_ref_read();
let main_count = csr::wrpll::frequency_counter_counter_sys_read();
(ref_count, main_count)
}
}
fn reset_plls() -> Result<(), &'static str> {
*H_INTEGRATOR.lock() = 0;
*M_INTEGRATOR.lock() = 0;
set_adpll(i2c::DCXO::Main, 0)?;
set_adpll(i2c::DCXO::Helper, 0)?;
Ok(())
}
fn clear_pending(interrupt: FIQ) {
match interrupt {
FIQ::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_write(1) },
FIQ::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_write(1) },
};
}
fn is_pending(interrupt: FIQ) -> bool {
match interrupt {
FIQ::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_read() == 1 },
FIQ::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_read() == 1 },
}
}
pub fn interrupt_handler() {
if is_pending(FIQ::RefTag) {
tag_collector::collect_tags(FIQ::RefTag);
clear_pending(FIQ::RefTag);
helper_pll().expect("failed to run helper DCXO PLL");
}
if is_pending(FIQ::MainTag) {
tag_collector::collect_tags(FIQ::MainTag);
clear_pending(FIQ::MainTag);
}
if tag_collector::phase_diff_ready() {
main_pll().expect("failed to run main DCXO PLL");
tag_collector::clear_phase_diff_ready();
}
}
fn helper_pll() -> Result<(), &'static str> {
// unsafe {
// use libboard_zynq::println;
// ERR_ARR[COUNTER] = tag_collector::get_period_error();
// if COUNTER == SIZE - 1 {
// for i in 0..SIZE {
// println!("{}", ERR_ARR[i])
// }
// COUNTER = 0;
// let mut timer = GlobalTimer::get();
// timer.delay_us(20_000_000);
// }
// COUNTER += 1;
// }
let period_err = tag_collector::get_period_error();
let mut integrator_lock = H_INTEGRATOR.lock();
*integrator_lock += period_err * KI;
let mut h_adpll = *BASE_ADPLL.lock() + period_err * KP + *integrator_lock;
h_adpll = h_adpll.clamp(-ADPLL_MAX, ADPLL_MAX);
set_adpll(i2c::DCXO::Helper, h_adpll)?;
Ok(())
}
const SIZE: usize = 2000;
static mut COUNTER: usize = 0;
static mut ERR_ARR: [i32; SIZE] = [0; SIZE];
static mut FIN_ADPLL: i32 = 0;
fn main_pll() -> Result<(), &'static str> {
// unsafe {
// use libboard_zynq::println;
// ERR_ARR[COUNTER] = tag_collector::get_phase_error();
// if COUNTER == SIZE - 1 {
// for i in 0..SIZE {
// println!("{}", ERR_ARR[i])
// }
// println!("{:>3} Zero crossing adpll = {:>5}", SIZE, FIN_ADPLL);
// COUNTER = 0;
// let mut timer = GlobalTimer::get();
// timer.delay_us(20_000_000);
// }
// COUNTER += 1;
// }
let phase_err = tag_collector::get_phase_error();
let mut integrator_lock = M_INTEGRATOR.lock();
*integrator_lock += phase_err * KI;
let mut m_adpll = *BASE_ADPLL.lock() + phase_err * KP + *integrator_lock;
m_adpll = m_adpll.clamp(-ADPLL_MAX, ADPLL_MAX);
set_adpll(i2c::DCXO::Main, m_adpll)?;
unsafe {
if ERR_ARR[COUNTER] == 0 {
FIN_ADPLL = m_adpll;
}
}
Ok(())
}
pub fn select_recovered_clock(rc: bool, timer: &mut GlobalTimer) {
set_fiq(false);
if rc {
tag_collector::reset();
reset_plls().expect("failed to reset main and helper PLL");
info!("warming up refclk...");
// refclk need a couple seconds for freq counter to read it properly
// timer.delay_us(20_000_000);
set_base_adpll(timer).expect("failed to set base adpll");
let ppm = 0.0;
*BASE_ADPLL.lock() += (ppm / 0.0001164) as i32;
timer.delay_us(200);
info!("KP = {}, KI = {}", KP, KI);
info!("adding {}ppm to main & helper base adpll ({})", ppm, *BASE_ADPLL.lock());
// clear gateware pending flag
clear_pending(FIQ::RefTag);
clear_pending(FIQ::MainTag);
unsafe {
info!("ref tag = {} ", csr::wrpll::ref_tag_read());
info!("main tag = {} ", csr::wrpll::main_tag_read());
}
// use nFIQ to avoid IRQ being disabled by mutex lock and mess up PLL
set_fiq(true);
info!("WRPLL interrupt enabled");
timer.delay_ms(500);
unsafe {
info!("main_tag_ev_enable_read() = {}", csr::wrpll::main_tag_ev_enable_read());
info!("main_tag_ev_status_read() = {}", csr::wrpll::main_tag_ev_status_read());
info!("ref_tag_ev_enable_read() = {}", csr::wrpll::ref_tag_ev_enable_read());
info!("ref_tag_ev_status_read() = {}", csr::wrpll::ref_tag_ev_status_read());
// loop {
// if is_pending(FIQ::RefTag) {
// info!("REF tag is pending")
// }
// if is_pending(FIQ::MainTag) {
// info!("Main tag is pending")
// }
// // info!("ref tag = {} ", csr::wrpll::ref_tag_read());
// // info!("main tag = {} ", csr::wrpll::main_tag_read());
// timer.delay_ms(500);
// }
}
}
}
}
#[cfg(has_wrpll_refclk)]
pub mod wrpll_refclk {
use super::*;
pub struct MmcmSetting {
pub clkout0_reg1: u16, //0x08
pub clkout0_reg2: u16, //0x09
pub clkfbout_reg1: u16, //0x14
pub clkfbout_reg2: u16, //0x15
pub div_reg: u16, //0x16
pub lock_reg1: u16, //0x18
pub lock_reg2: u16, //0x19
pub lock_reg3: u16, //0x1A
pub power_reg: u16, //0x28
pub filt_reg1: u16, //0x4E
pub filt_reg2: u16, //0x4F
}
fn one_clock_cycle(timer: &mut GlobalTimer) {
unsafe {
csr::wrpll_refclk::mmcm_dclk_write(1);
timer.delay_us(1);
csr::wrpll_refclk::mmcm_dclk_write(0);
timer.delay_us(1);
}
}
fn set_addr(address: u8) {
unsafe {
csr::wrpll_refclk::mmcm_daddr_write(address);
}
}
fn set_data(value: u16) {
unsafe {
csr::wrpll_refclk::mmcm_din_write(value);
}
}
fn set_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::wrpll_refclk::mmcm_den_write(val);
}
}
fn set_write_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::wrpll_refclk::mmcm_dwen_write(val);
}
}
fn get_data() -> u16 {
unsafe { csr::wrpll_refclk::mmcm_dout_read() }
}
fn drp_ready() -> bool {
unsafe { csr::wrpll_refclk::mmcm_dready_read() == 1 }
}
fn read(timer: &mut GlobalTimer, address: u8) -> u16 {
set_addr(address);
set_enable(true);
// Set DADDR on the MMCM and assert DEN for one clock cycle
one_clock_cycle(timer);
set_enable(false);
while !drp_ready() {
// keep the clock signal until data is ready
one_clock_cycle(timer);
}
get_data()
}
fn write(timer: &mut GlobalTimer, address: u8, value: u16) {
set_addr(address);
set_data(value);
set_write_enable(true);
set_enable(true);
// Set DADDR, DI on the MMCM and assert DWE, DEN for one clock cycle
one_clock_cycle(timer);
set_write_enable(false);
set_enable(false);
while !drp_ready() {
// keep the clock signal until write is finished
one_clock_cycle(timer);
}
}
fn reset(rst: bool) {
unsafe {
let val = if rst { 1 } else { 0 };
csr::wrpll_refclk::mmcm_reset_write(val)
}
}
pub fn setup(timer: &mut GlobalTimer, settings: MmcmSetting) -> Result<(), &'static str> {
// Based on "DRP State Machine" from XAPP888
// hold reset HIGH during mmcm config
reset(true);
write(timer, 0x08, settings.clkout0_reg1);
write(timer, 0x09, settings.clkout0_reg2);
write(timer, 0x14, settings.clkfbout_reg1);
write(timer, 0x15, settings.clkfbout_reg2);
write(timer, 0x16, settings.div_reg);
write(timer, 0x18, settings.lock_reg1);
write(timer, 0x19, settings.lock_reg2);
write(timer, 0x1A, settings.lock_reg3);
write(timer, 0x28, settings.power_reg);
write(timer, 0x4E, settings.filt_reg1);
write(timer, 0x4F, settings.filt_reg2);
reset(false);
// wait for the mmcm to lock
timer.delay_us(100);
let locked = unsafe { csr::wrpll_refclk::mmcm_locked_read() == 1 };
if !locked {
return Err("failed to generate 125Mhz ref clock from SMA CLKIN");
}
// const TIMER_WIDTH: u32 = 24;
// const COUNTER_DIV: u32 = 2;
// let (ref_count, main_count) = wrpll::get_freq_counts(timer);
// let f_sys = 125e6;
// info!(
// "ref counter = {} freq = {}",
// ref_count,
// (ref_count as f64 * f_sys) as f64 / (1 << (TIMER_WIDTH - COUNTER_DIV)) as f64
// );
// info!(
// "Main counter = {} freq = {}",
// main_count,
// (main_count as f64 * f_sys) as f64 / (1 << (TIMER_WIDTH - COUNTER_DIV)) as f64
// );
Ok(())
}
}

View File

@ -0,0 +1,22 @@
#[cfg(has_wrpll)]
use libboard_artiq::si549;
use libboard_zynq::{println, stdio};
use libcortex_a9::{interrupt_handler, regs::MPIDR};
use libregister::RegisterR;
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
// println!("hello?");
match MPIDR.read().cpu_id() {
0 => {
// nFIQ is driven directly and bypass GIC
#[cfg(has_wrpll)]
si549::wrpll::interrupt_handler();
return;
}
_ => {}
};
stdio::drop_uart();
println!("FIQ");
loop {}
});

View File

@ -21,6 +21,7 @@ pub use pl::csr::rtio_core;
use void::Void; use void::Void;
pub mod eh_artiq; pub mod eh_artiq;
pub mod fiq;
pub mod i2c; pub mod i2c;
pub mod irq; pub mod irq;
pub mod kernel; pub mod kernel;

View File

@ -104,6 +104,11 @@ pub fn main_core0() {
io_expander1 io_expander1
.init(i2c_bus) .init(i2c_bus)
.expect("I2C I/O expander #1 initialization failed"); .expect("I2C I/O expander #1 initialization failed");
// Drive CLK_SEL to true
#[cfg(has_si549)]
io_expander0.set(1, 7, true);
// Drive TX_DISABLE to false on SFP0..3 // Drive TX_DISABLE to false on SFP0..3
io_expander0.set(0, 1, false); io_expander0.set(0, 1, false);
io_expander1.set(0, 1, false); io_expander1.set(0, 1, false);

View File

@ -4,6 +4,10 @@ use ksupport::i2c;
use libboard_artiq::pl; use libboard_artiq::pl;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_artiq::si5324; use libboard_artiq::si5324;
#[cfg(has_si549)]
use libboard_artiq::si549;
#[cfg(has_wrpll)]
use libboard_artiq::si549::wrpll_refclk;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_zynq::i2c::I2c; use libboard_zynq::i2c::I2c;
use libboard_zynq::timer::GlobalTimer; use libboard_zynq::timer::GlobalTimer;
@ -260,6 +264,139 @@ fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324"); si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
} }
#[cfg(has_wrpll)]
fn wrpll_setup(timer: &mut GlobalTimer, clk: RtioClock, si549_settings: si549::FrequencySetting) {
let mmcm_setting = match clk {
RtioClock::Ext0_Synth0_10to125 => si549::wrpll_refclk::MmcmSetting {
clkout0_reg1: 0x1083,
clkout0_reg2: 0x0080,
clkfbout_reg1: 0x179e,
clkfbout_reg2: 0x4c00,
div_reg: 0x1041,
lock_reg1: 0x00fa,
lock_reg2: 0x7c01,
lock_reg3: 0xffe9,
power_reg: 0x9900,
filt_reg1: 0x0808,
filt_reg2: 0x0800,
},
RtioClock::Ext0_Synth0_80to125 => si549::wrpll_refclk::MmcmSetting {
clkout0_reg1: 0x1145,
clkout0_reg2: 0x0000,
clkfbout_reg1: 0x11c7,
clkfbout_reg2: 0x5880,
div_reg: 0x1041,
lock_reg1: 0x028a,
lock_reg2: 0x7c01,
lock_reg3: 0xffe9,
power_reg: 0x9900,
filt_reg1: 0x0808,
filt_reg2: 0x9800,
},
RtioClock::Ext0_Synth0_100to125 => si549::wrpll_refclk::MmcmSetting {
clkout0_reg1: 0x1145,
clkout0_reg2: 0x0000,
clkfbout_reg1: 0x1145,
clkfbout_reg2: 0x4c00,
div_reg: 0x1041,
lock_reg1: 0x0339,
lock_reg2: 0x7c01,
lock_reg3: 0xffe9,
power_reg: 0x9900,
filt_reg1: 0x0808,
filt_reg2: 0x9800,
},
RtioClock::Ext0_Synth0_125to125 => si549::wrpll_refclk::MmcmSetting {
clkout0_reg1: 0x1145,
clkout0_reg2: 0x0000,
clkfbout_reg1: 0x1145,
clkfbout_reg2: 0x0000,
div_reg: 0x1041,
lock_reg1: 0x03e8,
lock_reg2: 0x7001,
lock_reg3: 0xf3e9,
power_reg: 0x0100,
filt_reg1: 0x0808,
filt_reg2: 0x1100,
},
_ => unreachable!(),
};
si549::main_setup(timer, si549_settings).expect("cannot initialize main Si549");
si549::wrpll::helper_setup(timer, si549_settings).expect("cannot initialize helper Si549");
si549::wrpll_refclk::setup(timer, mmcm_setting).expect("cannot initialize ref clk for wrpll");
// si549::wrpll::select_recovered_clock(true, timer);
}
fn get_si549_setting(clk: RtioClock) -> si549::FrequencySetting {
match clk {
RtioClock::Ext0_Synth0_10to125 => {
info!("using 10MHz reference to make 125MHz RTIO clock with WRPLL");
}
RtioClock::Ext0_Synth0_80to125 => {
info!("using 80MHz reference to make 125MHz RTIO clock with WRPLL");
}
RtioClock::Ext0_Synth0_100to125 => {
info!("using 100MHz reference to make 125MHz RTIO clock with WRPLL");
}
RtioClock::Ext0_Synth0_125to125 => {
info!("using 125MHz reference to make 125MHz RTIO clock with WRPLL");
}
RtioClock::Int_150 => {
info!("using internal 150MHz RTIO clock");
}
RtioClock::Int_100 => {
info!("using internal 100MHz RTIO clock");
}
RtioClock::Int_125 => {
info!("using internal 125MHz RTIO clock");
}
_ => {
warn!(
"rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.",
clk
);
}
};
match clk {
RtioClock::Int_100 => {
si549::FrequencySetting {
main: si549::DividerConfig {
hsdiv: 0x06C,
lsdiv: 0,
fbdiv: 0x046C5F49797,
},
#[cfg(has_wrpll)]
helper: si549::DividerConfig {
// 100Mhz*32767/32768
hsdiv: 0x06C,
lsdiv: 0,
fbdiv: 0x046C5670BBD,
},
}
}
_ => {
// Everything else use 125Mhz
si549::FrequencySetting {
main: si549::DividerConfig {
hsdiv: 0x058,
lsdiv: 0,
fbdiv: 0x04815791F25,
},
#[cfg(has_wrpll)]
helper: si549::DividerConfig {
// 125Mhz*32767/32768
hsdiv: 0x058,
lsdiv: 0,
fbdiv: 0x04814E8F442,
},
}
}
}
}
pub fn init(timer: &mut GlobalTimer, cfg: &Config) { pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
let clk = get_rtio_clock_cfg(cfg); let clk = get_rtio_clock_cfg(cfg);
#[cfg(has_si5324)] #[cfg(has_si5324)]
@ -274,9 +411,30 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
} }
} }
#[cfg(has_si549)]
{
let si549_settings = get_si549_setting(clk);
match clk {
RtioClock::Ext0_Synth0_10to125
| RtioClock::Ext0_Synth0_80to125
| RtioClock::Ext0_Synth0_100to125
| RtioClock::Ext0_Synth0_125to125 => {
wrpll_setup(timer, clk, si549_settings);
}
_ => {
wrpll_setup(timer, RtioClock::Ext0_Synth0_125to125, si549_settings);
// si549::main_setup(timer, si549_settings).expect("cannot initialize main Si549");
}
}
}
#[cfg(has_drtio)] #[cfg(has_drtio)]
init_drtio(timer); init_drtio(timer);
#[cfg(not(has_drtio))] #[cfg(not(has_drtio))]
init_rtio(timer); init_rtio(timer);
// !! move back to cfg after testing
si549::wrpll::select_recovered_clock(true, timer);
} }

View File

@ -29,6 +29,8 @@ use libboard_artiq::grabber;
use libboard_artiq::io_expander; use libboard_artiq::io_expander;
#[cfg(has_si5324)] #[cfg(has_si5324)]
use libboard_artiq::si5324; use libboard_artiq::si5324;
#[cfg(has_si549)]
use libboard_artiq::si549;
use libboard_artiq::{drtio_routing, drtioaux, use libboard_artiq::{drtio_routing, drtioaux,
drtioaux_proto::{MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE}, drtioaux_proto::{MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE},
identifier_read, logger, identifier_read, logger,
@ -823,6 +825,38 @@ const SI5324_SETTINGS: si5324::FrequencySettings = si5324::FrequencySettings {
crystal_as_ckin2: true, crystal_as_ckin2: true,
}; };
#[cfg(all(has_si549, rtio_frequency = "125.0"))]
const SI549_SETTINGS: si549::FrequencySetting = si549::FrequencySetting {
main: si549::DividerConfig {
hsdiv: 0x058,
lsdiv: 0,
fbdiv: 0x04815791F25,
},
#[cfg(has_wrpll)]
helper: si549::DividerConfig {
// 125Mhz*32767/32768
hsdiv: 0x058,
lsdiv: 0,
fbdiv: 0x04814E8F442,
},
};
#[cfg(all(has_si549, rtio_frequency = "100.0"))]
pub const SI549_SETTINGS: si549::FrequencySetting = si549::FrequencySetting {
main: si549::DividerConfig {
hsdiv: 0x06C,
lsdiv: 0,
fbdiv: 0x046C5F49797,
},
#[cfg(has_wrpll)]
helper: si549::DividerConfig {
// 100Mhz*32767/32768
hsdiv: 0x06C,
lsdiv: 0,
fbdiv: 0x046C5670BBD,
},
};
static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17]; static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
#[no_mangle] #[no_mangle]
@ -856,6 +890,11 @@ pub extern "C" fn main_core0() -> i32 {
io_expander1 io_expander1
.init(&mut i2c) .init(&mut i2c)
.expect("I2C I/O expander #1 initialization failed"); .expect("I2C I/O expander #1 initialization failed");
// Drive CLK_SEL to true
#[cfg(has_si549)]
io_expander0.set(1, 7, true);
// Drive TX_DISABLE to false on SFP0..3 // Drive TX_DISABLE to false on SFP0..3
io_expander0.set(0, 1, false); io_expander0.set(0, 1, false);
io_expander1.set(0, 1, false); io_expander1.set(0, 1, false);
@ -867,6 +906,8 @@ pub extern "C" fn main_core0() -> i32 {
#[cfg(has_si5324)] #[cfg(has_si5324)]
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324"); si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
#[cfg(has_si549)]
si549::main_setup(&mut timer, SI549_SETTINGS).expect("cannot initialize main Si549");
timer.delay_us(100_000); timer.delay_us(100_000);
info!("Switching SYS clocks..."); info!("Switching SYS clocks...");
@ -884,6 +925,8 @@ pub extern "C" fn main_core0() -> i32 {
unsafe { unsafe {
csr::gt_drtio::txenable_write(0xffffffffu32 as _); csr::gt_drtio::txenable_write(0xffffffffu32 as _);
} }
#[cfg(has_wrpll)]
si549::wrpll::helper_setup(&mut timer, SI549_SETTINGS).expect("cannot initialize helper Si549");
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()]; let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
@ -929,6 +972,9 @@ pub extern "C" fn main_core0() -> i32 {
si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew");
} }
#[cfg(has_wrpll)]
si549::wrpll::select_recovered_clock(true, &mut timer);
// Various managers created here, so when link is dropped, all DMA traces // Various managers created here, so when link is dropped, all DMA traces
// are cleared out for a clean slate on subsequent connections, // are cleared out for a clean slate on subsequent connections,
// without a manual intervention. // without a manual intervention.
@ -1026,6 +1072,8 @@ pub extern "C" fn main_core0() -> i32 {
info!("uplink is down, switching to local oscillator clock"); info!("uplink is down, switching to local oscillator clock");
#[cfg(has_siphaser)] #[cfg(has_siphaser)]
si5324::siphaser::select_recovered_clock(&mut i2c, false, &mut timer).expect("failed to switch clocks"); si5324::siphaser::select_recovered_clock(&mut i2c, false, &mut timer).expect("failed to switch clocks");
#[cfg(has_wrpll)]
si549::wrpll::select_recovered_clock(false, &mut timer);
} }
} }