This commit is contained in:
Sebastien Bourdeauducq 2015-07-01 22:23:10 +02:00
commit 5ace0f8e7a
20 changed files with 264 additions and 181 deletions

View File

@ -17,7 +17,7 @@ before_install:
- pip install coveralls
install:
- conda build conda/artiq
- conda install $HOME/miniconda/conda-bld/linux-64/artiq-*.tar.bz2
- conda install artiq --use-local
script:
- coverage run --source=artiq setup.py test
- make -C doc/manual html

View File

@ -1,4 +1,2 @@
from artiq.language.core import *
from artiq.language.experiment import Experiment
from artiq.language.db import *
from artiq.language.units import *
from artiq import language
from artiq.language import *

View File

@ -18,9 +18,11 @@ def get_argparser():
help="USB serial number of the device. "
"The serial number is written on a sticker under "
"the device, you should write for example "
"-d \"SN:03461\". You must prepend enough 0 for it "
"to be 5 digits."
" Omit for simulation mode.")
"-d \"SN:03461\". You must prepend enough 0s for "
"it to be 5 digits. If omitted, the first "
"available device will be used.")
parser.add_argument("--simulation", action="store_true",
help="Put the driver in simulation mode.")
verbosity_args(parser)
return parser
@ -28,7 +30,7 @@ def get_argparser():
def main():
args = get_argparser().parse_args()
init_logger(args)
if args.device is None:
if args.simulation:
lda = Ldasim()
else:
lda = Lda(args.device, args.product)

View File

@ -4,6 +4,7 @@
import argparse
import logging
import sys
from artiq.devices.novatech409b.driver import Novatech409B
from artiq.protocols.pc_rpc import simple_server_loop
@ -19,7 +20,10 @@ def get_argparser():
simple_network_args(parser, 3254)
parser.add_argument(
"-d", "--device", default=None,
help="serial port. Omit for simulation mode.")
help="serial port.")
parser.add_argument(
"--simulation", action="store_true",
help="Put the driver in simulation mode, even if --device is used.")
verbosity_args(parser)
return parser
@ -28,7 +32,12 @@ def main():
args = get_argparser().parse_args()
init_logger(args)
dev = Novatech409B(args.device)
if not args.simulation and args.device is None:
print("You need to specify either --simulation or -d/--device "
"argument. Use --help for more information.")
sys.exit(1)
dev = Novatech409B(args.device if not args.simulation else None)
try:
simple_server_loop(
{"novatech409b": dev}, args.bind, args.port)

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import sys
from artiq.devices.pdq2.driver import Pdq2
from artiq.protocols.pc_rpc import simple_server_loop
@ -12,7 +13,10 @@ def get_argparser():
simple_network_args(parser, 3252)
parser.add_argument(
"-d", "--device", default=None,
help="serial port. Omit for simulation mode.")
help="serial port.")
parser.add_argument(
"--simulation", action="store_true",
help="Put the driver in simulation mode, even if --device is used.")
parser.add_argument(
"--dump", default="pdq2_dump.bin",
help="file to dump pdq2 data into, for later simulation")
@ -24,7 +28,13 @@ def main():
args = get_argparser().parse_args()
init_logger(args)
port = None
if args.device is None:
if not args.simulation and args.device is None:
print("You need to specify either --simulation or -d/--device "
"argument. Use --help for more information.")
sys.exit(1)
if args.simulation:
port = open(args.dump, "wb")
dev = Pdq2(url=args.device, dev=port)
try:

View File

@ -2,6 +2,7 @@
# Yann Sionneau <ys@m-labs.hk>, 2015
import argparse
import sys
from artiq.protocols.pc_rpc import simple_server_loop
from artiq.devices.pxi6733.driver import DAQmx, DAQmxSim
@ -12,10 +13,12 @@ def get_argparser():
parser = argparse.ArgumentParser(description="NI PXI 6733 controller")
simple_network_args(parser, 3256)
parser.add_argument("-C", "--channels", default=None,
help="List of channels (e.g. Dev1/ao0, Dev1/ao1:3)."
" Omit for simulation mode.")
help="List of channels (e.g. Dev1/ao0, Dev1/ao1:3).")
parser.add_argument("-c", "--clock", default="PFI5",
help="Input clock pin name (default: PFI5)")
parser.add_argument("--simulation", action='store_true',
help="Put the driver in simulation mode, even if "
"--channels is used.")
verbosity_args(parser)
return parser
@ -24,7 +27,12 @@ def main():
args = get_argparser().parse_args()
init_logger(args)
if args.channels is None:
if not args.simulation and args.channels is None:
print("You need to specify either --simulation or -C/--channels "
"argument. Use --help for more information.")
sys.exit(1)
if args.simulation:
daq = DAQmxSim()
else:
daq = DAQmx(args.channels,

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import sys
from artiq.devices.thorlabs_tcube.driver import Tdc, Tpz, TdcSim, TpzSim
from artiq.protocols.pc_rpc import simple_server_loop
@ -14,8 +15,10 @@ def get_argparser():
choices=["TDC001", "TPZ001"])
parser.add_argument("-d", "--device", default=None,
help="serial device. See documentation for how to "
"specify a USB Serial Number. Omit for simulation "
"mode.")
"specify a USB Serial Number.")
parser.add_argument("--simulation", action="store_true",
help="Put the driver in simulation mode, even if "
"--device is used.")
simple_network_args(parser, 3255)
verbosity_args(parser)
return parser
@ -25,7 +28,12 @@ def main():
args = get_argparser().parse_args()
init_logger(args)
if args.device is None:
if not args.simulation and args.device is None:
print("You need to specify either --simulation or -d/--device "
"argument. Use --help for more information.")
sys.exit(1)
if args.simulation:
if args.product == "TDC001":
dev = TdcSim()
elif args.product == "TPZ001":

