Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2022-02-14 16:38:09 +08:00
commit 7b56a72da0
19 changed files with 515 additions and 155 deletions

View File

@ -5,3 +5,4 @@ include versioneer.py
include artiq/_version.py
include artiq/coredevice/coredevice_generic.schema.json
include artiq/compiler/kernel.ld
include artiq/afws.pem

View File

@ -23,7 +23,7 @@ Highlights:
* On Kasli, the number of FIFO lanes in the scalable events dispatcher (SED) can now be configured in
the JSON hardware description file.
* ``artiq_ddb_template`` generates edge-counter keys that start with the key of the corresponding
TTL device (e.g. ``"ttl_0_counter"`` for the edge counter on TTL device``"ttl_0"``)
TTL device (e.g. ``ttl_0_counter`` for the edge counter on TTL device ``ttl_0``).
* ``artiq_master`` now has an ``--experiment-subdir`` option to scan only a subdirectory of the
repository when building the list of experiments.
* The configuration entry ``rtio_clock`` supports multiple clocking settings, deprecating the usage

23
artiq/afws.pem Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID0zCCArugAwIBAgIUPkNfEUx/uau3z8SD4mgMbCK/DEgwDQYJKoZIhvcNAQEL
BQAweTELMAkGA1UEBhMCSEsxEzARBgNVBAgMClNvbWUtU3RhdGUxFzAVBgNVBAoM
Dk0tTGFicyBMaW1pdGVkMRkwFwYDVQQDDBBuaXhibGQubS1sYWJzLmhrMSEwHwYJ
KoZIhvcNAQkBFhJoZWxwZGVza0BtLWxhYnMuaGswHhcNMjIwMjA2MTA1ODQ0WhcN
MjUwMjA1MTA1ODQ0WjB5MQswCQYDVQQGEwJISzETMBEGA1UECAwKU29tZS1TdGF0
ZTEXMBUGA1UECgwOTS1MYWJzIExpbWl0ZWQxGTAXBgNVBAMMEG5peGJsZC5tLWxh
YnMuaGsxITAfBgkqhkiG9w0BCQEWEmhlbHBkZXNrQG0tbGFicy5oazCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAPWetZhoggPR2ae7waGzv1AQ8NQO3noW
8DofVjusNpX5i/YB0waAr1bm1tALLJoHV2r/gTxujlXCe/L/WG1DLseCf6NO9sHg
t0FLhDpF9kPMWBgauVVLepd2Y2yU1G8eFuEVGnsiQSu0IzsZP5FQBJSyxvxJ+V/L
EW9ox91VGOP9VZR9jqdlYjGhcwClHA/nHe0q1fZq42+9rG466I5yIlNSoa7ilhTU
2C2doxy6Sr6VJYnLEMQqoIF65t3MkKi9iaqN7az0OCrj6XR0P5iKBzUhIgMUd2qs
7Id0XUdbQvaoaRI67vhGkNr+f4rdAUNCDGcbbokuBnmE7/gva6BAABUCAwEAAaNT
MFEwHQYDVR0OBBYEFM2e2FmcytXhKyfC1KEjVJ2mPSy3MB8GA1UdIwQYMBaAFM2e
2FmcytXhKyfC1KEjVJ2mPSy3MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAKH0z5vlbfTghjYWwd2yEEFBbZx5XxaLHboFQpFpxu9sZoidVs047tco
MOr1py9juiNGGM8G35sw9306f+thDFwqlQfSExUwp5pRQNq+mxglMSF05HWDqBwb
wnItKi/WXpkMQXgpQJFVeflz4B4ZFNlH1UQl5bwacXOM9NM9zO7duCjVXmGE0yxi
VQyApfPQYu9whCSowDYYaA0toJeikMzGfWxhlAH79/2Qmit8KcSCbX1fK/QoRZLa
5NeUi/OlJbBpkgTrfzfMLphmsPWPAVMeUKzqd/vXfG6ZBOZZm6e6sl8RBycBezII
15WekikTE5+T54/E0xiu+zIW/Xhhk14=
-----END CERTIFICATE-----

View File

