From ed4d57c6385c7ae7c739b0d88643ccdb2a6976b4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 29 Oct 2016 21:17:13 +0800 Subject: [PATCH 01/47] use new Migen signal attribute API --- artiq/gateware/rtio/core.py | 6 ++---- artiq/gateware/targets/kc705.py | 11 ++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 69babebac..a248632da 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -24,10 +24,8 @@ class _GrayCodeTransfer(Module): self.sync.rtio += value_gray_rtio.eq(self.i ^ self.i[1:]) # transfer to system clock domain value_gray_sys = Signal(width) - self.specials += [ - NoRetiming(value_gray_rtio), - MultiReg(value_gray_rtio, value_gray_sys) - ] + value_gray_rtio.attr.add("no_retiming") + self.specials += MultiReg(value_gray_rtio, value_gray_sys) # convert back to binary value_sys = Signal(width) self.comb += value_sys[-1].eq(value_gray_sys[-1]) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 7209a2b3b..960623e8f 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -8,7 +8,6 @@ from migen.genlib.cdc import MultiReg from migen.build.generic_platform import * from migen.build.xilinx.vivado import XilinxVivadoToolchain from migen.build.xilinx.ise import XilinxISEToolchain -from migen.fhdl.specials import Keep from misoc.interconnect.csr import * from misoc.interconnect import wishbone @@ -147,12 +146,10 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.csr_devices.append("rtio_moninj") - self.specials += [ - Keep(self.rtio.cd_rsys.clk), - Keep(self.rtio_crg.cd_rtio.clk), - Keep(self.ethphy.crg.cd_eth_rx.clk), - Keep(self.ethphy.crg.cd_eth_tx.clk), - ] + self.rtio.cd_rsys.clk.attr.add("keep") + self.rtio_crg.cd_rtio.clk.attr.add("keep") + self.ethphy.crg.cd_eth_rx.clk.attr.add("keep") + self.ethphy.crg.cd_eth_tx.clk.attr.add("keep") self.platform.add_period_constraint(self.rtio.cd_rsys.clk, 8.) self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) From c656a5353247534f69f1aa397a4ddc48a023cf12 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 29 Oct 2016 21:28:01 +0800 Subject: [PATCH 02/47] kc705: clean up clock constraints --- artiq/gateware/targets/kc705.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 960623e8f..4e95bc604 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -146,20 +146,11 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.csr_devices.append("rtio_moninj") - self.rtio.cd_rsys.clk.attr.add("keep") self.rtio_crg.cd_rtio.clk.attr.add("keep") - self.ethphy.crg.cd_eth_rx.clk.attr.add("keep") - self.ethphy.crg.cd_eth_tx.clk.attr.add("keep") - - self.platform.add_period_constraint(self.rtio.cd_rsys.clk, 8.) self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) - self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 8.) - self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.) self.platform.add_false_path_constraints( self.rtio.cd_rsys.clk, - self.rtio_crg.cd_rtio.clk, - self.ethphy.crg.cd_eth_rx.clk, - self.ethphy.crg.cd_eth_tx.clk) + self.rtio_crg.cd_rtio.clk) self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, self.get_native_sdram_if()) From 2ac85cd40fe08a422f460792677d29c7798fd54b Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 29 Oct 2016 21:34:25 +0000 Subject: [PATCH 03/47] runtime: implement prototype background RPCs. --- artiq/gateware/amp/kernel_cpu.py | 2 +- artiq/runtime.rs/Cargo.toml | 2 +- artiq/runtime.rs/libksupport/Cargo.lock | 22 +++++++ artiq/runtime.rs/libksupport/Cargo.toml | 4 ++ artiq/runtime.rs/libksupport/api.rs | 1 + artiq/runtime.rs/libksupport/dyld.rs | 1 - artiq/runtime.rs/libksupport/lib.rs | 58 ++++++++++++++++-- artiq/runtime.rs/libstd_artiq/lib.rs | 51 +--------------- artiq/runtime.rs/src/kernel_proto.rs | 6 +- artiq/runtime.rs/src/lib.rs | 54 +++++++++++++++- artiq/runtime.rs/src/{rpc.rs => rpc_proto.rs} | 14 +++-- artiq/runtime.rs/src/rpc_queue.rs | 61 +++++++++++++++++++ artiq/runtime.rs/src/session.rs | 41 +++++++++---- artiq/runtime.rs/src/session_proto.rs | 5 +- artiq/runtime/Makefile | 3 + artiq/runtime/ksupport.ld | 6 +- artiq/runtime/ksupport_glue.c | 4 +- artiq/runtime/runtime.ld | 4 +- 18 files changed, 252 insertions(+), 87 deletions(-) rename artiq/runtime.rs/src/{rpc.rs => rpc_proto.rs} (97%) create mode 100644 artiq/runtime.rs/src/rpc_queue.rs diff --git a/artiq/gateware/amp/kernel_cpu.py b/artiq/gateware/amp/kernel_cpu.py index 2736027e3..6aca00d32 100644 --- a/artiq/gateware/amp/kernel_cpu.py +++ b/artiq/gateware/amp/kernel_cpu.py @@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder class KernelCPU(Module): def __init__(self, platform, - exec_address=0x40400000, + exec_address=0x40800000, main_mem_origin=0x40000000, l2_size=8192): self._reset = CSRStorage(reset=1) diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 29d415a15..1355f7a4e 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -16,7 +16,7 @@ path = "src/lib.rs" std_artiq = { path = "libstd_artiq" } lwip = { path = "liblwip", default-features = false } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } -log = { version = "0.3", default-features = false, features = ["max_level_debug"] } +log = { version = "0.3", default-features = false, features = [] } log_buffer = { version = "1.0" } byteorder = { version = "0.5", default-features = false } diff --git a/artiq/runtime.rs/libksupport/Cargo.lock b/artiq/runtime.rs/libksupport/Cargo.lock index da7d2b081..df5a6a1bb 100644 --- a/artiq/runtime.rs/libksupport/Cargo.lock +++ b/artiq/runtime.rs/libksupport/Cargo.lock @@ -1,4 +1,26 @@ [root] name = "ksupport" version = "0.0.0" +dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "std_artiq 0.0.0", +] +[[package]] +name = "alloc_artiq" +version = "0.0.0" + +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "std_artiq" +version = "0.0.0" +dependencies = [ + "alloc_artiq 0.0.0", +] + +[metadata] +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" diff --git a/artiq/runtime.rs/libksupport/Cargo.toml b/artiq/runtime.rs/libksupport/Cargo.toml index e5a29ff08..9ba7d5f65 100644 --- a/artiq/runtime.rs/libksupport/Cargo.toml +++ b/artiq/runtime.rs/libksupport/Cargo.toml @@ -8,6 +8,10 @@ name = "ksupport" path = "lib.rs" crate-type = ["staticlib"] +[dependencies] +std_artiq = { path = "../libstd_artiq" } +byteorder = { version = "0.5", default-features = false } + [profile.dev] panic = 'unwind' opt-level = 2 diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index 46570feab..aa6483bbd 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -91,6 +91,7 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(watchdog_clear = ::watchdog_clear), api!(send_rpc = ::send_rpc), + api!(send_async_rpc = ::send_async_rpc), api!(recv_rpc = ::recv_rpc), api!(cache_get = ::cache_get), diff --git a/artiq/runtime.rs/libksupport/dyld.rs b/artiq/runtime.rs/libksupport/dyld.rs index c25f7daea..c73653a19 100644 --- a/artiq/runtime.rs/libksupport/dyld.rs +++ b/artiq/runtime.rs/libksupport/dyld.rs @@ -1,5 +1,4 @@ use core::{ptr, slice, str}; -use core::slice::SliceExt; use libc::{c_void, c_char, c_int, size_t}; #[allow(non_camel_case_types)] diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index 5b12c360e..53e62f4e5 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -1,25 +1,51 @@ -#![feature(lang_items, needs_panic_runtime, asm, libc, core_slice_ext)] +#![feature(lang_items, needs_panic_runtime, asm, libc, stmt_expr_attributes)] #![no_std] #![needs_panic_runtime] +#[macro_use] +extern crate std_artiq as std; extern crate libc; +extern crate byteorder; #[path = "../src/board.rs"] mod board; #[path = "../src/mailbox.rs"] mod mailbox; +#[path = "../src/rpc_queue.rs"] +mod rpc_queue; + +#[path = "../src/proto.rs"] +mod proto; #[path = "../src/kernel_proto.rs"] mod kernel_proto; +#[path = "../src/rpc_proto.rs"] +mod rpc_proto; mod dyld; mod api; use core::{mem, ptr, slice, str}; +use std::io::Cursor; use libc::{c_char, size_t}; use kernel_proto::*; use dyld::Library; +#[no_mangle] +pub extern "C" fn malloc(_size: usize) -> *mut libc::c_void { + unimplemented!() +} + +#[no_mangle] +pub extern "C" fn realloc(_ptr: *mut libc::c_void, _size: usize) -> *mut libc::c_void { + unimplemented!() +} + +#[no_mangle] +pub extern "C" fn free(_ptr: *mut libc::c_void) { + unimplemented!() +} + fn send(request: &Message) { unsafe { mailbox::send(request as *const _ as usize) } while !mailbox::acknowledged() {} @@ -81,13 +107,37 @@ extern fn send_rpc(service: u32, tag: *const u8, data: *const *const ()) { let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) }; send(&RpcSend { - service: service as u32, - batch: service == 0, + async: false, + service: service, tag: tag, data: data }) } +extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) { + extern { fn strlen(s: *const c_char) -> size_t; } + let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) }; + + while rpc_queue::full() {} + rpc_queue::enqueue(|mut slice| { + let length = { + let mut writer = Cursor::new(&mut slice[4..]); + try!(rpc_proto::send_args(&mut writer, service, tag, data)); + writer.position() + }; + proto::write_u32(&mut slice, length as u32) + }).unwrap_or_else(|err| { + assert!(err.kind() == std::io::ErrorKind::UnexpectedEof); + + send(&RpcSend { + async: true, + service: service, + tag: tag, + data: data + }) + }) +} + extern fn recv_rpc(slot: *mut ()) -> usize { send(&RpcRecvRequest(slot)); recv!(&RpcRecvReply(ref result) => { @@ -206,7 +256,7 @@ unsafe fn attribute_writeback(typeinfo: *const ()) { attributes = attributes.offset(1); if !(*attribute).tag.is_null() { - send_rpc(0, (*attribute).tag, [ + send_async_rpc(0, (*attribute).tag, [ &object as *const _ as *const (), &(*attribute).name as *const _ as *const (), (object as usize + (*attribute).offset) as *const () diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 568dc8663..dafffbc9f 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -8,7 +8,7 @@ extern crate rustc_unicode; extern crate alloc_artiq; extern crate alloc; #[macro_use] -#[macro_reexport(vec)] +#[macro_reexport(vec, format)] extern crate collections; extern crate libc; @@ -31,52 +31,3 @@ pub mod prelude { pub mod error; pub mod io; - -use core::fmt::Write; - -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); -} - -#[macro_export] -macro_rules! println { - ($fmt:expr) => (print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); -} - -extern { - fn putchar(c: libc::c_int) -> libc::c_int; - fn readchar() -> libc::c_char; -} - -pub struct Console; - -impl core::fmt::Write for Console { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - for c in s.bytes() { unsafe { putchar(c as i32); } } - Ok(()) - } -} - -pub fn print_fmt(args: self::core::fmt::Arguments) { - let _ = Console.write_fmt(args); -} - -#[lang = "panic_fmt"] -extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { - let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args); - let _ = write!(Console, "waiting for debugger...\n"); - unsafe { - let _ = readchar(); - loop { asm!("l.trap 0") } - } -} - -// Allow linking with crates that are built as -Cpanic=unwind even when the root crate -// is built with -Cpanic=abort. -#[allow(non_snake_case)] -#[no_mangle] -pub extern "C" fn _Unwind_Resume() -> ! { - loop {} -} diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index a75875738..d98be19f8 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -3,8 +3,8 @@ use core::marker::PhantomData; use core::fmt; -pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000; -pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40440000; +pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800080; +pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40840000; pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; pub const KSUPPORT_HEADER_SIZE: usize = 0x80; @@ -42,8 +42,8 @@ pub enum Message<'a> { WatchdogClear { id: usize }, RpcSend { + async: bool, service: u32, - batch: bool, tag: &'a [u8], data: *const *const () }, diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 278b738e6..e661c427f 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm)] +#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm, + lang_items)] #[macro_use] extern crate std_artiq as std; @@ -11,13 +12,54 @@ extern crate byteorder; extern crate fringe; extern crate lwip; +use core::fmt::Write; use logger::BufferLogger; +extern { + fn putchar(c: libc::c_int) -> libc::c_int; + fn readchar() -> libc::c_char; +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +pub struct Console; + +impl core::fmt::Write for Console { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + for c in s.bytes() { unsafe { putchar(c as i32); } } + Ok(()) + } +} + +pub fn print_fmt(args: self::core::fmt::Arguments) { + let _ = Console.write_fmt(args); +} + +#[lang = "panic_fmt"] +extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { + let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args); + let _ = write!(Console, "waiting for debugger...\n"); + unsafe { + let _ = readchar(); + loop { asm!("l.trap 0") } + } +} + mod board; mod config; mod clock; mod rtio_crg; mod mailbox; +mod rpc_queue; mod urc; mod sched; @@ -29,9 +71,9 @@ mod kernel_proto; mod session_proto; mod moninj_proto; mod analyzer_proto; +mod rpc_proto; mod kernel; -mod rpc; mod session; mod moninj; #[cfg(has_rtio_analyzer)] @@ -44,6 +86,14 @@ extern { include!(concat!(env!("OUT_DIR"), "/git_info.rs")); +// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort. +// This is never called. +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn _Unwind_Resume() -> ! { + loop {} +} + #[no_mangle] pub unsafe extern fn rust_main() { static mut LOG_BUFFER: [u8; 4096] = [0; 4096]; diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc_proto.rs similarity index 97% rename from artiq/runtime.rs/src/rpc.rs rename to artiq/runtime.rs/src/rpc_proto.rs index 21b966dc9..d302d389d 100644 --- a/artiq/runtime.rs/src/rpc.rs +++ b/artiq/runtime.rs/src/rpc_proto.rs @@ -1,5 +1,7 @@ -use std::slice; -use std::io::{self, Read, Write, BufWriter}; +#![allow(dead_code)] + +use core::slice; +use std::io::{self, Read, Write}; use proto::*; use self::tag::{Tag, TagIterator, split_tag}; @@ -74,6 +76,7 @@ unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (), pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (), alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> { let mut it = TagIterator::new(tag_bytes); + #[cfg(not(ksupport))] trace!("recv ...->{}", it); let tag = it.next().expect("truncated tag"); @@ -98,7 +101,6 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: }) } - let writer = &mut BufWriter::new(writer); try!(write_u8(writer, tag.as_u8())); match tag { Tag::None => Ok(()), @@ -161,14 +163,16 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: } } -pub fn send_args(writer: &mut Write, tag_bytes: &[u8], +pub fn send_args(writer: &mut Write, service: u32, tag_bytes: &[u8], data: *const *const ()) -> io::Result<()> { let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes); let mut args_it = TagIterator::new(arg_tags_bytes); let return_it = TagIterator::new(return_tag_bytes); - trace!("send ({})->{}", args_it, return_it); + #[cfg(not(ksupport))] + trace!("send<{}>({})->{}", service, args_it, return_it); + try!(write_u32(writer, service)); for index in 0.. { if let Some(arg_tag) = args_it.next() { let mut data = unsafe { *data.offset(index) }; diff --git a/artiq/runtime.rs/src/rpc_queue.rs b/artiq/runtime.rs/src/rpc_queue.rs new file mode 100644 index 000000000..f0f89f144 --- /dev/null +++ b/artiq/runtime.rs/src/rpc_queue.rs @@ -0,0 +1,61 @@ +#![allow(dead_code)] + +use core::ptr::{read_volatile, write_volatile}; +use core::slice; +use board; + +const SEND_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 4) as *mut usize; +const RECV_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 8) as *mut usize; + +const QUEUE_BEGIN: usize = 0x40400000; +const QUEUE_END: usize = 0x40800000; +const QUEUE_CHUNK: usize = 0x1000; + +pub unsafe fn init() { + write_volatile(SEND_MAILBOX, QUEUE_BEGIN); + write_volatile(RECV_MAILBOX, QUEUE_END); +} + +fn next(mut addr: usize) -> usize { + debug_assert!(addr % QUEUE_CHUNK == 0); + debug_assert!(addr >= QUEUE_BEGIN && addr < QUEUE_END); + + addr += QUEUE_CHUNK; + if addr == QUEUE_END { addr = QUEUE_BEGIN } + addr +} + +pub fn empty() -> bool { + unsafe { read_volatile(SEND_MAILBOX) == read_volatile(RECV_MAILBOX) } +} + +pub fn full() -> bool { + unsafe { next(read_volatile(SEND_MAILBOX)) == read_volatile(RECV_MAILBOX) } +} + +pub fn enqueue(f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result { + debug_assert!(!full()); + + unsafe { + let slice = slice::from_raw_parts_mut(read_volatile(SEND_MAILBOX) as *mut u8, QUEUE_CHUNK); + f(slice).and_then(|x| { + write_volatile(SEND_MAILBOX, next(read_volatile(SEND_MAILBOX))); + Ok(x) + }) + } +} + +pub fn dequeue(f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result { + debug_assert!(!empty()); + + unsafe { + board::flush_cpu_dcache(); + let slice = slice::from_raw_parts_mut(read_volatile(RECV_MAILBOX) as *mut u8, QUEUE_CHUNK); + f(slice).and_then(|x| { + write_volatile(RECV_MAILBOX, next(read_volatile(RECV_MAILBOX))); + Ok(x) + }) + } +} diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 0c05f4a5c..dacb14d67 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -1,16 +1,16 @@ use std::prelude::v1::*; use std::{mem, str}; use std::cell::RefCell; -use std::fmt::Write; -use std::io::{self, Read}; -use {config, rtio_crg, clock, mailbox, kernel}; +use std::io::{self, Read, Write, BufWriter}; +use {config, rtio_crg, clock, mailbox, rpc_queue, kernel}; use logger::BufferLogger; use cache::Cache; use urc::Urc; use sched::{ThreadHandle, Waiter, Spawner}; use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; +use byteorder::{ByteOrder, NetworkEndian}; -use rpc; +use rpc_proto as rpc; use session_proto as host; use kernel_proto as kern; @@ -132,7 +132,8 @@ fn kern_recv_notrace(waiter: Waiter, f: F) -> io::Result where F: FnOnce(&kern::Message) -> io::Result { try!(waiter.until(|| mailbox::receive() != 0)); if !kernel::validate(mailbox::receive()) { - return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid kernel CPU pointer")) + let message = format!("invalid kernel CPU pointer 0x{:x}", mailbox::receive()); + return Err(io::Error::new(io::ErrorKind::InvalidData, message)) } f(unsafe { mem::transmute::(mailbox::receive()) }) @@ -352,6 +353,7 @@ fn process_kern_message(waiter: Waiter, kern_recv_dotrace(request); match request { &kern::Log(args) => { + use std::fmt::Write; try!(session.log_buffer.write_fmt(args) .map_err(|_| io_error("cannot append to session log buffer"))); session.flush_log_buffer(); @@ -383,15 +385,13 @@ fn process_kern_message(waiter: Waiter, kern_acknowledge() } - &kern::RpcSend { service, batch, tag, data } => { + &kern::RpcSend { async, service, tag, data } => { match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { - try!(host_write(stream, host::Reply::RpcRequest { - service: service - })); - try!(rpc::send_args(stream, tag, data)); - if !batch { + try!(host_write(stream, host::Reply::RpcRequest)); + try!(rpc::send_args(&mut BufWriter::new(stream), service, tag, data)); + if !async { session.kernel_state = KernelState::RpcWait } kern_acknowledge() @@ -465,12 +465,27 @@ fn process_kern_message(waiter: Waiter, }) } +fn process_kern_queued_rpc(stream: &mut TcpStream, + session: &mut Session) -> io::Result<()> { + rpc_queue::dequeue(|slice| { + trace!("comm<-kern (async RPC)"); + let length = NetworkEndian::read_u32(slice) as usize; + try!(host_write(stream, host::Reply::RpcRequest)); + try!(stream.write(&slice[4..][..length])); + Ok(()) + }) +} + fn host_kernel_worker(waiter: Waiter, stream: &mut TcpStream, congress: &mut Congress) -> io::Result<()> { let mut session = Session::new(congress); loop { + if !rpc_queue::empty() { + try!(process_kern_queued_rpc(stream, &mut session)) + } + if stream.readable() { try!(process_host_message(waiter, stream, &mut session)); } @@ -509,6 +524,10 @@ fn flash_kernel_worker(waiter: Waiter, try!(kern_run(&mut session)); loop { + if !rpc_queue::empty() { + return Err(io_error("unexpected background RPC in flash kernel")) + } + if mailbox::receive() != 0 { if try!(process_kern_message(waiter, None, &mut session)) { return Ok(()) diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index 7babdc35a..2ee656c35 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -107,7 +107,7 @@ pub enum Reply<'a> { backtrace: &'a [usize] }, - RpcRequest { service: u32 }, + RpcRequest, FlashRead(&'a [u8]), FlashOk, @@ -170,9 +170,8 @@ impl<'a> Reply<'a> { } }, - Reply::RpcRequest { service } => { + Reply::RpcRequest => { try!(write_u8(writer, 10)); - try!(write_u32(writer, service)); }, Reply::FlashRead(ref bytes) => { diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index c543b7d66..e8936fdf4 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,6 +7,8 @@ OBJECTS := flash_storage.o main.o OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug +CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b +export CORE_IO_COMMIT CFLAGS += \ -I$(LIBALLOC_DIRECTORY) \ @@ -54,6 +56,7 @@ $(RUSTOUT_DIRECTORY)/libksupport.a: --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \ --target=or1k-unknown-none -- \ $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ + --cfg ksupport \ -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ -L../libcompiler-rt diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index de2267304..1e0ed5040 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -6,10 +6,14 @@ INCLUDE generated/regions.ld /* First 4M of main memory are reserved for runtime * code/data/heap, then comes kernel memory. + * Next 4M of main memory are reserved for + * the background RPC queue. * First 256K of kernel memory are for support code. + * Support code is loaded at ORIGIN-0x80 so that ELF headers + * are also loaded. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x40400000, LENGTH = 0x40000 + ksupport (RWX) : ORIGIN = 0x40800080, LENGTH = 0x40000 } /* Kernel stack is at the end of main RAM. */ diff --git a/artiq/runtime/ksupport_glue.c b/artiq/runtime/ksupport_glue.c index 9de97b75d..2fa056f5e 100644 --- a/artiq/runtime/ksupport_glue.c +++ b/artiq/runtime/ksupport_glue.c @@ -8,8 +8,8 @@ void send_to_log(const char *ptr, size_t length); -#define KERNELCPU_EXEC_ADDRESS 0x40400000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x40440000 +#define KERNELCPU_EXEC_ADDRESS 0x40800080 +#define KERNELCPU_PAYLOAD_ADDRESS 0x40840000 #define KERNELCPU_LAST_ADDRESS 0x4fffffff #define KSUPPORT_HEADER_SIZE 0x80 diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index 5d3d00e08..42e7e2cd2 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -74,9 +74,7 @@ SECTIONS .heap : { _fheap = .; - . = ORIGIN(runtime) + LENGTH(runtime) - /* Leave room for ksupport headers. */ - - 0x1000; + . = ORIGIN(runtime) + LENGTH(runtime); _eheap = .; } > runtime From cd68577dbc450db74711466e50a0ae5d8cabf353 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 30 Oct 2016 00:40:26 +0000 Subject: [PATCH 04/47] compiler: add support for async RPCs. --- artiq/compiler/embedding.py | 24 ++++++++++++++-- artiq/compiler/prelude.py | 1 + .../compiler/transforms/asttyped_rewriter.py | 2 +- .../compiler/transforms/llvm_ir_generator.py | 16 +++++++++-- artiq/compiler/types.py | 15 ++++++---- artiq/language/core.py | 28 +++++++++++++++---- artiq/test/lit/embedding/async_rpc.py | 15 ++++++++++ .../lit/embedding/error_rpc_async_return.py | 15 ++++++++++ doc/manual/compiler.rst | 13 +++++++++ 9 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 artiq/test/lit/embedding/async_rpc.py create mode 100644 artiq/test/lit/embedding/error_rpc_async_return.py diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 7c6b7f617..4a5208c4a 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -994,8 +994,24 @@ class Stitcher: else: assert False + is_async = False + if hasattr(host_function, "artiq_embedded") and \ + "async" in host_function.artiq_embedded.flags: + is_async = True + + if not builtins.is_none(ret_type) and is_async: + note = diagnostic.Diagnostic("note", + "function called here", {}, + loc) + diag = diagnostic.Diagnostic("fatal", + "functions that return a value cannot be defined as async RPCs", {}, + self._function_loc(host_function.artiq_embedded.function), + notes=[note]) + self.engine.process(diag) + function_type = types.TRPC(ret_type, - service=self.embedding_map.store_object(host_function)) + service=self.embedding_map.store_object(host_function), + async=is_async) self.functions[function] = function_type return function_type @@ -1007,7 +1023,11 @@ class Stitcher: if function in self.functions: pass - elif not hasattr(host_function, "artiq_embedded"): + elif not hasattr(host_function, "artiq_embedded") or \ + (host_function.artiq_embedded.core_name is None and + host_function.artiq_embedded.portable is False and + host_function.artiq_embedded.syscall is None and + host_function.artiq_embedded.forbidden is False): self._quote_rpc(function, loc) elif host_function.artiq_embedded.function is not None: if host_function.__name__ == "": diff --git a/artiq/compiler/prelude.py b/artiq/compiler/prelude.py index 8c46176f9..e19ef6706 100644 --- a/artiq/compiler/prelude.py +++ b/artiq/compiler/prelude.py @@ -31,6 +31,7 @@ def globals(): # ARTIQ decorators "kernel": builtins.fn_kernel(), "portable": builtins.fn_kernel(), + "rpc": builtins.fn_kernel(), # ARTIQ context managers "parallel": builtins.obj_parallel(), diff --git a/artiq/compiler/transforms/asttyped_rewriter.py b/artiq/compiler/transforms/asttyped_rewriter.py index 199ea2ef9..3c840673e 100644 --- a/artiq/compiler/transforms/asttyped_rewriter.py +++ b/artiq/compiler/transforms/asttyped_rewriter.py @@ -509,7 +509,7 @@ class ASTTypedRewriter(algorithm.Transformer): visit_DictComp = visit_unsupported visit_Ellipsis = visit_unsupported visit_GeneratorExp = visit_unsupported - visit_Set = visit_unsupported + # visit_Set = visit_unsupported visit_SetComp = visit_unsupported visit_Starred = visit_unsupported visit_Yield = visit_unsupported diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index f935ef2b7..c755a43ee 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -351,6 +351,8 @@ class LLVMIRGenerator: llty = ll.FunctionType(lli32, [lldouble]) elif name == "send_rpc": llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) + elif name == "send_async_rpc": + llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) elif name == "recv_rpc": llty = ll.FunctionType(lli32, [llptr]) elif name == "now": @@ -366,7 +368,8 @@ class LLVMIRGenerator: llglobal = ll.Function(self.llmodule, llty, name) if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): llglobal.attributes.add("noreturn") - if name in ("rtio_log", "send_rpc", "watchdog_set", "watchdog_clear", + if name in ("rtio_log", "send_rpc", "send_async_rpc", + "watchdog_set", "watchdog_clear", self.target.print_function): llglobal.attributes.add("nounwind") else: @@ -1248,12 +1251,19 @@ class LLVMIRGenerator: llargptr = self.llbuilder.gep(llargs, [ll.Constant(lli32, index)]) self.llbuilder.store(llargslot, llargptr) - self.llbuilder.call(self.llbuiltin("send_rpc"), - [llservice, lltag, llargs]) + if fun_type.async: + self.llbuilder.call(self.llbuiltin("send_async_rpc"), + [llservice, lltag, llargs]) + else: + self.llbuilder.call(self.llbuiltin("send_rpc"), + [llservice, lltag, llargs]) # Don't waste stack space on saved arguments. self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) + if fun_type.async: + return ll.Undefined + # T result = { # void *ptr = NULL; # loop: int size = rpc_recv("tag", ptr); diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index e1781116e..05640b2ae 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -308,20 +308,22 @@ class TRPC(Type): :ivar ret: (:class:`Type`) return type :ivar service: (int) RPC service number + :ivar async: (bool) whether the RPC blocks until return """ attributes = OrderedDict() - def __init__(self, ret, service): + def __init__(self, ret, service, async=False): assert isinstance(ret, Type) - self.ret, self.service = ret, service + self.ret, self.service, self.async = ret, service, async def find(self): return self def unify(self, other): if isinstance(other, TRPC) and \ - self.service == other.service: + self.service == other.service and \ + self.async == other.async: self.ret.unify(other.ret) elif isinstance(other, TVar): other.unify(self) @@ -337,7 +339,8 @@ class TRPC(Type): def __eq__(self, other): return isinstance(other, TRPC) and \ - self.service == other.service + self.service == other.service and \ + self.async == other.async def __ne__(self, other): return not (self == other) @@ -727,7 +730,9 @@ class TypePrinter(object): elif isinstance(typ, TFunction): return signature elif isinstance(typ, TRPC): - return "[rpc #{}](...)->{}".format(typ.service, self.name(typ.ret, depth + 1)) + return "[rpc{} #{}](...)->{}".format(typ.service, + " async" if typ.async else "", + self.name(typ.ret, depth + 1)) elif isinstance(typ, TBuiltinFunction): return "".format(typ.name) elif isinstance(typ, (TConstructor, TExceptionConstructor)): diff --git a/artiq/language/core.py b/artiq/language/core.py index b08da2147..c1c19c802 100644 --- a/artiq/language/core.py +++ b/artiq/language/core.py @@ -7,7 +7,7 @@ from functools import wraps import numpy -__all__ = ["kernel", "portable", "syscall", "host_only", +__all__ = ["kernel", "portable", "rpc", "syscall", "host_only", "set_time_manager", "set_watchdog_factory", "TerminationRequested"] @@ -22,7 +22,7 @@ __all__.extend(kernel_globals) _ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo", - "core_name function syscall forbidden flags") + "core_name portable function syscall forbidden flags") def kernel(arg=None, flags={}): """ @@ -53,7 +53,7 @@ def kernel(arg=None, flags={}): def run_on_core(self, *k_args, **k_kwargs): return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs) run_on_core.artiq_embedded = _ARTIQEmbeddedInfo( - core_name=arg, function=function, syscall=None, + core_name=arg, portable=False, function=function, syscall=None, forbidden=False, flags=set(flags)) return run_on_core return inner_decorator @@ -83,7 +83,23 @@ def portable(arg=None, flags={}): return inner_decorator else: arg.artiq_embedded = \ - _ARTIQEmbeddedInfo(core_name=None, function=arg, syscall=None, + _ARTIQEmbeddedInfo(core_name=None, portable=True, function=arg, syscall=None, + forbidden=False, flags=set(flags)) + return arg + +def rpc(arg=None, flags={}): + """ + This decorator marks a function for execution on the host interpreter. + This is also the default behavior of ARTIQ; however, this decorator allows + specifying additional flags. + """ + if arg is None: + def inner_decorator(function): + return rpc(function, flags) + return inner_decorator + else: + arg.artiq_embedded = \ + _ARTIQEmbeddedInfo(core_name=None, portable=False, function=arg, syscall=None, forbidden=False, flags=set(flags)) return arg @@ -101,7 +117,7 @@ def syscall(arg=None, flags={}): if isinstance(arg, str): def inner_decorator(function): function.artiq_embedded = \ - _ARTIQEmbeddedInfo(core_name=None, function=None, + _ARTIQEmbeddedInfo(core_name=None, portable=False, function=None, syscall=function.__name__, forbidden=False, flags=set(flags)) return function @@ -119,7 +135,7 @@ def host_only(function): in the host Python interpreter. """ function.artiq_embedded = \ - _ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None, + _ARTIQEmbeddedInfo(core_name=None, portable=False, function=None, syscall=None, forbidden=True, flags={}) return function diff --git a/artiq/test/lit/embedding/async_rpc.py b/artiq/test/lit/embedding/async_rpc.py new file mode 100644 index 000000000..691a093c3 --- /dev/null +++ b/artiq/test/lit/embedding/async_rpc.py @@ -0,0 +1,15 @@ +# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s +# RUN: OutputCheck %s --file-to-check=%t.ll + +from artiq.language.core import * +from artiq.language.types import * + +# CHECK: call void @send_async_rpc + +@rpc(flags={"async"}) +def foo(): + pass + +@kernel +def entrypoint(): + foo() diff --git a/artiq/test/lit/embedding/error_rpc_async_return.py b/artiq/test/lit/embedding/error_rpc_async_return.py new file mode 100644 index 000000000..72f3c79c3 --- /dev/null +++ b/artiq/test/lit/embedding/error_rpc_async_return.py @@ -0,0 +1,15 @@ +# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t +# RUN: OutputCheck %s --file-to-check=%t + +from artiq.language.core import * +from artiq.language.types import * + +# CHECK-L: ${LINE:+2}: fatal: functions that return a value cannot be defined as async RPCs +@rpc(flags={"async"}) +def foo() -> TInt32: + pass + +@kernel +def entrypoint(): + # CHECK-L: ${LINE:+1}: note: function called here + foo() diff --git a/doc/manual/compiler.rst b/doc/manual/compiler.rst index 6a8271a5f..8185b2611 100644 --- a/doc/manual/compiler.rst +++ b/doc/manual/compiler.rst @@ -47,6 +47,19 @@ The Python types correspond to ARTIQ type annotations as follows: | range | TRange32, TRange64 | +-------------+-------------------------+ +Asynchronous RPCs +----------------- + +If an RPC returns no value, it can be invoked in a way that does not block until the RPC finishes +execution, but only until it is queued. (Submitting asynchronous RPCs too rapidly, as well as +submitting asynchronous RPCs with arguments that are too large, can still block until completion.) + +To define an asynchronous RPC, use the ``@rpc`` annotation with a flag: + + @rpc(flags={"async"}) + def record_result(x): + self.results.append(x) + Additional optimizations ------------------------ From 2392113bb6a0a0cfbb54077c9c47456848e27769 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 30 Oct 2016 11:12:36 +0800 Subject: [PATCH 05/47] kc705: use misoc clock for false path --- artiq/gateware/targets/kc705.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 4e95bc604..ea38273d4 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -149,7 +149,7 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.rtio_crg.cd_rtio.clk.attr.add("keep") self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) self.platform.add_false_path_constraints( - self.rtio.cd_rsys.clk, + self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk) self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, From 617e345d167113ccc413e66f1c06a99de49188dc Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 31 Oct 2016 15:11:50 +0000 Subject: [PATCH 06/47] gateware: fix kernel CPU exec address. --- artiq/gateware/amp/kernel_cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/amp/kernel_cpu.py b/artiq/gateware/amp/kernel_cpu.py index 6aca00d32..9ad73099a 100644 --- a/artiq/gateware/amp/kernel_cpu.py +++ b/artiq/gateware/amp/kernel_cpu.py @@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder class KernelCPU(Module): def __init__(self, platform, - exec_address=0x40800000, + exec_address=0x40800080, main_mem_origin=0x40000000, l2_size=8192): self._reset = CSRStorage(reset=1) From 898a716b91de1f8f055a143964daedb661cbf393 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 31 Oct 2016 18:13:15 +0000 Subject: [PATCH 07/47] runtime: work around mor1kx ignoring low bits of reset address. Fixes #599. --- artiq/gateware/amp/kernel_cpu.py | 2 +- artiq/runtime.rs/src/kernel_proto.rs | 2 +- artiq/runtime.rs/src/rpc_queue.rs | 4 ++-- artiq/runtime/ksupport.ld | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/amp/kernel_cpu.py b/artiq/gateware/amp/kernel_cpu.py index 9ad73099a..6aca00d32 100644 --- a/artiq/gateware/amp/kernel_cpu.py +++ b/artiq/gateware/amp/kernel_cpu.py @@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder class KernelCPU(Module): def __init__(self, platform, - exec_address=0x40800080, + exec_address=0x40800000, main_mem_origin=0x40000000, l2_size=8192): self._reset = CSRStorage(reset=1) diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index d98be19f8..afaf7c35e 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::fmt; -pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800080; +pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800000; pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40840000; pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; pub const KSUPPORT_HEADER_SIZE: usize = 0x80; diff --git a/artiq/runtime.rs/src/rpc_queue.rs b/artiq/runtime.rs/src/rpc_queue.rs index f0f89f144..35fbe0df2 100644 --- a/artiq/runtime.rs/src/rpc_queue.rs +++ b/artiq/runtime.rs/src/rpc_queue.rs @@ -8,7 +8,7 @@ const SEND_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 4) as *mut usize; const RECV_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 8) as *mut usize; const QUEUE_BEGIN: usize = 0x40400000; -const QUEUE_END: usize = 0x40800000; +const QUEUE_END: usize = 0x407fff80; const QUEUE_CHUNK: usize = 0x1000; pub unsafe fn init() { @@ -21,7 +21,7 @@ fn next(mut addr: usize) -> usize { debug_assert!(addr >= QUEUE_BEGIN && addr < QUEUE_END); addr += QUEUE_CHUNK; - if addr == QUEUE_END { addr = QUEUE_BEGIN } + if addr >= QUEUE_END { addr = QUEUE_BEGIN } addr } diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index 1e0ed5040..333db0739 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -13,7 +13,7 @@ INCLUDE generated/regions.ld * are also loaded. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x40800080, LENGTH = 0x40000 + ksupport (RWX) : ORIGIN = 0x40800000, LENGTH = 0x40000 } /* Kernel stack is at the end of main RAM. */ From 18ae8d54a39a495afa09802fe27ef6bf5183032f Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 02:33:00 +0000 Subject: [PATCH 08/47] gateware: fix mailbox. --- artiq/gateware/amp/mailbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/amp/mailbox.py b/artiq/gateware/amp/mailbox.py index 6f4a8987f..4c93557df 100644 --- a/artiq/gateware/amp/mailbox.py +++ b/artiq/gateware/amp/mailbox.py @@ -12,10 +12,10 @@ class Mailbox(Module): values = Array([Signal(32) for _ in range(size)]) for i in self.i1, self.i2: self.sync += [ - i.dat_r.eq(values[i.adr]), + i.dat_r.eq(values[i.adr & 0xff]), i.ack.eq(0), If(i.cyc & i.stb & ~i.ack, i.ack.eq(1), - If(i.we, values[i.adr].eq(i.dat_w)) + If(i.we, values[i.adr & 0xff].eq(i.dat_w)) ) ] From 43cd970100434a0029536ee125ab8c3271626ed8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 1 Nov 2016 11:11:22 +0800 Subject: [PATCH 09/47] make set_dataset and mutate_dataset async RPCs --- artiq/language/environment.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/language/environment.py b/artiq/language/environment.py index 822279e48..626af2550 100644 --- a/artiq/language/environment.py +++ b/artiq/language/environment.py @@ -3,6 +3,7 @@ from inspect import isclass from artiq.protocols import pyon from artiq.language import units +from artiq.language.core import rpc __all__ = ["NoDefault", @@ -274,6 +275,7 @@ class HasEnvironment: kernel_invariants = getattr(self, "kernel_invariants", set()) self.kernel_invariants = kernel_invariants | {key} + @rpc(flags={"async"}) def set_dataset(self, key, value, broadcast=False, persist=False, save=True): """Sets the contents and handling modes of a dataset. @@ -290,6 +292,7 @@ class HasEnvironment: """ self.__dataset_mgr.set(key, value, broadcast, persist, save) + @rpc(flags={"async"}) def mutate_dataset(self, key, index, value): """Mutate an existing dataset at the given index (e.g. set a value at a given position in a NumPy array) From 636d4efe8174c6e75b17cfa605441b5f6ef50694 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 06:28:43 +0000 Subject: [PATCH 10/47] gateware: rewrite mailbox to use bits_for. --- artiq/gateware/amp/mailbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/amp/mailbox.py b/artiq/gateware/amp/mailbox.py index 4c93557df..7c60056d1 100644 --- a/artiq/gateware/amp/mailbox.py +++ b/artiq/gateware/amp/mailbox.py @@ -12,10 +12,10 @@ class Mailbox(Module): values = Array([Signal(32) for _ in range(size)]) for i in self.i1, self.i2: self.sync += [ - i.dat_r.eq(values[i.adr & 0xff]), + i.dat_r.eq(values[i.adr[:bits_for(size-1)]]), i.ack.eq(0), If(i.cyc & i.stb & ~i.ack, i.ack.eq(1), - If(i.we, values[i.adr & 0xff].eq(i.dat_w)) + If(i.we, values[i.adr[:bits_for(size-1)]].eq(i.dat_w)) ) ] From c1e6d4b67c16b9f8d5013eb5c9e9aa55952bdab7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 06:51:44 +0000 Subject: [PATCH 11/47] runtime: fix multiple async RPC bugs. --- artiq/coredevice/comm_generic.py | 27 +++++++++++++++++---------- artiq/coredevice/core.py | 1 + artiq/runtime.rs/src/kernel.rs | 3 +++ artiq/runtime.rs/src/rpc_queue.rs | 2 +- artiq/runtime.rs/src/session.rs | 4 ++-- artiq/runtime.rs/src/session_proto.rs | 5 +++-- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 3163b1ba8..30804d541 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -141,6 +141,9 @@ class CommGeneric: (value, ) = struct.unpack(">d", self._read_chunk(8)) return value + def _read_bool(self): + return True if self._read_int8() else False + def _read_bytes(self): return self._read_chunk(self._read_int32()) @@ -177,6 +180,9 @@ class CommGeneric: def _write_float64(self, value): self.write(struct.pack(">d", value)) + def _write_bool(self, value): + self.write(struct.pack("B", value)) + def _write_bytes(self, value): self._write_int32(len(value)) self.write(value) @@ -405,24 +411,25 @@ class CommGeneric: raise IOError("Unknown RPC value tag: {}".format(repr(tag))) def _serve_rpc(self, embedding_map): - service_id = self._read_int32() - if service_id == 0: - service = lambda obj, attr, value: setattr(obj, attr, value) - else: - service = embedding_map.retrieve_object(service_id) - + async = self._read_bool() + service_id = self._read_int32() + service = embedding_map.retrieve_object(service_id) args, kwargs = self._receive_rpc_args(embedding_map) return_tags = self._read_bytes() - logger.debug("rpc service: [%d]%r %r %r -> %s", service_id, service, args, kwargs, return_tags) + logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service, + (" (async)" if async else ""), args, kwargs, return_tags) try: result = service(*args, **kwargs) - logger.debug("rpc service: %d %r %r == %r", service_id, args, kwargs, result) - - if service_id != 0: + if async: + return + else: self._write_header(_H2DMsgType.RPC_REPLY) self._write_bytes(return_tags) self._send_rpc_value(bytearray(return_tags), result, result, service) + + logger.debug("rpc service: %d %r %r = %r", service_id, args, kwargs, result) + except Exception as exn: logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn) diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 8f9330287..1b1b7b3a4 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -103,6 +103,7 @@ class Core: def run(self, function, args, kwargs): result = None + @rpc(flags={"async"}) def set_result(new_result): nonlocal result result = new_result diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index e50902cf5..5d7e17696 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -1,6 +1,7 @@ use core::ptr; use board::csr; use mailbox; +use rpc_queue; use kernel_proto::{KERNELCPU_EXEC_ADDRESS, KERNELCPU_LAST_ADDRESS, KSUPPORT_HEADER_SIZE}; @@ -16,6 +17,8 @@ pub unsafe fn start() { ptr::copy_nonoverlapping(ksupport_image.as_ptr(), ksupport_addr, ksupport_image.len()); csr::kernel_cpu::reset_write(0); + + rpc_queue::init(); } pub fn stop() { diff --git a/artiq/runtime.rs/src/rpc_queue.rs b/artiq/runtime.rs/src/rpc_queue.rs index 35fbe0df2..3155813eb 100644 --- a/artiq/runtime.rs/src/rpc_queue.rs +++ b/artiq/runtime.rs/src/rpc_queue.rs @@ -13,7 +13,7 @@ const QUEUE_CHUNK: usize = 0x1000; pub unsafe fn init() { write_volatile(SEND_MAILBOX, QUEUE_BEGIN); - write_volatile(RECV_MAILBOX, QUEUE_END); + write_volatile(RECV_MAILBOX, QUEUE_BEGIN); } fn next(mut addr: usize) -> usize { diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index dacb14d67..5a7bb76c2 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -389,7 +389,7 @@ fn process_kern_message(waiter: Waiter, match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { - try!(host_write(stream, host::Reply::RpcRequest)); + try!(host_write(stream, host::Reply::RpcRequest { async: async })); try!(rpc::send_args(&mut BufWriter::new(stream), service, tag, data)); if !async { session.kernel_state = KernelState::RpcWait @@ -470,7 +470,7 @@ fn process_kern_queued_rpc(stream: &mut TcpStream, rpc_queue::dequeue(|slice| { trace!("comm<-kern (async RPC)"); let length = NetworkEndian::read_u32(slice) as usize; - try!(host_write(stream, host::Reply::RpcRequest)); + try!(host_write(stream, host::Reply::RpcRequest { async: true })); try!(stream.write(&slice[4..][..length])); Ok(()) }) diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index 2ee656c35..fd48c46b3 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -107,7 +107,7 @@ pub enum Reply<'a> { backtrace: &'a [usize] }, - RpcRequest, + RpcRequest { async: bool }, FlashRead(&'a [u8]), FlashOk, @@ -170,8 +170,9 @@ impl<'a> Reply<'a> { } }, - Reply::RpcRequest => { + Reply::RpcRequest { async } => { try!(write_u8(writer, 10)); + try!(write_u8(writer, async as u8)); }, Reply::FlashRead(ref bytes) => { From 2095d01b84ebc018f2bd438dd8da5a8d846eb9fa Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 08:55:49 +0000 Subject: [PATCH 12/47] runtime: dirty hacks to remove allocations in ksupport. --- artiq/runtime.rs/Cargo.toml | 2 +- artiq/runtime.rs/libksupport/lib.rs | 2 +- artiq/runtime.rs/liblwip/Cargo.toml | 2 +- artiq/runtime.rs/libstd_artiq/Cargo.toml | 3 ++ artiq/runtime.rs/libstd_artiq/io/error.rs | 45 +++++++++++++++++++++-- artiq/runtime.rs/libstd_artiq/lib.rs | 13 ++++++- artiq/runtime.rs/src/proto.rs | 3 +- 7 files changed, 62 insertions(+), 8 deletions(-) diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 1355f7a4e..146ff9089 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["staticlib"] path = "src/lib.rs" [dependencies] -std_artiq = { path = "libstd_artiq" } +std_artiq = { path = "libstd_artiq", features = ["alloc"] } lwip = { path = "liblwip", default-features = false } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } log = { version = "0.3", default-features = false, features = [] } diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index 53e62f4e5..73b35c4bc 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -127,7 +127,7 @@ extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) { }; proto::write_u32(&mut slice, length as u32) }).unwrap_or_else(|err| { - assert!(err.kind() == std::io::ErrorKind::UnexpectedEof); + assert!(err.kind() == std::io::ErrorKind::WriteZero); send(&RpcSend { async: true, diff --git a/artiq/runtime.rs/liblwip/Cargo.toml b/artiq/runtime.rs/liblwip/Cargo.toml index 886f69415..8d00166eb 100644 --- a/artiq/runtime.rs/liblwip/Cargo.toml +++ b/artiq/runtime.rs/liblwip/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" [dependencies] lwip-sys = { path = "../liblwip-sys" } -std_artiq = { path = "../libstd_artiq" } +std_artiq = { path = "../libstd_artiq", features = ["alloc"] } [features] default = ["preemption"] diff --git a/artiq/runtime.rs/libstd_artiq/Cargo.toml b/artiq/runtime.rs/libstd_artiq/Cargo.toml index dfd38df4a..a6435b1f5 100644 --- a/artiq/runtime.rs/libstd_artiq/Cargo.toml +++ b/artiq/runtime.rs/libstd_artiq/Cargo.toml @@ -9,3 +9,6 @@ path = "lib.rs" [dependencies] alloc_artiq = { path = "../liballoc_artiq" } + +[features] +alloc = [] diff --git a/artiq/runtime.rs/libstd_artiq/io/error.rs b/artiq/runtime.rs/libstd_artiq/io/error.rs index f8684a4ec..37ecc4507 100644 --- a/artiq/runtime.rs/libstd_artiq/io/error.rs +++ b/artiq/runtime.rs/libstd_artiq/io/error.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use alloc::boxed::Box; +#[cfg(feature="alloc")] use alloc::boxed::Box; +#[cfg(not(feature="alloc"))] use ::FakeBox as Box; use core::convert::Into; use core::fmt; use core::marker::{Send, Sync}; @@ -62,13 +63,19 @@ pub struct Error { enum Repr { Os(i32), + #[cfg(feature="alloc")] Custom(Box), + #[cfg(not(feature="alloc"))] + Custom(Custom), } #[derive(Debug)] struct Custom { kind: ErrorKind, + #[cfg(feature="alloc")] error: Box, + #[cfg(not(feature="alloc"))] + error: &'static str } /// A list specifying general categories of I/O error. @@ -162,12 +169,21 @@ impl Error { /// // errors can also be created from other errors /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// ``` + #[cfg(feature="alloc")] pub fn new(kind: ErrorKind, error: E) -> Error where E: Into> { Self::_new(kind, error.into()) } + #[cfg(not(feature="alloc"))] + pub fn new(kind: ErrorKind, error: E) -> Error + where E: Into<&'static str> + { + Self::_new(kind, error.into()) + } + + #[cfg(feature="alloc")] fn _new(kind: ErrorKind, error: Box) -> Error { Error { repr: Repr::Custom(Box::new(Custom { @@ -177,6 +193,16 @@ impl Error { } } + #[cfg(not(feature="alloc"))] + fn _new(kind: ErrorKind, error: &'static str) -> Error { + Error { + repr: Repr::Custom(Box::new(Custom { + kind: kind, + error: error, + })) + } + } + /// Creates a new instance of an `Error` from a particular OS error code. pub fn from_raw_os_error(code: i32) -> Error { Error { repr: Repr::Os(code) } @@ -198,6 +224,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, @@ -210,6 +237,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, @@ -221,6 +249,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn into_inner(self) -> Option> { match self.repr { Repr::Os(..) => None, @@ -282,14 +311,24 @@ impl error::Error for Error { ErrorKind::UnexpectedEof => "unexpected end of file", ErrorKind::__Nonexhaustive => unreachable!() }, - Repr::Custom(ref c) => c.error.description(), + Repr::Custom(ref c) => { + #[cfg(feature="alloc")] + { c.error.description() } + #[cfg(not(feature="alloc"))] + { c.error } + }, } } fn cause(&self) -> Option<&error::Error> { match self.repr { Repr::Os(..) => None, - Repr::Custom(ref c) => c.error.cause(), + Repr::Custom(ref _c) => { + #[cfg(feature="alloc")] + { _c.error.cause() } + #[cfg(not(feature="alloc"))] + { None } + } } } } diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index dafffbc9f..648baa61e 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -1,6 +1,7 @@ #![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, question_mark, unicode, reflect_marker, raw, int_error_internals, - try_from, try_borrow, macro_reexport, allow_internal_unstable)] + try_from, try_borrow, macro_reexport, allow_internal_unstable, + stmt_expr_attributes)] #![no_std] #![needs_panic_runtime] @@ -31,3 +32,13 @@ pub mod prelude { pub mod error; pub mod io; + +// Provide Box::new wrapper +#[cfg(not(feature="alloc"))] +struct FakeBox(core::marker::PhantomData); +#[cfg(not(feature="alloc"))] +impl FakeBox { + fn new(val: T) -> T { + val + } +} diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs index e3b40c3c1..9d59b6ad6 100644 --- a/artiq/runtime.rs/src/proto.rs +++ b/artiq/runtime.rs/src/proto.rs @@ -67,7 +67,8 @@ pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { pub fn read_string(reader: &mut Read) -> io::Result { let bytes = try!(read_bytes(reader)); - String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + String::from_utf8(bytes) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8")) } pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { From 6fcd57a41a342c710c0b5fc0d216a20f4df177ad Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 10:30:42 +0000 Subject: [PATCH 13/47] runtime: fix remaining async RPC bugs. --- artiq/coredevice/comm_generic.py | 20 ++++++++++++-------- artiq/runtime.rs/Cargo.toml | 2 +- artiq/runtime.rs/libksupport/lib.rs | 7 ++++--- artiq/runtime.rs/src/session.rs | 3 ++- artiq/runtime/ksupport_glue.c | 2 +- artiq/test/coredevice/test_embedding.py | 21 +++++++++++++++++++++ 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 30804d541..8afa8c096 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -413,23 +413,27 @@ class CommGeneric: def _serve_rpc(self, embedding_map): async = self._read_bool() service_id = self._read_int32() - service = embedding_map.retrieve_object(service_id) args, kwargs = self._receive_rpc_args(embedding_map) return_tags = self._read_bytes() + + if service_id is 0: + service = lambda obj, attr, value: setattr(obj, attr, value) + else: + service = embedding_map.retrieve_object(service_id) logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service, (" (async)" if async else ""), args, kwargs, return_tags) + if async: + service(*args, **kwargs) + return + try: result = service(*args, **kwargs) - if async: - return - else: - self._write_header(_H2DMsgType.RPC_REPLY) - self._write_bytes(return_tags) - self._send_rpc_value(bytearray(return_tags), result, result, service) - logger.debug("rpc service: %d %r %r = %r", service_id, args, kwargs, result) + self._write_header(_H2DMsgType.RPC_REPLY) + self._write_bytes(return_tags) + self._send_rpc_value(bytearray(return_tags), result, result, service) except Exception as exn: logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn) diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 146ff9089..5a459d204 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -16,7 +16,7 @@ path = "src/lib.rs" std_artiq = { path = "libstd_artiq", features = ["alloc"] } lwip = { path = "liblwip", default-features = false } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } -log = { version = "0.3", default-features = false, features = [] } +log = { version = "0.3", default-features = false, features = ["max_level_debug"] } log_buffer = { version = "1.0" } byteorder = { version = "0.5", default-features = false } diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index 73b35c4bc..dc1c853e4 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -12,8 +12,6 @@ extern crate byteorder; mod board; #[path = "../src/mailbox.rs"] mod mailbox; -#[path = "../src/rpc_queue.rs"] -mod rpc_queue; #[path = "../src/proto.rs"] mod proto; @@ -72,7 +70,7 @@ macro_rules! recv { } macro_rules! print { - ($($arg:tt)*) => ($crate::send(&Log(format_args!($($arg)*)))); + ($($arg:tt)*) => ($crate::send(&$crate::kernel_proto::Log(format_args!($($arg)*)))); } macro_rules! println { @@ -80,6 +78,9 @@ macro_rules! println { ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); } +#[path = "../src/rpc_queue.rs"] +mod rpc_queue; + #[lang = "panic_fmt"] extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! { println!("panic at {}:{}: {}", file, line, args); diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 5a7bb76c2..8ad957a61 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -471,6 +471,7 @@ fn process_kern_queued_rpc(stream: &mut TcpStream, trace!("comm<-kern (async RPC)"); let length = NetworkEndian::read_u32(slice) as usize; try!(host_write(stream, host::Reply::RpcRequest { async: true })); + trace!("{:?}" ,&slice[4..][..length]); try!(stream.write(&slice[4..][..length])); Ok(()) }) @@ -482,7 +483,7 @@ fn host_kernel_worker(waiter: Waiter, let mut session = Session::new(congress); loop { - if !rpc_queue::empty() { + while !rpc_queue::empty() { try!(process_kern_queued_rpc(stream, &mut session)) } diff --git a/artiq/runtime/ksupport_glue.c b/artiq/runtime/ksupport_glue.c index 2fa056f5e..515635c94 100644 --- a/artiq/runtime/ksupport_glue.c +++ b/artiq/runtime/ksupport_glue.c @@ -8,7 +8,7 @@ void send_to_log(const char *ptr, size_t length); -#define KERNELCPU_EXEC_ADDRESS 0x40800080 +#define KERNELCPU_EXEC_ADDRESS 0x40800000 #define KERNELCPU_PAYLOAD_ADDRESS 0x40840000 #define KERNELCPU_LAST_ADDRESS 0x4fffffff #define KSUPPORT_HEADER_SIZE 0x80 diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 683468b1d..8ad7cef63 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -217,6 +217,27 @@ class AnnotationTest(ExperimentCase): exp = self.create(_Annotation) self.assertEqual(exp.overflow(1), True) +class _Async(EnvExperiment): + def build(self): + self.setattr_device("core") + + @rpc(flags={"async"}) + def recv_async(self, data): + pass + + @kernel + def run(self): + # fast async path + self.recv_async([0]*128) + # slow async path + self.recv_async([0]*4096) + + +class AsyncTest(ExperimentCase): + def test_args(self): + exp = self.create(_RPCTypes) + exp.run() + class _Payload1MB(EnvExperiment): def build(self): From b30734a105def55f82520f1ecea6ccd479398ddc Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 1 Nov 2016 13:22:22 +0000 Subject: [PATCH 14/47] runtime: fix a race condition with async RPCs. session.rs has code like: while !rpc_queue::empty() { try!(process_kern_queued_rpc(stream, &mut session)) } // A if mailbox::receive() != 0 { try!(process_kern_message(waiter, Some(stream), &mut session)); } If both an async and a mailbox RPC (async or large sync) are posted at point A then they will be processed out of order. This commit fixes the issue by flushing the async RPC queue before posting any RPC to the mailbox. --- artiq/runtime.rs/libksupport/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index dc1c853e4..7e4d66b59 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -107,6 +107,7 @@ extern fn send_rpc(service: u32, tag: *const u8, data: *const *const ()) { extern { fn strlen(s: *const c_char) -> size_t; } let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) }; + while !rpc_queue::empty() {} send(&RpcSend { async: false, service: service, @@ -130,6 +131,7 @@ extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) { }).unwrap_or_else(|err| { assert!(err.kind() == std::io::ErrorKind::WriteZero); + while !rpc_queue::empty() {} send(&RpcSend { async: true, service: service, From a252b88a365109150cd1981821cde07c8a5500cb Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 3 Nov 2016 23:56:13 +0800 Subject: [PATCH 15/47] manual: fix binutils patch download URL --- doc/manual/installing_from_source.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index 2836e085c..f66f43383 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -33,7 +33,7 @@ and the ARTIQ kernels. $ wget https://ftp.gnu.org/gnu/binutils/binutils-2.27.tar.bz2 $ tar xvf binutils-2.27.tar.bz2 $ cd binutils-2.27 - $ curl -L 'https://github.com/m-labs/conda-recipes/blob/ece4cefbcce5548c5bd7fd4740d71ecd6930065e/conda/binutils-or1k-linux/fix-R_OR1K_GOTOFF-relocations.patch' | patch -p1 + $ curl -L https://raw.githubusercontent.com/m-labs/conda-recipes/ece4cefbcce5548c5bd7fd4740d71ecd6930065e/conda/binutils-or1k-linux/fix-R_OR1K_GOTOFF-relocations.patch' | patch -p1 $ mkdir build $ cd build From 77bc2477448a72a48f54ce384ddaf7e67af681ef Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 4 Nov 2016 00:04:26 +0800 Subject: [PATCH 16/47] manual: add missing cd --- doc/manual/installing_from_source.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index f66f43383..513672986 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -62,6 +62,7 @@ and the ARTIQ kernels. $ cd ~/artiq-dev $ git clone https://github.com/m-labs/rust + $ cd rust $ git checkout artiq $ mkdir build $ cd build From 208f3f856862d8f653f8f8e1878d592590342fa3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Nov 2016 03:35:33 +0000 Subject: [PATCH 17/47] manual: fix Rust installation instructions. --- doc/manual/installing_from_source.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index 513672986..43c0a61c4 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -64,17 +64,22 @@ and the ARTIQ kernels. $ git clone https://github.com/m-labs/rust $ cd rust $ git checkout artiq + $ git submodule update --init $ mkdir build $ cd build - $ ../configure --prefix=/usr/local/rust-or1k --llvm-root=/usr/local/llvm-or1k/bin/llvm-config --disable-manage-submodules + $ ../configure --prefix=/usr/local/rust-or1k --llvm-root=/usr/local/llvm-or1k --disable-manage-submodules $ sudo make install -j4 - $ libs="libcore liballoc librustc_unicode libcollections liblibc_mini libunwind libpanic_unwind" + $ libs="libcore liballoc librustc_unicode libcollections liblibc_mini libunwind" + $ rustc="/usr/local/rust-or1k/bin/rustc --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s" + $ destdir="/usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/" $ mkdir ../build-or1k $ cd ../build-or1k - $ for lib in ${libs}; do /usr/local/rust-or1k/bin/rustc src/${lib}/lib.rs; done - $ /usr/local/rust-or1k/bin/rustc -Cpanic=abort src/libpanic_abort/lib.rs - $ sudo cp * /usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/ + $ for lib in ${libs}; do ${rustc} ../src/${lib}/lib.rs; done + $ ${rustc} -Cpanic=abort ../src/libpanic_abort/lib.rs + $ ${rustc} -Cpanic=unwind ../src/libpanic_unwind/lib.rs --cfg llvm_libunwind + $ sudo mkdir -p ${destdir} + $ sudo cp *.rlib ${destdir} .. note:: Compilation of LLVM can take more than 30 min on some machines. Compilation of Rust can take more than two hours. From 9d58b4516ca640b802b272ceee66d3c53a868a97 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 4 Nov 2016 11:54:04 +0800 Subject: [PATCH 18/47] manual: Vivado is now default --- doc/manual/installing_from_source.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index 43c0a61c4..b80fbbe14 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -173,7 +173,7 @@ These steps are required to generate gateware bitstream (``.bit``) files, build $ python3.5 -m artiq.gateware.targets.kc705 -H nist_qc1 # or nist_qc2 - .. note:: Add ``--toolchain vivado`` if you wish to use Vivado instead of ISE. + .. note:: Add ``--toolchain ise`` if you wish to use ISE instead of Vivado. * Then, gather the binaries and flash them: :: From 8ec73cb9ec6b655f6348b0f6071c3abf7f003c77 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 4 Nov 2016 16:20:46 +0800 Subject: [PATCH 19/47] dashboard: pack moninj widgets (#603) --- artiq/dashboard/moninj.py | 70 +++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index b821865b0..22c0bb963 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -23,35 +23,26 @@ _mode_enc = { } -class _MoninjWidget(QtWidgets.QFrame): - def __init__(self): - QtWidgets.QFrame.__init__(self) - qfm = QtGui.QFontMetrics(self.font()) - self._size = QtCore.QSize( - 18*qfm.averageCharWidth(), - 6*qfm.lineSpacing()) - self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - - self.setFrameShape(QtWidgets.QFrame.Box) - self.setFrameShadow(QtWidgets.QFrame.Raised) - - def sizeHint(self): - return self._size - - -class _TTLWidget(_MoninjWidget): +class _TTLWidget(QtWidgets.QFrame): def __init__(self, channel, send_to_device, force_out, title): + QtWidgets.QFrame.__init__(self) + self.channel = channel self.send_to_device = send_to_device self.force_out = force_out - _MoninjWidget.__init__(self) + self.setFrameShape(QtWidgets.QFrame.Box) + self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() + grid.setContentsMargins(0, 0, 0, 0) + grid.setHorizontalSpacing(0) + grid.setVerticalSpacing(0) self.setLayout(grid) label = QtWidgets.QLabel(title) label.setAlignment(QtCore.Qt.AlignCenter) - label.setWordWrap(True) + label.setSizePolicy(QtWidgets.QSizePolicy.Ignored, + QtWidgets.QSizePolicy.Preferred) grid.addWidget(label, 1, 1) self.stack = QtWidgets.QStackedWidget() @@ -62,9 +53,18 @@ class _TTLWidget(_MoninjWidget): self.stack.addWidget(self.direction) grid_cb = LayoutWidget() - self.override = QtWidgets.QCheckBox("Override") + grid_cb.layout.setContentsMargins(0, 0, 0, 0) + grid_cb.layout.setHorizontalSpacing(0) + grid_cb.layout.setVerticalSpacing(0) + self.override = QtWidgets.QToolButton() + self.override.setText("OVR") + self.override.setCheckable(True) + self.override.setToolTip("Override") grid_cb.addWidget(self.override, 3, 1) - self.level = QtWidgets.QCheckBox("Level") + self.level = QtWidgets.QToolButton() + self.level.setText("LVL") + self.level.setCheckable(True) + self.level.setToolTip("Level") grid_cb.addWidget(self.level, 3, 2) self.stack.addWidget(grid_cb) @@ -78,19 +78,19 @@ class _TTLWidget(_MoninjWidget): grid.setRowStretch(4, 1) self.programmatic_change = False - self.override.stateChanged.connect(self.override_toggled) - self.level.stateChanged.connect(self.level_toggled) + self.override.clicked.connect(self.override_toggled) + self.level.clicked.connect(self.level_toggled) self.set_value(0, False, False) def enterEvent(self, event): self.stack.setCurrentIndex(1) - _MoninjWidget.enterEvent(self, event) + QtWidgets.QFrame.enterEvent(self, event) def leaveEvent(self, event): if not self.override.isChecked(): self.stack.setCurrentIndex(0) - _MoninjWidget.leaveEvent(self, event) + QtWidgets.QFrame.leaveEvent(self, event) def override_toggled(self, override): if self.programmatic_change: @@ -125,11 +125,11 @@ class _TTLWidget(_MoninjWidget): color = " color=\"red\"" else: color = "" - self.value.setText("{}".format( + self.value.setText("{}".format( color, value_s)) oe = oe or self.force_out direction = "OUT" if oe else "IN" - self.direction.setText("" + direction + "") + self.direction.setText("" + direction + "") self.programmatic_change = True try: @@ -144,24 +144,30 @@ class _TTLWidget(_MoninjWidget): return self.channel -class _DDSWidget(_MoninjWidget): +class _DDSWidget(QtWidgets.QFrame): def __init__(self, bus_channel, channel, sysclk, title): + QtWidgets.QFrame.__init__(self) + self.bus_channel = bus_channel self.channel = channel self.sysclk = sysclk - _MoninjWidget.__init__(self) + self.setFrameShape(QtWidgets.QFrame.Box) + self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() + grid.setContentsMargins(0, 0, 0, 0) + grid.setHorizontalSpacing(0) + grid.setVerticalSpacing(0) self.setLayout(grid) label = QtWidgets.QLabel(title) label.setAlignment(QtCore.Qt.AlignCenter) - label.setWordWrap(True) + label.setSizePolicy(QtWidgets.QSizePolicy.Ignored, + QtWidgets.QSizePolicy.Preferred) grid.addWidget(label, 1, 1) self.value = QtWidgets.QLabel() self.value.setAlignment(QtCore.Qt.AlignCenter) - self.value.setWordWrap(True) grid.addWidget(self.value, 2, 1, 6, 1) grid.setRowStretch(1, 1) @@ -172,7 +178,7 @@ class _DDSWidget(_MoninjWidget): def set_value(self, ftw): frequency = ftw*self.sysclk()/2**32 - self.value.setText("{:.7f} MHz" + self.value.setText("{:.7f} MHz" .format(frequency/1e6)) def sort_key(self): From f102f2d4e60e24ecdd9eb299352ebb0eac2399c8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 4 Nov 2016 09:11:21 +0000 Subject: [PATCH 20/47] manual: fix rustc invocation. --- doc/manual/installing_from_source.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index b80fbbe14..32d1e3481 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -71,7 +71,7 @@ and the ARTIQ kernels. $ sudo make install -j4 $ libs="libcore liballoc librustc_unicode libcollections liblibc_mini libunwind" - $ rustc="/usr/local/rust-or1k/bin/rustc --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s" + $ rustc="/usr/local/rust-or1k/bin/rustc --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s -L ." $ destdir="/usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/" $ mkdir ../build-or1k $ cd ../build-or1k From 7dcc987dd73f001a1887b7219a075434fd4c03f8 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 5 Nov 2016 02:28:43 +0000 Subject: [PATCH 21/47] compiler: Fix break/continue targets in loop else blocks Previously, they would still target the respective labels in the just-exited loop. Signed-off-by: David Nadlinger --- .../compiler/transforms/artiq_ir_generator.py | 86 +++++++++---------- artiq/test/lit/integration/for.py | 24 ++++++ artiq/test/lit/integration/while.py | 24 ++++++ 3 files changed, 91 insertions(+), 43 deletions(-) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 330ff0ecf..4575c381c 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -452,30 +452,30 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_block = body self.visit(node.body) post_body = self.current_block - - if any(node.orelse): - else_tail = self.add_block("while.else") - self.current_block = else_tail - self.visit(node.orelse) - post_else_tail = self.current_block - - tail = self.add_block("while.tail") - self.current_block = tail - - if any(node.orelse): - if not post_else_tail.is_terminated(): - post_else_tail.append(ir.Branch(tail)) - else: - else_tail = tail - - post_head.append(ir.BranchIf(cond, body, else_tail)) - if not post_body.is_terminated(): - post_body.append(ir.Branch(head)) - break_block.append(ir.Branch(tail)) finally: self.break_target = old_break self.continue_target = old_continue + if any(node.orelse): + else_tail = self.add_block("while.else") + self.current_block = else_tail + self.visit(node.orelse) + post_else_tail = self.current_block + + tail = self.add_block("while.tail") + self.current_block = tail + + if any(node.orelse): + if not post_else_tail.is_terminated(): + post_else_tail.append(ir.Branch(tail)) + else: + else_tail = tail + + post_head.append(ir.BranchIf(cond, body, else_tail)) + if not post_body.is_terminated(): + post_body.append(ir.Branch(head)) + break_block.append(ir.Branch(tail)) + def iterable_len(self, value, typ=_size_type): if builtins.is_listish(value.type): if isinstance(value, ir.Constant): @@ -541,33 +541,33 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_assign = None self.visit(node.body) post_body = self.current_block - - if any(node.orelse): - else_tail = self.add_block("for.else") - self.current_block = else_tail - self.visit(node.orelse) - post_else_tail = self.current_block - - tail = self.add_block("for.tail") - self.current_block = tail - - if any(node.orelse): - if not post_else_tail.is_terminated(): - post_else_tail.append(ir.Branch(tail)) - else: - else_tail = tail - - if node.trip_count is not None: - head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail)) - else: - head.append(ir.BranchIf(cond, body, else_tail)) - if not post_body.is_terminated(): - post_body.append(ir.Branch(continue_block)) - break_block.append(ir.Branch(tail)) finally: self.break_target = old_break self.continue_target = old_continue + if any(node.orelse): + else_tail = self.add_block("for.else") + self.current_block = else_tail + self.visit(node.orelse) + post_else_tail = self.current_block + + tail = self.add_block("for.tail") + self.current_block = tail + + if any(node.orelse): + if not post_else_tail.is_terminated(): + post_else_tail.append(ir.Branch(tail)) + else: + else_tail = tail + + if node.trip_count is not None: + head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail)) + else: + head.append(ir.BranchIf(cond, body, else_tail)) + if not post_body.is_terminated(): + post_body.append(ir.Branch(continue_block)) + break_block.append(ir.Branch(tail)) + def visit_Break(self, node): self.append(ir.Branch(self.break_target)) diff --git a/artiq/test/lit/integration/for.py b/artiq/test/lit/integration/for.py index 9c305f5da..7156bee5d 100644 --- a/artiq/test/lit/integration/for.py +++ b/artiq/test/lit/integration/for.py @@ -27,3 +27,27 @@ for x in range(10): assert False else: assert False + +# Verify continue target is reset in else block. +cond = False +while True: + if cond: + break + cond = True + for _ in range(1): + pass + else: + continue + assert False +else: + assert False + +# Verify for target is reset in else block. +while True: + for _ in range(1): + pass + else: + break + assert False +else: + assert False diff --git a/artiq/test/lit/integration/while.py b/artiq/test/lit/integration/while.py index 7cd2ac626..e7d03c0d7 100644 --- a/artiq/test/lit/integration/while.py +++ b/artiq/test/lit/integration/while.py @@ -29,3 +29,27 @@ while cond: cond = False continue assert False + +# Verify continue target is reset in else block. +cond = False +while True: + if cond: + break + cond = True + while False: + assert False + else: + continue + assert False +else: + assert False + +# Verify break target is reset in else block. +while True: + while False: + assert False + else: + break + assert False +else: + assert False From 453e8b7eb3a660575112b5d56f82614179837ca3 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 6 Nov 2016 23:52:27 +0800 Subject: [PATCH 22/47] runtime: support configurations without moninj, log or dds --- artiq/runtime.rs/libksupport/api.rs | 7 +++++-- artiq/runtime.rs/src/lib.rs | 2 ++ artiq/runtime/rtio.c | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index aa6483bbd..81c4d75df 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -105,13 +105,16 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(rtio_input_timestamp), api!(rtio_input_data), -// #if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) + #[cfg(rtio_dds_count)] api!(dds_init), + #[cfg(rtio_dds_count)] api!(dds_init_sync), + #[cfg(rtio_dds_count)] api!(dds_batch_enter), + #[cfg(rtio_dds_count)] api!(dds_batch_exit), + #[cfg(rtio_dds_count)] api!(dds_set), -// #endif api!(i2c_init), api!(i2c_start), diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index e661c427f..48efd5d0d 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -75,6 +75,7 @@ mod rpc_proto; mod kernel; mod session; +#[cfg(has_rtio_moninj)] mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; @@ -109,6 +110,7 @@ pub unsafe extern fn rust_main() { let mut scheduler = sched::Scheduler::new(); scheduler.spawner().spawn(16384, session::thread); + #[cfg(has_rtio_moninj)] scheduler.spawner().spawn(4096, moninj::thread); #[cfg(has_rtio_analyzer)] scheduler.spawner().spawn(4096, analyzer::thread); diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 09f71d9d4..ac5168224 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -124,6 +124,7 @@ unsigned int rtio_input_data(int channel) void rtio_log_va(long long int timestamp, const char *fmt, va_list args) { +#ifdef CONFIG_RTIO_LOG_CHANNEL // This executes on the kernel CPU's stack, which is specifically designed // for allocation of this kind of massive buffers. int len = vsnprintf(NULL, 0, fmt, args); @@ -152,6 +153,7 @@ void rtio_log_va(long long int timestamp, const char *fmt, va_list args) i = 0; } } +#endif } void rtio_log(long long int timestamp, const char *fmt, ...) From 6e77f65d500f22381edaf75454ffb4ac1a33dd7d Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Mon, 7 Nov 2016 14:33:17 +0000 Subject: [PATCH 23/47] compiler: Clarify recv_rpc value names and documentation [nfc] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the phi emitted for the pointer parameter to recv_rpc was – rather confusingly – called "size", and the pseudo-code in the comment had bit-rotted. Signed-off-by: David Nadlinger --- artiq/compiler/transforms/llvm_ir_generator.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index c755a43ee..e9643aa04 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1265,10 +1265,12 @@ class LLVMIRGenerator: return ll.Undefined # T result = { - # void *ptr = NULL; - # loop: int size = rpc_recv("tag", ptr); + # void *ret_ptr = alloca(sizeof(T)); + # void *ptr = ret_ptr; + # loop: int size = recv_rpc(ptr); + # // Non-zero: Provide `size` bytes of extra storage for variable-length data. # if(size) { ptr = alloca(size); goto loop; } - # else *(T*)ptr + # else *(T*)ret_ptr # } llprehead = self.llbuilder.basic_block llhead = self.llbuilder.append_basic_block(name="rpc.head") @@ -1283,7 +1285,7 @@ class LLVMIRGenerator: self.llbuilder.branch(llhead) self.llbuilder.position_at_end(llhead) - llphi = self.llbuilder.phi(llslotgen.type, name="rpc.size") + llphi = self.llbuilder.phi(llslotgen.type, name="rpc.ptr") llphi.add_incoming(llslotgen, llprehead) if llunwindblock: llsize = self.llbuilder.invoke(self.llbuiltin("recv_rpc"), [llphi], From 798a5f70df9a9f7f486a71152ddf6ee572e3d9cc Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 11:53:26 +0000 Subject: [PATCH 24/47] Revert "runtime: remove some redundant libm functions copied inline." This reverts commit fee75bd50f9240b7b0377291718d7354498c9b62. LLVM does not have lround as a libcall and this inhibits LICM. --- .../compiler/transforms/llvm_ir_generator.py | 9 +++--- artiq/runtime.rs/libksupport/api.rs | 2 +- artiq/runtime/ksupport_glue.c | 29 +++++++++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index e9643aa04..c86888a0f 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -323,6 +323,8 @@ class LLVMIRGenerator: llty = ll.FunctionType(llvoid, []) elif name == "llvm.floor.f64": llty = ll.FunctionType(lldouble, [lldouble]) + elif name == "llvm.round.f64": + llty = ll.FunctionType(lldouble, [lldouble]) elif name == "llvm.pow.f64": llty = ll.FunctionType(lldouble, [lldouble, lldouble]) elif name == "llvm.powi.f64": @@ -347,8 +349,6 @@ class LLVMIRGenerator: llty = ll.FunctionType(lli32, [llptr]) elif name == "strcmp": llty = ll.FunctionType(lli32, [llptr, llptr]) - elif name == "lround": - llty = ll.FunctionType(lli32, [lldouble]) elif name == "send_rpc": llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) elif name == "send_async_rpc": @@ -1044,8 +1044,9 @@ class LLVMIRGenerator: name=insn.name) elif insn.op == "round": llarg = self.map(insn.operands[0]) - return self.llbuilder.call(self.llbuiltin("lround"), [llarg], - name=insn.name) + llvalue = self.llbuilder.call(self.llbuiltin("llvm.round.f64"), [llarg]) + return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type), + name=insn.name) elif insn.op == "globalenv": def get_outer(llenv, env_ty): if "$outer" in env_ty.params: diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index 81c4d75df..5f9d7f967 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -74,7 +74,7 @@ static mut API: &'static [(&'static str, *const ())] = &[ /* libm */ api!(sqrt), - api!(lround), + api!(round), /* exceptions */ api!(_Unwind_Resume), diff --git a/artiq/runtime/ksupport_glue.c b/artiq/runtime/ksupport_glue.c index 515635c94..f0d496dc3 100644 --- a/artiq/runtime/ksupport_glue.c +++ b/artiq/runtime/ksupport_glue.c @@ -70,10 +70,33 @@ int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), voi } /* called by kernel */ -long lround(double x); -long lround(double x) +double round(double x); +double round(double x) { - return x < 0 ? floor(x) : ceil(x); + union {double f; uint64_t i;} u = {x}; + int e = u.i >> 52 & 0x7ff; + double y; + + if (e >= 0x3ff+52) + return x; + if (u.i >> 63) + x = -x; + if (e < 0x3ff-1) { + /* we don't do it in ARTIQ */ + /* raise inexact if x!=0 */ + // FORCE_EVAL(x + 0x1p52); + return 0*u.f; + } + y = (double)(x + 0x1p52) - 0x1p52 - x; + if (y > 0.5) + y = y + x - 1; + else if (y <= -0.5) + y = y + x + 1; + else + y = y + x; + if (u.i >> 63) + y = -y; + return y; } /* called by kernel */ From 0d7688017bfdd7116fcedd71da3e9b488fdcd165 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 7 Nov 2016 09:31:43 +0000 Subject: [PATCH 25/47] Revert "Revert "Update for LLVM 3.9."" This reverts commit 5f5975844a6af08639250d689d3f78bf213b8508. --- artiq/compiler/targets.py | 5 +-- .../compiler/transforms/llvm_ir_generator.py | 38 +++++++++---------- conda/artiq/meta.yaml | 2 +- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index aab813737..0fd2aa0fe 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -86,14 +86,11 @@ class Target: llmachine = lltarget.create_target_machine( features=",".join(["+{}".format(f) for f in self.features]), reloc="pic", codemodel="default") - llmachine.set_verbose(True) + llmachine.set_asm_verbosity(True) return llmachine def optimize(self, llmodule): - llmachine = self.target_machine() llpassmgr = llvm.create_module_pass_manager() - llmachine.target_data.add_pass(llpassmgr) - llmachine.add_analysis_passes(llpassmgr) # Register our alias analysis passes. llpassmgr.add_basic_alias_analysis_pass() diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index c86888a0f..b99c6ed3b 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -37,9 +37,16 @@ def memoize(generator): class DebugInfoEmitter: def __init__(self, llmodule): self.llmodule = llmodule - self.llsubprograms = [] + self.llcompileunit = None self.cache = {} + llident = self.llmodule.add_named_metadata('llvm.ident') + llident.add(self.emit_metadata(["ARTIQ"])) + + llflags = self.llmodule.add_named_metadata('llvm.module.flags') + llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) + llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) + def emit_metadata(self, operands): def map_operand(operand): if operand is None: @@ -67,14 +74,13 @@ class DebugInfoEmitter: }) @memoize - def emit_compile_unit(self, source_buffer, llsubprograms): + def emit_compile_unit(self, source_buffer): return self.emit_debug_info("DICompileUnit", { "language": ll.DIToken("DW_LANG_Python"), "file": self.emit_file(source_buffer), "producer": "ARTIQ", "runtimeVersion": 0, "emissionKind": 2, # full=1, lines only=2 - "subprograms": self.emit_metadata(llsubprograms) }, is_distinct=True) @memoize @@ -86,21 +92,26 @@ class DebugInfoEmitter: @memoize def emit_subprogram(self, func, llfunc): source_buffer = func.loc.source_buffer + + if self.llcompileunit is None: + self.llcompileunit = self.emit_compile_unit(source_buffer) + llcompileunits = self.llmodule.add_named_metadata('llvm.dbg.cu') + llcompileunits.add(self.llcompileunit) + display_name = "{}{}".format(func.name, types.TypePrinter().name(func.type)) - llsubprogram = self.emit_debug_info("DISubprogram", { + return self.emit_debug_info("DISubprogram", { "name": func.name, "linkageName": llfunc.name, "type": self.emit_subroutine_type(func.type), "file": self.emit_file(source_buffer), "line": func.loc.line(), + "unit": self.llcompileunit, "scope": self.emit_file(source_buffer), "scopeLine": func.loc.line(), "isLocal": func.is_internal, "isDefinition": True, "variables": self.emit_metadata([]) }, is_distinct=True) - self.llsubprograms.append(llsubprogram) - return llsubprogram @memoize def emit_loc(self, loc, scope): @@ -110,18 +121,6 @@ class DebugInfoEmitter: "scope": scope }) - def finalize(self, source_buffer): - llident = self.llmodule.add_named_metadata('llvm.ident') - llident.add(self.emit_metadata(["ARTIQ"])) - - llflags = self.llmodule.add_named_metadata('llvm.module.flags') - llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) - llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) - - llcompile_units = self.llmodule.add_named_metadata('llvm.dbg.cu') - llcompile_units.add(self.emit_compile_unit(source_buffer, tuple(self.llsubprograms))) - - class LLVMIRGenerator: def __init__(self, engine, module_name, target, embedding_map): self.engine = engine @@ -410,9 +409,6 @@ class LLVMIRGenerator: for func in functions: self.process_function(func) - if any(functions): - self.debug_info_emitter.finalize(functions[0].loc.source_buffer) - if attribute_writeback and self.embedding_map is not None: self.emit_attribute_writeback() diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index ca300b8d3..f2cc0aeea 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.10.0.dev py35_24 + - llvmlite-artiq 0.11.0.dev py35_25 - lit - outputcheck - scipy From e0297039c9de975336aff3a74e4f58c8fcc2c9d5 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 12:08:20 +0000 Subject: [PATCH 26/47] artiq_run: fix bitrot in .ll/.bc runners. --- artiq/frontend/artiq_run.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/artiq/frontend/artiq_run.py b/artiq/frontend/artiq_run.py index 4972a5cd7..004171a0f 100755 --- a/artiq/frontend/artiq_run.py +++ b/artiq/frontend/artiq_run.py @@ -73,8 +73,7 @@ class LLVMIRRunner(FileRunner): with open(self.file, "r") as f: llmodule = llvm.parse_assembly(f.read()) llmodule.verify() - return self.target.link([self.target.assemble(llmodule)], - init_fn="__modinit__") + return self.target.link([self.target.assemble(llmodule)]) class LLVMBitcodeRunner(FileRunner): @@ -82,8 +81,7 @@ class LLVMBitcodeRunner(FileRunner): with open(self.file, "rb") as f: llmodule = llvm.parse_bitcode(f.read()) llmodule.verify() - return self.target.link([self.target.assemble(llmodule)], - init_fn="__modinit__") + return self.target.link([self.target.assemble(llmodule)]) class DummyScheduler: From a8fd697d41a1e34b07b5132b6ccad8671777287c Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 12:57:06 +0000 Subject: [PATCH 27/47] runtime: unbreak 453e8b7. Running rustc --cfg 'foo="1"' does not result in a statement of the form do_thing() to be compilex in. --- artiq/runtime.rs/libksupport/api.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index 5f9d7f967..b9788be26 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -105,15 +105,15 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(rtio_input_timestamp), api!(rtio_input_data), - #[cfg(rtio_dds_count)] + #[cfg(has_rtio_dds_count)] api!(dds_init), - #[cfg(rtio_dds_count)] + #[cfg(has_rtio_dds_count)] api!(dds_init_sync), - #[cfg(rtio_dds_count)] + #[cfg(has_rtio_dds_count)] api!(dds_batch_enter), - #[cfg(rtio_dds_count)] + #[cfg(has_rtio_dds_count)] api!(dds_batch_exit), - #[cfg(rtio_dds_count)] + #[cfg(has_rtio_dds_count)] api!(dds_set), api!(i2c_init), From 7b81ed1d1851ce12a047cf3d8e61167271b83b8d Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 12:58:20 +0000 Subject: [PATCH 28/47] Revert "Revert "Revert "Update for LLVM 3.9.""" This reverts commit 0d7688017bfdd7116fcedd71da3e9b488fdcd165. --- artiq/compiler/targets.py | 5 ++- .../compiler/transforms/llvm_ir_generator.py | 38 ++++++++++--------- conda/artiq/meta.yaml | 2 +- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index 0fd2aa0fe..aab813737 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -86,11 +86,14 @@ class Target: llmachine = lltarget.create_target_machine( features=",".join(["+{}".format(f) for f in self.features]), reloc="pic", codemodel="default") - llmachine.set_asm_verbosity(True) + llmachine.set_verbose(True) return llmachine def optimize(self, llmodule): + llmachine = self.target_machine() llpassmgr = llvm.create_module_pass_manager() + llmachine.target_data.add_pass(llpassmgr) + llmachine.add_analysis_passes(llpassmgr) # Register our alias analysis passes. llpassmgr.add_basic_alias_analysis_pass() diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index b99c6ed3b..c86888a0f 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -37,16 +37,9 @@ def memoize(generator): class DebugInfoEmitter: def __init__(self, llmodule): self.llmodule = llmodule - self.llcompileunit = None + self.llsubprograms = [] self.cache = {} - llident = self.llmodule.add_named_metadata('llvm.ident') - llident.add(self.emit_metadata(["ARTIQ"])) - - llflags = self.llmodule.add_named_metadata('llvm.module.flags') - llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) - llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) - def emit_metadata(self, operands): def map_operand(operand): if operand is None: @@ -74,13 +67,14 @@ class DebugInfoEmitter: }) @memoize - def emit_compile_unit(self, source_buffer): + def emit_compile_unit(self, source_buffer, llsubprograms): return self.emit_debug_info("DICompileUnit", { "language": ll.DIToken("DW_LANG_Python"), "file": self.emit_file(source_buffer), "producer": "ARTIQ", "runtimeVersion": 0, "emissionKind": 2, # full=1, lines only=2 + "subprograms": self.emit_metadata(llsubprograms) }, is_distinct=True) @memoize @@ -92,26 +86,21 @@ class DebugInfoEmitter: @memoize def emit_subprogram(self, func, llfunc): source_buffer = func.loc.source_buffer - - if self.llcompileunit is None: - self.llcompileunit = self.emit_compile_unit(source_buffer) - llcompileunits = self.llmodule.add_named_metadata('llvm.dbg.cu') - llcompileunits.add(self.llcompileunit) - display_name = "{}{}".format(func.name, types.TypePrinter().name(func.type)) - return self.emit_debug_info("DISubprogram", { + llsubprogram = self.emit_debug_info("DISubprogram", { "name": func.name, "linkageName": llfunc.name, "type": self.emit_subroutine_type(func.type), "file": self.emit_file(source_buffer), "line": func.loc.line(), - "unit": self.llcompileunit, "scope": self.emit_file(source_buffer), "scopeLine": func.loc.line(), "isLocal": func.is_internal, "isDefinition": True, "variables": self.emit_metadata([]) }, is_distinct=True) + self.llsubprograms.append(llsubprogram) + return llsubprogram @memoize def emit_loc(self, loc, scope): @@ -121,6 +110,18 @@ class DebugInfoEmitter: "scope": scope }) + def finalize(self, source_buffer): + llident = self.llmodule.add_named_metadata('llvm.ident') + llident.add(self.emit_metadata(["ARTIQ"])) + + llflags = self.llmodule.add_named_metadata('llvm.module.flags') + llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) + llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) + + llcompile_units = self.llmodule.add_named_metadata('llvm.dbg.cu') + llcompile_units.add(self.emit_compile_unit(source_buffer, tuple(self.llsubprograms))) + + class LLVMIRGenerator: def __init__(self, engine, module_name, target, embedding_map): self.engine = engine @@ -409,6 +410,9 @@ class LLVMIRGenerator: for func in functions: self.process_function(func) + if any(functions): + self.debug_info_emitter.finalize(functions[0].loc.source_buffer) + if attribute_writeback and self.embedding_map is not None: self.emit_attribute_writeback() diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index f2cc0aeea..ca300b8d3 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.11.0.dev py35_25 + - llvmlite-artiq 0.10.0.dev py35_24 - lit - outputcheck - scipy From ec8fe6f8bd858b8bd2a9595f317a6ede52dced7b Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 14:22:47 +0000 Subject: [PATCH 29/47] Revert "Revert "Revert "Revert "Update for LLVM 3.9."""" This reverts commit 7b81ed1d1851ce12a047cf3d8e61167271b83b8d. --- artiq/compiler/targets.py | 5 +-- .../compiler/transforms/llvm_ir_generator.py | 38 +++++++++---------- conda/artiq/meta.yaml | 2 +- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index aab813737..0fd2aa0fe 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -86,14 +86,11 @@ class Target: llmachine = lltarget.create_target_machine( features=",".join(["+{}".format(f) for f in self.features]), reloc="pic", codemodel="default") - llmachine.set_verbose(True) + llmachine.set_asm_verbosity(True) return llmachine def optimize(self, llmodule): - llmachine = self.target_machine() llpassmgr = llvm.create_module_pass_manager() - llmachine.target_data.add_pass(llpassmgr) - llmachine.add_analysis_passes(llpassmgr) # Register our alias analysis passes. llpassmgr.add_basic_alias_analysis_pass() diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index c86888a0f..b99c6ed3b 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -37,9 +37,16 @@ def memoize(generator): class DebugInfoEmitter: def __init__(self, llmodule): self.llmodule = llmodule - self.llsubprograms = [] + self.llcompileunit = None self.cache = {} + llident = self.llmodule.add_named_metadata('llvm.ident') + llident.add(self.emit_metadata(["ARTIQ"])) + + llflags = self.llmodule.add_named_metadata('llvm.module.flags') + llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) + llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) + def emit_metadata(self, operands): def map_operand(operand): if operand is None: @@ -67,14 +74,13 @@ class DebugInfoEmitter: }) @memoize - def emit_compile_unit(self, source_buffer, llsubprograms): + def emit_compile_unit(self, source_buffer): return self.emit_debug_info("DICompileUnit", { "language": ll.DIToken("DW_LANG_Python"), "file": self.emit_file(source_buffer), "producer": "ARTIQ", "runtimeVersion": 0, "emissionKind": 2, # full=1, lines only=2 - "subprograms": self.emit_metadata(llsubprograms) }, is_distinct=True) @memoize @@ -86,21 +92,26 @@ class DebugInfoEmitter: @memoize def emit_subprogram(self, func, llfunc): source_buffer = func.loc.source_buffer + + if self.llcompileunit is None: + self.llcompileunit = self.emit_compile_unit(source_buffer) + llcompileunits = self.llmodule.add_named_metadata('llvm.dbg.cu') + llcompileunits.add(self.llcompileunit) + display_name = "{}{}".format(func.name, types.TypePrinter().name(func.type)) - llsubprogram = self.emit_debug_info("DISubprogram", { + return self.emit_debug_info("DISubprogram", { "name": func.name, "linkageName": llfunc.name, "type": self.emit_subroutine_type(func.type), "file": self.emit_file(source_buffer), "line": func.loc.line(), + "unit": self.llcompileunit, "scope": self.emit_file(source_buffer), "scopeLine": func.loc.line(), "isLocal": func.is_internal, "isDefinition": True, "variables": self.emit_metadata([]) }, is_distinct=True) - self.llsubprograms.append(llsubprogram) - return llsubprogram @memoize def emit_loc(self, loc, scope): @@ -110,18 +121,6 @@ class DebugInfoEmitter: "scope": scope }) - def finalize(self, source_buffer): - llident = self.llmodule.add_named_metadata('llvm.ident') - llident.add(self.emit_metadata(["ARTIQ"])) - - llflags = self.llmodule.add_named_metadata('llvm.module.flags') - llflags.add(self.emit_metadata([2, "Debug Info Version", 3])) - llflags.add(self.emit_metadata([2, "Dwarf Version", 4])) - - llcompile_units = self.llmodule.add_named_metadata('llvm.dbg.cu') - llcompile_units.add(self.emit_compile_unit(source_buffer, tuple(self.llsubprograms))) - - class LLVMIRGenerator: def __init__(self, engine, module_name, target, embedding_map): self.engine = engine @@ -410,9 +409,6 @@ class LLVMIRGenerator: for func in functions: self.process_function(func) - if any(functions): - self.debug_info_emitter.finalize(functions[0].loc.source_buffer) - if attribute_writeback and self.embedding_map is not None: self.emit_attribute_writeback() diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index ca300b8d3..f2cc0aeea 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.10.0.dev py35_24 + - llvmlite-artiq 0.11.0.dev py35_25 - lit - outputcheck - scipy From 99ad9b591796414f1fe550ccf928a2626a92536d Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 8 Nov 2016 23:33:03 +0800 Subject: [PATCH 30/47] add has_dds, use config flags --- artiq/gateware/targets/kc705.py | 12 +++++++----- artiq/gateware/targets/pipistrello.py | 3 ++- artiq/runtime.rs/libksupport/api.rs | 10 +++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index ea38273d4..486f047f0 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -137,6 +137,8 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.register_kernel_cpu_csrdevice("i2c") self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_DDS"] = None + def add_rtio(self, rtio_channels): self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) self.csr_devices.append("rtio_crg") @@ -198,7 +200,7 @@ class NIST_QC1(_NIST_Ions): self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_DDS_COUNT"] = 1 self.config["DDS_CHANNELS_PER_BUS"] = 8 - self.config["DDS_AD9858"] = True + self.config["DDS_AD9858"] = None phy = dds.AD9858(platform.request("dds"), 8) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, @@ -272,8 +274,8 @@ class NIST_CLOCK(_NIST_Ions): self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_DDS_COUNT"] = 1 self.config["DDS_CHANNELS_PER_BUS"] = 11 - self.config["DDS_AD9914"] = True - self.config["DDS_ONEHOT_SEL"] = True + self.config["DDS_AD9914"] = None + self.config["DDS_ONEHOT_SEL"] = None phy = dds.AD9914(platform.request("dds"), 11, onehot=True) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, @@ -350,8 +352,8 @@ class NIST_QC2(_NIST_Ions): self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_DDS_COUNT"] = 2 self.config["DDS_CHANNELS_PER_BUS"] = 12 - self.config["DDS_AD9914"] = True - self.config["DDS_ONEHOT_SEL"] = True + self.config["DDS_AD9914"] = None + self.config["DDS_ONEHOT_SEL"] = None for backplane_offset in range(2): phy = dds.AD9914( platform.request("dds", backplane_offset), 12, onehot=True) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index f9afa683b..a0a5f0134 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -206,10 +206,11 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=64, ififo_depth=64)) + self.config["HAS_DDS"] = None self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_DDS_COUNT"] = 1 self.config["DDS_CHANNELS_PER_BUS"] = 8 - self.config["DDS_AD9858"] = True + self.config["DDS_AD9858"] = None dds_pins = platform.request("dds") self.comb += dds_pins.p.eq(0) phy = dds.AD9858(dds_pins, 8) diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index b9788be26..f30b099b0 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -105,15 +105,15 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(rtio_input_timestamp), api!(rtio_input_data), - #[cfg(has_rtio_dds_count)] + #[cfg(has_dds)] api!(dds_init), - #[cfg(has_rtio_dds_count)] + #[cfg(has_dds)] api!(dds_init_sync), - #[cfg(has_rtio_dds_count)] + #[cfg(has_dds)] api!(dds_batch_enter), - #[cfg(has_rtio_dds_count)] + #[cfg(has_dds)] api!(dds_batch_exit), - #[cfg(has_rtio_dds_count)] + #[cfg(has_dds)] api!(dds_set), api!(i2c_init), From 124b257e05c711b3d6bec8fd7cca5717f11b668b Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 8 Nov 2016 22:08:04 +0000 Subject: [PATCH 31/47] conda: update for LLVM 3.9. --- conda/artiq/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index f2cc0aeea..de54eeec5 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.11.0.dev py35_25 + - llvmlite-artiq 0.12.0.dev py35_27 - lit - outputcheck - scipy From bfbdba92057c021f1876db004e5851ac2b991918 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 8 Nov 2016 01:05:27 +0000 Subject: [PATCH 32/47] compiler: Emit all-kernel_invariant objects as LLVM constants This enables constant propagation optimisations, as verified by the included test case. This is only a first stop-gap measure, though; we should support optimisation based on kernel invariants on a more fine-grained level. --- .../compiler/transforms/llvm_ir_generator.py | 4 +++ .../lit/embedding/invariant_propagation.py | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 artiq/test/lit/embedding/invariant_propagation.py diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index b99c6ed3b..1095c2e64 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1384,6 +1384,7 @@ class LLVMIRGenerator: def _quote_attributes(): llglobal = None llfields = [] + emit_as_constant = True for attr in typ.attributes: if attr == "__objectid__": objectid = self.embedding_map.store_object(value) @@ -1404,6 +1405,8 @@ class LLVMIRGenerator: not types.is_c_function(typ.attributes[attr])) if is_class_function: attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue) + if not (types.is_instance(typ) and attr in typ.constant_attributes): + emit_as_constant = False llattrvalue = self._quote(attrvalue, typ.attributes[attr], lambda: path() + [attr]) llfields.append(llattrvalue) @@ -1411,6 +1414,7 @@ class LLVMIRGenerator: llclosureptr = self.get_global_closure_ptr(typ, attr) llclosureptr.initializer = llattrvalue + llglobal.global_constant = emit_as_constant llglobal.initializer = ll.Constant(llty.pointee, llfields) llglobal.linkage = "private" return llglobal diff --git a/artiq/test/lit/embedding/invariant_propagation.py b/artiq/test/lit/embedding/invariant_propagation.py new file mode 100644 index 000000000..e1af5c9c1 --- /dev/null +++ b/artiq/test/lit/embedding/invariant_propagation.py @@ -0,0 +1,26 @@ +# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s +# RUN: OutputCheck %s --file-to-check=%t.ll + +from artiq.language.core import * +from artiq.language.types import * + +class Class: + kernel_invariants = {"foo"} + + def __init__(self): + self.foo = True + + @kernel + def run(self): + if self.foo: + print("bar") + else: + # Make sure all the code for this branch will be completely elided: + # CHECK-NOT: baz + print("baz") + +obj = Class() + +@kernel +def entrypoint(): + obj.run() From eee8d0539885ebee609a94ce16a49ce092c77f8e Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 9 Nov 2016 14:59:57 +0000 Subject: [PATCH 33/47] doc: clarify kernel_invariant doc (fixes #609). --- doc/manual/compiler.rst | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/doc/manual/compiler.rst b/doc/manual/compiler.rst index 8185b2611..02c56e516 100644 --- a/doc/manual/compiler.rst +++ b/doc/manual/compiler.rst @@ -54,7 +54,7 @@ If an RPC returns no value, it can be invoked in a way that does not block until execution, but only until it is queued. (Submitting asynchronous RPCs too rapidly, as well as submitting asynchronous RPCs with arguments that are too large, can still block until completion.) -To define an asynchronous RPC, use the ``@rpc`` annotation with a flag: +To define an asynchronous RPC, use the ``@rpc`` annotation with a flag: :: @rpc(flags={"async"}) def record_result(x): @@ -87,7 +87,7 @@ Kernel invariants The compiler attempts to remove or hoist out of loops any redundant memory load operations, as well as propagate known constants into function bodies, which can enable further optimization. However, it must make conservative assumptions about code that it is unable to observe, because such code can change the value of the attribute, making the optimization invalid. -When an attribute is known to never change while the kernel is running, it can be marked as a *kernel invariant* to enable more aggressive optimization for this specific attribute: :: +When an attribute is known to never change while the kernel is running, it can be marked as a *kernel invariant* to enable more aggressive optimization for this specific attribute. :: class Converter: kernel_invariants = {"ratio"} @@ -99,4 +99,32 @@ When an attribute is known to never change while the kernel is running, it can b def convert(self, value): return value * self.ratio ** 2 -In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.ratio ** 2`` never changes and replace it with a constant, removing an expensive floating-point operation. +In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.ratio ** 2`` never changes and replace it with a constant, removing an expensive floating-point operation. :: + + class Worker: + kernel_invariants = {"interval"} + + def __init__(self, interval=1.0*us): + self.interval = interval + + def work(self): + # something useful + + class Looper: + def __init__(self, worker): + self.worker = worker + + @kernel + def loop(self): + for _ in range(100): + delay(self.worker.interval / 5.0) + self.worker.work() + +In the synthetic example above, the compiler will be able to detect that the result of evaluating ``self.interval / 5.0`` never changes, even though it neither knows the value of ``self.worker.interval`` beforehand nor can it see through the ``self.worker.work()`` function call, and hoist the expensive floating-point division out of the loop, transforming the code for ``loop`` into an equivalent of the following: :: + + @kernel + def loop(self): + precomputed_delay_mu = seconds_to_mu(self.worker.interval / 5.0) + for _ in range(100): + delay_mu(precomputed_delay_mu) + self.worker.work() From 71f1d388600573d3d36f1995cc076e7b6bd81b94 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 9 Nov 2016 15:10:53 +0000 Subject: [PATCH 34/47] conda: tighten pythonparser dependency (fixes #600). --- conda/artiq/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index de54eeec5..7d3491235 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -37,7 +37,7 @@ requirements: - pygit2 - aiohttp - binutils-or1k-linux - - pythonparser + - pythonparser 0.0 py_6 - levenshtein test: From 67e743d74a15425a224162363e27bba8cc66f459 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 9 Nov 2016 15:19:27 +0000 Subject: [PATCH 35/47] conda: use pythonparser 1.0. --- conda/artiq/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index 7d3491235..05ac59ceb 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -37,7 +37,7 @@ requirements: - pygit2 - aiohttp - binutils-or1k-linux - - pythonparser 0.0 py_6 + - pythonparser 1.0 - levenshtein test: From 0e76cbc4147e61f4405a64768428be6d136786af Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 10 Nov 2016 01:04:36 +0000 Subject: [PATCH 36/47] artiq_compile: actually disable attribute writeback. I wrote both halves of this condition but forgot to hook them together. Fixes #586. --- artiq/compiler/module.py | 6 ++++-- artiq/coredevice/core.py | 6 ++++-- artiq/frontend/artiq_compile.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index 0e07a5b60..e169a656e 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -40,7 +40,8 @@ class Source: return cls(source.Buffer(f.read(), filename, 1), engine=engine) class Module: - def __init__(self, src, ref_period=1e-6): + def __init__(self, src, ref_period=1e-6, attribute_writeback=True): + self.attribute_writeback = attribute_writeback self.engine = src.engine self.embedding_map = src.embedding_map self.name = src.name @@ -79,7 +80,8 @@ class Module: llvm_ir_generator = transforms.LLVMIRGenerator( engine=self.engine, module_name=self.name, target=target, embedding_map=self.embedding_map) - return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True) + return llvm_ir_generator.process(self.artiq_ir, + attribute_writeback=self.attribute_writeback) def __repr__(self): printer = types.TypePrinter() diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 1b1b7b3a4..58c2ca7a9 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -81,7 +81,7 @@ class Core: self.core = self self.comm.core = self - def compile(self, function, args, kwargs, set_result=None, with_attr_writeback=True): + def compile(self, function, args, kwargs, set_result=None, attribute_writeback=True): try: engine = _DiagnosticEngine(all_errors_are_fatal=True) @@ -89,7 +89,9 @@ class Core: stitcher.stitch_call(function, args, kwargs, set_result) stitcher.finalize() - module = Module(stitcher, ref_period=self.ref_period) + module = Module(stitcher, + ref_period=self.ref_period, + attribute_writeback=attribute_writeback) target = OR1KTarget() library = target.compile_and_link([module]) diff --git a/artiq/frontend/artiq_compile.py b/artiq/frontend/artiq_compile.py index 16d211399..ef801516c 100755 --- a/artiq/frontend/artiq_compile.py +++ b/artiq/frontend/artiq_compile.py @@ -55,7 +55,7 @@ def main(): object_map, kernel_library, _, _ = \ core.compile(exp.run, [exp_inst], {}, - with_attr_writeback=False) + attribute_writeback=False) except CompileError as error: return finally: From 3b6cbb1f063035bb5e24724827666c6b15fe0a9d Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 10 Nov 2016 20:25:15 +0000 Subject: [PATCH 37/47] artiq_devtool: implement. --- artiq/frontend/artiq_devtool.py | 144 ++++++++++++++++++++++++++++++++ setup.py | 2 + 2 files changed, 146 insertions(+) create mode 100644 artiq/frontend/artiq_devtool.py diff --git a/artiq/frontend/artiq_devtool.py b/artiq/frontend/artiq_devtool.py new file mode 100644 index 000000000..4c6aba681 --- /dev/null +++ b/artiq/frontend/artiq_devtool.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3.5 + +# This script makes the following assumptions: +# * miniconda is installed remotely at ~/miniconda +# * misoc and artiq are installed remotely via conda + +import sys +import argparse +import subprocess +import socket +import select +import threading +import paramiko + +from artiq.tools import verbosity_args, init_logger, logger +from random import Random + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ core device " + "development tool") + + verbosity_args(parser) + + parser.add_argument("--host", nargs=1, metavar="HOST", + type=str, default="lab.m-labs.hk", + help="SSH host where the development board is located") + parser.add_argument("--serial", nargs=1, metavar="SERIAL", + type=str, default="/dev/ttyUSB0", + help="TTY device corresponding to the development board") + parser.add_argument("--ip", nargs=1, metavar="IP", + type=str, default="kc705.lab.m-labs.hk", + help="IP address corresponding to the development board") + + parser.add_argument("actions", metavar="ACTION", + type=str, default=[], nargs="+", + help="actions to perform (sequence of: build boot connect)") + + return parser + + +def main(): + args = get_argparser().parse_args() + init_logger(args) + + ssh = None + def get_ssh(): + nonlocal ssh + if ssh is not None: + return ssh + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(args.host) + return ssh + + sftp = None + def get_sftp(): + nonlocal sftp + if sftp is not None: + return sftp + sftp = get_ssh().open_sftp() + return sftp + + rng = Random() + tmp = "artiq" + "".join([rng.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(6)]) + env = "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' " + + def run_command(cmd): + logger.info("Executing {}".format(cmd)) + chan = get_ssh().get_transport().open_session() + chan.set_combine_stderr(True) + chan.exec_command(cmd.format(tmp=tmp, env=env, serial=args.serial, ip=args.ip)) + return chan.makefile() + + def drain(chan): + while True: + char = chan.read(1) + if char == b"": + break + sys.stderr.write(char.decode("utf-8")) + + for action in args.actions: + if action == "build": + logger.info("Building runtime") + subprocess.call(["python3", "-m", "artiq.gateware.targets.kc705", + "-H", "nist_clock", + "--no-compile-gateware", + "--output-dir", "/tmp/kc705"]) + + elif action == "boot": + logger.info("Uploading runtime") + get_sftp().mkdir("/tmp/{tmp}".format(tmp=tmp)) + get_sftp().put("/tmp/kc705/software/runtime/runtime.bin", + "/tmp/{tmp}/runtime.bin".format(tmp=tmp)) + + logger.info("Booting runtime") + flterm = run_command( + "{env} python3 flterm.py {serial} " + + "--kernel /tmp/{tmp}/runtime.bin --upload-only") + artiq_flash = run_command( + "{env} artiq_flash start") + drain(flterm) + + elif action == "connect": + def forwarder(port): + listener = socket.socket() + listener.bind(('localhost', port)) + listener.listen(1) + while True: + local_stream, peer_addr = listener.accept() + logger.info("Accepting %s:%s and opening SSH channel to %s:%s", + *peer_addr, args.ip, port) + remote_stream = get_ssh().get_transport() \ + .open_channel('direct-tcpip', (args.ip, port), peer_addr) + while True: + r, w, x = select.select([local_stream, remote_stream], [], []) + if local_stream in r: + data = local_stream.recv(1024) + if data == b"": + break + remote_stream.send(data) + if remote_stream in r: + data = remote_stream.recv(1024) + if data == b"": + break + local_stream.send(data) + + for port in (1381, 1382): + thread = threading.Thread(target=forwarder, args=(port,), + name="port-{}".format(port), daemon=True) + thread.start() + + logger.info("Connecting to device") + flterm = run_command( + "{env} python3 flterm.py {serial} --output-only") + drain(flterm) + + else: + logger.error("Unknown action {}".format(action)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index c3c50e8c1..693da9a21 100755 --- a/setup.py +++ b/setup.py @@ -18,6 +18,7 @@ requirements = [ "quamash", "pyqtgraph", "pygit2", "aiohttp", "llvmlite_artiq", "pythonparser", "python-Levenshtein", "lit", "OutputCheck", + "paramiko" ] console_scripts = [ @@ -27,6 +28,7 @@ console_scripts = [ "artiq_coreconfig=artiq.frontend.artiq_coreconfig:main", "artiq_corelog=artiq.frontend.artiq_corelog:main", "artiq_ctlmgr=artiq.frontend.artiq_ctlmgr:main", + "artiq_devtool=artiq.frontend.artiq_devtool:main", "artiq_influxdb=artiq.frontend.artiq_influxdb:main", "artiq_master=artiq.frontend.artiq_master:main", "artiq_mkfs=artiq.frontend.artiq_mkfs:main", From fbc24204434551acf125e6cbd2b96d8c0403e5ea Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 11 Nov 2016 11:05:37 +0800 Subject: [PATCH 38/47] setup: remove paramiko dependency (optional and developer-only) --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 693da9a21..00ecb3355 100755 --- a/setup.py +++ b/setup.py @@ -18,7 +18,6 @@ requirements = [ "quamash", "pyqtgraph", "pygit2", "aiohttp", "llvmlite_artiq", "pythonparser", "python-Levenshtein", "lit", "OutputCheck", - "paramiko" ] console_scripts = [ From 8b6418c604705c0c45b3138e8529267abbd2b819 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 10 Nov 2016 21:05:24 +0000 Subject: [PATCH 39/47] artiq_devtool: more robust port forwarding. --- artiq/frontend/artiq_devtool.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/artiq/frontend/artiq_devtool.py b/artiq/frontend/artiq_devtool.py index 4c6aba681..5eb844666 100644 --- a/artiq/frontend/artiq_devtool.py +++ b/artiq/frontend/artiq_devtool.py @@ -105,6 +105,7 @@ def main(): elif action == "connect": def forwarder(port): listener = socket.socket() + listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.bind(('localhost', port)) listener.listen(1) while True: @@ -114,17 +115,22 @@ def main(): remote_stream = get_ssh().get_transport() \ .open_channel('direct-tcpip', (args.ip, port), peer_addr) while True: - r, w, x = select.select([local_stream, remote_stream], [], []) - if local_stream in r: - data = local_stream.recv(1024) - if data == b"": - break - remote_stream.send(data) - if remote_stream in r: - data = remote_stream.recv(1024) - if data == b"": - break - local_stream.send(data) + try: + r, w, x = select.select([local_stream, remote_stream], [], []) + if local_stream in r: + data = local_stream.recv(1024) + if data == b"": + break + remote_stream.send(data) + if remote_stream in r: + data = remote_stream.recv(1024) + if data == b"": + break + local_stream.send(data) + except Exception as e: + logger.exception("Forward error on port %s", port) + local_stream.close() + remote_stream.close() for port in (1381, 1382): thread = threading.Thread(target=forwarder, args=(port,), From d3ee858d1670094080f7e27c56e683b081c3beef Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 04:08:58 +0000 Subject: [PATCH 40/47] =?UTF-8?q?llvm=5Fir=5Fgenerator:=20use=20!{?= =?UTF-8?q?=E2=86=92unconditionally.}invariant.load=20metadata.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps LICM, among other things. --- artiq/compiler/transforms/llvm_ir_generator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 1095c2e64..0a41479b8 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -651,7 +651,7 @@ class LLVMIRGenerator: llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], inbounds=True) llouterenv = self.llbuilder.load(llptr) - llouterenv.set_metadata('invariant.load', self.empty_metadata) + llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata) llouterenv.set_metadata('nonnull', self.empty_metadata) return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) @@ -795,7 +795,7 @@ class LLVMIRGenerator: inbounds=True, name="ptr.{}".format(insn.name)) llvalue = self.llbuilder.load(llptr, name="val.{}".format(insn.name)) if types.is_instance(typ) and attr in typ.constant_attributes: - llvalue.set_metadata('invariant.load', self.empty_metadata) + llvalue.set_metadata('unconditionally.invariant.load', self.empty_metadata) if isinstance(llvalue.type, ll.PointerType): self.mark_dereferenceable(llvalue) return llvalue @@ -1050,7 +1050,7 @@ class LLVMIRGenerator: llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], inbounds=True) llouterenv = self.llbuilder.load(llptr) - llouterenv.set_metadata('invariant.load', self.empty_metadata) + llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata) llouterenv.set_metadata('nonnull', self.empty_metadata) return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) else: From 5eb940deb7b0af34aeec1701ad8fbaa53d05c12b Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 04:09:34 +0000 Subject: [PATCH 41/47] conda: bump llvmlite-artiq dep. --- conda/artiq/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index 05ac59ceb..580a20397 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.12.0.dev py35_27 + - llvmlite-artiq 0.12.0.dev py35_28 - lit - outputcheck - scipy From 7c2b1155ef6c49eb7ab378ad050ba0dd250834cd Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 13:58:00 +0000 Subject: [PATCH 42/47] conda: bump llvmlite-artiq dep. --- conda/artiq/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq/meta.yaml b/conda/artiq/meta.yaml index 580a20397..0d85408bb 100644 --- a/conda/artiq/meta.yaml +++ b/conda/artiq/meta.yaml @@ -18,7 +18,7 @@ requirements: - binutils-or1k-linux run: - python >=3.5.2 - - llvmlite-artiq 0.12.0.dev py35_28 + - llvmlite-artiq 0.12.0.dev py35_29 - lit - outputcheck - scipy From dca3fb5c9658b7a977f7e1205e9d4747c4aa7e7a Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 20:11:55 +0000 Subject: [PATCH 43/47] artiq_devtool: abort if build failed. --- artiq/frontend/artiq_devtool.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/artiq/frontend/artiq_devtool.py b/artiq/frontend/artiq_devtool.py index 5eb844666..7d4d71f5a 100644 --- a/artiq/frontend/artiq_devtool.py +++ b/artiq/frontend/artiq_devtool.py @@ -83,10 +83,14 @@ def main(): for action in args.actions: if action == "build": logger.info("Building runtime") - subprocess.call(["python3", "-m", "artiq.gateware.targets.kc705", - "-H", "nist_clock", - "--no-compile-gateware", - "--output-dir", "/tmp/kc705"]) + try: + subprocess.check_call(["python3", "-m", "artiq.gateware.targets.kc705", + "-H", "nist_clock", + "--no-compile-gateware", + "--output-dir", "/tmp/kc705"]) + except subprocess.CalledProcessError: + logger.error("Build failed") + sys.exit(1) elif action == "boot": logger.info("Uploading runtime") From acc5e53b32597adc749fe990ff7eed713e0168be Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 18:28:29 +0000 Subject: [PATCH 44/47] runtime: print microsecond timestamps in debug messages. --- artiq/runtime.rs/src/clock.rs | 7 +++++++ artiq/runtime.rs/src/logger.rs | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/artiq/runtime.rs/src/clock.rs b/artiq/runtime.rs/src/clock.rs index 3c203a196..40e56eea9 100644 --- a/artiq/runtime.rs/src/clock.rs +++ b/artiq/runtime.rs/src/clock.rs @@ -12,6 +12,13 @@ pub fn init() { } } +pub fn get_us() -> u64 { + unsafe { + csr::timer0::update_value_write(1); + (INIT - csr::timer0::value_read()) / (FREQ / 1_000_000) + } +} + pub fn get_ms() -> u64 { unsafe { csr::timer0::update_value_write(1); diff --git a/artiq/runtime.rs/src/logger.rs b/artiq/runtime.rs/src/logger.rs index d09995bf7..e01248da2 100644 --- a/artiq/runtime.rs/src/logger.rs +++ b/artiq/runtime.rs/src/logger.rs @@ -2,6 +2,7 @@ use core::{mem, ptr}; use core::cell::RefCell; use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; use log_buffer::LogBuffer; +use clock; pub struct BufferLogger { buffer: RefCell> @@ -57,10 +58,11 @@ impl Log for BufferLogger { fn log(&self, record: &LogRecord) { if self.enabled(record.metadata()) { use core::fmt::Write; - writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}", - record.level(), record.target(), record.args()).unwrap(); - println!("{:>5}({}): {}", - record.level(), record.target(), record.args()); + writeln!(self.buffer.borrow_mut(), + "[{:12}us] {:>5}({}): {}", + clock::get_us(), record.level(), record.target(), record.args()).unwrap(); + println!("[{:12}us] {:>5}({}): {}", + clock::get_us(), record.level(), record.target(), record.args()); } } } From 3ce1826891e35a25aceb2fd766e44b446cf8a5a5 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 20:16:25 +0000 Subject: [PATCH 45/47] runtime: don't print debug messages to the UART. It takes ~4ms to print an empty log line because of how slow the UART is. This makes the log timestamps useless for debugging performance problems. After this commit, it takes ~75us to print an empty log line instead, which pessimizes test_rpc_timing by less than 2ms with tracing enabled. --- artiq/runtime.rs/src/lib.rs | 2 +- artiq/runtime.rs/src/logger.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 48efd5d0d..b3350dfc8 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -97,7 +97,7 @@ pub extern "C" fn _Unwind_Resume() -> ! { #[no_mangle] pub unsafe extern fn rust_main() { - static mut LOG_BUFFER: [u8; 4096] = [0; 4096]; + static mut LOG_BUFFER: [u8; 65536] = [0; 65536]; BufferLogger::new(&mut LOG_BUFFER[..]) .register(move || { info!("booting ARTIQ..."); diff --git a/artiq/runtime.rs/src/logger.rs b/artiq/runtime.rs/src/logger.rs index e01248da2..77d1d01d5 100644 --- a/artiq/runtime.rs/src/logger.rs +++ b/artiq/runtime.rs/src/logger.rs @@ -1,6 +1,6 @@ use core::{mem, ptr}; use core::cell::RefCell; -use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; +use log::{self, Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter}; use log_buffer::LogBuffer; use clock; @@ -61,8 +61,10 @@ impl Log for BufferLogger { writeln!(self.buffer.borrow_mut(), "[{:12}us] {:>5}({}): {}", clock::get_us(), record.level(), record.target(), record.args()).unwrap(); - println!("[{:12}us] {:>5}({}): {}", - clock::get_us(), record.level(), record.target(), record.args()); + if record.level() <= LogLevel::Info { + println!("[{:12}us] {:>5}({}): {}", + clock::get_us(), record.level(), record.target(), record.args()); + } } } } From feed91d8b2bb73b49ce89e52678d19e1988254c7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 12 Nov 2016 23:06:33 +0000 Subject: [PATCH 46/47] runtime: buffer RPC send packets. This brings mean RPC time from ~45ms to ~2ms. The cause of the slowness without buffering is, primarily, that lwip is severely pessimized by small writes, whether with Nagle on or off. (In fact, disabling Nagle makes it function *better* on many small writes, which begs the question of what's the point of having Nagle there in the first place.) In practical terms, the slowness appears only when writing a 4-byte buffer (the synchronization segment); writing buffers of other sizes does not trigger the problem. This all is extremely confusing and the fix is partly palliative, but since it seems to work reliably and we're migrating off lwip I think it is unwise to spend any more time debugging this. --- artiq/runtime.rs/src/session.rs | 7 ++++--- artiq/test/coredevice/test_rtio.py | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 8ad957a61..8589971a0 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -114,7 +114,7 @@ fn host_read(stream: &mut TcpStream) -> io::Result { Ok(request) } -fn host_write(stream: &mut TcpStream, reply: host::Reply) -> io::Result<()> { +fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> { trace!("comm->host {:?}", reply); reply.write_to(stream) } @@ -389,8 +389,9 @@ fn process_kern_message(waiter: Waiter, match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { - try!(host_write(stream, host::Reply::RpcRequest { async: async })); - try!(rpc::send_args(&mut BufWriter::new(stream), service, tag, data)); + let writer = &mut BufWriter::new(stream); + try!(host_write(writer, host::Reply::RpcRequest { async: async })); + try!(rpc::send_args(writer, service, tag, data)); if !async { session.kernel_state = KernelState::RpcWait } diff --git a/artiq/test/coredevice/test_rtio.py b/artiq/test/coredevice/test_rtio.py index f60abd643..061d5fc3c 100644 --- a/artiq/test/coredevice/test_rtio.py +++ b/artiq/test/coredevice/test_rtio.py @@ -450,6 +450,8 @@ class RPCTest(ExperimentCase): "timings are dependent on CPU load and network conditions") def test_rpc_timing(self): self.execute(RPCTiming) - self.assertGreater(self.dataset_mgr.get("rpc_time_mean"), 100*ns) - self.assertLess(self.dataset_mgr.get("rpc_time_mean"), 15*ms) - self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 2*ms) + rpc_time_mean = self.dataset_mgr.get("rpc_time_mean") + print(rpc_time_mean) + self.assertGreater(rpc_time_mean, 100*ns) + self.assertLess(rpc_time_mean, 2*ms) + self.assertLess(self.dataset_mgr.get("rpc_time_stddev"), 1*ms) From 18c394976ebd841c3ba31e5e72a92821a81e3cc0 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 13 Nov 2016 00:33:24 +0000 Subject: [PATCH 47/47] runtime: disable the Nagle algorithm entirely. See also commit feed91d; that commit fixed the test_rpc_timing test, but caused frequent hangs elsewhere, which were also caused by buggy Nagle implementation. Just disable this entirely, as with our explicit buffering it provides no benefit anyway. --- artiq/runtime.rs/liblwip-sys/lib.rs | 1 + artiq/runtime.rs/liblwip/lib.rs | 1 + artiq/runtime/main.c | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/artiq/runtime.rs/liblwip-sys/lib.rs b/artiq/runtime.rs/liblwip-sys/lib.rs index 82a7c5bcf..912b7c381 100644 --- a/artiq/runtime.rs/liblwip-sys/lib.rs +++ b/artiq/runtime.rs/liblwip-sys/lib.rs @@ -149,6 +149,7 @@ extern { // nonstandard pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; pub fn tcp_so_options_(pcb: *mut tcp_pcb) -> *mut u8; + pub fn tcp_nagle_disable_(pcb: *mut tcp_pcb); pub fn udp_new() -> *mut udp_pcb; pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb; diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index f94b65dac..7eed9497b 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -549,6 +549,7 @@ impl TcpStream { lwip_sys::tcp_recv(raw, Some(recv)); lwip_sys::tcp_sent(raw, Some(sent)); lwip_sys::tcp_err(raw, Some(err)); + lwip_sys::tcp_nagle_disable_(raw); TcpStream { raw: raw, state: state } } } diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index d5db5e5dc..c9f6f0951 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -177,6 +177,10 @@ u8_t* tcp_so_options_(struct tcp_pcb *pcb) { return &pcb->so_options; } +void tcp_nagle_disable_(struct tcp_pcb *pcb) { + tcp_nagle_disable(pcb); +} + int main(void) { irq_setmask(0);