View File

@ -4,10 +4,22 @@ from mibuild.generic_platform import *
papilio_adapter_io = [
("ext_led", 0, Pins("B:7"), IOStandard("LVTTL")),
# to feed the 125 MHz clock (preferrably from DDS SYNC_CLK)
# to the FPGA, use the xtrig pair.
#
# on papiliopro-adapter, xtrig (C:12) is connected to a GCLK
#
# on pipistrello, C:15 is the only GCLK in proximity, used as a button
# input, BTN2/PMT2 in papiliopro-adapter
# either improve the DDS box to feed 125MHz into the PMT2 pair, or:
#
# * disconnect C:15 from its periphery on the adapter board
# * bridge C:15 to the xtrig output of the transciever
# * optionally, disconnect C:12 from its periphery
("xtrig", 0, Pins("C:12"), IOStandard("LVTTL")),
("pmt", 0, Pins("C:13"), IOStandard("LVTTL")),
("pmt", 1, Pins("C:14"), IOStandard("LVTTL")),
("xtrig", 0, Pins("C:12"), IOStandard("LVTTL")),
("dds_clock", 0, Pins("C:15"), IOStandard("LVTTL")), # PMT2
("pmt", 2, Pins("C:15"), IOStandard("LVTTL")), # rarely equipped
("ttl", 0, Pins("C:11"), IOStandard("LVTTL")),
("ttl", 1, Pins("C:10"), IOStandard("LVTTL")),

View File

@ -0,0 +1,4 @@
from artiq.language.core import *
from artiq.language.experiment import Experiment
from artiq.language.db import *
from artiq.language.units import *

120
artiq/test/coredevice.py Normal file
View File

@ -0,0 +1,120 @@
from math import sqrt
from artiq.language import *
from artiq.test.hardware_testbench import ExperimentCase
from artiq.coredevice.runtime_exceptions import RTIOUnderflow
class RTT(Experiment, AutoDB):
class DBKeys:
core = Device()
ttl_inout = Device()
rtt = Result()
@kernel
def run(self):
self.ttl_inout.output()
delay(1*us)
with parallel:
self.ttl_inout.gate_rising(2*us)
with sequential:
delay(1*us)
t0 = now()
self.ttl_inout.pulse(1*us)
self.rtt = self.ttl_inout.timestamp() - t0
class Loopback(Experiment, AutoDB):
class DBKeys:
core = Device()
loop_in = Device()
loop_out = Device()
rtt = Result()
@kernel
def run(self):
with parallel:
self.loop_in.gate_rising(2*us)
with sequential:
delay(1*us)
t0 = now()
self.loop_out.pulse(1*us)
self.rtt = self.loop_in.timestamp() - t0
class PulseRate(Experiment, AutoDB):
class DBKeys:
core = Device()
loop_out = Device()
pulse_rate = Result()
@kernel
def run(self):
dt = time_to_cycles(1000*ns)
while True:
try:
for i in range(1000):
self.loop_out.pulse(cycles_to_time(dt))
delay(cycles_to_time(dt))
except RTIOUnderflow:
dt += 1
self.core.break_realtime()
else:
self.pulse_rate = cycles_to_time(2*dt)
break
class CoredeviceTest(ExperimentCase):
def test_rtt(self):
rtt = self.execute(RTT)["rtt"]
print(rtt)
self.assertGreater(rtt, 0*ns)
self.assertLess(rtt, 100*ns)
def test_loopback(self):
rtt = self.execute(Loopback)["rtt"]
print(rtt)
self.assertGreater(rtt, 0*ns)
self.assertLess(rtt, 40*ns)
def test_pulse_rate(self):
rate = self.execute(PulseRate)["pulse_rate"]
print(rate)
self.assertGreater(rate, 100*ns)
self.assertLess(rate, 2500*ns)
class RPCTiming(Experiment, AutoDB):
class DBKeys:
core = Device()
repeats = Argument(100)
rpc_time_mean = Result()
rpc_time_stddev = Result()
def nop(self, x):
pass
@kernel
def bench(self):
self.ts = [0. for _ in range(self.repeats)]
for i in range(self.repeats):
t1 = self.core.get_rtio_time()
self.nop(1)
t2 = self.core.get_rtio_time()
self.ts[i] = t2 - t1
def run(self):
self.bench()
mean = sum(self.ts)/self.repeats
self.rpc_time_stddev = sqrt(
sum([(t - mean)**2 for t in self.ts])/self.repeats)*s
self.rpc_time_mean = mean*s
class RPCTest(ExperimentCase):
def test_rpc_timing(self):
res = self.execute(RPCTiming)
print(res)
self.assertGreater(res["rpc_time_mean"], 100*ns)
self.assertLess(res["rpc_time_mean"], 10*ms)
self.assertLess(res["rpc_time_stddev"], 1*ms)

View File

@ -0,0 +1,43 @@
import os
import sys
import unittest
import logging
from artiq.language import *
from artiq.protocols.file_db import FlatFileDB
from artiq.master.worker_db import DBHub, ResultDB
from artiq.frontend.artiq_run import (
DummyScheduler, DummyWatchdog, SimpleParamLogger)
artiq_root = os.getenv("ARTIQ_ROOT")
logger = logging.getLogger(__name__)
@unittest.skipUnless(artiq_root, "no ARTIQ_ROOT")
class ExperimentCase(unittest.TestCase):
def setUp(self):
self.ddb = FlatFileDB(os.path.join(artiq_root, "ddb.pyon"))
self.pdb = FlatFileDB(os.path.join(artiq_root, "pdb.pyon"))
self.rdb = ResultDB(lambda description: None, lambda mod: None)
self.dbh = DBHub(self.ddb, self.pdb, self.rdb)
def execute(self, cls, **kwargs):
expid = {
"file": sys.modules[cls.__module__].__file__,
"experiment": cls.__name__,
"arguments": kwargs
}
sched = DummyScheduler(expid)
try:
try:
exp = cls(self.dbh, scheduler=sched, **kwargs)
except KeyError as e:
# skip if ddb does not match requirements
raise unittest.SkipTest(*e.args)
self.rdb.build()
exp.run()
exp.analyze()
return self.rdb.data.read
finally:
self.dbh.close_devices()

View File

@ -1,18 +0,0 @@
from artiq import *
import pulse_rate, rtio_skew, rpc_timing
_exps = [pulse_rate.PulseRate, rtio_skew.RTIOSkew, rpc_timing.RPCTiming]
class AllBenchmarks(Experiment, AutoDB):
"""All benchmarks"""
def build(self):
self.se = []
for exp in _exps:
self.se.append(exp(self.dbh))
def run(self):
for se in self.se:
se.run()

View File

@ -1,28 +0,0 @@
{
"comm": {
"type": "local",
"module": "artiq.coredevice.comm_tcp",
"class": "Comm",
"arguments": {"host": "192.168.0.42"}
},
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {}
},
"pmt0": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 0}
},
"ttl0": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 2}
},
}