@ -119,6 +119,18 @@ class ADF5356:
else:
self.sync()
@kernel
def set_att(self, att: float):
"""Set digital step attenuator in SI units.
This method will write the attenuator settings of the channel.
.. seealso:: :meth:`artiq.coredevice.mirny.Mirny.set_att`
:param att: Attenuation in dB.
"""
self.cpld.set_att(self.channel, att)
@kernel
def set_att_mu(self, att: int32):
"""Set digital step attenuator in machine units.

View File

@ -2,7 +2,7 @@ import asyncio
import logging
import struct
from enum import Enum
from .comm import set_keepalive
__all__ = ["TTLProbe", "TTLOverride", "CommMonInj"]
@ -29,6 +29,8 @@ class CommMonInj:
async def connect(self, host, port=1383):
self._reader, self._writer = await asyncio.open_connection(host, port)
set_keepalive(self._writer.transport.get_extra_info('socket'), 1, 1, 3)
try:
self._writer.write(b"ARTIQ moninj\n")
# get device endian

View File

@ -1,7 +1,7 @@
"""RTIO driver for Mirny (4 channel GHz PLLs)
"""
from artiq.language.core import nac3, Kernel, KernelInvariant, kernel
from artiq.language.core import nac3, Kernel, KernelInvariant, kernel, portable
from artiq.language.units import us
from numpy import int32
@ -144,6 +144,18 @@ class Mirny:
self.write_reg(1, (self.clk_sel << 4))
self.core.delay(1000. * us)
@portable
def att_to_mu(self, att: float) -> int32:
"""Convert an attenuation setting in dB to machine units.
:param att: Attenuation setting in dB.
:return: Digital attenuation setting.
"""
code = int32(255) - int32(round(att * 8.))
if code < 0 or code > 255:
raise ValueError("Invalid Mirny attenuation!")
return code
@kernel
def set_att_mu(self, channel: int32, att: int32):
"""Set digital step attenuator in machine units.
@ -153,6 +165,21 @@ class Mirny:
self.bus.set_config_mu(SPI_CONFIG | SPI_END, 16, SPIT_WR, SPI_CS)
self.bus.write(((channel | 8) << 25) | (att << 16))
@kernel
def set_att(self, channel: int32, att: float):
"""Set digital step attenuator in SI units.
This method will write the attenuator settings of the selected channel.
.. seealso:: :meth:`set_att_mu`
:param channel: Attenuator channel (0-3).
:param att: Attenuation setting in dB. Higher value is more
attenuation. Minimum attenuation is 0*dB, maximum attenuation is
31.5*dB.
"""
self.set_att_mu(channel, self.att_to_mu(att))
@kernel
def write_ext(self, addr: int32, length: int32, data: int32, ext_div: int32 = SPIT_WR):
"""Perform SPI write to a prefixed address"""

View File

