artiq/artiq/gateware/targets/kc705.py

603 lines
23 KiB
Python
Executable File

#!/usr/bin/env python3.5
import argparse
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen.build.generic_platform import *
from migen.build.xilinx.vivado import XilinxVivadoToolchain
from migen.build.xilinx.ise import XilinxISEToolchain
from migen.fhdl.specials import Keep
from migen.genlib.io import DifferentialInput
from jesd204b.common import (JESD204BTransportSettings,
JESD204BPhysicalSettings,
JESD204BSettings)
from jesd204b.phy.gtx import GTXQuadPLL
from jesd204b.phy import JESD204BPhyTX
from jesd204b.core import JESD204BCoreTX
from jesd204b.core import JESD204BCoreTXControl
from misoc.interconnect.csr import *
from misoc.interconnect import wishbone
from misoc.cores import gpio
from misoc.cores import spi as spi_csr
from misoc.integration.soc_core import mem_decoder
from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict
from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.soc import AMPSoC, build_artiq_soc
from artiq.gateware import rtio, nist_qc1, nist_clock, nist_qc2, phaser
from artiq.gateware.rtio.phy import (ttl_simple, ttl_serdes_7series,
dds, spi, sawg)
from artiq import __version__ as artiq_version
class _RTIOCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self._clock_sel = CSRStorage()
self._pll_reset = CSRStorage(reset=1)
self._pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
# 10 MHz when using 125MHz input
self.clock_domains.cd_ext_clkout = ClockDomain(reset_less=True)
ext_clkout = platform.request("user_sma_gpio_p_33")
self.sync.ext_clkout += ext_clkout.eq(~ext_clkout)
rtio_external_clk = Signal()
user_sma_clock = platform.request("user_sma_clock")
platform.add_period_constraint(user_sma_clock.p, 8.0)
self.specials += Instance("IBUFDS",
i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
o_O=rtio_external_clk)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
ext_clkout_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01,
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self._clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self._pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
p_CLKOUT1_DIVIDE=50, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=ext_clkout_clk),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
Instance("BUFG", i_I=ext_clkout_clk, o_O=self.cd_ext_clkout.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked, self._pll_locked.status)
]
# The default user SMA voltage on KC705 is 2.5V, and the Migen platform
# follows this default. But since the SMAs are on the same bank as the DDS,
# which is set to 3.3V by reprogramming the KC705 power ICs, we need to
# redefine them here.
_sma33_io = [
("user_sma_gpio_p_33", 0, Pins("Y23"), IOStandard("LVCMOS33")),
("user_sma_gpio_n_33", 0, Pins("Y24"), IOStandard("LVCMOS33")),
]
_ams101_dac = [
("ams101_dac", 0,
Subsignal("ldac", Pins("XADC:GPIO0")),
Subsignal("clk", Pins("XADC:GPIO1")),
Subsignal("mosi", Pins("XADC:GPIO2")),
Subsignal("cs_n", Pins("XADC:GPIO3")),
IOStandard("LVTTL")
)
]
class _NIST_Ions(MiniSoC, AMPSoC):
mem_map = {
"timer_kernel": 0x10000000, # (shadow @0x90000000)
"rtio": 0x20000000, # (shadow @0xa0000000)
"i2c": 0x30000000, # (shadow @0xb0000000)
"mailbox": 0x70000000 # (shadow @0xf0000000)
}
mem_map.update(MiniSoC.mem_map)
def __init__(self, cpu_type="or1k", **kwargs):
MiniSoC.__init__(self,
cpu_type=cpu_type,
sdram_controller_type="minicon",
l2_size=128*1024,
with_timer=False,
ident=artiq_version,
**kwargs)
AMPSoC.__init__(self)
if isinstance(self.platform.toolchain, XilinxVivadoToolchain):
self.platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
if isinstance(self.platform.toolchain, XilinxISEToolchain):
self.platform.toolchain.bitgen_opt += " -g compress"
self.submodules.leds = gpio.GPIOOut(Cat(
self.platform.request("user_led", 0),
self.platform.request("user_led", 1)))
self.csr_devices.append("leds")
self.platform.add_extension(_sma33_io)
self.platform.add_extension(_ams101_dac)
i2c = self.platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.register_kernel_cpu_csrdevice("i2c")
self.config["I2C_BUS_COUNT"] = 1
def add_rtio(self, rtio_channels, rtio_crg=None):
if rtio_crg is None:
rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
self.submodules.rtio_crg = rtio_crg
self.csr_devices.append("rtio_crg")
self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.specials += [
Keep(self.rtio.cd_rsys.clk),
Keep(self.rtio_crg.cd_rtio.clk),
Keep(self.ethphy.crg.cd_eth_rx.clk),
Keep(self.ethphy.crg.cd_eth_tx.clk),
]
self.platform.add_period_constraint(self.rtio.cd_rsys.clk, 8.)
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 8.)
self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.)
self.platform.add_false_path_constraints(
self.rtio.cd_rsys.clk,
self.rtio_crg.cd_rtio.clk,
self.ethphy.crg.cd_eth_rx.clk,
self.ethphy.crg.cd_eth_tx.clk)
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio,
self.get_native_sdram_if())
self.csr_devices.append("rtio_analyzer")
class NIST_QC1(_NIST_Ions):
"""
NIST QC1 hardware, as used in the Penning lab, with FMC to SCSI cables
adapter.
"""
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
platform = self.platform
platform.add_extension(nist_qc1.fmc_adapter_io)
self.comb += [
platform.request("ttl_l_tx_en").eq(1),
platform.request("ttl_h_tx_en").eq(1)
]
rtio_channels = []
for i in range(2):
phy = ttl_serdes_7series.Inout_8X(platform.request("pmt", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
for i in range(15):
phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_serdes_7series.Inout_8X(platform.request("user_sma_gpio_n_33"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
phy = ttl_simple.ClockGen(platform.request("ttl", 15))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 8
self.config["DDS_AD9858"] = True
phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
assert self.rtio.fine_ts_width <= 3
self.config["DDS_RTIO_CLK_RATIO"] = 8 >> self.rtio.fine_ts_width
class NIST_CLOCK(_NIST_Ions):
"""
NIST clock hardware, with old backplane and 11 DDS channels
"""
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
platform = self.platform
platform.add_extension(nist_clock.fmc_adapter_io)
rtio_channels = []
for i in range(16):
if i % 4 == 3:
phy = ttl_serdes_7series.Inout_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
else:
phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
for i in range(2):
phy = ttl_serdes_7series.Inout_8X(platform.request("pmt", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_serdes_7series.Inout_8X(platform.request("user_sma_gpio_n_33"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
phy = ttl_simple.ClockGen(platform.request("la32_p"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = spi.SPIMaster(ams101_dac)
self.submodules += phy
self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=4, ififo_depth=4))
for i in range(3):
phy = spi.SPIMaster(self.platform.request("spi", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=128, ififo_depth=128))
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 11
self.config["DDS_AD9914"] = True
self.config["DDS_ONEHOT_SEL"] = True
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
assert self.rtio.fine_ts_width <= 3
self.config["DDS_RTIO_CLK_RATIO"] = 24 >> self.rtio.fine_ts_width
class NIST_QC2(_NIST_Ions):
"""
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
and 24 DDS channels. Two backplanes are used.
"""
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
platform = self.platform
platform.add_extension(nist_qc2.fmc_adapter_io)
rtio_channels = []
clock_generators = []
# All TTL channels are In+Out capable
for i in range(40):
phy = ttl_serdes_7series.Inout_8X(
platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
# CLK0, CLK1 are for clock generators, on backplane SMP connectors
for i in range(2):
phy = ttl_simple.ClockGen(
platform.request("clkout", i))
self.submodules += phy
clock_generators.append(rtio.Channel.from_phy(phy))
# user SMA on KC705 board
phy = ttl_serdes_7series.Inout_8X(platform.request("user_sma_gpio_n_33"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
# AMS101 DAC on KC705 XADC header - optional
ams101_dac = self.platform.request("ams101_dac", 0)
phy = ttl_simple.Output(ams101_dac.ldac)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
# add clock generators after RTIO_REGULAR_TTL_COUNT
rtio_channels += clock_generators
phy = spi.SPIMaster(ams101_dac)
self.submodules += phy
self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=4, ififo_depth=4))
for i in range(4):
phy = spi.SPIMaster(self.platform.request("spi", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=128, ififo_depth=128))
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 2
self.config["DDS_CHANNELS_PER_BUS"] = 12
self.config["DDS_AD9914"] = True
self.config["DDS_ONEHOT_SEL"] = True
for backplane_offset in range(2):
phy = dds.AD9914(
platform.request("dds", backplane_offset), 12, onehot=True)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels)
assert self.rtio.fine_ts_width <= 3
self.config["DDS_RTIO_CLK_RATIO"] = 24 >> self.rtio.fine_ts_width
class _PhaserCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk):
self._clock_sel = CSRStorage()
self._pll_reset = CSRStorage(reset=1)
self._pll_locked = CSRStatus()
self.refclk = Signal()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01, p_REF_JITTER2=0.01,
p_CLKIN1_PERIOD=4.0, p_CLKIN2_PERIOD=4.0,
i_CLKIN1=0, i_CLKIN2=self.refclk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self._clock_sel.storage,
# VCO @ 1GHz when using 125MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=2,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self._pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked | ~self._clock_sel.storage,
self._pll_locked.status)
]
class AD9154JESD(Module, AutoCSR):
def __init__(self, platform):
ps = JESD204BPhysicalSettings(l=4, m=4, n=16, np=16)
ts = JESD204BTransportSettings(f=2, s=1, k=16, cs=1)
settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
linerate = 10e9
refclk_freq = 250e6
fabric_freq = 250*1000*1000
sync_pads = platform.request("ad9154_sync")
self.jsync = Signal()
self.refclk = Signal()
self.specials += DifferentialInput(
sync_pads.p, sync_pads.n, self.jsync)
self.clock_domains.cd_jesd = ClockDomain()
refclk_pads = platform.request("ad9154_refclk")
platform.add_period_constraint(refclk_pads.p, 1e9/refclk_freq)
self.specials += [
Instance("IBUFDS_GTE2", i_CEB=0,
i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=self.refclk),
Instance("BUFG", i_I=self.refclk, o_O=self.cd_jesd.clk),
AsyncResetSynchronizer(self.cd_jesd, ResetSignal("rio_phy")),
]
platform.add_period_constraint(self.cd_jesd.clk, 1e9/refclk_freq)
qpll = GTXQuadPLL(self.refclk, refclk_freq, linerate)
self.submodules += qpll
phys = []
for i in range(4):
phy = JESD204BPhyTX(
qpll, platform.request("ad9154_jesd", i), fabric_freq)
platform.add_period_constraint(phy.gtx.cd_tx.clk, 40*1e9/linerate)
platform.add_false_path_constraints(self.cd_jesd.clk,
phy.gtx.cd_tx.clk)
phys.append(phy)
to_jesd = ClockDomainsRenamer("jesd")
self.submodules.core = to_jesd(JESD204BCoreTX(phys, settings,
converter_data_width=32))
self.submodules.control = to_jesd(JESD204BCoreTXControl(self.core))
self.comb += [
platform.request("ad9154_txen", 0).eq(1),
platform.request("ad9154_txen", 1).eq(1),
self.core.start.eq(self.jsync),
platform.request("user_led", 3).eq(self.jsync),
]
# blinking leds for transceiver reset status
for i in range(4):
led = platform.request("user_led", 4 + i)
counter = Signal(max=fabric_freq//2 + 1)
sync = getattr(self.sync, "phy{}_tx".format(i))
sync += \
If(counter == 0,
led.eq(~led),
counter.eq(fabric_freq//2)
).Else(
counter.eq(counter - 1)
)
class AD9154(Module, AutoCSR):
def __init__(self, platform):
ad9154_spi = platform.request("ad9154_spi")
self.comb += ad9154_spi.en.eq(1)
self.submodules.spi = spi_csr.SPIMaster(ad9154_spi)
self.submodules.jesd = AD9154JESD(platform)
self.sawgs = [sawg.Channel(width=16, parallelism=4) for i in range(4)]
self.submodules += self.sawgs
x = Signal()
y = Signal()
z = Signal()
self.sync.jesd += x.eq(~x), z.eq(x == y)
self.sync.rio_phy += y.eq(x)
for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs):
self.sync.jesd += conv.eq(Mux(z, Cat(ch.o[:2]), Cat(ch.o[2:])))
class Phaser(_NIST_Ions):
mem_map = {
"ad9154": 0x50000000,
}
mem_map.update(_NIST_Ions.mem_map)
def __init__(self, cpu_type="or1k", **kwargs):
_NIST_Ions.__init__(self, cpu_type, **kwargs)
platform = self.platform
platform.add_extension(phaser.fmc_adapter_io)
self.submodules.ad9154 = AD9154(platform)
self.register_kernel_cpu_csrdevice("ad9154")
self.config["AD9154_DAC_CS"] = 1 << 0
self.config["AD9154_CLK_CS"] = 1 << 1
rtio_channels = []
phy = ttl_serdes_7series.Inout_8X(
platform.request("user_sma_gpio_n"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
sysref_pads = platform.request("ad9154_sysref")
phy = ttl_serdes_7series.Input_8X(sysref_pads.p, sysref_pads.n)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=32,
ofifo_depth=2))
phy = ttl_simple.Input(self.ad9154.jesd.jsync)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=32,
ofifo_depth=2))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels)
rtio_channels.extend(rtio.Channel.from_phy(phy)
for sawg in self.ad9154.sawgs
for phy in sawg.phys)
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.add_rtio(rtio_channels, _PhaserCRG(platform, self.crg.cd_sys.clk))
self.comb += self.rtio_crg.refclk.eq(self.ad9154.jesd.cd_jesd.clk)
def main():
parser = argparse.ArgumentParser(
description="ARTIQ core device builder / KC705 "
"+ NIST Ions QC1/CLOCK/QC2 hardware adapters")
builder_args(parser)
soc_kc705_args(parser)
parser.add_argument("-H", "--hw-adapter", default="nist_clock",
help="hardware adapter type: "
"nist_qc1/nist_clock/nist_qc2/phaser "
"(default: %(default)s)")
args = parser.parse_args()
hw_adapter = args.hw_adapter.lower()
if hw_adapter == "nist_qc1":
cls = NIST_QC1
elif hw_adapter == "nist_clock":
cls = NIST_CLOCK
elif hw_adapter == "nist_qc2":
cls = NIST_QC2
elif hw_adapter == "phaser":
cls = Phaser
else:
raise SystemExit("Invalid hardware adapter string (-H/--hw-adapter)")
soc = cls(**soc_kc705_argdict(args))
build_artiq_soc(soc, builder_argdict(args))
if __name__ == "__main__":
main()