View File

@ -1 +0,0 @@
{}

View File

@ -1,26 +0,0 @@
from artiq import *
from artiq.coredevice.runtime_exceptions import RTIOUnderflow
class PulseRate(Experiment, AutoDB):
"""Sustained pulse rate"""
class DBKeys:
core = Device()
ttl0 = Device()
pulse_rate = Result()
@kernel
def run(self):
T = time_to_cycles(100*ns)
while True:
try:
for i in range(1000):
self.ttl0.pulse(cycles_to_time(T))
delay(cycles_to_time(T))
except RTIOUnderflow:
T += 1
self.core.break_realtime()
else:
self.pulse_rate = cycles_to_time(2*T)
break

View File

@ -1,32 +0,0 @@
from math import sqrt
from artiq import *
class RPCTiming(Experiment, AutoDB):
"""RPC timing"""
class DBKeys:
core = Device()
repeats = Argument(100)
rpc_time_mean = Result()
rpc_time_stddev = Result()
def nop(self, x):
pass
@kernel
def bench(self):
self.ts = [0.0 for _ in range(self.repeats)]
for i in range(self.repeats):
t1 = self.core.get_rtio_time()
self.nop(10)
t2 = self.core.get_rtio_time()
self.ts[i] = t2 - t1
def run(self):
self.bench()
mean = sum(self.ts)/self.repeats
self.rpc_time_stddev = sqrt(
sum([(t - mean)**2 for t in self.ts])/self.repeats)*s
self.rpc_time_mean = mean*s

View File

