Compare commits
8 Commits
cbe48f9412
...
ff68521bfd
Author | SHA1 | Date |
---|---|---|
morgan | ff68521bfd | |
morgan | 712b27153e | |
morgan | 82e9fbc6d2 | |
morgan | 917579f005 | |
morgan | 74d07a579c | |
morgan | 303aad20ac | |
morgan | 945a9f1c47 | |
morgan | 4f34a7c6d0 |
20
flake.lock
20
flake.lock
|
@ -11,11 +11,11 @@
|
||||||
"src-pythonparser": "src-pythonparser"
|
"src-pythonparser": "src-pythonparser"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706785107,
|
"lastModified": 1710303235,
|
||||||
"narHash": "sha256-Uj72tqigiOCdewSSBBMg6zUpVKhwjAo1HeLJgvyZ3oc=",
|
"narHash": "sha256-0rIfVoL8RInAQSDVfjpLdMqIYdnVsA8DdMk2+aqvwrM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "3aaa7e04f26a495e8847e47424bfc16d76d82bf8",
|
"rev": "c4323e1179aa0b9c9b4c135f894f267715cf2391",
|
||||||
"revCount": 8672,
|
"revCount": 8727,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/m-labs/artiq.git"
|
"url": "https://github.com/m-labs/artiq.git"
|
||||||
},
|
},
|
||||||
|
@ -37,11 +37,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1701573753,
|
"lastModified": 1707216368,
|
||||||
"narHash": "sha256-vhEtXjb9AM6/HnsgfVmhJQeqQ9JqysUm7iWNzTIbexs=",
|
"narHash": "sha256-ZXoqzG2QsVsybALLYXs473avXcyKSZNh2kIgcPo60XQ=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "artiq-comtools",
|
"repo": "artiq-comtools",
|
||||||
"rev": "199bdabf4de49cb7ada8a4ac7133008e0f8434b7",
|
"rev": "e5d0204490bccc07ef9141b0d7c405ab01cb8273",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -118,11 +118,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706515015,
|
"lastModified": 1707347730,
|
||||||
"narHash": "sha256-eFfY5A7wlYy3jD/75lx6IJRueg4noE+jowl0a8lIlVo=",
|
"narHash": "sha256-0etC/exQIaqC9vliKhc3eZE2Mm2wgLa0tj93ZF/egvM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f4a8d6d5324c327dcc2d863eb7f3cc06ad630df4",
|
"rev": "6832d0d99649db3d65a0e15fa51471537b2c56a6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.cdc import PulseSynchronizer, MultiReg
|
||||||
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
|
|
||||||
|
class DDMTDSampler(Module):
|
||||||
|
def __init__(self, cd_ref, main_clk_se):
|
||||||
|
self.ref_beating = Signal()
|
||||||
|
self.main_beating = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
ref_beating_FF = Signal()
|
||||||
|
main_beating_FF = Signal()
|
||||||
|
self.specials += [
|
||||||
|
# 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)
|
||||||
|
)
|
||||||
|
]
|
|
@ -26,6 +26,7 @@ import analyzer
|
||||||
import acpki
|
import acpki
|
||||||
import drtio_aux_controller
|
import drtio_aux_controller
|
||||||
import zynq_clocking
|
import zynq_clocking
|
||||||
|
import wrpll
|
||||||
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 = {
|
||||||
|
@ -47,6 +48,19 @@ eem_iostandard_dict = {
|
||||||
def eem_iostandard(eem):
|
def eem_iostandard(eem):
|
||||||
return IOStandard(eem_iostandard_dict[eem])
|
return IOStandard(eem_iostandard_dict[eem])
|
||||||
|
|
||||||
|
class ClockSynthesis(Module):
|
||||||
|
def __init__(self, platform):
|
||||||
|
self.se = Signal()
|
||||||
|
|
||||||
|
clk_synth = platform.request("cdr_clk_clean_fabric")
|
||||||
|
platform.add_period_constraint(clk_synth.p, 8.0)
|
||||||
|
|
||||||
|
self.specials += [
|
||||||
|
Instance("IBUFGDS",
|
||||||
|
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
|
||||||
|
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=self.se
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
class SMAClkinForward(Module):
|
class SMAClkinForward(Module):
|
||||||
def __init__(self, platform):
|
def __init__(self, platform):
|
||||||
|
@ -108,6 +122,7 @@ class GenericStandalone(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
clk_freq = description["rtio_frequency"]
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
platform = kasli_soc.Platform()
|
platform = kasli_soc.Platform()
|
||||||
platform.toolchain.bitstream_commands.extend([
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
@ -120,24 +135,10 @@ class GenericStandalone(SoCCore):
|
||||||
|
|
||||||
self.config["HW_REV"] = description["hw_rev"]
|
self.config["HW_REV"] = description["hw_rev"]
|
||||||
|
|
||||||
|
self.submodules.clk_synth = ClockSynthesis(self.platform)
|
||||||
self.submodules += SMAClkinForward(self.platform)
|
|
||||||
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
|
||||||
|
|
||||||
clk_synth = platform.request("cdr_clk_clean_fabric")
|
|
||||||
clk_synth_se = Signal()
|
|
||||||
clk_synth_se_buf = Signal()
|
clk_synth_se_buf = Signal()
|
||||||
platform.add_period_constraint(clk_synth.p, 8.0)
|
self.specials += Instance("BUFG", i_I=self.clk_synth.se, o_O=clk_synth_se_buf)
|
||||||
|
|
||||||
self.specials += [
|
|
||||||
Instance("IBUFGDS",
|
|
||||||
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="FALSE",
|
|
||||||
i_I=clk_synth.p, i_IB=clk_synth.n, o_O=clk_synth_se
|
|
||||||
),
|
|
||||||
Instance("BUFG", i_I=clk_synth_se, o_O=clk_synth_se_buf),
|
|
||||||
]
|
|
||||||
fix_serdes_timing_path(platform)
|
fix_serdes_timing_path(platform)
|
||||||
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
self.submodules.bootstrap = GTPBootstrapClock(self.platform, clk_freq)
|
||||||
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
||||||
|
@ -149,6 +150,23 @@ class GenericStandalone(SoCCore):
|
||||||
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||||
self.crg.cd_sys = self.sys_crg.cd_sys
|
self.crg.cd_sys = self.sys_crg.cd_sys
|
||||||
|
|
||||||
|
if with_wrpll:
|
||||||
|
self.submodules.wrpll_refclk = wrpll.SMAFrequencyMultiplier(platform.request("sma_clkin"))
|
||||||
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
|
platform=self.platform,
|
||||||
|
cd_ref=self.wrpll_refclk.cd_ref,
|
||||||
|
main_clk_se=self.clk_synth.se)
|
||||||
|
self.csr_devices.append("wrpll_refclk")
|
||||||
|
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["SI5324_SOFT_RESET"] = None
|
||||||
|
|
||||||
|
|
||||||
self.rtio_channels = []
|
self.rtio_channels = []
|
||||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
if has_grabber:
|
if has_grabber:
|
||||||
|
@ -207,6 +225,7 @@ class GenericStandalone(SoCCore):
|
||||||
class GenericMaster(SoCCore):
|
class GenericMaster(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
clk_freq = description["rtio_frequency"]
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
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"])
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
|
@ -222,8 +241,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(
|
||||||
|
@ -257,8 +274,22 @@ 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")
|
||||||
|
|
||||||
self.config["HAS_SI5324"] = None
|
if with_wrpll:
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
self.submodules.clk_synth = ClockSynthesis(self.platform)
|
||||||
|
self.submodules.wrpll_refclk = wrpll.SMAFrequencyMultiplier(platform.request("sma_clkin"))
|
||||||
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
|
platform=self.platform,
|
||||||
|
cd_ref=self.wrpll_refclk.cd_ref,
|
||||||
|
main_clk_se=self.clk_synth.se)
|
||||||
|
self.csr_devices.append("wrpll_refclk")
|
||||||
|
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["SI5324_SOFT_RESET"] = None
|
||||||
|
|
||||||
self.rtio_channels = []
|
self.rtio_channels = []
|
||||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
|
@ -400,6 +431,7 @@ class GenericMaster(SoCCore):
|
||||||
class GenericSatellite(SoCCore):
|
class GenericSatellite(SoCCore):
|
||||||
def __init__(self, description, acpki=False):
|
def __init__(self, description, acpki=False):
|
||||||
clk_freq = description["rtio_frequency"]
|
clk_freq = description["rtio_frequency"]
|
||||||
|
with_wrpll = description["enable_wrpll"]
|
||||||
|
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
|
|
||||||
|
@ -551,14 +583,27 @@ 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)
|
||||||
|
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
if with_wrpll:
|
||||||
si5324_clkin=platform.request("cdr_clk"),
|
self.submodules.clk_synth = ClockSynthesis(self.platform)
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
self.submodules.wrpll = wrpll.WRPLL(
|
||||||
ultrascale=False,
|
platform=self.platform,
|
||||||
rtio_clk_freq=self.gt_drtio.rtio_clk_freq)
|
cd_ref=self.gt_drtio.cd_rtio_rx0,
|
||||||
self.csr_devices.append("siphaser")
|
main_clk_se=self.clk_synth.se)
|
||||||
self.config["HAS_SI5324"] = None
|
self.submodules.wrpll_skewtester = wrpll.SkewTester(self.rx_synchronizer)
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
self.csr_devices.append("wrpll_skewtester")
|
||||||
|
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(
|
||||||
|
si5324_clkin=platform.request("cdr_clk"),
|
||||||
|
rx_synchronizer=self.rx_synchronizer,
|
||||||
|
ultrascale=False,
|
||||||
|
rtio_clk_freq=self.gt_drtio.rtio_clk_freq)
|
||||||
|
self.csr_devices.append("siphaser")
|
||||||
|
self.config["HAS_SI5324"] = None
|
||||||
|
self.config["SI5324_SOFT_RESET"] = None
|
||||||
|
|
||||||
gtx0 = self.gt_drtio.gtxs[0]
|
gtx0 = self.gt_drtio.gtxs[0]
|
||||||
platform.add_false_path_constraints(
|
platform.add_false_path_constraints(
|
||||||
|
|
|
@ -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),
|
||||||
|
)
|
||||||
|
]
|
|
@ -0,0 +1,215 @@
|
||||||
|
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):
|
||||||
|
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 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)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
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),
|
||||||
|
]
|
|
@ -10,6 +10,7 @@ name = "libboard_artiq"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"]
|
||||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
||||||
|
calibrate_wrpll_skew = []
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
build_zynq = { path = "../libbuild_zynq" }
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
@ -25,7 +26,7 @@ void = { version = "1", default-features = false }
|
||||||
|
|
||||||
io = { path = "../libio", features = ["byteorder"] }
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
||||||
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core", "dummy_fiq_handler"] }
|
libsupport_zynq = { path = "@@ZYNQ_RS@@/libsupport_zynq", default-features = false, features = ["alloc_core"] }
|
||||||
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
libregister = { path = "@@ZYNQ_RS@@/libregister" }
|
||||||
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn"] }
|
libconfig = { path = "@@ZYNQ_RS@@/libconfig", features = ["fat_lfn"] }
|
||||||
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
libcortex_a9 = { path = "@@ZYNQ_RS@@/libcortex_a9" }
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
use libboard_zynq::{println, stdio};
|
||||||
|
use libcortex_a9::{interrupt_handler, regs::MPIDR};
|
||||||
|
use libregister::RegisterR;
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
use crate::si549;
|
||||||
|
|
||||||
|
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
|
||||||
|
match MPIDR.read().cpu_id() {
|
||||||
|
0 => {
|
||||||
|
// nFIQ is driven directly and bypass GIC
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
si549::wrpll::interrupt_handler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("FIQ");
|
||||||
|
loop {}
|
||||||
|
});
|
|
@ -1,6 +1,7 @@
|
||||||
use libboard_zynq::i2c;
|
use libboard_zynq::i2c;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
|
// Only the bare minimum registers. Bits/IO connections equivalent between IC types.
|
||||||
|
@ -19,11 +20,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] = [
|
||||||
|
@ -33,6 +38,7 @@ const IODIR1: [u8; 2] = [
|
||||||
|
|
||||||
pub struct IoExpander {
|
pub struct IoExpander {
|
||||||
address: u8,
|
address: u8,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &'static [(u8, u8, u8)],
|
virtual_led_mapping: &'static [(u8, u8, u8)],
|
||||||
iodir: [u8; 2],
|
iodir: [u8; 2],
|
||||||
out_current: [u8; 2],
|
out_current: [u8; 2],
|
||||||
|
@ -42,17 +48,18 @@ pub struct IoExpander {
|
||||||
|
|
||||||
impl IoExpander {
|
impl IoExpander {
|
||||||
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
|
pub fn new(i2c: &mut i2c::I2c, index: u8) -> Result<Self, &'static str> {
|
||||||
#[cfg(hw_rev = "v1.0")]
|
#[cfg(all(hw_rev = "v1.0", has_virtual_leds))]
|
||||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
|
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
|
||||||
#[cfg(hw_rev = "v1.1")]
|
#[cfg(all(hw_rev = "v1.1", has_virtual_leds))]
|
||||||
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
|
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)];
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
|
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
|
||||||
|
|
||||||
// Both expanders on SHARED I2C bus
|
// Both expanders on SHARED I2C bus
|
||||||
let mut io_expander = match index {
|
let mut io_expander = match index {
|
||||||
0 => IoExpander {
|
0 => IoExpander {
|
||||||
address: 0x40,
|
address: 0x40,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
|
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
|
||||||
iodir: IODIR0,
|
iodir: IODIR0,
|
||||||
out_current: [0; 2],
|
out_current: [0; 2],
|
||||||
|
@ -66,6 +73,7 @@ impl IoExpander {
|
||||||
},
|
},
|
||||||
1 => IoExpander {
|
1 => IoExpander {
|
||||||
address: 0x42,
|
address: 0x42,
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
|
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
|
||||||
iodir: IODIR1,
|
iodir: IODIR1,
|
||||||
out_current: [0; 2],
|
out_current: [0; 2],
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
extern crate core_io;
|
extern crate core_io;
|
||||||
extern crate crc;
|
extern crate crc;
|
||||||
|
@ -19,7 +21,8 @@ pub mod drtioaux;
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
pub mod drtioaux_async;
|
pub mod drtioaux_async;
|
||||||
pub mod drtioaux_proto;
|
pub mod drtioaux_proto;
|
||||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
pub mod fiq;
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
pub mod io_expander;
|
pub mod io_expander;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
#[cfg(has_drtio)]
|
#[cfg(has_drtio)]
|
||||||
|
@ -35,7 +38,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 {
|
||||||
|
|
|
@ -0,0 +1,814 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
const ADPLL_MAX: i32 = (950.0 / 0.0001164) as i32;
|
||||||
|
|
||||||
|
pub struct DividerConfig {
|
||||||
|
pub hsdiv: u16,
|
||||||
|
pub lsdiv: u8,
|
||||||
|
pub fbdiv: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FrequencySetting {
|
||||||
|
pub main: DividerConfig,
|
||||||
|
pub helper: DividerConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod i2c {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DCXO {
|
||||||
|
Main,
|
||||||
|
Helper,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn half_period(timer: &mut GlobalTimer) {
|
||||||
|
timer.delay_us(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_i(dcxo: DCXO) -> bool {
|
||||||
|
match dcxo {
|
||||||
|
DCXO::Main => unsafe { csr::wrpll::main_dcxo_sda_in_read() == 1 },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::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::wrpll::main_dcxo_sda_oe_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::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::wrpll::main_dcxo_sda_out_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::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::wrpll::main_dcxo_scl_oe_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::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::wrpll::main_dcxo_scl_out_write(val) },
|
||||||
|
DCXO::Helper => unsafe { csr::wrpll::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::wrpll::main_dcxo_bitbang_enable_write(1);
|
||||||
|
csr::wrpll::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::wrpll::main_dcxo_bitbang_enable_write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Main Si549 started");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helper_setup(timer: &mut GlobalTimer, settings: &FrequencySetting) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll::helper_reset_write(1);
|
||||||
|
csr::wrpll::helper_dcxo_bitbang_enable_write(1);
|
||||||
|
csr::wrpll::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::wrpll::helper_dcxo_bitbang_enable_write(0);
|
||||||
|
}
|
||||||
|
info!("Helper Si549 started");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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::wrpll::main_dcxo_bitbang_enable_read() == 1 {
|
||||||
|
return Err("Main si549 bitbang mode is active when using gateware i2c");
|
||||||
|
}
|
||||||
|
|
||||||
|
while csr::wrpll::main_dcxo_adpll_busy_read() == 1 {}
|
||||||
|
csr::wrpll::main_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
csr::wrpll::main_dcxo_adpll_write(adpll as u32);
|
||||||
|
|
||||||
|
csr::wrpll::main_dcxo_adpll_stb_write(1);
|
||||||
|
csr::wrpll::main_dcxo_adpll_stb_write(0);
|
||||||
|
|
||||||
|
if csr::wrpll::main_dcxo_nack_read() == 1 {
|
||||||
|
return Err("Main si549 failed to ack adpll write");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
i2c::DCXO::Helper => unsafe {
|
||||||
|
if csr::wrpll::helper_dcxo_bitbang_enable_read() == 1 {
|
||||||
|
return Err("Helper si549 bitbang mode is active when using gateware i2c");
|
||||||
|
}
|
||||||
|
|
||||||
|
while csr::wrpll::helper_dcxo_adpll_busy_read() == 1 {}
|
||||||
|
csr::wrpll::helper_dcxo_i2c_address_write(ADDRESS);
|
||||||
|
csr::wrpll::helper_dcxo_adpll_write(adpll as u32);
|
||||||
|
|
||||||
|
csr::wrpll::helper_dcxo_adpll_stb_write(1);
|
||||||
|
csr::wrpll::helper_dcxo_adpll_stb_write(0);
|
||||||
|
|
||||||
|
if csr::wrpll::helper_dcxo_nack_read() == 1 {
|
||||||
|
return Err("Helper si549 failed to ack adpll write");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
pub mod wrpll {
|
||||||
|
|
||||||
|
use libcortex_a9::mutex::Mutex;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const BEATING_PERIOD: i32 = 0x8000;
|
||||||
|
const BEATING_HALFPERIOD: i32 = 0x4000;
|
||||||
|
const TIMER_WIDTH: u32 = 24;
|
||||||
|
const COUNTER_DIV: u32 = 2;
|
||||||
|
|
||||||
|
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 ISR {
|
||||||
|
RefTag,
|
||||||
|
MainTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tag_collector {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GTX_CDR")]
|
||||||
|
static TAG_OFFSET: Mutex<u32> = Mutex::new(19050);
|
||||||
|
#[cfg(wrpll_ref_clk = "SMA_CLKIN")]
|
||||||
|
static TAG_OFFSET: Mutex<u32> = Mutex::new(0);
|
||||||
|
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: ISR) {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => {
|
||||||
|
*REF_TAG.lock() = unsafe { csr::wrpll::ref_tag_read() };
|
||||||
|
*REF_TAG_READY.lock() = true;
|
||||||
|
}
|
||||||
|
ISR::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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
pub fn set_tag_offset(offset: u32) {
|
||||||
|
*TAG_OFFSET.lock() = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
pub fn get_tag_offset() -> u32 {
|
||||||
|
*TAG_OFFSET.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) - TAG_OFFSET mod BEATING_PERIOD
|
||||||
|
let mut phase_error = (*MAIN_TAG.lock())
|
||||||
|
.overflowing_sub(*REF_TAG.lock() + *TAG_OFFSET.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_isr(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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: ISR) {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_write(1) },
|
||||||
|
ISR::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_write(1) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_pending(interrupt: ISR) -> bool {
|
||||||
|
match interrupt {
|
||||||
|
ISR::RefTag => unsafe { csr::wrpll::ref_tag_ev_pending_read() == 1 },
|
||||||
|
ISR::MainTag => unsafe { csr::wrpll::main_tag_ev_pending_read() == 1 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_handler() {
|
||||||
|
if is_pending(ISR::RefTag) {
|
||||||
|
tag_collector::collect_tags(ISR::RefTag);
|
||||||
|
clear_pending(ISR::RefTag);
|
||||||
|
|
||||||
|
helper_pll().expect("failed to run helper DCXO PLL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_pending(ISR::MainTag) {
|
||||||
|
tag_collector::collect_tags(ISR::MainTag);
|
||||||
|
clear_pending(ISR::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> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_pll() -> Result<(), &'static str> {
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GTX_CDR")]
|
||||||
|
fn test_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
// wait for PLL to stabilize
|
||||||
|
timer.delay_us(20_000);
|
||||||
|
|
||||||
|
info!("testing the skew of SYS CLK...");
|
||||||
|
if has_timing_error(timer) {
|
||||||
|
return Err("the skew cannot satisfy setup/hold time constraint of RX synchronizer");
|
||||||
|
}
|
||||||
|
info!("the skew of SYS CLK met the timing constraint");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GTX_CDR")]
|
||||||
|
fn has_timing_error(timer: &mut GlobalTimer) -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_skewtester::error_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_us(5_000);
|
||||||
|
unsafe { csr::wrpll_skewtester::error_read() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32, &'static str> {
|
||||||
|
const STEP: u32 = 8;
|
||||||
|
const STABLE_THRESHOLD: u32 = 10;
|
||||||
|
|
||||||
|
enum FSM {
|
||||||
|
Init,
|
||||||
|
WaitEdge,
|
||||||
|
GotEdge,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state: FSM = FSM::Init;
|
||||||
|
let mut offset: u32 = tag_collector::get_tag_offset();
|
||||||
|
let mut median_edge: u32 = 0;
|
||||||
|
let mut stable_counter: u32 = 0;
|
||||||
|
|
||||||
|
for _ in 0..(BEATING_PERIOD as u32 / STEP) as usize {
|
||||||
|
tag_collector::set_tag_offset(offset);
|
||||||
|
offset += STEP;
|
||||||
|
// wait for PLL to stabilize
|
||||||
|
timer.delay_us(20_000);
|
||||||
|
|
||||||
|
let error = has_timing_error(timer);
|
||||||
|
// A median edge deglitcher
|
||||||
|
match state {
|
||||||
|
FSM::Init => {
|
||||||
|
if error != target {
|
||||||
|
stable_counter += 1;
|
||||||
|
} else {
|
||||||
|
stable_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stable_counter >= STABLE_THRESHOLD {
|
||||||
|
state = FSM::WaitEdge;
|
||||||
|
stable_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FSM::WaitEdge => {
|
||||||
|
if error == target {
|
||||||
|
state = FSM::GotEdge;
|
||||||
|
median_edge = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FSM::GotEdge => {
|
||||||
|
if error != target {
|
||||||
|
median_edge += STEP;
|
||||||
|
stable_counter = 0;
|
||||||
|
} else {
|
||||||
|
stable_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stable_counter >= STABLE_THRESHOLD {
|
||||||
|
return Ok(median_edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err("failed to find timing error edge");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> {
|
||||||
|
info!("calibrating skew to meet timing constraint...");
|
||||||
|
|
||||||
|
// clear calibrated value
|
||||||
|
tag_collector::set_tag_offset(0);
|
||||||
|
let rising = find_edge(true, timer)? as i32;
|
||||||
|
let falling = find_edge(false, timer)? as i32;
|
||||||
|
|
||||||
|
let width = BEATING_PERIOD - (falling - rising);
|
||||||
|
let result = falling + width / 2;
|
||||||
|
tag_collector::set_tag_offset(result as u32);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"calibration successful, error zone: {} -> {}, width: {} ({}deg), middle of working region: {}",
|
||||||
|
rising,
|
||||||
|
falling,
|
||||||
|
width,
|
||||||
|
360 * width / BEATING_PERIOD,
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_recovered_clock(rc: bool, timer: &mut GlobalTimer) {
|
||||||
|
set_isr(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");
|
||||||
|
|
||||||
|
// clear gateware pending flag
|
||||||
|
clear_pending(ISR::RefTag);
|
||||||
|
clear_pending(ISR::MainTag);
|
||||||
|
|
||||||
|
// use nFIQ to avoid IRQ being disabled by mutex lock and mess up PLL
|
||||||
|
set_isr(true);
|
||||||
|
info!("WRPLL interrupt enabled");
|
||||||
|
|
||||||
|
#[cfg(feature = "calibrate_wrpll_skew")]
|
||||||
|
calibrate_skew(timer).expect("failed to set the correct skew");
|
||||||
|
|
||||||
|
#[cfg(wrpll_ref_clk = "GTX_CDR")]
|
||||||
|
test_skew(timer).expect("skew test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
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, mmcm_bypass: bool) -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::refclk_reset_write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mmcm_bypass {
|
||||||
|
info!("Bypassing mmcm");
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::mmcm_bypass_write(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 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("mmcm failed to generate 125Mhz ref clock from SMA CLKIN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::wrpll_refclk::refclk_reset_write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,14 +8,14 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
#[cfg(all(feature = "target_kasli_soc", has_virtual_leds))]
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use ksupport;
|
use ksupport;
|
||||||
use libasync::task;
|
use libasync::task;
|
||||||
#[cfg(has_drtio_eem)]
|
#[cfg(has_drtio_eem)]
|
||||||
use libboard_artiq::drtio_eem;
|
use libboard_artiq::drtio_eem;
|
||||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
use libboard_artiq::io_expander;
|
use libboard_artiq::io_expander;
|
||||||
use libboard_artiq::{identifier_read, logger, pl};
|
use libboard_artiq::{identifier_read, logger, pl};
|
||||||
use libboard_zynq::{gic, mpcore, timer::GlobalTimer};
|
use libboard_zynq::{gic, mpcore, timer::GlobalTimer};
|
||||||
|
@ -43,7 +43,7 @@ extern "C" {
|
||||||
static __exceptions_start: u32;
|
static __exceptions_start: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
#[cfg(all(feature = "target_kasli_soc", has_virtual_leds))]
|
||||||
async fn io_expanders_service(
|
async fn io_expanders_service(
|
||||||
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
|
i2c_bus: RefCell<&mut libboard_zynq::i2c::I2c>,
|
||||||
io_expander0: RefCell<io_expander::IoExpander>,
|
io_expander0: RefCell<io_expander::IoExpander>,
|
||||||
|
@ -101,7 +101,7 @@ pub fn main_core0() {
|
||||||
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
||||||
|
|
||||||
ksupport::i2c::init();
|
ksupport::i2c::init();
|
||||||
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
{
|
{
|
||||||
let i2c_bus = unsafe { (ksupport::i2c::I2C_BUS).as_mut().unwrap() };
|
let i2c_bus = unsafe { (ksupport::i2c::I2C_BUS).as_mut().unwrap() };
|
||||||
let mut io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
|
let mut io_expander0 = io_expander::IoExpander::new(i2c_bus, 0).unwrap();
|
||||||
|
@ -112,6 +112,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);
|
||||||
|
@ -119,6 +124,7 @@ pub fn main_core0() {
|
||||||
io_expander1.set(1, 1, false);
|
io_expander1.set(1, 1, false);
|
||||||
io_expander0.service(i2c_bus).unwrap();
|
io_expander0.service(i2c_bus).unwrap();
|
||||||
io_expander1.service(i2c_bus).unwrap();
|
io_expander1.service(i2c_bus).unwrap();
|
||||||
|
#[cfg(has_virtual_leds)]
|
||||||
task::spawn(io_expanders_service(
|
task::spawn(io_expanders_service(
|
||||||
RefCell::new(i2c_bus),
|
RefCell::new(i2c_bus),
|
||||||
RefCell::new(io_expander0),
|
RefCell::new(io_expander0),
|
||||||
|
|
|
@ -4,6 +4,8 @@ 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_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 +262,150 @@ 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(all(has_si549, has_wrpll))]
|
||||||
|
fn wrpll_setup(timer: &mut GlobalTimer, clk: RtioClock, si549_settings: &si549::FrequencySetting) {
|
||||||
|
// register values are directly copied from preconfigured mmcm
|
||||||
|
let (mmcm_setting, mmcm_bypass) = match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 62.5, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 5
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_80to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 15.625, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_100to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 12.5, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
RtioClock::Ext0_Synth0_125to125 => (
|
||||||
|
si549::wrpll_refclk::MmcmSetting {
|
||||||
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 10
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
si549::helper_setup(timer, &si549_settings).expect("cannot initialize helper Si549");
|
||||||
|
si549::wrpll_refclk::setup(timer, mmcm_setting, mmcm_bypass).expect("cannot initialize ref clk for wrpll");
|
||||||
|
si549::wrpll::select_recovered_clock(true, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
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_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,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
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 +420,29 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
let si549_settings = get_si549_setting(clk);
|
||||||
|
|
||||||
|
#[cfg(has_si549)]
|
||||||
|
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);
|
||||||
|
|
||||||
|
#[cfg(all(has_si549, has_wrpll))]
|
||||||
|
{
|
||||||
|
// SYS CLK switch will reset CSRs that are used by WRPLL
|
||||||
|
match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125
|
||||||
|
| RtioClock::Ext0_Synth0_80to125
|
||||||
|
| RtioClock::Ext0_Synth0_100to125
|
||||||
|
| RtioClock::Ext0_Synth0_125to125 => {
|
||||||
|
wrpll_setup(timer, clk, &si549_settings);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ build = "build.rs"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
||||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
||||||
|
calibrate_wrpll_skew = ["libboard_artiq/calibrate_wrpll_skew"]
|
||||||
default = ["target_zc706", ]
|
default = ["target_zc706", ]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -828,6 +830,36 @@ 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,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
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]
|
||||||
|
@ -864,6 +896,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);
|
||||||
|
@ -875,6 +912,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...");
|
||||||
|
@ -892,6 +931,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_si549)]
|
||||||
|
si549::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()];
|
||||||
|
@ -937,6 +978,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.
|
||||||
|
@ -1034,6 +1078,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue