From e6c18364ae8c3fb2acb3a2811e16739bd6509a76 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 26 Jan 2022 07:51:02 +0800 Subject: [PATCH 01/31] flake: consistent version string --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 78c9dc823..6c5d94861 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,7 @@ artiqVersionMajor = 7; 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"; rustManifest = pkgs.fetchurl { url = "https://static.rust-lang.org/dist/2021-01-29/channel-rust-nightly.toml"; From ee511758ce92a3917f59f82279bfaacd674b5cc7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 26 Jan 2022 07:51:35 +0800 Subject: [PATCH 02/31] fix typo --- artiq/coredevice/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 9600bdd29..7b6967743 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -103,7 +103,7 @@ class CoreException: 'another exception occurred:\n\n').join(tracebacks) return 'Core Device Traceback:\n' +\ traceback_str +\ - '\n\nEnd of Code Device Traceback\n' + '\n\nEnd of Core Device Traceback\n' class InternalError(Exception): From 234a82aaa9599da5fca61cf8c6d1755aa175721d Mon Sep 17 00:00:00 2001 From: Steve Fan <19037626d@connect.polyu.hk> Date: Thu, 27 Jan 2022 17:45:09 +0800 Subject: [PATCH 03/31] dashboard: prioritize min as part of default value resolution (#1839) --- artiq/gui/entries.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/artiq/gui/entries.py b/artiq/gui/entries.py index 7e5e80916..b63be3902 100644 --- a/artiq/gui/entries.py +++ b/artiq/gui/entries.py @@ -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 From 93328ad8ee6e29ccca65e28e3a6c9708da339e25 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Thu, 27 Jan 2022 23:03:28 +0800 Subject: [PATCH 04/31] compiler: only allow constant exception messages Otherwise, the exception message might be allocated on a stack, and will become a dangling pointer when the exception is raised. This will break some code that constructs exceptions with a function by passing the message as a parameter because we cannot know if the parameter is a constant. A way to mitigate this would be to defer this check to LLVM IR codegen stage, and do inlining first for those exception allocation functions, but I am not sure if we will guarantee inlining for certain functions, and whether this is really needed. --- artiq/compiler/transforms/artiq_ir_generator.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 40f50718a..9062b6806 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -2198,7 +2198,8 @@ class ARTIQIRGenerator(algorithm.Visitor): return phi # Keep this function with builtins.TException.attributes. - def alloc_exn(self, typ, message=None, param0=None, param1=None, param2=None): + def alloc_exn(self, typ, message=None, param0=None, param1=None, + param2=None, nomsgcheck=False): typ = typ.find() name = "{}:{}".format(typ.id, typ.name) name_id = self.embedding_map.store_str(name) @@ -2212,8 +2213,16 @@ class ARTIQIRGenerator(algorithm.Visitor): if message is None: attributes.append(ir.Constant(typ.name, builtins.TStr())) + elif isinstance(message, ir.Constant) or nomsgcheck: + attributes.append(message) # message else: - attributes.append(message) # message + diag = diagnostic.Diagnostic( + "error", + "only constant exception messages are supported", + {}, + self.current_loc if message.loc is None else message.loc + ) + self.engine.process(diag) param_type = builtins.TInt64() for param in [param0, param1, param2]: @@ -2674,7 +2683,8 @@ class ARTIQIRGenerator(algorithm.Visitor): old_final_branch, self.final_branch = self.final_branch, None old_unwind, self.unwind_target = self.unwind_target, None - exn = self.alloc_exn(builtins.TException("AssertionError"), message=msg) + exn = self.alloc_exn(builtins.TException("AssertionError"), + message=msg, nomsgcheck=True) self.append(ir.SetAttr(exn, "#__file__", file)) self.append(ir.SetAttr(exn, "#__line__", line)) self.append(ir.SetAttr(exn, "#__col__", col)) From 34008b7a21255f915207a038b79facae010fa6cd Mon Sep 17 00:00:00 2001 From: Steve Fan <19037626d@connect.polyu.hk> Date: Fri, 28 Jan 2022 20:49:55 +0800 Subject: [PATCH 05/31] Backport of "fixes alignment and size problem" from artiq-zynq (#1841) --- artiq/firmware/libproto_artiq/rpc_proto.rs | 60 +++++++++++++++++++--- artiq/test/coredevice/test_embedding.py | 23 +++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/artiq/firmware/libproto_artiq/rpc_proto.rs b/artiq/firmware/libproto_artiq/rpc_proto.rs index 0295b5297..a93a0e82c 100644 --- a/artiq/firmware/libproto_artiq/rpc_proto.rs +++ b/artiq/firmware/libproto_artiq/rpc_proto.rs @@ -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(ptr: *const ()) -> *const T { let alignment = core::mem::align_of::() 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(ptr: *mut ()) -> *mut T { let alignment = core::mem::align_of::() 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(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(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(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(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(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::(), + Tag::Int32 => core::mem::align_of::(), + Tag::Int64 => core::mem::align_of::(), + Tag::Float64 => core::mem::align_of::(), + // 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::>(), + // 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 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(); diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 8f5e4e3bc..4e86e9aa5 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -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() From afb98a1903f638fa46822d2d0b367cd31e4c9bc6 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 30 Jan 2022 19:31:20 +0800 Subject: [PATCH 06/31] flake: export makeArtiqBoardPackage --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 6c5d94861..fd644e58a 100644 --- a/flake.nix +++ b/flake.nix @@ -372,6 +372,8 @@ }; }; + inherit makeArtiqBoardPackage; + defaultPackage.x86_64-linux = pkgs.python3.withPackages(ps: [ packages.x86_64-linux.artiq ]); devShell.x86_64-linux = pkgs.mkShell { From dd3279e50622b5ceb1c24b4d1891f442cc0acce3 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 30 Jan 2022 19:38:56 +0800 Subject: [PATCH 07/31] flake: add jsonschema to makeArtiqBoardPackage --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index fd644e58a..1f05811ec 100644 --- a/flake.nix +++ b/flake.nix @@ -260,7 +260,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_11.clang-unwrapped From ef25640937415fe1592c0cc523807743a29b7932 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Feb 2022 19:01:40 +0800 Subject: [PATCH 08/31] compiler: fix noreturn attribute on __artiq_resume --- artiq/compiler/transforms/llvm_ir_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 60a5dc482..8017981d3 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -403,7 +403,7 @@ class LLVMIRGenerator: if isinstance(llty, ll.FunctionType): llglobal = ll.Function(self.llmodule, llty, name) - if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): + if name in ("__artiq_raise", "__artiq_resume", "llvm.trap"): llglobal.attributes.add("noreturn") if name in ("rtio_log", "rpc_send", "rpc_send_async", self.target.print_function): From 245fe6e9ea8ac1b9d8e8832af79dde91445f722c Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Feb 2022 16:04:00 +0800 Subject: [PATCH 09/31] flake: remove non-HITL board packages Those can be built externally by calling makeArtiqBoardPackage directly. --- flake.nix | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/flake.nix b/flake.nix index 1f05811ec..48b074838 100644 --- a/flake.nix +++ b/flake.nix @@ -350,26 +350,6 @@ target = "kc705"; variant = "nist_clock"; }; - artiq-board-kc705-nist_qc2 = makeArtiqBoardPackage { - target = "kc705"; - variant = "nist_qc2"; - }; - 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"; - }; }; inherit makeArtiqBoardPackage; From 3cddb14174ba962aba65df19c03d4b53da3cbac1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Feb 2022 16:33:17 +0800 Subject: [PATCH 10/31] flake: break artiq false dependencies --- flake.nix | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 48b074838..7d21236bf 100644 --- a/flake.nix +++ b/flake.nix @@ -101,8 +101,13 @@ rustc $src/artiq/test/libartiq_support/lib.rs -Cpanic=unwind -g ''; installPhase = '' - mkdir $out - cp libartiq_support.so $out + mkdir -p $out/lib $out/bin + cp libartiq_support.so $out/lib + cat > $out/bin/libartiq-support << EOF + #!/bin/sh + echo $out/lib/libartiq_support.so + EOF + chmod 755 $out/bin/libartiq-support ''; }; @@ -165,13 +170,13 @@ ]; # FIXME: automatically propagate lld_11 llvm_11 dependencies - checkInputs = [ pkgs.lld_11 pkgs.llvm_11 pkgs.lit outputcheck ]; + checkInputs = [ pkgs.lld_11 pkgs.llvm_11 libartiq-support pkgs.lit outputcheck ]; checkPhase = '' python -m unittest discover -v artiq.test TESTDIR=`mktemp -d` cp --no-preserve=mode,ownership -R $src/artiq/test/lit $TESTDIR - LIBARTIQ_SUPPORT=${libartiq-support}/libartiq_support.so lit -v $TESTDIR/lit + LIBARTIQ_SUPPORT=`libartiq-support` lit -v $TESTDIR/lit ''; }; From 8866ab301aa908e57a3a35484b15c9a4ae40a6c1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 2 Feb 2022 16:39:49 +0800 Subject: [PATCH 11/31] flake: update dependencies --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index fa8cd3d65..3fc2fe6b4 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "mozilla-overlay": { "flake": false, "locked": { - "lastModified": 1638887313, - "narHash": "sha256-FMYV6rVtvSIfthgC1sK1xugh3y7muoQcvduMdriz4ag=", + "lastModified": 1643634764, + "narHash": "sha256-EcFlgzZnZSHwZixELYV1pa267t+u5mCeLhSNBeAA/+c=", "owner": "mozilla", "repo": "nixpkgs-mozilla", - "rev": "7c1e8b1dd6ed0043fb4ee0b12b815256b0b9de6f", + "rev": "f233fdc4ff6ba2ffeb1e3e3cd6d63bb1297d6996", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1642522226, - "narHash": "sha256-m/j9U8KYuwwxjwgRCjmEj8ejftvdMLJ+NGXh/L2I4FU=", + "lastModified": 1643503720, + "narHash": "sha256-tJic20ufuRnG8V+fTCd3YU6xl1ImxNspoEkXHct0AG4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "610d4ea2750e064bf34b33fa38cb671edd893d3d", + "rev": "0f316e4d72daed659233817ffe52bf08e081b5de", "type": "github" }, "original": { From e45cb217be58beabdf460bc43329a0c94a99b7c0 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Thu, 3 Feb 2022 23:57:17 +0000 Subject: [PATCH 12/31] firmware: Explicitly use wrapping integer math in PRNGs Patch by Hannah McLaughlin; apparently, the overflow actually doesn't get checked/reported without `opt-level = 2` and `lto = "thin"`. --- artiq/firmware/bootloader/main.rs | 4 ++-- artiq/firmware/libboard_misoc/sdram.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index 9a41bdb4b..8c428bedb 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -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 diff --git a/artiq/firmware/libboard_misoc/sdram.rs b/artiq/firmware/libboard_misoc/sdram.rs index b3e112eb0..1227b411d 100644 --- a/artiq/firmware/libboard_misoc/sdram.rs +++ b/artiq/firmware/libboard_misoc/sdram.rs @@ -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; } From 798a412c6f8e81b1c07a88248d08f617aa1fb724 Mon Sep 17 00:00:00 2001 From: Steve Fan <19037626d@connect.polyu.hk> Date: Fri, 4 Feb 2022 13:51:19 +0800 Subject: [PATCH 13/31] comm_moninj: set keepalive for socket (#1843) --- artiq/coredevice/comm_moninj.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/comm_moninj.py b/artiq/coredevice/comm_moninj.py index 392ad701a..f03e91bbc 100644 --- a/artiq/coredevice/comm_moninj.py +++ b/artiq/coredevice/comm_moninj.py @@ -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 From a74196aa27d90e6076fa2ca88b4320c08981d9de Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 7 Feb 2022 13:31:47 +0800 Subject: [PATCH 14/31] mirny: allow set attenuation with dB --- artiq/coredevice/adf5356.py | 12 ++++++++++++ artiq/coredevice/mirny.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/adf5356.py b/artiq/coredevice/adf5356.py index 845067541..f20ead804 100644 --- a/artiq/coredevice/adf5356.py +++ b/artiq/coredevice/adf5356.py @@ -102,6 +102,18 @@ class ADF5356: else: self.sync() + @kernel + def set_att(self, att): + """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): """Set digital step attenuator in machine units. diff --git a/artiq/coredevice/mirny.py b/artiq/coredevice/mirny.py index 70daf1f61..b3643bbf1 100644 --- a/artiq/coredevice/mirny.py +++ b/artiq/coredevice/mirny.py @@ -1,7 +1,7 @@ """RTIO driver for Mirny (4 channel GHz PLLs) """ -from artiq.language.core import kernel, delay +from artiq.language.core import kernel, delay, portable from artiq.language.units import us from numpy import int32 @@ -132,6 +132,18 @@ class Mirny: self.write_reg(1, (self.clk_sel << 4)) delay(1000 * us) + @portable(flags={"fast-math"}) + def att_to_mu(self, att): + """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, att): """Set digital step attenuator in machine units. @@ -141,6 +153,21 @@ class Mirny: self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 16, SPIT_WR, SPI_CS) self.bus.write(((channel | 8) << 25) | (att << 16)) + @kernel + def set_att(self, channel, att): + """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, length, data, ext_div=SPIT_WR): """Perform SPI write to a prefixed address""" From d5eec652eefe09fed731feda4598059cc6c655e4 Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 7 Feb 2022 13:36:23 +0800 Subject: [PATCH 15/31] tester: specify att with dB --- artiq/frontend/artiq_sinara_tester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/frontend/artiq_sinara_tester.py b/artiq/frontend/artiq_sinara_tester.py index 31f947631..a4f999029 100755 --- a/artiq/frontend/artiq_sinara_tester.py +++ b/artiq/frontend/artiq_sinara_tester.py @@ -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() From 833acb6925c14af05da6961b018821d7f8af940b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 7 Feb 2022 14:28:00 +0800 Subject: [PATCH 16/31] add AFWS client --- MANIFEST.in | 1 + artiq/afws.pem | 23 +++++ artiq/frontend/afws_client.py | 166 ++++++++++++++++++++++++++++++++++ setup.py | 1 + 4 files changed, 191 insertions(+) create mode 100644 artiq/afws.pem create mode 100755 artiq/frontend/afws_client.py diff --git a/MANIFEST.in b/MANIFEST.in index 27aa85a93..0c094f0eb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -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 diff --git a/artiq/afws.pem b/artiq/afws.pem new file mode 100644 index 000000000..cebce3e97 --- /dev/null +++ b/artiq/afws.pem @@ -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----- diff --git a/artiq/frontend/afws_client.py b/artiq/frontend/afws_client.py new file mode 100755 index 000000000..e16776123 --- /dev/null +++ b/artiq/frontend/afws_client.py @@ -0,0 +1,166 @@ +#!/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 + version = artiq.__version__ + if version.endswith(".beta"): + version = version[:-5] + version = version.split(".") + if len(version) != 3: + return None + major, minor, rev = version + return 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) + 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: + if any(os.scandir(args.directory)): + print("Output directory already exists and is not empty. 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() diff --git a/setup.py b/setup.py index d888e9402..cefeb6418 100755 --- a/setup.py +++ b/setup.py @@ -33,6 +33,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 = [ From cc69482dad1b756bd1ba7ea3339961c0df3abac7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Feb 2022 21:05:39 +0800 Subject: [PATCH 17/31] afws: nix requires full Git commit hash --- artiq/frontend/afws_client.py | 9 +-------- flake.nix | 7 ++++++- versioneer.py | 18 ++++++++++++++---- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/artiq/frontend/afws_client.py b/artiq/frontend/afws_client.py index e16776123..f88fcada1 100755 --- a/artiq/frontend/afws_client.py +++ b/artiq/frontend/afws_client.py @@ -26,14 +26,7 @@ def get_artiq_rev(): import artiq except ImportError: return None - version = artiq.__version__ - if version.endswith(".beta"): - version = version[:-5] - version = version.split(".") - if len(version) != 3: - return None - major, minor, rev = version - return rev + return artiq._version.get_rev() def zip_unarchive(data, directory): diff --git a/flake.nix b/flake.nix index 7d21236bf..cfe9646c8 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,7 @@ artiqVersionMinor = self.sourceInfo.revCount or 0; artiqVersionId = self.sourceInfo.shortRev or "unknown"; 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"; @@ -148,7 +149,11 @@ 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 llvmlite diff --git a/versioneer.py b/versioneer.py index 93f1c661d..dd191d875 100644 --- a/versioneer.py +++ b/versioneer.py @@ -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 From b6f5ba8b5b32fad29a54aa37e114cfc707027bd4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Feb 2022 21:26:12 +0800 Subject: [PATCH 18/31] afws_client: improve error message when output already exists --- artiq/frontend/afws_client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/artiq/frontend/afws_client.py b/artiq/frontend/afws_client.py index f88fcada1..88bd8cdd3 100755 --- a/artiq/frontend/afws_client.py +++ b/artiq/frontend/afws_client.py @@ -132,8 +132,12 @@ def main(): try: os.mkdir(args.directory) except FileExistsError: - if any(os.scandir(args.directory)): - print("Output directory already exists and is not empty. Please remove it and try again.") + 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: From b893d97d7b778a3e8c6e7f8d4cd39ba63e6a7859 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Feb 2022 21:52:48 +0800 Subject: [PATCH 19/31] afws_client: add login successful message --- artiq/frontend/afws_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/frontend/afws_client.py b/artiq/frontend/afws_client.py index 88bd8cdd3..7d9a78265 100755 --- a/artiq/frontend/afws_client.py +++ b/artiq/frontend/afws_client.py @@ -117,6 +117,7 @@ def main(): 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: ") From 03a367f565cb557cb1ddc3614f3ec7d581f38e72 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 9 Feb 2022 10:41:30 +0800 Subject: [PATCH 20/31] flake: export more packages --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index cfe9646c8..59162f34b 100644 --- a/flake.nix +++ b/flake.nix @@ -355,7 +355,7 @@ }; in rec { packages.x86_64-linux = rec { - inherit migen misoc vivadoEnv vivado openocd-bscanspi artiq; + inherit sipyco pythonparser qasync migen misoc asyncserial microscope vivadoEnv vivado openocd-bscanspi artiq; artiq-board-kc705-nist_clock = makeArtiqBoardPackage { target = "kc705"; variant = "nist_clock"; From a0070d4396136b84e0a50a1c57ee72bcf4e7f6f9 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 9 Feb 2022 10:53:52 +0800 Subject: [PATCH 21/31] flake: add docs dependencies --- flake.nix | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 59162f34b..f632892ce 100644 --- a/flake.nix +++ b/flake.nix @@ -353,9 +353,28 @@ 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 = with pkgs.python3Packages; [ wavedrom sphinx xcffib cairosvg ]; + }; + artiq-manual-latex = 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 sipyco pythonparser qasync migen misoc asyncserial microscope vivadoEnv vivado openocd-bscanspi artiq; + inherit migen misoc asyncserial microscope vivadoEnv vivado; + inherit sipyco pythonparser qasync openocd-bscanspi artiq; + inherit sphinxcontrib-wavedrom artiq-manual-latex; artiq-board-kc705-nist_clock = makeArtiqBoardPackage { target = "kc705"; variant = "nist_clock"; @@ -380,6 +399,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 artiq-manual-latex ]; }; From 0e178e40ac29ae9688073c5612e440423c3744fc Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 11 Feb 2022 14:23:56 +0800 Subject: [PATCH 22/31] RELEASE_NOTES: fix formatting --- RELEASE_NOTES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index fdb45ed7a..3cba6ae3e 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -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 From 034a0fdb35f20bf561fb6578d81077bf4ec99b4a Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 11 Feb 2022 14:24:41 +0800 Subject: [PATCH 23/31] flake: install recommended wavedrom-cli. Closes #1845 --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f632892ce..3b12632b2 100644 --- a/flake.nix +++ b/flake.nix @@ -362,7 +362,7 @@ sha256 = "sha256-ukZd3ajt0Sx3LByof4R80S31F5t1yo+L8QUADrMMm2A="; }; buildInputs = [ pkgs.python3Packages.setuptools_scm ]; - propagatedBuildInputs = with pkgs.python3Packages; [ wavedrom sphinx xcffib cairosvg ]; + propagatedBuildInputs = [ pkgs.nodejs pkgs.nodePackages.wavedrom-cli ] ++ (with pkgs.python3Packages; [ wavedrom sphinx xcffib cairosvg ]); }; artiq-manual-latex = pkgs.texlive.combine { inherit (pkgs.texlive) From 9e37fb95d62cedf1f08eecb13ac190cd8d405921 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 11 Feb 2022 14:25:10 +0800 Subject: [PATCH 24/31] manual: use recommended contents caption --- doc/manual/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/manual/index.rst b/doc/manual/index.rst index c7ecde30f..76b0b8f61 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -1,9 +1,8 @@ ARTIQ documentation =================== -Contents: - .. toctree:: + :caption: Contents :maxdepth: 2 introduction From bbfa926fa66f647e1942fc34d4a69e6de2176fb7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 11 Feb 2022 14:36:18 +0800 Subject: [PATCH 25/31] flake: add documentation outputs --- flake.nix | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index 3b12632b2..c827f19e9 100644 --- a/flake.nix +++ b/flake.nix @@ -364,21 +364,65 @@ buildInputs = [ pkgs.python3Packages.setuptools_scm ]; propagatedBuildInputs = [ pkgs.nodejs pkgs.nodePackages.wavedrom-cli ] ++ (with pkgs.python3Packages; [ wavedrom sphinx xcffib cairosvg ]); }; - artiq-manual-latex = pkgs.texlive.combine { + 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 asyncserial microscope vivadoEnv vivado; + packages.x86_64-linux = { inherit sipyco pythonparser qasync openocd-bscanspi artiq; - inherit sphinxcontrib-wavedrom artiq-manual-latex; + inherit migen misoc asyncserial microscope vivadoEnv vivado; artiq-board-kc705-nist_clock = makeArtiqBoardPackage { target = "kc705"; variant = "nist_clock"; }; + 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 + ]; + preBuild = '' + ''; + 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-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; @@ -400,7 +444,7 @@ 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 artiq-manual-latex + pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom latex-artiq-manual ]; }; @@ -458,6 +502,7 @@ touch $out ''; }; + inherit (packages.x86_64-linux) artiq-manual-html artiq-manual-pdf; }; }; From 0649e69d94ec4f22c895e9e8ce396098bb36d29d Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 12 Feb 2022 10:24:45 +0800 Subject: [PATCH 26/31] flake: cleanup --- flake.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index c827f19e9..3c6a1ae7e 100644 --- a/flake.nix +++ b/flake.nix @@ -387,11 +387,9 @@ pkgs.python3Packages.sphinx pkgs.python3Packages.sphinx_rtd_theme pkgs.python3Packages.sphinx-argparse sphinxcontrib-wavedrom ]; - preBuild = '' - ''; buildPhase = '' export VERSIONEER_OVERRIDE=${artiqVersion} - export SOURCE_DATE_EPOCH=${builtins.toString self.sourceInfo.lastModified} + export SOURCE_DATE_EPOCH=${builtins.toString self.sourceInfo.lastModified} cd doc/manual make html ''; From 8433cc6731182b1f19272ea04b3a5a27d3bacae7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 12 Feb 2022 10:59:10 +0800 Subject: [PATCH 27/31] flake: use sipyco flake --- flake.lock | 40 ++++++++++++++++++++++------------------ flake.nix | 15 +++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/flake.lock b/flake.lock index 3fc2fe6b4..3e80c3957 100644 --- a/flake.lock +++ b/flake.lock @@ -36,10 +36,30 @@ "inputs": { "mozilla-overlay": "mozilla-overlay", "nixpkgs": "nixpkgs", + "sipyco": "sipyco", "src-migen": "src-migen", "src-misoc": "src-misoc", - "src-pythonparser": "src-pythonparser", - "src-sipyco": "src-sipyco" + "src-pythonparser": "src-pythonparser" + } + }, + "sipyco": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1644634510, + "narHash": "sha256-5p88NKK/DTdb0F0+/7uObBohthdxHWLl782AaiPRf8E=", + "owner": "m-labs", + "repo": "sipyco", + "rev": "e9e69527927761544ab6d62f751e1ff512d1e661", + "type": "github" + }, + "original": { + "owner": "m-labs", + "repo": "sipyco", + "type": "github" } }, "src-migen": { @@ -91,22 +111,6 @@ "repo": "pythonparser", "type": "github" } - }, - "src-sipyco": { - "flake": false, - "locked": { - "lastModified": 1641866796, - "narHash": "sha256-TSH0IgNbi9IcMcBDb2nWRphKlxstbWeATjrGbi6K2m0=", - "owner": "m-labs", - "repo": "sipyco", - "rev": "b04234c49379cd446d4cb3346d4741868d86841a", - "type": "github" - }, - "original": { - "owner": "m-labs", - "repo": "sipyco", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 3c6a1ae7e..dcea33514 100644 --- a/flake.nix +++ b/flake.nix @@ -3,13 +3,14 @@ inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.11; inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; }; - inputs.src-sipyco = { url = github:m-labs/sipyco; flake = false; }; + inputs.sipyco.url = github:m-labs/sipyco; + inputs.sipyco.inputs.nixpkgs.follows = "nixpkgs"; inputs.src-pythonparser = { url = github:m-labs/pythonparser; flake = false; }; 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, nixpkgs, mozilla-overlay, src-sipyco, src-pythonparser, src-migen, src-misoc }: + outputs = { self, nixpkgs, mozilla-overlay, sipyco, src-pythonparser, src-migen, src-misoc }: let pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; }; @@ -53,12 +54,6 @@ fontconfig ]; - sipyco = pkgs.python3Packages.buildPythonPackage { - name = "sipyco"; - src = src-sipyco; - propagatedBuildInputs = with pkgs.python3Packages; [ pybase64 numpy ]; - }; - pythonparser = pkgs.python3Packages.buildPythonPackage { name = "pythonparser"; src = src-pythonparser; @@ -157,7 +152,7 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite - propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 llvmlite-new sipyco pythonparser ] + propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 llvmlite-new sipyco.packages.x86_64-linux.sipyco pythonparser ] ++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial python-Levenshtein h5py pyqt5 qasync ]); dontWrapQtApps = true; @@ -372,7 +367,7 @@ }; in rec { packages.x86_64-linux = { - inherit sipyco pythonparser qasync openocd-bscanspi artiq; + inherit pythonparser qasync openocd-bscanspi artiq; inherit migen misoc asyncserial microscope vivadoEnv vivado; artiq-board-kc705-nist_clock = makeArtiqBoardPackage { target = "kc705"; From b3b73948a256e2d738f05948671dacbd55dcf557 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 12 Feb 2022 11:04:41 +0800 Subject: [PATCH 28/31] flake: update dependencies --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 3e80c3957..70c4759f6 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1643503720, - "narHash": "sha256-tJic20ufuRnG8V+fTCd3YU6xl1ImxNspoEkXHct0AG4=", + "lastModified": 1644472683, + "narHash": "sha256-sP6iM4NksOYO6NFfTJ96cg+ClPnq6cdY30xKA1iYtyU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0f316e4d72daed659233817ffe52bf08e081b5de", + "rev": "7adc9c14ec74b27358a8df9b973087e351425a79", "type": "github" }, "original": { From 5a8928fbf351fcc252389710e9a9c588ea9aa0f2 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 12 Feb 2022 17:48:35 +0800 Subject: [PATCH 29/31] flake: set pythonparser version --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index dcea33514..55e84d1dd 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,8 @@ ]; pythonparser = pkgs.python3Packages.buildPythonPackage { - name = "pythonparser"; + pname = "pythonparser"; + version = "1.4"; src = src-pythonparser; doCheck = false; propagatedBuildInputs = with pkgs.python3Packages; [ regex ]; From 6dfc854673eeba66f7c59be14f7f262f3a779acd Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 13 Feb 2022 17:15:25 +0800 Subject: [PATCH 30/31] flake: install artiq-comtools --- flake.lock | 30 +++++++++++++++++++++++++++--- flake.nix | 7 +++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index 70c4759f6..38f992b86 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,28 @@ { "nodes": { + "artiq-comtools": { + "inputs": { + "nixpkgs": [ + "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": { @@ -34,6 +57,7 @@ }, "root": { "inputs": { + "artiq-comtools": "artiq-comtools", "mozilla-overlay": "mozilla-overlay", "nixpkgs": "nixpkgs", "sipyco": "sipyco", @@ -49,11 +73,11 @@ ] }, "locked": { - "lastModified": 1644634510, - "narHash": "sha256-5p88NKK/DTdb0F0+/7uObBohthdxHWLl782AaiPRf8E=", + "lastModified": 1644649772, + "narHash": "sha256-LE9L5bDSunCPEnuf5Ed8enTAXA2vkTSmjvqPX9ILO0Y=", "owner": "m-labs", "repo": "sipyco", - "rev": "e9e69527927761544ab6d62f751e1ff512d1e661", + "rev": "8e4382352bc64bd01c9db35d9c9b0ef42b8b9d3b", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 55e84d1dd..dd9c23457 100644 --- a/flake.nix +++ b/flake.nix @@ -6,11 +6,14 @@ inputs.sipyco.url = github:m-labs/sipyco; inputs.sipyco.inputs.nixpkgs.follows = "nixpkgs"; inputs.src-pythonparser = { url = github:m-labs/pythonparser; flake = false; }; + inputs.artiq-comtools.url = github:m-labs/artiq-comtools; + inputs.artiq-comtools.inputs.nixpkgs.follows = "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, nixpkgs, mozilla-overlay, sipyco, src-pythonparser, src-migen, src-misoc }: + outputs = { self, nixpkgs, mozilla-overlay, sipyco, src-pythonparser, artiq-comtools, src-migen, src-misoc }: let pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; }; @@ -153,7 +156,7 @@ nativeBuildInputs = [ pkgs.qt5.wrapQtAppsHook ]; # keep llvm_x and lld_x in sync with llvmlite - propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 llvmlite-new sipyco.packages.x86_64-linux.sipyco pythonparser ] + propagatedBuildInputs = [ pkgs.llvm_11 pkgs.lld_11 llvmlite-new sipyco.packages.x86_64-linux.sipyco pythonparser artiq-comtools.packages.x86_64-linux.artiq-comtools ] ++ (with pkgs.python3Packages; [ pyqtgraph pygit2 numpy dateutil scipy prettytable pyserial python-Levenshtein h5py pyqt5 qasync ]); dontWrapQtApps = true; From 65eab31f2343dd120e81ddc315e16dc62da41391 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 14 Feb 2022 15:54:17 +0800 Subject: [PATCH 31/31] simplify board package format and artiq_flash --- artiq/frontend/artiq_flash.py | 80 +++++++++-------------------------- flake.nix | 10 ++--- 2 files changed, 25 insertions(+), 65 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index 62b8dc39c..12475cf0b 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -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() diff --git a/flake.nix b/flake.nix index dd9c23457..502b8ecbe 100644 --- a/flake.nix +++ b/flake.nix @@ -260,7 +260,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 { @@ -299,7 +299,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 ] @@ -312,7 +312,7 @@ ''; # don't mangle ELF files as they are not for NixOS dontFixup = true; - }); + }; openocd-bscanspi = let bscan_spi_bitstreams-pkg = pkgs.stdenv.mkDerivation { @@ -454,7 +454,7 @@ __networked = true; buildInputs = [ - (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ artiq artiq-board-kc705-nist_clock ps.paramiko ])) + (pkgs.python3.withPackages(ps: with packages.x86_64-linux; [ artiq ps.paramiko ])) pkgs.llvm_11 pkgs.lld_11 pkgs.openssh @@ -488,7 +488,7 @@ # Read "Ok" line when remote successfully locked read LOCK_OK - artiq_flash -t kc705 -H rpi-1 + artiq_flash -t kc705 -H rpi-1 -d ${packages.x86_64-linux.artiq-board-kc705-nist_clock} sleep 15 export ARTIQ_ROOT=`python -c "import artiq; print(artiq.__path__[0])"`/examples/kc705_nist_clock