@ -1,29 +0,0 @@
from artiq import *
class PulseNotReceived(Exception):
pass
class RTIOSkew(Experiment, AutoDB):
"""RTIO skew"""
class DBKeys:
core = Device()
pmt0 = Device()
rtio_skew = Result()
@kernel
def run(self):
self.pmt0.output()
delay(1*us)
with parallel:
self.pmt0.gate_rising(10*us)
with sequential:
delay(5*us)
out_t = now()
self.pmt0.pulse(5*us)
in_t = self.pmt0.timestamp()
if in_t < 0*s:
raise PulseNotReceived
self.rtio_skew = out_t - in_t

View File

@ -177,7 +177,7 @@ General guidelines
* Use new-style formatting (``str.format``) except for logging where it is not well supported, and double quotes for strings.
* The device identification (e.g. serial number, or entry in ``/dev``) to attach to must be passed as a command-line parameter to the controller. We suggest using ``-d`` and ``--device`` as parameter name.
* Controllers must be able to operate in "simulation" mode, where they behave properly even if the associated hardware is not connected. For example, they can print the data to the console instead of sending it to the device, or dump it into a file.
* We suggest that the simulation mode is entered whenever the ``-d/--device`` option is omitted.
* The simulation mode is entered whenever the ``--simulation`` option is specified.
* Keep command line parameters consistent across clients/controllers. When adding new command line options, look for a client/controller that does a similar thing and follow its use of ``argparse``. If the original client/controller could use ``argparse`` in a better way, improve it.
* Use docstrings for all public methods of the driver (note that those will be retrieved by ``artiq_rpctool``).
* Choose a free default TCP port and add it to the default port list in this manual.

View File

@ -13,20 +13,24 @@ The low-cost Pipistrello FPGA board can be used as a lower-cost but slower alter
When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are mapped to RTIO channels as follows:
+--------------+----------+-----------------+
| RTIO channel | TTL line | Capability |
+==============+==========+=================+
| 0 | PMT0 | Input only |
+--------------+----------+-----------------+
| 1 | PMT1 | Input only |
+--------------+----------+-----------------+
| 2-18 | TTL0-16 | Output only |
+--------------+----------+-----------------+
| 19-21 | LEDs | Output only |
+--------------+----------+-----------------+
| 22 | TTL2 | Output only |
+--------------+----------+-----------------+
+--------------+----------+------------+
| RTIO channel | TTL line | Capability |
+==============+==========+============+
| 0 | PMT0 | Input |
+--------------+----------+------------+
| 1 | PMT1 | Input |
+--------------+----------+------------+
| 2-17 | TTL0-15 | Output |
+--------------+----------+------------+
| 18 | EXT_LED | Output |
+--------------+----------+------------+
| 19 | USER_LED | Output |
+--------------+----------+------------+
| 20 | DDS | Output |
+--------------+----------+------------+
The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Pipistrello board), the corresponding pins on the Pipistrello can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention.
The board can accept an external RTIO clock connected to PMT2.
The board can accept an external RTIO clock connected to PMT2. If the DDS box
does not drive the PMT2 pair, use XTRIG and patch the XTRIG transciever output
on the adapter board onto C:15 disconnecting PMT2.

View File

@ -24,7 +24,7 @@ class _RTIOCRG(Module, AutoCSR):
self.specials += Instance("DCM_CLKGEN",
p_CLKFXDV_DIVIDE=2,
p_CLKFX_DIVIDE=f.denominator,
p_CLKFX_MD_MAX=1.6,
p_CLKFX_MD_MAX=float(f),
p_CLKFX_MULTIPLY=f.numerator,
p_CLKIN_PERIOD=1e9/clk_freq,
p_SPREAD_SPECTRUM="NONE",
@ -34,7 +34,7 @@ class _RTIOCRG(Module, AutoCSR):
i_FREEZEDCM=0,
i_RST=ResetSignal())
rtio_external_clk = platform.request("dds_clock")
rtio_external_clk = platform.request("pmt", 2)
platform.add_period_constraint(rtio_external_clk, 8.0)
self.specials += Instance("BUFGMUX",
i_I0=rtio_internal_clk,
@ -83,6 +83,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
self.submodules.leds = gpio.GPIOOut(Cat(
platform.request("user_led", 0),
platform.request("user_led", 1),
platform.request("user_led", 2),
platform.request("user_led", 3),
))
self.comb += [
@ -95,11 +97,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
for i in range(2):
phy = ttl_simple.Inout(platform.request("pmt", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
phy = ttl_simple.Inout(platform.request("xtrig", 0))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512,
ofifo_depth=4))
for i in range(16):
phy = ttl_simple.Output(platform.request("ttl", i))
@ -110,10 +109,10 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4))
for i in range(2, 5):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4))
phy = ttl_simple.Output(platform.request("user_led", 4))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=4))
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))