@ -65,8 +65,8 @@ fn memory_test(total: &mut usize, wrong: &mut usize) -> bool {
})
}
fn prng32(seed: &mut u32) -> u32 { *seed = 1664525 * *seed + 1013904223; *seed }
fn prng16(seed: &mut u16) -> u16 { *seed = 25173 * *seed + 13849; *seed }
fn prng32(seed: &mut u32) -> u32 { *seed = u32::wrapping_add(u32::wrapping_mul(1664525, *seed), 1013904223); *seed }
fn prng16(seed: &mut u16) -> u16 { *seed = u16::wrapping_add(u16::wrapping_mul(25173, *seed), 13849); *seed }
for _ in 0..4 {
// Test data bus

View File

@ -211,9 +211,10 @@ mod ddr {
// Generate pseudo-random sequence
let mut prs = [0; DFII_NPHASES * DFII_PIX_DATA_SIZE];
let mut prv = 42;
let mut prv: u32 = 42;
for b in prs.iter_mut() {
prv = 1664525 * prv + 1013904223;
prv = u32::wrapping_add(u32::wrapping_mul(1664525, prv), 1013904223);
*b = prv as u8;
}
@ -296,7 +297,7 @@ mod ddr {
let mut prs = [0; DFII_NPHASES * DFII_PIX_DATA_SIZE];
let mut prv = 42;
for b in prs.iter_mut() {
prv = 1664525 * prv + 1013904223;
prv = u32::wrapping_add(u32::wrapping_mul(1664525, prv), 1013904223);
*b = prv as u8;
}

View File

@ -5,15 +5,20 @@ use byteorder::{NativeEndian, ByteOrder};
use io::{ProtoRead, Read, Write, ProtoWrite, Error};
use self::tag::{Tag, TagIterator, split_tag};
#[inline]
fn alignment_offset(alignment: isize, ptr: isize) -> isize {
(-ptr).rem_euclid(alignment)
}
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
let alignment = core::mem::align_of::<T>() as isize;
let fix = (alignment - (ptr as isize) % alignment) % alignment;
let fix = alignment_offset(alignment as isize, ptr as isize);
((ptr as isize) + fix) as *const T
}
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
let alignment = core::mem::align_of::<T>() as isize;
let fix = (alignment - (ptr as isize) % alignment) % alignment;
let fix = alignment_offset(alignment as isize, ptr as isize);
((ptr as isize) + fix) as *mut T
}
@ -54,6 +59,7 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
})
}
Tag::Tuple(it, arity) => {
*data = data.offset(alignment_offset(tag.alignment() as isize, *data as isize));
let mut it = it.clone();
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
@ -69,9 +75,12 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
let length = (*ptr).length as usize;
let tag = it.clone().next().expect("truncated tag");
(*ptr).elements = alloc(tag.size() * (*ptr).length as usize)?;
let padding = if let Tag::Int64 | Tag::Float64 = tag { 4 } else { 0 };
let mut data = alloc(tag.size() * length + padding)?;
let mut data = (*ptr).elements;
data = data.offset(alignment_offset(tag.alignment() as isize, data as isize));
(*ptr).elements = data;
match tag {
Tag::Bool => {
let dest = slice::from_raw_parts_mut(data as *mut u8, length);
@ -109,9 +118,11 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
let length = total_len as usize;
let elt_tag = it.clone().next().expect("truncated tag");
*buffer = alloc(elt_tag.size() * total_len as usize)?;
let padding = if let Tag::Int64 | Tag::Float64 = tag { 4 } else { 0 };
let mut data = alloc(elt_tag.size() * length + padding)?;
data = data.offset(alignment_offset(tag.alignment() as isize, data as isize));
let mut data = *buffer;
*buffer = data;
match elt_tag {
Tag::Bool => {
let dest = slice::from_raw_parts_mut(data as *mut u8, length);
@ -139,6 +150,7 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
})
}
Tag::Range(it) => {
*data = data.offset(alignment_offset(tag.alignment() as isize, *data as isize));
let tag = it.clone().next().expect("truncated tag");
recv_value(reader, tag, data, alloc)?;
recv_value(reader, tag, data, alloc)?;
@ -336,6 +348,7 @@ pub fn send_args<W>(writer: &mut W, service: u32, tag_bytes: &[u8], data: *const
mod tag {
use core::fmt;
use super::alignment_offset;
pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) {
let tag_separator =
@ -385,6 +398,33 @@ mod tag {
}
}
pub fn alignment(self) -> usize {
use cslice::CSlice;
match self {
Tag::None => 1,
Tag::Bool => core::mem::align_of::<u8>(),
Tag::Int32 => core::mem::align_of::<i32>(),
Tag::Int64 => core::mem::align_of::<i64>(),
Tag::Float64 => core::mem::align_of::<f64>(),
// struct type: align to largest element
Tag::Tuple(it, arity) => {
let it = it.clone();
it.take(arity.into()).map(|t| t.alignment()).max().unwrap()
},
Tag::Range(it) => {
let it = it.clone();
it.take(3).map(|t| t.alignment()).max().unwrap()
}
// CSlice basically
Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) =>
core::mem::align_of::<CSlice<()>>(),
// array buffer is allocated, so no need for alignment first
Tag::Array(_, _) => 1,
// will not be sent from the host
_ => unreachable!("unexpected tag from host")
}
}
pub fn size(self) -> usize {
match self {
Tag::None => 0,
@ -401,6 +441,7 @@ mod tag {
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
size += tag.size();
size += alignment_offset(tag.alignment() as isize, size as isize) as usize;
}
size
}
@ -469,6 +510,13 @@ mod tag {
}
}
impl<'a> Iterator for TagIterator<'a> {
type Item = Tag<'a>;
fn next(&mut self) -> Option<Self::Item> {
(self as &mut TagIterator<'a>).next()
}
}
impl<'a> fmt::Display for TagIterator<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut it = self.clone();

164
artiq/frontend/afws_client.py Executable file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env python3
import sys
import argparse
import os
import socket
import ssl
import io
import zipfile
from getpass import getpass
def get_artiq_cert():
try:
import artiq
except ImportError:
return None
filename = os.path.join(os.path.dirname(artiq.__file__), "afws.pem")
if not os.path.isfile(filename):
return None
return filename
def get_artiq_rev():
try:
import artiq
except ImportError:
return None
return artiq._version.get_rev()
def zip_unarchive(data, directory):
buf = io.BytesIO(data)
with zipfile.ZipFile(buf) as archive:
archive.extractall(directory)
class Client:
def __init__(self, server, port, cafile):
self.ssl_context = ssl.create_default_context(cafile=cafile)
self.raw_socket = socket.create_connection((server, port))
try:
self.socket = self.ssl_context.wrap_socket(self.raw_socket, server_hostname=server)
except:
self.raw_socket.close()
raise
self.fsocket = self.socket.makefile("rwb")
def close(self):
self.socket.close()
self.raw_socket.close()
def send_command(self, *command):
self.fsocket.write((" ".join(command) + "\n").encode())
self.fsocket.flush()
def read_reply(self):
return self.fsocket.readline().decode("ascii").split()
def login(self, username, password):
self.send_command("LOGIN", username, password)
return self.read_reply() == ["HELLO"]
def build(self, rev, variant):
self.send_command("BUILD", rev, variant)
reply = self.read_reply()[0]
if reply != "BUILDING":
return reply, None
print("Build in progress. This may take 10-15 minutes.")
reply, status = self.read_reply()
if reply != "DONE":
raise ValueError("Unexpected server reply: expected 'DONE', got '{}'".format(reply))
if status != "done":
return status, None
print("Build completed. Downloading...")
reply, length = self.read_reply()
if reply != "PRODUCT":
raise ValueError("Unexpected server reply: expected 'PRODUCT', got '{}'".format(reply))
contents = self.fsocket.read(int(length))
print("Download completed.")
return "OK", contents
def passwd(self, password):
self.send_command("PASSWD", password)
return self.read_reply() == ["OK"]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--server", default="nixbld.m-labs.hk", help="server to connect to (default: %(default)s)")
parser.add_argument("--port", default=7402, type=int, help="port to connect to (default: %(default)d)")
parser.add_argument("--cert", default=None, help="SSL certificate file used to authenticate server (default: afws.pem in ARTIQ)")
parser.add_argument("username", help="user name for logging into AFWS")
action = parser.add_subparsers(dest="action")
action.required = True
act_build = action.add_parser("build", help="build and download firmware")
act_build.add_argument("--rev", default=None, help="revision to build (default: currently installed ARTIQ revision)")
act_build.add_argument("variant", help="variant to build")
act_build.add_argument("directory", help="output directory")
act_passwd = action.add_parser("passwd", help="change password")
args = parser.parse_args()
cert = args.cert
if cert is None:
cert = get_artiq_cert()
if cert is None:
print("SSL certificate not found in ARTIQ. Specify manually using --cert.")
sys.exit(1)
if args.action == "passwd":
password = getpass("Current password: ")
else:
password = getpass()
client = Client(args.server, args.port, cert)
try:
if not client.login(args.username, password):
print("Login failed")
sys.exit(1)
print("Logged in successfully.")
if args.action == "passwd":
print("Password must made of alphanumeric characters (a-z, A-Z, 0-9) and be at least 8 characters long.")
password = getpass("New password: ")
password_confirm = getpass("New password (again): ")
while password != password_confirm:
print("Passwords do not match")
password = getpass("New password: ")
password_confirm = getpass("New password (again): ")
if not client.passwd(password):
print("Failed to change password")
sys.exit(1)
elif args.action == "build":
try:
os.mkdir(args.directory)
except FileExistsError:
try:
if any(os.scandir(args.directory)):
print("Output directory already exists and is not empty. Please remove it and try again.")
sys.exit(1)
except NotADirectoryError:
print("A file with the same name as the output directory already exists. Please remove it and try again.")
sys.exit(1)
rev = args.rev
if rev is None:
rev = get_artiq_rev()
if rev is None:
print("Unable to determine currently installed ARTIQ revision. Specify manually using --rev.")
sys.exit(1)
result, contents = client.build(rev, args.variant)
if result != "OK":
if result == "UNAUTHORIZED":
print("You are not authorized to build this variant. Your firmware subscription may have expired. Contact helpdesk\x40m-labs.hk.")
else:
print("Build failed: {}".format(result))
sys.exit(1)
zip_unarchive(contents, args.directory)
else:
raise ValueError
finally:
client.close()
if __name__ == "__main__":
main()

View File

@ -13,7 +13,6 @@ from collections import defaultdict
from sipyco import common_args
from artiq import __version__ as artiq_version
from artiq import __artiq_dir__ as artiq_dir
from artiq.remoting import SSHClient, LocalClient
from artiq.frontend.bit2bin import bit2bin
@ -63,13 +62,11 @@ Prerequisites:
parser.add_argument("-t", "--target", default="kasli",
help="target board, default: %(default)s, one of: "
"kasli sayma metlino kc705")
parser.add_argument("-V", "--variant", default=None,
help="board variant. Autodetected if only one is installed.")
parser.add_argument("-I", "--preinit-command", default=[], action="append",
help="add a pre-initialization OpenOCD command. "
"Useful for selecting a board when several are connected.")
parser.add_argument("-f", "--storage", help="write file to storage area")
parser.add_argument("-d", "--dir", help="look for board binaries in this directory")
parser.add_argument("-d", "--dir", default=None, help="look for board binaries in this directory")
parser.add_argument("--srcbuild", help="board binaries directory is laid out as a source build tree",
default=False, action="store_true")
parser.add_argument("--no-rtm-jtag", help="do not attempt JTAG to the RTM",
@ -338,56 +335,19 @@ def main():
},
}[args.target]
bin_dir = args.dir
if bin_dir is None:
bin_dir = os.path.join(artiq_dir, "board-support")
needs_artifacts = not args.action or any(
action in args.action
for action in ["gateware", "rtm_gateware", "bootloader", "firmware", "load", "rtm_load"])
variant = args.variant
if needs_artifacts and variant is None:
variants = []
if args.srcbuild:
for entry in os.scandir(bin_dir):
if entry.is_dir():
variants.append(entry.name)
else:
prefix = args.target + "-"
for entry in os.scandir(bin_dir):
if entry.is_dir() and entry.name.startswith(prefix):
variants.append(entry.name[len(prefix):])
if args.target == "sayma":
try:
variants.remove("rtm")
except ValueError:
pass
if all(action in ["rtm_gateware", "storage", "rtm_load", "erase", "start"]
for action in args.action) and args.action:
pass
elif len(variants) == 0:
raise FileNotFoundError("no variants found, did you install a board binary package?")
elif len(variants) == 1:
variant = variants[0]
else:
raise ValueError("more than one variant found for selected board, specify -V. "
"Found variants: {}".format(" ".join(sorted(variants))))
if needs_artifacts:
if args.srcbuild:
variant_dir = variant
else:
variant_dir = args.target + "-" + variant
if args.target == "sayma":
if args.srcbuild:
rtm_variant_dir = "rtm"
else:
rtm_variant_dir = "sayma-rtm"
if not args.action:
if args.target == "sayma" and variant != "simplesatellite" and variant != "master":
if args.target == "sayma":
args.action = "gateware rtm_gateware bootloader firmware start".split()
else:
args.action = "gateware bootloader firmware start".split()
needs_artifacts = any(
action in args.action
for action in ["gateware", "rtm_gateware", "bootloader", "firmware", "load", "rtm_load"])
if needs_artifacts and args.dir is None:
raise ValueError("the directory containing the binaries need to be specified using -d.")
binary_dir = args.dir
rtm_binary_dir = os.path.join(binary_dir, "rtm")
if args.host is None:
client = LocalClient()
@ -400,14 +360,14 @@ def main():
programmer_cls = config["programmer"]
programmer = programmer_cls(client, preinit_script=args.preinit_command)
def artifact_path(this_variant_dir, *path_filename):
def artifact_path(this_binary_dir, *path_filename):
if args.srcbuild:
# source tree - use path elements to locate file
return os.path.join(bin_dir, this_variant_dir, *path_filename)
return os.path.join(this_binary_dir, *path_filename)
else:
# flat tree - all files in the same directory, discard path elements
*_, filename = path_filename
return os.path.join(bin_dir, this_variant_dir, filename)
return os.path.join(this_binary_dir, filename)
def convert_gateware(bit_filename, header=False):
bin_handle, bin_filename = tempfile.mkstemp(
@ -429,15 +389,15 @@ def main():
for action in args.action:
if action == "gateware":
gateware_bin = convert_gateware(
artifact_path(variant_dir, "gateware", "top.bit"))
artifact_path(binary_dir, "gateware", "top.bit"))
programmer.write_binary(*config["gateware"], gateware_bin)
elif action == "rtm_gateware":
rtm_gateware_bin = convert_gateware(
artifact_path(rtm_variant_dir, "gateware", "top.bit"), header=True)
artifact_path(rtm_binary_dir, "gateware", "top.bit"), header=True)
programmer.write_binary(*config["rtm_gateware"],
rtm_gateware_bin)
elif action == "bootloader":
bootloader_bin = artifact_path(variant_dir, "software", "bootloader", "bootloader.bin")
bootloader_bin = artifact_path(binary_dir, "software", "bootloader", "bootloader.bin")
programmer.write_binary(*config["bootloader"], bootloader_bin)
elif action == "storage":
storage_img = args.storage
@ -445,7 +405,7 @@ def main():
elif action == "firmware":
firmware_fbis = []
for firmware in "satman", "runtime":
filename = artifact_path(variant_dir, "software", firmware, firmware + ".fbi")
filename = artifact_path(binary_dir, "software", firmware, firmware + ".fbi")
if os.path.exists(filename):
firmware_fbis.append(filename)
if not firmware_fbis:
@ -456,13 +416,13 @@ def main():
programmer.write_binary(*config["firmware"], firmware_fbis[0])
elif action == "load":
if args.target == "sayma":
gateware_bit = artifact_path(variant_dir, "gateware", "top.bit")
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
programmer.load(gateware_bit, 1)
else:
gateware_bit = artifact_path(variant_dir, "gateware", "top.bit")
gateware_bit = artifact_path(binary_dir, "gateware", "top.bit")
programmer.load(gateware_bit, 0)
elif action == "rtm_load":
rtm_gateware_bit = artifact_path(rtm_variant_dir, "gateware", "top.bit")
rtm_gateware_bit = artifact_path(rtm_binary_dir, "gateware", "top.bit")
programmer.load(rtm_gateware_bit, 0)
elif action == "start":
programmer.start()

View File

@ -331,7 +331,7 @@ class SinaraTester(EnvExperiment):
self.core.break_realtime()
channel.init()
channel.set_att_mu(160)
channel.set_att(11.5*dB)
channel.sw.on()
self.core.break_realtime()

View File

@ -100,6 +100,17 @@ class NumberEntryInt(QtWidgets.QSpinBox):
if "default" in procdesc:
return procdesc["default"]
else:
have_max = "max" in procdesc and procdesc["max"] is not None
have_min = "min" in procdesc and procdesc["min"] is not None
if have_max and have_min:
if procdesc["min"] <= 0 < procdesc["max"]:
return 0
elif have_min and not have_max:
if procdesc["min"] >= 0:
return procdesc["min"]
elif not have_min and have_max:
if procdesc["max"] < 0:
return procdesc["max"]
return 0

View File

@ -515,3 +515,26 @@ class NumpyBoolTest(ExperimentCase):
def test_numpy_bool(self):
"""Test NumPy bools decay to ARTIQ compiler builtin bools as expected"""
self.create(_NumpyBool).run()
class _Alignment(EnvExperiment):
def build(self):
self.setattr_device("core")
@rpc
def a_tuple(self) -> TList(TTuple([TBool, TFloat, TBool])):
return [(True, 1234.5678, True)]
@kernel
def run(self):
a, b, c = self.a_tuple()[0]
d, e, f = self.a_tuple()[0]
assert a == d
assert b == e
assert c == f
return 0
class AlignmentTest(ExperimentCase):
def test_tuple(self):
self.create(_Alignment).run()

View File

@ -1,9 +1,8 @@
ARTIQ documentation
===================
Contents:
.. toctree::
:caption: Contents
:maxdepth: 2
introduction

View File

@ -1,5 +1,29 @@
{
"nodes": {
"artiq-comtools": {
"inputs": {
"nixpkgs": [
"nac3",
"nixpkgs"
],
"sipyco": [
"sipyco"
]
},
"locked": {
"lastModified": 1644743100,
"narHash": "sha256-XqxMq2l2DXSovV7r2k/FXjYRM3bvVl3Mjy+C1koVAx4=",
"owner": "m-labs",
"repo": "artiq-comtools",
"rev": "8a126dd7d0a3f2d50ae151ec633cd52587d98796",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "artiq-comtools",
"type": "github"
}
},
"mozilla-overlay": {
"flake": false,
"locked": {
@ -16,6 +40,24 @@
"type": "github"
}
},
"nac3": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1644759922,
"narHash": "sha256-3hNFahCeHp0seppVONSlAMXIzn0vjCJGrJj6CySLLxw=",
"ref": "master",
"rev": "21d9182ba2924cf9cc555a201e661d2ea474eed9",
"revCount": 594,
"type": "git",
"url": "https://git.m-labs.hk/M-Labs/nac3.git"
},
"original": {
"type": "git",
"url": "https://git.m-labs.hk/M-Labs/nac3.git"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1644472683,
@ -34,11 +76,33 @@
},
"root": {
"inputs": {
"artiq-comtools": "artiq-comtools",
"mozilla-overlay": "mozilla-overlay",
"nac3": "nac3",
"sipyco": "sipyco",
"src-migen": "src-migen",
"src-misoc": "src-misoc",
"src-nac3": "src-nac3",
"src-sipyco": "src-sipyco"
"src-misoc": "src-misoc"
}
},
"sipyco": {
"inputs": {
"nixpkgs": [
"nac3",
"nixpkgs"
]
},
"locked": {
"lastModified": 1644649772,
"narHash": "sha256-LE9L5bDSunCPEnuf5Ed8enTAXA2vkTSmjvqPX9ILO0Y=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "8e4382352bc64bd01c9db35d9c9b0ef42b8b9d3b",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "sipyco",
"type": "github"
}
},
"src-migen": {
@ -74,40 +138,6 @@
"type": "git",
"url": "https://github.com/m-labs/misoc.git"
}
},
"src-nac3": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1644723702,
"narHash": "sha256-2pLJHYvQmoXhdy3WDZpfU7kDMpgH/G7uREZOyql7R10=",
"ref": "master",
"rev": "4b8e70f7462b139e388f098d42f9f47e4915f431",
"revCount": 589,
"type": "git",
"url": "https://git.m-labs.hk/M-Labs/nac3.git"
},
"original": {
"type": "git",
"url": "https://git.m-labs.hk/M-Labs/nac3.git"
}
},
"src-sipyco": {
"flake": false,
"locked": {
"lastModified": 1644649772,
"narHash": "sha256-LE9L5bDSunCPEnuf5Ed8enTAXA2vkTSmjvqPX9ILO0Y=",
"owner": "m-labs",
"repo": "sipyco",
"rev": "8e4382352bc64bd01c9db35d9c9b0ef42b8b9d3b",
"type": "github"
},
"original": {
"owner": "m-labs",
"repo": "sipyco",
"type": "github"
}
}
},
"root": "root",

