From f49f1fcbfcfa69560775fd8c544b0829279b9f55 Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Fri, 9 Apr 2021 13:31:15 +0800 Subject: [PATCH] sayma_amc: add option to generate a 9MHz sq wave on the MCXs --- artiq/gateware/rtio/phy/ttl_serdes_nortio.py | 77 +++++++++++++++++++ .../rtio/phy/ttl_serdes_ultrascale.py | 22 +++++- artiq/gateware/targets/sayma_amc.py | 13 +++- 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 artiq/gateware/rtio/phy/ttl_serdes_nortio.py diff --git a/artiq/gateware/rtio/phy/ttl_serdes_nortio.py b/artiq/gateware/rtio/phy/ttl_serdes_nortio.py new file mode 100644 index 000000000..0b66ce32a --- /dev/null +++ b/artiq/gateware/rtio/phy/ttl_serdes_nortio.py @@ -0,0 +1,77 @@ +from migen import * +from migen.genlib.coding import PriorityEncoder + +from artiq.gateware.rtio import rtlink + + +def _mk_edges(w, direction): + l = [(1 << i) - 1 for i in range(w)] + if direction == "rising": + l = [((1 << w) - 1) ^ x for x in l] + elif direction == "falling": + pass + else: + raise ValueError + return l + + +class _SerdesSquareWaveDriver(Module): + def __init__(self, serdes_o, rtio_freq, wave_freq): + assert wave_freq <= rtio_freq + serdes_width = len(serdes_o) + assert serdes_width & (serdes_width-1) == 0 # serdes_width must be 2**n + + edges = Array(_mk_edges(serdes_width, "rising")) + edges_n = Array(_mk_edges(serdes_width, "falling")) + + phase_accumulator = Signal(32) + tuning_word = int((wave_freq/rtio_freq) * 2**32) + + fine_ts = Signal() # indicates which rtiox period within the + # current rtio period should the edge be changed + logic_level = Signal(reset=1) + logic_level_d = Signal(reset=1) + self.comb += [ + fine_ts.eq(phase_accumulator[-log2_int(serdes_width):]), + logic_level.eq(~phase_accumulator[-1]), + ] + # Using CD rio such that RtioInitRequest + # resets the phase accumulator and logic level registers + # (Refer to :class:`artiq.gateware.rtio.core.Core`) + self.sync.rio += [ + logic_level_d.eq(logic_level), + If(~logic_level_d & logic_level, + serdes_o.eq(edges[fine_ts]), + ).Elif(logic_level_d & ~logic_level, + serdes_o.eq(edges_n[fine_ts]), + ).Else( + serdes_o.eq(Replicate(logic_level_d, serdes_width)), + ), + phase_accumulator.eq(phase_accumulator + tuning_word), + ] + + +SEDRES_DRIVER_TYPES = { + "square_wave": _SerdesSquareWaveDriver, +} + + +class Output(Module): + def __init__(self, serdes, driver_type, **kwargs): + assert driver_type in SEDRES_DRIVER_TYPES.keys() + + # Include an unused, dummy rtlink interface just to consume an RTIO channel + self.rtlink = rtlink.Interface( + rtlink.OInterface(1, fine_ts_width=log2_int(len(serdes.o)))) + self.probes = [Signal()] + self.overrides = [Signal(), Signal()] + + # # # + + self.submodules += SEDRES_DRIVER_TYPES[driver_type]( + serdes.o, **kwargs) + + +class InOut(Module): + def __init__(self, serdes): + raise NotImplementedError() diff --git a/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py b/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py index 0dbe613ac..7b2f8fdcc 100644 --- a/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py +++ b/artiq/gateware/rtio/phy/ttl_serdes_ultrascale.py @@ -1,6 +1,6 @@ from migen import * -from artiq.gateware.rtio.phy import ttl_serdes_generic +from artiq.gateware.rtio.phy import ttl_serdes_generic, ttl_serdes_nortio class _OSERDESE3(Module): @@ -99,3 +99,23 @@ class InOut(ttl_serdes_generic.InOut): i_INTERMDISABLE=~serdes.t_out, i_I=serdes.ser_out, o_O=serdes.ser_in, i_T=serdes.t_out, io_IO=pad, io_IOB=pad_n) + + +class CustomOutput(ttl_serdes_nortio.Output): + def __init__(self, dw, pad, pad_n=None, dci=False, **kwargs): + serdes = _OSERDESE3(dw) + self.submodules += serdes + ttl_serdes_nortio.Output.__init__(self, serdes, **kwargs) + + if pad_n is None: + self.comb += pad.eq(serdes.ser_out) + else: + self.specials += Instance("IOBUFDS", + i_I=serdes.ser_out, + i_T=serdes.t_out, + io_IO=pad, io_IOB=pad_n) + + +class CustomInOut(ttl_serdes_nortio.InOut): + def __init__(self, dw, pad, pad_n=None, dci=False, **kwargs): + raise NotImplementedError() diff --git a/artiq/gateware/targets/sayma_amc.py b/artiq/gateware/targets/sayma_amc.py index 8856a1a44..96713d111 100755 --- a/artiq/gateware/targets/sayma_amc.py +++ b/artiq/gateware/targets/sayma_amc.py @@ -294,7 +294,7 @@ class Satellite(SatelliteBase): """ DRTIO satellite with local DAC/SAWG channels, as well as TTL channels via FMC and VHDCI carrier. """ - def __init__(self, jdcg_type, ttlout=False, **kwargs): + def __init__(self, jdcg_type, ttlout=False, mcx_sqwave=False, **kwargs): SatelliteBase.__init__(self, identifier_suffix="." + jdcg_type, **kwargs) platform = self.platform @@ -320,7 +320,13 @@ class Satellite(SatelliteBase): rtio_channels.append(rtio.Channel.from_phy(phy)) for i in range(2): mcx_io = platform.request("mcx_io", i) - if ttlout: + if mcx_sqwave: + phy = ttl_serdes_ultrascale.CustomOutput(4, mcx_io.level, + driver_type="square_wave", + rtio_freq=self.rtio_clk_freq, + wave_freq=9e6) + self.comb += mcx_io.direction.eq(1) + elif ttlout: phy = ttl_serdes_ultrascale.Output(4, mcx_io.level) self.comb += mcx_io.direction.eq(1) else: @@ -433,6 +439,8 @@ def main(): "development and debugging.") parser.add_argument("--ttlout", default=False, action="store_true", help="force only outputs on the MCX TTL IOs") + parser.add_argument("--mcx-sqwave", default=False, action="store_true", + help="generate a square wave on the MCX TTL IOs") parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") args = parser.parse_args() @@ -443,6 +451,7 @@ def main(): with_sfp=args.sfp, jdcg_type=args.jdcg_type, ttlout=args.ttlout, + mcx_sqwave=args.mcx_sqwave, gateware_identifier_str=args.gateware_identifier_str, **soc_sayma_amc_argdict(args)) elif variant == "simplesatellite":