122
flake.nix
View File

@ -2,20 +2,25 @@
description = "A leading-edge control system for quantum information experiments";
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
inputs.src-sipyco = { url = github:m-labs/sipyco; flake = false; };
inputs.src-nac3 = { type = "git"; url = "https://git.m-labs.hk/M-Labs/nac3.git"; };
inputs.sipyco.url = github:m-labs/sipyco;
inputs.sipyco.inputs.nixpkgs.follows = "nac3/nixpkgs";
inputs.nac3 = { type = "git"; url = "https://git.m-labs.hk/M-Labs/nac3.git"; };
inputs.artiq-comtools.url = github:m-labs/artiq-comtools;
inputs.artiq-comtools.inputs.nixpkgs.follows = "nac3/nixpkgs";
inputs.artiq-comtools.inputs.sipyco.follows = "sipyco";
inputs.src-migen = { url = github:m-labs/migen; flake = false; };
inputs.src-misoc = { type = "git"; url = "https://github.com/m-labs/misoc.git"; submodules = true; flake = false; };
outputs = { self, mozilla-overlay, src-sipyco, src-nac3, src-migen, src-misoc }:
outputs = { self, mozilla-overlay, sipyco, nac3, artiq-comtools, src-migen, src-misoc }:
let
pkgs = import src-nac3.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
pkgs = import nac3.inputs.nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
artiqVersionMajor = 8;
artiqVersionMinor = self.sourceInfo.revCount or 0;
artiqVersionId = self.sourceInfo.shortRev or "unknown";
artiqVersion = (builtins.toString artiqVersionMajor) + "." + (builtins.toString artiqVersionMinor) + "-" + artiqVersionId + "-beta";
artiqVersion = (builtins.toString artiqVersionMajor) + "." + (builtins.toString artiqVersionMinor) + "." + artiqVersionId + ".beta";
artiqRev = self.sourceInfo.rev or "unknown";
rustManifest = pkgs.fetchurl {
url = "https://static.rust-lang.org/dist/2021-01-29/channel-rust-nightly.toml";
@ -51,12 +56,6 @@
fontconfig
];
sipyco = pkgs.python3Packages.buildPythonPackage {
name = "sipyco";
src = src-sipyco;
propagatedBuildInputs = with pkgs.python3Packages; [ pybase64 numpy ];
};
qasync = pkgs.python3Packages.buildPythonPackage rec {
pname = "qasync";
version = "0.19.0";
@ -78,11 +77,15 @@
version = artiqVersion;
src = self;
preBuild = "export VERSIONEER_OVERRIDE=${version}";
preBuild =
''
export VERSIONEER_OVERRIDE=${version}
export VERSIONEER_REV=${artiqRev}
'';
nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ];
# keep llvm_x and lld_x in sync with nac3
propagatedBuildInputs = [ pkgs.llvm_13 pkgs.lld_13 src-nac3.packages.x86_64-linux.nac3artiq sipyco ]
propagatedBuildInputs = [ pkgs.llvm_13 pkgs.lld_13 nac3.packages.x86_64-linux.nac3artiq sipyco.packages.x86_64-linux.sipyco artiq-comtools.packages.x86_64-linux.artiq-comtools ]
++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial h5py pyqt5 qasync ]);
dontWrapQtApps = true;
@ -183,7 +186,7 @@
};
makeArtiqBoardPackage = { target, variant, buildCommand ? "python -m artiq.gateware.targets.${target} -V ${variant}" }:
pkgs.python3Packages.toPythonModule (pkgs.stdenv.mkDerivation {
pkgs.stdenv.mkDerivation {
name = "artiq-board-${target}-${variant}";
phases = [ "buildPhase" "checkPhase" "installPhase" ];
cargoDeps = rustPlatform.fetchCargoTarball {
@ -192,7 +195,7 @@
sha256 = "sha256-YyycMsDzR+JRcMZJd6A/CRi2J9nKmaWY/KXUnAQaZ00=";
};
nativeBuildInputs = [
(pkgs.python3.withPackages(ps: [ migen misoc artiq ]))
(pkgs.python3.withPackages(ps: [ ps.jsonschema migen misoc artiq]))
rustPlatform.rust.rustc
rustPlatform.rust.cargo
pkgs.llvmPackages_13.clang-unwrapped
@ -222,7 +225,7 @@
'';
installPhase =
''
TARGET_DIR=$out/${pkgs.python3Packages.python.sitePackages}/artiq/board-support/${target}-${variant}
TARGET_DIR=$out
mkdir -p $TARGET_DIR
cp artiq_${target}/${variant}/gateware/top.bit $TARGET_DIR
if [ -e artiq_${target}/${variant}/software/bootloader/bootloader.bin ]
@ -235,7 +238,7 @@
'';
# don't mangle ELF files as they are not for NixOS
dontFixup = true;
});
};
openocd-bscanspi = let
bscan_spi_bitstreams-pkg = pkgs.stdenv.mkDerivation {
@ -275,36 +278,79 @@
name = "openocd-bscanspi";
paths = [ openocd-fixed bscan_spi_bitstreams-pkg ];
};
sphinxcontrib-wavedrom = pkgs.python3Packages.buildPythonPackage rec {
pname = "sphinxcontrib-wavedrom";
version = "3.0.2";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "sha256-ukZd3ajt0Sx3LByof4R80S31F5t1yo+L8QUADrMMm2A=";
};
buildInputs = [ pkgs.python3Packages.setuptools_scm ];
propagatedBuildInputs = [ pkgs.nodejs pkgs.nodePackages.wavedrom-cli ] ++ (with pkgs.python3Packages; [ wavedrom sphinx xcffib cairosvg ]);
};
latex-artiq-manual = pkgs.texlive.combine {
inherit (pkgs.texlive)
scheme-basic latexmk cmap collection-fontsrecommended fncychap
titlesec tabulary varwidth framed fancyvrb float wrapfig parskip
upquote capt-of needspace etoolbox;
};
in rec {
packages.x86_64-linux = rec {
inherit migen misoc vivadoEnv vivado openocd-bscanspi artiq;
inherit (src-nac3.packages.x86_64-linux) python3-mimalloc;
inherit (nac3.packages.x86_64-linux) python3-mimalloc;
inherit qasync openocd-bscanspi artiq;
inherit migen misoc asyncserial microscope vivadoEnv vivado;
artiq-board-kc705-nist_clock = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_clock";
};
artiq-board-kc705-nist_qc2 = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_qc2";
inherit sphinxcontrib-wavedrom latex-artiq-manual;
artiq-manual-html = pkgs.stdenvNoCC.mkDerivation rec {
name = "artiq-manual-html-${version}";
version = artiqVersion;
src = self;
buildInputs = [
pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme
pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom
];
buildPhase = ''
export VERSIONEER_OVERRIDE=${artiqVersion}
export SOURCE_DATE_EPOCH=${builtins.toString self.sourceInfo.lastModified}
cd doc/manual
make html
'';
installPhase = ''
cp -r _build/html $out
mkdir $out/nix-support
echo doc manual $out index.html >> $out/nix-support/hydra-build-products
'';
};
artiq-board-kc705-nist_clock_master = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_clock_master";
};
artiq-board-kc705-nist_qc2_master = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_qc2_master";
};
artiq-board-kc705-nist_clock_satellite = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_clock";
};
artiq-board-kc705-nist_qc2_satellite = makeArtiqBoardPackage {
target = "kc705";
variant = "nist_qc2";
artiq-manual-pdf = pkgs.stdenvNoCC.mkDerivation rec {
name = "artiq-manual-pdf-${version}";
version = artiqVersion;
src = self;
buildInputs = [
pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme
pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom
latex-artiq-manual
];
buildPhase = ''
export VERSIONEER_OVERRIDE=${artiq.version}
export SOURCE_DATE_EPOCH=${builtins.toString self.sourceInfo.lastModified}
cd doc/manual
make latexpdf
'';
installPhase = ''
mkdir $out
cp _build/latex/ARTIQ.pdf $out
mkdir $out/nix-support
echo doc-pdf manual $out ARTIQ.pdf >> $out/nix-support/hydra-build-products
'';
};
};
inherit makeArtiqBoardPackage;
defaultPackage.x86_64-linux = packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ packages.x86_64-linux.artiq ]);
devShell.x86_64-linux = pkgs.mkShell {
@ -321,6 +367,8 @@
packages.x86_64-linux.vivadoEnv
packages.x86_64-linux.vivado
packages.x86_64-linux.openocd-bscanspi
pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme
pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom latex-artiq-manual
];
};

View File

@ -35,6 +35,7 @@ console_scripts = [
"artiq_run = artiq.frontend.artiq_run:main",
"artiq_flash = artiq.frontend.artiq_flash:main",
"aqctl_corelog = artiq.frontend.aqctl_corelog:main",
"afws_client = artiq.frontend.afws_client:main",
]
gui_scripts = [

View File

@ -1,9 +1,13 @@
import os
import sys
VERSION_FILE = """
def get_version():
return "{version}"
def get_rev():
return "{rev}"
"""
def get_version():
@ -18,10 +22,13 @@ def get_version():
version += ".beta"
return version
def write_to_version_file(filename, version):
def get_rev():
return os.getenv("VERSIONEER_REV", default="unknown")
def write_to_version_file(filename, version, rev):
os.unlink(filename)
with open(filename, "w") as f:
f.write(VERSION_FILE.format(version=version))
f.write(VERSION_FILE.format(version=version, rev=rev))
def get_cmdclass():
@ -36,11 +43,12 @@ def get_cmdclass():
class cmd_build_py(_build_py):
def run(self):
version = get_version()
rev = get_rev()
_build_py.run(self)
target_versionfile = os.path.join(self.build_lib,
"artiq", "_version.py")
print("UPDATING %s" % target_versionfile)
write_to_version_file(target_versionfile, version)
write_to_version_file(target_versionfile, version, rev)
cmds["build_py"] = cmd_build_py
@ -54,6 +62,7 @@ def get_cmdclass():
def run(self):
version = get_version()
self._versioneer_generated_version = version
self._versioneer_rev = get_rev()
# unless we update this, the command will keep using the old
# version
self.distribution.metadata.version = version
@ -64,7 +73,8 @@ def get_cmdclass():
target_versionfile = os.path.join(base_dir, "artiq", "_version.py")
print("UPDATING %s" % target_versionfile)
write_to_version_file(target_versionfile,
self._versioneer_generated_version)
self._versioneer_generated_version,
self._versioneer_rev)
cmds["sdist"] = cmd_sdist
return cmds