From 9562d8d1df5df9d71623b4eb8b44824e8c2b918c Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 23 Sep 2016 08:14:09 +0000 Subject: [PATCH 001/127] Rust: liblwip: call tcp_recved where appropriate. --- artiq/runtime.rs/liblwip/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index 49af078a0..7c1c32c53 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -505,7 +505,10 @@ impl TcpStream { Some(_) => () } match state.recv_buffer.pop_front() { - Some(Ok(pbuf)) => return Ok(Some(pbuf)), + Some(Ok(pbuf)) => { + unsafe { lwip_sys::tcp_recved(self.raw, pbuf.len() as u16) } + return Ok(Some(pbuf)) + }, _ => unreachable!() } } From b8137103c3334dd91b3f4c53f7d3819f456e43ac Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 23 Sep 2016 08:13:14 +0000 Subject: [PATCH 002/127] Rust: fix prelude. --- artiq/runtime.rs/libstd_artiq/io/buffered.rs | 2 ++ artiq/runtime.rs/libstd_artiq/io/cursor.rs | 2 ++ artiq/runtime.rs/libstd_artiq/io/prelude.rs | 3 --- artiq/runtime.rs/libstd_artiq/lib.rs | 7 ++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/artiq/runtime.rs/libstd_artiq/io/buffered.rs b/artiq/runtime.rs/libstd_artiq/io/buffered.rs index e23b74ff1..3088e4e63 100644 --- a/artiq/runtime.rs/libstd_artiq/io/buffered.rs +++ b/artiq/runtime.rs/libstd_artiq/io/buffered.rs @@ -17,6 +17,8 @@ use core::cmp; use core::fmt; use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; use io::memchr; +use alloc::boxed::Box; +use collections::vec::Vec; /// The `BufReader` struct adds buffering to any reader. /// diff --git a/artiq/runtime.rs/libstd_artiq/io/cursor.rs b/artiq/runtime.rs/libstd_artiq/io/cursor.rs index 1db50ee5b..88e7646d5 100644 --- a/artiq/runtime.rs/libstd_artiq/io/cursor.rs +++ b/artiq/runtime.rs/libstd_artiq/io/cursor.rs @@ -13,6 +13,8 @@ use io::prelude::*; use core::cmp; use io::{self, SeekFrom, Error, ErrorKind}; +use alloc::boxed::Box; +use collections::vec::Vec; /// A `Cursor` wraps another type and provides it with a /// [`Seek`](trait.Seek.html) implementation. diff --git a/artiq/runtime.rs/libstd_artiq/io/prelude.rs b/artiq/runtime.rs/libstd_artiq/io/prelude.rs index eae5acf07..58df71e69 100644 --- a/artiq/runtime.rs/libstd_artiq/io/prelude.rs +++ b/artiq/runtime.rs/libstd_artiq/io/prelude.rs @@ -20,6 +20,3 @@ pub use super::{Read, Write, Seek}; pub use super::BufRead; - - pub use alloc::boxed::Box; - pub use collections::vec::Vec; diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 1ec357302..83c4fd83d 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -21,9 +21,10 @@ pub use collections::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, lin pub mod prelude { pub mod v1 { pub use core::prelude::v1::*; - pub use collections::*; - pub use io::{Read, Write, Seek}; - pub use io::BufRead; + pub use collections::boxed::Box; + pub use collections::borrow::ToOwned; + pub use collections::string::{String, ToString}; + pub use collections::vec::Vec; } } From 89d4621c09310953afb96a4946e96828dd85f3b1 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 27 Sep 2016 13:19:32 +0000 Subject: [PATCH 003/127] Rust: fix TcpStream::read. --- artiq/runtime.rs/src/io.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime.rs/src/io.rs b/artiq/runtime.rs/src/io.rs index 0607be728..a89e2e82f 100644 --- a/artiq/runtime.rs/src/io.rs +++ b/artiq/runtime.rs/src/io.rs @@ -293,7 +293,7 @@ impl<'a> Read for TcpStream<'a> { let (pbuf, pos) = self.buffer.take().unwrap(); let slice = &pbuf.as_slice()[pos..]; let len = ::std::cmp::min(buf.len(), slice.len()); - buf.copy_from_slice(&slice[..len]); + buf[..len].copy_from_slice(&slice[..len]); if len < slice.len() { self.buffer = Some((pbuf, pos + len)) } From b14c19a886aaeb41e4b56d4f8ea8ea2d95de47f4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 27 Sep 2016 13:36:55 +0000 Subject: [PATCH 004/127] Rust: add skeleton session protocol implementation. Only ident requests are supported right now. --- artiq/runtime.rs/Cargo.lock | 7 + artiq/runtime.rs/Cargo.toml | 3 +- artiq/runtime.rs/libstd_artiq/lib.rs | 3 +- artiq/runtime.rs/src/lib.rs | 25 +-- artiq/runtime.rs/src/session/mod.rs | 103 +++++++++ artiq/runtime.rs/src/session/protocol.rs | 272 +++++++++++++++++++++++ 6 files changed, 389 insertions(+), 24 deletions(-) create mode 100644 artiq/runtime.rs/src/session/mod.rs create mode 100644 artiq/runtime.rs/src/session/protocol.rs diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index 6e5a7538d..9fc25d063 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -2,6 +2,7 @@ name = "runtime" version = "0.0.0" dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lwip 0.0.0", "std_artiq 0.0.0", @@ -11,6 +12,11 @@ dependencies = [ 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 = "fringe" version = "1.1.0" @@ -44,5 +50,6 @@ dependencies = [ ] [metadata] +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987689dcfad85eee8d76b477865641ec483e63fb86d52966bfc350c4a647d78a" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 962e076cb..adc6c37b2 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -10,8 +10,9 @@ path = "src/lib.rs" [dependencies] std_artiq = { path = "libstd_artiq" } -fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } lwip = { path = "liblwip" } +fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } +byteorder = { version = "0.5", default-features = false } [profile.dev] panic = 'abort' diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 83c4fd83d..9e95c2a24 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -1,6 +1,6 @@ #![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, question_mark, unicode, reflect_marker, raw, int_error_internals, - try_from, try_borrow)] + try_from, try_borrow, macro_reexport, allow_internal_unstable)] #![no_std] #![needs_panic_runtime] @@ -8,6 +8,7 @@ extern crate rustc_unicode; extern crate alloc_artiq; extern crate alloc; #[macro_use] +#[macro_reexport(vec)] extern crate collections; extern crate libc; diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 04e987129..f482efeaa 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -2,44 +2,25 @@ #[macro_use] extern crate std_artiq as std; +extern crate byteorder; use std::prelude::v1::*; pub mod io; +pub mod session; extern { fn network_init(); fn lwip_service(); } -fn timer(waiter: io::Waiter) { - loop { - println!("tick"); - waiter.sleep(std::time::Duration::from_millis(1000)).unwrap(); - } -} - -fn echo(waiter: io::Waiter) { - let addr = io::SocketAddr::new(io::IP_ANY, 1234); - let listener = io::TcpListener::bind(waiter, addr).unwrap(); - loop { - let (mut stream, _addr) = listener.accept().unwrap(); - loop { - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - stream.write(&buf).unwrap(); - } - } -} - #[no_mangle] pub unsafe extern fn rust_main() { println!("Accepting network sessions in Rust."); network_init(); let mut scheduler = io::Scheduler::new(); - scheduler.spawn(4096, timer); - scheduler.spawn(4096, echo); + scheduler.spawn(4096, session::handler); loop { lwip_service(); scheduler.run() diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session/mod.rs new file mode 100644 index 000000000..b97af72bf --- /dev/null +++ b/artiq/runtime.rs/src/session/mod.rs @@ -0,0 +1,103 @@ +use std::prelude::v1::*; +use std::io::{self, Read}; +use self::protocol::*; + +mod protocol; + +#[derive(Debug, Clone, Copy)] +enum KernelState { + Absent, + Loaded, + Running, + RpcWait +} + +#[derive(Debug)] +pub struct Session { + kernel_state: KernelState, +} + +extern { + fn kloader_stop(); + fn watchdog_init(); + fn kloader_start_idle_kernel(); +} + +impl Session { + pub fn start() -> Session { + unsafe { kloader_stop(); } + Session { + kernel_state: KernelState::Absent + } + } + + pub fn end(self) { + unsafe { + kloader_stop(); + watchdog_init(); + kloader_start_idle_kernel(); + } + } +} + +fn check_magic(stream: &mut ::io::TcpStream) -> io::Result<()> { + const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; + + let mut magic: [u8; 14] = [0; 14]; + try!(stream.read_exact(&mut magic)); + if magic != MAGIC { + Err(io::Error::new(io::ErrorKind::InvalidData, "unrecognized magic")) + } else { + Ok(()) + } +} + +fn handle_request(stream: &mut ::io::TcpStream) -> io::Result<()> { + fn read_request(stream: &mut ::io::TcpStream) -> io::Result { + let request = try!(Request::read_from(stream)); + println!("comm<-host {:?}", request); + Ok(request) + } + + fn write_reply(stream: &mut ::io::TcpStream, reply: Reply) -> io::Result<()> { + println!("comm->host {:?}", reply); + reply.write_to(stream) + } + + match try!(read_request(stream)) { + Request::Ident => { + let mut ident: [u8; 256]; + let ident = unsafe { + extern { fn get_ident(ident: *mut u8); } + + ident = ::core::mem::uninitialized(); + get_ident(ident.as_mut_ptr()); + &ident[..ident.iter().position(|&c| c == 0).unwrap()] + }; + + write_reply(stream, Reply::Ident(ident)) + }, + _ => unreachable!() + } +} + +fn handle_requests(stream: &mut ::io::TcpStream) -> io::Result<()> { + try!(check_magic(stream)); + loop { + try!(handle_request(stream)) + } +} + +pub fn handler(waiter: ::io::Waiter) { + let addr = ::io::SocketAddr::new(::io::IP_ANY, 1381); + let listener = ::io::TcpListener::bind(waiter, addr).unwrap(); + loop { + let (mut stream, _addr) = listener.accept().unwrap(); + match handle_requests(&mut stream) { + Ok(()) => (), + Err(err) => { + println!("cannot handle network request: {:?}", err); + } + } + } +} diff --git a/artiq/runtime.rs/src/session/protocol.rs b/artiq/runtime.rs/src/session/protocol.rs new file mode 100644 index 000000000..cdb27a3f7 --- /dev/null +++ b/artiq/runtime.rs/src/session/protocol.rs @@ -0,0 +1,272 @@ +use std::prelude::v1::*; +use std::io::{self, Read, Write}; +use byteorder::{ByteOrder, NetworkEndian}; + +// FIXME: replace these with byteorder core io traits once those are in +fn read_u8(reader: &mut Read) -> io::Result { + let mut bytes = [0; 1]; + try!(reader.read_exact(&mut bytes)); + Ok(bytes[0]) +} + +fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> { + let bytes = [value; 1]; + writer.write_all(&bytes) +} + +fn read_u32(reader: &mut Read) -> io::Result { + let mut bytes = [0; 4]; + try!(reader.read_exact(&mut bytes)); + Ok(NetworkEndian::read_u32(&bytes)) +} + +fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> { + let mut bytes = [0; 4]; + NetworkEndian::write_u32(&mut bytes, value); + writer.write_all(&bytes) +} + +fn read_u64(reader: &mut Read) -> io::Result { + let mut bytes = [0; 4]; + try!(reader.read_exact(&mut bytes)); + Ok(NetworkEndian::read_u64(&bytes)) +} + +fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> { + let mut bytes = [0; 4]; + NetworkEndian::write_u64(&mut bytes, value); + writer.write_all(&bytes) +} + +fn read_bytes(reader: &mut Read) -> io::Result> { + let length = try!(read_u32(reader)); + let mut value = vec![0; length as usize]; + try!(reader.read_exact(&mut value)); + Ok(value) +} + +fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { + try!(write_u32(writer, value.len() as u32)); + writer.write_all(value) +} + +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)) +} + +fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { + write_bytes(writer, value.as_bytes()) +} + +fn read_sync(reader: &mut Read) -> io::Result<()> { + let mut sync = [0; 4]; + for i in 0.. { + sync[i % 4] = try!(read_u8(reader)); + if sync == [0x5a; 4] { break } + } + Ok(()) +} + +fn write_sync(writer: &mut Write) -> io::Result<()> { + writer.write_all(&[0x5a; 4]) +} + +#[derive(Debug)] +pub struct Exception { + name: String, + message: String, + param: [u64; 3], + file: String, + line: u32, + column: u32, + function: String, +} + +impl Exception { + pub fn read_from(reader: &mut Read) -> io::Result { + Ok(Exception { + name: try!(read_string(reader)), + message: try!(read_string(reader)), + param: [try!(read_u64(reader)), + try!(read_u64(reader)), + try!(read_u64(reader))], + file: try!(read_string(reader)), + line: try!(read_u32(reader)), + column: try!(read_u32(reader)), + function: try!(read_string(reader)) + }) + } + + pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { + try!(write_string(writer, &self.name)); + try!(write_string(writer, &self.message)); + try!(write_u64(writer, self.param[0])); + try!(write_u64(writer, self.param[1])); + try!(write_u64(writer, self.param[2])); + try!(write_string(writer, &self.file)); + try!(write_u32(writer, self.line)); + try!(write_u32(writer, self.column)); + try!(write_string(writer, &self.function)); + Ok(()) + } +} + +#[derive(Debug)] +pub enum Request { + Log, + LogClear, + + Ident, + SwitchClock(u8), + + LoadLibrary(Vec), + RunKernel, + + RpcReply { tag: String }, // FIXME + RpcException(Exception), + + FlashRead { key: String }, + FlashWrite { key: String, value: Vec }, + FlashErase { key: String }, + FlashRemove { key: String }, +} + +impl Request { + pub fn read_from(reader: &mut Read) -> io::Result { + const HEADER_SIZE: usize = 9; + + try!(read_sync(reader)); + let length = try!(read_u32(reader)) as usize; + let type_ = try!(read_u8(reader)); + + Ok(match type_ { + 1 => Request::Log, + 2 => Request::LogClear, + 3 => Request::Ident, + 4 => Request::SwitchClock(try!(read_u8(reader))), + 5 => { + let mut code = vec![0; length - HEADER_SIZE]; + try!(reader.read_exact(&mut code)); + Request::LoadLibrary(code) + }, + 6 => Request::RunKernel, + 7 => Request::RpcReply { + tag: try!(read_string(reader)) + }, + 8 => Request::RpcException(try!(Exception::read_from(reader))), + 9 => Request::FlashRead { + key: try!(read_string(reader)) + }, + 10 => Request::FlashWrite { + key: try!(read_string(reader)), + value: try!(read_bytes(reader)) + }, + 11 => Request::FlashErase { + key: try!(read_string(reader)) + }, + 12 => Request::FlashRemove { + key: try!(read_string(reader)) + }, + _ => unreachable!() + }) + } +} + +#[derive(Debug)] +pub enum Reply<'a> { + Log(&'a str), + + Ident(&'a [u8]), + ClockSwitchCompleted, + ClockSwitchFailed, + + LoadCompleted, + LoadFailed, + + KernelFinished, + KernelStartupFailed, + KernelException(Exception), + + RpcRequest { service: u32 }, + + FlashRead(&'a [u8]), + FlashOk, + FlashError, + + WatchdogExpired, + ClockFailure, +} + +impl<'a> Reply<'a> { + pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { + let mut buf = Vec::new(); + try!(write_sync(&mut buf)); + try!(write_u32(&mut buf, 0)); // length placeholder + + match *self { + Reply::Log(ref log) => { + try!(write_u8(&mut buf, 1)); + try!(buf.write(log.as_bytes())); + }, + + Reply::Ident(ident) => { + try!(write_u8(&mut buf, 2)); + try!(buf.write(b"AROR")); + try!(buf.write(ident)); + }, + Reply::ClockSwitchCompleted => { + try!(write_u8(&mut buf, 3)); + }, + Reply::ClockSwitchFailed => { + try!(write_u8(&mut buf, 4)); + }, + + Reply::LoadCompleted => { + try!(write_u8(&mut buf, 5)); + }, + Reply::LoadFailed => { + try!(write_u8(&mut buf, 6)); + }, + + Reply::KernelFinished => { + try!(write_u8(&mut buf, 7)); + }, + Reply::KernelStartupFailed => { + try!(write_u8(&mut buf, 8)); + }, + Reply::KernelException(ref exception) => { + try!(write_u8(&mut buf, 9)); + try!(exception.write_to(writer)); + }, + + Reply::RpcRequest { service } => { + try!(write_u8(&mut buf, 10)); + try!(write_u32(&mut buf, service)); + }, + + Reply::FlashRead(ref bytes) => { + try!(write_u8(&mut buf, 11)); + try!(buf.write(bytes)); + }, + Reply::FlashOk => { + try!(write_u8(&mut buf, 12)); + }, + Reply::FlashError => { + try!(write_u8(&mut buf, 13)); + }, + + Reply::WatchdogExpired => { + try!(write_u8(&mut buf, 14)); + }, + Reply::ClockFailure => { + try!(write_u8(&mut buf, 15)); + }, + } + + let len = buf.len(); + try!(write_u32(&mut &mut buf[4..8], len as u32)); + + writer.write_all(&buf) + } +} From fdcb27ccff7359e6154cfb29dcc69a07751d02f1 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 28 Sep 2016 18:25:25 +0000 Subject: [PATCH 005/127] Rust: add support for artiq_corelog. --- artiq/runtime.rs/Cargo.lock | 14 ++++++ artiq/runtime.rs/Cargo.toml | 2 + artiq/runtime.rs/src/buffer_logger.rs | 57 ++++++++++++++++++++++++ artiq/runtime.rs/src/io.rs | 10 +++-- artiq/runtime.rs/src/lib.rs | 28 ++++++++---- artiq/runtime.rs/src/session/mod.rs | 48 +++++++++++++++----- artiq/runtime.rs/src/session/protocol.rs | 4 +- 7 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 artiq/runtime.rs/src/buffer_logger.rs diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index 9fc25d063..5114170b2 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -4,6 +4,8 @@ version = "0.0.0" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lwip 0.0.0", "std_artiq 0.0.0", ] @@ -30,6 +32,16 @@ name = "libc" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log_buffer" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lwip" version = "0.0.0" @@ -53,3 +65,5 @@ dependencies = [ "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987689dcfad85eee8d76b477865641ec483e63fb86d52966bfc350c4a647d78a" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" +"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8beb5ba24eca52f9958874445c4de5e086a7e82a1ec6b7ab81e5fcfb134f25a" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index adc6c37b2..fe5770958 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -12,6 +12,8 @@ path = "src/lib.rs" std_artiq = { path = "libstd_artiq" } lwip = { path = "liblwip" } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } +log = { version = "0.3", default-features = false } +log_buffer = { version = "1.0" } byteorder = { version = "0.5", default-features = false } [profile.dev] diff --git a/artiq/runtime.rs/src/buffer_logger.rs b/artiq/runtime.rs/src/buffer_logger.rs new file mode 100644 index 000000000..4ec9927eb --- /dev/null +++ b/artiq/runtime.rs/src/buffer_logger.rs @@ -0,0 +1,57 @@ +use core::mem; +use core::cell::RefCell; +use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; +use log_buffer::LogBuffer; + +pub struct BufferLogger { + buffer: RefCell> +} + +// We can never preempt from within the logger, so there can be no data races. +unsafe impl Sync for BufferLogger {} + +impl BufferLogger { + pub fn new(buffer: &'static mut [u8]) -> BufferLogger { + BufferLogger { + buffer: RefCell::new(LogBuffer::new(buffer)) + } + } + + pub fn register(&self, f: F) { + // log::set_logger_raw captures a pointer to ourselves, so we must prevent + // ourselves from being moved or dropped after that function is called (and + // before log::shutdown_logger_raw is called). + unsafe { + log::set_logger_raw(|max_log_level| { + max_log_level.set(LogLevelFilter::Trace); + self as *const Log + }).expect("global logger can only be initialized once"); + } + f(self); + log::shutdown_logger_raw().unwrap(); + } + + pub fn clear(&self) { + self.buffer.borrow_mut().clear() + } + + pub fn extract R>(&self, f: F) -> R { + f(self.buffer.borrow_mut().extract()) + } +} + +impl Log for BufferLogger { + fn enabled(&self, _metadata: &log::LogMetadata) -> bool { + true + } + + fn log(&self, record: &LogRecord) { + if self.enabled(record.metadata()) { + use core::fmt::Write; + writeln!(self.buffer.borrow_mut(), "{}({}): {}", + record.level(), record.location().module_path(), record.args()).unwrap(); + println!("{}({}): {}", + record.level(), record.location().module_path(), record.args()); + } + } +} diff --git a/artiq/runtime.rs/src/io.rs b/artiq/runtime.rs/src/io.rs index a89e2e82f..f48d6d6ff 100644 --- a/artiq/runtime.rs/src/io.rs +++ b/artiq/runtime.rs/src/io.rs @@ -39,7 +39,7 @@ impl Scheduler { Scheduler { threads: Vec::new(), index: 0 } } - pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { + pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { let stack = OwnedStack::new(stack_size); let thread = Thread { generator: Generator::unsafe_new(stack, move |yielder, _| { @@ -286,8 +286,12 @@ impl<'a> Read for TcpStream<'a> { fn read(&mut self, buf: &mut [u8]) -> Result { if self.buffer.is_none() { try!(self.waiter.tcp_readable(&self.lower)); - let pbuf = try!(self.lower.try_read()).unwrap(); - self.buffer = Some((pbuf, 0)) + match self.lower.try_read() { + Ok(Some(pbuf)) => self.buffer = Some((pbuf, 0)), + Ok(None) => unreachable!(), + Err(lwip::Error::ConnectionClosed) => return Ok(0), + Err(err) => return Err(Error::from(err)) + } } let (pbuf, pos) = self.buffer.take().unwrap(); diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index f482efeaa..afb7003c1 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,12 +1,18 @@ #![no_std] +#![feature(const_fn)] #[macro_use] extern crate std_artiq as std; +#[macro_use] +extern crate log; +extern crate log_buffer; extern crate byteorder; use std::prelude::v1::*; +use buffer_logger::BufferLogger; pub mod io; +pub mod buffer_logger; pub mod session; extern { @@ -16,13 +22,19 @@ extern { #[no_mangle] pub unsafe extern fn rust_main() { - println!("Accepting network sessions in Rust."); - network_init(); + static mut log_buffer: [u8; 4096] = [0; 4096]; + BufferLogger::new(&mut log_buffer[..]) + .register(move |logger| { + info!("Accepting network sessions in Rust."); + network_init(); - let mut scheduler = io::Scheduler::new(); - scheduler.spawn(4096, session::handler); - loop { - lwip_service(); - scheduler.run() - } + let mut scheduler = io::Scheduler::new(); + scheduler.spawn(4096, move |waiter| { + session::handler(waiter, logger) + }); + loop { + lwip_service(); + scheduler.run() + } + }) } diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session/mod.rs index b97af72bf..f4bec57a9 100644 --- a/artiq/runtime.rs/src/session/mod.rs +++ b/artiq/runtime.rs/src/session/mod.rs @@ -1,5 +1,6 @@ use std::prelude::v1::*; -use std::io::{self, Read}; +use std::str; +use std::io::{self, Read, ErrorKind}; use self::protocol::*; mod protocol; @@ -52,15 +53,16 @@ fn check_magic(stream: &mut ::io::TcpStream) -> io::Result<()> { } } -fn handle_request(stream: &mut ::io::TcpStream) -> io::Result<()> { +fn handle_request(stream: &mut ::io::TcpStream, + logger: &::buffer_logger::BufferLogger) -> io::Result<()> { fn read_request(stream: &mut ::io::TcpStream) -> io::Result { let request = try!(Request::read_from(stream)); - println!("comm<-host {:?}", request); + trace!("comm<-host {:?}", request); Ok(request) } fn write_reply(stream: &mut ::io::TcpStream, reply: Reply) -> io::Result<()> { - println!("comm->host {:?}", reply); + trace!("comm->host {:?}", reply); reply.write_to(stream) } @@ -75,28 +77,50 @@ fn handle_request(stream: &mut ::io::TcpStream) -> io::Result<()> { &ident[..ident.iter().position(|&c| c == 0).unwrap()] }; - write_reply(stream, Reply::Ident(ident)) - }, + write_reply(stream, Reply::Ident(str::from_utf8(ident).unwrap())) + } + + Request::Log => { + // Logging the packet with the log is inadvisable + trace!("comm->host Log(...)"); + logger.extract(move |log| { + Reply::Log(log).write_to(stream) + }) + } + + Request::LogClear => { + logger.clear(); + write_reply(stream, Reply::Log("")) + } + _ => unreachable!() } } -fn handle_requests(stream: &mut ::io::TcpStream) -> io::Result<()> { +fn handle_requests(stream: &mut ::io::TcpStream, + logger: &::buffer_logger::BufferLogger) -> io::Result<()> { try!(check_magic(stream)); loop { - try!(handle_request(stream)) + try!(handle_request(stream, logger)) } } -pub fn handler(waiter: ::io::Waiter) { +pub fn handler(waiter: ::io::Waiter, + logger: &::buffer_logger::BufferLogger) { let addr = ::io::SocketAddr::new(::io::IP_ANY, 1381); let listener = ::io::TcpListener::bind(waiter, addr).unwrap(); loop { - let (mut stream, _addr) = listener.accept().unwrap(); - match handle_requests(&mut stream) { + let (mut stream, addr) = listener.accept().unwrap(); + info!("new connection from {:?}", addr); + + match handle_requests(&mut stream, logger) { Ok(()) => (), Err(err) => { - println!("cannot handle network request: {:?}", err); + if err.kind() == ErrorKind::UnexpectedEof { + info!("connection closed"); + } else { + error!("cannot handle network request: {:?}", err); + } } } } diff --git a/artiq/runtime.rs/src/session/protocol.rs b/artiq/runtime.rs/src/session/protocol.rs index cdb27a3f7..deaa2abdd 100644 --- a/artiq/runtime.rs/src/session/protocol.rs +++ b/artiq/runtime.rs/src/session/protocol.rs @@ -177,7 +177,7 @@ impl Request { pub enum Reply<'a> { Log(&'a str), - Ident(&'a [u8]), + Ident(&'a str), ClockSwitchCompleted, ClockSwitchFailed, @@ -213,7 +213,7 @@ impl<'a> Reply<'a> { Reply::Ident(ident) => { try!(write_u8(&mut buf, 2)); try!(buf.write(b"AROR")); - try!(buf.write(ident)); + try!(buf.write(ident.as_bytes())); }, Reply::ClockSwitchCompleted => { try!(write_u8(&mut buf, 3)); From 3263def5c8b2c3997c067fde87065e98d002e07d Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 14:04:42 +0000 Subject: [PATCH 006/127] Rust: use generated CSR functions. --- artiq/runtime.rs/src/board.rs | 15 +++++++++++++++ artiq/runtime.rs/src/buffer_logger.rs | 4 ++-- artiq/runtime.rs/src/lib.rs | 1 + artiq/runtime.rs/src/session/mod.rs | 11 +---------- 4 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 artiq/runtime.rs/src/board.rs diff --git a/artiq/runtime.rs/src/board.rs b/artiq/runtime.rs/src/board.rs new file mode 100644 index 000000000..aa97b1edb --- /dev/null +++ b/artiq/runtime.rs/src/board.rs @@ -0,0 +1,15 @@ +use core::{cmp, ptr, str}; + +include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); +include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); + +pub fn ident(buf: &mut [u8]) -> &str { + unsafe { + let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE); + let len = cmp::min(len as usize, buf.len()); + for i in 0..len { + buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8 + } + str::from_utf8_unchecked(&buf[..len]) + } +} diff --git a/artiq/runtime.rs/src/buffer_logger.rs b/artiq/runtime.rs/src/buffer_logger.rs index 4ec9927eb..e10a5a876 100644 --- a/artiq/runtime.rs/src/buffer_logger.rs +++ b/artiq/runtime.rs/src/buffer_logger.rs @@ -48,9 +48,9 @@ impl Log for BufferLogger { fn log(&self, record: &LogRecord) { if self.enabled(record.metadata()) { use core::fmt::Write; - writeln!(self.buffer.borrow_mut(), "{}({}): {}", + writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}", record.level(), record.location().module_path(), record.args()).unwrap(); - println!("{}({}): {}", + println!("{:>5}({}): {}", record.level(), record.location().module_path(), record.args()); } } diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index afb7003c1..22feccda2 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -11,6 +11,7 @@ extern crate byteorder; use std::prelude::v1::*; use buffer_logger::BufferLogger; +pub mod board; pub mod io; pub mod buffer_logger; pub mod session; diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session/mod.rs index f4bec57a9..b4d05bb99 100644 --- a/artiq/runtime.rs/src/session/mod.rs +++ b/artiq/runtime.rs/src/session/mod.rs @@ -68,16 +68,7 @@ fn handle_request(stream: &mut ::io::TcpStream, match try!(read_request(stream)) { Request::Ident => { - let mut ident: [u8; 256]; - let ident = unsafe { - extern { fn get_ident(ident: *mut u8); } - - ident = ::core::mem::uninitialized(); - get_ident(ident.as_mut_ptr()); - &ident[..ident.iter().position(|&c| c == 0).unwrap()] - }; - - write_reply(stream, Reply::Ident(str::from_utf8(ident).unwrap())) + write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))) } Request::Log => { From 1e392cca648c4147e1b65d2eab1f88dccb5a0dbb Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 14:48:26 +0000 Subject: [PATCH 007/127] runtime: remove "test mode" functionality. --- artiq/runtime/Makefile | 6 +- artiq/runtime/bridge.c | 131 ------- artiq/runtime/bridge.h | 6 - artiq/runtime/bridge_ctl.c | 106 ------ artiq/runtime/bridge_ctl.h | 15 - artiq/runtime/kloader.c | 5 - artiq/runtime/kloader.h | 1 - artiq/runtime/ksupport.c | 6 - artiq/runtime/main.c | 53 +-- artiq/runtime/messages.h | 54 --- artiq/runtime/test_mode.c | 700 ------------------------------------- artiq/runtime/test_mode.h | 6 - artiq/runtime/ttl.h | 8 - 13 files changed, 8 insertions(+), 1089 deletions(-) delete mode 100644 artiq/runtime/bridge.c delete mode 100644 artiq/runtime/bridge.h delete mode 100644 artiq/runtime/bridge_ctl.c delete mode 100644 artiq/runtime/bridge_ctl.h delete mode 100644 artiq/runtime/test_mode.c delete mode 100644 artiq/runtime/test_mode.h delete mode 100644 artiq/runtime/ttl.h diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index e0c276a12..bc9439a84 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -4,10 +4,10 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ - session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ - ksupport_data.o kloader.o test_mode.o main.o + session.o log.o analyzer.o moninj.o net_server.o \ + ksupport_data.o kloader.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o dds.o i2c.o + rtio.o dds.o i2c.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c deleted file mode 100644 index e5b40fefa..000000000 --- a/artiq/runtime/bridge.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "mailbox.h" -#include "messages.h" -#include "rtio.h" -#include "ttl.h" -#include "dds.h" -#include "bridge.h" - -#define TIME_BUFFER (8000 << CONFIG_RTIO_FINE_TS_WIDTH) - -static void rtio_output_blind(int channel, int addr, int data) -{ - rtio_chan_sel_write(channel); -#ifdef CSR_RTIO_O_ADDRESS_ADDR - rtio_o_address_write(addr); -#endif - rtio_o_data_write(data); - rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER); - rtio_o_we_write(1); -} - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) -static void dds_write(int bus_channel, int addr, int data) -{ - rtio_output_blind(bus_channel, addr, data); -} - -static int dds_read(int bus_channel, int addr) -{ - int r; - -#ifdef CONFIG_DDS_AD9858 -#define DDS_READ_FLAG 128 -#endif -#ifdef CONFIG_DDS_AD9914 -#define DDS_READ_FLAG 256 -#endif - dds_write(bus_channel, addr | DDS_READ_FLAG, 0); - while(rtio_i_status_read() & RTIO_I_STATUS_EMPTY); - r = rtio_i_data_read(); - rtio_i_re_write(1); - return r; -} -#endif - -static void send_ready(void) -{ - struct msg_base msg; - - msg.type = MESSAGE_TYPE_BRG_READY; - mailbox_send_and_wait(&msg); -} - -void bridge_main(void) -{ - struct msg_base *umsg; - - rtio_init(); - send_ready(); - while(1) { - umsg = mailbox_wait_and_receive(); - switch(umsg->type) { - case MESSAGE_TYPE_BRG_TTL_OE: { - struct msg_brg_ttl_out *msg; - - msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_BRG_TTL_O: { - struct msg_brg_ttl_out *msg; - - msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); - mailbox_acknowledge(); - break; - } -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - case MESSAGE_TYPE_BRG_DDS_SEL: { - struct msg_brg_dds_sel *msg; - - msg = (struct msg_brg_dds_sel *)umsg; - dds_write(msg->bus_channel, DDS_GPIO, msg->channel << 1); - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_BRG_DDS_RESET: { - unsigned int g; - struct msg_brg_dds_reset *msg; - - msg = (struct msg_brg_dds_reset *)umsg; - g = dds_read(msg->bus_channel, DDS_GPIO); - dds_write(msg->bus_channel, DDS_GPIO, g | 1); - dds_write(msg->bus_channel, DDS_GPIO, g); - - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_BRG_DDS_READ_REQUEST: { - struct msg_brg_dds_read_request *msg; - struct msg_brg_dds_read_reply rmsg; - - msg = (struct msg_brg_dds_read_request *)umsg; - rmsg.type = MESSAGE_TYPE_BRG_DDS_READ_REPLY; - rmsg.data = dds_read(msg->bus_channel, msg->address); - mailbox_send_and_wait(&rmsg); - break; - } - case MESSAGE_TYPE_BRG_DDS_WRITE: { - struct msg_brg_dds_write *msg; - - msg = (struct msg_brg_dds_write *)umsg; - dds_write(msg->bus_channel, msg->address, msg->data); - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_BRG_DDS_FUD: { - struct msg_brg_dds_fud *msg; - - msg = (struct msg_brg_dds_fud *)umsg; - dds_write(msg->bus_channel, DDS_FUD, 0); - mailbox_acknowledge(); - break; - } -#endif /* CONFIG_RTIO_DDS_COUNT */ - default: - mailbox_acknowledge(); - break; - } - } -} diff --git a/artiq/runtime/bridge.h b/artiq/runtime/bridge.h deleted file mode 100644 index d9e4f3654..000000000 --- a/artiq/runtime/bridge.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __BRIDGE_H -#define __BRIDGE_H - -void bridge_main(void); - -#endif /* __BRIDGE_H */ diff --git a/artiq/runtime/bridge_ctl.c b/artiq/runtime/bridge_ctl.c deleted file mode 100644 index 1ebc4b0bc..000000000 --- a/artiq/runtime/bridge_ctl.c +++ /dev/null @@ -1,106 +0,0 @@ -#include - -#include "kloader.h" -#include "mailbox.h" -#include "messages.h" -#include "bridge_ctl.h" - -void brg_start(void) -{ - struct msg_base *umsg; - - kloader_start_bridge(); - - while(1) { - umsg = mailbox_wait_and_receive(); - if(umsg->type == MESSAGE_TYPE_BRG_READY) { - mailbox_acknowledge(); - break; - } else { - printf("Warning: unexpected message %d from AMP bridge\n", umsg->type); - mailbox_acknowledge(); - } - } -} - -void brg_ttloe(int n, int value) -{ - struct msg_brg_ttl_out msg; - - msg.type = MESSAGE_TYPE_BRG_TTL_OE; - msg.channel = n; - msg.value = value; - mailbox_send_and_wait(&msg); -} - -void brg_ttlo(int n, int value) -{ - struct msg_brg_ttl_out msg; - - msg.type = MESSAGE_TYPE_BRG_TTL_O; - msg.channel = n; - msg.value = value; - mailbox_send_and_wait(&msg); -} - -void brg_ddssel(int bus_channel, int channel) -{ - struct msg_brg_dds_sel msg; - - msg.type = MESSAGE_TYPE_BRG_DDS_SEL; - msg.bus_channel = bus_channel; - msg.channel = channel; - mailbox_send_and_wait(&msg); -} - -void brg_ddsreset(int bus_channel) -{ - struct msg_brg_dds_reset msg; - - msg.type = MESSAGE_TYPE_BRG_DDS_RESET; - msg.bus_channel = bus_channel; - mailbox_send_and_wait(&msg); -} - -unsigned int brg_ddsread(int bus_channel, unsigned int address) -{ - struct msg_brg_dds_read_request msg; - struct msg_brg_dds_read_reply *rmsg; - unsigned int r; - - msg.type = MESSAGE_TYPE_BRG_DDS_READ_REQUEST; - msg.bus_channel = bus_channel; - msg.address = address; - mailbox_send(&msg); - while(1) { - rmsg = mailbox_wait_and_receive(); - if(rmsg->type == MESSAGE_TYPE_BRG_DDS_READ_REPLY) { - r = rmsg->data; - mailbox_acknowledge(); - return r; - } else { - printf("Warning: unexpected message %d from AMP bridge\n", rmsg->type); - mailbox_acknowledge(); - } - } -} - -void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data) -{ - struct msg_brg_dds_write msg; - - msg.type = MESSAGE_TYPE_BRG_DDS_WRITE; - msg.bus_channel = bus_channel; - msg.address = address; - msg.data = data; - mailbox_send_and_wait(&msg); -} - -void brg_ddsfud(int bus_channel) -{ - struct msg_brg_dds_fud msg; - - msg.type = MESSAGE_TYPE_BRG_DDS_FUD; - msg.bus_channel = bus_channel; - mailbox_send_and_wait(&msg); -} diff --git a/artiq/runtime/bridge_ctl.h b/artiq/runtime/bridge_ctl.h deleted file mode 100644 index 2929ee8f0..000000000 --- a/artiq/runtime/bridge_ctl.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __BRIDGE_CTL_H -#define __BRIDGE_CTL_H - -void brg_start(void); - -void brg_ttloe(int n, int value); -void brg_ttlo(int n, int value); - -void brg_ddssel(int bus_channel, int channel); -void brg_ddsreset(int bus_channel); -unsigned int brg_ddsread(int bus_channel, unsigned int address); -void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data); -void brg_ddsfud(int bus_channel); - -#endif /* __BRIDGE_CTL_H */ diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c index 19a4b4190..cf8c9610c 100644 --- a/artiq/runtime/kloader.c +++ b/artiq/runtime/kloader.c @@ -26,11 +26,6 @@ static void start_kernel_cpu(struct msg_load_request *msg) kernel_cpu_reset_write(0); } -void kloader_start_bridge() -{ - start_kernel_cpu(NULL); -} - static int load_or_start_kernel(const void *library, int run_kernel) { static struct dyld_info library_info; diff --git a/artiq/runtime/kloader.h b/artiq/runtime/kloader.h index feba89de9..2ca8d6a6f 100644 --- a/artiq/runtime/kloader.h +++ b/artiq/runtime/kloader.h @@ -12,7 +12,6 @@ int kloader_load_library(const void *code); void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, size_t *backtrace_size); -void kloader_start_bridge(void); int kloader_start_startup_kernel(void); int kloader_start_idle_kernel(void); void kloader_start_kernel(void); diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 60a8eb023..5bf530cbe 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -12,7 +12,6 @@ #include "kloader.h" #include "mailbox.h" #include "messages.h" -#include "bridge.h" #include "artiq_personality.h" #include "rtio.h" #include "dds.h" @@ -377,11 +376,6 @@ int main(void) .error = NULL }; - if(request == NULL) { - bridge_main(); - while(1); - } - if(request->library != NULL) { if(!dyld_load(request->library, KERNELCPU_PAYLOAD_ADDRESS, resolve_runtime_export, request->library_info, diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 25fdc3b31..e1ded8b75 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -24,12 +24,10 @@ #include #endif -#include "bridge_ctl.h" #include "kloader.h" #include "flash_storage.h" #include "clock.h" #include "rtiocrg.h" -#include "test_mode.h" #include "net_server.h" #include "session.h" #include "analyzer.h" @@ -208,6 +206,8 @@ static struct net_server_instance analyzer_inst = { static void regular_main(void) { + session_startup_kernel(); + puts("Accepting network sessions."); network_init(); net_server_init(&session_inst); @@ -225,41 +225,6 @@ static void regular_main(void) } } -static void blink_led(void) -{ - int i; - long long int t; - - for(i=0;i<3;i++) { -#ifdef CSR_LEDS_BASE - leds_out_write(1); -#endif - t = clock_get_ms(); - while(clock_get_ms() < t + 250); -#ifdef CSR_LEDS_BASE - leds_out_write(0); -#endif - t = clock_get_ms(); - while(clock_get_ms() < t + 250); - } -} - -static int check_test_mode(void) -{ - char c; - long long int t; - - t = clock_get_ms(); - while(clock_get_ms() < t + 1000) { - if(readchar_nonblock()) { - c = readchar(); - if((c == 't')||(c == 'T')) - return 1; - } - } - return 0; -} - extern void _fheap, _eheap; extern void rust_main(); @@ -279,17 +244,9 @@ int main(void) alloc_give(&_fheap, &_eheap - &_fheap); clock_init(); rtiocrg_init(); - puts("Press 't' to enter test mode..."); - blink_led(); - if(check_test_mode()) { - puts("Entering test mode."); - test_main(); - } else { - puts("Entering regular mode."); - // rust_main(); - session_startup_kernel(); - regular_main(); - } + // rust_main(); + regular_main(); + return 0; } diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index 1a297d630..f16b4a1c3 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -24,16 +24,6 @@ enum { MESSAGE_TYPE_CACHE_PUT_REQUEST, MESSAGE_TYPE_CACHE_PUT_REPLY, MESSAGE_TYPE_LOG, - - MESSAGE_TYPE_BRG_READY, - MESSAGE_TYPE_BRG_TTL_O, - MESSAGE_TYPE_BRG_TTL_OE, - MESSAGE_TYPE_BRG_DDS_SEL, - MESSAGE_TYPE_BRG_DDS_RESET, - MESSAGE_TYPE_BRG_DDS_READ_REQUEST, - MESSAGE_TYPE_BRG_DDS_READ_REPLY, - MESSAGE_TYPE_BRG_DDS_WRITE, - MESSAGE_TYPE_BRG_DDS_FUD, }; struct msg_base { @@ -132,48 +122,4 @@ struct msg_log { va_list args; }; -/* bridge messages */ - -struct msg_brg_ttl_out { - /* used for OE and O */ - int type; - int channel; - int value; -}; - -struct msg_brg_dds_sel { - int type; - int bus_channel; - int channel; -}; - -struct msg_brg_dds_reset { - int type; - int bus_channel; -}; - -struct msg_brg_dds_read_request { - int type; - int bus_channel; - unsigned int address; -}; - -struct msg_brg_dds_read_reply { - int type; - int bus_channel; - unsigned int data; -}; - -struct msg_brg_dds_write { - int type; - int bus_channel; - unsigned int address; - unsigned int data; -}; - -struct msg_brg_dds_fud { - int type; - int bus_channel; -}; - #endif /* __MESSAGES_H */ diff --git a/artiq/runtime/test_mode.c b/artiq/runtime/test_mode.c deleted file mode 100644 index 38f9c5dcb..000000000 --- a/artiq/runtime/test_mode.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright (C) 2014, 2015 M-Labs Limited - * Copyright (C) 2014, 2015 Robert Jordens - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "dds.h" -#include "flash_storage.h" -#include "bridge_ctl.h" -#include "clock.h" -#include "test_mode.h" - -#ifdef CSR_LEDS_BASE -static void leds(char *value) -{ - char *c; - unsigned int value2; - - if(*value == 0) { - printf("leds \n"); - return; - } - - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - leds_out_write(value2); -} -#endif - -#ifdef CSR_RTIO_CRG_BASE -static void clksrc(char *value) -{ - char *c; - unsigned int value2; - - if(*value == 0) { - printf("clksrc \n"); - return; - } - - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - rtio_crg_clock_sel_write(value2); -} -#endif - -static void ttloe(char *n, char *value) -{ - char *c; - unsigned int n2, value2; - - if((*n == 0)||(*value == 0)) { - printf("ttloe \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ttloe(n2, value2); -} - -static void ttlo(char *n, char *value) -{ - char *c; - unsigned int n2, value2; - - if((*n == 0)||(*value == 0)) { - printf("ttlo \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ttlo(n2, value2); -} - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - -static int bus_channel = CONFIG_RTIO_FIRST_DDS_CHANNEL; - -static void ddsbus(char *n) -{ - char *c; - unsigned int n2; - - if(*n == 0) { - printf("ddsbus \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect bus channel\n"); - return; - } - - bus_channel = n2; -} - - -static void ddssel(char *n) -{ - char *c; - unsigned int n2; - - if(*n == 0) { - printf("ddssel \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - -#ifdef CONFIG_DDS_ONEHOT_SEL - n2 = 1 << n2; -#endif - brg_ddssel(bus_channel, n2); -} - -static void ddsw(char *addr, char *value) -{ - char *c; - unsigned int addr2, value2; - - if((*addr == 0) || (*value == 0)) { - printf("ddsr \n"); - return; - } - - addr2 = strtoul(addr, &c, 0); - if(*c != 0) { - printf("incorrect address\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ddswrite(bus_channel, addr2, value2); -} - -static void ddsr(char *addr) -{ - char *c; - unsigned int addr2; - - if(*addr == 0) { - printf("ddsr \n"); - return; - } - - addr2 = strtoul(addr, &c, 0); - if(*c != 0) { - printf("incorrect address\n"); - return; - } - -#ifdef CONFIG_DDS_AD9858 - printf("0x%02x\n", brg_ddsread(bus_channel, addr2)); -#endif -#ifdef CONFIG_DDS_AD9914 - printf("0x%04x\n", brg_ddsread(bus_channel, addr2)); -#endif -} - -static void ddsfud(void) -{ - brg_ddsfud(bus_channel); -} - -static void ddsftw(char *n, char *ftw) -{ - char *c; - unsigned int n2, ftw2; - - if((*n == 0) || (*ftw == 0)) { - printf("ddsftw \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - ftw2 = strtoul(ftw, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - -#ifdef CONFIG_DDS_ONEHOT_SEL - n2 = 1 << n2; -#endif - brg_ddssel(bus_channel, n2); - -#ifdef CONFIG_DDS_AD9858 - brg_ddswrite(bus_channel, DDS_FTW0, ftw2 & 0xff); - brg_ddswrite(bus_channel, DDS_FTW1, (ftw2 >> 8) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW2, (ftw2 >> 16) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW3, (ftw2 >> 24) & 0xff); -#endif -#ifdef CONFIG_DDS_AD9914 - brg_ddswrite(bus_channel, DDS_FTWL, ftw2 & 0xffff); - brg_ddswrite(bus_channel, DDS_FTWH, (ftw2 >> 16) & 0xffff); -#endif - - brg_ddsfud(bus_channel); -} - -static void ddsreset(void) -{ - brg_ddsreset(bus_channel); -} - -#ifdef CONFIG_DDS_AD9858 -static void ddsinit(void) -{ - brg_ddsreset(bus_channel); - brg_ddswrite(bus_channel, DDS_CFR0, 0x78); - brg_ddswrite(bus_channel, DDS_CFR1, 0x00); - brg_ddswrite(bus_channel, DDS_CFR2, 0x00); - brg_ddswrite(bus_channel, DDS_CFR3, 0x00); - brg_ddsfud(bus_channel); -} -#endif - -#ifdef CONFIG_DDS_AD9914 -static void ddsinit(void) -{ - long long int t; - - brg_ddsreset(bus_channel); - brg_ddswrite(bus_channel, DDS_CFR1H, 0x0000); /* Enable cosine output */ - brg_ddswrite(bus_channel, DDS_CFR2L, 0x8900); /* Enable matched latency */ - brg_ddswrite(bus_channel, DDS_CFR2H, 0x0080); /* Enable profile mode */ - brg_ddswrite(bus_channel, DDS_ASF, 0x0fff); /* Set amplitude to maximum */ - brg_ddswrite(bus_channel, DDS_CFR4H, 0x0105); /* Enable DAC calibration */ - brg_ddswrite(bus_channel, DDS_FUD, 0); - t = clock_get_ms(); - while(clock_get_ms() < t + 2); - brg_ddswrite(bus_channel, DDS_CFR4H, 0x0005); /* Disable DAC calibration */ - brg_ddsfud(bus_channel); -} -#endif - -static void do_ddstest_one(unsigned int i) -{ - unsigned int v[12] = { - 0xaaaaaaaa, 0x55555555, 0xa5a5a5a5, 0x5a5a5a5a, - 0x00000000, 0xffffffff, 0x12345678, 0x87654321, - 0x0000ffff, 0xffff0000, 0x00ff00ff, 0xff00ff00, - }; - unsigned int f, g, j; - -#ifdef CONFIG_DDS_ONEHOT_SEL - brg_ddssel(bus_channel, 1 << i); -#else - brg_ddssel(bus_channel, i); -#endif - ddsinit(); - - for(j=0; j<12; j++) { - f = v[j]; -#ifdef CONFIG_DDS_AD9858 - brg_ddswrite(bus_channel, DDS_FTW0, f & 0xff); - brg_ddswrite(bus_channel, DDS_FTW1, (f >> 8) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW2, (f >> 16) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW3, (f >> 24) & 0xff); -#endif -#ifdef CONFIG_DDS_AD9914 - brg_ddswrite(bus_channel, DDS_FTWL, f & 0xffff); - brg_ddswrite(bus_channel, DDS_FTWH, (f >> 16) & 0xffff); -#endif - brg_ddsfud(bus_channel); -#ifdef CONFIG_DDS_AD9858 - g = brg_ddsread(bus_channel, DDS_FTW0); - g |= brg_ddsread(bus_channel, DDS_FTW1) << 8; - g |= brg_ddsread(bus_channel, DDS_FTW2) << 16; - g |= brg_ddsread(bus_channel, DDS_FTW3) << 24; -#endif -#ifdef CONFIG_DDS_AD9914 - g = brg_ddsread(bus_channel, DDS_FTWL); - g |= brg_ddsread(bus_channel, DDS_FTWH) << 16; -#endif - if(g != f) - printf("readback fail on DDS %d, 0x%08x != 0x%08x\n", i, g, f); - } -} - -static void ddstest(char *n, char *channel) -{ - int i, j; - char *c; - unsigned int n2; - int channel2; - - if((*n == 0) || (*channel == 0)) { - printf("ddstest \n"); - return; - } - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect cycles\n"); - return; - } - if(strcmp(channel, "all") == 0) - channel2 = -1; - else { - channel2 = strtoul(channel, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - } - - if(channel2 >= 0) { - for(i=0;i 0) - printf("KO[%u] remain == %u, expected 0\n", testnum, remain); - if(readlength != length) - printf("KO[%u] read length == %u, expected %u\n", testnum, readlength, length); - if(remain == 0 && readlength == length) - printf("."); - - readbuf[readlength] = 0; - if(memcmp(expected, readbuf, readlength) == 0) - printf(".\n"); - else - printf("KO[%u] read %s instead of %s\n", testnum, readbuf, expected); -} - -static void check_doesnt_exist(char *key, unsigned int testnum) -{ - char readbuf; - unsigned int remain, readlength; - - readlength = fs_read(key, &readbuf, sizeof(readbuf), &remain); - if(remain > 0) - printf("KO[%u] remain == %u, expected 0\n", testnum, remain); - if(readlength > 0) - printf("KO[%u] readlength == %d, expected 0\n", testnum, readlength); - if(remain == 0 && readlength == 0) - printf(".\n"); -} - -static void check_write(unsigned int ret) -{ - if(!ret) - printf("KO"); - else - printf("."); -} - -static inline void test_sector_is_full(void) -{ - char c; - char value[4096]; - char key[2] = {0, 0}; - - fs_erase(); - memset(value, '@', sizeof(value)); - for(c = 1; c <= CONFIG_SPIFLASH_SECTOR_SIZE/sizeof(value); c++) { - key[0] = c; - check_write(fs_write(key, value, sizeof(value) - 6)); - } - check_write(!fs_write("this_should_fail", "fail", 5)); - printf("\n"); -} - -static void test_one_big_record(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - check_write(!fs_write("b", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - fs_remove("a"); - check_doesnt_exist("a", testnum); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - fs_remove("a"); - check_doesnt_exist("a", testnum); - value[0] = '!'; - check_write(fs_write("b", value, sizeof(value) - 6)); - check_read("b", value, sizeof(value) - 6, testnum); -} - -static void test_flush_duplicate_rollback(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - /* This makes the flash storage full with one big record */ - check_write(fs_write("a", value, CONFIG_SPIFLASH_SECTOR_SIZE - 6)); - /* This should trigger the try_to_flush_duplicate code which - * at first will not keep the old "a" record value because we are - * overwriting it. But then it should roll back to the old value - * because the new record is too large. - */ - value[0] = '!'; - check_write(!fs_write("a", value, sizeof(value))); - /* check we still have the old record value */ - value[0] = '@'; - check_read("a", value, CONFIG_SPIFLASH_SECTOR_SIZE - 6, testnum); -} - -static void test_too_big_fails(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - check_write(!fs_write("a", value, sizeof(value) - 6 + /* TOO BIG */ 1)); - check_doesnt_exist("a", testnum); -} - -static void fs_test(void) -{ - int i; - char writebuf[] = "abcdefghijklmnopqrst"; - char read_check[4096]; - int vect_length = sizeof(writebuf); - - memset(read_check, '@', sizeof(read_check)); - printf("testing...\n"); - for(i = 0; i < vect_length; i++) { - printf("%u.0:", i); - fs_erase(); - check_write(fs_write("a", writebuf, i)); - check_read("a", writebuf, i, i); - - printf("%u.1:", i); - fsfull(); - check_read("a", writebuf, i, i); - - printf("%u.2:", i); - check_read("plip", read_check, sizeof(read_check), i); - - printf("%u.3:", i); - check_write(fs_write("a", "b", 2)); - check_read("a", "b", 2, i); - - printf("%u.4:", i); - fsfull(); - check_read("a", "b", 2, i); - - printf("%u.5:", i); - check_doesnt_exist("notfound", i); - - printf("%u.6:", i); - fs_remove("a"); - check_doesnt_exist("a", i); - - printf("%u.7:", i); - fsfull(); - check_doesnt_exist("a", i); - } - - printf("%u:", vect_length); - test_sector_is_full(); - - printf("%u:", vect_length+1); - test_one_big_record(vect_length+1); - - printf("%u:", vect_length+2); - test_flush_duplicate_rollback(vect_length+2); - - printf("%u:", vect_length+3); - test_too_big_fails(vect_length+3); -} - -#endif - -static void help(void) -{ - puts("Available commands:"); - puts("help - this message"); -#ifdef CSR_RTIO_CRG_BASE - puts("clksrc - select RTIO clock source"); -#endif - puts("ttloe - set TTL output enable"); - puts("ttlo - set TTL output value"); - puts("ddsbus - select the DDS bus RTIO channel"); - puts("ddssel - select a DDS"); - puts("ddsinit - reset, config, FUD DDS"); - puts("ddsreset - reset DDS"); - puts("ddsw - write to DDS register"); - puts("ddsr - read DDS register"); - puts("ddsfud - pulse FUD"); - puts("ddsftw - write FTW"); - puts("ddstest - perform test sequence on DDS"); -#ifdef CSR_LEDS_BASE - puts("leds - set LEDs"); -#endif -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - puts("fserase - erase flash storage"); - puts("fswrite - write to flash storage"); - puts("fsread - read flash storage"); - puts("fsremove - remove a key-value record from flash storage"); - puts("fstest - run flash storage tests. WARNING: erases the storage area"); -#endif -} - -static void readstr(char *s, int size) -{ - char c[2]; - int ptr; - - c[1] = 0; - ptr = 0; - while(1) { - c[0] = readchar(); - switch(c[0]) { - case 0x7f: - case 0x08: - if(ptr > 0) { - ptr--; - putsnonl("\x08 \x08"); - } - break; - case 0x07: - break; - case '\r': - case '\n': - s[ptr] = 0x00; - putsnonl("\n"); - return; - default: - putsnonl(c); - s[ptr] = c[0]; - ptr++; - break; - } - } -} - -static char *get_token(char **str) -{ - char *c, *d; - - c = (char *)strchr(*str, ' '); - if(c == NULL) { - d = *str; - *str = *str+strlen(*str); - return d; - } - *c = 0; - d = *str; - *str = c+1; - return d; -} - - -static void do_command(char *c) -{ - char *token; - - token = get_token(&c); - - if(strcmp(token, "help") == 0) help(); -#ifdef CSR_LEDS_BASE - else if(strcmp(token, "leds") == 0) leds(get_token(&c)); -#endif - -#ifdef CSR_RTIO_CRG_BASE - else if(strcmp(token, "clksrc") == 0) clksrc(get_token(&c)); -#endif - - else if(strcmp(token, "ttloe") == 0) ttloe(get_token(&c), get_token(&c)); - else if(strcmp(token, "ttlo") == 0) ttlo(get_token(&c), get_token(&c)); - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - else if(strcmp(token, "ddsbus") == 0) ddsbus(get_token(&c)); - else if(strcmp(token, "ddssel") == 0) ddssel(get_token(&c)); - else if(strcmp(token, "ddsw") == 0) ddsw(get_token(&c), get_token(&c)); - else if(strcmp(token, "ddsr") == 0) ddsr(get_token(&c)); - else if(strcmp(token, "ddsreset") == 0) ddsreset(); - else if(strcmp(token, "ddsinit") == 0) ddsinit(); - else if(strcmp(token, "ddsfud") == 0) ddsfud(); - else if(strcmp(token, "ddsftw") == 0) ddsftw(get_token(&c), get_token(&c)); - else if(strcmp(token, "ddstest") == 0) ddstest(get_token(&c), get_token(&c)); -#endif - -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - else if(strcmp(token, "fserase") == 0) fs_erase(); - else if(strcmp(token, "fswrite") == 0) fswrite(get_token(&c), c, strlen(c)); - else if(strcmp(token, "fsread") == 0) fsread(get_token(&c)); - else if(strcmp(token, "fsremove") == 0) fs_remove(get_token(&c)); - else if(strcmp(token, "fstest") == 0) fs_test(); -#endif - - else if(strcmp(token, "") != 0) - printf("Command not found\n"); -} - -void test_main(void) -{ - char buffer[64]; - - brg_start(); - - while(1) { - putsnonl("\e[1mtest>\e[0m "); - readstr(buffer, 64); - do_command(buffer); - } -} diff --git a/artiq/runtime/test_mode.h b/artiq/runtime/test_mode.h deleted file mode 100644 index fb719d7d4..000000000 --- a/artiq/runtime/test_mode.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __TEST_MODE_H -#define __TEST_MODE_H - -void test_main(void); - -#endif diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h deleted file mode 100644 index 87fa74b7c..000000000 --- a/artiq/runtime/ttl.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __TTL_H -#define __TTL_H - -#define TTL_O_ADDR 0 -#define TTL_OE_ADDR 1 -#define TTL_SENSITIVITY_ADDR 2 - -#endif /* __TTL_H */ From 83940ae4a6e4748179bb2d123acfc1164fe85124 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 18:54:08 +0000 Subject: [PATCH 008/127] Rust: add support for artiq_coreconfig. --- artiq/runtime.rs/src/buffer_logger.rs | 3 +- artiq/runtime.rs/src/config.rs | 63 ++++++++++++++++++++++++ artiq/runtime.rs/src/lib.rs | 4 +- artiq/runtime.rs/src/session/mod.rs | 28 +++++++++-- artiq/runtime.rs/src/session/protocol.rs | 10 ++-- artiq/runtime/main.c | 4 +- 6 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 artiq/runtime.rs/src/config.rs diff --git a/artiq/runtime.rs/src/buffer_logger.rs b/artiq/runtime.rs/src/buffer_logger.rs index e10a5a876..7e7537f8e 100644 --- a/artiq/runtime.rs/src/buffer_logger.rs +++ b/artiq/runtime.rs/src/buffer_logger.rs @@ -1,4 +1,3 @@ -use core::mem; use core::cell::RefCell; use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; use log_buffer::LogBuffer; @@ -41,7 +40,7 @@ impl BufferLogger { } impl Log for BufferLogger { - fn enabled(&self, _metadata: &log::LogMetadata) -> bool { + fn enabled(&self, _metadata: &LogMetadata) -> bool { true } diff --git a/artiq/runtime.rs/src/config.rs b/artiq/runtime.rs/src/config.rs new file mode 100644 index 000000000..df3d1868e --- /dev/null +++ b/artiq/runtime.rs/src/config.rs @@ -0,0 +1,63 @@ +use std::cmp; +use std::vec::Vec; +use libc::{c_void, c_char, c_int, c_uint}; + +extern { + fn fs_remove(key: *const c_char); + fn fs_erase(); + fn fs_write(key: *const c_char, buffer: *const c_void, buflen: c_uint) -> c_int; + fn fs_read(key: *const c_char, buffer: *mut c_void, buflen: c_uint, + remain: *mut c_uint) -> c_uint; +} + +macro_rules! c_str { + ($s:ident) => { + { + let mut c = [0; 64 + 1]; + let len = cmp::min($s.len(), c.len() - 1); + c[..len].copy_from_slice($s.as_bytes()); + c + } + } +} + +pub fn read(key: &str, buf: &mut [u8]) -> Result { + let key_c = c_str!(key); + let mut remain: c_uint = 0; + let result = unsafe { + fs_read(key_c.as_ptr() as *const c_char, + buf.as_mut_ptr() as *mut c_void, buf.len() as c_uint, &mut remain) + }; + if remain == 0 { Ok(result as usize) } else { Err(remain as usize) } +} + +pub fn read_to_end(key: &str) -> Vec { + let mut value = Vec::new(); + match read(key, &mut []) { + Ok(0) => (), + Ok(_) => unreachable!(), + Err(size) => { + value.resize(size, 0); + read(key, &mut value).unwrap(); + } + } + value +} + +pub fn write(key: &str, buf: &[u8]) -> Result<(), ()> { + let key_c = c_str!(key); + let result = unsafe { + fs_write(key_c.as_ptr() as *const c_char, + buf.as_ptr() as *mut c_void, buf.len() as c_uint) + }; + if result == 1 { Ok(()) } else { Err(()) } +} + +pub fn remove(key: &str) { + let key_c = c_str!(key); + unsafe { fs_remove(key_c.as_ptr() as *const c_char) } +} + +pub fn erase() { + unsafe { fs_erase() } +} diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 22feccda2..3c1c6ed32 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,8 +1,9 @@ #![no_std] -#![feature(const_fn)] +#![feature(libc)] #[macro_use] extern crate std_artiq as std; +extern crate libc; #[macro_use] extern crate log; extern crate log_buffer; @@ -13,6 +14,7 @@ use buffer_logger::BufferLogger; pub mod board; pub mod io; +pub mod config; pub mod buffer_logger; pub mod session; diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session/mod.rs index b4d05bb99..fca04c563 100644 --- a/artiq/runtime.rs/src/session/mod.rs +++ b/artiq/runtime.rs/src/session/mod.rs @@ -1,6 +1,7 @@ use std::prelude::v1::*; use std::str; use std::io::{self, Read, ErrorKind}; +use config; use self::protocol::*; mod protocol; @@ -67,9 +68,8 @@ fn handle_request(stream: &mut ::io::TcpStream, } match try!(read_request(stream)) { - Request::Ident => { - write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))) - } + Request::Ident => + write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))), Request::Log => { // Logging the packet with the log is inadvisable @@ -84,6 +84,28 @@ fn handle_request(stream: &mut ::io::TcpStream, write_reply(stream, Reply::Log("")) } + Request::FlashRead { ref key } => { + let value = config::read_to_end(key); + write_reply(stream, Reply::FlashRead(&value)) + } + + Request::FlashWrite { ref key, ref value } => { + match config::write(key, value) { + Ok(_) => write_reply(stream, Reply::FlashOk), + Err(_) => write_reply(stream, Reply::FlashError) + } + } + + Request::FlashRemove { ref key } => { + config::remove(key); + write_reply(stream, Reply::FlashOk) + } + + Request::FlashErase => { + config::erase(); + write_reply(stream, Reply::FlashOk) + } + _ => unreachable!() } } diff --git a/artiq/runtime.rs/src/session/protocol.rs b/artiq/runtime.rs/src/session/protocol.rs index deaa2abdd..ba3a64ae2 100644 --- a/artiq/runtime.rs/src/session/protocol.rs +++ b/artiq/runtime.rs/src/session/protocol.rs @@ -51,7 +51,9 @@ fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { } fn read_string(reader: &mut Read) -> io::Result { - let bytes = try!(read_bytes(reader)); + let mut bytes = try!(read_bytes(reader)); + let len = bytes.len() - 1; // length without trailing \0 + bytes.resize(len, 0); // FIXME: don't send \0 in the first place String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } @@ -128,8 +130,8 @@ pub enum Request { FlashRead { key: String }, FlashWrite { key: String, value: Vec }, - FlashErase { key: String }, FlashRemove { key: String }, + FlashErase, } impl Request { @@ -162,9 +164,7 @@ impl Request { key: try!(read_string(reader)), value: try!(read_bytes(reader)) }, - 11 => Request::FlashErase { - key: try!(read_string(reader)) - }, + 11 => Request::FlashErase, 12 => Request::FlashRemove { key: try!(read_string(reader)) }, diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index e1ded8b75..25e826200 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -245,8 +245,8 @@ int main(void) clock_init(); rtiocrg_init(); - // rust_main(); - regular_main(); + rust_main(); + // regular_main(); return 0; } From 9c18f1b55541d9f13ae84d901c331cd6722b27a3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 20:36:04 +0000 Subject: [PATCH 009/127] Rust: port clock, rtio_crg routines. --- artiq/runtime.rs/src/clock.rs | 77 +++++++++++++++++++++++++++++ artiq/runtime.rs/src/lib.rs | 5 +- artiq/runtime.rs/src/rtio_crg.rs | 47 ++++++++++++++++++ artiq/runtime.rs/src/session/mod.rs | 45 ++++++++++++++--- artiq/runtime/main.c | 8 +-- 5 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 artiq/runtime.rs/src/clock.rs create mode 100644 artiq/runtime.rs/src/rtio_crg.rs diff --git a/artiq/runtime.rs/src/clock.rs b/artiq/runtime.rs/src/clock.rs new file mode 100644 index 000000000..5644f472c --- /dev/null +++ b/artiq/runtime.rs/src/clock.rs @@ -0,0 +1,77 @@ +use board::csr::timer0; + +const INIT: u64 = ::core::i64::MAX as u64; +const FREQ: u64 = ::board::csr::CONFIG_CLOCK_FREQUENCY as u64; + +pub fn init() { + unsafe { + timer0::en_write(0); + timer0::load_write(INIT); + timer0::reload_write(INIT); + timer0::en_write(1); + } +} + +pub fn get_ms() -> u64 { + unsafe { + timer0::update_value_write(1); + (INIT - timer0::value_read()) / (FREQ / 1_000) + } +} + +pub fn spin_us(interval: u64) { + unsafe { + timer0::update_value_write(1); + let threshold = timer0::value_read() - interval * (FREQ / 1_000_000); + while timer0::value_read() > threshold { + timer0::update_value_write(1) + } + } +} + +#[derive(Debug, Clone, Copy)] +struct Watchdog { + active: bool, + threshold: u64 +} + +pub const MAX_WATCHDOGS: usize = 16; + +#[derive(Debug)] +pub struct WatchdogSet { + watchdogs: [Watchdog; MAX_WATCHDOGS] +} + +impl WatchdogSet { + pub fn new() -> WatchdogSet { + WatchdogSet { + watchdogs: [Watchdog { active: false, threshold: 0 }; MAX_WATCHDOGS] + } + } + + pub fn set_ms(&mut self, interval: u64) -> Result { + for (index, watchdog) in self.watchdogs.iter_mut().enumerate() { + if !watchdog.active { + watchdog.active = true; + watchdog.threshold = get_ms() + interval; + return Ok(index) + } + } + + warn!("cannot add watchdog; all {} watchdogs used", MAX_WATCHDOGS); + Err(()) + } + + pub fn clear(&mut self, index: usize) { + if index < MAX_WATCHDOGS { + self.watchdogs[index].active = false + } + } + + pub fn expired(&self) -> bool { + self.watchdogs.iter() + .filter(|wd| wd.active) + .min_by_key(|wd| wd.threshold) + .map_or(false, |wd| get_ms() > wd.threshold) + } +} diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 3c1c6ed32..edb7b06a2 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -15,6 +15,8 @@ use buffer_logger::BufferLogger; pub mod board; pub mod io; pub mod config; +pub mod clock; +pub mod rtio_crg; pub mod buffer_logger; pub mod session; @@ -28,7 +30,8 @@ pub unsafe extern fn rust_main() { static mut log_buffer: [u8; 4096] = [0; 4096]; BufferLogger::new(&mut log_buffer[..]) .register(move |logger| { - info!("Accepting network sessions in Rust."); + clock::init(); + rtio_crg::init(); network_init(); let mut scheduler = io::Scheduler::new(); diff --git a/artiq/runtime.rs/src/rtio_crg.rs b/artiq/runtime.rs/src/rtio_crg.rs new file mode 100644 index 000000000..b1867396d --- /dev/null +++ b/artiq/runtime.rs/src/rtio_crg.rs @@ -0,0 +1,47 @@ +use board::csr; +use {clock, config}; + +pub fn init() { + unsafe { csr::rtio_crg::pll_reset_write(0) } + + let mut opt = [b'i']; + let clk; + match config::read("startup_clock", &mut opt) { + Ok(0) | Ok(1) if &opt == b"i" => { + info!("startup RTIO clock: internal"); + clk = 0 + } + Ok(1) if &opt == b"e" => { + info!("startup RTIO clock: external"); + clk = 1 + } + _ => { + error!("unrecognized startup_clock configuration entry"); + clk = 0 + } + }; + + if !switch_clock(clk) { + error!("startup RTIO clock failed"); + warn!("this may cause the system initialization to fail"); + warn!("fix clocking and reset the device"); + } +} + +pub fn check() -> bool { + unsafe { csr::rtio_crg::pll_locked_read() != 0 } +} + +pub fn switch_clock(clk: u8) -> bool { + unsafe { + let cur_clk = csr::rtio_crg::clock_sel_read(); + if clk != cur_clk { + csr::rtio_crg::pll_reset_write(1); + csr::rtio_crg::clock_sel_write(clk); + csr::rtio_crg::pll_reset_write(0); + } + } + + clock::spin_us(150); + return check() +} diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session/mod.rs index fca04c563..ae82ab8e5 100644 --- a/artiq/runtime.rs/src/session/mod.rs +++ b/artiq/runtime.rs/src/session/mod.rs @@ -1,7 +1,7 @@ use std::prelude::v1::*; use std::str; use std::io::{self, Read, ErrorKind}; -use config; +use {config, rtio_crg}; use self::protocol::*; mod protocol; @@ -26,14 +26,23 @@ extern { } impl Session { - pub fn start() -> Session { + pub fn new() -> Session { unsafe { kloader_stop(); } Session { kernel_state: KernelState::Absent } } - pub fn end(self) { + pub fn running(&self) -> bool { + match self.kernel_state { + KernelState::Absent | KernelState::Loaded => false, + KernelState::Running | KernelState::RpcWait => true + } + } +} + +impl Drop for Session { + fn drop(&mut self) { unsafe { kloader_stop(); watchdog_init(); @@ -55,10 +64,14 @@ fn check_magic(stream: &mut ::io::TcpStream) -> io::Result<()> { } fn handle_request(stream: &mut ::io::TcpStream, - logger: &::buffer_logger::BufferLogger) -> io::Result<()> { + logger: &::buffer_logger::BufferLogger, + session: &mut Session) -> io::Result<()> { fn read_request(stream: &mut ::io::TcpStream) -> io::Result { let request = try!(Request::read_from(stream)); - trace!("comm<-host {:?}", request); + match &request { + &Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"), + _ => trace!("comm<-host {:?}", request) + } Ok(request) } @@ -71,6 +84,7 @@ fn handle_request(stream: &mut ::io::TcpStream, Request::Ident => write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))), + // artiq_corelog Request::Log => { // Logging the packet with the log is inadvisable trace!("comm->host Log(...)"); @@ -84,6 +98,7 @@ fn handle_request(stream: &mut ::io::TcpStream, write_reply(stream, Reply::Log("")) } + // artiq_coreconfig Request::FlashRead { ref key } => { let value = config::read_to_end(key); write_reply(stream, Reply::FlashRead(&value)) @@ -106,6 +121,20 @@ fn handle_request(stream: &mut ::io::TcpStream, write_reply(stream, Reply::FlashOk) } + // artiq_run/artiq_master + Request::SwitchClock(clk) => { + if session.running() { + error!("attempted to switch RTIO clock while kernel was running"); + write_reply(stream, Reply::ClockSwitchFailed) + } else { + if rtio_crg::switch_clock(clk) { + write_reply(stream, Reply::ClockSwitchCompleted) + } else { + write_reply(stream, Reply::ClockSwitchFailed) + } + } + } + _ => unreachable!() } } @@ -113,8 +142,10 @@ fn handle_request(stream: &mut ::io::TcpStream, fn handle_requests(stream: &mut ::io::TcpStream, logger: &::buffer_logger::BufferLogger) -> io::Result<()> { try!(check_magic(stream)); + + let mut session = Session::new(); loop { - try!(handle_request(stream, logger)) + try!(handle_request(stream, logger, &mut session)) } } @@ -122,6 +153,8 @@ pub fn handler(waiter: ::io::Waiter, logger: &::buffer_logger::BufferLogger) { let addr = ::io::SocketAddr::new(::io::IP_ANY, 1381); let listener = ::io::TcpListener::bind(waiter, addr).unwrap(); + info!("accepting network sessions in Rust"); + loop { let (mut stream, addr) = listener.accept().unwrap(); info!("new connection from {:?}", addr); diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 25e826200..70451bb73 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -206,6 +206,8 @@ static struct net_server_instance analyzer_inst = { static void regular_main(void) { + clock_init(); + rtiocrg_init(); session_startup_kernel(); puts("Accepting network sessions."); @@ -242,11 +244,9 @@ int main(void) puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); alloc_give(&_fheap, &_eheap - &_fheap); - clock_init(); - rtiocrg_init(); - rust_main(); - // regular_main(); + // rust_main(); + regular_main(); return 0; } From 9d00023401ec96e310cc5525da4f7386cf2c0900 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 20:56:35 +0000 Subject: [PATCH 010/127] Rust: move a few things around (NFC). --- artiq/runtime.rs/src/lib.rs | 22 ++++++++------- .../src/{buffer_logger.rs => logger.rs} | 0 artiq/runtime.rs/src/{io.rs => sched.rs} | 0 .../src/{session/mod.rs => session.rs} | 28 +++++++++---------- .../{session/protocol.rs => session_proto.rs} | 0 5 files changed, 26 insertions(+), 24 deletions(-) rename artiq/runtime.rs/src/{buffer_logger.rs => logger.rs} (100%) rename artiq/runtime.rs/src/{io.rs => sched.rs} (100%) rename artiq/runtime.rs/src/{session/mod.rs => session.rs} (85%) rename artiq/runtime.rs/src/{session/protocol.rs => session_proto.rs} (100%) diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index edb7b06a2..d9664c588 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -9,16 +9,18 @@ extern crate log; extern crate log_buffer; extern crate byteorder; -use std::prelude::v1::*; -use buffer_logger::BufferLogger; +use logger::BufferLogger; -pub mod board; -pub mod io; -pub mod config; -pub mod clock; -pub mod rtio_crg; -pub mod buffer_logger; -pub mod session; +mod board; +mod sched; +mod config; +mod clock; +mod rtio_crg; + +mod logger; + +mod session_proto; +mod session; extern { fn network_init(); @@ -34,7 +36,7 @@ pub unsafe extern fn rust_main() { rtio_crg::init(); network_init(); - let mut scheduler = io::Scheduler::new(); + let mut scheduler = sched::Scheduler::new(); scheduler.spawn(4096, move |waiter| { session::handler(waiter, logger) }); diff --git a/artiq/runtime.rs/src/buffer_logger.rs b/artiq/runtime.rs/src/logger.rs similarity index 100% rename from artiq/runtime.rs/src/buffer_logger.rs rename to artiq/runtime.rs/src/logger.rs diff --git a/artiq/runtime.rs/src/io.rs b/artiq/runtime.rs/src/sched.rs similarity index 100% rename from artiq/runtime.rs/src/io.rs rename to artiq/runtime.rs/src/sched.rs diff --git a/artiq/runtime.rs/src/session/mod.rs b/artiq/runtime.rs/src/session.rs similarity index 85% rename from artiq/runtime.rs/src/session/mod.rs rename to artiq/runtime.rs/src/session.rs index ae82ab8e5..d95bc9619 100644 --- a/artiq/runtime.rs/src/session/mod.rs +++ b/artiq/runtime.rs/src/session.rs @@ -2,9 +2,9 @@ use std::prelude::v1::*; use std::str; use std::io::{self, Read, ErrorKind}; use {config, rtio_crg}; -use self::protocol::*; - -mod protocol; +use logger::BufferLogger; +use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY}; +use session_proto::*; #[derive(Debug, Clone, Copy)] enum KernelState { @@ -51,7 +51,7 @@ impl Drop for Session { } } -fn check_magic(stream: &mut ::io::TcpStream) -> io::Result<()> { +fn check_magic(stream: &mut TcpStream) -> io::Result<()> { const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; let mut magic: [u8; 14] = [0; 14]; @@ -63,10 +63,10 @@ fn check_magic(stream: &mut ::io::TcpStream) -> io::Result<()> { } } -fn handle_request(stream: &mut ::io::TcpStream, - logger: &::buffer_logger::BufferLogger, +fn handle_request(stream: &mut TcpStream, + logger: &BufferLogger, session: &mut Session) -> io::Result<()> { - fn read_request(stream: &mut ::io::TcpStream) -> io::Result { + fn read_request(stream: &mut TcpStream) -> io::Result { let request = try!(Request::read_from(stream)); match &request { &Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"), @@ -75,7 +75,7 @@ fn handle_request(stream: &mut ::io::TcpStream, Ok(request) } - fn write_reply(stream: &mut ::io::TcpStream, reply: Reply) -> io::Result<()> { + fn write_reply(stream: &mut TcpStream, reply: Reply) -> io::Result<()> { trace!("comm->host {:?}", reply); reply.write_to(stream) } @@ -139,8 +139,8 @@ fn handle_request(stream: &mut ::io::TcpStream, } } -fn handle_requests(stream: &mut ::io::TcpStream, - logger: &::buffer_logger::BufferLogger) -> io::Result<()> { +fn handle_requests(stream: &mut TcpStream, + logger: &BufferLogger) -> io::Result<()> { try!(check_magic(stream)); let mut session = Session::new(); @@ -149,10 +149,10 @@ fn handle_requests(stream: &mut ::io::TcpStream, } } -pub fn handler(waiter: ::io::Waiter, - logger: &::buffer_logger::BufferLogger) { - let addr = ::io::SocketAddr::new(::io::IP_ANY, 1381); - let listener = ::io::TcpListener::bind(waiter, addr).unwrap(); +pub fn handler(waiter: Waiter, + logger: &BufferLogger) { + let addr = SocketAddr::new(IP_ANY, 1381); + let listener = TcpListener::bind(waiter, addr).unwrap(); info!("accepting network sessions in Rust"); loop { diff --git a/artiq/runtime.rs/src/session/protocol.rs b/artiq/runtime.rs/src/session_proto.rs similarity index 100% rename from artiq/runtime.rs/src/session/protocol.rs rename to artiq/runtime.rs/src/session_proto.rs From 55b25354779992f93522ab5178c7ef0939c1a51d Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 29 Sep 2016 22:04:52 +0000 Subject: [PATCH 011/127] Rust: print git commit during startup. --- artiq/runtime.rs/Cargo.lock | 33 +++++++++++++++++++++++++++++ artiq/runtime.rs/Cargo.toml | 4 ++++ artiq/runtime.rs/build.rs | 42 +++++++++++++++++++++++++++++++++++++ artiq/runtime.rs/src/lib.rs | 4 ++++ artiq/runtime/main.c | 4 ++-- 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 artiq/runtime.rs/build.rs diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index 5114170b2..1f5913d15 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -8,6 +8,7 @@ dependencies = [ "log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lwip 0.0.0", "std_artiq 0.0.0", + "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -27,6 +28,15 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.15" @@ -61,9 +71,32 @@ dependencies = [ "alloc_artiq 0.0.0", ] +[[package]] +name = "walkdir" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987689dcfad85eee8d76b477865641ec483e63fb86d52966bfc350c4a647d78a" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8beb5ba24eca52f9958874445c4de5e086a7e82a1ec6b7ab81e5fcfb134f25a" +"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index fe5770958..eb8c3f2c9 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -2,6 +2,10 @@ authors = ["The ARTIQ Project Developers"] name = "runtime" version = "0.0.0" +build = "build.rs" + +[build-dependencies] +walkdir = "0.1" [lib] name = "artiq_rust" diff --git a/artiq/runtime.rs/build.rs b/artiq/runtime.rs/build.rs new file mode 100644 index 000000000..e545ea3d4 --- /dev/null +++ b/artiq/runtime.rs/build.rs @@ -0,0 +1,42 @@ +extern crate walkdir; + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::process::Command; + +use walkdir::WalkDir; + +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("git_info.rs"); + let mut f = File::create(&dest_path).unwrap(); + + writeln!(f, "const GIT_COMMIT: &'static str = {:?};", + git_describe().unwrap()).unwrap(); + + println!("cargo:rerun-if-changed=../../.git/HEAD"); + for entry in WalkDir::new("../../.git/refs") { + let entry = entry.unwrap(); + println!("cargo:rerun-if-changed={}", entry.path().display()); + } +} + +// Returns `None` if git is not available. +fn git_describe() -> Option { + Command::new("git") + .arg("describe") + .arg("--tags") + .arg("--dirty") + .arg("--always") + .arg("--long") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .map(|mut s| { + let len = s.trim_right().len(); + s.truncate(len); + s + }) +} diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index d9664c588..b0520b629 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -27,11 +27,15 @@ extern { fn lwip_service(); } +include!(concat!(env!("OUT_DIR"), "/git_info.rs")); + #[no_mangle] pub unsafe extern fn rust_main() { static mut log_buffer: [u8; 4096] = [0; 4096]; BufferLogger::new(&mut log_buffer[..]) .register(move |logger| { + info!("booting ARTIQ runtime ({})", GIT_COMMIT); + clock::init(); rtio_crg::init(); network_init(); diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 70451bb73..c2460b32a 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -206,6 +206,8 @@ static struct net_server_instance analyzer_inst = { static void regular_main(void) { + puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); + clock_init(); rtiocrg_init(); session_startup_kernel(); @@ -241,8 +243,6 @@ int main(void) irq_setie(1); uart_init(); - puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); - alloc_give(&_fheap, &_eheap - &_fheap); // rust_main(); From c6a57d20435d831d36fe1b444c205e487e3dc9e4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 30 Sep 2016 00:15:20 +0000 Subject: [PATCH 012/127] Rust: port mailbox routines. --- artiq/runtime.rs/src/board.rs | 4 +++ artiq/runtime.rs/src/lib.rs | 1 + artiq/runtime.rs/src/mailbox.rs | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 artiq/runtime.rs/src/mailbox.rs diff --git a/artiq/runtime.rs/src/board.rs b/artiq/runtime.rs/src/board.rs index aa97b1edb..94ad10291 100644 --- a/artiq/runtime.rs/src/board.rs +++ b/artiq/runtime.rs/src/board.rs @@ -3,6 +3,10 @@ use core::{cmp, ptr, str}; include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); +extern { + pub fn flush_cpu_dcache(); +} + pub fn ident(buf: &mut [u8]) -> &str { unsafe { let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE); diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index b0520b629..215f3c7d5 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -16,6 +16,7 @@ mod sched; mod config; mod clock; mod rtio_crg; +mod mailbox; mod logger; diff --git a/artiq/runtime.rs/src/mailbox.rs b/artiq/runtime.rs/src/mailbox.rs new file mode 100644 index 000000000..5d13225ce --- /dev/null +++ b/artiq/runtime.rs/src/mailbox.rs @@ -0,0 +1,51 @@ +use core::ptr::{read_volatile, write_volatile}; +use board; + +const MAILBOX: *mut u32 = board::mem::MAILBOX_BASE as *mut u32; +static mut last: u32 = 0; + +pub fn send(data: u32) { + unsafe { + last = data; + write_volatile(MAILBOX, data) + } +} + +pub fn acknowledged() -> bool { + unsafe { + let data = read_volatile(MAILBOX); + data == 0 || data != last + } +} + +pub fn send_and_wait(data: u32) { + send(data); + while !acknowledged() {} +} + +pub fn receive() -> u32 { + unsafe { + let data = read_volatile(MAILBOX); + if data == last { + 0 + } else { + if data != 0 { + board::flush_cpu_dcache() + } + data + } + } +} + +pub fn wait_and_receive() -> u32 { + loop { + let data = receive(); + if data != 0 { + return data + } + } +} + +pub fn acknowledge() { + unsafe { write_volatile(MAILBOX, 0) } +} From 7cfa667d9829bc965004205f8f47aaed4bf388d2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 30 Sep 2016 01:25:46 +0000 Subject: [PATCH 013/127] runtime: eliminate struct dyld_info from kernel interface. --- artiq/runtime/kloader.c | 42 +++++++++++++----------------- artiq/runtime/ksupport.c | 56 +++++++++++++++++++--------------------- artiq/runtime/messages.h | 4 +-- artiq/runtime/session.c | 5 ++++ 4 files changed, 51 insertions(+), 56 deletions(-) diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c index cf8c9610c..06cf737c5 100644 --- a/artiq/runtime/kloader.c +++ b/artiq/runtime/kloader.c @@ -1,8 +1,6 @@ #include #include -#include - #include "kloader.h" #include "log.h" #include "clock.h" @@ -10,8 +8,13 @@ #include "mailbox.h" #include "messages.h" -static void start_kernel_cpu(struct msg_load_request *msg) +int kloader_load_library(const void *library) { + if(!kernel_cpu_reset_read()) { + core_log("BUG: attempted to load kernel library while kernel CPU is running\n"); + return 0; + } + // Stop kernel CPU before messing with its code. kernel_cpu_reset_write(1); @@ -22,22 +25,15 @@ static void start_kernel_cpu(struct msg_load_request *msg) &_binary_ksupport_elf_end - &_binary_ksupport_elf_start); // Start kernel CPU. - mailbox_send(msg); kernel_cpu_reset_write(0); -} -static int load_or_start_kernel(const void *library, int run_kernel) -{ - static struct dyld_info library_info; struct msg_load_request request = { - .library = library, - .library_info = &library_info, - .run_kernel = run_kernel, + .type = MESSAGE_TYPE_LOAD_REQUEST, + .library = library, }; - start_kernel_cpu(&request); + mailbox_send(&request); struct msg_load_reply *reply = mailbox_wait_and_receive(); - mailbox_acknowledge(); if(reply->type != MESSAGE_TYPE_LOAD_REPLY) { core_log("BUG: unexpected reply to load/run request\n"); @@ -52,14 +48,14 @@ static int load_or_start_kernel(const void *library, int run_kernel) return 1; } -int kloader_load_library(const void *library) +void kloader_start_kernel() { - if(!kernel_cpu_reset_read()) { - core_log("BUG: attempted to load kernel library while kernel CPU is running\n"); - return 0; + if(kernel_cpu_reset_read()) { + core_log("BUG: attempted to load kernel library while kernel CPU is stopped\n"); + return; } - return load_or_start_kernel(library, 0); + mailbox_acknowledge(); } void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, @@ -78,11 +74,6 @@ void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, *backtrace_size = cursor - backtrace; } -void kloader_start_kernel() -{ - load_or_start_kernel(NULL, 1); -} - static int kloader_start_flash_kernel(char *key) { #if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) @@ -98,7 +89,10 @@ static int kloader_start_flash_kernel(char *key) return 0; } - return load_or_start_kernel(buffer, 1); + if(!kloader_load_library(buffer)) + return 0; + kloader_start_kernel(); + return 1; #else return 0; #endif diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 5bf530cbe..00066cda3 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -370,44 +370,40 @@ static void now_save(void) int main(void); int main(void) { - struct msg_load_request *request = mailbox_receive(); + static struct dyld_info library_info; + + struct msg_load_request *request = mailbox_wait_and_receive(); struct msg_load_reply load_reply = { .type = MESSAGE_TYPE_LOAD_REPLY, .error = NULL }; - if(request->library != NULL) { - if(!dyld_load(request->library, KERNELCPU_PAYLOAD_ADDRESS, - resolve_runtime_export, request->library_info, - &load_reply.error)) { - mailbox_send(&load_reply); - while(1); - } - - void *__bss_start = dyld_lookup("__bss_start", request->library_info); - void *_end = dyld_lookup("_end", request->library_info); - memset(__bss_start, 0, _end - __bss_start); - } - - if(request->run_kernel) { - void (*kernel_run)() = request->library_info->init; - void *typeinfo = dyld_lookup("typeinfo", request->library_info); - - mailbox_send_and_wait(&load_reply); - - now_init(); - kernel_run(); - now_save(); - - attribute_writeback(typeinfo); - - struct msg_base finished_reply; - finished_reply.type = MESSAGE_TYPE_FINISHED; - mailbox_send_and_wait(&finished_reply); - } else { + if(!dyld_load(request->library, KERNELCPU_PAYLOAD_ADDRESS, + resolve_runtime_export, &library_info, + &load_reply.error)) { mailbox_send(&load_reply); + while(1); } + void *__bss_start = dyld_lookup("__bss_start", &library_info); + void *_end = dyld_lookup("_end", &library_info); + memset(__bss_start, 0, _end - __bss_start); + + void (*kernel_run)() = library_info.init; + void *typeinfo = dyld_lookup("typeinfo", &library_info); + + mailbox_send_and_wait(&load_reply); + + now_init(); + kernel_run(); + now_save(); + + attribute_writeback(typeinfo); + + struct msg_base finished_reply; + finished_reply.type = MESSAGE_TYPE_FINISHED; + mailbox_send_and_wait(&finished_reply); + while(1); } diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index f16b4a1c3..6b72e0c4b 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -6,6 +6,7 @@ #include enum { + MESSAGE_TYPE_LOAD_REQUEST, MESSAGE_TYPE_LOAD_REPLY, MESSAGE_TYPE_NOW_INIT_REQUEST, MESSAGE_TYPE_NOW_INIT_REPLY, @@ -33,9 +34,8 @@ struct msg_base { /* kernel messages */ struct msg_load_request { + int type; const void *library; - struct dyld_info *library_info; - int run_kernel; }; struct msg_load_reply { diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index bbe89cb47..7e8236e86 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -929,6 +929,11 @@ static int process_kmsg(struct msg_base *umsg) return 0; if(kloader_is_essential_kmsg(umsg->type)) return 1; /* handled elsewhere */ + if(user_kernel_state == USER_KERNEL_LOADED && + umsg->type == MESSAGE_TYPE_LOAD_REPLY) { + // Kernel standing by. + return 1; + } if(user_kernel_state == USER_KERNEL_WAIT_RPC && umsg->type == MESSAGE_TYPE_RPC_RECV_REQUEST) { // Handled and acknowledged when we receive From 1cbb187136becbbbe17e2be458877d95a563ecae Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 30 Sep 2016 03:07:27 +0000 Subject: [PATCH 014/127] runtime: eliminate va_list from kernel interface. --- .../compiler/transforms/llvm_ir_generator.py | 14 ++++--- artiq/runtime/ksupport.c | 40 +++++++++++++------ artiq/runtime/ksupport.h | 2 +- artiq/runtime/messages.h | 6 +-- artiq/runtime/session.c | 6 +-- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 826db2b4d..ecc21c3cb 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -19,6 +19,7 @@ lli32 = ll.IntType(32) lli64 = ll.IntType(64) lldouble = ll.DoubleType() llptr = ll.IntType(8).as_pointer() +llptrptr = ll.IntType(8).as_pointer().as_pointer() llmetadata = ll.MetaData() @@ -349,8 +350,7 @@ class LLVMIRGenerator: elif name == "strcmp": llty = ll.FunctionType(lli32, [llptr, llptr]) elif name == "send_rpc": - llty = ll.FunctionType(llvoid, [lli32, llptr], - var_arg=True) + llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) elif name == "recv_rpc": llty = ll.FunctionType(lli32, [llptr]) elif name == "now": @@ -1233,7 +1233,8 @@ class LLVMIRGenerator: llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], name="rpc.stack") - llargs = [] + llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)), + name="rpc.args") for index, arg in enumerate(args): if builtins.is_none(arg.type): llargslot = self.llbuilder.alloca(ll.LiteralStructType([]), @@ -1243,10 +1244,13 @@ class LLVMIRGenerator: llargslot = self.llbuilder.alloca(llarg.type, name="rpc.arg{}".format(index)) self.llbuilder.store(llarg, llargslot) - llargs.append(llargslot) + llargslot = self.llbuilder.bitcast(llargslot, llptr) + + llargptr = self.llbuilder.gep(llargs, [ll.Constant(lli32, index)]) + self.llbuilder.store(llargslot, llargptr) self.llbuilder.call(self.llbuiltin("send_rpc"), - [llservice, lltag] + llargs) + [llservice, lltag, llargs]) # Don't waste stack space on saved arguments. self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 00066cda3..7d5b5d21a 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -272,13 +272,19 @@ double sqrt(double x) /* called by libunwind */ int fprintf(FILE *stream, const char *fmt, ...) { - struct msg_log request; + va_list args; + va_start(args, fmt); + char buf[256]; + int len = vscnprintf(buf, sizeof(buf), fmt, args); + + va_end(args); + + struct msg_log request; request.type = MESSAGE_TYPE_LOG; - request.fmt = fmt; - va_start(request.args, fmt); + request.buf = buf; + request.len = len; mailbox_send_and_wait(&request); - va_end(request.args); return 0; } @@ -462,7 +468,7 @@ void watchdog_clear(int id) mailbox_send_and_wait(&request); } -void send_rpc(int service, const char *tag, ...) +void send_rpc(int service, const char *tag, void **data) { struct msg_rpc_send request; @@ -472,9 +478,8 @@ void send_rpc(int service, const char *tag, ...) request.type = MESSAGE_TYPE_RPC_BATCH; request.service = service; request.tag = tag; - va_start(request.args, tag); + request.data = data; mailbox_send_and_wait(&request); - va_end(request.args); } int recv_rpc(void *slot) @@ -537,7 +542,12 @@ void attribute_writeback(void *utypes) if(attr->tag) { uintptr_t value = (uintptr_t)object + attr->offset; - send_rpc(0, attr->tag, &object, &attr->name, value); + void *args[] = { + &object, + &attr->name, + (void*)value + }; + send_rpc(0, attr->tag, args); } } } @@ -590,11 +600,17 @@ void cache_put(const char *key, struct artiq_list value) void core_log(const char *fmt, ...) { - struct msg_log request; + va_list args; + va_start(args, fmt); + char buf[256]; + int len = vscnprintf(buf, sizeof(buf), fmt, args); + + va_end(args); + + struct msg_log request; request.type = MESSAGE_TYPE_LOG; - request.fmt = fmt; - va_start(request.args, fmt); + request.buf = buf; + request.len = len; mailbox_send_and_wait(&request); - va_end(request.args); } diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h index fc3ef7662..6624ffcfa 100644 --- a/artiq/runtime/ksupport.h +++ b/artiq/runtime/ksupport.h @@ -8,7 +8,7 @@ struct artiq_list { int watchdog_set(int ms); void watchdog_clear(int id); -void send_rpc(int service, const char *tag, ...); +void send_rpc(int service, const char *tag, void **data); int recv_rpc(void *slot); struct artiq_list cache_get(const char *key); void cache_put(const char *key, struct artiq_list value); diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index 6b72e0c4b..30b5bba1d 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -79,7 +79,7 @@ struct msg_rpc_send { int type; int service; const char *tag; - va_list args; + void **data; }; struct msg_rpc_recv_request { @@ -118,8 +118,8 @@ struct msg_cache_put_reply { struct msg_log { int type; - const char *fmt; - va_list args; + const char *buf; + size_t len; }; #endif /* __MESSAGES_H */ diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index 7e8236e86..1cb361bed 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -893,13 +893,13 @@ static int send_rpc_value(const char **tag, void **value) return 1; } -static int send_rpc_request(int service, const char *tag, va_list args) +static int send_rpc_request(int service, const char *tag, void **data) { out_packet_start(REMOTEMSG_TYPE_RPC_REQUEST); out_packet_int32(service); while(*tag != ':') { - void *value = va_arg(args, void*); + void *value = *data++; if(!kloader_validate_kpointer(value)) return 0; if(!send_rpc_value(&tag, &value)) @@ -994,7 +994,7 @@ static int process_kmsg(struct msg_base *umsg) case MESSAGE_TYPE_RPC_BATCH: { struct msg_rpc_send *msg = (struct msg_rpc_send *)umsg; - if(!send_rpc_request(msg->service, msg->tag, msg->args)) { + if(!send_rpc_request(msg->service, msg->tag, msg->data)) { core_log("Failed to send RPC request (service %d, tag %s)\n", msg->service, msg->tag); return 0; // restart session From b3b1ea71c5db8ef171dfc41f621fab146ea3babc Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 04:20:27 +0000 Subject: [PATCH 015/127] Rust: implement basic communication with kernel CPU. --- artiq/runtime.rs/Cargo.toml | 2 +- artiq/runtime.rs/liblwip-sys/lib.rs | 18 +- artiq/runtime.rs/liblwip/lib.rs | 21 +- artiq/runtime.rs/src/kernel.rs | 37 +++ artiq/runtime.rs/src/kernel_proto.rs | 323 +++++++++++++++++++++++++++ artiq/runtime.rs/src/lib.rs | 6 +- artiq/runtime.rs/src/logger.rs | 4 +- artiq/runtime.rs/src/mailbox.rs | 28 +-- artiq/runtime.rs/src/sched.rs | 42 +++- artiq/runtime.rs/src/session.rs | 244 ++++++++++++++------ artiq/runtime/Makefile | 6 +- artiq/runtime/kloader.c | 2 +- 12 files changed, 619 insertions(+), 114 deletions(-) create mode 100644 artiq/runtime.rs/src/kernel.rs create mode 100644 artiq/runtime.rs/src/kernel_proto.rs diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index eb8c3f2c9..aba3d1aae 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -8,7 +8,7 @@ build = "build.rs" walkdir = "0.1" [lib] -name = "artiq_rust" +name = "runtime" crate-type = ["staticlib"] path = "src/lib.rs" diff --git a/artiq/runtime.rs/liblwip-sys/lib.rs b/artiq/runtime.rs/liblwip-sys/lib.rs index aaac1d424..5ea2e7d1e 100644 --- a/artiq/runtime.rs/liblwip-sys/lib.rs +++ b/artiq/runtime.rs/liblwip-sys/lib.rs @@ -122,25 +122,25 @@ extern { pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err; pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb; pub fn tcp_accept(pcb: *mut tcp_pcb, - accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb, - err: err) -> err); + accept: Option err>); pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16, connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err; pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err; pub fn tcp_sent(pcb: *mut tcp_pcb, - sent: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err); + sent: Option err>); pub fn tcp_recv(pcb: *mut tcp_pcb, - recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf, - err: err) -> err); + recv: Option err>); pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16); pub fn tcp_poll(pcb: *mut tcp_pcb, - poll: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb), + poll: Option, interval: u8); pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err; pub fn tcp_close(pcb: *mut tcp_pcb) -> err; pub fn tcp_abort(pcb: *mut tcp_pcb); pub fn tcp_err(pcb: *mut tcp_pcb, - err: extern fn(arg: *mut c_void, err: err)); + err: Option); // nonstandard pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; @@ -154,7 +154,7 @@ extern { pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err; pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err; pub fn udp_recv(pcb: *mut udp_pcb, - recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf, - addr: *mut ip_addr, port: u16), + recv: Option, recv_arg: *mut c_void); } diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index 7c1c32c53..f8938653b 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -279,12 +279,12 @@ impl UdpSocket { recv_buffer: LinkedList::new() })); let arg = &mut *state as *mut RefCell as *mut _; - lwip_sys::udp_recv(raw, recv, arg); + lwip_sys::udp_recv(raw, Some(recv), arg); Ok(UdpSocket { raw: raw, state: state }) } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } @@ -376,12 +376,12 @@ impl TcpListener { })); let arg = &mut *state as *mut RefCell as *mut _; lwip_sys::tcp_arg(raw2, arg); - lwip_sys::tcp_accept(raw2, accept); + lwip_sys::tcp_accept(raw2, Some(accept)); Ok(TcpListener { raw: raw2, state: state }) } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } @@ -467,14 +467,14 @@ impl TcpStream { })); let arg = &mut *state as *mut RefCell as *mut _; lwip_sys::tcp_arg(raw, arg); - lwip_sys::tcp_recv(raw, recv); - lwip_sys::tcp_sent(raw, sent); - lwip_sys::tcp_err(raw, err); + lwip_sys::tcp_recv(raw, Some(recv)); + lwip_sys::tcp_sent(raw, Some(sent)); + lwip_sys::tcp_err(raw, Some(err)); TcpStream { raw: raw, state: state } } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } @@ -536,6 +536,11 @@ impl TcpStream { impl Drop for TcpStream { fn drop(&mut self) { unsafe { + // lwip *will* try to call back after tcp_close + lwip_sys::tcp_recv(self.raw, None); + lwip_sys::tcp_sent(self.raw, None); + lwip_sys::tcp_err(self.raw, None); + // tcp_close can fail here, but in drop() we don't care let _ = lwip_sys::tcp_close(self.raw); } diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs new file mode 100644 index 000000000..06f9c4822 --- /dev/null +++ b/artiq/runtime.rs/src/kernel.rs @@ -0,0 +1,37 @@ +use core::ptr; +use board::csr::kernel_cpu; +use mailbox; + +const KERNELCPU_EXEC_ADDRESS: usize = 0x42000000; +const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x42020000; +const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024); +const KSUPPORT_HEADER_SIZE: usize = 0x80; + +pub unsafe fn start() { + if kernel_cpu::reset_read() == 0 { + panic!("attempted to start kernel CPU when it is already running") + } + + stop(); + + extern { + static _binary_ksupport_elf_start: (); + static _binary_ksupport_elf_end: (); + } + let ksupport_start = &_binary_ksupport_elf_start as *const _ as usize; + let ksupport_end = &_binary_ksupport_elf_end as *const _ as usize; + ptr::copy_nonoverlapping(ksupport_start as *const u8, + (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8, + ksupport_end - ksupport_start); + + kernel_cpu::reset_write(0); +} + +pub fn stop() { + unsafe { kernel_cpu::reset_write(1) } + mailbox::acknowledge(); +} + +pub fn validate(ptr: usize) -> bool { + ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS +} diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs new file mode 100644 index 000000000..9a16e1c1a --- /dev/null +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -0,0 +1,323 @@ +use std::io; +use mailbox; +use kernel; + +#[derive(Debug)] +pub struct Exception<'a> { + pub name: &'a str, + pub file: &'a str, + pub line: u32, + pub column: u32, + pub function: &'a str, + pub message: &'a str, + pub param: [u64; 3], +} + +pub use self::c::BacktraceItem; + +#[derive(Debug)] +pub enum Message<'a> { + LoadRequest(&'a [u8]), + LoadReply { error: Option<&'a str> }, + + NowInitRequest, + NowInitReply(u64), + NowSave(u64), + + RunFinished, + RunException { + exception: Exception<'a>, + backtrace: &'a [BacktraceItem] + }, + + WatchdogSetRequest { ms: u64 }, + WatchdogSetReply { id: usize }, + WatchdogClear { id: usize }, + + RpcSend { + service: u32, + tag: &'a [u8], + data: *const *const () + }, + RpcRecvRequest { + slot: *mut () + }, + RpcRecvReply { + alloc_size: usize, + exception: Option> + }, + + CacheGetRequest { key: &'a str }, + CacheGetReply { value: &'static [u32] }, + CachePutRequest { key: &'a str, value: &'static [u32] }, + CachePutReply { succeeded: bool }, + + Log(&'a str) +} + +pub use self::Message::*; + +impl<'a> Message<'a> { + fn into_lower R>(self, f: F) -> R { + match self { + Message::LoadRequest(library) => { + let msg = c::LoadRequest { + ty: c::Type::LoadRequest, + library: library.as_ptr() as *const _ + }; + f(&msg as *const _ as *const _) + } + + Message::NowInitReply(now) => { + let msg = c::NowInitReply { + ty: c::Type::NowInitReply, + now: now + }; + f(&msg as *const _ as *const _) + } + + other => panic!("Message::into_lower: {:?} unimplemented", other) + } + } + + unsafe fn from_lower(ptr: *const ()) -> Self { + let msg = ptr as *const c::Message; + match (*msg).ty { + c::Type::LoadReply => { + let msg = ptr as *const c::LoadReply; + let error = if (*msg).error.is_null() { + None + } else { + Some(c::from_c_str((*msg).error)) + }; + Message::LoadReply { error: error } + } + + c::Type::NowInitRequest => Message::NowInitRequest, + c::Type::NowSave => { + let msg = ptr as *const c::NowSave; + Message::NowSave((*msg).now) + } + + c::Type::RunFinished => Message::RunFinished, + + c::Type::Log => { + let msg = ptr as *const c::Log; + Message::Log(c::from_c_str_len((*msg).buf, (*msg).len)) + } + + ref other => panic!("Message::from_lower: {:?} unimplemented", other) + } + } + + pub fn send_and_wait(self, waiter: ::sched::Waiter) -> io::Result<()> { + self.into_lower(|ptr| { + unsafe { mailbox::send(ptr as usize) } + waiter.until(mailbox::acknowledged) + }) + } + + pub fn wait_and_receive(waiter: ::sched::Waiter, f: F) -> io::Result + where F: FnOnce(Message<'a>) -> 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 msg = unsafe { Self::from_lower(mailbox::receive() as *const ()) }; + Ok(try!(f(msg))) + } + + pub fn acknowledge() { + unsafe { mailbox::acknowledge() } + } +} + +// Low-level representation, compatible with the C code in ksupport +mod c { + use libc::{c_void, c_int, c_char, size_t}; + + #[repr(u32)] + #[derive(Debug)] + pub enum Type { + LoadRequest, + LoadReply, + NowInitRequest, + NowInitReply, + NowSave, + RunFinished, + RunException, + WatchdogSetRequest, + WatchdogSetReply, + WatchdogClear, + RpcSend, + RpcRecvRequest, + RpcRecvReply, + RpcBatch, + CacheGetRequest, + CacheGetReply, + CachePutRequest, + CachePutReply, + Log, + } + + #[repr(C)] + #[derive(Debug)] + pub struct Message { + pub ty: Type + } + + // kernel messages + + #[repr(C)] + #[derive(Debug)] + pub struct LoadRequest { + pub ty: Type, + pub library: *const c_void, + } + + #[repr(C)] + #[derive(Debug)] + pub struct LoadReply { + pub ty: Type, + pub error: *const c_char + } + + #[repr(C)] + #[derive(Debug)] + pub struct NowInitReply { + pub ty: Type, + pub now: u64 + } + + #[repr(C)] + #[derive(Debug)] + pub struct NowSave { + pub ty: Type, + pub now: u64 + } + + #[repr(C)] + #[derive(Debug)] + pub struct RunException { + pub ty: Type, + pub exception: *const Exception, + pub backtrace: *const BacktraceItem, + pub backtrace_size: size_t + } + + #[repr(C)] + #[derive(Debug)] + pub struct WatchdogSetRequest { + pub ty: Type, + pub ms: c_int + } + + #[repr(C)] + #[derive(Debug)] + pub struct WatchdogSetReply { + pub ty: Type, + pub id: c_int + } + + #[repr(C)] + #[derive(Debug)] + pub struct WatchdogClear { + pub ty: Type, + pub id: c_int + } + + #[repr(C)] + #[derive(Debug)] + pub struct RpcSend { + pub ty: Type, + pub service: c_int, + pub tag: *const c_char, + pub data: *const *const c_void + } + + #[repr(C)] + #[derive(Debug)] + pub struct RpcRecvRequest { + pub ty: Type, + pub slot: *mut c_void + } + + #[repr(C)] + #[derive(Debug)] + pub struct RpcRecvReply { + pub ty: Type, + pub alloc_size: c_int, + pub exception: *const Exception + } + + #[repr(C)] + #[derive(Debug)] + pub struct CacheGetRequest { + pub ty: Type, + pub key: *const c_char + } + + #[repr(C)] + #[derive(Debug)] + pub struct CacheGetReply { + pub ty: Type, + pub length: size_t, + pub elements: *const u32 + } + + #[repr(C)] + #[derive(Debug)] + pub struct CachePutRequest { + pub ty: Type, + pub key: *const c_char, + pub length: size_t, + pub elements: *const u32 + } + + #[repr(C)] + #[derive(Debug)] + pub struct CachePutReply { + pub ty: Type, + pub succeeded: c_int + } + + #[repr(C)] + #[derive(Debug)] + pub struct Log { + pub ty: Type, + pub buf: *const c_char, + pub len: size_t + } + + // Supplementary structures + + #[repr(C)] + #[derive(Debug)] + pub struct Exception { + pub name: *const c_char, // or typeinfo + pub file: *const c_char, + pub line: u32, + pub column: u32, + pub function: *const c_char, + pub message: *const c_char, + pub param: [u64; 3], + } + + #[repr(C)] + #[derive(Debug)] + pub struct BacktraceItem { + pub function: usize, + pub offset: usize + } + + pub unsafe fn from_c_str_len<'a>(ptr: *const c_char, len: size_t) -> &'a str { + use core::{str, slice}; + str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, len)) + } + + pub unsafe fn from_c_str<'a>(ptr: *const c_char) -> &'a str { + extern { fn strlen(cs: *const c_char) -> size_t; } + from_c_str_len(ptr, strlen(ptr)) + } +} diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 215f3c7d5..5f3e382ca 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -20,7 +20,10 @@ mod mailbox; mod logger; +mod kernel_proto; mod session_proto; + +mod kernel; mod session; extern { @@ -42,9 +45,10 @@ pub unsafe extern fn rust_main() { network_init(); let mut scheduler = sched::Scheduler::new(); - scheduler.spawn(4096, move |waiter| { + scheduler.spawn(8192, move |waiter| { session::handler(waiter, logger) }); + loop { lwip_service(); scheduler.run() diff --git a/artiq/runtime.rs/src/logger.rs b/artiq/runtime.rs/src/logger.rs index 7e7537f8e..377470a2e 100644 --- a/artiq/runtime.rs/src/logger.rs +++ b/artiq/runtime.rs/src/logger.rs @@ -48,9 +48,9 @@ impl Log for BufferLogger { if self.enabled(record.metadata()) { use core::fmt::Write; writeln!(self.buffer.borrow_mut(), "{:>5}({}): {}", - record.level(), record.location().module_path(), record.args()).unwrap(); + record.level(), record.target(), record.args()).unwrap(); println!("{:>5}({}): {}", - record.level(), record.location().module_path(), record.args()); + record.level(), record.target(), record.args()); } } } diff --git a/artiq/runtime.rs/src/mailbox.rs b/artiq/runtime.rs/src/mailbox.rs index 5d13225ce..030b7fb28 100644 --- a/artiq/runtime.rs/src/mailbox.rs +++ b/artiq/runtime.rs/src/mailbox.rs @@ -1,14 +1,12 @@ use core::ptr::{read_volatile, write_volatile}; use board; -const MAILBOX: *mut u32 = board::mem::MAILBOX_BASE as *mut u32; -static mut last: u32 = 0; +const MAILBOX: *mut usize = board::mem::MAILBOX_BASE as *mut usize; +static mut last: usize = 0; -pub fn send(data: u32) { - unsafe { - last = data; - write_volatile(MAILBOX, data) - } +pub unsafe fn send(data: usize) { + last = data; + write_volatile(MAILBOX, data) } pub fn acknowledged() -> bool { @@ -18,12 +16,7 @@ pub fn acknowledged() -> bool { } } -pub fn send_and_wait(data: u32) { - send(data); - while !acknowledged() {} -} - -pub fn receive() -> u32 { +pub fn receive() -> usize { unsafe { let data = read_volatile(MAILBOX); if data == last { @@ -37,15 +30,6 @@ pub fn receive() -> u32 { } } -pub fn wait_and_receive() -> u32 { - loop { - let data = receive(); - if data != 0 { - return data - } - } -} - pub fn acknowledge() { unsafe { write_volatile(MAILBOX, 0) } } diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index f48d6d6ff..3d1dcc8c4 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -104,8 +104,8 @@ impl Scheduler { } } -#[derive(Debug)] enum WaitEvent { + Completion(*const (Fn() -> bool + 'static)), UdpReadable(*const RefCell), TcpAcceptable(*const RefCell), TcpWriteable(*const RefCell), @@ -115,6 +115,8 @@ enum WaitEvent { impl WaitEvent { fn completed(&self) -> bool { match *self { + WaitEvent::Completion(f) => + unsafe { (*f)() }, WaitEvent::UdpReadable(state) => unsafe { (*state).borrow().readable() }, WaitEvent::TcpAcceptable(state) => @@ -127,12 +129,27 @@ impl WaitEvent { } } +// *const DST doesn't have impl Debug +impl ::core::fmt::Debug for WaitEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> + ::core::result::Result<(), ::core::fmt::Error> { + write!(f, "WaitEvent...") + } +} + unsafe impl Send for WaitEvent {} #[derive(Debug, Clone, Copy)] pub struct Waiter<'a>(&'a Yielder); impl<'a> Waiter<'a> { + pub fn relinquish(&self) { + self.0.suspend(WaitRequest { + timeout: None, + event: None + }); + } + pub fn sleep(&self, duration: Duration) -> Result<()> { let request = WaitRequest { timeout: Some(Instant::now() + duration), @@ -154,6 +171,13 @@ impl<'a> Waiter<'a> { } } + pub fn until bool + 'static>(&self, f: F) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: Some(WaitEvent::Completion(&f as *const _)) + }) + } + pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> { self.suspend(WaitRequest { timeout: None, @@ -239,6 +263,10 @@ impl<'a> UdpSocket<'a> { (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); Ok(len) } + + pub fn readable(&self) -> bool { + self.lower.state().borrow().readable() + } } #[derive(Debug)] @@ -265,6 +293,10 @@ impl<'a> TcpListener<'a> { buffer: None }, addr)) } + + pub fn acceptable(&self) -> bool { + self.lower.state().borrow().acceptable() + } } pub use self::lwip::Shutdown; @@ -280,6 +312,14 @@ impl<'a> TcpStream<'a> { pub fn shutdown(&self, how: Shutdown) -> Result<()> { Ok(try!(self.lower.shutdown(how))) } + + pub fn readable(&self) -> bool { + self.buffer.is_some() || self.lower.state().borrow().readable() + } + + pub fn writeable(&self) -> bool { + self.lower.state().borrow().writeable() + } } impl<'a> Read for TcpStream<'a> { diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index d95bc9619..03def4132 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -1,12 +1,23 @@ use std::prelude::v1::*; use std::str; -use std::io::{self, Read, ErrorKind}; -use {config, rtio_crg}; +use std::io::{self, Read}; +use {config, rtio_crg, clock, mailbox, kernel}; use logger::BufferLogger; use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY}; -use session_proto::*; -#[derive(Debug, Clone, Copy)] +use session_proto as host; +use kernel_proto as kern; + +macro_rules! unexpected { + ($($arg:tt)*) => { + { + error!($($arg)*); + return Err(io::Error::new(io::ErrorKind::InvalidData, "protocol error")) + } + }; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum KernelState { Absent, Loaded, @@ -17,19 +28,16 @@ enum KernelState { #[derive(Debug)] pub struct Session { kernel_state: KernelState, -} - -extern { - fn kloader_stop(); - fn watchdog_init(); - fn kloader_start_idle_kernel(); + watchdog_set: clock::WatchdogSet, + now: u64 } impl Session { pub fn new() -> Session { - unsafe { kloader_stop(); } Session { - kernel_state: KernelState::Absent + kernel_state: KernelState::Absent, + watchdog_set: clock::WatchdogSet::new(), + now: 0 } } @@ -41,16 +49,6 @@ impl Session { } } -impl Drop for Session { - fn drop(&mut self) { - unsafe { - kloader_stop(); - watchdog_init(); - kloader_start_idle_kernel(); - } - } -} - fn check_magic(stream: &mut TcpStream) -> io::Result<()> { const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; @@ -63,89 +61,203 @@ fn check_magic(stream: &mut TcpStream) -> io::Result<()> { } } -fn handle_request(stream: &mut TcpStream, - logger: &BufferLogger, - session: &mut Session) -> io::Result<()> { - fn read_request(stream: &mut TcpStream) -> io::Result { - let request = try!(Request::read_from(stream)); - match &request { - &Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"), - _ => trace!("comm<-host {:?}", request) - } - Ok(request) +fn host_read(stream: &mut TcpStream) -> io::Result { + let request = try!(host::Request::read_from(stream)); + match &request { + &host::Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"), + _ => trace!("comm<-host {:?}", request) } + Ok(request) +} - fn write_reply(stream: &mut TcpStream, reply: Reply) -> io::Result<()> { - trace!("comm->host {:?}", reply); - reply.write_to(stream) +fn host_write(stream: &mut TcpStream, reply: host::Reply) -> io::Result<()> { + trace!("comm->host {:?}", reply); + reply.write_to(stream) +} + +fn kern_send<'a>(waiter: Waiter, request: kern::Message<'a>) -> io::Result<()> { + match &request { + &kern::LoadRequest(_) => trace!("comm->kern LoadRequest(...)"), + _ => trace!("comm->kern {:?}", request) } + request.send_and_wait(waiter) +} - match try!(read_request(stream)) { - Request::Ident => - write_reply(stream, Reply::Ident(::board::ident(&mut [0; 64]))), +fn kern_recv(waiter: Waiter, f: F) -> io::Result + where F: FnOnce(kern::Message) -> io::Result { + kern::Message::wait_and_receive(waiter, |reply| { + trace!("comm<-kern {:?}", reply); + f(reply) + }) +} + +fn kern_acknowledge() -> io::Result<()> { + kern::Message::acknowledge(); + Ok(()) +} + +fn comm_handle(waiter: Waiter, + stream: &mut TcpStream, + logger: &BufferLogger, + session: &mut Session) -> io::Result<()> { + match try!(host_read(stream)) { + host::Request::Ident => + host_write(stream, host::Reply::Ident(::board::ident(&mut [0; 64]))), // artiq_corelog - Request::Log => { + host::Request::Log => { // Logging the packet with the log is inadvisable trace!("comm->host Log(...)"); logger.extract(move |log| { - Reply::Log(log).write_to(stream) + host::Reply::Log(log).write_to(stream) }) } - Request::LogClear => { + host::Request::LogClear => { logger.clear(); - write_reply(stream, Reply::Log("")) + host_write(stream, host::Reply::Log("")) } // artiq_coreconfig - Request::FlashRead { ref key } => { + host::Request::FlashRead { ref key } => { let value = config::read_to_end(key); - write_reply(stream, Reply::FlashRead(&value)) + host_write(stream, host::Reply::FlashRead(&value)) } - Request::FlashWrite { ref key, ref value } => { + host::Request::FlashWrite { ref key, ref value } => { match config::write(key, value) { - Ok(_) => write_reply(stream, Reply::FlashOk), - Err(_) => write_reply(stream, Reply::FlashError) + Ok(_) => host_write(stream, host::Reply::FlashOk), + Err(_) => host_write(stream, host::Reply::FlashError) } } - Request::FlashRemove { ref key } => { + host::Request::FlashRemove { ref key } => { config::remove(key); - write_reply(stream, Reply::FlashOk) + host_write(stream, host::Reply::FlashOk) } - Request::FlashErase => { + host::Request::FlashErase => { config::erase(); - write_reply(stream, Reply::FlashOk) + host_write(stream, host::Reply::FlashOk) } // artiq_run/artiq_master - Request::SwitchClock(clk) => { + host::Request::SwitchClock(clk) => { if session.running() { - error!("attempted to switch RTIO clock while kernel was running"); - write_reply(stream, Reply::ClockSwitchFailed) + error!("attempted to switch RTIO clock while a kernel was running"); + return host_write(stream, host::Reply::ClockSwitchFailed) + } + + if rtio_crg::switch_clock(clk) { + host_write(stream, host::Reply::ClockSwitchCompleted) } else { - if rtio_crg::switch_clock(clk) { - write_reply(stream, Reply::ClockSwitchCompleted) - } else { - write_reply(stream, Reply::ClockSwitchFailed) - } + host_write(stream, host::Reply::ClockSwitchFailed) } } - _ => unreachable!() + host::Request::LoadLibrary(library) => { + if session.running() { + error!("attempted to load a new kernel while a kernel was running"); + return host_write(stream, host::Reply::LoadFailed) + } + + unsafe { kernel::start() } + + try!(kern_send(waiter, kern::LoadRequest(&library))); + kern_recv(waiter, |reply| { + match reply { + kern::LoadReply { error: None } => { + session.kernel_state = KernelState::Loaded; + host_write(stream, host::Reply::LoadCompleted) + } + kern::LoadReply { error: Some(cause) } => { + error!("cannot load kernel: {}", cause); + host_write(stream, host::Reply::LoadFailed) + } + other => unexpected!("unexpected reply from kernel CPU: {:?}", other) + } + }) + } + + host::Request::RunKernel => { + if session.kernel_state != KernelState::Loaded { + error!("attempted to run a kernel while not in Loaded state"); + return host_write(stream, host::Reply::KernelStartupFailed) + } + + session.kernel_state = KernelState::Running; + kern_acknowledge() + } + + request => unexpected!("unexpected {:?}", request) } } -fn handle_requests(stream: &mut TcpStream, - logger: &BufferLogger) -> io::Result<()> { +fn kern_handle(waiter: Waiter, + stream: &mut TcpStream, + session: &mut Session) -> io::Result<()> { + kern::Message::wait_and_receive(waiter, |request| { + match (&request, session.kernel_state) { + (&kern::LoadReply { .. }, KernelState::Loaded) | + (&kern::RpcRecvRequest { .. }, KernelState::RpcWait) => { + // We're standing by; ignore the message. + return Ok(()) + } + (_, KernelState::Running) => (), + _ => { + unexpected!("unexpected request {:?} from kernel CPU in {:?} state", + request, session.kernel_state) + } + } + + trace!("comm<-kern {:?}", request); + match request { + kern::Log(log) => { + info!(target: "kernel", "{}", log); + kern_acknowledge() + } + + kern::NowInitRequest => + kern_send(waiter, kern::NowInitReply(session.now)), + + kern::NowSave(now) => { + session.now = now; + kern_acknowledge() + } + + request => unexpected!("unexpected {:?}", request) + } + }) +} + +fn handle(waiter: Waiter, + stream: &mut TcpStream, + logger: &BufferLogger) -> io::Result<()> { try!(check_magic(stream)); let mut session = Session::new(); loop { - try!(handle_request(stream, logger, &mut session)) + if stream.readable() { + try!(comm_handle(waiter, stream, logger, &mut session)) + } + + if mailbox::receive() != 0 { + try!(kern_handle(waiter, stream, &mut session)) + } + + if session.kernel_state == KernelState::Running { + if session.watchdog_set.expired() { + try!(host_write(stream, host::Reply::WatchdogExpired)); + return Err(io::Error::new(io::ErrorKind::Other, "watchdog expired")) + } + + if !rtio_crg::check() { + try!(host_write(stream, host::Reply::ClockFailure)); + return Err(io::Error::new(io::ErrorKind::Other, "RTIO clock failure")) + } + } + + waiter.relinquish() } } @@ -159,13 +271,13 @@ pub fn handler(waiter: Waiter, let (mut stream, addr) = listener.accept().unwrap(); info!("new connection from {:?}", addr); - match handle_requests(&mut stream, logger) { + match handle(waiter, &mut stream, logger) { Ok(()) => (), Err(err) => { - if err.kind() == ErrorKind::UnexpectedEof { + if err.kind() == io::ErrorKind::UnexpectedEof { info!("connection closed"); } else { - error!("cannot handle network request: {:?}", err); + error!("session aborted: {:?}", err); } } } diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index bc9439a84..a29982ecf 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -27,7 +27,7 @@ all: runtime.bin runtime.fbi %.fbi: %.bin @echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< -runtime.elf: $(OBJECTS) libartiq_rust.a +runtime.elf: $(OBJECTS) libruntime.a $(LD) $(LDFLAGS) \ --gc-sections \ -T $(RUNTIME_DIRECTORY)/runtime.ld \ @@ -40,7 +40,7 @@ runtime.elf: $(OBJECTS) libartiq_rust.a -L../liballoc \ -L../liblwip \ -Lcargo/or1k-unknown-none/debug/ \ - -lartiq_rust -lbase -lm -lcompiler-rt -lalloc -llwip + -lruntime -lbase -lm -lcompiler-rt -lalloc -llwip @chmod -x $@ ksupport.elf: $(OBJECTS_KSUPPORT) @@ -60,7 +60,7 @@ ksupport.elf: $(OBJECTS_KSUPPORT) ksupport_data.o: ksupport.elf $(LD) -r -b binary -o $@ $< -libartiq_rust.a: +libruntime.a: CARGO_TARGET_DIR="./cargo" \ cargo rustc --verbose \ --manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \ diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c index 06cf737c5..3dc98e539 100644 --- a/artiq/runtime/kloader.c +++ b/artiq/runtime/kloader.c @@ -167,7 +167,7 @@ void kloader_service_essential_kmsg(void) case MESSAGE_TYPE_LOG: { struct msg_log *msg = (struct msg_log *)umsg; - core_log_va(msg->fmt, msg->args); + core_log("%s", msg->buf); mailbox_acknowledge(); break; } From d3dcb4b8a20c23db5082070911c33f6510526606 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 06:12:30 +0000 Subject: [PATCH 016/127] runtime: remove useless copy of flush_cpu_dcache(). ksupport used to not link to libbase, I think. --- artiq/runtime/mailbox.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/artiq/runtime/mailbox.c b/artiq/runtime/mailbox.c index 98e49045b..1faa6d374 100644 --- a/artiq/runtime/mailbox.c +++ b/artiq/runtime/mailbox.c @@ -10,25 +10,6 @@ static unsigned int last_transmission; -static void _flush_cpu_dcache(void) -{ - unsigned long dccfgr; - unsigned long cache_set_size; - unsigned long cache_ways; - unsigned long cache_block_size; - unsigned long cache_size; - int i; - - dccfgr = mfspr(SPR_DCCFGR); - cache_ways = 1 << (dccfgr & SPR_ICCFGR_NCW); - cache_set_size = 1 << ((dccfgr & SPR_DCCFGR_NCS) >> 3); - cache_block_size = (dccfgr & SPR_DCCFGR_CBS) ? 32 : 16; - cache_size = cache_set_size * cache_ways * cache_block_size; - - for (i = 0; i < cache_size; i += cache_block_size) - mtspr(SPR_DCBIR, i); -} - void mailbox_send(void *ptr) { last_transmission = (unsigned int)ptr; @@ -58,7 +39,7 @@ void *mailbox_receive(void) return NULL; else { if(r) { - _flush_cpu_dcache(); + flush_cpu_dcache(); } return (void *)r; } From 999290fe521700d0ac25334ff860d933ed38d220 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 06:50:16 +0000 Subject: [PATCH 017/127] runtime: link ksupport with libm, not runtime. We need libm for the %g format specifier. --- artiq/runtime/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index a29982ecf..1068a1db9 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -36,11 +36,10 @@ runtime.elf: $(OBJECTS) libruntime.a $(OBJECTS) \ -L../libcompiler-rt \ -L../libbase \ - -L../libm \ -L../liballoc \ -L../liblwip \ -Lcargo/or1k-unknown-none/debug/ \ - -lruntime -lbase -lm -lcompiler-rt -lalloc -llwip + -lruntime -lbase-nofloat -lcompiler-rt -lalloc -llwip @chmod -x $@ ksupport.elf: $(OBJECTS_KSUPPORT) @@ -51,10 +50,11 @@ ksupport.elf: $(OBJECTS_KSUPPORT) ../libbase/crt0-$(CPU).o \ $^ \ -L../libbase \ + -L../libm \ -L../libcompiler-rt \ -L../libunwind \ -L../libdyld \ - -lbase-nofloat -lcompiler-rt -ldyld -lunwind + -lbase -lm -lcompiler-rt -ldyld -lunwind @chmod -x $@ ksupport_data.o: ksupport.elf From ab3bd67412c2783df0b92fd6987c181e6c06d7fd Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 07:56:34 +0000 Subject: [PATCH 018/127] Rust: style (NFC). --- artiq/runtime.rs/src/clock.rs | 22 +++++++++++----------- artiq/runtime.rs/src/kernel.rs | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artiq/runtime.rs/src/clock.rs b/artiq/runtime.rs/src/clock.rs index 5644f472c..b9a0584b3 100644 --- a/artiq/runtime.rs/src/clock.rs +++ b/artiq/runtime.rs/src/clock.rs @@ -1,30 +1,30 @@ -use board::csr::timer0; +use board::csr; const INIT: u64 = ::core::i64::MAX as u64; const FREQ: u64 = ::board::csr::CONFIG_CLOCK_FREQUENCY as u64; pub fn init() { unsafe { - timer0::en_write(0); - timer0::load_write(INIT); - timer0::reload_write(INIT); - timer0::en_write(1); + csr::timer0::en_write(0); + csr::timer0::load_write(INIT); + csr::timer0::reload_write(INIT); + csr::timer0::en_write(1); } } pub fn get_ms() -> u64 { unsafe { - timer0::update_value_write(1); - (INIT - timer0::value_read()) / (FREQ / 1_000) + csr::timer0::update_value_write(1); + (INIT - csr::timer0::value_read()) / (FREQ / 1_000) } } pub fn spin_us(interval: u64) { unsafe { - timer0::update_value_write(1); - let threshold = timer0::value_read() - interval * (FREQ / 1_000_000); - while timer0::value_read() > threshold { - timer0::update_value_write(1) + csr::timer0::update_value_write(1); + let threshold = csr::timer0::value_read() - interval * (FREQ / 1_000_000); + while csr::timer0::value_read() > threshold { + csr::timer0::update_value_write(1) } } } diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index 06f9c4822..4967ec597 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -1,5 +1,5 @@ use core::ptr; -use board::csr::kernel_cpu; +use board::csr; use mailbox; const KERNELCPU_EXEC_ADDRESS: usize = 0x42000000; @@ -8,7 +8,7 @@ const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024); const KSUPPORT_HEADER_SIZE: usize = 0x80; pub unsafe fn start() { - if kernel_cpu::reset_read() == 0 { + if csr::kernel_cpu::reset_read() == 0 { panic!("attempted to start kernel CPU when it is already running") } @@ -24,11 +24,11 @@ pub unsafe fn start() { (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8, ksupport_end - ksupport_start); - kernel_cpu::reset_write(0); + csr::kernel_cpu::reset_write(0); } pub fn stop() { - unsafe { kernel_cpu::reset_write(1) } + unsafe { csr::kernel_cpu::reset_write(1) } mailbox::acknowledge(); } From 5701b2095be89adf1900179ad60099981d72f4c3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 16:07:45 +0000 Subject: [PATCH 019/127] Rust: implement all messages used in the kernel interface. --- artiq/runtime.rs/src/kernel_proto.rs | 114 ++++++++++++++++++++++++++- artiq/runtime.rs/src/session.rs | 5 +- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index 9a16e1c1a..e1e4c4dc1 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -1,3 +1,5 @@ +use core::{ptr, mem, slice}; +use std::string::String; use std::io; use mailbox; use kernel; @@ -76,6 +78,60 @@ impl<'a> Message<'a> { f(&msg as *const _ as *const _) } + Message::WatchdogSetReply { id } => { + let msg = c::WatchdogSetReply { + ty: c::Type::WatchdogSetReply, + id: id as _ + }; + f(&msg as *const _ as *const _) + } + + Message::RpcRecvReply { alloc_size, exception } => { + let exn = exception.map(|exception| { + // FIXME: disgusting + let name = String::from(exception.name) + "\0"; + let file = String::from(exception.file) + "\0"; + let function = String::from(exception.function) + "\0"; + let message = String::from(exception.message) + "\0"; + let exn = c::Exception { + name: name.as_ptr() as *const _, + file: file.as_ptr() as *const _, + line: exception.line, + column: exception.column, + function: function.as_ptr() as *const _, + message: message.as_ptr() as *const _, + param: exception.param, + }; + mem::forget(name); + mem::forget(file); + mem::forget(function); + mem::forget(message); + exn + }); + let msg = c::RpcRecvReply { + ty: c::Type::RpcRecvReply, + alloc_size: alloc_size as _, + exception: exn.map_or(ptr::null(), |exn| &exn as *const _) + }; + f(&msg as *const _ as *const _) + } + + Message::CacheGetReply { value } => { + let msg = c::CacheGetReply { + ty: c::Type::CacheGetReply, + length: value.len(), + elements: value.as_ptr() + }; + f(&msg as *const _ as *const _) + } + Message::CachePutReply { succeeded } => { + let msg = c::CachePutReply { + ty: c::Type::CachePutReply, + succeeded: succeeded as _ + }; + f(&msg as *const _ as *const _) + } + other => panic!("Message::into_lower: {:?} unimplemented", other) } } @@ -100,6 +156,57 @@ impl<'a> Message<'a> { } c::Type::RunFinished => Message::RunFinished, + c::Type::RunException => { + let msg = ptr as *const c::RunException; + let exc = (*msg).exception; + Message::RunException { + exception: Exception { + name: c::from_c_str((*exc).name), + file: c::from_c_str((*exc).file), + line: (*exc).line, + column: (*exc).column, + function: c::from_c_str((*exc).function), + message: c::from_c_str((*exc).message), + param: (*exc).param, + }, + backtrace: slice::from_raw_parts((*msg).backtrace, (*msg).backtrace_size) + } + } + + c::Type::WatchdogSetRequest => { + let msg = ptr as *const c::WatchdogSetRequest; + Message::WatchdogSetRequest { ms: (*msg).ms as u64 } + }, + c::Type::WatchdogClear => { + let msg = ptr as *const c::WatchdogClear; + Message::WatchdogClear { id: (*msg).id as usize } + } + + c::Type::RpcSend => { + let msg = ptr as *const c::RpcSend; + Message::RpcSend { + service: (*msg).service as _, + tag: slice::from_raw_parts((*msg).tag as *const _, + c::strlen((*msg).tag) as usize), + data: (*msg).data as *const _ + } + } + c::Type::RpcRecvRequest => { + let msg = ptr as *const c::RpcRecvRequest; + Message::RpcRecvRequest { slot: (*msg).slot as *mut _ } + } + + c::Type::CacheGetRequest => { + let msg = ptr as *const c::CacheGetRequest; + let key = c::from_c_str((*msg).key); + Message::CacheGetRequest { key: key } + } + c::Type::CachePutRequest => { + let msg = ptr as *const c::CachePutRequest; + let key = c::from_c_str((*msg).key); + let value = slice::from_raw_parts((*msg).elements, (*msg).length); + Message::CachePutRequest { key: key, value: value } + } c::Type::Log => { let msg = ptr as *const c::Log; @@ -129,13 +236,16 @@ impl<'a> Message<'a> { } pub fn acknowledge() { - unsafe { mailbox::acknowledge() } + mailbox::acknowledge() } } // Low-level representation, compatible with the C code in ksupport mod c { use libc::{c_void, c_int, c_char, size_t}; + use core::{str, slice}; + + extern { pub fn strlen(ptr: *const c_char) -> size_t; } #[repr(u32)] #[derive(Debug)] @@ -312,12 +422,10 @@ mod c { } pub unsafe fn from_c_str_len<'a>(ptr: *const c_char, len: size_t) -> &'a str { - use core::{str, slice}; str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, len)) } pub unsafe fn from_c_str<'a>(ptr: *const c_char) -> &'a str { - extern { fn strlen(cs: *const c_char) -> size_t; } from_c_str_len(ptr, strlen(ptr)) } } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 03def4132..65c250279 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -186,10 +186,11 @@ fn comm_handle(waiter: Waiter, } session.kernel_state = KernelState::Running; + // TODO: make this a separate request kern_acknowledge() } - request => unexpected!("unexpected {:?}", request) + request => unexpected!("unexpected request {:?} from host machine", request) } } @@ -225,7 +226,7 @@ fn kern_handle(waiter: Waiter, kern_acknowledge() } - request => unexpected!("unexpected {:?}", request) + request => unexpected!("unexpected request {:?} from kernel CPU", request) } }) } From d825393e81c0829868ee0524ed874c9a9a98e450 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 16:26:57 +0000 Subject: [PATCH 020/127] Rust: implement watchdogs. --- artiq/runtime.rs/src/clock.rs | 1 - artiq/runtime.rs/src/session.rs | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/artiq/runtime.rs/src/clock.rs b/artiq/runtime.rs/src/clock.rs index b9a0584b3..3c203a196 100644 --- a/artiq/runtime.rs/src/clock.rs +++ b/artiq/runtime.rs/src/clock.rs @@ -58,7 +58,6 @@ impl WatchdogSet { } } - warn!("cannot add watchdog; all {} watchdogs used", MAX_WATCHDOGS); Err(()) } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 65c250279..c9ef4383f 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -17,6 +17,10 @@ macro_rules! unexpected { }; } +fn io_error(msg: &str) -> io::Error { + io::Error::new(io::ErrorKind::Other, msg) +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum KernelState { Absent, @@ -49,6 +53,12 @@ impl Session { } } +impl Drop for Session { + fn drop(&mut self) { + kernel::stop() + } +} + fn check_magic(stream: &mut TcpStream) -> io::Result<()> { const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; @@ -226,6 +236,17 @@ fn kern_handle(waiter: Waiter, kern_acknowledge() } + kern::WatchdogSetRequest { ms } => { + let id = try!(session.watchdog_set.set_ms(ms) + .map_err(|()| io_error("out of watchdogs"))); + kern_send(waiter, kern::WatchdogSetReply { id: id }) + } + + kern::WatchdogClear { id } => { + session.watchdog_set.clear(id); + kern_acknowledge() + } + request => unexpected!("unexpected request {:?} from kernel CPU", request) } }) @@ -249,12 +270,12 @@ fn handle(waiter: Waiter, if session.kernel_state == KernelState::Running { if session.watchdog_set.expired() { try!(host_write(stream, host::Reply::WatchdogExpired)); - return Err(io::Error::new(io::ErrorKind::Other, "watchdog expired")) + return Err(io_error("watchdog expired")) } if !rtio_crg::check() { try!(host_write(stream, host::Reply::ClockFailure)); - return Err(io::Error::new(io::ErrorKind::Other, "RTIO clock failure")) + return Err(io_error("RTIO clock failure")) } } From 8bced9dcb5b5b08f93e5591d477ccf77974fab45 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 1 Oct 2016 18:24:53 +0000 Subject: [PATCH 021/127] Rust: implement cache. --- artiq/runtime.rs/src/cache.rs | 47 ++++++++++++++++++++ artiq/runtime.rs/src/kernel_proto.rs | 1 + artiq/runtime.rs/src/lib.rs | 1 + artiq/runtime.rs/src/sched.rs | 2 + artiq/runtime.rs/src/session.rs | 66 +++++++++++++++++++++------- 5 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 artiq/runtime.rs/src/cache.rs diff --git a/artiq/runtime.rs/src/cache.rs b/artiq/runtime.rs/src/cache.rs new file mode 100644 index 000000000..cc755bab6 --- /dev/null +++ b/artiq/runtime.rs/src/cache.rs @@ -0,0 +1,47 @@ +use std::vec::Vec; +use std::string::String; +use std::btree_map::BTreeMap; + +#[derive(Debug)] +struct Entry { + data: Vec, + borrowed: bool +} + +#[derive(Debug)] +pub struct Cache { + entries: BTreeMap +} + +impl Cache { + pub fn new() -> Cache { + Cache { entries: BTreeMap::new() } + } + + pub fn get(&mut self, key: &str) -> *const [u32] { + match self.entries.get_mut(key) { + None => &[], + Some(ref mut entry) => { + entry.borrowed = true; + &entry.data[..] + } + } + } + + pub fn put(&mut self, key: &str, data: &[u32]) -> Result<(), ()> { + match self.entries.get_mut(key) { + None => (), + Some(ref mut entry) => { + if entry.borrowed { return Err(()) } + entry.data = Vec::from(data); + return Ok(()) + } + } + + self.entries.insert(String::from(key), Entry { + data: Vec::from(data), + borrowed: false + }); + Ok(()) + } +} diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index e1e4c4dc1..e7c76a508 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -249,6 +249,7 @@ mod c { #[repr(u32)] #[derive(Debug)] + #[allow(dead_code)] pub enum Type { LoadRequest, LoadReply, diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 5f3e382ca..8070552fc 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -19,6 +19,7 @@ mod rtio_crg; mod mailbox; mod logger; +mod cache; mod kernel_proto; mod session_proto; diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 3d1dcc8c4..1e7b58a5a 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + extern crate fringe; extern crate lwip; diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index c9ef4383f..56d7d81f6 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -1,8 +1,10 @@ use std::prelude::v1::*; +use std::mem; use std::str; use std::io::{self, Read}; use {config, rtio_crg, clock, mailbox, kernel}; use logger::BufferLogger; +use cache::Cache; use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY}; use session_proto as host; @@ -21,6 +23,22 @@ fn io_error(msg: &str) -> io::Error { io::Error::new(io::ErrorKind::Other, msg) } +// Persistent state +#[derive(Debug)] +struct Congress { + now: u64, + cache: Cache +} + +impl Congress { + fn new() -> Congress { + Congress { + now: 0, + cache: Cache::new() + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum KernelState { Absent, @@ -29,23 +47,22 @@ enum KernelState { RpcWait } +// Per-connection state #[derive(Debug)] -pub struct Session { +struct Session { kernel_state: KernelState, - watchdog_set: clock::WatchdogSet, - now: u64 + watchdog_set: clock::WatchdogSet } impl Session { - pub fn new() -> Session { + fn new() -> Session { Session { kernel_state: KernelState::Absent, - watchdog_set: clock::WatchdogSet::new(), - now: 0 + watchdog_set: clock::WatchdogSet::new() } } - pub fn running(&self) -> bool { + fn running(&self) -> bool { match self.kernel_state { KernelState::Absent | KernelState::Loaded => false, KernelState::Running | KernelState::RpcWait => true @@ -106,9 +123,9 @@ fn kern_acknowledge() -> io::Result<()> { Ok(()) } -fn comm_handle(waiter: Waiter, +fn comm_handle(logger: &BufferLogger, + waiter: Waiter, stream: &mut TcpStream, - logger: &BufferLogger, session: &mut Session) -> io::Result<()> { match try!(host_read(stream)) { host::Request::Ident => @@ -205,7 +222,7 @@ fn comm_handle(waiter: Waiter, } fn kern_handle(waiter: Waiter, - stream: &mut TcpStream, + congress: &mut Congress, session: &mut Session) -> io::Result<()> { kern::Message::wait_and_receive(waiter, |request| { match (&request, session.kernel_state) { @@ -229,10 +246,10 @@ fn kern_handle(waiter: Waiter, } kern::NowInitRequest => - kern_send(waiter, kern::NowInitReply(session.now)), + kern_send(waiter, kern::NowInitReply(congress.now)), kern::NowSave(now) => { - session.now = now; + congress.now = now; kern_acknowledge() } @@ -247,24 +264,37 @@ fn kern_handle(waiter: Waiter, kern_acknowledge() } + kern::CacheGetRequest { key } => { + let value = congress.cache.get(key); + kern_send(waiter, kern::CacheGetReply { + value: unsafe { mem::transmute::<*const [u32], &'static [u32]>(value) } + }) + } + + kern::CachePutRequest { key, value } => { + let succeeded = congress.cache.put(key, value).is_ok(); + kern_send(waiter, kern::CachePutReply { succeeded: succeeded }) + } + request => unexpected!("unexpected request {:?} from kernel CPU", request) } }) } -fn handle(waiter: Waiter, +fn handle(logger: &BufferLogger, + waiter: Waiter, stream: &mut TcpStream, - logger: &BufferLogger) -> io::Result<()> { + congress: &mut Congress) -> io::Result<()> { try!(check_magic(stream)); let mut session = Session::new(); loop { if stream.readable() { - try!(comm_handle(waiter, stream, logger, &mut session)) + try!(comm_handle(logger, waiter, stream, &mut session)) } if mailbox::receive() != 0 { - try!(kern_handle(waiter, stream, &mut session)) + try!(kern_handle(waiter, congress, &mut session)) } if session.kernel_state == KernelState::Running { @@ -285,6 +315,8 @@ fn handle(waiter: Waiter, pub fn handler(waiter: Waiter, logger: &BufferLogger) { + let mut congress = Congress::new(); + let addr = SocketAddr::new(IP_ANY, 1381); let listener = TcpListener::bind(waiter, addr).unwrap(); info!("accepting network sessions in Rust"); @@ -293,7 +325,7 @@ pub fn handler(waiter: Waiter, let (mut stream, addr) = listener.accept().unwrap(); info!("new connection from {:?}", addr); - match handle(waiter, &mut stream, logger) { + match handle(logger, waiter, &mut stream, &mut congress) { Ok(()) => (), Err(err) => { if err.kind() == io::ErrorKind::UnexpectedEof { From 30e997f0450951bd198280fae60f451e7544c4f6 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 2 Oct 2016 04:37:24 +0000 Subject: [PATCH 022/127] Rust: implement idle kernels and session takeover. --- artiq/runtime.rs/Cargo.lock | 6 +- artiq/runtime.rs/Cargo.toml | 2 +- artiq/runtime.rs/liblwip/Cargo.toml | 4 + artiq/runtime.rs/liblwip/lib.rs | 12 +++ artiq/runtime.rs/src/kernel.rs | 12 +-- artiq/runtime.rs/src/lib.rs | 19 ++-- artiq/runtime.rs/src/logger.rs | 16 ++- artiq/runtime.rs/src/sched.rs | 160 +++++++++++++++++++++++----- artiq/runtime.rs/src/session.rs | 155 ++++++++++++++++++--------- artiq/runtime.rs/src/urc.rs | 31 ++++++ 10 files changed, 316 insertions(+), 101 deletions(-) create mode 100644 artiq/runtime.rs/src/urc.rs diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index 1f5913d15..eb4659fcb 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -5,7 +5,7 @@ dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lwip 0.0.0", "std_artiq 0.0.0", "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -49,7 +49,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "log_buffer" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -96,7 +96,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" -"checksum log_buffer 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8beb5ba24eca52f9958874445c4de5e086a7e82a1ec6b7ab81e5fcfb134f25a" +"checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493" "checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index aba3d1aae..725da7533 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -14,7 +14,7 @@ path = "src/lib.rs" [dependencies] std_artiq = { path = "libstd_artiq" } -lwip = { path = "liblwip" } +lwip = { path = "liblwip", default-features = false } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } log = { version = "0.3", default-features = false } log_buffer = { version = "1.0" } diff --git a/artiq/runtime.rs/liblwip/Cargo.toml b/artiq/runtime.rs/liblwip/Cargo.toml index cd76fe969..886f69415 100644 --- a/artiq/runtime.rs/liblwip/Cargo.toml +++ b/artiq/runtime.rs/liblwip/Cargo.toml @@ -10,3 +10,7 @@ path = "lib.rs" [dependencies] lwip-sys = { path = "../liblwip-sys" } std_artiq = { path = "../libstd_artiq" } + +[features] +default = ["preemption"] +preemption = [] diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index f8938653b..c996074f0 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -175,6 +175,9 @@ pub struct Pbuf<'payload> { phantom: PhantomData<&'payload [u8]> } +#[cfg(not(feature = "preemption"))] +unsafe impl<'payload> Send for Pbuf<'payload> {} + impl<'payload> Pbuf<'payload> { unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> { Pbuf { raw: raw, phantom: PhantomData } @@ -259,6 +262,9 @@ pub struct UdpSocket { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for UdpSocket {} + impl UdpSocket { pub fn new() -> Result { extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb, @@ -347,6 +353,9 @@ pub struct TcpListener { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for TcpListener {} + impl TcpListener { pub fn bind(addr: SocketAddr) -> Result { extern fn accept(arg: *mut c_void, newpcb: *mut lwip_sys::tcp_pcb, @@ -428,6 +437,9 @@ pub struct TcpStream { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for TcpStream {} + impl TcpStream { fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream { extern fn recv(arg: *mut c_void, _raw: *mut lwip_sys::tcp_pcb, diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index 4967ec597..050b57ec4 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -15,14 +15,14 @@ pub unsafe fn start() { stop(); extern { - static _binary_ksupport_elf_start: (); - static _binary_ksupport_elf_end: (); + static _binary_ksupport_elf_start: u8; + static _binary_ksupport_elf_end: u8; } - let ksupport_start = &_binary_ksupport_elf_start as *const _ as usize; - let ksupport_end = &_binary_ksupport_elf_end as *const _ as usize; - ptr::copy_nonoverlapping(ksupport_start as *const u8, + let ksupport_start = &_binary_ksupport_elf_start as *const _; + let ksupport_end = &_binary_ksupport_elf_end as *const _; + ptr::copy_nonoverlapping(ksupport_start, (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8, - ksupport_end - ksupport_start); + ksupport_end as usize - ksupport_start as usize); csr::kernel_cpu::reset_write(0); } diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 8070552fc..9b913c9dd 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc)] +#![feature(libc, borrow_state, const_fn)] #[macro_use] extern crate std_artiq as std; @@ -8,16 +8,19 @@ extern crate libc; extern crate log; extern crate log_buffer; extern crate byteorder; +extern crate fringe; +extern crate lwip; use logger::BufferLogger; mod board; -mod sched; mod config; mod clock; mod rtio_crg; mod mailbox; +mod urc; +mod sched; mod logger; mod cache; @@ -36,9 +39,9 @@ include!(concat!(env!("OUT_DIR"), "/git_info.rs")); #[no_mangle] pub unsafe extern fn rust_main() { - static mut log_buffer: [u8; 4096] = [0; 4096]; - BufferLogger::new(&mut log_buffer[..]) - .register(move |logger| { + static mut LOG_BUFFER: [u8; 4096] = [0; 4096]; + BufferLogger::new(&mut LOG_BUFFER[..]) + .register(move || { info!("booting ARTIQ runtime ({})", GIT_COMMIT); clock::init(); @@ -46,13 +49,11 @@ pub unsafe extern fn rust_main() { network_init(); let mut scheduler = sched::Scheduler::new(); - scheduler.spawn(8192, move |waiter| { - session::handler(waiter, logger) - }); + scheduler.spawner().spawn(8192, session::handler); loop { + scheduler.run(); lwip_service(); - scheduler.run() } }) } diff --git a/artiq/runtime.rs/src/logger.rs b/artiq/runtime.rs/src/logger.rs index 377470a2e..d09995bf7 100644 --- a/artiq/runtime.rs/src/logger.rs +++ b/artiq/runtime.rs/src/logger.rs @@ -1,3 +1,4 @@ +use core::{mem, ptr}; use core::cell::RefCell; use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; use log_buffer::LogBuffer; @@ -6,9 +7,10 @@ pub struct BufferLogger { buffer: RefCell> } -// We can never preempt from within the logger, so there can be no data races. unsafe impl Sync for BufferLogger {} +static mut LOGGER: *const BufferLogger = ptr::null(); + impl BufferLogger { pub fn new(buffer: &'static mut [u8]) -> BufferLogger { BufferLogger { @@ -16,7 +18,7 @@ impl BufferLogger { } } - pub fn register(&self, f: F) { + pub fn register(&self, f: F) { // log::set_logger_raw captures a pointer to ourselves, so we must prevent // ourselves from being moved or dropped after that function is called (and // before log::shutdown_logger_raw is called). @@ -25,9 +27,17 @@ impl BufferLogger { max_log_level.set(LogLevelFilter::Trace); self as *const Log }).expect("global logger can only be initialized once"); + LOGGER = self; } - f(self); + f(); log::shutdown_logger_raw().unwrap(); + unsafe { + LOGGER = ptr::null(); + } + } + + pub fn with_instance R>(f: F) -> R { + f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) }) } pub fn clear(&self) { diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 1e7b58a5a..124af9410 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -1,14 +1,14 @@ #![allow(dead_code)] -extern crate fringe; -extern crate lwip; - -use std::cell::RefCell; +use std::cell::{RefCell, BorrowState}; use std::vec::Vec; +use std::rc::Rc; use std::time::{Instant, Duration}; use std::io::{Read, Write, Result, Error, ErrorKind}; -use self::fringe::OwnedStack; -use self::fringe::generator::{Generator, Yielder}; +use fringe::OwnedStack; +use fringe::generator::{Generator, Yielder, State as GeneratorState}; +use lwip; +use urc::Urc; #[derive(Debug)] struct WaitRequest { @@ -25,38 +25,87 @@ enum WaitResult { #[derive(Debug)] struct Thread { - generator: Generator, + generator: Generator, waiting_for: WaitRequest, interrupted: bool } -#[derive(Debug)] -pub struct Scheduler { - threads: Vec, - index: usize -} - -impl Scheduler { - pub fn new() -> Scheduler { - Scheduler { threads: Vec::new(), index: 0 } - } - - pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { +impl Thread { + unsafe fn new(spawner: Spawner, stack_size: usize, f: F) -> ThreadHandle + where F: 'static + FnOnce(Waiter, Spawner) + Send { let stack = OwnedStack::new(stack_size); - let thread = Thread { - generator: Generator::unsafe_new(stack, move |yielder, _| { - f(Waiter(yielder)) + ThreadHandle::new(Thread { + generator: Generator::unsafe_new(stack, |yielder, _| { + f(Waiter(yielder), spawner) }), waiting_for: WaitRequest { timeout: None, event: None }, interrupted: false - }; - self.threads.push(thread) + }) + } + + pub fn terminated(&self) -> bool { + // FIXME: https://github.com/nathan7/libfringe/pull/56 + match self.generator.state() { + GeneratorState::Unavailable => true, + GeneratorState::Runnable => false + } + } + + pub fn interrupt(&mut self) { + self.interrupted = true + } +} + +#[derive(Debug, Clone)] +pub struct ThreadHandle(Urc>); + +impl ThreadHandle { + fn new(thread: Thread) -> ThreadHandle { + ThreadHandle(Urc::new(RefCell::new(thread))) + } + + pub fn terminated(&self) -> bool { + match self.0.borrow_state() { + BorrowState::Unused => self.0.borrow().terminated(), + _ => false // the running thread hasn't terminated + } + } + + pub fn interrupt(&self) { + // FIXME: use try_borrow() instead once it's available + match self.0.borrow_state() { + BorrowState::Unused => self.0.borrow_mut().interrupt(), + _ => panic!("cannot interrupt the running thread") + } + } +} + +#[derive(Debug)] +pub struct Scheduler { + threads: Vec, + index: usize, + spawner: Spawner +} + +impl Scheduler { + pub fn new() -> Scheduler { + Scheduler { + threads: Vec::new(), + index: 0, + spawner: Spawner::new() + } + } + + pub fn spawner(&self) -> &Spawner { + &self.spawner } pub fn run(&mut self) { + self.threads.append(&mut *self.spawner.queue.borrow_mut()); + if self.threads.len() == 0 { return } let now = Instant::now(); @@ -66,7 +115,7 @@ impl Scheduler { self.index = (self.index + 1) % self.threads.len(); let result = { - let thread = &mut self.threads[self.index]; + let thread = &mut *self.threads[self.index].0.borrow_mut(); match thread.waiting_for { _ if thread.interrupted => { thread.interrupted = false; @@ -97,7 +146,8 @@ impl Scheduler { }, Some(wait_request) => { // The thread has suspended itself. - self.threads[self.index].waiting_for = wait_request + let thread = &mut *self.threads[self.index].0.borrow_mut(); + thread.waiting_for = wait_request } } @@ -106,8 +156,27 @@ impl Scheduler { } } +#[derive(Debug, Clone)] +pub struct Spawner { + queue: Urc>> +} + +impl Spawner { + fn new() -> Spawner { + Spawner { queue: Urc::new(RefCell::new(Vec::new())) } + } + + pub fn spawn(&self, stack_size: usize, f: F) -> ThreadHandle + where F: 'static + FnOnce(Waiter, Spawner) + Send { + let handle = unsafe { Thread::new(self.clone(), stack_size, f) }; + self.queue.borrow_mut().push(handle.clone()); + handle + } +} + enum WaitEvent { Completion(*const (Fn() -> bool + 'static)), + Termination(*const RefCell), UdpReadable(*const RefCell), TcpAcceptable(*const RefCell), TcpWriteable(*const RefCell), @@ -119,6 +188,8 @@ impl WaitEvent { match *self { WaitEvent::Completion(f) => unsafe { (*f)() }, + WaitEvent::Termination(thread) => + unsafe { (*thread).borrow().terminated() }, WaitEvent::UdpReadable(state) => unsafe { (*state).borrow().readable() }, WaitEvent::TcpAcceptable(state) => @@ -173,6 +244,13 @@ impl<'a> Waiter<'a> { } } + pub fn join(&self, thread: ThreadHandle) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: Some(WaitEvent::Termination(&*thread.0)) + }) + } + pub fn until bool + 'static>(&self, f: F) -> Result<()> { self.suspend(WaitRequest { timeout: None, @@ -211,7 +289,7 @@ impl<'a> Waiter<'a> { // Wrappers around lwip -pub use self::lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr}; +pub use lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr}; #[derive(Debug)] pub struct UdpSocket<'a> { @@ -227,6 +305,14 @@ impl<'a> UdpSocket<'a> { }) } + pub fn into_lower(self) -> lwip::UdpSocket { + self.lower + } + + pub fn from_lower(waiter: Waiter<'a>, inner: lwip::UdpSocket) -> UdpSocket { + UdpSocket { waiter: waiter, lower: inner } + } + pub fn bind(&self, addr: SocketAddr) -> Result<()> { Ok(try!(self.lower.bind(addr))) } @@ -285,6 +371,14 @@ impl<'a> TcpListener<'a> { }) } + pub fn into_lower(self) -> lwip::TcpListener { + self.lower + } + + pub fn from_lower(waiter: Waiter<'a>, inner: lwip::TcpListener) -> TcpListener { + TcpListener { waiter: waiter, lower: inner } + } + pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> { try!(self.waiter.tcp_acceptable(&self.lower)); let stream_lower = self.lower.try_accept().unwrap(); @@ -301,7 +395,9 @@ impl<'a> TcpListener<'a> { } } -pub use self::lwip::Shutdown; +pub use lwip::Shutdown; + +pub struct TcpStreamInner(lwip::TcpStream, Option<(lwip::Pbuf<'static>, usize)>); #[derive(Debug)] pub struct TcpStream<'a> { @@ -311,6 +407,14 @@ pub struct TcpStream<'a> { } impl<'a> TcpStream<'a> { + pub fn into_lower(self) -> TcpStreamInner { + TcpStreamInner(self.lower, self.buffer) + } + + pub fn from_lower(waiter: Waiter<'a>, inner: TcpStreamInner) -> TcpStream { + TcpStream { waiter: waiter, lower: inner.0, buffer: inner.1 } + } + pub fn shutdown(&self, how: Shutdown) -> Result<()> { Ok(try!(self.lower.shutdown(how))) } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 56d7d81f6..3b280498c 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -1,11 +1,13 @@ use std::prelude::v1::*; -use std::mem; -use std::str; +use std::{mem, str}; +use std::cell::RefCell; use std::io::{self, Read}; use {config, rtio_crg, clock, mailbox, kernel}; use logger::BufferLogger; use cache::Cache; -use sched::{Waiter, TcpListener, TcpStream, SocketAddr, IP_ANY}; +use urc::Urc; +use sched::{ThreadHandle, Waiter, Spawner}; +use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; use session_proto as host; use kernel_proto as kern; @@ -49,14 +51,16 @@ enum KernelState { // Per-connection state #[derive(Debug)] -struct Session { +struct Session<'a> { + congress: &'a mut Congress, kernel_state: KernelState, watchdog_set: clock::WatchdogSet } -impl Session { - fn new() -> Session { +impl<'a> Session<'a> { + fn new(congress: &mut Congress) -> Session { Session { + congress: congress, kernel_state: KernelState::Absent, watchdog_set: clock::WatchdogSet::new() } @@ -70,7 +74,7 @@ impl Session { } } -impl Drop for Session { +impl<'a> Drop for Session<'a> { fn drop(&mut self) { kernel::stop() } @@ -123,10 +127,9 @@ fn kern_acknowledge() -> io::Result<()> { Ok(()) } -fn comm_handle(logger: &BufferLogger, - waiter: Waiter, - stream: &mut TcpStream, - session: &mut Session) -> io::Result<()> { +fn process_host_message(waiter: Waiter, + stream: &mut TcpStream, + session: &mut Session) -> io::Result<()> { match try!(host_read(stream)) { host::Request::Ident => host_write(stream, host::Reply::Ident(::board::ident(&mut [0; 64]))), @@ -135,13 +138,15 @@ fn comm_handle(logger: &BufferLogger, host::Request::Log => { // Logging the packet with the log is inadvisable trace!("comm->host Log(...)"); - logger.extract(move |log| { - host::Reply::Log(log).write_to(stream) + BufferLogger::with_instance(|logger| { + logger.extract(|log| { + host::Reply::Log(log).write_to(stream) + }) }) } host::Request::LogClear => { - logger.clear(); + BufferLogger::with_instance(|logger| logger.clear()); host_write(stream, host::Reply::Log("")) } @@ -221,9 +226,8 @@ fn comm_handle(logger: &BufferLogger, } } -fn kern_handle(waiter: Waiter, - congress: &mut Congress, - session: &mut Session) -> io::Result<()> { +fn process_kern_message(waiter: Waiter, + session: &mut Session) -> io::Result<()> { kern::Message::wait_and_receive(waiter, |request| { match (&request, session.kernel_state) { (&kern::LoadReply { .. }, KernelState::Loaded) | @@ -246,10 +250,10 @@ fn kern_handle(waiter: Waiter, } kern::NowInitRequest => - kern_send(waiter, kern::NowInitReply(congress.now)), + kern_send(waiter, kern::NowInitReply(session.congress.now)), kern::NowSave(now) => { - congress.now = now; + session.congress.now = now; kern_acknowledge() } @@ -265,14 +269,14 @@ fn kern_handle(waiter: Waiter, } kern::CacheGetRequest { key } => { - let value = congress.cache.get(key); + let value = session.congress.cache.get(key); kern_send(waiter, kern::CacheGetReply { value: unsafe { mem::transmute::<*const [u32], &'static [u32]>(value) } }) } kern::CachePutRequest { key, value } => { - let succeeded = congress.cache.put(key, value).is_ok(); + let succeeded = session.congress.cache.put(key, value).is_ok(); kern_send(waiter, kern::CachePutReply { succeeded: succeeded }) } @@ -281,20 +285,17 @@ fn kern_handle(waiter: Waiter, }) } -fn handle(logger: &BufferLogger, - waiter: Waiter, - stream: &mut TcpStream, - congress: &mut Congress) -> io::Result<()> { - try!(check_magic(stream)); - - let mut session = Session::new(); +fn host_kernel_worker(waiter: Waiter, + stream: &mut TcpStream, + congress: &mut Congress) -> io::Result<()> { + let mut session = Session::new(congress); loop { if stream.readable() { - try!(comm_handle(logger, waiter, stream, &mut session)) + try!(process_host_message(waiter, stream, &mut session)); } if mailbox::receive() != 0 { - try!(kern_handle(waiter, congress, &mut session)) + try!(process_kern_message(waiter, &mut session)) } if session.kernel_state == KernelState::Running { @@ -313,27 +314,79 @@ fn handle(logger: &BufferLogger, } } -pub fn handler(waiter: Waiter, - logger: &BufferLogger) { - let mut congress = Congress::new(); - - let addr = SocketAddr::new(IP_ANY, 1381); - let listener = TcpListener::bind(waiter, addr).unwrap(); - info!("accepting network sessions in Rust"); - +fn flash_kernel_worker(waiter: Waiter, + congress: &mut Congress) -> io::Result<()> { + let mut session = Session::new(congress); loop { - let (mut stream, addr) = listener.accept().unwrap(); - info!("new connection from {:?}", addr); - - match handle(logger, waiter, &mut stream, &mut congress) { - Ok(()) => (), - Err(err) => { - if err.kind() == io::ErrorKind::UnexpectedEof { - info!("connection closed"); - } else { - error!("session aborted: {:?}", err); - } - } - } + try!(process_kern_message(waiter, &mut session)) + } +} + +fn respawn(spawner: Spawner, waiter: Waiter, + handle: &mut Option, + f: F) where F: 'static + FnOnce(Waiter, Spawner) + Send { + match handle.take() { + None => (), + Some(handle) => { + info!("terminating running kernel"); + handle.interrupt(); + waiter.join(handle).expect("cannot join interrupt thread") + } + } + + *handle = Some(spawner.spawn(8192, f)) +} + +pub fn handler(waiter: Waiter, spawner: Spawner) { + let congress = Urc::new(RefCell::new(Congress::new())); + + let addr = SocketAddr::new(IP_ANY, 1381); + let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket"); + info!("accepting network sessions in Rust"); + + let mut kernel_thread = None; + loop { + if listener.acceptable() { + let (mut stream, addr) = listener.accept().expect("cannot accept client"); + match check_magic(&mut stream) { + Ok(()) => (), + Err(_) => continue + } + info!("new connection from {:?}", addr); + + let stream = stream.into_lower(); + let congress = congress.clone(); + respawn(spawner.clone(), waiter, &mut kernel_thread, move |waiter, _spawner| { + let mut stream = TcpStream::from_lower(waiter, stream); + let mut congress = congress.borrow_mut(); + match host_kernel_worker(waiter, &mut stream, &mut congress) { + Ok(()) => (), + Err(err) => { + if err.kind() == io::ErrorKind::UnexpectedEof { + info!("connection closed"); + } else { + error!("session aborted: {:?}", err); + } + } + } + }) + } + + if kernel_thread.is_none() { + info!("no connection, starting idle kernel"); + let congress = congress.clone(); + respawn(spawner.clone(), waiter, &mut kernel_thread, move |waiter, _spawner| { + let mut congress = congress.borrow_mut(); + match flash_kernel_worker(waiter, &mut congress) { + Ok(()) => + info!("idle kernel finished, standing by"), + Err(err) => { + error!("idle kernel aborted: {:?}", err); + } + } + }) + } + + waiter.relinquish() } } diff --git a/artiq/runtime.rs/src/urc.rs b/artiq/runtime.rs/src/urc.rs new file mode 100644 index 000000000..3ca67f9ec --- /dev/null +++ b/artiq/runtime.rs/src/urc.rs @@ -0,0 +1,31 @@ +use std::rc::Rc; +use std::ops::{Deref, DerefMut}; +use std::fmt; + +pub struct Urc(Rc); + +impl Urc { + pub fn new(value: T) -> Urc { Urc(Rc::new(value)) } +} + +unsafe impl Send for Urc {} + +unsafe impl Sync for Urc {} + +impl Deref for Urc { + type Target = T; + + fn deref(&self) -> &Self::Target { self.0.deref() } +} + +impl Clone for Urc { + fn clone(&self) -> Urc { + Urc(self.0.clone()) + } +} + +impl fmt::Debug for Urc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} From 398b709e25796709d740bd77720fd8d23a101ea8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 03:26:53 +0000 Subject: [PATCH 023/127] Rust: use try_borrow where applicable. --- artiq/runtime.rs/src/lib.rs | 2 +- artiq/runtime.rs/src/sched.rs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 9b913c9dd..4ddb0ba52 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc, borrow_state, const_fn)] +#![feature(libc, borrow_state, const_fn, try_borrow)] #[macro_use] extern crate std_artiq as std; diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 124af9410..a5ab646ce 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -68,17 +68,16 @@ impl ThreadHandle { } pub fn terminated(&self) -> bool { - match self.0.borrow_state() { - BorrowState::Unused => self.0.borrow().terminated(), - _ => false // the running thread hasn't terminated + match self.0.try_borrow() { + Ok(thread) => thread.terminated(), + Err(_) => false // the running thread hasn't terminated } } pub fn interrupt(&self) { - // FIXME: use try_borrow() instead once it's available - match self.0.borrow_state() { - BorrowState::Unused => self.0.borrow_mut().interrupt(), - _ => panic!("cannot interrupt the running thread") + match self.0.try_borrow_mut() { + Ok(mut thread) => thread.interrupt(), + Err(_) => panic!("cannot interrupt the running thread") } } } From 6bbaff81bfb1b4d76064503ebc33480f0d0acc45 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 05:20:56 +0000 Subject: [PATCH 024/127] Rust: implement idle kernels. --- artiq/runtime.rs/src/lib.rs | 2 +- artiq/runtime.rs/src/sched.rs | 3 +- artiq/runtime.rs/src/session.rs | 105 ++++++++++++++++---------- artiq/runtime.rs/src/session_proto.rs | 4 +- artiq/runtime.rs/src/urc.rs | 2 +- 5 files changed, 69 insertions(+), 47 deletions(-) diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 4ddb0ba52..40963f72a 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc, borrow_state, const_fn, try_borrow)] +#![feature(libc, const_fn, try_borrow)] #[macro_use] extern crate std_artiq as std; diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index a5ab646ce..87d8562ff 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -1,8 +1,7 @@ #![allow(dead_code)] -use std::cell::{RefCell, BorrowState}; +use std::cell::RefCell; use std::vec::Vec; -use std::rc::Rc; use std::time::{Instant, Duration}; use std::io::{Read, Write, Result, Error, ErrorKind}; use fringe::OwnedStack; diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 3b280498c..2188bd29b 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -95,7 +95,7 @@ fn check_magic(stream: &mut TcpStream) -> io::Result<()> { fn host_read(stream: &mut TcpStream) -> io::Result { let request = try!(host::Request::read_from(stream)); match &request { - &host::Request::LoadLibrary(_) => trace!("comm<-host LoadLibrary(...)"), + &host::Request::LoadKernel(_) => trace!("comm<-host LoadLibrary(...)"), _ => trace!("comm<-host {:?}", request) } Ok(request) @@ -127,6 +127,38 @@ fn kern_acknowledge() -> io::Result<()> { Ok(()) } +unsafe fn kern_load(waiter: Waiter, session: &mut Session, library: &[u8]) -> io::Result<()> { + if session.running() { + unexpected!("attempted to load a new kernel while a kernel was running") + } + + kernel::start(); + + try!(kern_send(waiter, kern::LoadRequest(&library))); + kern_recv(waiter, |reply| { + match reply { + kern::LoadReply { error: None } => { + session.kernel_state = KernelState::Loaded; + Ok(()) + } + kern::LoadReply { error: Some(cause) } => + unexpected!("cannot load kernel: {}", cause), + other => + unexpected!("unexpected reply from kernel CPU: {:?}", other) + } + }) +} + +fn kern_run(session: &mut Session) -> io::Result<()> { + if session.kernel_state != KernelState::Loaded { + unexpected!("attempted to run a kernel while not in Loaded state") + } + + session.kernel_state = KernelState::Running; + // TODO: make this a separate request + kern_acknowledge() +} + fn process_host_message(waiter: Waiter, stream: &mut TcpStream, session: &mut Session) -> io::Result<()> { @@ -187,40 +219,17 @@ fn process_host_message(waiter: Waiter, } } - host::Request::LoadLibrary(library) => { - if session.running() { - error!("attempted to load a new kernel while a kernel was running"); - return host_write(stream, host::Reply::LoadFailed) - } + host::Request::LoadKernel(kernel) => + match unsafe { kern_load(waiter, session, &kernel) } { + Ok(()) => host_write(stream, host::Reply::LoadCompleted), + Err(_) => host_write(stream, host::Reply::LoadFailed) + }, - unsafe { kernel::start() } - - try!(kern_send(waiter, kern::LoadRequest(&library))); - kern_recv(waiter, |reply| { - match reply { - kern::LoadReply { error: None } => { - session.kernel_state = KernelState::Loaded; - host_write(stream, host::Reply::LoadCompleted) - } - kern::LoadReply { error: Some(cause) } => { - error!("cannot load kernel: {}", cause); - host_write(stream, host::Reply::LoadFailed) - } - other => unexpected!("unexpected reply from kernel CPU: {:?}", other) - } - }) - } - - host::Request::RunKernel => { - if session.kernel_state != KernelState::Loaded { - error!("attempted to run a kernel while not in Loaded state"); - return host_write(stream, host::Reply::KernelStartupFailed) - } - - session.kernel_state = KernelState::Running; - // TODO: make this a separate request - kern_acknowledge() - } + host::Request::RunKernel => + match kern_run(session) { + Ok(()) => Ok(()), + Err(_) => host_write(stream, host::Reply::KernelStartupFailed) + }, request => unexpected!("unexpected request {:?} from host machine", request) } @@ -289,6 +298,7 @@ fn host_kernel_worker(waiter: Waiter, stream: &mut TcpStream, congress: &mut Congress) -> io::Result<()> { let mut session = Session::new(congress); + loop { if stream.readable() { try!(process_host_message(waiter, stream, &mut session)); @@ -315,8 +325,14 @@ fn host_kernel_worker(waiter: Waiter, } fn flash_kernel_worker(waiter: Waiter, - congress: &mut Congress) -> io::Result<()> { + congress: &mut Congress, + config_key: &str) -> io::Result<()> { let mut session = Session::new(congress); + + let kernel = config::read_to_end(config_key); + try!(unsafe { kern_load(waiter, &mut session, &kernel) }); + try!(kern_run(&mut session)); + loop { try!(process_kern_message(waiter, &mut session)) } @@ -328,9 +344,11 @@ fn respawn(spawner: Spawner, waiter: Waiter, match handle.take() { None => (), Some(handle) => { - info!("terminating running kernel"); - handle.interrupt(); - waiter.join(handle).expect("cannot join interrupt thread") + if !handle.terminated() { + info!("terminating running kernel"); + handle.interrupt(); + waiter.join(handle).expect("cannot join interrupt thread") + } } } @@ -372,16 +390,21 @@ pub fn handler(waiter: Waiter, spawner: Spawner) { }) } - if kernel_thread.is_none() { + if kernel_thread.as_ref().map_or(true, |h| h.terminated()) { info!("no connection, starting idle kernel"); + let congress = congress.clone(); respawn(spawner.clone(), waiter, &mut kernel_thread, move |waiter, _spawner| { let mut congress = congress.borrow_mut(); - match flash_kernel_worker(waiter, &mut congress) { + match flash_kernel_worker(waiter, &mut congress, "idle_kernel") { Ok(()) => info!("idle kernel finished, standing by"), Err(err) => { - error!("idle kernel aborted: {:?}", err); + if err.kind() == io::ErrorKind::Interrupted { + info!("idle kernel interrupted"); + } else { + error!("idle kernel aborted: {:?}", err); + } } } }) diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index ba3a64ae2..b3b90d8b5 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -122,7 +122,7 @@ pub enum Request { Ident, SwitchClock(u8), - LoadLibrary(Vec), + LoadKernel(Vec), RunKernel, RpcReply { tag: String }, // FIXME @@ -150,7 +150,7 @@ impl Request { 5 => { let mut code = vec![0; length - HEADER_SIZE]; try!(reader.read_exact(&mut code)); - Request::LoadLibrary(code) + Request::LoadKernel(code) }, 6 => Request::RunKernel, 7 => Request::RpcReply { diff --git a/artiq/runtime.rs/src/urc.rs b/artiq/runtime.rs/src/urc.rs index 3ca67f9ec..cec7751ea 100644 --- a/artiq/runtime.rs/src/urc.rs +++ b/artiq/runtime.rs/src/urc.rs @@ -1,5 +1,5 @@ use std::rc::Rc; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::fmt; pub struct Urc(Rc); From 0cd87af386d11cbfb59c9e5a95566c7e809c96ce Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 05:27:54 +0000 Subject: [PATCH 025/127] Rust: don't crash kernel CPU when no flash kernel is present. --- artiq/runtime.rs/src/sched.rs | 14 +++++++------- artiq/runtime.rs/src/session.rs | 11 +++++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 87d8562ff..34dd5bc8c 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -214,13 +214,6 @@ unsafe impl Send for WaitEvent {} pub struct Waiter<'a>(&'a Yielder); impl<'a> Waiter<'a> { - pub fn relinquish(&self) { - self.0.suspend(WaitRequest { - timeout: None, - event: None - }); - } - pub fn sleep(&self, duration: Duration) -> Result<()> { let request = WaitRequest { timeout: Some(Instant::now() + duration), @@ -242,6 +235,13 @@ impl<'a> Waiter<'a> { } } + pub fn relinquish(&self) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: None + }) + } + pub fn join(&self, thread: ThreadHandle) -> Result<()> { self.suspend(WaitRequest { timeout: None, diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 2188bd29b..ad3f9ee94 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -320,7 +320,7 @@ fn host_kernel_worker(waiter: Waiter, } } - waiter.relinquish() + try!(waiter.relinquish()) } } @@ -330,6 +330,13 @@ fn flash_kernel_worker(waiter: Waiter, let mut session = Session::new(congress); let kernel = config::read_to_end(config_key); + if kernel.len() == 0 { + info!("no kernel present in config key {}", config_key); + loop { + try!(waiter.relinquish()) + } + } + try!(unsafe { kern_load(waiter, &mut session, &kernel) }); try!(kern_run(&mut session)); @@ -410,6 +417,6 @@ pub fn handler(waiter: Waiter, spawner: Spawner) { }) } - waiter.relinquish() + let _ = waiter.relinquish(); } } From 2b3bc30396be26dea5d9e718db0d8eba312bd5e8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 06:08:08 +0000 Subject: [PATCH 026/127] Rust: implement startup kernels. --- artiq/runtime.rs/src/lib.rs | 6 ++-- artiq/runtime.rs/src/session.rs | 60 +++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 40963f72a..5d73b8a17 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -42,14 +42,16 @@ pub unsafe extern fn rust_main() { static mut LOG_BUFFER: [u8; 4096] = [0; 4096]; BufferLogger::new(&mut LOG_BUFFER[..]) .register(move || { - info!("booting ARTIQ runtime ({})", GIT_COMMIT); + info!("booting ARTIQ..."); + info!("software version {}", GIT_COMMIT); + info!("gateware version {}", ::board::ident(&mut [0; 64])); clock::init(); rtio_crg::init(); network_init(); let mut scheduler = sched::Scheduler::new(); - scheduler.spawner().spawn(8192, session::handler); + scheduler.spawner().spawn(8192, session::thread); loop { scheduler.run(); diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index ad3f9ee94..e4fdf2924 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -208,8 +208,7 @@ fn process_host_message(waiter: Waiter, // artiq_run/artiq_master host::Request::SwitchClock(clk) => { if session.running() { - error!("attempted to switch RTIO clock while a kernel was running"); - return host_write(stream, host::Reply::ClockSwitchFailed) + unexpected!("attempted to switch RTIO clock while a kernel was running") } if rtio_crg::switch_clock(clk) { @@ -236,13 +235,13 @@ fn process_host_message(waiter: Waiter, } fn process_kern_message(waiter: Waiter, - session: &mut Session) -> io::Result<()> { + session: &mut Session) -> io::Result { kern::Message::wait_and_receive(waiter, |request| { match (&request, session.kernel_state) { (&kern::LoadReply { .. }, KernelState::Loaded) | (&kern::RpcRecvRequest { .. }, KernelState::RpcWait) => { // We're standing by; ignore the message. - return Ok(()) + return Ok(false) } (_, KernelState::Running) => (), _ => { @@ -289,8 +288,13 @@ fn process_kern_message(waiter: Waiter, kern_send(waiter, kern::CachePutReply { succeeded: succeeded }) } + kern::RunFinished => { + session.kernel_state = KernelState::Absent; + return Ok(true) + } + request => unexpected!("unexpected request {:?} from kernel CPU", request) - } + }.and(Ok(false)) }) } @@ -305,7 +309,9 @@ fn host_kernel_worker(waiter: Waiter, } if mailbox::receive() != 0 { - try!(process_kern_message(waiter, &mut session)) + if try!(process_kern_message(waiter, &mut session)) { + try!(host_write(stream, host::Reply::KernelFinished)) + } } if session.kernel_state == KernelState::Running { @@ -331,17 +337,28 @@ fn flash_kernel_worker(waiter: Waiter, let kernel = config::read_to_end(config_key); if kernel.len() == 0 { - info!("no kernel present in config key {}", config_key); - loop { - try!(waiter.relinquish()) - } + return Err(io::Error::new(io::ErrorKind::NotFound, "kernel not found")) } try!(unsafe { kern_load(waiter, &mut session, &kernel) }); try!(kern_run(&mut session)); loop { - try!(process_kern_message(waiter, &mut session)) + if mailbox::receive() != 0 { + if try!(process_kern_message(waiter, &mut session)) { + return Ok(()) + } + } + + if session.watchdog_set.expired() { + return Err(io_error("watchdog expired")) + } + + if !rtio_crg::check() { + return Err(io_error("RTIO clock failure")) + } + + try!(waiter.relinquish()) } } @@ -362,9 +379,21 @@ fn respawn(spawner: Spawner, waiter: Waiter, *handle = Some(spawner.spawn(8192, f)) } -pub fn handler(waiter: Waiter, spawner: Spawner) { +pub fn thread(waiter: Waiter, spawner: Spawner) { let congress = Urc::new(RefCell::new(Congress::new())); + info!("running startup kernel"); + match flash_kernel_worker(waiter, &mut congress.borrow_mut(), "startup_kernel") { + Ok(()) => info!("startup kernel finished"), + Err(err) => { + if err.kind() == io::ErrorKind::NotFound { + info!("no startup kernel found") + } else { + error!("startup kernel aborted: {}", err); + } + } + } + let addr = SocketAddr::new(IP_ANY, 1381); let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket"); info!("accepting network sessions in Rust"); @@ -390,7 +419,7 @@ pub fn handler(waiter: Waiter, spawner: Spawner) { if err.kind() == io::ErrorKind::UnexpectedEof { info!("connection closed"); } else { - error!("session aborted: {:?}", err); + error!("session aborted: {}", err); } } } @@ -409,8 +438,11 @@ pub fn handler(waiter: Waiter, spawner: Spawner) { Err(err) => { if err.kind() == io::ErrorKind::Interrupted { info!("idle kernel interrupted"); + } else if err.kind() == io::ErrorKind::NotFound { + info!("no idle kernel found"); + while waiter.relinquish().is_ok() {} } else { - error!("idle kernel aborted: {:?}", err); + error!("idle kernel aborted: {}", err); } } } From 0e2cd38135166d64dbf1bdd2d771ac138882a4df Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 06:40:38 +0000 Subject: [PATCH 027/127] Rust: set the SOF_KEEPALIVE flag on session sockets. --- artiq/runtime.rs/liblwip-sys/lib.rs | 5 +++++ artiq/runtime.rs/liblwip/lib.rs | 12 ++++++++++++ artiq/runtime.rs/src/sched.rs | 8 ++++++++ artiq/runtime.rs/src/session.rs | 1 + artiq/runtime/main.c | 4 ++++ 5 files changed, 30 insertions(+) diff --git a/artiq/runtime.rs/liblwip-sys/lib.rs b/artiq/runtime.rs/liblwip-sys/lib.rs index 5ea2e7d1e..82a7c5bcf 100644 --- a/artiq/runtime.rs/liblwip-sys/lib.rs +++ b/artiq/runtime.rs/liblwip-sys/lib.rs @@ -103,6 +103,10 @@ pub struct udp_pcb { pub const TCP_WRITE_FLAG_COPY: u8 = 0x01; pub const TCP_WRITE_FLAG_MORE: u8 = 0x02; +pub const SOF_REUSEADDR: u8 = 0x04; +pub const SOF_KEEPALIVE: u8 = 0x08; +pub const SOF_BROADCAST: u8 = 0x20; + extern { pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf; pub fn pbuf_realloc(p: *mut pbuf, length: u16); @@ -144,6 +148,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 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 c996074f0..4a6e1b3c1 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -397,6 +397,18 @@ impl TcpListener { pub fn try_accept(&self) -> Option { self.state.borrow_mut().backlog.pop_front() } + + pub fn keepalive(&self) -> bool { + unsafe { *lwip_sys::tcp_so_options_(self.raw) & lwip_sys::SOF_KEEPALIVE != 0 } + } + + pub fn set_keepalive(&self, keepalive: bool) { + if keepalive { + unsafe { *lwip_sys::tcp_so_options_(self.raw) |= lwip_sys::SOF_KEEPALIVE } + } else { + unsafe { *lwip_sys::tcp_so_options_(self.raw) &= !lwip_sys::SOF_KEEPALIVE } + } + } } impl Drop for TcpListener { diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 34dd5bc8c..4737ee0c6 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -391,6 +391,14 @@ impl<'a> TcpListener<'a> { pub fn acceptable(&self) -> bool { self.lower.state().borrow().acceptable() } + + pub fn keepalive(&self) -> bool { + self.lower.keepalive() + } + + pub fn set_keepalive(&self, keepalive: bool) { + self.lower.set_keepalive(keepalive) + } } pub use lwip::Shutdown; diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index e4fdf2924..879ad159d 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -396,6 +396,7 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { let addr = SocketAddr::new(IP_ANY, 1381); let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket"); + listener.set_keepalive(true); info!("accepting network sessions in Rust"); let mut kernel_thread = None; diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index c2460b32a..bf1091526 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -237,6 +237,10 @@ u16_t tcp_sndbuf_(struct tcp_pcb *pcb) { return tcp_sndbuf(pcb); } +u8_t* tcp_so_options_(struct tcp_pcb *pcb) { + return &pcb->so_options; +} + int main(void) { irq_setmask(0); From b590c6c7d8657f693c4024621d75bc0036df76ce Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 08:15:13 +0000 Subject: [PATCH 028/127] Rust: import --cfg flags generated by misoc. --- artiq/runtime/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 1068a1db9..30a8b1d7f 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -65,6 +65,7 @@ libruntime.a: cargo rustc --verbose \ --manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \ --target=or1k-unknown-none -- \ + $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ -L../libcompiler-rt From 2e4d19a1ce1e9b608e832c47962b590c4f989298 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 08:58:21 +0000 Subject: [PATCH 029/127] Rust: add some conditional compilation back to rtio_crg. --- artiq/runtime.rs/src/rtio_crg.rs | 58 +++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/artiq/runtime.rs/src/rtio_crg.rs b/artiq/runtime.rs/src/rtio_crg.rs index b1867396d..416bab03f 100644 --- a/artiq/runtime.rs/src/rtio_crg.rs +++ b/artiq/runtime.rs/src/rtio_crg.rs @@ -1,8 +1,42 @@ -use board::csr; -use {clock, config}; +use config; + +#[cfg(has_rtio_crg)] +mod imp { + use board::csr; + use clock; + + pub fn init() { + unsafe { csr::rtio_crg::pll_reset_write(0) } + } + + pub fn check() -> bool { + unsafe { csr::rtio_crg::pll_locked_read() != 0 } + } + + pub fn switch_clock(clk: u8) -> bool { + unsafe { + let cur_clk = csr::rtio_crg::clock_sel_read(); + if clk != cur_clk { + csr::rtio_crg::pll_reset_write(1); + csr::rtio_crg::clock_sel_write(clk); + csr::rtio_crg::pll_reset_write(0); + } + } + + clock::spin_us(150); + return check() + } +} + +#[cfg(not(has_rtio_crg))] +mod imp { + pub fn init() {} + pub fn check() -> bool { true } + pub fn switch_clock(clk: u8) -> bool { true } +} pub fn init() { - unsafe { csr::rtio_crg::pll_reset_write(0) } + imp::init(); let mut opt = [b'i']; let clk; @@ -28,20 +62,4 @@ pub fn init() { } } -pub fn check() -> bool { - unsafe { csr::rtio_crg::pll_locked_read() != 0 } -} - -pub fn switch_clock(clk: u8) -> bool { - unsafe { - let cur_clk = csr::rtio_crg::clock_sel_read(); - if clk != cur_clk { - csr::rtio_crg::pll_reset_write(1); - csr::rtio_crg::clock_sel_write(clk); - csr::rtio_crg::pll_reset_write(0); - } - } - - clock::spin_us(150); - return check() -} +pub use self::imp::{check, switch_clock}; From 2fefd0ad4af36e2609985160554c9c6f7e365db8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 4 Oct 2016 12:38:52 +0000 Subject: [PATCH 030/127] Rust: implement moninj. --- artiq/runtime.rs/liblwip/lib.rs | 66 +++++++++++--- artiq/runtime.rs/src/lib.rs | 6 +- artiq/runtime.rs/src/moninj.rs | 124 ++++++++++++++++++++++++++ artiq/runtime.rs/src/moninj_proto.rs | 65 ++++++++++++++ artiq/runtime.rs/src/proto.rs | 52 +++++++++++ artiq/runtime.rs/src/sched.rs | 8 +- artiq/runtime.rs/src/session_proto.rs | 44 +-------- 7 files changed, 308 insertions(+), 57 deletions(-) create mode 100644 artiq/runtime.rs/src/moninj.rs create mode 100644 artiq/runtime.rs/src/moninj_proto.rs create mode 100644 artiq/runtime.rs/src/proto.rs diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index 4a6e1b3c1..81c874bc6 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -9,6 +9,7 @@ extern crate std_artiq as std; use core::marker::PhantomData; use core::cell::RefCell; +use core::fmt; use alloc::boxed::Box; use collections::LinkedList; use libc::c_void; @@ -102,19 +103,54 @@ fn result_from(err: lwip_sys::err, f: F) -> Result #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum IpAddr { - Ip4([u8; 4]), - Ip6([u16; 8]), - IpAny + V4([u8; 4]), + V6([u16; 8]), + Any } -pub const IP4_ANY: IpAddr = IpAddr::Ip4([0, 0, 0, 0]); -pub const IP6_ANY: IpAddr = IpAddr::Ip6([0, 0, 0, 0, 0, 0, 0, 0]); -pub const IP_ANY: IpAddr = IpAddr::IpAny; +pub const IP4_ANY: IpAddr = IpAddr::V4([0, 0, 0, 0]); +pub const IP6_ANY: IpAddr = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0]); +pub const IP_ANY: IpAddr = IpAddr::Any; + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpAddr::V4(ref octets) => + write!(f, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]), + + IpAddr::V6(ref segments) => { + #[derive(Clone, Copy, PartialEq, Eq)] + enum State { Head, Skip, Tail }; + + let mut state = State::Head; + for (idx, &segment) in segments.iter().enumerate() { + match state { + State::Head | State::Skip if segment == 0 => + state = State::Skip, + State::Skip if segment != 0 => { + state = State::Tail; + try!(write!(f, ":{:x}", segment)) + } + _ => try!(write!(f, "{:x}", segment)) + } + + if state != State::Skip && idx != 15 { + try!(write!(f, ":")) + } + } + Ok(()) + }, + + IpAddr::Any => + write!(f, "*") + } + } +} impl IpAddr { fn into_raw(self) -> lwip_sys::ip_addr { match self { - IpAddr::Ip4(ref octets) => + IpAddr::V4(octets) => lwip_sys::ip_addr { data: [(octets[0] as u32) << 24 | (octets[1] as u32) << 16 | @@ -123,7 +159,7 @@ impl IpAddr { 0, 0, 0], type_: lwip_sys::IPADDR_TYPE_V4 }, - IpAddr::Ip6(ref segments) => + IpAddr::V6(segments) => lwip_sys::ip_addr { data: [(segments[0] as u32) << 16 | (segments[1] as u32), (segments[2] as u32) << 16 | (segments[3] as u32), @@ -131,7 +167,7 @@ impl IpAddr { (segments[6] as u32) << 16 | (segments[7] as u32)], type_: lwip_sys::IPADDR_TYPE_V6 }, - IpAddr::IpAny => + IpAddr::Any => lwip_sys::ip_addr { data: [0; 4], type_: lwip_sys::IPADDR_TYPE_ANY @@ -142,17 +178,17 @@ impl IpAddr { unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr { match *raw { lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V4, data } => - IpAddr::Ip4([(data[0] >> 24) as u8, + IpAddr::V4([(data[0] >> 24) as u8, (data[0] >> 16) as u8, (data[0] >> 8) as u8, (data[0] >> 0) as u8]), lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V6, data } => - IpAddr::Ip6([(data[0] >> 16) as u16, data[0] as u16, + IpAddr::V6([(data[0] >> 16) as u16, data[0] as u16, (data[1] >> 16) as u16, data[1] as u16, (data[2] >> 16) as u16, data[2] as u16, (data[3] >> 16) as u16, data[3] as u16]), lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } => - IpAddr::IpAny + IpAddr::Any } } } @@ -163,6 +199,12 @@ pub struct SocketAddr { pub port: u16 } +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.ip, self.port) + } +} + impl SocketAddr { pub fn new(ip: IpAddr, port: u16) -> SocketAddr { SocketAddr { ip: ip, port: port } diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 5d73b8a17..0e1860f48 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc, const_fn, try_borrow)] +#![feature(libc, const_fn, try_borrow, stmt_expr_attributes)] #[macro_use] extern crate std_artiq as std; @@ -24,11 +24,14 @@ mod sched; mod logger; mod cache; +mod proto; mod kernel_proto; mod session_proto; +mod moninj_proto; mod kernel; mod session; +mod moninj; extern { fn network_init(); @@ -52,6 +55,7 @@ pub unsafe extern fn rust_main() { let mut scheduler = sched::Scheduler::new(); scheduler.spawner().spawn(8192, session::thread); + scheduler.spawner().spawn(4096, moninj::thread); loop { scheduler.run(); diff --git a/artiq/runtime.rs/src/moninj.rs b/artiq/runtime.rs/src/moninj.rs new file mode 100644 index 000000000..13c8ef4cd --- /dev/null +++ b/artiq/runtime.rs/src/moninj.rs @@ -0,0 +1,124 @@ +use std::vec::Vec; +use std::io; +use board::csr; +use sched::{Waiter, Spawner}; +use sched::{UdpSocket, SocketAddr, IP_ANY}; +use moninj_proto::*; + +const MONINJ_TTL_OVERRIDE_ENABLE: u8 = 0; +const MONINJ_TTL_OVERRIDE_O: u8 = 1; +const MONINJ_TTL_OVERRIDE_OE: u8 = 2; + +fn worker(socket: &mut UdpSocket) -> io::Result<()> { + let mut buf = Vec::new(); + loop { + let addr = try!(socket.recv_from(&mut buf)); + let request = try!(Request::read_from(&mut io::Cursor::new(&buf))); + trace!("{} -> {:?}", addr, request); + + match request { + Request::Monitor => { + let mut dds_ftws = [0; (csr::CONFIG_RTIO_DDS_COUNT as usize * + csr::CONFIG_DDS_CHANNELS_PER_BUS as usize)]; + let mut reply = Reply::default(); + + for i in 0..csr::CONFIG_RTIO_REGULAR_TTL_COUNT as u8 { + unsafe { + csr::rtio_moninj::mon_chan_sel_write(i); + csr::rtio_moninj::mon_probe_sel_write(0); + csr::rtio_moninj::mon_value_update_write(1); + if csr::rtio_moninj::mon_value_read() != 0 { + reply.ttl_levels |= 1 << i; + } + csr::rtio_moninj::mon_probe_sel_write(1); + csr::rtio_moninj::mon_value_update_write(1); + if csr::rtio_moninj::mon_value_read() != 0 { + reply.ttl_oes |= 1 << i; + } + csr::rtio_moninj::inj_chan_sel_write(i); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); + if csr::rtio_moninj::inj_value_read() != 0 { + reply.ttl_overrides |= 1 << i; + } + } + } + + reply.dds_rtio_first_channel = csr::CONFIG_RTIO_FIRST_DDS_CHANNEL as u16; + reply.dds_channels_per_bus = csr::CONFIG_DDS_CHANNELS_PER_BUS as u16; + + for j in 0..csr::CONFIG_RTIO_DDS_COUNT { + unsafe { + csr::rtio_moninj::mon_chan_sel_write( + (csr::CONFIG_RTIO_FIRST_DDS_CHANNEL + j) as u8); + for i in 0..csr::CONFIG_DDS_CHANNELS_PER_BUS { + csr::rtio_moninj::mon_probe_sel_write(i as u8); + csr::rtio_moninj::mon_value_update_write(1); + dds_ftws[(csr::CONFIG_DDS_CHANNELS_PER_BUS * j + i) as usize] = + csr::rtio_moninj::mon_value_read(); + } + } + } + reply.dds_ftws = &dds_ftws; + + trace!("{} <- {:?}", addr, reply); + buf.clear(); + try!(reply.write_to(&mut buf)); + try!(socket.send_to(&buf, addr)); + }, + + Request::TtlSet { channel, mode: TtlMode::Experiment } => { + unsafe { + csr::rtio_moninj::inj_chan_sel_write(channel); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); + csr::rtio_moninj::inj_value_write(0); + } + }, + + Request::TtlSet { channel, mode: TtlMode::High } => { + unsafe { + csr::rtio_moninj::inj_chan_sel_write(channel); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); + csr::rtio_moninj::inj_value_write(1); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); + csr::rtio_moninj::inj_value_write(1); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); + csr::rtio_moninj::inj_value_write(1); + } + }, + + Request::TtlSet { channel, mode: TtlMode::Low } => { + unsafe { + csr::rtio_moninj::inj_chan_sel_write(channel); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); + csr::rtio_moninj::inj_value_write(0); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); + csr::rtio_moninj::inj_value_write(1); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); + csr::rtio_moninj::inj_value_write(1); + } + }, + + Request::TtlSet { channel, mode: TtlMode::Input } => { + unsafe { + csr::rtio_moninj::inj_chan_sel_write(channel); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); + csr::rtio_moninj::inj_value_write(0); + csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); + csr::rtio_moninj::inj_value_write(1); + } + } + } + } +} + +pub fn thread(waiter: Waiter, _spawner: Spawner) { + let mut socket = UdpSocket::new(waiter).expect("cannot create socket"); + socket.bind(SocketAddr::new(IP_ANY, 3250)).expect("cannot bind socket"); + + loop { + match worker(&mut socket) { + Ok(()) => unreachable!(), + Err(err) => error!("moninj aborted: {}", err) + } + } +} diff --git a/artiq/runtime.rs/src/moninj_proto.rs b/artiq/runtime.rs/src/moninj_proto.rs new file mode 100644 index 000000000..2dd613a6e --- /dev/null +++ b/artiq/runtime.rs/src/moninj_proto.rs @@ -0,0 +1,65 @@ +use std::io::{self, Read, Write}; +use proto::*; + +#[derive(Debug)] +pub enum TtlMode { + Experiment, + High, + Low, + Input +} + +impl TtlMode { + pub fn read_from(reader: &mut Read) -> io::Result { + Ok(match try!(read_u8(reader)) { + 0 => TtlMode::Experiment, + 1 => TtlMode::High, + 2 => TtlMode::Low, + 3 => TtlMode::Input, + _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown TTL mode")) + }) + } +} + +#[derive(Debug)] +pub enum Request { + Monitor, + TtlSet { channel: u8, mode: TtlMode } +} + +impl Request { + pub fn read_from(reader: &mut Read) -> io::Result { + Ok(match try!(read_u8(reader)) { + 1 => Request::Monitor, + 2 => Request::TtlSet { + channel: try!(read_u8(reader)), + mode: try!(TtlMode::read_from(reader)) + }, + _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown request type")) + }) + } +} + +#[derive(Debug, Default)] +pub struct Reply<'a> { + pub ttl_levels: u64, + pub ttl_oes: u64, + pub ttl_overrides: u64, + pub dds_rtio_first_channel: u16, + pub dds_channels_per_bus: u16, + pub dds_ftws: &'a [u32] +} + +impl<'a> Reply<'a> { + pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { + try!(write_u64(writer, self.ttl_levels)); + try!(write_u64(writer, self.ttl_oes)); + try!(write_u64(writer, self.ttl_overrides)); + try!(write_u16(writer, self.dds_rtio_first_channel)); + try!(write_u16(writer, self.dds_channels_per_bus)); + for dds_ftw in self.dds_ftws { + try!(write_u32(writer, *dds_ftw)); + } + Ok(()) + } +} diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs new file mode 100644 index 000000000..acb22998b --- /dev/null +++ b/artiq/runtime.rs/src/proto.rs @@ -0,0 +1,52 @@ +#![allow(dead_code)] + +use std::io::{self, Read, Write}; +use byteorder::{ByteOrder, NetworkEndian}; + +// FIXME: replace these with byteorder core io traits once those are in +pub fn read_u8(reader: &mut Read) -> io::Result { + let mut bytes = [0; 1]; + try!(reader.read_exact(&mut bytes)); + Ok(bytes[0]) +} + +pub fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> { + let bytes = [value; 1]; + writer.write_all(&bytes) +} + +pub fn read_u16(reader: &mut Read) -> io::Result { + let mut bytes = [0; 2]; + try!(reader.read_exact(&mut bytes)); + Ok(NetworkEndian::read_u16(&bytes)) +} + +pub fn write_u16(writer: &mut Write, value: u16) -> io::Result<()> { + let mut bytes = [0; 2]; + NetworkEndian::write_u16(&mut bytes, value); + writer.write_all(&bytes) +} + +pub fn read_u32(reader: &mut Read) -> io::Result { + let mut bytes = [0; 4]; + try!(reader.read_exact(&mut bytes)); + Ok(NetworkEndian::read_u32(&bytes)) +} + +pub fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> { + let mut bytes = [0; 4]; + NetworkEndian::write_u32(&mut bytes, value); + writer.write_all(&bytes) +} + +pub fn read_u64(reader: &mut Read) -> io::Result { + let mut bytes = [0; 8]; + try!(reader.read_exact(&mut bytes)); + Ok(NetworkEndian::read_u64(&bytes)) +} + +pub fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> { + let mut bytes = [0; 8]; + NetworkEndian::write_u64(&mut bytes, value); + writer.write_all(&bytes) +} diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 4737ee0c6..ef4c8876f 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -328,12 +328,12 @@ impl<'a> UdpSocket<'a> { Ok(buf.len()) } - pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { + pub fn recv_from(&self, buf: &mut Vec) -> Result { try!(self.waiter.udp_readable(&self.lower)); let (pbuf, addr) = self.lower.try_recv().unwrap(); - let len = ::std::cmp::min(buf.len(), pbuf.len()); - (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); - Ok((len, addr)) + buf.clear(); + buf.extend_from_slice(&pbuf.as_slice()); + Ok(addr) } pub fn send(&self, buf: &[u8]) -> Result { diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index b3b90d8b5..d2118f911 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -1,42 +1,6 @@ use std::prelude::v1::*; use std::io::{self, Read, Write}; -use byteorder::{ByteOrder, NetworkEndian}; - -// FIXME: replace these with byteorder core io traits once those are in -fn read_u8(reader: &mut Read) -> io::Result { - let mut bytes = [0; 1]; - try!(reader.read_exact(&mut bytes)); - Ok(bytes[0]) -} - -fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> { - let bytes = [value; 1]; - writer.write_all(&bytes) -} - -fn read_u32(reader: &mut Read) -> io::Result { - let mut bytes = [0; 4]; - try!(reader.read_exact(&mut bytes)); - Ok(NetworkEndian::read_u32(&bytes)) -} - -fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> { - let mut bytes = [0; 4]; - NetworkEndian::write_u32(&mut bytes, value); - writer.write_all(&bytes) -} - -fn read_u64(reader: &mut Read) -> io::Result { - let mut bytes = [0; 4]; - try!(reader.read_exact(&mut bytes)); - Ok(NetworkEndian::read_u64(&bytes)) -} - -fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> { - let mut bytes = [0; 4]; - NetworkEndian::write_u64(&mut bytes, value); - writer.write_all(&bytes) -} +use proto::*; fn read_bytes(reader: &mut Read) -> io::Result> { let length = try!(read_u32(reader)); @@ -140,9 +104,9 @@ impl Request { try!(read_sync(reader)); let length = try!(read_u32(reader)) as usize; - let type_ = try!(read_u8(reader)); + let ty = try!(read_u8(reader)); - Ok(match type_ { + Ok(match ty { 1 => Request::Log, 2 => Request::LogClear, 3 => Request::Ident, @@ -168,7 +132,7 @@ impl Request { 12 => Request::FlashRemove { key: try!(read_string(reader)) }, - _ => unreachable!() + _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown request type")) }) } } From 0a29c00fcc24d93044b42812ef159f1568c49293 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 5 Oct 2016 05:59:38 +0000 Subject: [PATCH 031/127] Rust: implement analyzer. --- artiq/runtime.rs/liblwip/lib.rs | 56 ++++++++++--- artiq/runtime.rs/src/analyzer.rs | 109 +++++++++++++++++++++++++ artiq/runtime.rs/src/analyzer_proto.rs | 22 +++++ artiq/runtime.rs/src/board.rs | 1 + artiq/runtime.rs/src/lib.rs | 7 +- artiq/runtime.rs/src/sched.rs | 4 +- artiq/runtime.rs/src/session.rs | 2 +- 7 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 artiq/runtime.rs/src/analyzer.rs create mode 100644 artiq/runtime.rs/src/analyzer_proto.rs diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index 81c874bc6..f94b65dac 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -8,6 +8,7 @@ extern crate lwip_sys; extern crate std_artiq as std; use core::marker::PhantomData; +use core::ptr; use core::cell::RefCell; use core::fmt; use alloc::boxed::Box; @@ -33,6 +34,8 @@ pub enum Error { ConnectionReset, ConnectionClosed, IllegalArgument, + // Not used by lwip; added for building blocking interfaces. + Interrupted } impl Error { @@ -54,6 +57,7 @@ impl Error { Error::ConnectionReset => "connection reset", Error::ConnectionClosed => "connection closed", Error::IllegalArgument => "illegal argument", + Error::Interrupted => "interrupted" } } } @@ -72,7 +76,12 @@ impl error::Error for Error { impl From for std::io::Error { fn from(lower: Error) -> std::io::Error { - std::io::Error::new(std::io::ErrorKind::Other, lower) + use std::io; + + match lower { + Error::Interrupted => io::Error::new(io::ErrorKind::Interrupted, "interrupted"), + err => io::Error::new(io::ErrorKind::Other, err) + } } } @@ -472,7 +481,8 @@ pub enum Shutdown { #[derive(Debug)] pub struct TcpStreamState { recv_buffer: LinkedList>>, - send_avail: usize + send_avail: usize, + total_sent: usize } impl TcpStreamState { @@ -511,10 +521,12 @@ impl TcpStream { } extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb, - _len: u16) -> lwip_sys::err { + len: u16) -> lwip_sys::err { unsafe { let state = arg as *mut RefCell; - (*state).borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(raw) as usize; + let mut state = (*state).borrow_mut(); + state.send_avail = lwip_sys::tcp_sndbuf_(raw) as usize; + state.total_sent = state.total_sent.wrapping_add(len as usize); } lwip_sys::ERR_OK } @@ -529,7 +541,8 @@ impl TcpStream { unsafe { let mut state = Box::new(RefCell::new(TcpStreamState { recv_buffer: LinkedList::new(), - send_avail: lwip_sys::tcp_sndbuf_(raw) as usize + send_avail: lwip_sys::tcp_sndbuf_(raw) as usize, + total_sent: 0 })); let arg = &mut *state as *mut RefCell as *mut _; lwip_sys::tcp_arg(raw, arg); @@ -544,22 +557,39 @@ impl TcpStream { &*self.state } - pub fn write(&self, data: &[u8]) -> Result { - let sndbuf = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize; + unsafe fn write_common(&self, data: &[u8], copy: bool) -> Result { + let sndbuf = lwip_sys::tcp_sndbuf_(self.raw) as usize; let len = if data.len() < sndbuf { data.len() } else { sndbuf }; - let result = result_from(unsafe { + let result = result_from({ lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16, - lwip_sys::TCP_WRITE_FLAG_COPY | - lwip_sys::TCP_WRITE_FLAG_MORE) + lwip_sys::TCP_WRITE_FLAG_MORE | + if copy { lwip_sys::TCP_WRITE_FLAG_COPY } else { 0 }) }, || len); - self.state.borrow_mut().send_avail = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize; + self.state.borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(self.raw) as usize; result } + pub fn write(&self, data: &[u8]) -> Result { + unsafe { self.write_common(data, true) } + } + + pub fn write_in_place(&self, data: &[u8], mut relinquish: F) -> Result + where F: FnMut() -> Result<()> { + let cursor = self.state.borrow().total_sent; + let written = try!(unsafe { self.write_common(data, false) }); + loop { + let cursor_now = self.state.borrow().total_sent; + if cursor_now >= cursor.wrapping_add(written) { + return Ok(written) + } else { + try!(relinquish()) + } + } + } + pub fn flush(&self) -> Result<()> { - const EMPTY_DATA: [u8; 0] = []; result_from(unsafe { - lwip_sys::tcp_write(self.raw, &EMPTY_DATA as *const [u8] as *const _, 0, 0) + lwip_sys::tcp_write(self.raw, ptr::null(), 0, 0) }, || ()) } diff --git a/artiq/runtime.rs/src/analyzer.rs b/artiq/runtime.rs/src/analyzer.rs new file mode 100644 index 000000000..c16bf1f95 --- /dev/null +++ b/artiq/runtime.rs/src/analyzer.rs @@ -0,0 +1,109 @@ +use std::io::{self, Read, Write}; +use board::{self, csr}; +use sched::{Waiter, Spawner}; +use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; +use analyzer_proto::*; + +const BUFFER_SIZE: usize = 512 * 1024; + +// hack until https://github.com/rust-lang/rust/issues/33626 is fixed +#[repr(simd)] +struct Align64(u64, u64, u64, u64, u64, u64, u64, u64); + +struct Buffer { + data: [u8; BUFFER_SIZE], + __alignment: [Align64; 0] +} + +static mut BUFFER: Buffer = Buffer { + data: [0; BUFFER_SIZE], + __alignment: [] +}; + +fn arm() { + unsafe { + let base_addr = &mut BUFFER.data[0] as *mut _ as usize; + let last_addr = &mut BUFFER.data[BUFFER_SIZE - 1] as *mut _ as usize; + csr::rtio_analyzer::message_encoder_overflow_reset_write(1); + csr::rtio_analyzer::dma_base_address_write(base_addr as u64); + csr::rtio_analyzer::dma_last_address_write(last_addr as u64); + csr::rtio_analyzer::dma_reset_write(1); + csr::rtio_analyzer::enable_write(1); + } +} + +fn disarm() { + unsafe { + csr::rtio_analyzer::enable_write(0); + while csr::rtio_analyzer::busy_read() != 0 {} + board::flush_cpu_dcache(); + board::flush_l2_cache(); + } +} + +fn worker(mut stream: TcpStream) -> io::Result<()> { + let data = unsafe { &BUFFER.data[..] }; + let overflow_occurred = unsafe { csr::rtio_analyzer::message_encoder_overflow_read() != 0 }; + let total_byte_count = unsafe { csr::rtio_analyzer::dma_byte_count_read() }; + let pointer = (total_byte_count % BUFFER_SIZE as u64) as usize; + let wraparound = total_byte_count >= BUFFER_SIZE as u64; + + let header = Header { + total_byte_count: total_byte_count, + sent_bytes: if wraparound { BUFFER_SIZE as u32 } else { total_byte_count as u32 }, + overflow_occurred: overflow_occurred, + log_channel: csr::CONFIG_RTIO_LOG_CHANNEL as u8, + dds_onehot_sel: csr::CONFIG_DDS_ONEHOT_SEL != 0 + }; + trace!("{:?}", header); + + try!(header.write_to(&mut stream)); + if wraparound { + try!(stream.write(&data[pointer..])); + try!(stream.write(&data[..pointer])); + } else { + try!(stream.write(&data[..pointer])); + } + + Ok(()) +} + +// TODO: remove this, it's pointless in analyzer +fn check_magic(stream: &mut TcpStream) -> io::Result<()> { + const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; + + let mut magic: [u8; 14] = [0; 14]; + try!(stream.read_exact(&mut magic)); + if magic != MAGIC { + Err(io::Error::new(io::ErrorKind::InvalidData, "unrecognized magic")) + } else { + Ok(()) + } +} + +pub fn thread(waiter: Waiter, _spawner: Spawner) { + // verify that the hack above works + assert!(::core::mem::align_of::() == 64); + + let addr = SocketAddr::new(IP_ANY, 1382); + let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket"); + listener.set_keepalive(true); + + loop { + arm(); + + let (mut stream, addr) = listener.accept().expect("cannot accept client"); + match check_magic(&mut stream) { + Ok(()) => (), + Err(_) => continue + } + info!("connection from {}", addr); + + disarm(); + + match worker(stream) { + Ok(()) => (), + Err(err) => error!("analyzer aborted: {}", err) + } + } +} diff --git a/artiq/runtime.rs/src/analyzer_proto.rs b/artiq/runtime.rs/src/analyzer_proto.rs new file mode 100644 index 000000000..344882d3b --- /dev/null +++ b/artiq/runtime.rs/src/analyzer_proto.rs @@ -0,0 +1,22 @@ +use std::io::{self, Write}; +use proto::*; + +#[derive(Debug)] +pub struct Header { + pub sent_bytes: u32, + pub total_byte_count: u64, + pub overflow_occurred: bool, + pub log_channel: u8, + pub dds_onehot_sel: bool +} + +impl Header { + pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { + try!(write_u32(writer, self.sent_bytes)); + try!(write_u64(writer, self.total_byte_count)); + try!(write_u8(writer, self.overflow_occurred as u8)); + try!(write_u8(writer, self.log_channel)); + try!(write_u8(writer, self.dds_onehot_sel as u8)); + Ok(()) + } +} diff --git a/artiq/runtime.rs/src/board.rs b/artiq/runtime.rs/src/board.rs index 94ad10291..21c8d3d85 100644 --- a/artiq/runtime.rs/src/board.rs +++ b/artiq/runtime.rs/src/board.rs @@ -5,6 +5,7 @@ include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); extern { pub fn flush_cpu_dcache(); + pub fn flush_l2_cache(); } pub fn ident(buf: &mut [u8]) -> &str { diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 0e1860f48..68a0ff269 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc, const_fn, try_borrow, stmt_expr_attributes)] +#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd)] #[macro_use] extern crate std_artiq as std; @@ -28,10 +28,13 @@ mod proto; mod kernel_proto; mod session_proto; mod moninj_proto; +mod analyzer_proto; mod kernel; mod session; mod moninj; +#[cfg(has_rtio_analyzer)] +mod analyzer; extern { fn network_init(); @@ -56,6 +59,8 @@ pub unsafe extern fn rust_main() { let mut scheduler = sched::Scheduler::new(); scheduler.spawner().spawn(8192, session::thread); scheduler.spawner().spawn(4096, moninj::thread); + #[cfg(has_rtio_analyzer)] + scheduler.spawner().spawn(4096, analyzer::thread); loop { scheduler.run(); diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index ef4c8876f..1e84cd73e 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -460,7 +460,9 @@ impl<'a> Read for TcpStream<'a> { impl<'a> Write for TcpStream<'a> { fn write(&mut self, buf: &[u8]) -> Result { try!(self.waiter.tcp_writeable(&self.lower)); - Ok(try!(self.lower.write(buf))) + Ok(try!(self.lower.write_in_place(buf, + || self.waiter.relinquish() + .map_err(|_| lwip::Error::Interrupted)))) } fn flush(&mut self) -> Result<()> { diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 879ad159d..42f180e44 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -407,7 +407,7 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { Ok(()) => (), Err(_) => continue } - info!("new connection from {:?}", addr); + info!("new connection from {}", addr); let stream = stream.into_lower(); let congress = congress.clone(); From 4cfc4e89b983b209989603ba5ec8dbc3fdcc028a Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 5 Oct 2016 14:15:53 +0000 Subject: [PATCH 032/127] Rust: add basic RPC support. --- artiq/runtime.rs/src/kernel_proto.rs | 6 +- artiq/runtime.rs/src/lib.rs | 3 +- artiq/runtime.rs/src/proto.rs | 13 ++ artiq/runtime.rs/src/rpc.rs | 187 ++++++++++++++++++++++++++ artiq/runtime.rs/src/session.rs | 48 ++++++- artiq/runtime.rs/src/session_proto.rs | 30 ++--- 6 files changed, 262 insertions(+), 25 deletions(-) create mode 100644 artiq/runtime.rs/src/rpc.rs diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index e7c76a508..eb8fb7c2a 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -38,6 +38,7 @@ pub enum Message<'a> { RpcSend { service: u32, + batch: bool, tag: &'a [u8], data: *const *const () }, @@ -182,10 +183,11 @@ impl<'a> Message<'a> { Message::WatchdogClear { id: (*msg).id as usize } } - c::Type::RpcSend => { + c::Type::RpcSend | c::Type::RpcBatch => { let msg = ptr as *const c::RpcSend; Message::RpcSend { service: (*msg).service as _, + batch: (*msg).ty == c::Type::RpcBatch, tag: slice::from_raw_parts((*msg).tag as *const _, c::strlen((*msg).tag) as usize), data: (*msg).data as *const _ @@ -248,7 +250,7 @@ mod c { extern { pub fn strlen(ptr: *const c_char) -> size_t; } #[repr(u32)] - #[derive(Debug)] + #[derive(Debug, PartialEq, Eq)] #[allow(dead_code)] pub enum Type { LoadRequest, diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 68a0ff269..a9da393ec 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -31,6 +31,7 @@ mod moninj_proto; mod analyzer_proto; mod kernel; +mod rpc; mod session; mod moninj; #[cfg(has_rtio_analyzer)] @@ -57,7 +58,7 @@ pub unsafe extern fn rust_main() { network_init(); let mut scheduler = sched::Scheduler::new(); - scheduler.spawner().spawn(8192, session::thread); + scheduler.spawner().spawn(16384, session::thread); scheduler.spawner().spawn(4096, moninj::thread); #[cfg(has_rtio_analyzer)] scheduler.spawner().spawn(4096, analyzer::thread); diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs index acb22998b..d7df40b8c 100644 --- a/artiq/runtime.rs/src/proto.rs +++ b/artiq/runtime.rs/src/proto.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] use std::io::{self, Read, Write}; +use std::vec::Vec; use byteorder::{ByteOrder, NetworkEndian}; // FIXME: replace these with byteorder core io traits once those are in @@ -50,3 +51,15 @@ pub fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> { NetworkEndian::write_u64(&mut bytes, value); writer.write_all(&bytes) } + +pub fn read_bytes(reader: &mut Read) -> io::Result> { + let length = try!(read_u32(reader)); + let mut value = vec![0; length as usize]; + try!(reader.read_exact(&mut value)); + Ok(value) +} + +pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { + try!(write_u32(writer, value.len() as u32)); + writer.write_all(value) +} diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc.rs new file mode 100644 index 000000000..5507a98e0 --- /dev/null +++ b/artiq/runtime.rs/src/rpc.rs @@ -0,0 +1,187 @@ +use std::io::{self, Read, Write}; +use proto::{write_u8, write_bytes}; +use self::tag::{Tag, TagIterator, split_tag}; + +fn recv_value(reader: &mut Read, tag: Tag, data: &mut *const ()) -> io::Result<()> { + match tag { + Tag::None => Ok(()), + _ => unreachable!() + } +} + +pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *const ()) -> io::Result<()> { + let mut it = TagIterator::new(tag_bytes); + trace!("recv ...->{}", it); + + let mut data = data; + try!(recv_value(reader, it.next().expect("RPC without a return value"), &mut data)); + + Ok(()) +} + +fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> { + match tag { + Tag::None => write_u8(writer, b'n'), + _ => unreachable!() + } +} + +pub fn send_args(writer: &mut Write, 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); + + for index in 0.. { + if let Some(arg_tag) = args_it.next() { + let mut data = unsafe { *data.offset(index) }; + try!(send_value(writer, arg_tag, &mut data)); + } else { + break + } + } + try!(write_u8(writer, 0)); + try!(write_bytes(writer, return_tag_bytes)); + + Ok(()) +} + +mod tag { + use core::fmt; + + pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) { + let tag_separator = + tag_bytes.iter() + .position(|&b| b == b':') + .expect("tag without a return separator"); + let (arg_tags_bytes, rest) = tag_bytes.split_at(tag_separator); + let return_tag_bytes = &rest[1..]; + (arg_tags_bytes, return_tag_bytes) + } + + #[derive(Debug)] + pub enum Tag<'a> { + None, + Bool, + Int32, + Int64, + Float64, + String, + Tuple(TagIterator<'a>, u8), + List(TagIterator<'a>), + Array(TagIterator<'a>), + Range(TagIterator<'a>), + Keyword(TagIterator<'a>), + Object + } + + #[derive(Debug, Clone, Copy)] + pub struct TagIterator<'a> { + data: &'a [u8] + } + + impl<'a> TagIterator<'a> { + pub fn new(data: &'a [u8]) -> TagIterator<'a> { + TagIterator { data: data } + } + + pub fn next(&mut self) -> Option> { + if self.data.len() == 0 { + return None + } + + let tag_byte = self.data[0]; + self.data = &self.data[1..]; + Some(match tag_byte { + b'n' => Tag::None, + b'b' => Tag::Bool, + b'i' => Tag::Int32, + b'I' => Tag::Int64, + b'f' => Tag::Float64, + b's' => Tag::String, + b't' => { + let count = self.data[0]; + self.data = &self.data[1..]; + Tag::Tuple(self.skip(count), count) + } + b'l' => Tag::List(self.skip(1)), + b'a' => Tag::Array(self.skip(1)), + b'r' => Tag::Range(self.skip(1)), + b'k' => Tag::Keyword(self.skip(1)), + b'O' => Tag::Object, + _ => unreachable!() + }) + } + + fn skip(&mut self, count: u8) -> TagIterator<'a> { + let origin = self.clone(); + for _ in 0..count { + self.next().expect("truncated tag"); + } + origin + } + } + + impl<'a> fmt::Display for TagIterator<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut it = self.clone(); + let mut first = true; + while let Some(tag) = it.next() { + if first { + first = false + } else { + try!(write!(f, ", ")) + } + + match tag { + Tag::None => + try!(write!(f, "None")), + Tag::Bool => + try!(write!(f, "Bool")), + Tag::Int32 => + try!(write!(f, "Int32")), + Tag::Int64 => + try!(write!(f, "Int64")), + Tag::Float64 => + try!(write!(f, "Float64")), + Tag::String => + try!(write!(f, "String")), + Tag::Tuple(it, cnt) => { + try!(write!(f, "Tuple(")); + for i in 0..cnt { + try!(it.fmt(f)); + if i != cnt - 1 { try!(write!(f, ", ")) } + } + try!(write!(f, ")")) + } + Tag::List(it) => { + try!(write!(f, "List(")); + try!(it.fmt(f)); + try!(write!(f, ")")) + } + Tag::Array(it) => { + try!(write!(f, "Array(")); + try!(it.fmt(f)); + try!(write!(f, ")")) + } + Tag::Range(it) => { + try!(write!(f, "Range(")); + try!(it.fmt(f)); + try!(write!(f, ")")) + } + Tag::Keyword(it) => { + try!(write!(f, "Keyword(")); + try!(it.fmt(f)); + try!(write!(f, ")")) + } + Tag::Object => + try!(write!(f, "Object")) + } + } + + Ok(()) + } + } +} diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 42f180e44..d4b85ead6 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -9,6 +9,7 @@ use urc::Urc; use sched::{ThreadHandle, Waiter, Spawner}; use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; +use rpc; use session_proto as host; use kernel_proto as kern; @@ -230,11 +231,33 @@ fn process_host_message(waiter: Waiter, Err(_) => host_write(stream, host::Reply::KernelStartupFailed) }, + host::Request::RpcReply { tag, data } => { + if session.kernel_state != KernelState::RpcWait { + unexpected!("unsolicited RPC reply") + } + + try!(kern_recv(waiter, |reply| { + match reply { + kern::RpcRecvRequest { slot } => { + let mut data = io::Cursor::new(data); + rpc::recv_return(&mut data, &tag, slot) + } + other => + unexpected!("unexpected reply from kernel CPU: {:?}", other) + } + })); + try!(kern_send(waiter, kern::RpcRecvReply { alloc_size: 0, exception: None })); + + session.kernel_state = KernelState::Running; + Ok(()) + } + request => unexpected!("unexpected request {:?} from host machine", request) } } fn process_kern_message(waiter: Waiter, + mut stream: Option<&mut TcpStream>, session: &mut Session) -> io::Result { kern::Message::wait_and_receive(waiter, |request| { match (&request, session.kernel_state) { @@ -276,6 +299,24 @@ fn process_kern_message(waiter: Waiter, kern_acknowledge() } + kern::RpcSend { service, batch, tag, data } => { + match stream { + None => unexpected!("unexpected RPC in flash kernel"), + Some(ref mut stream) => { + let mut buf = Vec::new(); + try!(rpc::send_args(&mut buf, tag, data)); + try!(host_write(stream, host::Reply::RpcRequest { + service: service, + data: &buf[..] + })); + if !batch { + session.kernel_state = KernelState::RpcWait + } + kern_acknowledge() + } + } + } + kern::CacheGetRequest { key } => { let value = session.congress.cache.get(key); kern_send(waiter, kern::CacheGetReply { @@ -289,6 +330,7 @@ fn process_kern_message(waiter: Waiter, } kern::RunFinished => { + try!(kern_acknowledge()); session.kernel_state = KernelState::Absent; return Ok(true) } @@ -309,7 +351,7 @@ fn host_kernel_worker(waiter: Waiter, } if mailbox::receive() != 0 { - if try!(process_kern_message(waiter, &mut session)) { + if try!(process_kern_message(waiter, Some(stream), &mut session)) { try!(host_write(stream, host::Reply::KernelFinished)) } } @@ -345,7 +387,7 @@ fn flash_kernel_worker(waiter: Waiter, loop { if mailbox::receive() != 0 { - if try!(process_kern_message(waiter, &mut session)) { + if try!(process_kern_message(waiter, None, &mut session)) { return Ok(()) } } @@ -376,7 +418,7 @@ fn respawn(spawner: Spawner, waiter: Waiter, } } - *handle = Some(spawner.spawn(8192, f)) + *handle = Some(spawner.spawn(16384, f)) } pub fn thread(waiter: Waiter, spawner: Spawner) { diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index d2118f911..ba32fbc5a 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -2,18 +2,6 @@ use std::prelude::v1::*; use std::io::{self, Read, Write}; use proto::*; -fn read_bytes(reader: &mut Read) -> io::Result> { - let length = try!(read_u32(reader)); - let mut value = vec![0; length as usize]; - try!(reader.read_exact(&mut value)); - Ok(value) -} - -fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { - try!(write_u32(writer, value.len() as u32)); - writer.write_all(value) -} - fn read_string(reader: &mut Read) -> io::Result { let mut bytes = try!(read_bytes(reader)); let len = bytes.len() - 1; // length without trailing \0 @@ -89,7 +77,7 @@ pub enum Request { LoadKernel(Vec), RunKernel, - RpcReply { tag: String }, // FIXME + RpcReply { tag: Vec, data: Vec }, RpcException(Exception), FlashRead { key: String }, @@ -115,11 +103,14 @@ impl Request { let mut code = vec![0; length - HEADER_SIZE]; try!(reader.read_exact(&mut code)); Request::LoadKernel(code) - }, + } 6 => Request::RunKernel, - 7 => Request::RpcReply { - tag: try!(read_string(reader)) - }, + 7 => { + let tag = try!(read_bytes(reader)); + let mut data = vec![0; length - HEADER_SIZE - 4 - tag.len()]; + try!(reader.read_exact(&mut data)); + Request::RpcReply { tag: tag, data: data } + } 8 => Request::RpcException(try!(Exception::read_from(reader))), 9 => Request::FlashRead { key: try!(read_string(reader)) @@ -152,7 +143,7 @@ pub enum Reply<'a> { KernelStartupFailed, KernelException(Exception), - RpcRequest { service: u32 }, + RpcRequest { service: u32, data: &'a [u8] }, FlashRead(&'a [u8]), FlashOk, @@ -204,9 +195,10 @@ impl<'a> Reply<'a> { try!(exception.write_to(writer)); }, - Reply::RpcRequest { service } => { + Reply::RpcRequest { service, data } => { try!(write_u8(&mut buf, 10)); try!(write_u32(&mut buf, service)); + try!(buf.write(data)); }, Reply::FlashRead(ref bytes) => { From 5a630067cbae096e9dd65e7dc6d6f671bbb5b4c7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 06:31:27 +0000 Subject: [PATCH 033/127] Rust: implement sending for all RPC types. --- artiq/runtime.rs/src/proto.rs | 14 +++ artiq/runtime.rs/src/rpc.rs | 130 ++++++++++++++++++++++---- artiq/runtime.rs/src/session_proto.rs | 11 --- 3 files changed, 126 insertions(+), 29 deletions(-) diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs index d7df40b8c..87746e494 100644 --- a/artiq/runtime.rs/src/proto.rs +++ b/artiq/runtime.rs/src/proto.rs @@ -2,6 +2,7 @@ use std::io::{self, Read, Write}; use std::vec::Vec; +use std::string::String; use byteorder::{ByteOrder, NetworkEndian}; // FIXME: replace these with byteorder core io traits once those are in @@ -63,3 +64,16 @@ pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { try!(write_u32(writer, value.len() as u32)); writer.write_all(value) } + +pub fn read_string(reader: &mut Read) -> io::Result { + let mut bytes = try!(read_bytes(reader)); + let len = bytes.len() - 1; // length without trailing \0 + bytes.resize(len, 0); // FIXME: don't send \0 in the first place + String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +} + +pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { + try!(write_u32(writer, (value.len() + 1) as u32)); + try!(writer.write_all(value.as_bytes())); + write_u8(writer, 0) +} diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc.rs index 5507a98e0..cc01c8d10 100644 --- a/artiq/runtime.rs/src/rpc.rs +++ b/artiq/runtime.rs/src/rpc.rs @@ -1,5 +1,5 @@ use std::io::{self, Read, Write}; -use proto::{write_u8, write_bytes}; +use proto::*; use self::tag::{Tag, TagIterator, split_tag}; fn recv_value(reader: &mut Read, tag: Tag, data: &mut *const ()) -> io::Result<()> { @@ -19,10 +19,88 @@ pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *const ()) -> io:: Ok(()) } -fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> { +pub unsafe fn from_c_str<'a>(ptr: *const u8) -> &'a str { + use core::{str, slice}; + extern { fn strlen(ptr: *const u8) -> usize; } + str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, strlen(ptr))) +} + +unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> { + macro_rules! consume_value { + ($ty:ty, |$ptr:ident| $map:expr) => ({ + let ptr = (*data) as *const $ty; + *data = ptr.offset(1) as *const (); + (|$ptr: *const $ty| $map)(ptr) + }) + } + + try!(write_u8(writer, tag.as_u8())); match tag { - Tag::None => write_u8(writer, b'n'), - _ => unreachable!() + Tag::None => Ok(()), + Tag::Bool => { + consume_value!(u8, |ptr| + write_u8(writer, *ptr)) + } + Tag::Int32 => { + consume_value!(u32, |ptr| + write_u32(writer, *ptr)) + } + Tag::Int64 => { + consume_value!(u64, |ptr| + write_u64(writer, *ptr)) + } + Tag::Float64 => { + consume_value!(u64, |ptr| + write_u64(writer, *ptr)) + } + Tag::String => { + consume_value!(*const u8, |ptr| + write_string(writer, from_c_str(*ptr))) + } + Tag::Tuple(it, arity) => { + let mut it = it.clone(); + try!(write_u8(writer, arity)); + for _ in 0..arity { + let tag = it.next().expect("truncated tag"); + try!(send_value(writer, tag, data)) + } + Ok(()) + } + Tag::List(it) | Tag::Array(it) => { + struct List { length: u32, elements: *const () }; + consume_value!(List, |ptr| { + try!(write_u32(writer, (*ptr).length)); + let tag = it.clone().next().expect("truncated tag"); + let mut data = (*ptr).elements; + for _ in 0..(*ptr).length as usize { + try!(send_value(writer, tag, &mut data)); + } + Ok(()) + }) + } + Tag::Range(it) => { + let tag = it.clone().next().expect("truncated tag"); + try!(send_value(writer, tag, data)); + try!(send_value(writer, tag, data)); + try!(send_value(writer, tag, data)); + Ok(()) + } + Tag::Keyword(it) => { + struct Keyword { name: *const u8, contents: () }; + consume_value!(Keyword, |ptr| { + try!(write_string(writer, from_c_str((*ptr).name))); + let tag = it.clone().next().expect("truncated tag"); + let mut data = &(*ptr).contents as *const (); + send_value(writer, tag, &mut data) + }) + // Tag::Keyword never appears in composite types, so we don't have + // to accurately advance data. + } + Tag::Object => { + struct Object { id: u32 }; + consume_value!(*const Object, |ptr| + write_u32(writer, (**ptr).id)) + } } } @@ -37,7 +115,7 @@ pub fn send_args(writer: &mut Write, tag_bytes: &[u8], for index in 0.. { if let Some(arg_tag) = args_it.next() { let mut data = unsafe { *data.offset(index) }; - try!(send_value(writer, arg_tag, &mut data)); + try!(unsafe { send_value(writer, arg_tag, &mut data) }); } else { break } @@ -61,7 +139,7 @@ mod tag { (arg_tags_bytes, return_tag_bytes) } - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] pub enum Tag<'a> { None, Bool, @@ -77,6 +155,25 @@ mod tag { Object } + impl<'a> Tag<'a> { + pub fn as_u8(self) -> u8 { + match self { + Tag::None => b'n', + Tag::Bool => b'b', + Tag::Int32 => b'i', + Tag::Int64 => b'I', + Tag::Float64 => b'f', + Tag::String => b's', + Tag::Tuple(_, _) => b't', + Tag::List(_) => b'l', + Tag::Array(_) => b'a', + Tag::Range(_) => b'r', + Tag::Keyword(_) => b'k', + Tag::Object => b'O', + } + } + } + #[derive(Debug, Clone, Copy)] pub struct TagIterator<'a> { data: &'a [u8] @@ -104,23 +201,23 @@ mod tag { b't' => { let count = self.data[0]; self.data = &self.data[1..]; - Tag::Tuple(self.skip(count), count) + Tag::Tuple(self.sub(count), count) } - b'l' => Tag::List(self.skip(1)), - b'a' => Tag::Array(self.skip(1)), - b'r' => Tag::Range(self.skip(1)), - b'k' => Tag::Keyword(self.skip(1)), + b'l' => Tag::List(self.sub(1)), + b'a' => Tag::Array(self.sub(1)), + b'r' => Tag::Range(self.sub(1)), + b'k' => Tag::Keyword(self.sub(1)), b'O' => Tag::Object, _ => unreachable!() }) } - fn skip(&mut self, count: u8) -> TagIterator<'a> { - let origin = self.clone(); + fn sub(&mut self, count: u8) -> TagIterator<'a> { + let data = self.data; for _ in 0..count { self.next().expect("truncated tag"); } - origin + TagIterator { data: &data[..(data.len() - self.data.len())] } } } @@ -150,10 +247,7 @@ mod tag { try!(write!(f, "String")), Tag::Tuple(it, cnt) => { try!(write!(f, "Tuple(")); - for i in 0..cnt { - try!(it.fmt(f)); - if i != cnt - 1 { try!(write!(f, ", ")) } - } + try!(it.fmt(f)); try!(write!(f, ")")) } Tag::List(it) => { diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index ba32fbc5a..fcac0b516 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -2,17 +2,6 @@ use std::prelude::v1::*; use std::io::{self, Read, Write}; use proto::*; -fn read_string(reader: &mut Read) -> io::Result { - let mut bytes = try!(read_bytes(reader)); - let len = bytes.len() - 1; // length without trailing \0 - bytes.resize(len, 0); // FIXME: don't send \0 in the first place - String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) -} - -fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { - write_bytes(writer, value.as_bytes()) -} - fn read_sync(reader: &mut Read) -> io::Result<()> { let mut sync = [0; 4]; for i in 0.. { From 516c6fdea9792171c6c9cb74cfcd7783daa484f9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 10:55:33 +0000 Subject: [PATCH 034/127] language: export TTuple. --- artiq/language/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/language/types.py b/artiq/language/types.py index e7e4e64bb..d146f929a 100644 --- a/artiq/language/types.py +++ b/artiq/language/types.py @@ -6,7 +6,7 @@ annotations. from artiq.compiler import types, builtins __all__ = ["TNone", "TBool", "TInt32", "TInt64", "TFloat", - "TStr", "TList", "TRange32", "TRange64", "TVar"] + "TStr", "TTuple", "TList", "TRange32", "TRange64", "TVar"] TNone = builtins.TNone() TBool = builtins.TBool() @@ -14,6 +14,7 @@ TInt32 = builtins.TInt(types.TValue(32)) TInt64 = builtins.TInt(types.TValue(64)) TFloat = builtins.TFloat() TStr = builtins.TStr() +TTuple = types.TTuple TList = builtins.TList TRange32 = builtins.TRange(builtins.TInt(types.TValue(32))) TRange64 = builtins.TRange(builtins.TInt(types.TValue(64))) From 3362887d75c519cb449022c99d2e50f1e7415dbe Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 10:55:54 +0000 Subject: [PATCH 035/127] Rust: implement receiving for all RPC types. --- artiq/runtime.rs/src/rpc.rs | 125 ++++++++++++++++++++++++++------ artiq/runtime.rs/src/session.rs | 12 ++- 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc.rs index cc01c8d10..beef28119 100644 --- a/artiq/runtime.rs/src/rpc.rs +++ b/artiq/runtime.rs/src/rpc.rs @@ -1,20 +1,84 @@ +use std::slice; use std::io::{self, Read, Write}; use proto::*; use self::tag::{Tag, TagIterator, split_tag}; -fn recv_value(reader: &mut Read, tag: Tag, data: &mut *const ()) -> io::Result<()> { +unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (), + alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> { + macro_rules! consume_value { + ($ty:ty, |$ptr:ident| $map:expr) => ({ + let ptr = (*data) as *mut $ty; + *data = ptr.offset(1) as *mut (); + (|$ptr: *mut $ty| $map)(ptr) + }) + } + match tag { Tag::None => Ok(()), - _ => unreachable!() + Tag::Bool => + consume_value!(u8, |ptr| { + *ptr = try!(read_u8(reader)); Ok(()) + }), + Tag::Int32 => + consume_value!(u32, |ptr| { + *ptr = try!(read_u32(reader)); Ok(()) + }), + Tag::Int64 | Tag::Float64 => + consume_value!(u64, |ptr| { + *ptr = try!(read_u64(reader)); Ok(()) + }), + Tag::String => { + consume_value!(*mut u8, |ptr| { + let length = try!(read_u32(reader)); + // NB: the received string includes a trailing \0 + *ptr = try!(alloc(length as usize)) as *mut u8; + try!(reader.read_exact(slice::from_raw_parts_mut(*ptr, length as usize))); + Ok(()) + }) + } + Tag::Tuple(it, arity) => { + let mut it = it.clone(); + for _ in 0..arity { + let tag = it.next().expect("truncated tag"); + try!(recv_value(reader, tag, data, alloc)) + } + Ok(()) + } + Tag::List(it) | Tag::Array(it) => { + struct List { length: u32, elements: *mut () }; + consume_value!(List, |ptr| { + (*ptr).length = try!(read_u32(reader)); + + let tag = it.clone().next().expect("truncated tag"); + (*ptr).elements = try!(alloc(tag.size() * (*ptr).length as usize)); + + let mut data = (*ptr).elements; + for _ in 0..(*ptr).length as usize { + try!(recv_value(reader, tag, &mut data, alloc)); + } + Ok(()) + }) + } + Tag::Range(it) => { + let tag = it.clone().next().expect("truncated tag"); + try!(recv_value(reader, tag, data, alloc)); + try!(recv_value(reader, tag, data, alloc)); + try!(recv_value(reader, tag, data, alloc)); + Ok(()) + } + Tag::Keyword(_) => unreachable!(), + Tag::Object => unreachable!() } } -pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *const ()) -> io::Result<()> { +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); trace!("recv ...->{}", it); + let tag = it.next().expect("truncated tag"); let mut data = data; - try!(recv_value(reader, it.next().expect("RPC without a return value"), &mut data)); + try!(unsafe { recv_value(reader, tag, &mut data, alloc) }); Ok(()) } @@ -37,26 +101,18 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: try!(write_u8(writer, tag.as_u8())); match tag { Tag::None => Ok(()), - Tag::Bool => { + Tag::Bool => consume_value!(u8, |ptr| - write_u8(writer, *ptr)) - } - Tag::Int32 => { + write_u8(writer, *ptr)), + Tag::Int32 => consume_value!(u32, |ptr| - write_u32(writer, *ptr)) - } - Tag::Int64 => { + write_u32(writer, *ptr)), + Tag::Int64 | Tag::Float64 => consume_value!(u64, |ptr| - write_u64(writer, *ptr)) - } - Tag::Float64 => { - consume_value!(u64, |ptr| - write_u64(writer, *ptr)) - } - Tag::String => { + write_u64(writer, *ptr)), + Tag::String => consume_value!(*const u8, |ptr| - write_string(writer, from_c_str(*ptr))) - } + write_string(writer, from_c_str(*ptr))), Tag::Tuple(it, arity) => { let mut it = it.clone(); try!(write_u8(writer, arity)); @@ -172,6 +228,33 @@ mod tag { Tag::Object => b'O', } } + + pub fn size(self) -> usize { + match self { + Tag::None => 0, + Tag::Bool => 1, + Tag::Int32 => 4, + Tag::Int64 => 8, + Tag::Float64 => 8, + Tag::String => 4, + Tag::Tuple(it, arity) => { + let mut size = 0; + for _ in 0..arity { + let tag = it.clone().next().expect("truncated tag"); + size += tag.size(); + } + size + } + Tag::List(_) => 8, + Tag::Array(_) => 8, + Tag::Range(it) => { + let tag = it.clone().next().expect("truncated tag"); + tag.size() * 3 + } + Tag::Keyword(_) => unreachable!(), + Tag::Object => unreachable!(), + } + } } #[derive(Debug, Clone, Copy)] @@ -245,7 +328,7 @@ mod tag { try!(write!(f, "Float64")), Tag::String => try!(write!(f, "String")), - Tag::Tuple(it, cnt) => { + Tag::Tuple(it, _) => { try!(write!(f, "Tuple(")); try!(it.fmt(f)); try!(write!(f, ")")) diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index d4b85ead6..f549c90a7 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -240,7 +240,17 @@ fn process_host_message(waiter: Waiter, match reply { kern::RpcRecvRequest { slot } => { let mut data = io::Cursor::new(data); - rpc::recv_return(&mut data, &tag, slot) + rpc::recv_return(&mut data, &tag, slot, &|size| { + try!(kern_send(waiter, kern::RpcRecvReply { + alloc_size: size, exception: None + })); + kern_recv(waiter, |reply| { + match reply { + kern::RpcRecvRequest { slot } => Ok(slot), + _ => unreachable!() + } + }) + }) } other => unexpected!("unexpected reply from kernel CPU: {:?}", other) From 2ae30b5a955ec1c356f610679eba8ef51863c11a Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 10:56:19 +0000 Subject: [PATCH 036/127] Rust: aggregate kernel CPU log messages and print line by line. --- artiq/runtime.rs/src/session.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index f549c90a7..0cb6fb7e5 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -55,7 +55,8 @@ enum KernelState { struct Session<'a> { congress: &'a mut Congress, kernel_state: KernelState, - watchdog_set: clock::WatchdogSet + watchdog_set: clock::WatchdogSet, + log_buffer: String } impl<'a> Session<'a> { @@ -63,7 +64,8 @@ impl<'a> Session<'a> { Session { congress: congress, kernel_state: KernelState::Absent, - watchdog_set: clock::WatchdogSet::new() + watchdog_set: clock::WatchdogSet::new(), + log_buffer: String::new() } } @@ -286,8 +288,16 @@ fn process_kern_message(waiter: Waiter, trace!("comm<-kern {:?}", request); match request { kern::Log(log) => { - info!(target: "kernel", "{}", log); - kern_acknowledge() + session.log_buffer += log; + try!(kern_acknowledge()); + + if &log[log.len() - 1..] == "\n" { + for line in session.log_buffer.lines() { + info!(target: "kernel", "{}", line); + } + session.log_buffer.clear() + } + Ok(()) } kern::NowInitRequest => From 6b2789e3dbc6d394a22a3746eb4e965ba459a207 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 12:46:05 +0000 Subject: [PATCH 037/127] test: add more RPC tests. --- artiq/test/coredevice/test_embedding.py | 72 +++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 988d23e73..683468b1d 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -70,7 +70,73 @@ class DefaultArgTest(ExperimentCase): self.assertEqual(exp.run(), 42) -class _RPC(EnvExperiment): +class _RPCTypes(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("led") + + def return_bool(self) -> TBool: + return True + + def return_int32(self) -> TInt32: + return 1 + + def return_int64(self) -> TInt64: + return 0x100000000 + + def return_float(self) -> TFloat: + return 1.0 + + def return_str(self) -> TStr: + return "foo" + + def return_tuple(self) -> TTuple([TInt32, TInt32]): + return (1, 2) + + def return_list(self) -> TList(TInt32): + return [2, 3] + + def return_range(self) -> TRange32: + return range(10) + + @kernel + def run_recv(self): + core_log(self.return_bool()) + core_log(self.return_int32()) + core_log(self.return_int64()) + core_log(self.return_float()) + core_log(self.return_str()) + core_log(self.return_tuple()) + core_log(self.return_list()) + core_log(self.return_range()) + + def accept(self, value): + pass + + @kernel + def run_send(self): + self.accept(True) + self.accept(1) + self.accept(0x100000000) + self.accept(1.0) + self.accept("foo") + self.accept((2, 3)) + self.accept([1, 2]) + self.accept(range(10)) + self.accept(self) + + def run(self): + self.run_send() + self.run_recv() + + +class RPCTypesTest(ExperimentCase): + def test_args(self): + exp = self.create(_RPCTypes) + exp.run() + + +class _RPCCalls(EnvExperiment): def build(self): self.setattr_device("core") @@ -121,9 +187,9 @@ class _RPC(EnvExperiment): sleep(1.0) -class RPCTest(ExperimentCase): +class RPCCallsTest(ExperimentCase): def test_args(self): - exp = self.create(_RPC) + exp = self.create(_RPCCalls) self.assertEqual(exp.args0(), 0) self.assertEqual(exp.args1(), 1) self.assertEqual(exp.args2(), 2) From 84214ab0d1bd1c04ae61faec1d13899b936a6739 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 12:55:19 +0000 Subject: [PATCH 038/127] Rust: don't panic when loading a kernel twice in same session. --- artiq/runtime.rs/src/session.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 0cb6fb7e5..a2e98d904 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -351,7 +351,10 @@ fn process_kern_message(waiter: Waiter, kern::RunFinished => { try!(kern_acknowledge()); + + kernel::stop(); session.kernel_state = KernelState::Absent; + return Ok(true) } From 226fa723bb57c79ca79ee79435d5901ef5922c8a Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 13:42:35 +0000 Subject: [PATCH 039/127] Rust: implement exceptional kernel termination. This also adjusts the way backtraces are serialized by kloader. --- artiq/runtime.rs/src/kernel.rs | 1 - artiq/runtime.rs/src/kernel_proto.rs | 13 +--- artiq/runtime.rs/src/session.rs | 37 +++++++++-- artiq/runtime.rs/src/session_proto.rs | 91 ++++++++++++++------------- artiq/runtime/artiq_personality.c | 5 +- artiq/runtime/artiq_personality.h | 7 +-- artiq/runtime/kloader.c | 16 ----- artiq/runtime/kloader.h | 4 -- artiq/runtime/ksupport.c | 15 ++++- artiq/runtime/messages.h | 2 +- artiq/runtime/session.c | 6 +- 11 files changed, 98 insertions(+), 99 deletions(-) diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index 050b57ec4..bffc2c12e 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -3,7 +3,6 @@ use board::csr; use mailbox; const KERNELCPU_EXEC_ADDRESS: usize = 0x42000000; -const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x42020000; const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024); const KSUPPORT_HEADER_SIZE: usize = 0x80; diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index eb8fb7c2a..b84a93df0 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -15,8 +15,6 @@ pub struct Exception<'a> { pub param: [u64; 3], } -pub use self::c::BacktraceItem; - #[derive(Debug)] pub enum Message<'a> { LoadRequest(&'a [u8]), @@ -29,7 +27,7 @@ pub enum Message<'a> { RunFinished, RunException { exception: Exception<'a>, - backtrace: &'a [BacktraceItem] + backtrace: &'a [usize] }, WatchdogSetRequest { ms: u64 }, @@ -315,7 +313,7 @@ mod c { pub struct RunException { pub ty: Type, pub exception: *const Exception, - pub backtrace: *const BacktraceItem, + pub backtrace: *const usize, pub backtrace_size: size_t } @@ -417,13 +415,6 @@ mod c { pub param: [u64; 3], } - #[repr(C)] - #[derive(Debug)] - pub struct BacktraceItem { - pub function: usize, - pub offset: usize - } - pub unsafe fn from_c_str_len<'a>(ptr: *const c_char, len: size_t) -> &'a str { str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, len)) } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index a2e98d904..2f04926d7 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -350,12 +350,39 @@ fn process_kern_message(waiter: Waiter, } kern::RunFinished => { - try!(kern_acknowledge()); - kernel::stop(); session.kernel_state = KernelState::Absent; - return Ok(true) + match stream { + None => return Ok(true), + Some(ref mut stream) => + host_write(stream, host::Reply::KernelFinished) + } + } + + kern::RunException { exception: ref exn, backtrace } => { + kernel::stop(); + session.kernel_state = KernelState::Absent; + + match stream { + None => { + error!("exception in flash kernel"); + error!("{}: {} {:?}", exn.name, exn.message, exn.param); + error!("at {}:{}:{} in {}", exn.file, exn.line, exn.column, exn.function); + return Ok(true) + }, + Some(ref mut stream) => + host_write(stream, host::Reply::KernelException { + name: exn.name, + message: exn.message, + param: exn.param, + file: exn.file, + line: exn.line, + column: exn.column, + function: exn.function, + backtrace: backtrace + }) + } } request => unexpected!("unexpected request {:?} from kernel CPU", request) @@ -374,9 +401,7 @@ fn host_kernel_worker(waiter: Waiter, } if mailbox::receive() != 0 { - if try!(process_kern_message(waiter, Some(stream), &mut session)) { - try!(host_write(stream, host::Reply::KernelFinished)) - } + try!(process_kern_message(waiter, Some(stream), &mut session)); } if session.kernel_state == KernelState::Running { diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index fcac0b516..b9f7d92fc 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -15,46 +15,6 @@ fn write_sync(writer: &mut Write) -> io::Result<()> { writer.write_all(&[0x5a; 4]) } -#[derive(Debug)] -pub struct Exception { - name: String, - message: String, - param: [u64; 3], - file: String, - line: u32, - column: u32, - function: String, -} - -impl Exception { - pub fn read_from(reader: &mut Read) -> io::Result { - Ok(Exception { - name: try!(read_string(reader)), - message: try!(read_string(reader)), - param: [try!(read_u64(reader)), - try!(read_u64(reader)), - try!(read_u64(reader))], - file: try!(read_string(reader)), - line: try!(read_u32(reader)), - column: try!(read_u32(reader)), - function: try!(read_string(reader)) - }) - } - - pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { - try!(write_string(writer, &self.name)); - try!(write_string(writer, &self.message)); - try!(write_u64(writer, self.param[0])); - try!(write_u64(writer, self.param[1])); - try!(write_u64(writer, self.param[2])); - try!(write_string(writer, &self.file)); - try!(write_u32(writer, self.line)); - try!(write_u32(writer, self.column)); - try!(write_string(writer, &self.function)); - Ok(()) - } -} - #[derive(Debug)] pub enum Request { Log, @@ -67,7 +27,15 @@ pub enum Request { RunKernel, RpcReply { tag: Vec, data: Vec }, - RpcException(Exception), + RpcException { + name: String, + message: String, + param: [u64; 3], + file: String, + line: u32, + column: u32, + function: String, + }, FlashRead { key: String }, FlashWrite { key: String, value: Vec }, @@ -100,7 +68,17 @@ impl Request { try!(reader.read_exact(&mut data)); Request::RpcReply { tag: tag, data: data } } - 8 => Request::RpcException(try!(Exception::read_from(reader))), + 8 => Request::RpcException { + name: try!(read_string(reader)), + message: try!(read_string(reader)), + param: [try!(read_u64(reader)), + try!(read_u64(reader)), + try!(read_u64(reader))], + file: try!(read_string(reader)), + line: try!(read_u32(reader)), + column: try!(read_u32(reader)), + function: try!(read_string(reader)) + }, 9 => Request::FlashRead { key: try!(read_string(reader)) }, @@ -130,7 +108,16 @@ pub enum Reply<'a> { KernelFinished, KernelStartupFailed, - KernelException(Exception), + KernelException { + name: &'a str, + message: &'a str, + param: [u64; 3], + file: &'a str, + line: u32, + column: u32, + function: &'a str, + backtrace: &'a [usize] + }, RpcRequest { service: u32, data: &'a [u8] }, @@ -179,9 +166,23 @@ impl<'a> Reply<'a> { Reply::KernelStartupFailed => { try!(write_u8(&mut buf, 8)); }, - Reply::KernelException(ref exception) => { + Reply::KernelException { + name, message, param, file, line, column, function, backtrace + } => { try!(write_u8(&mut buf, 9)); - try!(exception.write_to(writer)); + try!(write_string(&mut buf, name)); + try!(write_string(&mut buf, message)); + try!(write_u64(&mut buf, param[0])); + try!(write_u64(&mut buf, param[1])); + try!(write_u64(&mut buf, param[2])); + try!(write_string(&mut buf, file)); + try!(write_u32(&mut buf, line)); + try!(write_u32(&mut buf, column)); + try!(write_string(&mut buf, function)); + try!(write_u32(&mut buf, backtrace.len() as u32)); + for &addr in backtrace { + try!(write_u32(&mut buf, addr as u32)) + } }, Reply::RpcRequest { service, data } => { diff --git a/artiq/runtime/artiq_personality.c b/artiq/runtime/artiq_personality.c index 3d52f3c30..2cc5704b4 100644 --- a/artiq/runtime/artiq_personality.c +++ b/artiq/runtime/artiq_personality.c @@ -233,7 +233,7 @@ struct artiq_raised_exception { struct _Unwind_Exception unwind; struct artiq_exception artiq; int handled; - struct artiq_backtrace_item backtrace[1024]; + uintptr_t backtrace[1024]; size_t backtrace_size; }; @@ -303,8 +303,7 @@ static _Unwind_Reason_Code __artiq_uncaught_exception( uintptr_t pcOffset = pc - funcStart; EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset); - inflight->backtrace[inflight->backtrace_size].function = funcStart; - inflight->backtrace[inflight->backtrace_size].offset = pcOffset; + inflight->backtrace[inflight->backtrace_size] = funcStart + pcOffset; ++inflight->backtrace_size; if(actions & _UA_END_OF_STACK) { diff --git a/artiq/runtime/artiq_personality.h b/artiq/runtime/artiq_personality.h index 0ddf9e88f..b1ed1b8d9 100644 --- a/artiq/runtime/artiq_personality.h +++ b/artiq/runtime/artiq_personality.h @@ -17,11 +17,6 @@ struct artiq_exception { int64_t param[3]; }; -struct artiq_backtrace_item { - intptr_t function; - intptr_t offset; -}; - #ifdef __cplusplus extern "C" { #endif @@ -48,7 +43,7 @@ void __artiq_reraise(void) /* Called by the runtime */ void __artiq_terminate(struct artiq_exception *artiq_exn, - struct artiq_backtrace_item *backtrace, + uintptr_t *backtrace, size_t backtrace_size) __attribute__((noreturn)); diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c index 3dc98e539..04cd6b90a 100644 --- a/artiq/runtime/kloader.c +++ b/artiq/runtime/kloader.c @@ -58,22 +58,6 @@ void kloader_start_kernel() mailbox_acknowledge(); } -void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, - size_t *backtrace_size) { - struct artiq_backtrace_item *cursor = backtrace; - - // Remove all backtrace items belonging to ksupport and subtract - // shared object base from the addresses. - for(int i = 0; i < *backtrace_size; i++) { - if(backtrace[i].function > KERNELCPU_PAYLOAD_ADDRESS) { - backtrace[i].function -= KERNELCPU_PAYLOAD_ADDRESS; - *cursor++ = backtrace[i]; - } - } - - *backtrace_size = cursor - backtrace; -} - static int kloader_start_flash_kernel(char *key) { #if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) diff --git a/artiq/runtime/kloader.h b/artiq/runtime/kloader.h index 2ca8d6a6f..a330c4660 100644 --- a/artiq/runtime/kloader.h +++ b/artiq/runtime/kloader.h @@ -1,16 +1,12 @@ #ifndef __KLOADER_H #define __KLOADER_H -#include "artiq_personality.h" - #define KERNELCPU_EXEC_ADDRESS 0x42000000 #define KERNELCPU_PAYLOAD_ADDRESS 0x42020000 #define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) #define KSUPPORT_HEADER_SIZE 0x80 int kloader_load_library(const void *code); -void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace, - size_t *backtrace_size); int kloader_start_startup_kernel(void); int kloader_start_idle_kernel(void); diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 7d5b5d21a..ff454440f 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -415,13 +415,26 @@ int main(void) /* called from __artiq_personality */ void __artiq_terminate(struct artiq_exception *artiq_exn, - struct artiq_backtrace_item *backtrace, + uintptr_t *backtrace, size_t backtrace_size) { struct msg_exception msg; now_save(); + uintptr_t *cursor = backtrace; + + // Remove all backtrace items belonging to ksupport and subtract + // shared object base from the addresses. + for(int i = 0; i < backtrace_size; i++) { + if(backtrace[i] > KERNELCPU_PAYLOAD_ADDRESS) { + backtrace[i] -= KERNELCPU_PAYLOAD_ADDRESS; + *cursor++ = backtrace[i]; + } + } + + backtrace_size = cursor - backtrace; + msg.type = MESSAGE_TYPE_EXCEPTION; msg.exception = artiq_exn; msg.backtrace = backtrace; diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h index 30b5bba1d..a19190cfd 100644 --- a/artiq/runtime/messages.h +++ b/artiq/runtime/messages.h @@ -56,7 +56,7 @@ struct msg_now_save { struct msg_exception { int type; struct artiq_exception *exception; - struct artiq_backtrace_item *backtrace; + uintptr_t *backtrace; size_t backtrace_size; }; diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c index 1cb361bed..054d808e1 100644 --- a/artiq/runtime/session.c +++ b/artiq/runtime/session.c @@ -973,13 +973,9 @@ static int process_kmsg(struct msg_base *umsg) out_packet_int32(msg->exception->column); out_packet_string(msg->exception->function); - kloader_filter_backtrace(msg->backtrace, - &msg->backtrace_size); - out_packet_int32(msg->backtrace_size); for(int i = 0; i < msg->backtrace_size; i++) { - struct artiq_backtrace_item *item = &msg->backtrace[i]; - out_packet_int32(item->function + item->offset); + out_packet_int32(msg->backtrace[i]); } out_packet_finish(); From e92f20546a8416db5cfdeec2332ea387b55b670e Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 14:08:02 +0000 Subject: [PATCH 040/127] Rust: implement exceptional RPC replies. --- artiq/runtime.rs/src/kernel_proto.rs | 2 +- artiq/runtime.rs/src/session.rs | 31 +++++++++++++++++++++++++++- artiq/runtime/Makefile | 4 ++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index b84a93df0..64a074518 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -110,7 +110,7 @@ impl<'a> Message<'a> { let msg = c::RpcRecvReply { ty: c::Type::RpcRecvReply, alloc_size: alloc_size as _, - exception: exn.map_or(ptr::null(), |exn| &exn as *const _) + exception: exn.as_ref().map_or(ptr::null(), |exn| exn as *const _) }; f(&msg as *const _ as *const _) } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 2f04926d7..644381ea8 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -264,7 +264,36 @@ fn process_host_message(waiter: Waiter, Ok(()) } - request => unexpected!("unexpected request {:?} from host machine", request) + host::Request::RpcException { + name, message, param, file, line, column, function + } => { + if session.kernel_state != KernelState::RpcWait { + unexpected!("unsolicited RPC reply") + } + + try!(kern_recv(waiter, |reply| { + match reply { + kern::RpcRecvRequest { .. } => Ok(()), + other => + unexpected!("unexpected reply from kernel CPU: {:?}", other) + } + })); + try!(kern_send(waiter, kern::RpcRecvReply { + alloc_size: 0, + exception: Some(kern::Exception { + name: &name, + message: &message, + param: param, + file: &file, + line: line, + column: column, + function: &function + }) + })); + + session.kernel_state = KernelState::Running; + Ok(()) + } } } diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 30a8b1d7f..0f8aec3cd 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -15,8 +15,8 @@ CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ -I$(LIBLWIP_DIRECTORY)/../lwip/src/include \ - -I$(LIBLWIP_DIRECTORY) \ - -DNDEBUG + -I$(LIBLWIP_DIRECTORY) +CFLAGS += -DNDEBUG all: runtime.bin runtime.fbi From 3e829d0d01ead9b32d26a30895e0402237e753ca Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 14:19:12 +0000 Subject: [PATCH 041/127] Rust: unborrow cache after kernel stops. --- artiq/runtime.rs/src/cache.rs | 6 ++++++ artiq/runtime.rs/src/session.rs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/artiq/runtime.rs/src/cache.rs b/artiq/runtime.rs/src/cache.rs index cc755bab6..4136b96f3 100644 --- a/artiq/runtime.rs/src/cache.rs +++ b/artiq/runtime.rs/src/cache.rs @@ -44,4 +44,10 @@ impl Cache { }); Ok(()) } + + pub unsafe fn unborrow(&mut self) { + for (_key, entry) in self.entries.iter_mut() { + entry.borrowed = false; + } + } } diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 644381ea8..5b135b226 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -381,6 +381,7 @@ fn process_kern_message(waiter: Waiter, kern::RunFinished => { kernel::stop(); session.kernel_state = KernelState::Absent; + unsafe { session.congress.cache.unborrow() } match stream { None => return Ok(true), @@ -392,6 +393,7 @@ fn process_kern_message(waiter: Waiter, kern::RunException { exception: ref exn, backtrace } => { kernel::stop(); session.kernel_state = KernelState::Absent; + unsafe { session.congress.cache.unborrow() } match stream { None => { From 5428a866b3a61477f55d1e119d80710f53dc4fc7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 15:35:43 +0000 Subject: [PATCH 042/127] runtime: the Rust runtime is now just the runtime. --- artiq/runtime.rs/libstd_artiq/lib.rs | 1 - .../runtime.rs/libstd_artiq/time/duration.rs | 117 -- artiq/runtime.rs/libstd_artiq/time/instant.rs | 81 -- artiq/runtime.rs/libstd_artiq/time/mod.rs | 5 - artiq/runtime.rs/src/lib.rs | 10 + artiq/runtime.rs/src/sched.rs | 10 +- artiq/runtime/Makefile | 4 +- artiq/runtime/analyzer.c | 158 --- artiq/runtime/analyzer.h | 14 - artiq/runtime/clock.c | 89 -- artiq/runtime/clock.h | 15 - artiq/runtime/dds.c | 1 - artiq/runtime/flash_storage.c | 23 +- artiq/runtime/kloader.c | 179 --- artiq/runtime/kloader.h | 20 - artiq/runtime/ksupport.c | 6 +- artiq/runtime/log.c | 51 - artiq/runtime/log.h | 13 - artiq/runtime/main.c | 67 +- artiq/runtime/moninj.c | 166 --- artiq/runtime/moninj.h | 6 - artiq/runtime/net_server.c | 227 ---- artiq/runtime/net_server.h | 26 - artiq/runtime/rtiocrg.c | 71 -- artiq/runtime/rtiocrg.h | 8 - artiq/runtime/session.c | 1136 ----------------- artiq/runtime/session.h | 13 - 27 files changed, 33 insertions(+), 2484 deletions(-) delete mode 100644 artiq/runtime.rs/libstd_artiq/time/duration.rs delete mode 100644 artiq/runtime.rs/libstd_artiq/time/instant.rs delete mode 100644 artiq/runtime.rs/libstd_artiq/time/mod.rs delete mode 100644 artiq/runtime/analyzer.c delete mode 100644 artiq/runtime/analyzer.h delete mode 100644 artiq/runtime/clock.c delete mode 100644 artiq/runtime/clock.h delete mode 100644 artiq/runtime/kloader.c delete mode 100644 artiq/runtime/kloader.h delete mode 100644 artiq/runtime/log.c delete mode 100644 artiq/runtime/log.h delete mode 100644 artiq/runtime/moninj.c delete mode 100644 artiq/runtime/moninj.h delete mode 100644 artiq/runtime/net_server.c delete mode 100644 artiq/runtime/net_server.h delete mode 100644 artiq/runtime/rtiocrg.c delete mode 100644 artiq/runtime/rtiocrg.h delete mode 100644 artiq/runtime/session.c delete mode 100644 artiq/runtime/session.h diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 9e95c2a24..e2fe3b9d8 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -29,7 +29,6 @@ pub mod prelude { } } -pub mod time; pub mod error; pub mod io; diff --git a/artiq/runtime.rs/libstd_artiq/time/duration.rs b/artiq/runtime.rs/libstd_artiq/time/duration.rs deleted file mode 100644 index c3d7bd1ed..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/duration.rs +++ /dev/null @@ -1,117 +0,0 @@ -use core::ops::{Add, Sub, Mul, Div}; - -const MILLIS_PER_SEC: u64 = 1_000; -const NANOS_PER_MILLI: u32 = 1_000_000; - -/// A duration type to represent a span of time, typically used for system -/// timeouts. -/// -/// Each duration is composed of a number of seconds and nanosecond precision. -/// APIs binding a system timeout will typically round up the nanosecond -/// precision if the underlying system does not support that level of precision. -/// -/// Durations implement many common traits, including `Add`, `Sub`, and other -/// ops traits. Currently a duration may only be inspected for its number of -/// seconds and its nanosecond precision. -/// -/// # Examples -/// -/// ``` -/// use std::time::Duration; -/// -/// let five_seconds = Duration::new(5, 0); -/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); -/// -/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5); -/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5); -/// -/// let ten_millis = Duration::from_millis(10); -/// ``` -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Duration { - millis: u64 -} - -impl Duration { - /// Creates a new `Duration` from the specified number of seconds and - /// additional nanosecond precision. - /// - /// If the nanoseconds is greater than 1 billion (the number of nanoseconds - /// in a second), then it will carry over into the seconds provided. - pub fn new(secs: u64, nanos: u32) -> Duration { - Duration { millis: secs * MILLIS_PER_SEC + (nanos / NANOS_PER_MILLI) as u64 } - } - - /// Creates a new `Duration` from the specified number of seconds. - pub fn from_secs(secs: u64) -> Duration { - Duration { millis: secs * MILLIS_PER_SEC } - } - - /// Creates a new `Duration` from the specified number of milliseconds. - pub fn from_millis(millis: u64) -> Duration { - Duration { millis: millis } - } - - /// Returns the number of whole milliseconds represented by this duration. - pub fn as_millis(&self) -> u64 { self.millis } - - /// Returns the number of whole seconds represented by this duration. - /// - /// The extra precision represented by this duration is ignored (e.g. extra - /// nanoseconds are not represented in the returned value). - pub fn as_secs(&self) -> u64 { - self.millis / MILLIS_PER_SEC - } - - /// Returns the nanosecond precision represented by this duration. - /// - /// This method does **not** return the length of the duration when - /// represented by nanoseconds. The returned number always represents a - /// fractional portion of a second (e.g. it is less than one billion). - pub fn subsec_nanos(&self) -> u32 { - (self.millis % MILLIS_PER_SEC) as u32 * NANOS_PER_MILLI - } -} - -impl Add for Duration { - type Output = Duration; - - fn add(self, rhs: Duration) -> Duration { - Duration { - millis: self.millis.checked_add(rhs.millis) - .expect("overflow when adding durations") - } - } -} - -impl Sub for Duration { - type Output = Duration; - - fn sub(self, rhs: Duration) -> Duration { - Duration { - millis: self.millis.checked_sub(rhs.millis) - .expect("overflow when subtracting durations") - } - } -} - -impl Mul for Duration { - type Output = Duration; - - fn mul(self, rhs: u32) -> Duration { - Duration { - millis: self.millis.checked_mul(rhs as u64) - .expect("overflow when multiplying duration") - } - } -} - -impl Div for Duration { - type Output = Duration; - - fn div(self, rhs: u32) -> Duration { - Duration { - millis: self.millis / (rhs as u64) - } - } -} diff --git a/artiq/runtime.rs/libstd_artiq/time/instant.rs b/artiq/runtime.rs/libstd_artiq/time/instant.rs deleted file mode 100644 index 2282b0f5d..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/instant.rs +++ /dev/null @@ -1,81 +0,0 @@ -use core::ops::{Add, Sub}; -use time::duration::Duration; - -/// A measurement of a monotonically increasing clock. -/// -/// Instants are always guaranteed to be greater than any previously measured -/// instant when created, and are often useful for tasks such as measuring -/// benchmarks or timing how long an operation takes. -/// -/// Note, however, that instants are not guaranteed to be **steady**. In other -/// words, each tick of the underlying clock may not be the same length (e.g. -/// some seconds may be longer than others). An instant may jump forwards or -/// experience time dilation (slow down or speed up), but it will never go -/// backwards. -/// -/// Instants are opaque types that can only be compared to one another. There is -/// no method to get "the number of seconds" from an instant. Instead, it only -/// allows measuring the duration between two instants (or comparing two -/// instants). -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Instant { - millis: u64 -} - -impl Instant { - /// Returns an instant corresponding to "now". - pub fn now() -> Instant { - extern { - fn clock_get_ms() -> i64; - } - - Instant { millis: unsafe { clock_get_ms() as u64 } } - } - - /// Returns the amount of time elapsed from another instant to this one. - /// - /// # Panics - /// - /// This function will panic if `earlier` is later than `self`, which should - /// only be possible if `earlier` was created after `self`. Because - /// `Instant` is monotonic, the only time that this should happen should be - /// a bug. - pub fn duration_from_earlier(&self, earlier: Instant) -> Duration { - let millis = self.millis.checked_sub(earlier.millis) - .expect("`earlier` is later than `self`"); - Duration::from_millis(millis) - } - - /// Returns the amount of time elapsed since this instant was created. - /// - /// # Panics - /// - /// This function may panic if the current time is earlier than this - /// instant, which is something that can happen if an `Instant` is - /// produced synthetically. - pub fn elapsed(&self) -> Duration { - Instant::now().duration_from_earlier(*self) - } -} - -impl Add for Instant { - type Output = Instant; - - fn add(self, other: Duration) -> Instant { - Instant { - millis: self.millis.checked_add(other.as_millis()) - .expect("overflow when adding duration to instant") - } - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - Instant { - millis: self.millis.checked_sub(other.as_millis()) - .expect("overflow when subtracting duration from instant") - } - } -} diff --git a/artiq/runtime.rs/libstd_artiq/time/mod.rs b/artiq/runtime.rs/libstd_artiq/time/mod.rs deleted file mode 100644 index c1269983d..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use self::duration::Duration; -pub use self::instant::Instant; - -mod duration; -mod instant; diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index a9da393ec..45728f469 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -69,3 +69,13 @@ pub unsafe extern fn rust_main() { } }) } + +#[no_mangle] +pub fn sys_now() -> u32 { + clock::get_ms() as u32 +} + +#[no_mangle] +pub fn sys_jiffies() -> u32 { + clock::get_ms() as u32 +} diff --git a/artiq/runtime.rs/src/sched.rs b/artiq/runtime.rs/src/sched.rs index 1e84cd73e..4e698f94b 100644 --- a/artiq/runtime.rs/src/sched.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -2,16 +2,16 @@ use std::cell::RefCell; use std::vec::Vec; -use std::time::{Instant, Duration}; use std::io::{Read, Write, Result, Error, ErrorKind}; use fringe::OwnedStack; use fringe::generator::{Generator, Yielder, State as GeneratorState}; use lwip; +use clock; use urc::Urc; #[derive(Debug)] struct WaitRequest { - timeout: Option, + timeout: Option, event: Option } @@ -106,7 +106,7 @@ impl Scheduler { if self.threads.len() == 0 { return } - let now = Instant::now(); + let now = clock::get_ms(); let start_index = self.index; loop { @@ -214,9 +214,9 @@ unsafe impl Send for WaitEvent {} pub struct Waiter<'a>(&'a Yielder); impl<'a> Waiter<'a> { - pub fn sleep(&self, duration: Duration) -> Result<()> { + pub fn sleep(&self, duration_ms: u64) -> Result<()> { let request = WaitRequest { - timeout: Some(Instant::now() + duration), + timeout: Some(clock::get_ms() + duration_ms), event: None }; diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 0f8aec3cd..5bc5325f4 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -3,9 +3,7 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 -OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ - session.o log.o analyzer.o moninj.o net_server.o \ - ksupport_data.o kloader.o main.o +OBJECTS := isr.o flash_storage.o ksupport_data.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ rtio.o dds.o i2c.o diff --git a/artiq/runtime/analyzer.c b/artiq/runtime/analyzer.c deleted file mode 100644 index 84e15b929..000000000 --- a/artiq/runtime/analyzer.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include - -#include "log.h" -#include "analyzer.h" - - -#ifdef CSR_RTIO_ANALYZER_BASE - -struct analyzer_header { - unsigned int sent_bytes; - unsigned long long int total_byte_count; - unsigned char overflow_occured; - unsigned char log_channel; - unsigned char dds_onehot_sel; -} __attribute__((packed)); - - -#define ANALYZER_BUFFER_SIZE (512*1024) - -static struct analyzer_header analyzer_header; -static char analyzer_buffer[ANALYZER_BUFFER_SIZE] __attribute__((aligned(64))); - -static void arm(void) -{ - rtio_analyzer_message_encoder_overflow_reset_write(1); - rtio_analyzer_dma_base_address_write((unsigned int)analyzer_buffer); - rtio_analyzer_dma_last_address_write((unsigned int)analyzer_buffer + ANALYZER_BUFFER_SIZE - 1); - rtio_analyzer_dma_reset_write(1); - rtio_analyzer_enable_write(1); -} - -static void disarm(void) -{ - rtio_analyzer_enable_write(0); - while(rtio_analyzer_busy_read()); - flush_cpu_dcache(); - flush_l2_cache(); -} - -void analyzer_init(void) -{ - arm(); -} - -enum { - SEND_STATE_HEADER, - SEND_STATE_POST_POINTER, /* send from pointer to end of buffer */ - SEND_STATE_PRE_POINTER, /* send from start of buffer to pointer-1 */ - SEND_STATE_TERMINATE -}; - -static int send_state; -static int pointer; -static int wraparound; -static int offset_consumed; -static int offset_sent; - -void analyzer_start(void) -{ - disarm(); - - analyzer_header.total_byte_count = rtio_analyzer_dma_byte_count_read(); - pointer = analyzer_header.total_byte_count % ANALYZER_BUFFER_SIZE; - wraparound = analyzer_header.total_byte_count >= ANALYZER_BUFFER_SIZE; - - if(wraparound) - analyzer_header.sent_bytes = ANALYZER_BUFFER_SIZE; - else - analyzer_header.sent_bytes = analyzer_header.total_byte_count; - - analyzer_header.overflow_occured = rtio_analyzer_message_encoder_overflow_read(); - analyzer_header.log_channel = CONFIG_RTIO_LOG_CHANNEL; -#ifdef CONFIG_DDS_ONEHOT_SEL - analyzer_header.dds_onehot_sel = 1; -#else - analyzer_header.dds_onehot_sel = 0; -#endif - - offset_consumed = 0; - offset_sent = 0; - send_state = SEND_STATE_HEADER; -} - -void analyzer_end(void) -{ - arm(); -} - -int analyzer_input(void *data, int length) -{ - core_log("no input should be received by analyzer, dropping connection\n"); - return -1; -} - -void analyzer_poll(void **data, int *length, int *close_flag) -{ - *close_flag = 0; - switch(send_state) { - case SEND_STATE_HEADER: - *length = sizeof(struct analyzer_header) - offset_consumed; - *data = (char *)&analyzer_header + offset_consumed; - break; - case SEND_STATE_POST_POINTER: - *length = ANALYZER_BUFFER_SIZE - pointer - offset_consumed; - *data = analyzer_buffer + pointer + offset_consumed; - break; - case SEND_STATE_PRE_POINTER: - *length = pointer - offset_consumed; - *data = analyzer_buffer + offset_consumed; - break; - case SEND_STATE_TERMINATE: - *length = -1; - break; - default: - *length = 0; - break; - } -} - -void analyzer_ack_consumed(int length) -{ - offset_consumed += length; -} - -void analyzer_ack_sent(int length) -{ - offset_sent += length; - switch(send_state) { - case SEND_STATE_HEADER: - if(offset_sent >= sizeof(struct analyzer_header)) { - offset_consumed = 0; - offset_sent = 0; - if(wraparound) - send_state = SEND_STATE_POST_POINTER; - else { - if(pointer) - send_state = SEND_STATE_PRE_POINTER; - else - send_state = SEND_STATE_TERMINATE; - } - } - break; - case SEND_STATE_POST_POINTER: - if(pointer + offset_consumed >= ANALYZER_BUFFER_SIZE) { - offset_consumed = 0; - offset_sent = 0; - send_state = SEND_STATE_PRE_POINTER; - } - break; - case SEND_STATE_PRE_POINTER: - if(offset_sent >= pointer) - send_state = SEND_STATE_TERMINATE; - break; - } -} - -#endif diff --git a/artiq/runtime/analyzer.h b/artiq/runtime/analyzer.h deleted file mode 100644 index c94b664b2..000000000 --- a/artiq/runtime/analyzer.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __ANALYZER_H -#define __ANALYZER_H - -void analyzer_init(void); - -void analyzer_start(void); -void analyzer_end(void); - -int analyzer_input(void *data, int length); -void analyzer_poll(void **data, int *length, int *close_flag); -void analyzer_ack_consumed(int length); -void analyzer_ack_sent(int length); - -#endif /* __ANALYZER_H */ diff --git a/artiq/runtime/clock.c b/artiq/runtime/clock.c deleted file mode 100644 index bbcf87d7c..000000000 --- a/artiq/runtime/clock.c +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#include "log.h" -#include "clock.h" - - -void clock_init(void) -{ - timer0_en_write(0); - timer0_load_write(0x7fffffffffffffffLL); - timer0_reload_write(0x7fffffffffffffffLL); - timer0_en_write(1); -} - -long long int clock_get_ms(void) -{ - long long int clock_sys; - long long int clock_ms; - - timer0_update_value_write(1); - clock_sys = 0x7fffffffffffffffLL - timer0_value_read(); - - clock_ms = clock_sys/(CONFIG_CLOCK_FREQUENCY/1000); - return clock_ms; -} - -void busywait_us(long long int us) -{ - long long int threshold; - - timer0_update_value_write(1); - threshold = timer0_value_read() - us*CONFIG_CLOCK_FREQUENCY/1000000LL; - while(timer0_value_read() > threshold) - timer0_update_value_write(1); -} - -struct watchdog { - int active; - long long int threshold; -}; - -static struct watchdog watchdogs[MAX_WATCHDOGS]; - -void watchdog_init(void) -{ - int i; - - for(i=0;i= MAX_WATCHDOGS)) - return; - watchdogs[id].active = 0; -} - -int watchdog_expired(void) -{ - int i; - long long int t; - - t = 0x7fffffffffffffffLL; - for(i=0;i t; -} diff --git a/artiq/runtime/clock.h b/artiq/runtime/clock.h deleted file mode 100644 index 4f4895443..000000000 --- a/artiq/runtime/clock.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __CLOCK_H -#define __CLOCK_H - -void clock_init(void); -long long int clock_get_ms(void); -void busywait_us(long long us); - -#define MAX_WATCHDOGS 16 - -void watchdog_init(void); -int watchdog_set(int ms); -void watchdog_clear(int id); -int watchdog_expired(void); - -#endif /* __CLOCK_H */ diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index 718d32bcd..5b221ecf9 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -6,7 +6,6 @@ #include "artiq_personality.h" #include "rtio.h" -#include "log.h" #include "dds.h" #define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH) diff --git a/artiq/runtime/flash_storage.c b/artiq/runtime/flash_storage.c index dfa74906e..3e18151d6 100644 --- a/artiq/runtime/flash_storage.c +++ b/artiq/runtime/flash_storage.c @@ -9,7 +9,6 @@ #include #include -#include "log.h" #include "flash_storage.h" #if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) @@ -63,24 +62,24 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f return 0; if(record->size < 6) { - core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n", - record->size, record->raw_record); + // core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n", + // record->size, record->raw_record); if(fatal) *fatal = 1; return 0; } if(is->seek > is->buf_len - sizeof(record->size) - 2) { /* 2 is the minimum key length */ - core_log("flash_storage might be corrupted: END_MARKER missing at the end of " - "the storage sector\n"); + // core_log("flash_storage might be corrupted: END_MARKER missing at the end of " + // "the storage sector\n"); if(fatal) *fatal = 1; return 0; } if(record->size > is->buf_len - is->seek) { - core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n", - record->size, record->raw_record); + // core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n", + // record->size, record->raw_record); if(fatal) *fatal = 1; return 0; @@ -90,8 +89,8 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f record->key_len = strnlen(record->key, record->size - sizeof(record->size)) + 1; if(record->key_len == record->size - sizeof(record->size) + 1) { - core_log("flash_storage might be corrupted: invalid key length at address %08x\n", - record->raw_record); + // core_log("flash_storage might be corrupted: invalid key length at address %08x\n", + // record->raw_record); if(fatal) *fatal = 1; return 0; @@ -265,7 +264,7 @@ int fs_write(const char *key, const void *buffer, unsigned int buf_len) return 0; // Storage is definitely full. fatal_error: - core_log("fatal error: flash storage might be corrupted\n"); + // core_log("fatal error: flash storage might be corrupted\n"); return 0; } @@ -295,8 +294,8 @@ unsigned int fs_read(const char *key, void *buffer, unsigned int buf_len, unsign } } - if(fatal) - core_log("fatal error: flash storage might be corrupted\n"); + // if(fatal) + // core_log("fatal error: flash storage might be corrupted\n"); return read_length; } diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c deleted file mode 100644 index 04cd6b90a..000000000 --- a/artiq/runtime/kloader.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include - -#include "kloader.h" -#include "log.h" -#include "clock.h" -#include "flash_storage.h" -#include "mailbox.h" -#include "messages.h" - -int kloader_load_library(const void *library) -{ - if(!kernel_cpu_reset_read()) { - core_log("BUG: attempted to load kernel library while kernel CPU is running\n"); - return 0; - } - - // Stop kernel CPU before messing with its code. - kernel_cpu_reset_write(1); - - // Load kernel support code. - extern void _binary_ksupport_elf_start, _binary_ksupport_elf_end; - memcpy((void *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE), - &_binary_ksupport_elf_start, - &_binary_ksupport_elf_end - &_binary_ksupport_elf_start); - - // Start kernel CPU. - kernel_cpu_reset_write(0); - - struct msg_load_request request = { - .type = MESSAGE_TYPE_LOAD_REQUEST, - .library = library, - }; - mailbox_send(&request); - - struct msg_load_reply *reply = mailbox_wait_and_receive(); - - if(reply->type != MESSAGE_TYPE_LOAD_REPLY) { - core_log("BUG: unexpected reply to load/run request\n"); - return 0; - } - - if(reply->error != NULL) { - core_log("cannot load kernel: %s\n", reply->error); - return 0; - } - - return 1; -} - -void kloader_start_kernel() -{ - if(kernel_cpu_reset_read()) { - core_log("BUG: attempted to load kernel library while kernel CPU is stopped\n"); - return; - } - - mailbox_acknowledge(); -} - -static int kloader_start_flash_kernel(char *key) -{ -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - char buffer[32*1024]; - unsigned int length, remain; - - length = fs_read(key, buffer, sizeof(buffer), &remain); - if(length <= 0) - return 0; - - if(remain) { - core_log("ERROR: kernel %s is too large\n", key); - return 0; - } - - if(!kloader_load_library(buffer)) - return 0; - kloader_start_kernel(); - return 1; -#else - return 0; -#endif -} - -int kloader_start_startup_kernel(void) -{ - return kloader_start_flash_kernel("startup_kernel"); -} - -int kloader_start_idle_kernel(void) -{ - return kloader_start_flash_kernel("idle_kernel"); -} - -void kloader_stop(void) -{ - kernel_cpu_reset_write(1); - mailbox_acknowledge(); -} - -int kloader_validate_kpointer(void *p) -{ - unsigned int v = (unsigned int)p; - if((v < KERNELCPU_EXEC_ADDRESS) || (v > KERNELCPU_LAST_ADDRESS)) { - core_log("Received invalid pointer from kernel CPU: 0x%08x\n", v); - return 0; - } - return 1; -} - -int kloader_is_essential_kmsg(int msgtype) -{ - switch(msgtype) { - case MESSAGE_TYPE_NOW_INIT_REQUEST: - case MESSAGE_TYPE_NOW_SAVE: - case MESSAGE_TYPE_LOG: - case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: - case MESSAGE_TYPE_WATCHDOG_CLEAR: - return 1; - default: - return 0; - } -} - -static long long int now = 0; - -void kloader_service_essential_kmsg(void) -{ - struct msg_base *umsg; - - umsg = mailbox_receive(); - if(umsg) { - if(!kloader_validate_kpointer(umsg)) - return; - switch(umsg->type) { - case MESSAGE_TYPE_NOW_INIT_REQUEST: { - struct msg_now_init_reply reply; - - reply.type = MESSAGE_TYPE_NOW_INIT_REPLY; - reply.now = now; - mailbox_send_and_wait(&reply); - break; - } - case MESSAGE_TYPE_NOW_SAVE: { - struct msg_now_save *msg = (struct msg_now_save *)umsg; - - now = msg->now; - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_LOG: { - struct msg_log *msg = (struct msg_log *)umsg; - - core_log("%s", msg->buf); - mailbox_acknowledge(); - break; - } - case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: { - struct msg_watchdog_set_request *msg = (struct msg_watchdog_set_request *)umsg; - struct msg_watchdog_set_reply reply; - - reply.type = MESSAGE_TYPE_WATCHDOG_SET_REPLY; - reply.id = watchdog_set(msg->ms); - mailbox_send_and_wait(&reply); - break; - } - case MESSAGE_TYPE_WATCHDOG_CLEAR: { - struct msg_watchdog_clear *msg = (struct msg_watchdog_clear *)umsg; - - watchdog_clear(msg->id); - mailbox_acknowledge(); - break; - } - default: - /* handled elsewhere */ - break; - } - } -} diff --git a/artiq/runtime/kloader.h b/artiq/runtime/kloader.h deleted file mode 100644 index a330c4660..000000000 --- a/artiq/runtime/kloader.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __KLOADER_H -#define __KLOADER_H - -#define KERNELCPU_EXEC_ADDRESS 0x42000000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000 -#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) -#define KSUPPORT_HEADER_SIZE 0x80 - -int kloader_load_library(const void *code); - -int kloader_start_startup_kernel(void); -int kloader_start_idle_kernel(void); -void kloader_start_kernel(void); -void kloader_stop(void); - -int kloader_validate_kpointer(void *p); -int kloader_is_essential_kmsg(int msgtype); -void kloader_service_essential_kmsg(void); - -#endif /* __KLOADER_H */ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index ff454440f..5de733c5e 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -9,7 +9,6 @@ #include #include "ksupport.h" -#include "kloader.h" #include "mailbox.h" #include "messages.h" #include "artiq_personality.h" @@ -17,6 +16,11 @@ #include "dds.h" #include "i2c.h" +#define KERNELCPU_EXEC_ADDRESS 0x42000000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000 +#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) +#define KSUPPORT_HEADER_SIZE 0x80 + double round(double x); double sqrt(double x); diff --git a/artiq/runtime/log.c b/artiq/runtime/log.c deleted file mode 100644 index 5779d8c94..000000000 --- a/artiq/runtime/log.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "log.h" - -static int buffer_cursor; -static char buffer[LOG_BUFFER_SIZE]; - -void core_log(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - core_log_va(fmt, args); - va_end(args); -} - -void core_log_va(const char *fmt, va_list args) -{ - char outbuf[256]; - int len = vscnprintf(outbuf, sizeof(outbuf), fmt, args); - - for(int i = 0; i < len; i++) { - buffer[buffer_cursor] = outbuf[i]; - buffer_cursor = (buffer_cursor + 1) % LOG_BUFFER_SIZE; - } - -#ifdef CSR_ETHMAC_BASE - /* Since main comms are over ethernet, the serial port - * is free for us to use. */ - putsnonl(outbuf); -#endif -} - -void core_log_get(char *outbuf) -{ - int j = buffer_cursor; - for(int i = 0; i < LOG_BUFFER_SIZE; i++) { - outbuf[i] = buffer[j]; - j = (j + 1) % LOG_BUFFER_SIZE; - } -} - -void core_log_clear() -{ - memset(buffer, 0, sizeof(buffer)); -} diff --git a/artiq/runtime/log.h b/artiq/runtime/log.h deleted file mode 100644 index fcb1fcf4e..000000000 --- a/artiq/runtime/log.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __LOG_H -#define __LOG_H - -#include - -#define LOG_BUFFER_SIZE 4096 - -void core_log(const char *fmt, ...); -void core_log_va(const char *fmt, va_list args); -void core_log_get(char *outbuf); -void core_log_clear(void); - -#endif /* __LOG_H */ diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index bf1091526..d5db5e5dc 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -24,24 +24,7 @@ #include #endif -#include "kloader.h" #include "flash_storage.h" -#include "clock.h" -#include "rtiocrg.h" -#include "net_server.h" -#include "session.h" -#include "analyzer.h" -#include "moninj.h" - -u32_t sys_now(void) -{ - return clock_get_ms(); -} - -u32_t sys_jiffies(void) -{ - return clock_get_ms(); -} static struct netif netif; @@ -182,53 +165,6 @@ void network_init(void) #endif /* CSR_ETHMAC_BASE */ -static struct net_server_instance session_inst = { - .port = 1381, - .start = session_start, - .end = session_end, - .input = session_input, - .poll = session_poll, - .ack_consumed = session_ack_consumed, - .ack_sent = session_ack_sent -}; - -#ifdef CSR_RTIO_ANALYZER_BASE -static struct net_server_instance analyzer_inst = { - .port = 1382, - .start = analyzer_start, - .end = analyzer_end, - .input = analyzer_input, - .poll = analyzer_poll, - .ack_consumed = analyzer_ack_consumed, - .ack_sent = analyzer_ack_sent -}; -#endif - -static void regular_main(void) -{ - puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); - - clock_init(); - rtiocrg_init(); - session_startup_kernel(); - - puts("Accepting network sessions."); - network_init(); - net_server_init(&session_inst); -#ifdef CSR_RTIO_ANALYZER_BASE - analyzer_init(); - net_server_init(&analyzer_inst); -#endif - moninj_init(); - - session_end(); - while(1) { - lwip_service(); - kloader_service_essential_kmsg(); - net_server_service(); - } -} - extern void _fheap, _eheap; extern void rust_main(); @@ -249,8 +185,7 @@ int main(void) alloc_give(&_fheap, &_eheap - &_fheap); - // rust_main(); - regular_main(); + rust_main(); return 0; } diff --git a/artiq/runtime/moninj.c b/artiq/runtime/moninj.c deleted file mode 100644 index 67116626f..000000000 --- a/artiq/runtime/moninj.c +++ /dev/null @@ -1,166 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "moninj.h" - -enum { - MONINJ_REQ_MONITOR = 1, - MONINJ_REQ_TTLSET = 2 -}; - -enum { - MONINJ_TTL_MODE_EXP = 0, - MONINJ_TTL_MODE_1 = 1, - MONINJ_TTL_MODE_0 = 2, - MONINJ_TTL_MODE_IN = 3 -}; - -enum { - MONINJ_TTL_OVERRIDE_ENABLE = 0, - MONINJ_TTL_OVERRIDE_O = 1, - MONINJ_TTL_OVERRIDE_OE = 2 -}; - -static struct udp_pcb *listen_pcb; - -struct monitor_reply { - long long int ttl_levels; - long long int ttl_oes; - long long int ttl_overrides; - unsigned short int dds_rtio_first_channel; - unsigned short int dds_channels_per_bus; -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - unsigned int dds_ftws[CONFIG_RTIO_DDS_COUNT*CONFIG_DDS_CHANNELS_PER_BUS]; -#endif -} __attribute__((packed)); - -static void moninj_monitor(const ip_addr_t *addr, u16_t port) -{ - struct monitor_reply reply; - int i; - struct pbuf *reply_p; - - reply.ttl_levels = 0; - reply.ttl_oes = 0; - reply.ttl_overrides = 0; - for(i=0;i 0)) - int j; - - reply.dds_rtio_first_channel = CONFIG_RTIO_FIRST_DDS_CHANNEL; - reply.dds_channels_per_bus = CONFIG_DDS_CHANNELS_PER_BUS; - for(j=0;jpayload, &reply, sizeof(struct monitor_reply)); - udp_sendto(listen_pcb, reply_p, addr, port); - pbuf_free(reply_p); -} - -static void moninj_ttlset(int channel, int mode) -{ - rtio_moninj_inj_chan_sel_write(channel); - switch(mode) { - case MONINJ_TTL_MODE_EXP: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(0); - break; - case MONINJ_TTL_MODE_1: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - case MONINJ_TTL_MODE_0: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); - rtio_moninj_inj_value_write(0); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - case MONINJ_TTL_MODE_IN: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(0); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - default: - core_log("unknown TTL mode %d\n", mode); - break; - } -} - -static void moninj_recv(void *arg, struct udp_pcb *upcb, struct pbuf *req, - const ip_addr_t *addr, u16_t port) -{ - char *p = (char *)req->payload; - - if(req->len >= 1) { - switch(p[0]) { - case MONINJ_REQ_MONITOR: - moninj_monitor(addr, port); - break; - case MONINJ_REQ_TTLSET: - if(req->len < 3) - break; - moninj_ttlset(p[1], p[2]); - break; - default: - break; - } - } - pbuf_free(req); /* beware: addr may point into the req pbuf */ -} - -void moninj_init(void) -{ - listen_pcb = udp_new(); - if(!listen_pcb) { - core_log("Failed to create UDP listening PCB\n"); - return; - } - udp_bind(listen_pcb, IP_ADDR_ANY, 3250); - udp_recv(listen_pcb, moninj_recv, NULL); -} diff --git a/artiq/runtime/moninj.h b/artiq/runtime/moninj.h deleted file mode 100644 index 1224b3b6e..000000000 --- a/artiq/runtime/moninj.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MONINJ_H -#define __MONINJ_H - -void moninj_init(void); - -#endif /* __MONINJ_H */ diff --git a/artiq/runtime/net_server.c b/artiq/runtime/net_server.c deleted file mode 100644 index 4d09f0cf3..000000000 --- a/artiq/runtime/net_server.c +++ /dev/null @@ -1,227 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net_server.h" - -struct net_server_connstate { - struct net_server_instance *instance; - int magic_recognized; - struct pbuf *rp; - int rp_offset; -}; - -static struct net_server_connstate *cs_new(struct net_server_instance *instance) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)mem_malloc(sizeof(struct net_server_connstate)); - if(!cs) - return NULL; - cs->instance = instance; - cs->magic_recognized = 0; - cs->rp = NULL; - cs->rp_offset = 0; - return cs; -} - -static void cs_free(struct net_server_connstate *cs) -{ - if(cs->rp) - pbuf_free(cs->rp); - mem_free(cs); -} - -static const char net_server_magic[] = "ARTIQ coredev\n"; - -static int magic_ok(struct net_server_connstate *cs) -{ - return cs->magic_recognized >= 14; -} - -static void net_server_close(struct net_server_connstate *cs, struct tcp_pcb *pcb) -{ - struct net_server_instance *instance; - - instance = cs->instance; - if(cs == instance->open_session_cs) { - instance->end(); - instance->open_session_cs = NULL; - instance->open_session_pcb = NULL; - } - - if(pcb) { - /* lwip loves to call back with broken pointers. Prevent that. */ - tcp_arg(pcb, NULL); - tcp_recv(pcb, NULL); - tcp_sent(pcb, NULL); - tcp_err(pcb, NULL); - - tcp_close(pcb); - } - cs_free(cs); -} - -static err_t net_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - if(p) { - if(cs->rp) - pbuf_cat(cs->rp, p); - else { - cs->rp = p; - cs->rp_offset = 0; - } - } else - net_server_close(cs, pcb); - return ERR_OK; -} - -static err_t net_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - cs->instance->ack_sent(len); - return ERR_OK; -} - -static void tcp_pcb_service(void *arg, struct tcp_pcb *pcb) -{ - struct net_server_connstate *cs; - struct net_server_instance *instance; - int remaining_in_pbuf; - char *rpp; - struct pbuf *next; - int r; - - cs = (struct net_server_connstate *)arg; - instance = cs->instance; - - /* Reader interface */ - while(cs->rp) { - remaining_in_pbuf = cs->rp->len - cs->rp_offset; - rpp = (char *)cs->rp->payload; - while(remaining_in_pbuf > 0) { - if(cs == instance->open_session_cs) { - r = instance->input(&rpp[cs->rp_offset], remaining_in_pbuf); - if(r > 0) { - tcp_recved(pcb, r); - cs->rp_offset += r; - remaining_in_pbuf -= r; - } else if(r == 0) - return; - else - net_server_close(cs, pcb); - } else { - if(rpp[cs->rp_offset] == net_server_magic[cs->magic_recognized]) { - cs->magic_recognized++; - if(magic_ok(cs)) { - if(instance->open_session_cs) - net_server_close(instance->open_session_cs, - instance->open_session_pcb); - instance->start(); - instance->open_session_cs = cs; - instance->open_session_pcb = pcb; - tcp_sent(pcb, net_server_sent); - } - } else { - net_server_close(cs, pcb); - return; - } - remaining_in_pbuf--; - cs->rp_offset++; - tcp_recved(pcb, 1); - } - } - next = cs->rp->next; - if(cs->rp->tot_len != cs->rp->len) { - pbuf_ref(next); - pbuf_free(cs->rp); - cs->rp = next; - cs->rp_offset = 0; - } else { - pbuf_free(cs->rp); - cs->rp = NULL; - } - } - - /* Writer interface */ - if(cs == instance->open_session_cs) { - void *data; - int len, sndbuf, close_flag; - - cs->instance->poll(&data, &len, &close_flag); - if(len > 0) { - sndbuf = tcp_sndbuf(pcb); - if(len > sndbuf) - len = sndbuf; - tcp_write(pcb, data, len, 0); - instance->ack_consumed(len); - } - if(close_flag) - tcp_output(pcb); - if((len < 0) || close_flag) - net_server_close(cs, pcb); - } -} - -static void net_server_err(void *arg, err_t err) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - net_server_close(cs, NULL); -} - -static err_t net_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) -{ - struct net_server_instance *instance; - struct net_server_connstate *cs; - - instance = (struct net_server_instance *)arg; - cs = cs_new(instance); - if(!cs) - return ERR_MEM; - tcp_accepted(instance->listen_pcb); - tcp_arg(newpcb, cs); - tcp_recv(newpcb, net_server_recv); - tcp_err(newpcb, net_server_err); - return ERR_OK; -} - -void net_server_init(struct net_server_instance *instance) -{ - struct tcp_pcb *bind_pcb; - - bind_pcb = tcp_new(); - bind_pcb->so_options |= SOF_KEEPALIVE; - tcp_bind(bind_pcb, IP_ADDR_ANY, instance->port); - - instance->listen_pcb = tcp_listen(bind_pcb); - tcp_arg(instance->listen_pcb, instance); - tcp_accept(instance->listen_pcb, net_server_accept); -} - -extern struct tcp_pcb *tcp_active_pcbs; - -void net_server_service(void) -{ - struct tcp_pcb *pcb; - - pcb = tcp_active_pcbs; - while(pcb) { - if(pcb->recv == net_server_recv) /* filter our connections */ - tcp_pcb_service(pcb->callback_arg, pcb); - pcb = pcb->next; - } -} diff --git a/artiq/runtime/net_server.h b/artiq/runtime/net_server.h deleted file mode 100644 index 9bb57d6c6..000000000 --- a/artiq/runtime/net_server.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __NET_SERVER_H -#define __NET_SERVER_H - -struct net_server_connstate; -struct tcp_pcb; - -struct net_server_instance { - int port; - - void (*start)(void); - void (*end)(void); - int (*input)(void *data, int length); - void (*poll)(void **data, int *length, int *close_flag); - void (*ack_consumed)(int length); - void (*ack_sent)(int length); - - /* internal use */ - struct tcp_pcb *listen_pcb; - struct net_server_connstate *open_session_cs; - struct tcp_pcb *open_session_pcb; -}; - -void net_server_init(struct net_server_instance *instance); -void net_server_service(void); - -#endif /* __NET_SERVER_H */ diff --git a/artiq/runtime/rtiocrg.c b/artiq/runtime/rtiocrg.c deleted file mode 100644 index 2851cc994..000000000 --- a/artiq/runtime/rtiocrg.c +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include "log.h" -#include "clock.h" -#include "flash_storage.h" -#include "rtiocrg.h" - -void rtiocrg_init(void) -{ - char b; - int clk; - -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(0); -#endif - b = 'i'; - clk = 0; - fs_read("startup_clock", &b, 1, NULL); - if(b == 'i') - core_log("Startup RTIO clock: internal\n"); - else if(b == 'e') { - core_log("Startup RTIO clock: external\n"); - clk = 1; - } else - core_log("ERROR: unrecognized startup_clock entry in flash storage\n"); - - if(!rtiocrg_switch_clock(clk)) { - core_log("ERROR: startup RTIO clock failed\n"); - core_log("WARNING: this may cause the system initialization to fail\n"); - core_log("WARNING: fix clocking and reset the device\n"); - } -} - -int rtiocrg_check(void) -{ -#if ((defined CSR_RTIO_CRG_BASE) && (defined CSR_RTIO_CRG_PLL_RESET_ADDR)) - return rtio_crg_pll_locked_read(); -#else - return 1; -#endif -} - -int rtiocrg_switch_clock(int clk) -{ -#ifdef CSR_RTIO_CRG_BASE - int current_clk; - - current_clk = rtio_crg_clock_sel_read(); - if(clk == current_clk) { -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - busywait_us(150); - if(!rtio_crg_pll_locked_read()) - return 0; -#endif - return 1; - } -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(1); -#endif - rtio_crg_clock_sel_write(clk); -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(0); - busywait_us(150); - if(!rtio_crg_pll_locked_read()) - return 0; -#endif - return 1; -#else /* CSR_RTIO_CRG_BASE */ - return 1; -#endif -} diff --git a/artiq/runtime/rtiocrg.h b/artiq/runtime/rtiocrg.h deleted file mode 100644 index df498f617..000000000 --- a/artiq/runtime/rtiocrg.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __RTIOCRG_H -#define __RTIOCRG_H - -void rtiocrg_init(void); -int rtiocrg_check(void); -int rtiocrg_switch_clock(int clk); - -#endif /* __RTIOCRG_H */ diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c deleted file mode 100644 index 054d808e1..000000000 --- a/artiq/runtime/session.c +++ /dev/null @@ -1,1136 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "mailbox.h" -#include "messages.h" - -#include "clock.h" -#include "log.h" -#include "kloader.h" -#include "artiq_personality.h" -#include "flash_storage.h" -#include "rtiocrg.h" -#include "session.h" - -// 2.5MiB in payload + 1KiB for headers. -// We need more than 1MiB to send a 1MiB list due to tags; -// about 5/4MiB for an 1MiB int32 list, 9/8MiB for an 1MiB int64 list. -#define BUFFER_SIZE (2560*1024 + 1024) -#define BUFFER_IN_SIZE BUFFER_SIZE -#define BUFFER_OUT_SIZE BUFFER_SIZE - -static int process_input(); -static int out_packet_available(); - -// ============================= Reader interface ============================= - -// Align the 9th byte (right after the header) of buffer_in so that -// the payload can be deserialized directly from the buffer using word reads. -static struct { - char padding[3]; - union { - char data[BUFFER_IN_SIZE]; - struct { - int32_t sync; - int32_t length; - int8_t type; - } __attribute__((packed)) header; - }; -} __attribute__((packed, aligned(4))) buffer_in; - -static int buffer_in_write_cursor, buffer_in_read_cursor; - -static void in_packet_reset() -{ - buffer_in_write_cursor = 0; - buffer_in_read_cursor = 0; -} - -static int in_packet_fill(uint8_t *data, int length) -{ - int consumed = 0; - while(consumed < length) { - /* Make sure the output buffer is available for any reply - * we might need to send. */ - if(!out_packet_available()) - break; - - if(buffer_in_write_cursor < 4) { - /* Haven't received the synchronization sequence yet. */ - buffer_in.data[buffer_in_write_cursor++] = data[consumed]; - - /* Framing error? */ - if(data[consumed++] != 0x5a) { - buffer_in_write_cursor = 0; - continue; - } - } else if(buffer_in_write_cursor < 8) { - /* Haven't received the packet length yet. */ - buffer_in.data[buffer_in_write_cursor++] = data[consumed++]; - } else if(buffer_in.header.length == 0) { - /* Zero-length packet means session reset. */ - return -2; - } else if(buffer_in.header.length > BUFFER_IN_SIZE) { - /* Packet wouldn't fit in the buffer. */ - return -1; - } else if(buffer_in.header.length > buffer_in_write_cursor) { - /* Receiving payload. */ - int remaining = buffer_in.header.length - buffer_in_write_cursor; - int amount = length - consumed > remaining ? remaining : length - consumed; - memcpy(&buffer_in.data[buffer_in_write_cursor], &data[consumed], - amount); - buffer_in_write_cursor += amount; - consumed += amount; - } - - if(buffer_in.header.length == buffer_in_write_cursor) { - /* We have a complete packet. */ - - buffer_in_read_cursor = sizeof(buffer_in.header); - if(!process_input()) - return -1; - - if(buffer_in_read_cursor < buffer_in_write_cursor) { - core_log("session.c: read underrun (%d bytes remaining)\n", - buffer_in_write_cursor - buffer_in_read_cursor); - } - - in_packet_reset(); - } - } - - return consumed; -} - -static void in_packet_chunk(void *ptr, int length) -{ - if(buffer_in_read_cursor + length > buffer_in_write_cursor) { - core_log("session.c: read overrun while trying to read %d bytes" - " (%d remaining)\n", - length, buffer_in_write_cursor - buffer_in_read_cursor); - } - - if(ptr != NULL) - memcpy(ptr, &buffer_in.data[buffer_in_read_cursor], length); - buffer_in_read_cursor += length; -} - -static int8_t in_packet_int8() -{ - int8_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static int32_t in_packet_int32() -{ - int32_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static int64_t in_packet_int64() -{ - int64_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static const void *in_packet_bytes(int *length) -{ - *length = in_packet_int32(); - const void *ptr = &buffer_in.data[buffer_in_read_cursor]; - in_packet_chunk(NULL, *length); - return ptr; -} - -static const char *in_packet_string() -{ - int length; - const char *string = in_packet_bytes(&length); - if(string[length - 1] != 0) { - core_log("session.c: string is not zero-terminated\n"); - return ""; - } - return string; -} - -// ============================= Writer interface ============================= - -static union { - char data[BUFFER_OUT_SIZE]; - struct { - int32_t sync; - int32_t length; - int8_t type; - } __attribute__((packed)) header; -} buffer_out; - -static int buffer_out_read_cursor, buffer_out_sent_cursor, buffer_out_write_cursor; - -static void out_packet_reset() -{ - buffer_out_read_cursor = 0; - buffer_out_write_cursor = 0; - buffer_out_sent_cursor = 0; -} - -static int out_packet_available() -{ - return buffer_out_write_cursor == 0; -} - -static void out_packet_extract(void **data, int *length) -{ - if(buffer_out_write_cursor > 0 && - buffer_out.header.length > 0) { - *data = &buffer_out.data[buffer_out_read_cursor]; - *length = buffer_out_write_cursor - buffer_out_read_cursor; - } else { - *length = 0; - } -} - -static void out_packet_advance_consumed(int length) -{ - if(buffer_out_read_cursor + length > buffer_out_write_cursor) { - core_log("session.c: write underrun (consume) while trying to" - " acknowledge %d bytes (%d remaining)\n", - length, buffer_out_write_cursor - buffer_out_read_cursor); - return; - } - - buffer_out_read_cursor += length; -} - -static void out_packet_advance_sent(int length) -{ - if(buffer_out_sent_cursor + length > buffer_out_write_cursor) { - core_log("session.c: write underrun (send) while trying to" - " acknowledge %d bytes (%d remaining)\n", - length, buffer_out_write_cursor - buffer_out_sent_cursor); - return; - } - - buffer_out_sent_cursor += length; - if(buffer_out_sent_cursor == buffer_out_write_cursor) - out_packet_reset(); -} - -static int out_packet_chunk(const void *ptr, int length) -{ - if(buffer_out_write_cursor + length > BUFFER_OUT_SIZE) { - core_log("session.c: write overrun while trying to write %d bytes" - " (%d remaining)\n", - length, BUFFER_OUT_SIZE - buffer_out_write_cursor); - return 0; - } - - memcpy(&buffer_out.data[buffer_out_write_cursor], ptr, length); - buffer_out_write_cursor += length; - return 1; -} - -static void out_packet_start(int type) -{ - buffer_out.header.sync = 0x5a5a5a5a; - buffer_out.header.type = type; - buffer_out.header.length = 0; - buffer_out_write_cursor = sizeof(buffer_out.header); -} - -static void out_packet_finish() -{ - buffer_out.header.length = buffer_out_write_cursor; -} - -static void out_packet_empty(int type) -{ - out_packet_start(type); - out_packet_finish(); -} - -static int out_packet_int8(int8_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_int32(int32_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_int64(int64_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_float64(double value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_bytes(const void *ptr, int length) -{ - return out_packet_int32(length) && - out_packet_chunk(ptr, length); -} - -static int out_packet_string(const char *string) -{ - return out_packet_bytes(string, strlen(string) + 1); -} - -// =============================== API handling =============================== - -static int user_kernel_state; - -enum { - USER_KERNEL_NONE = 0, - USER_KERNEL_LOADED, - USER_KERNEL_RUNNING, - USER_KERNEL_WAIT_RPC /* < must come after _RUNNING */ -}; - -void session_startup_kernel(void) -{ - struct msg_base *umsg; - - watchdog_init(); - if(!kloader_start_startup_kernel()) - return; - - core_log("Startup kernel started\n"); - while(1) { - kloader_service_essential_kmsg(); - - umsg = mailbox_receive(); - if(umsg) { - if(!kloader_validate_kpointer(umsg)) - break; - if(kloader_is_essential_kmsg(umsg->type)) - continue; - if(umsg->type == MESSAGE_TYPE_FINISHED) - break; - else if(umsg->type == MESSAGE_TYPE_EXCEPTION) { - core_log("WARNING: startup kernel ended with exception\n"); - break; - } else { - core_log("ERROR: received invalid message type from kernel CPU\n"); - break; - } - } - - if(watchdog_expired()) { - core_log("WARNING: watchdog expired in startup kernel\n"); - break; - } - } - kloader_stop(); - core_log("Startup kernel terminated\n"); -} - -void session_start(void) -{ - in_packet_reset(); - out_packet_reset(); - - kloader_stop(); - user_kernel_state = USER_KERNEL_NONE; -} - -void session_end(void) -{ - kloader_stop(); - watchdog_init(); - kloader_start_idle_kernel(); -} - -/* host to device */ -enum { - REMOTEMSG_TYPE_LOG_REQUEST = 1, - REMOTEMSG_TYPE_LOG_CLEAR, - - REMOTEMSG_TYPE_IDENT_REQUEST, - REMOTEMSG_TYPE_SWITCH_CLOCK, - - REMOTEMSG_TYPE_LOAD_LIBRARY, - REMOTEMSG_TYPE_RUN_KERNEL, - - REMOTEMSG_TYPE_RPC_REPLY, - REMOTEMSG_TYPE_RPC_EXCEPTION, - - REMOTEMSG_TYPE_FLASH_READ_REQUEST, - REMOTEMSG_TYPE_FLASH_WRITE_REQUEST, - REMOTEMSG_TYPE_FLASH_ERASE_REQUEST, - REMOTEMSG_TYPE_FLASH_REMOVE_REQUEST -}; - -/* device to host */ -enum { - REMOTEMSG_TYPE_LOG_REPLY = 1, - - REMOTEMSG_TYPE_IDENT_REPLY, - REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED, - REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED, - - REMOTEMSG_TYPE_LOAD_COMPLETED, - REMOTEMSG_TYPE_LOAD_FAILED, - - REMOTEMSG_TYPE_KERNEL_FINISHED, - REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED, - REMOTEMSG_TYPE_KERNEL_EXCEPTION, - - REMOTEMSG_TYPE_RPC_REQUEST, - - REMOTEMSG_TYPE_FLASH_READ_REPLY, - REMOTEMSG_TYPE_FLASH_OK_REPLY, - REMOTEMSG_TYPE_FLASH_ERROR_REPLY, - - REMOTEMSG_TYPE_WATCHDOG_EXPIRED, - REMOTEMSG_TYPE_CLOCK_FAILURE, -}; - -static int receive_rpc_value(const char **tag, void **slot); - -static int process_input(void) -{ - switch(buffer_in.header.type) { - case REMOTEMSG_TYPE_IDENT_REQUEST: { - char version[IDENT_SIZE]; - - get_ident(version); - - out_packet_start(REMOTEMSG_TYPE_IDENT_REPLY); - out_packet_chunk("AROR", 4); - out_packet_chunk(version, strlen(version)); - out_packet_finish(); - break; - } - - case REMOTEMSG_TYPE_SWITCH_CLOCK: { - int clk = in_packet_int8(); - - if(user_kernel_state >= USER_KERNEL_RUNNING) { - core_log("Attempted to switch RTIO clock while kernel running\n"); - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED); - break; - } - - if(rtiocrg_switch_clock(clk)) - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED); - else - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED); - break; - } - - case REMOTEMSG_TYPE_LOG_REQUEST: -#if (LOG_BUFFER_SIZE + 9) > BUFFER_OUT_SIZE -#error Output buffer cannot hold the log buffer -#endif - out_packet_start(REMOTEMSG_TYPE_LOG_REPLY); - core_log_get(&buffer_out.data[buffer_out_write_cursor]); - buffer_out_write_cursor += LOG_BUFFER_SIZE; - out_packet_finish(); - break; - - case REMOTEMSG_TYPE_LOG_CLEAR: - core_log_clear(); - out_packet_empty(REMOTEMSG_TYPE_LOG_REPLY); - break; - - case REMOTEMSG_TYPE_FLASH_READ_REQUEST: { -#if CONFIG_SPIFLASH_SECTOR_SIZE - 4 > BUFFER_OUT_SIZE - 9 -#error Output buffer cannot hold the flash storage data -#endif - const char *key = in_packet_string(); - int value_length; - - out_packet_start(REMOTEMSG_TYPE_FLASH_READ_REPLY); - value_length = fs_read(key, &buffer_out.data[buffer_out_write_cursor], - sizeof(buffer_out.data) - buffer_out_write_cursor, NULL); - buffer_out_write_cursor += value_length; - out_packet_finish(); - break; - } - - case REMOTEMSG_TYPE_FLASH_WRITE_REQUEST: { -#if CONFIG_SPIFLASH_SECTOR_SIZE - 4 > BUFFER_IN_SIZE - 9 -#error Input buffer cannot hold the flash storage data -#endif - const char *key, *value; - int value_length; - key = in_packet_string(); - value = in_packet_bytes(&value_length); - - if(fs_write(key, value, value_length)) - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - else - out_packet_empty(REMOTEMSG_TYPE_FLASH_ERROR_REPLY); - break; - } - - case REMOTEMSG_TYPE_FLASH_ERASE_REQUEST: - fs_erase(); - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - break; - - case REMOTEMSG_TYPE_FLASH_REMOVE_REQUEST: { - const char *key = in_packet_string(); - - fs_remove(key); - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - break; - } - - case REMOTEMSG_TYPE_LOAD_LIBRARY: { - const void *kernel = &buffer_in.data[buffer_in_read_cursor]; - buffer_in_read_cursor = buffer_in_write_cursor; - - if(user_kernel_state >= USER_KERNEL_RUNNING) { - core_log("Attempted to load new kernel library while already running\n"); - out_packet_empty(REMOTEMSG_TYPE_LOAD_FAILED); - break; - } - - if(kloader_load_library(kernel)) { - out_packet_empty(REMOTEMSG_TYPE_LOAD_COMPLETED); - user_kernel_state = USER_KERNEL_LOADED; - } else { - out_packet_empty(REMOTEMSG_TYPE_LOAD_FAILED); - } - break; - } - - case REMOTEMSG_TYPE_RUN_KERNEL: - if(user_kernel_state != USER_KERNEL_LOADED) { - core_log("Attempted to run kernel while not in the LOADED state\n"); - out_packet_empty(REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED); - break; - } - - watchdog_init(); - kloader_start_kernel(); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - - case REMOTEMSG_TYPE_RPC_REPLY: { - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - if(user_kernel_state != USER_KERNEL_WAIT_RPC) { - core_log("Unsolicited RPC reply\n"); - return 0; // restart session - } - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return 0; // restart session - } - - const char *tag = in_packet_string(); - void *slot = request->slot; - if(!receive_rpc_value(&tag, &slot)) { - core_log("Failed to receive RPC reply\n"); - return 0; // restart session - } - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = 0; - reply.exception = NULL; - mailbox_send_and_wait(&reply); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - } - - case REMOTEMSG_TYPE_RPC_EXCEPTION: { - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - struct artiq_exception exception; - exception.name = in_packet_string(); - exception.message = in_packet_string(); - exception.param[0] = in_packet_int64(); - exception.param[1] = in_packet_int64(); - exception.param[2] = in_packet_int64(); - exception.file = in_packet_string(); - exception.line = in_packet_int32(); - exception.column = in_packet_int32(); - exception.function = in_packet_string(); - - if(user_kernel_state != USER_KERNEL_WAIT_RPC) { - core_log("Unsolicited RPC exception reply\n"); - return 0; // restart session - } - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return 0; // restart session - } - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = 0; - reply.exception = &exception; - mailbox_send_and_wait(&reply); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - } - - default: - core_log("Received invalid packet type %d from host\n", - buffer_in.header.type); - return 0; - } - - return 1; -} - -// See comm_generic.py:_{send,receive}_rpc_value and llvm_ir_generator.py:_rpc_tag. -static void skip_rpc_value(const char **tag) { - switch(*(*tag)++) { - case 't': { - int size = *(*tag)++; - for(int i = 0; i < size; i++) - skip_rpc_value(tag); - break; - } - - case 'l': - case 'a': - skip_rpc_value(tag); - break; - - case 'r': - skip_rpc_value(tag); - break; - } -} - -static int sizeof_rpc_value(const char **tag) -{ - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - - int32_t length = 0; - for(int i = 0; i < size; i++) - length += sizeof_rpc_value(tag); - return length; - } - - case 'n': // None - return 0; - - case 'b': // bool - return sizeof(int8_t); - - case 'i': // int(width=32) - return sizeof(int32_t); - - case 'I': // int(width=64) - return sizeof(int64_t); - - case 'f': // float - return sizeof(double); - - case 'F': // Fraction - return sizeof(struct { int64_t numerator, denominator; }); - - case 's': // string - return sizeof(char *); - - case 'l': // list(elt='a) - case 'a': // array(elt='a) - skip_rpc_value(tag); - return sizeof(struct { int32_t length; struct {} *elements; }); - - case 'r': // range(elt='a) - return sizeof_rpc_value(tag) * 3; - - default: - core_log("sizeof_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } -} - -static void *alloc_rpc_value(int size) -{ - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = size; - reply.exception = NULL; - mailbox_send_and_wait(&reply); - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return NULL; - } - return request->slot; -} - -static int receive_rpc_value(const char **tag, void **slot) -{ - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - - for(int i = 0; i < size; i++) { - if(!receive_rpc_value(tag, slot)) - return 0; - } - break; - } - - case 'n': // None - break; - - case 'b': { // bool - *((*(int8_t**)slot)++) = in_packet_int8(); - break; - } - - case 'i': { // int(width=32) - *((*(int32_t**)slot)++) = in_packet_int32(); - break; - } - - case 'I': { // int(width=64) - *((*(int64_t**)slot)++) = in_packet_int64(); - break; - } - - case 'f': { // float - *((*(int64_t**)slot)++) = in_packet_int64(); - break; - } - - case 'F': { // Fraction - struct { int64_t numerator, denominator; } *fraction = *slot; - fraction->numerator = in_packet_int64(); - fraction->denominator = in_packet_int64(); - *slot = (void*)((intptr_t)(*slot) + sizeof(*fraction)); - break; - } - - case 's': { // string - const char *in_string = in_packet_string(); - char *out_string = alloc_rpc_value(strlen(in_string) + 1); - memcpy(out_string, in_string, strlen(in_string) + 1); - *((*(char***)slot)++) = out_string; - break; - } - - case 'l': // list(elt='a) - case 'a': { // array(elt='a) - struct { int32_t length; struct {} *elements; } *list = *slot; - list->length = in_packet_int32(); - - const char *tag_copy = *tag; - list->elements = alloc_rpc_value(sizeof_rpc_value(&tag_copy) * list->length); - - void *element = list->elements; - for(int i = 0; i < list->length; i++) { - const char *tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, &element)) - return 0; - } - skip_rpc_value(tag); - break; - } - - case 'r': { // range(elt='a) - const char *tag_copy; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // min - return 0; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // max - return 0; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // step - return 0; - *tag = tag_copy; - break; - } - - default: - core_log("receive_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } - - return 1; -} - -static int send_rpc_value(const char **tag, void **value) -{ - if(!out_packet_int8(**tag)) - return 0; - - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - if(!out_packet_int8(size)) - return 0; - - for(int i = 0; i < size; i++) { - if(!send_rpc_value(tag, value)) - return 0; - } - break; - } - - case 'n': // None - break; - - case 'b': { // bool - return out_packet_int8(*((*(int8_t**)value)++)); - } - - case 'i': { // int(width=32) - return out_packet_int32(*((*(int32_t**)value)++)); - } - - case 'I': { // int(width=64) - return out_packet_int64(*((*(int64_t**)value)++)); - } - - case 'f': { // float - return out_packet_float64(*((*(double**)value)++)); - } - - case 'F': { // Fraction - struct { int64_t numerator, denominator; } *fraction = *value; - if(!out_packet_int64(fraction->numerator)) - return 0; - if(!out_packet_int64(fraction->denominator)) - return 0; - *value = (void*)((intptr_t)(*value) + sizeof(*fraction)); - break; - } - - case 's': { // string - return out_packet_string(*((*(const char***)value)++)); - } - - case 'l': // list(elt='a) - case 'a': { // array(elt='a) - struct { uint32_t length; struct {} *elements; } *list = *value; - void *element = list->elements; - - if(!out_packet_int32(list->length)) - return 0; - - for(int i = 0; i < list->length; i++) { - const char *tag_copy = *tag; - if(!send_rpc_value(&tag_copy, &element)) { - core_log("failed to send list at element %d/%d\n", i, list->length); - return 0; - } - } - skip_rpc_value(tag); - - *value = (void*)((intptr_t)(*value) + sizeof(*list)); - break; - } - - case 'r': { // range(elt='a) - const char *tag_copy; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // min - return 0; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // max - return 0; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // step - return 0; - *tag = tag_copy; - break; - } - - case 'k': { // keyword(value='a) - struct { const char *name; struct {} contents; } *option = *value; - void *contents = &option->contents; - - if(!out_packet_string(option->name)) - return 0; - - // keyword never appears in composite types, so we don't have - // to accurately advance *value. - return send_rpc_value(tag, &contents); - } - - case 'O': { // host object - struct { uint32_t id; } **object = *value; - - if(!out_packet_int32((*object)->id)) - return 0; - - *value = (void*)((intptr_t)(*value) + sizeof(*object)); - break; - } - - default: - core_log("send_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } - - return 1; -} - -static int send_rpc_request(int service, const char *tag, void **data) -{ - out_packet_start(REMOTEMSG_TYPE_RPC_REQUEST); - out_packet_int32(service); - - while(*tag != ':') { - void *value = *data++; - if(!kloader_validate_kpointer(value)) - return 0; - if(!send_rpc_value(&tag, &value)) - return 0; - } - out_packet_int8(0); - - out_packet_string(tag + 1); // return tags - out_packet_finish(); - return 1; -} - -struct cache_row { - struct cache_row *next; - char *key; - size_t length; - int32_t *elements; - int borrowed; -}; - -static struct cache_row *cache; - -/* assumes output buffer is empty when called */ -static int process_kmsg(struct msg_base *umsg) -{ - if(!kloader_validate_kpointer(umsg)) - return 0; - if(kloader_is_essential_kmsg(umsg->type)) - return 1; /* handled elsewhere */ - if(user_kernel_state == USER_KERNEL_LOADED && - umsg->type == MESSAGE_TYPE_LOAD_REPLY) { - // Kernel standing by. - return 1; - } - if(user_kernel_state == USER_KERNEL_WAIT_RPC && - umsg->type == MESSAGE_TYPE_RPC_RECV_REQUEST) { - // Handled and acknowledged when we receive - // REMOTEMSG_TYPE_RPC_{EXCEPTION,REPLY}. - return 1; - } - if(user_kernel_state != USER_KERNEL_RUNNING) { - core_log("Received unexpected message from kernel CPU while not in running state\n"); - return 0; - } - - switch(umsg->type) { - case MESSAGE_TYPE_FINISHED: - out_packet_empty(REMOTEMSG_TYPE_KERNEL_FINISHED); - - for(struct cache_row *iter = cache; iter; iter = iter->next) - iter->borrowed = 0; - - kloader_stop(); - user_kernel_state = USER_KERNEL_LOADED; - - break; - - case MESSAGE_TYPE_EXCEPTION: { - struct msg_exception *msg = (struct msg_exception *)umsg; - - out_packet_start(REMOTEMSG_TYPE_KERNEL_EXCEPTION); - - out_packet_string(msg->exception->name); - out_packet_string(msg->exception->message); - out_packet_int64(msg->exception->param[0]); - out_packet_int64(msg->exception->param[1]); - out_packet_int64(msg->exception->param[2]); - - out_packet_string(msg->exception->file); - out_packet_int32(msg->exception->line); - out_packet_int32(msg->exception->column); - out_packet_string(msg->exception->function); - - out_packet_int32(msg->backtrace_size); - for(int i = 0; i < msg->backtrace_size; i++) { - out_packet_int32(msg->backtrace[i]); - } - - out_packet_finish(); - - kloader_stop(); - user_kernel_state = USER_KERNEL_LOADED; - mailbox_acknowledge(); - break; - } - - case MESSAGE_TYPE_RPC_SEND: - case MESSAGE_TYPE_RPC_BATCH: { - struct msg_rpc_send *msg = (struct msg_rpc_send *)umsg; - - if(!send_rpc_request(msg->service, msg->tag, msg->data)) { - core_log("Failed to send RPC request (service %d, tag %s)\n", - msg->service, msg->tag); - return 0; // restart session - } - - if(msg->type == MESSAGE_TYPE_RPC_SEND) - user_kernel_state = USER_KERNEL_WAIT_RPC; - mailbox_acknowledge(); - break; - } - - case MESSAGE_TYPE_CACHE_GET_REQUEST: { - struct msg_cache_get_request *request = (struct msg_cache_get_request *)umsg; - struct msg_cache_get_reply reply; - - reply.type = MESSAGE_TYPE_CACHE_GET_REPLY; - reply.length = 0; - reply.elements = NULL; - - for(struct cache_row *iter = cache; iter; iter = iter->next) { - if(!strcmp(iter->key, request->key)) { - reply.length = iter->length; - reply.elements = iter->elements; - iter->borrowed = 1; - break; - } - } - - mailbox_send(&reply); - break; - } - - case MESSAGE_TYPE_CACHE_PUT_REQUEST: { - struct msg_cache_put_request *request = (struct msg_cache_put_request *)umsg; - struct msg_cache_put_reply reply; - - reply.type = MESSAGE_TYPE_CACHE_PUT_REPLY; - - struct cache_row *row = NULL; - for(struct cache_row *iter = cache; iter; iter = iter->next) { - if(!strcmp(iter->key, request->key)) { - row = iter; - break; - } - } - - if(!row) { - row = calloc(1, sizeof(struct cache_row)); - row->key = calloc(strlen(request->key) + 1, 1); - strcpy(row->key, request->key); - row->next = cache; - cache = row; - } - - if(!row->borrowed) { - row->length = request->length; - if(row->length != 0) { - row->elements = calloc(row->length, sizeof(int32_t)); - memcpy(row->elements, request->elements, - sizeof(int32_t) * row->length); - } else { - free(row->elements); - row->elements = NULL; - } - - reply.succeeded = 1; - } else { - reply.succeeded = 0; - } - - mailbox_send(&reply); - break; - } - - default: { - core_log("Received invalid message type %d from kernel CPU\n", - umsg->type); - return 0; - } - } - - return 1; -} - -/* Returns amount of bytes consumed on success. - * Returns -1 in case of irrecoverable error - * (the session must be dropped and session_end called). - * Returns -2 if the host has requested session reset. - */ -int session_input(void *data, int length) -{ - return in_packet_fill((uint8_t*)data, length); -} - -/* *length is set to -1 in case of irrecoverable error - * (the session must be dropped and session_end called) - */ -void session_poll(void **data, int *length, int *close_flag) -{ - *close_flag = 0; - - if(user_kernel_state == USER_KERNEL_RUNNING) { - if(watchdog_expired()) { - core_log("Watchdog expired\n"); - - *close_flag = 1; - out_packet_empty(REMOTEMSG_TYPE_WATCHDOG_EXPIRED); - } - if(!rtiocrg_check()) { - core_log("RTIO clock failure\n"); - - *close_flag = 1; - out_packet_empty(REMOTEMSG_TYPE_CLOCK_FAILURE); - } - } - - if(!*close_flag) { - /* If the output buffer is available, - * check if the kernel CPU has something to transmit. - */ - if(out_packet_available()) { - struct msg_base *umsg = mailbox_receive(); - if(umsg) { - if(!process_kmsg(umsg)) { - *length = -1; - return; - } - } - } - } - - out_packet_extract(data, length); -} - -void session_ack_consumed(int length) -{ - out_packet_advance_consumed(length); -} - -void session_ack_sent(int length) -{ - out_packet_advance_sent(length); -} diff --git a/artiq/runtime/session.h b/artiq/runtime/session.h deleted file mode 100644 index 06ee421ed..000000000 --- a/artiq/runtime/session.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __SESSION_H -#define __SESSION_H - -void session_startup_kernel(void); -void session_start(void); -void session_end(void); - -int session_input(void *data, int length); -void session_poll(void **data, int *length, int *close_flag); -void session_ack_consumed(int length); -void session_ack_sent(int length); - -#endif /* __SESSION_H */ From bcdbd00e7b19036cc37b380c6e57403a60e894cf Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 15:38:33 +0000 Subject: [PATCH 043/127] runtime: remove unnecessary null bytes from session protocol. --- artiq/coredevice/comm_generic.py | 4 ++-- artiq/runtime.rs/src/proto.rs | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 8a8277ac1..1d42a12c1 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -166,7 +166,7 @@ class CommGeneric: return self._read_chunk(self._read_int32()) def _read_string(self): - return self._read_bytes()[:-1].decode("utf-8") + return self._read_bytes().decode("utf-8") # # Writer interface @@ -214,7 +214,7 @@ class CommGeneric: self._write_buffer.append(value) def _write_string(self, value): - self._write_bytes(value.encode("utf-8") + b"\0") + self._write_bytes(value.encode("utf-8")) # # Exported APIs diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs index 87746e494..e3b40c3c1 100644 --- a/artiq/runtime.rs/src/proto.rs +++ b/artiq/runtime.rs/src/proto.rs @@ -66,14 +66,10 @@ pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { } pub fn read_string(reader: &mut Read) -> io::Result { - let mut bytes = try!(read_bytes(reader)); - let len = bytes.len() - 1; // length without trailing \0 - bytes.resize(len, 0); // FIXME: don't send \0 in the first place + let bytes = try!(read_bytes(reader)); String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { - try!(write_u32(writer, (value.len() + 1) as u32)); - try!(writer.write_all(value.as_bytes())); - write_u8(writer, 0) + write_bytes(writer, value.as_bytes()) } From d619336503fc87288cb2e34c309e7002b453123b Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 15:44:55 +0000 Subject: [PATCH 044/127] test: update libartiq_support for changed personality ABI. --- artiq/test/libartiq_support/artiq_terminate.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/artiq/test/libartiq_support/artiq_terminate.c b/artiq/test/libartiq_support/artiq_terminate.c index 5b1315131..577048149 100644 --- a/artiq/test/libartiq_support/artiq_terminate.c +++ b/artiq/test/libartiq_support/artiq_terminate.c @@ -8,7 +8,7 @@ #include void __artiq_terminate(struct artiq_exception *exn, - struct artiq_backtrace_item *backtrace, + uintptr_t *backtrace, size_t backtrace_size) { printf("Uncaught %s: %s (%"PRIi64", %"PRIi64", %"PRIi64")\n" "at %s:%"PRIi32":%"PRIi32"\n", @@ -17,12 +17,7 @@ void __artiq_terminate(struct artiq_exception *exn, exn->file, exn->line, exn->column + 1); for(size_t i = 0; i < backtrace_size; i++) { - Dl_info info; - if(dladdr((void*) backtrace[i].function, &info) && info.dli_sname) { - printf("at %s+%p\n", info.dli_sname, (void*)backtrace[i].offset); - } else { - printf("at %p+%p\n", (void*)backtrace[i].function, (void*)backtrace[i].offset); - } + printf("at %p\n", backtrace[i]); } exit(1); From edafb08b439f99073364acd241d8e1753313db03 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 16:26:48 +0000 Subject: [PATCH 045/127] test: fix d6193365. --- artiq/test/libartiq_support/artiq_terminate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/libartiq_support/artiq_terminate.c b/artiq/test/libartiq_support/artiq_terminate.c index 577048149..be56c4b29 100644 --- a/artiq/test/libartiq_support/artiq_terminate.c +++ b/artiq/test/libartiq_support/artiq_terminate.c @@ -17,7 +17,7 @@ void __artiq_terminate(struct artiq_exception *exn, exn->file, exn->line, exn->column + 1); for(size_t i = 0; i < backtrace_size; i++) { - printf("at %p\n", backtrace[i]); + printf("at %"PRIxPTR"\n", backtrace[i]); } exit(1); From 557bc4bb56a2771f57f32fed425ddcc837da0caf Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 17:25:43 +0000 Subject: [PATCH 046/127] runtime: remove unnecessary buffering. --- artiq/coredevice/comm_generic.py | 76 +++++-------------- artiq/runtime.rs/src/session.rs | 36 ++++----- artiq/runtime.rs/src/session_proto.rs | 102 +++++++++++--------------- 3 files changed, 75 insertions(+), 139 deletions(-) diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 1d42a12c1..3163b1ba8 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -20,7 +20,7 @@ class _H2DMsgType(Enum): IDENT_REQUEST = 3 SWITCH_CLOCK = 4 - LOAD_LIBRARY = 5 + LOAD_KERNEL = 5 RUN_KERNEL = 6 RPC_REPLY = 7 @@ -68,9 +68,7 @@ RPCKeyword = namedtuple('RPCKeyword', ['name', 'value']) class CommGeneric: def __init__(self): - self._read_type = self._write_type = None - self._read_length = 0 - self._write_buffer = [] + self._read_type = None def open(self): """Opens the communication channel. @@ -99,10 +97,6 @@ class CommGeneric: def _read_header(self): self.open() - if self._read_length > 0: - raise IOError("Read underrun ({} bytes remaining)". - format(self._read_length)) - # Wait for a synchronization sequence, 5a 5a 5a 5a. sync_count = 0 while sync_count < 4: @@ -113,20 +107,11 @@ class CommGeneric: sync_count = 0 # Read message header. - (self._read_length, ) = struct.unpack(">l", self.read(4)) - if not self._read_length: # inband connection close - raise OSError("Connection closed") - (raw_type, ) = struct.unpack("B", self.read(1)) self._read_type = _D2HMsgType(raw_type) - if self._read_length < 9: - raise IOError("Read overrun in message header ({} remaining)". - format(self._read_length)) - self._read_length -= 9 - - logger.debug("receiving message: type=%r length=%d", - self._read_type, self._read_length) + logger.debug("receiving message: type=%r", + self._read_type) def _read_expect(self, ty): if self._read_type != ty: @@ -138,12 +123,6 @@ class CommGeneric: self._read_expect(ty) def _read_chunk(self, length): - if self._read_length < length: - raise IOError("Read overrun while trying to read {} bytes ({} remaining)" - " in packet {}". - format(length, self._read_length, self._read_type)) - - self._read_length -= length return self.read(length) def _read_int8(self): @@ -175,43 +154,32 @@ class CommGeneric: def _write_header(self, ty): self.open() - logger.debug("preparing to send message: type=%r", ty) - self._write_type = ty - self._write_buffer = [] + logger.debug("sending message: type=%r", ty) - def _write_flush(self): - # Calculate message size. - length = sum([len(chunk) for chunk in self._write_buffer]) - logger.debug("sending message: type=%r length=%d", self._write_type, length) - - # Write synchronization sequence, header and body. - self.write(struct.pack(">llB", 0x5a5a5a5a, - 9 + length, self._write_type.value)) - for chunk in self._write_buffer: - self.write(chunk) + # Write synchronization sequence and header. + self.write(struct.pack(">lB", 0x5a5a5a5a, ty.value)) def _write_empty(self, ty): self._write_header(ty) - self._write_flush() def _write_chunk(self, chunk): - self._write_buffer.append(chunk) + self.write(chunk) def _write_int8(self, value): - self._write_buffer.append(struct.pack("B", value)) + self.write(struct.pack("B", value)) def _write_int32(self, value): - self._write_buffer.append(struct.pack(">l", value)) + self.write(struct.pack(">l", value)) def _write_int64(self, value): - self._write_buffer.append(struct.pack(">q", value)) + self.write(struct.pack(">q", value)) def _write_float64(self, value): - self._write_buffer.append(struct.pack(">d", value)) + self.write(struct.pack(">d", value)) def _write_bytes(self, value): self._write_int32(len(value)) - self._write_buffer.append(value) + self.write(value) def _write_string(self, value): self._write_bytes(value.encode("utf-8")) @@ -232,7 +200,7 @@ class CommGeneric: if runtime_id != b"AROR": raise UnsupportedDevice("Unsupported runtime ID: {}" .format(runtime_id)) - gateware_version = self._read_chunk(self._read_length).decode("utf-8") + gateware_version = self._read_string() if gateware_version != software_version and \ gateware_version + ".dirty" != software_version: logger.warning("Mismatch between gateware (%s) " @@ -242,7 +210,6 @@ class CommGeneric: def switch_clock(self, external): self._write_header(_H2DMsgType.SWITCH_CLOCK) self._write_int8(external) - self._write_flush() self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED) @@ -251,7 +218,7 @@ class CommGeneric: self._read_header() self._read_expect(_D2HMsgType.LOG_REPLY) - return self._read_chunk(self._read_length).decode("utf-8", "replace") + return self._read_string() def clear_log(self): self._write_empty(_H2DMsgType.LOG_CLEAR) @@ -261,17 +228,15 @@ class CommGeneric: def flash_storage_read(self, key): self._write_header(_H2DMsgType.FLASH_READ_REQUEST) self._write_string(key) - self._write_flush() self._read_header() self._read_expect(_D2HMsgType.FLASH_READ_REPLY) - return self._read_chunk(self._read_length) + return self._read_string() def flash_storage_write(self, key, value): self._write_header(_H2DMsgType.FLASH_WRITE_REQUEST) self._write_string(key) self._write_bytes(value) - self._write_flush() self._read_header() if self._read_type == _D2HMsgType.FLASH_ERROR_REPLY: @@ -287,14 +252,12 @@ class CommGeneric: def flash_storage_remove(self, key): self._write_header(_H2DMsgType.FLASH_REMOVE_REQUEST) self._write_string(key) - self._write_flush() self._read_empty(_D2HMsgType.FLASH_OK_REPLY) def load(self, kernel_library): - self._write_header(_H2DMsgType.LOAD_LIBRARY) - self._write_chunk(kernel_library) - self._write_flush() + self._write_header(_H2DMsgType.LOAD_KERNEL) + self._write_bytes(kernel_library) self._read_empty(_D2HMsgType.LOAD_COMPLETED) @@ -460,7 +423,6 @@ class CommGeneric: self._write_header(_H2DMsgType.RPC_REPLY) self._write_bytes(return_tags) self._send_rpc_value(bytearray(return_tags), result, result, service) - self._write_flush() except Exception as exn: logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn) @@ -503,8 +465,6 @@ class CommGeneric: self._write_int32(-1) # column not known self._write_string(function) - self._write_flush() - def _serve_exception(self, embedding_map, symbolizer, demangler): name = self._read_string() message = self._read_string() diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index 5b135b226..bbdf30f48 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -233,31 +233,29 @@ fn process_host_message(waiter: Waiter, Err(_) => host_write(stream, host::Reply::KernelStartupFailed) }, - host::Request::RpcReply { tag, data } => { + host::Request::RpcReply { tag } => { if session.kernel_state != KernelState::RpcWait { unexpected!("unsolicited RPC reply") } - try!(kern_recv(waiter, |reply| { + let slot = try!(kern_recv(waiter, |reply| { match reply { - kern::RpcRecvRequest { slot } => { - let mut data = io::Cursor::new(data); - rpc::recv_return(&mut data, &tag, slot, &|size| { - try!(kern_send(waiter, kern::RpcRecvReply { - alloc_size: size, exception: None - })); - kern_recv(waiter, |reply| { - match reply { - kern::RpcRecvRequest { slot } => Ok(slot), - _ => unreachable!() - } - }) - }) - } + kern::RpcRecvRequest { slot } => Ok(slot), other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } })); + try!(rpc::recv_return(stream, &tag, slot, &|size| { + try!(kern_send(waiter, kern::RpcRecvReply { + alloc_size: size, exception: None + })); + kern_recv(waiter, |reply| { + match reply { + kern::RpcRecvRequest { slot } => Ok(slot), + _ => unreachable!() + } + }) + })); try!(kern_send(waiter, kern::RpcRecvReply { alloc_size: 0, exception: None })); session.kernel_state = KernelState::Running; @@ -352,12 +350,10 @@ fn process_kern_message(waiter: Waiter, match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { - let mut buf = Vec::new(); - try!(rpc::send_args(&mut buf, tag, data)); try!(host_write(stream, host::Reply::RpcRequest { - service: service, - data: &buf[..] + service: service })); + try!(rpc::send_args(stream, tag, data)); if !batch { session.kernel_state = KernelState::RpcWait } diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index b9f7d92fc..7babdc35a 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -26,7 +26,7 @@ pub enum Request { LoadKernel(Vec), RunKernel, - RpcReply { tag: Vec, data: Vec }, + RpcReply { tag: Vec }, RpcException { name: String, message: String, @@ -45,29 +45,17 @@ pub enum Request { impl Request { pub fn read_from(reader: &mut Read) -> io::Result { - const HEADER_SIZE: usize = 9; - try!(read_sync(reader)); - let length = try!(read_u32(reader)) as usize; - let ty = try!(read_u8(reader)); - - Ok(match ty { + Ok(match try!(read_u8(reader)) { 1 => Request::Log, 2 => Request::LogClear, 3 => Request::Ident, 4 => Request::SwitchClock(try!(read_u8(reader))), - 5 => { - let mut code = vec![0; length - HEADER_SIZE]; - try!(reader.read_exact(&mut code)); - Request::LoadKernel(code) - } + 5 => Request::LoadKernel(try!(read_bytes(reader))), 6 => Request::RunKernel, - 7 => { - let tag = try!(read_bytes(reader)); - let mut data = vec![0; length - HEADER_SIZE - 4 - tag.len()]; - try!(reader.read_exact(&mut data)); - Request::RpcReply { tag: tag, data: data } - } + 7 => Request::RpcReply { + tag: try!(read_bytes(reader)) + }, 8 => Request::RpcException { name: try!(read_string(reader)), message: try!(read_string(reader)), @@ -119,7 +107,7 @@ pub enum Reply<'a> { backtrace: &'a [usize] }, - RpcRequest { service: u32, data: &'a [u8] }, + RpcRequest { service: u32 }, FlashRead(&'a [u8]), FlashOk, @@ -131,88 +119,80 @@ pub enum Reply<'a> { impl<'a> Reply<'a> { pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { - let mut buf = Vec::new(); - try!(write_sync(&mut buf)); - try!(write_u32(&mut buf, 0)); // length placeholder - + try!(write_sync(writer)); match *self { Reply::Log(ref log) => { - try!(write_u8(&mut buf, 1)); - try!(buf.write(log.as_bytes())); + try!(write_u8(writer, 1)); + try!(write_string(writer, log)); }, Reply::Ident(ident) => { - try!(write_u8(&mut buf, 2)); - try!(buf.write(b"AROR")); - try!(buf.write(ident.as_bytes())); + try!(write_u8(writer, 2)); + try!(writer.write(b"AROR")); + try!(write_string(writer, ident)); }, Reply::ClockSwitchCompleted => { - try!(write_u8(&mut buf, 3)); + try!(write_u8(writer, 3)); }, Reply::ClockSwitchFailed => { - try!(write_u8(&mut buf, 4)); + try!(write_u8(writer, 4)); }, Reply::LoadCompleted => { - try!(write_u8(&mut buf, 5)); + try!(write_u8(writer, 5)); }, Reply::LoadFailed => { - try!(write_u8(&mut buf, 6)); + try!(write_u8(writer, 6)); }, Reply::KernelFinished => { - try!(write_u8(&mut buf, 7)); + try!(write_u8(writer, 7)); }, Reply::KernelStartupFailed => { - try!(write_u8(&mut buf, 8)); + try!(write_u8(writer, 8)); }, Reply::KernelException { name, message, param, file, line, column, function, backtrace } => { - try!(write_u8(&mut buf, 9)); - try!(write_string(&mut buf, name)); - try!(write_string(&mut buf, message)); - try!(write_u64(&mut buf, param[0])); - try!(write_u64(&mut buf, param[1])); - try!(write_u64(&mut buf, param[2])); - try!(write_string(&mut buf, file)); - try!(write_u32(&mut buf, line)); - try!(write_u32(&mut buf, column)); - try!(write_string(&mut buf, function)); - try!(write_u32(&mut buf, backtrace.len() as u32)); + try!(write_u8(writer, 9)); + try!(write_string(writer, name)); + try!(write_string(writer, message)); + try!(write_u64(writer, param[0])); + try!(write_u64(writer, param[1])); + try!(write_u64(writer, param[2])); + try!(write_string(writer, file)); + try!(write_u32(writer, line)); + try!(write_u32(writer, column)); + try!(write_string(writer, function)); + try!(write_u32(writer, backtrace.len() as u32)); for &addr in backtrace { - try!(write_u32(&mut buf, addr as u32)) + try!(write_u32(writer, addr as u32)) } }, - Reply::RpcRequest { service, data } => { - try!(write_u8(&mut buf, 10)); - try!(write_u32(&mut buf, service)); - try!(buf.write(data)); + Reply::RpcRequest { service } => { + try!(write_u8(writer, 10)); + try!(write_u32(writer, service)); }, Reply::FlashRead(ref bytes) => { - try!(write_u8(&mut buf, 11)); - try!(buf.write(bytes)); + try!(write_u8(writer, 11)); + try!(write_bytes(writer, bytes)); }, Reply::FlashOk => { - try!(write_u8(&mut buf, 12)); + try!(write_u8(writer, 12)); }, Reply::FlashError => { - try!(write_u8(&mut buf, 13)); + try!(write_u8(writer, 13)); }, Reply::WatchdogExpired => { - try!(write_u8(&mut buf, 14)); + try!(write_u8(writer, 14)); }, Reply::ClockFailure => { - try!(write_u8(&mut buf, 15)); + try!(write_u8(writer, 15)); }, } - - let len = buf.len(); - try!(write_u32(&mut &mut buf[4..8], len as u32)); - - writer.write_all(&buf) + Ok(()) } } From b4bbf44a0aebb526befeb39757634050f86f038e Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 17:32:26 +0000 Subject: [PATCH 047/127] runtime: buffer writes of RPC data. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves upload speed ~166×. --- artiq/runtime.rs/src/rpc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc.rs index beef28119..21b966dc9 100644 --- a/artiq/runtime.rs/src/rpc.rs +++ b/artiq/runtime.rs/src/rpc.rs @@ -1,5 +1,5 @@ use std::slice; -use std::io::{self, Read, Write}; +use std::io::{self, Read, Write, BufWriter}; use proto::*; use self::tag::{Tag, TagIterator, split_tag}; @@ -98,6 +98,7 @@ 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(()), From b52ecda1d532b91d5feec6470e571524efbf5ada Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 18:05:38 +0000 Subject: [PATCH 048/127] runtime: make memory map saner. --- artiq/gateware/amp/kernel_cpu.py | 2 +- artiq/runtime.rs/src/kernel.rs | 2 +- artiq/runtime/ksupport.c | 4 +- artiq/runtime/ksupport.ld | 12 +-- artiq/runtime/runtime.ld | 123 ++++++++++++++++--------------- 5 files changed, 71 insertions(+), 72 deletions(-) diff --git a/artiq/gateware/amp/kernel_cpu.py b/artiq/gateware/amp/kernel_cpu.py index b8aa29803..2736027e3 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=0x42000000, + exec_address=0x40400000, main_mem_origin=0x40000000, l2_size=8192): self._reset = CSRStorage(reset=1) diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index bffc2c12e..8f796ebe5 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -2,7 +2,7 @@ use core::ptr; use board::csr; use mailbox; -const KERNELCPU_EXEC_ADDRESS: usize = 0x42000000; +const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000; const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024); const KSUPPORT_HEADER_SIZE: usize = 0x80; diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 5de733c5e..959d61623 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -16,8 +16,8 @@ #include "dds.h" #include "i2c.h" -#define KERNELCPU_EXEC_ADDRESS 0x42000000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000 +#define KERNELCPU_EXEC_ADDRESS 0x40400000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x40420000 #define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) #define KSUPPORT_HEADER_SIZE 0x80 diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index da231b69b..4985009fe 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -3,18 +3,16 @@ ENTRY(_start) INCLUDE generated/regions.ld -/* First 32M of main memory are reserved for runtime +/* First 4M of main memory are reserved for runtime * code/data/heap, then comes kernel memory. * First 128K of kernel memory are for support code. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x42000000, LENGTH = 0x20000 + ksupport (RWX) : ORIGIN = 0x40400000, LENGTH = 0x20000 } -/* On AMP systems, kernel stack is at the end of main RAM, - * before the runtime stack. Leave 1M for runtime stack. - */ -PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 1024*1024 - 4); +/* Kernel stack is at the end of main RAM. */ +PROVIDE(_fstack = ORIGIN(main_ram) + LENGTH(main_ram) - 4); /* Force ld to make the ELF header as loadable. */ PHDRS @@ -73,7 +71,5 @@ SECTIONS *(COMMON) . = ALIGN(4); _ebss = .; - . = ALIGN(8); - _heapstart = .; } } diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index b912c23c8..eee37d9cf 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -7,75 +7,78 @@ INCLUDE generated/regions.ld * ld does not allow this expression here. */ MEMORY { - runtime : ORIGIN = 0x40000000, LENGTH = 0x2000000 /* 32M */ + runtime (RWX) : ORIGIN = 0x40000000, LENGTH = 0x400000 /* 4M */ } -/* Kernel memory space start right after the runtime, - * and ends before the runtime stack. - * Runtime stack is always at the end of main_ram. - * This stack is shared with the kernel on UP systems. - */ -PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 4); - SECTIONS { - .text : - { - _ftext = .; - *(.text .stub .text.* .gnu.linkonce.t.*) - _etext = .; - } > runtime + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > runtime - /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ - .got : { - _GLOBAL_OFFSET_TABLE_ = .; - *(.got) - } > runtime + /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ + .got : { + _GLOBAL_OFFSET_TABLE_ = .; + *(.got) + } > runtime - .got.plt : { - *(.got.plt) - } > runtime + .got.plt : { + *(.got.plt) + } > runtime - .rodata : - { - . = ALIGN(4); - _frodata = .; - *(.rodata .rodata.* .gnu.linkonce.r.*) - *(.rodata1) - _erodata = .; - } > runtime + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > runtime - .data : - { - . = ALIGN(4); - _fdata = .; - *(.data .data.* .gnu.linkonce.d.*) - *(.data1) - *(.sdata .sdata.* .gnu.linkonce.s.*) - _edata = .; - } > runtime + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = .; + } > runtime - .bss : - { - . = ALIGN(4); - _fbss = .; - *(.dynsbss) - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(4); - _ebss = .; - . = ALIGN(8); - } > runtime + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > runtime - /DISCARD/ : - { - *(.eh_frame) - } + .stack : + { + . = ALIGN(0x1000); + _estack = .; + . += 0x4000; + _fstack = . - 4; + } > runtime - _fheap = .; - . += 0x1800000; - _eheap = .; + .heap : + { + _fheap = .; + . = ORIGIN(runtime) + LENGTH(runtime) - 0x1000; + _eheap = .; + } > runtime + + /DISCARD/ : + { + *(.eh_frame) + } } From 4f11b071a5d64bc453aec3bc6092c6aa93d8733e Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 05:21:07 +0000 Subject: [PATCH 049/127] runtime: remove useless handshaking in analyzer. --- artiq/coredevice/comm_tcp.py | 2 +- artiq/runtime.rs/src/analyzer.rs | 21 ++------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/artiq/coredevice/comm_tcp.py b/artiq/coredevice/comm_tcp.py index 7c9395609..9d0a22703 100644 --- a/artiq/coredevice/comm_tcp.py +++ b/artiq/coredevice/comm_tcp.py @@ -29,7 +29,6 @@ def initialize_connection(host, port): sock.settimeout(None) set_keepalive(sock, 3, 2, 3) logger.debug("connected to host %s on port %d", host, port) - sock.sendall(b"ARTIQ coredev\n") return sock @@ -44,6 +43,7 @@ class Comm(CommGeneric): if hasattr(self, "socket"): return self.socket = initialize_connection(self.host, self.port) + self.socket.sendall(b"ARTIQ coredev\n") def close(self): if not hasattr(self, "socket"): diff --git a/artiq/runtime.rs/src/analyzer.rs b/artiq/runtime.rs/src/analyzer.rs index c16bf1f95..ea126512e 100644 --- a/artiq/runtime.rs/src/analyzer.rs +++ b/artiq/runtime.rs/src/analyzer.rs @@ -1,4 +1,4 @@ -use std::io::{self, Read, Write}; +use std::io::{self, Write}; use board::{self, csr}; use sched::{Waiter, Spawner}; use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; @@ -68,19 +68,6 @@ fn worker(mut stream: TcpStream) -> io::Result<()> { Ok(()) } -// TODO: remove this, it's pointless in analyzer -fn check_magic(stream: &mut TcpStream) -> io::Result<()> { - const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; - - let mut magic: [u8; 14] = [0; 14]; - try!(stream.read_exact(&mut magic)); - if magic != MAGIC { - Err(io::Error::new(io::ErrorKind::InvalidData, "unrecognized magic")) - } else { - Ok(()) - } -} - pub fn thread(waiter: Waiter, _spawner: Spawner) { // verify that the hack above works assert!(::core::mem::align_of::() == 64); @@ -92,11 +79,7 @@ pub fn thread(waiter: Waiter, _spawner: Spawner) { loop { arm(); - let (mut stream, addr) = listener.accept().expect("cannot accept client"); - match check_magic(&mut stream) { - Ok(()) => (), - Err(_) => continue - } + let (stream, addr) = listener.accept().expect("cannot accept client"); info!("connection from {}", addr); disarm(); From 8be60cc223056f42ea4c0f47d854bdcd31208ee9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 05:53:50 +0000 Subject: [PATCH 050/127] runtime: fix KERNELCPU_LAST_ADDRESS after layout change. --- artiq/runtime.rs/src/kernel.rs | 2 +- artiq/runtime/ksupport.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index 8f796ebe5..f38e951f0 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -3,7 +3,7 @@ use board::csr; use mailbox; const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000; -const KERNELCPU_LAST_ADDRESS: usize = (0x4fffffff - 1024*1024); +const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; const KSUPPORT_HEADER_SIZE: usize = 0x80; pub unsafe fn start() { diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 959d61623..8ec3185fc 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -18,7 +18,7 @@ #define KERNELCPU_EXEC_ADDRESS 0x40400000 #define KERNELCPU_PAYLOAD_ADDRESS 0x40420000 -#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024) +#define KERNELCPU_LAST_ADDRESS 0x4fffffff #define KSUPPORT_HEADER_SIZE 0x80 double round(double x); From ef10344b3e0d74fd5f1e7f398e4aaa66ecbd8e6a Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 06:27:10 +0000 Subject: [PATCH 051/127] runtime: rewrite isr() in Rust. --- artiq/runtime.rs/src/board.rs | 125 ++++++++++++++++++++++++++++++++++ artiq/runtime.rs/src/lib.rs | 13 +++- artiq/runtime/Makefile | 2 +- artiq/runtime/isr.c | 14 ---- 4 files changed, 138 insertions(+), 16 deletions(-) delete mode 100644 artiq/runtime/isr.c diff --git a/artiq/runtime.rs/src/board.rs b/artiq/runtime.rs/src/board.rs index 21c8d3d85..c73ffd0f5 100644 --- a/artiq/runtime.rs/src/board.rs +++ b/artiq/runtime.rs/src/board.rs @@ -1,8 +1,133 @@ +#![allow(dead_code)] + use core::{cmp, ptr, str}; include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs")); include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs")); +pub mod spr { + pub unsafe fn mfspr(reg: u32) -> u32 { + let value: u32; + asm!("l.mfspr $0, $1, 0" : "=r"(value) : "r"(reg) : : "volatile"); + value + } + + pub unsafe fn mtspr(reg: u32, value: u32) { + asm!("l.mtspr $0, $1, 0" : : "r"(reg), "r"(value) : : "volatile") + } + + /* Definition of special-purpose registers (SPRs). */ + + pub const MAX_GRPS: u32 = 32; + pub const MAX_SPRS_PER_GRP_BITS: u32 = 11; + pub const MAX_SPRS_PER_GRP: u32 = 1 << MAX_SPRS_PER_GRP_BITS; + pub const MAX_SPRS: u32 = 0x10000; + + /* Base addresses for the groups */ + pub const SPRGROUP_SYS: u32 = 0 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_DMMU: u32 = 1 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_IMMU: u32 = 2 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_DC: u32 = 3 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_IC: u32 = 4 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_MAC: u32 = 5 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_D: u32 = 6 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_PC: u32 = 7 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_PM: u32 = 8 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_PIC: u32 = 9 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_TT: u32 = 10 << MAX_SPRS_PER_GRP_BITS; + pub const SPRGROUP_FP: u32 = 11 << MAX_SPRS_PER_GRP_BITS; + + /* System control and status group */ + pub const SPR_VR: u32 = SPRGROUP_SYS + 0; + pub const SPR_UPR: u32 = SPRGROUP_SYS + 1; + pub const SPR_CPUCFGR: u32 = SPRGROUP_SYS + 2; + pub const SPR_DMMUCFGR: u32 = SPRGROUP_SYS + 3; + pub const SPR_IMMUCFGR: u32 = SPRGROUP_SYS + 4; + pub const SPR_DCCFGR: u32 = SPRGROUP_SYS + 5; + pub const SPR_ICCFGR: u32 = SPRGROUP_SYS + 6; + pub const SPR_DCFGR: u32 = SPRGROUP_SYS + 7; + pub const SPR_PCCFGR: u32 = SPRGROUP_SYS + 8; + pub const SPR_VR2: u32 = SPRGROUP_SYS + 9; + pub const SPR_AVR: u32 = SPRGROUP_SYS + 10; + pub const SPR_EVBAR: u32 = SPRGROUP_SYS + 11; + pub const SPR_AECR: u32 = SPRGROUP_SYS + 12; + pub const SPR_AESR: u32 = SPRGROUP_SYS + 13; + pub const SPR_NPC: u32 = SPRGROUP_SYS + 16; /* CZ 21/06/01 */ + pub const SPR_SR: u32 = SPRGROUP_SYS + 17; /* CZ 21/06/01 */ + pub const SPR_PPC: u32 = SPRGROUP_SYS + 18; /* CZ 21/06/01 */ + pub const SPR_FPCSR: u32 = SPRGROUP_SYS + 20; /* CZ 21/06/01 */ + pub const SPR_ISR_BASE: u32 = SPRGROUP_SYS + 21; + pub const SPR_EPCR_BASE: u32 = SPRGROUP_SYS + 32; /* CZ 21/06/01 */ + pub const SPR_EPCR_LAST: u32 = SPRGROUP_SYS + 47; /* CZ 21/06/01 */ + pub const SPR_EEAR_BASE: u32 = SPRGROUP_SYS + 48; + pub const SPR_EEAR_LAST: u32 = SPRGROUP_SYS + 63; + pub const SPR_ESR_BASE: u32 = SPRGROUP_SYS + 64; + pub const SPR_ESR_LAST: u32 = SPRGROUP_SYS + 79; + pub const SPR_GPR_BASE: u32 = SPRGROUP_SYS + 1024; + + // [snip] + + /* PIC group */ + pub const SPR_PICMR: u32 = SPRGROUP_PIC + 0; + pub const SPR_PICPR: u32 = SPRGROUP_PIC + 1; + pub const SPR_PICSR: u32 = SPRGROUP_PIC + 2; + + // [snip] + + /* + * Bit definitions for the Supervision Register + * + */ + pub const SPR_SR_SM: u32 = 0x00000001; /* Supervisor Mode */ + pub const SPR_SR_TEE: u32 = 0x00000002; /* Tick timer Exception Enable */ + pub const SPR_SR_IEE: u32 = 0x00000004; /* Interrupt Exception Enable */ + pub const SPR_SR_DCE: u32 = 0x00000008; /* Data Cache Enable */ + pub const SPR_SR_ICE: u32 = 0x00000010; /* Instruction Cache Enable */ + pub const SPR_SR_DME: u32 = 0x00000020; /* Data MMU Enable */ + pub const SPR_SR_IME: u32 = 0x00000040; /* Instruction MMU Enable */ + pub const SPR_SR_LEE: u32 = 0x00000080; /* Little Endian Enable */ + pub const SPR_SR_CE: u32 = 0x00000100; /* CID Enable */ + pub const SPR_SR_F: u32 = 0x00000200; /* Condition Flag */ + pub const SPR_SR_CY: u32 = 0x00000400; /* Carry flag */ + pub const SPR_SR_OV: u32 = 0x00000800; /* Overflow flag */ + pub const SPR_SR_OVE: u32 = 0x00001000; /* Overflow flag Exception */ + pub const SPR_SR_DSX: u32 = 0x00002000; /* Delay Slot Exception */ + pub const SPR_SR_EPH: u32 = 0x00004000; /* Exception Prefix High */ + pub const SPR_SR_FO: u32 = 0x00008000; /* Fixed one */ + pub const SPR_SR_SUMRA: u32 = 0x00010000; /* Supervisor SPR read access */ + pub const SPR_SR_RES: u32 = 0x0ffe0000; /* Reserved */ + pub const SPR_SR_CID: u32 = 0xf0000000; /* Context ID */ + +} + +pub mod irq { + use super::spr::*; + + pub fn get_ie() -> bool { + unsafe { mfspr(SPR_SR) & SPR_SR_IEE != 0 } + } + + pub fn set_ie(ie: bool) { + if ie { + unsafe { mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE) } + } else { + unsafe { mtspr(SPR_SR, mfspr(SPR_SR) & !SPR_SR_IEE) } + } + } + + pub fn get_mask() -> u32 { + unsafe { mfspr(SPR_PICMR) } + } + + pub fn set_mask(mask: u32) { + unsafe { mtspr(SPR_PICMR, mask) } + } + + pub fn pending() -> u32 { + unsafe { mfspr(SPR_PICSR) } + } +} + extern { pub fn flush_cpu_dcache(); pub fn flush_l2_cache(); diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 45728f469..278b738e6 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd)] +#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm)] #[macro_use] extern crate std_artiq as std; @@ -70,6 +70,17 @@ pub unsafe extern fn rust_main() { }) } +#[no_mangle] +pub unsafe extern fn isr() { + use board::{irq, csr}; + extern { fn uart_isr(); } + + let irqs = irq::pending() & irq::get_mask(); + if irqs & (1 << csr::UART_INTERRUPT) != 0 { + uart_isr() + } +} + #[no_mangle] pub fn sys_now() -> u32 { clock::get_ms() as u32 diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 5bc5325f4..2990c9b23 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -3,7 +3,7 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 -OBJECTS := isr.o flash_storage.o ksupport_data.o main.o +OBJECTS := flash_storage.o ksupport_data.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ rtio.o dds.o i2c.o diff --git a/artiq/runtime/isr.c b/artiq/runtime/isr.c deleted file mode 100644 index f42fa0694..000000000 --- a/artiq/runtime/isr.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include - -void isr(void); -void isr(void) -{ - unsigned int irqs; - - irqs = irq_pending() & irq_getmask(); - - if(irqs & (1 << UART_INTERRUPT)) - uart_isr(); -} From 8eeb6ea1b99956849c5e43b80f052c12e36d27d0 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 07:53:38 +0000 Subject: [PATCH 052/127] packaging: include runtime.rs in MANIFEST. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 2bdea6226..3fb533874 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ graft artiq/runtime +graft artiq/runtime.rs graft artiq/examples include artiq/gui/logo*.svg include versioneer.py From 4d790b452c11675fc82154d51bc61023169c168e Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 08:30:09 +0000 Subject: [PATCH 053/127] runtime: discard unnecessary sections. --- artiq/runtime/runtime.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index eee37d9cf..abcc69e91 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -80,5 +80,6 @@ SECTIONS /DISCARD/ : { *(.eh_frame) + *(.gcc_except_table) } } From 9c3394794e680e279f033e7b4794362a32d65041 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 7 Oct 2016 14:24:12 +0000 Subject: [PATCH 054/127] runtime: cap log level at debug. --- artiq/runtime.rs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 725da7533..29d415a15 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 } +log = { version = "0.3", default-features = false, features = ["max_level_debug"] } log_buffer = { version = "1.0" } byteorder = { version = "0.5", default-features = false } From 290498aca0d7709c5ed2d7053f0749ba4cbe8c30 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 12 Oct 2016 16:33:40 +0200 Subject: [PATCH 055/127] conda: misoc 0.4 (csr) --- conda/artiq-kc705-nist_clock/meta.yaml | 2 +- conda/artiq-kc705-nist_qc1/meta.yaml | 2 +- conda/artiq-kc705-nist_qc2/meta.yaml | 2 +- conda/artiq-pipistrello-nist_qc1/meta.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conda/artiq-kc705-nist_clock/meta.yaml b/conda/artiq-kc705-nist_clock/meta.yaml index 673f61257..853a5cfae 100644 --- a/conda/artiq-kc705-nist_clock/meta.yaml +++ b/conda/artiq-kc705-nist_clock/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-kc705-nist_qc1/meta.yaml b/conda/artiq-kc705-nist_qc1/meta.yaml index aa577ef8d..d5be16c2e 100644 --- a/conda/artiq-kc705-nist_qc1/meta.yaml +++ b/conda/artiq-kc705-nist_qc1/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-kc705-nist_qc2/meta.yaml b/conda/artiq-kc705-nist_qc2/meta.yaml index 7dce4b74f..54e5d2579 100644 --- a/conda/artiq-kc705-nist_qc2/meta.yaml +++ b/conda/artiq-kc705-nist_qc2/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-pipistrello-nist_qc1/meta.yaml b/conda/artiq-pipistrello-nist_qc1/meta.yaml index 14241a705..a69497fff 100644 --- a/conda/artiq-pipistrello-nist_qc1/meta.yaml +++ b/conda/artiq-pipistrello-nist_qc1/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k From e037d167f41ae48c1d7cfb865b4efe4f239935f1 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 12 Oct 2016 23:45:17 +0100 Subject: [PATCH 056/127] language: Add "A" (ampere) as well-known unit for arguments Signed-off-by: David Nadlinger --- artiq/language/units.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/language/units.py b/artiq/language/units.py index 83f1133a8..8c5266c74 100644 --- a/artiq/language/units.py +++ b/artiq/language/units.py @@ -18,3 +18,4 @@ _register_unit("s", "pnum_") _register_unit("Hz", "_kMG") _register_unit("dB", "_") _register_unit("V", "um_k") +_register_unit("A", "um_") From fee75bd50f9240b7b0377291718d7354498c9b62 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 15 Oct 2016 23:38:54 +0000 Subject: [PATCH 057/127] runtime: remove some redundant libm functions copied inline. --- .../compiler/transforms/llvm_ir_generator.py | 9 +- artiq/runtime/ksupport.c | 134 +----------------- 2 files changed, 9 insertions(+), 134 deletions(-) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index ecc21c3cb..1e5dfcde3 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -323,8 +323,6 @@ 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": @@ -349,6 +347,8 @@ 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 == "recv_rpc": @@ -1041,9 +1041,8 @@ class LLVMIRGenerator: name=insn.name) elif insn.op == "round": llarg = self.map(insn.operands[0]) - llvalue = self.llbuilder.call(self.llbuiltin("llvm.round.f64"), [llarg]) - return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type), - name=insn.name) + return self.llbuilder.call(self.llbuiltin("lround"), [llarg], + name=insn.name) elif insn.op == "globalenv": def get_outer(llenv, env_ty): if "$outer" in env_ty.params: diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 8ec3185fc..aaa313373 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -21,8 +22,7 @@ #define KERNELCPU_LAST_ADDRESS 0x4fffffff #define KSUPPORT_HEADER_SIZE 0x80 -double round(double x); -double sqrt(double x); +long lround(double x); void ksupport_abort(void); static void attribute_writeback(void *); @@ -91,8 +91,8 @@ static const struct symbol runtime_exports[] = { {"__powidf2", &__powidf2}, /* libm */ - {"round", &round}, {"sqrt", &sqrt}, + {"lround", &lround}, /* exceptions */ {"_Unwind_Resume", &_Unwind_Resume}, @@ -144,133 +144,9 @@ static const struct symbol runtime_exports[] = { {NULL, NULL} }; -double round(double x) +long lround(double 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; -} - -double sqrt(double x) -{ - static const double one = 1.0, tiny = 1.0e-300; - double z; - int32_t sign = (int)0x80000000; - int32_t ix0,s0,q,m,t,i; - uint32_t r,t1,s1,ix1,q1; - - union {double f; struct{uint32_t msw; uint32_t lsw;};} u = {x}; - ix0 = u.msw; - ix1 = u.lsw; - - /* take care of Inf and NaN */ - if((ix0&0x7ff00000)==0x7ff00000) { - return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf - sqrt(-inf)=sNaN */ - } - /* take care of zero */ - if(ix0<=0) { - if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ - else if(ix0<0) - return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ - } - /* normalize x */ - m = (ix0>>20); - if(m==0) { /* subnormal x */ - while(ix0==0) { - m -= 21; - ix0 |= (ix1>>11); ix1 <<= 21; - } - for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; - m -= i-1; - ix0 |= (ix1>>(32-i)); - ix1 <<= i; - } - m -= 1023; /* unbias exponent */ - ix0 = (ix0&0x000fffff)|0x00100000; - if(m&1){ /* odd m, double x to make it even */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - } - m >>= 1; /* m = [m/2] */ - - /* generate sqrt(x) bit by bit */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ - r = 0x00200000; /* r = moving bit from right to left */ - - while(r!=0) { - t = s0+r; - if(t<=ix0) { - s0 = t+r; - ix0 -= t; - q += r; - } - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - r>>=1; - } - - r = sign; - while(r!=0) { - t1 = s1+r; - t = s0; - if((t>31); - ix1 += ix1; - r>>=1; - } - - /* use floating add to find out rounding direction */ - if((ix0|ix1)!=0) { - z = one-tiny; /* trigger inexact flag */ - if (z>=one) { - z = one+tiny; - if (q1==(uint32_t)0xffffffff) { q1=0; q += 1;} - else if (z>one) { - if (q1==(uint32_t)0xfffffffe) q+=1; - q1+=2; - } else - q1 += (q1&1); - } - } - ix0 = (q>>1)+0x3fe00000; - ix1 = q1>>1; - if ((q&1)==1) ix1 |= sign; - ix0 += (m <<20); - - u.msw = ix0; - u.lsw = ix1; - return u.f; + return x < 0 ? floor(x) : ceil(x); } /* called by libunwind */ From a8c017bfccbad9bd6657e289be5d4b6d35ed8da3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 16 Oct 2016 16:24:25 +0000 Subject: [PATCH 058/127] runtime: port ksupport to Rust. --- artiq/compiler/module.py | 7 - artiq/compiler/targets.py | 7 +- artiq/compiler/testbench/perf_embedding.py | 4 +- artiq/runtime.rs/libksupport/Cargo.lock | 3 + artiq/runtime.rs/libksupport/Cargo.toml | 13 + artiq/runtime.rs/libksupport/api.rs | 120 +++++ artiq/runtime.rs/libksupport/dyld.rs | 57 +++ artiq/runtime.rs/libksupport/lib.rs | 252 ++++++++++ artiq/runtime.rs/libstd_artiq/lib.rs | 5 +- artiq/runtime.rs/src/kernel.rs | 16 +- artiq/runtime.rs/src/kernel_proto.rs | 407 +--------------- artiq/runtime.rs/src/session.rs | 186 +++++--- artiq/runtime/Makefile | 108 ++--- artiq/runtime/ksupport.c | 509 --------------------- artiq/runtime/ksupport.h | 17 - artiq/runtime/ksupport.ld | 24 +- artiq/runtime/ksupport_glue.c | 98 ++++ artiq/runtime/mailbox.c | 59 --- artiq/runtime/mailbox.h | 12 - artiq/runtime/messages.h | 125 ----- artiq/runtime/runtime.ld | 5 +- 21 files changed, 773 insertions(+), 1261 deletions(-) create mode 100644 artiq/runtime.rs/libksupport/Cargo.lock create mode 100644 artiq/runtime.rs/libksupport/Cargo.toml create mode 100644 artiq/runtime.rs/libksupport/api.rs create mode 100644 artiq/runtime.rs/libksupport/dyld.rs create mode 100644 artiq/runtime.rs/libksupport/lib.rs delete mode 100644 artiq/runtime/ksupport.c delete mode 100644 artiq/runtime/ksupport.h create mode 100644 artiq/runtime/ksupport_glue.c delete mode 100644 artiq/runtime/mailbox.c delete mode 100644 artiq/runtime/mailbox.h delete mode 100644 artiq/runtime/messages.h diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index 355e3072d..0e07a5b60 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -81,13 +81,6 @@ class Module: embedding_map=self.embedding_map) return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True) - def entry_point(self): - """Return the name of the function that is the entry point of this module.""" - if self.name != "": - return self.name + ".__modinit__" - else: - return "__modinit__" - def __repr__(self): printer = types.TypePrinter() globals = ["%s: %s" % (var, printer.name(self.globals[var])) for var in self.globals] diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index dff28bb2e..aab813737 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -161,9 +161,9 @@ class Target: return llmachine.emit_object(llmodule) - def link(self, objects, init_fn): + def link(self, objects): """Link the relocatable objects into a shared library for this target.""" - with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr", "-init", init_fn] + + with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] + ["{{obj{}}}".format(index) for index in range(len(objects))] + ["-o", "{output}"], output=b"", @@ -177,8 +177,7 @@ class Target: return library def compile_and_link(self, modules): - return self.link([self.assemble(self.compile(module)) for module in modules], - init_fn=modules[0].entry_point()) + return self.link([self.assemble(self.compile(module)) for module in modules]) def strip(self, library): with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"], diff --git a/artiq/compiler/testbench/perf_embedding.py b/artiq/compiler/testbench/perf_embedding.py index 81c0c02db..87d35ee04 100644 --- a/artiq/compiler/testbench/perf_embedding.py +++ b/artiq/compiler/testbench/perf_embedding.py @@ -47,7 +47,7 @@ def main(): target = OR1KTarget() llvm_ir = target.compile(module) elf_obj = target.assemble(llvm_ir) - elf_shlib = target.link([elf_obj], init_fn=module.entry_point()) + elf_shlib = target.link([elf_obj]) benchmark(lambda: embed(), "ARTIQ embedding") @@ -61,7 +61,7 @@ def main(): benchmark(lambda: target.assemble(llvm_ir), "LLVM machine code emission") - benchmark(lambda: target.link([elf_obj], init_fn=module.entry_point()), + benchmark(lambda: target.link([elf_obj]), "Linking") benchmark(lambda: target.strip(elf_shlib), diff --git a/artiq/runtime.rs/libksupport/Cargo.lock b/artiq/runtime.rs/libksupport/Cargo.lock new file mode 100644 index 000000000..042a9a750 --- /dev/null +++ b/artiq/runtime.rs/libksupport/Cargo.lock @@ -0,0 +1,3 @@ +[root] +name = "ksupport" +version = "0.0.0" diff --git a/artiq/runtime.rs/libksupport/Cargo.toml b/artiq/runtime.rs/libksupport/Cargo.toml new file mode 100644 index 000000000..e5a29ff08 --- /dev/null +++ b/artiq/runtime.rs/libksupport/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The ARTIQ Project Developers"] +name = "ksupport" +version = "0.0.0" + +[lib] +name = "ksupport" +path = "lib.rs" +crate-type = ["staticlib"] + +[profile.dev] +panic = 'unwind' +opt-level = 2 diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs new file mode 100644 index 000000000..46570feab --- /dev/null +++ b/artiq/runtime.rs/libksupport/api.rs @@ -0,0 +1,120 @@ +use libc::{c_void, c_char, size_t}; + +macro_rules! api { + ($i:ident) => ({ + extern { static $i: c_void; } + api!($i = &$i as *const _) + }); + ($i:ident, $d:item) => ({ + $d + api!($i = $i) + }); + ($i:ident = $e:expr) => { + (stringify!($i), unsafe { $e as *const () }) + } +} + +pub fn resolve(required: &str) -> usize { + unsafe { + API.iter() + .find(|&&(exported, _)| exported == required) + .map(|&(_, ptr)| ptr as usize) + .unwrap_or(0) + } +} + +#[allow(unused_unsafe)] +static mut API: &'static [(&'static str, *const ())] = &[ + api!(__divsi3), + api!(__modsi3), + api!(__ledf2), + api!(__gedf2), + api!(__unorddf2), + api!(__eqdf2), + api!(__ltdf2), + api!(__nedf2), + api!(__gtdf2), + api!(__negsf2), + api!(__negdf2), + api!(__addsf3), + api!(__subsf3), + api!(__mulsf3), + api!(__divsf3), + api!(__lshrdi3), + api!(__muldi3), + api!(__divdi3), + api!(__ashldi3), + api!(__ashrdi3), + api!(__udivmoddi4), + api!(__floatsisf), + api!(__floatunsisf), + api!(__fixsfsi), + api!(__fixunssfsi), + api!(__adddf3), + api!(__subdf3), + api!(__muldf3), + api!(__divdf3), + api!(__floatsidf), + api!(__floatunsidf), + api!(__floatdidf), + api!(__fixdfsi), + api!(__fixdfdi), + api!(__fixunsdfsi), + api!(__clzsi2), + api!(__ctzsi2), + api!(__udivdi3), + api!(__umoddi3), + api!(__moddi3), + api!(__powidf2), + + /* libc */ + api!(strcmp), + api!(strlen, extern { fn strlen(s: *const c_char) -> size_t; }), + api!(abort = ::abort), + + /* libm */ + api!(sqrt), + api!(lround), + + /* exceptions */ + api!(_Unwind_Resume), + api!(__artiq_personality), + api!(__artiq_raise), + api!(__artiq_reraise), + + /* proxified syscalls */ + api!(core_log), + + api!(now = &::NOW as *const _), + + api!(watchdog_set = ::watchdog_set), + api!(watchdog_clear = ::watchdog_clear), + + api!(send_rpc = ::send_rpc), + api!(recv_rpc = ::recv_rpc), + + api!(cache_get = ::cache_get), + api!(cache_put = ::cache_put), + + /* direct syscalls */ + api!(rtio_init), + api!(rtio_get_counter), + api!(rtio_log), + api!(rtio_output), + api!(rtio_input_timestamp), + api!(rtio_input_data), + +// #if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) + api!(dds_init), + api!(dds_init_sync), + api!(dds_batch_enter), + api!(dds_batch_exit), + api!(dds_set), +// #endif + + api!(i2c_init), + api!(i2c_start), + api!(i2c_stop), + api!(i2c_write), + api!(i2c_read), +]; diff --git a/artiq/runtime.rs/libksupport/dyld.rs b/artiq/runtime.rs/libksupport/dyld.rs new file mode 100644 index 000000000..c25f7daea --- /dev/null +++ b/artiq/runtime.rs/libksupport/dyld.rs @@ -0,0 +1,57 @@ +use core::{ptr, slice, str}; +use core::slice::SliceExt; +use libc::{c_void, c_char, c_int, size_t}; + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Default)] +struct dyld_info { + __opaque: [usize; 7] +} + +extern { + fn dyld_load(shlib: *const c_void, base: usize, + resolve: extern fn(*mut c_void, *const c_char) -> usize, + resolve_data: *mut c_void, + info: *mut dyld_info, error_out: *mut *const c_char) -> c_int; + fn dyld_lookup(symbol: *const c_char, info: *const dyld_info) -> *const c_void; + + fn strlen(ptr: *const c_char) -> size_t; +} + +pub struct Library { + lower: dyld_info +} + +impl Library { + pub unsafe fn load(shlib: &[u8], base: usize, mut resolve: F) + -> Result + where F: Fn(&str) -> usize { + extern fn wrapper(data: *mut c_void, name: *const c_char) -> usize + where F: Fn(&str) -> usize { + unsafe { + let f = data as *mut F; + let name = slice::from_raw_parts(name as *const u8, strlen(name)); + (*f)(str::from_utf8_unchecked(name)) + } + } + + let mut library = Library { lower: dyld_info::default() }; + let mut error: *const c_char = ptr::null(); + if dyld_load(shlib.as_ptr() as *const _, base, + wrapper::, &mut resolve as *mut _ as *mut _, + &mut library.lower, &mut error) == 0 { + let error = slice::from_raw_parts(error as *const u8, strlen(error)); + Err(str::from_utf8_unchecked(error)) + } else { + Ok(library) + } + } + + pub unsafe fn lookup(&self, symbol: &str) -> usize { + assert!(symbol.len() < 32); + let mut buf = [0u8; 32]; + buf[0..symbol.as_bytes().len()].copy_from_slice(symbol.as_bytes()); + dyld_lookup(&buf as *const _ as *const c_char, &self.lower) as usize + } +} diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs new file mode 100644 index 000000000..4163482ab --- /dev/null +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -0,0 +1,252 @@ +#![feature(lang_items, needs_panic_runtime, asm, libc, core_slice_ext)] + +#![no_std] +#![needs_panic_runtime] + +extern crate libc; + +#[path = "../src/board.rs"] +mod board; +#[path = "../src/mailbox.rs"] +mod mailbox; +#[path = "../src/kernel_proto.rs"] +mod kernel_proto; + +mod dyld; +mod api; + +use core::{mem, ptr, slice, str}; +use libc::{c_char, size_t}; +use kernel_proto::*; +use dyld::Library; + +fn send(request: &Message) { + unsafe { mailbox::send(request as *const _ as usize) } + while !mailbox::acknowledged() {} +} + +fn recv R>(f: F) -> R { + while mailbox::receive() == 0 {} + let result = f(unsafe { mem::transmute::(mailbox::receive()) }); + mailbox::acknowledge(); + result +} + +macro_rules! recv { + ($p:pat => $e:expr) => { + recv(|request| { + if let $p = request { + $e + } else { + send(&Log(format_args!("unexpected reply: {:?}", request))); + loop {} + } + }) + } +} + +macro_rules! print { + ($($arg:tt)*) => ($crate::send(&Log(format_args!($($arg)*)))); +} + +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +#[lang = "panic_fmt"] +extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! { + println!("panic at {}:{}: {}", file, line, args); + send(&RunAborted); + loop {} +} + +static mut NOW: u64 = 0; + +#[no_mangle] +pub extern fn send_to_log(ptr: *const u8, len: usize) { + send(&LogSlice(unsafe { + str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) + })) +} + +extern fn abort() -> ! { + println!("kernel called abort()"); + send(&RunAborted); + loop {} +} + +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)) }; + + send(&RpcSend { + service: service as u32, + batch: service == 0, + tag: tag, + data: data + }) +} + +extern fn recv_rpc(slot: *mut ()) -> usize { + send(&RpcRecvRequest(slot)); + recv!(&RpcRecvReply(ref result) => { + match result { + &Ok(alloc_size) => alloc_size, + &Err(ref exception) => unsafe { __artiq_raise(exception as *const _) } + } + }) +} + +#[allow(improper_ctypes)] +extern { + fn __artiq_raise(exn: *const ::kernel_proto::Exception) -> !; +} + +macro_rules! artiq_raise { + ($name:expr, $message:expr) => ({ + let exn = Exception { + name: concat!("0:artiq.coredevice.exceptions.", $name, "\0").as_bytes().as_ptr(), + file: concat!(file!(), "\0").as_bytes().as_ptr(), + line: line!(), + column: column!(), + // https://github.com/rust-lang/rfcs/pull/1719 + function: "(Rust function)\0".as_bytes().as_ptr(), + message: concat!($message, "\0").as_bytes().as_ptr(), + param: [0; 3], + phantom: ::core::marker::PhantomData + }; + unsafe { __artiq_raise(&exn as *const _) } + }) +} + +#[no_mangle] +pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception, + backtrace_data: *mut usize, + backtrace_size: usize) -> ! { + let backtrace = unsafe { slice::from_raw_parts_mut(backtrace_data, backtrace_size) }; + let mut cursor = 0; + for index in 0..backtrace.len() { + if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS { + backtrace[cursor] = backtrace[index] - kernel_proto::KERNELCPU_PAYLOAD_ADDRESS; + cursor += 1; + } + } + let backtrace = &mut backtrace[0..cursor]; + + send(&NowSave(unsafe { NOW })); + send(&RunException { + exception: unsafe { (*exception).clone() }, + backtrace: backtrace + }); + loop {} +} + +extern fn watchdog_set(ms: i32) -> usize { + // FIXME: fix ms + send(&WatchdogSetRequest { ms: ms as u64 }); + recv!(&WatchdogSetReply { id } => id) +} + +extern fn watchdog_clear(id: usize) { + send(&WatchdogClear { id: id }) +} + +extern fn cache_get(key: *const u8) -> (usize, *const u32) { + extern { fn strlen(s: *const c_char) -> size_t; } + let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) }; + let key = unsafe { str::from_utf8_unchecked(key) }; + + send(&CacheGetRequest { key: key }); + recv!(&CacheGetReply { value } => (value.len(), value.as_ptr())) +} + +extern fn cache_put(key: *const u8, &(len, ptr): &(usize, *const u32)) { + extern { fn strlen(s: *const c_char) -> size_t; } + let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) }; + let key = unsafe { str::from_utf8_unchecked(key) }; + + let value = unsafe { slice::from_raw_parts(ptr, len) }; + send(&CachePutRequest { key: key, value: value }); + recv!(&CachePutReply { succeeded } => { + if !succeeded { + artiq_raise!("CacheError", "cannot put into a busy cache row") + } + }) +} + +unsafe fn attribute_writeback(typeinfo: *const ()) { + struct Attr { + offset: usize, + tag: *const u8, + name: *const u8 + } + + struct Type { + attributes: *const *const Attr, + objects: *const *const () + } + + let mut tys = typeinfo as *const *const Type; + while !(*tys).is_null() { + let ty = *tys; + tys = tys.offset(1); + + let mut objects = (*ty).objects; + while !(*objects).is_null() { + let object = *objects; + objects = objects.offset(1); + + let mut attributes = (*ty).attributes; + while !(*attributes).is_null() { + let attribute = *attributes; + attributes = attributes.offset(1); + + if !(*attribute).tag.is_null() { + send_rpc(0, (*attribute).tag, [ + &object as *const _ as *const (), + &(*attribute).name as *const _ as *const (), + (object as usize + (*attribute).offset) as *const () + ].as_ptr()); + } + } + } + } +} + +#[no_mangle] +pub unsafe fn main() { + let library = recv!(&LoadRequest(library) => { + match Library::load(library, kernel_proto::KERNELCPU_PAYLOAD_ADDRESS, api::resolve) { + Err(error) => { + send(&LoadReply(Err(error))); + loop {} + }, + Ok(library) => { + send(&LoadReply(Ok(()))); + library + } + } + }); + + let __bss_start = library.lookup("__bss_start"); + let _end = library.lookup("_end"); + ptr::write_bytes(__bss_start as *mut u8, 0, _end - __bss_start); + + send(&NowInitRequest); + recv!(&NowInitReply(now) => NOW = now); + (mem::transmute::(library.lookup("__modinit__")))(); + send(&NowSave(NOW)); + + attribute_writeback(library.lookup("typeinfo") as *const ()); + + send(&RunFinished); + + loop {} +} + +#[no_mangle] +pub fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) { + println!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea); + send(&RunAborted) +} diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index e2fe3b9d8..568dc8663 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -65,9 +65,8 @@ pub fn print_fmt(args: self::core::fmt::Arguments) { #[lang = "panic_fmt"] extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { - let _ = write!(Console, "panic at {}:{}: ", file, line); - let _ = Console.write_fmt(args); - let _ = write!(Console, "\nwaiting for debugger...\n"); + let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args); + let _ = write!(Console, "waiting for debugger...\n"); unsafe { let _ = readchar(); loop { asm!("l.trap 0") } diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs index f38e951f0..e50902cf5 100644 --- a/artiq/runtime.rs/src/kernel.rs +++ b/artiq/runtime.rs/src/kernel.rs @@ -2,9 +2,7 @@ use core::ptr; use board::csr; use mailbox; -const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000; -const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; -const KSUPPORT_HEADER_SIZE: usize = 0x80; +use kernel_proto::{KERNELCPU_EXEC_ADDRESS, KERNELCPU_LAST_ADDRESS, KSUPPORT_HEADER_SIZE}; pub unsafe fn start() { if csr::kernel_cpu::reset_read() == 0 { @@ -13,15 +11,9 @@ pub unsafe fn start() { stop(); - extern { - static _binary_ksupport_elf_start: u8; - static _binary_ksupport_elf_end: u8; - } - let ksupport_start = &_binary_ksupport_elf_start as *const _; - let ksupport_end = &_binary_ksupport_elf_end as *const _; - ptr::copy_nonoverlapping(ksupport_start, - (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8, - ksupport_end as usize - ksupport_start as usize); + let ksupport_image = include_bytes!(concat!(env!("CARGO_TARGET_DIR"), "/../ksupport.elf")); + let ksupport_addr = (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8; + ptr::copy_nonoverlapping(ksupport_image.as_ptr(), ksupport_addr, ksupport_image.len()); csr::kernel_cpu::reset_write(0); } diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs index 64a074518..a75875738 100644 --- a/artiq/runtime.rs/src/kernel_proto.rs +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -1,24 +1,30 @@ -use core::{ptr, mem, slice}; -use std::string::String; -use std::io; -use mailbox; -use kernel; +#![allow(dead_code)] -#[derive(Debug)] +use core::marker::PhantomData; +use core::fmt; + +pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40400000; +pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40440000; +pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; +pub const KSUPPORT_HEADER_SIZE: usize = 0x80; + +#[repr(C)] +#[derive(Debug, Clone)] pub struct Exception<'a> { - pub name: &'a str, - pub file: &'a str, + pub name: *const u8, + pub file: *const u8, pub line: u32, pub column: u32, - pub function: &'a str, - pub message: &'a str, + pub function: *const u8, + pub message: *const u8, pub param: [u64; 3], + pub phantom: PhantomData<&'a str> } #[derive(Debug)] pub enum Message<'a> { LoadRequest(&'a [u8]), - LoadReply { error: Option<&'a str> }, + LoadReply(Result<(), &'a str>), NowInitRequest, NowInitReply(u64), @@ -29,6 +35,7 @@ pub enum Message<'a> { exception: Exception<'a>, backtrace: &'a [usize] }, + RunAborted, WatchdogSetRequest { ms: u64 }, WatchdogSetReply { id: usize }, @@ -40,386 +47,16 @@ pub enum Message<'a> { tag: &'a [u8], data: *const *const () }, - RpcRecvRequest { - slot: *mut () - }, - RpcRecvReply { - alloc_size: usize, - exception: Option> - }, + RpcRecvRequest(*mut ()), + RpcRecvReply(Result>), CacheGetRequest { key: &'a str }, CacheGetReply { value: &'static [u32] }, CachePutRequest { key: &'a str, value: &'static [u32] }, CachePutReply { succeeded: bool }, - Log(&'a str) + Log(fmt::Arguments<'a>), + LogSlice(&'a str) } pub use self::Message::*; - -impl<'a> Message<'a> { - fn into_lower R>(self, f: F) -> R { - match self { - Message::LoadRequest(library) => { - let msg = c::LoadRequest { - ty: c::Type::LoadRequest, - library: library.as_ptr() as *const _ - }; - f(&msg as *const _ as *const _) - } - - Message::NowInitReply(now) => { - let msg = c::NowInitReply { - ty: c::Type::NowInitReply, - now: now - }; - f(&msg as *const _ as *const _) - } - - Message::WatchdogSetReply { id } => { - let msg = c::WatchdogSetReply { - ty: c::Type::WatchdogSetReply, - id: id as _ - }; - f(&msg as *const _ as *const _) - } - - Message::RpcRecvReply { alloc_size, exception } => { - let exn = exception.map(|exception| { - // FIXME: disgusting - let name = String::from(exception.name) + "\0"; - let file = String::from(exception.file) + "\0"; - let function = String::from(exception.function) + "\0"; - let message = String::from(exception.message) + "\0"; - let exn = c::Exception { - name: name.as_ptr() as *const _, - file: file.as_ptr() as *const _, - line: exception.line, - column: exception.column, - function: function.as_ptr() as *const _, - message: message.as_ptr() as *const _, - param: exception.param, - }; - mem::forget(name); - mem::forget(file); - mem::forget(function); - mem::forget(message); - exn - }); - let msg = c::RpcRecvReply { - ty: c::Type::RpcRecvReply, - alloc_size: alloc_size as _, - exception: exn.as_ref().map_or(ptr::null(), |exn| exn as *const _) - }; - f(&msg as *const _ as *const _) - } - - Message::CacheGetReply { value } => { - let msg = c::CacheGetReply { - ty: c::Type::CacheGetReply, - length: value.len(), - elements: value.as_ptr() - }; - f(&msg as *const _ as *const _) - } - Message::CachePutReply { succeeded } => { - let msg = c::CachePutReply { - ty: c::Type::CachePutReply, - succeeded: succeeded as _ - }; - f(&msg as *const _ as *const _) - } - - other => panic!("Message::into_lower: {:?} unimplemented", other) - } - } - - unsafe fn from_lower(ptr: *const ()) -> Self { - let msg = ptr as *const c::Message; - match (*msg).ty { - c::Type::LoadReply => { - let msg = ptr as *const c::LoadReply; - let error = if (*msg).error.is_null() { - None - } else { - Some(c::from_c_str((*msg).error)) - }; - Message::LoadReply { error: error } - } - - c::Type::NowInitRequest => Message::NowInitRequest, - c::Type::NowSave => { - let msg = ptr as *const c::NowSave; - Message::NowSave((*msg).now) - } - - c::Type::RunFinished => Message::RunFinished, - c::Type::RunException => { - let msg = ptr as *const c::RunException; - let exc = (*msg).exception; - Message::RunException { - exception: Exception { - name: c::from_c_str((*exc).name), - file: c::from_c_str((*exc).file), - line: (*exc).line, - column: (*exc).column, - function: c::from_c_str((*exc).function), - message: c::from_c_str((*exc).message), - param: (*exc).param, - }, - backtrace: slice::from_raw_parts((*msg).backtrace, (*msg).backtrace_size) - } - } - - c::Type::WatchdogSetRequest => { - let msg = ptr as *const c::WatchdogSetRequest; - Message::WatchdogSetRequest { ms: (*msg).ms as u64 } - }, - c::Type::WatchdogClear => { - let msg = ptr as *const c::WatchdogClear; - Message::WatchdogClear { id: (*msg).id as usize } - } - - c::Type::RpcSend | c::Type::RpcBatch => { - let msg = ptr as *const c::RpcSend; - Message::RpcSend { - service: (*msg).service as _, - batch: (*msg).ty == c::Type::RpcBatch, - tag: slice::from_raw_parts((*msg).tag as *const _, - c::strlen((*msg).tag) as usize), - data: (*msg).data as *const _ - } - } - c::Type::RpcRecvRequest => { - let msg = ptr as *const c::RpcRecvRequest; - Message::RpcRecvRequest { slot: (*msg).slot as *mut _ } - } - - c::Type::CacheGetRequest => { - let msg = ptr as *const c::CacheGetRequest; - let key = c::from_c_str((*msg).key); - Message::CacheGetRequest { key: key } - } - c::Type::CachePutRequest => { - let msg = ptr as *const c::CachePutRequest; - let key = c::from_c_str((*msg).key); - let value = slice::from_raw_parts((*msg).elements, (*msg).length); - Message::CachePutRequest { key: key, value: value } - } - - c::Type::Log => { - let msg = ptr as *const c::Log; - Message::Log(c::from_c_str_len((*msg).buf, (*msg).len)) - } - - ref other => panic!("Message::from_lower: {:?} unimplemented", other) - } - } - - pub fn send_and_wait(self, waiter: ::sched::Waiter) -> io::Result<()> { - self.into_lower(|ptr| { - unsafe { mailbox::send(ptr as usize) } - waiter.until(mailbox::acknowledged) - }) - } - - pub fn wait_and_receive(waiter: ::sched::Waiter, f: F) -> io::Result - where F: FnOnce(Message<'a>) -> 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 msg = unsafe { Self::from_lower(mailbox::receive() as *const ()) }; - Ok(try!(f(msg))) - } - - pub fn acknowledge() { - mailbox::acknowledge() - } -} - -// Low-level representation, compatible with the C code in ksupport -mod c { - use libc::{c_void, c_int, c_char, size_t}; - use core::{str, slice}; - - extern { pub fn strlen(ptr: *const c_char) -> size_t; } - - #[repr(u32)] - #[derive(Debug, PartialEq, Eq)] - #[allow(dead_code)] - pub enum Type { - LoadRequest, - LoadReply, - NowInitRequest, - NowInitReply, - NowSave, - RunFinished, - RunException, - WatchdogSetRequest, - WatchdogSetReply, - WatchdogClear, - RpcSend, - RpcRecvRequest, - RpcRecvReply, - RpcBatch, - CacheGetRequest, - CacheGetReply, - CachePutRequest, - CachePutReply, - Log, - } - - #[repr(C)] - #[derive(Debug)] - pub struct Message { - pub ty: Type - } - - // kernel messages - - #[repr(C)] - #[derive(Debug)] - pub struct LoadRequest { - pub ty: Type, - pub library: *const c_void, - } - - #[repr(C)] - #[derive(Debug)] - pub struct LoadReply { - pub ty: Type, - pub error: *const c_char - } - - #[repr(C)] - #[derive(Debug)] - pub struct NowInitReply { - pub ty: Type, - pub now: u64 - } - - #[repr(C)] - #[derive(Debug)] - pub struct NowSave { - pub ty: Type, - pub now: u64 - } - - #[repr(C)] - #[derive(Debug)] - pub struct RunException { - pub ty: Type, - pub exception: *const Exception, - pub backtrace: *const usize, - pub backtrace_size: size_t - } - - #[repr(C)] - #[derive(Debug)] - pub struct WatchdogSetRequest { - pub ty: Type, - pub ms: c_int - } - - #[repr(C)] - #[derive(Debug)] - pub struct WatchdogSetReply { - pub ty: Type, - pub id: c_int - } - - #[repr(C)] - #[derive(Debug)] - pub struct WatchdogClear { - pub ty: Type, - pub id: c_int - } - - #[repr(C)] - #[derive(Debug)] - pub struct RpcSend { - pub ty: Type, - pub service: c_int, - pub tag: *const c_char, - pub data: *const *const c_void - } - - #[repr(C)] - #[derive(Debug)] - pub struct RpcRecvRequest { - pub ty: Type, - pub slot: *mut c_void - } - - #[repr(C)] - #[derive(Debug)] - pub struct RpcRecvReply { - pub ty: Type, - pub alloc_size: c_int, - pub exception: *const Exception - } - - #[repr(C)] - #[derive(Debug)] - pub struct CacheGetRequest { - pub ty: Type, - pub key: *const c_char - } - - #[repr(C)] - #[derive(Debug)] - pub struct CacheGetReply { - pub ty: Type, - pub length: size_t, - pub elements: *const u32 - } - - #[repr(C)] - #[derive(Debug)] - pub struct CachePutRequest { - pub ty: Type, - pub key: *const c_char, - pub length: size_t, - pub elements: *const u32 - } - - #[repr(C)] - #[derive(Debug)] - pub struct CachePutReply { - pub ty: Type, - pub succeeded: c_int - } - - #[repr(C)] - #[derive(Debug)] - pub struct Log { - pub ty: Type, - pub buf: *const c_char, - pub len: size_t - } - - // Supplementary structures - - #[repr(C)] - #[derive(Debug)] - pub struct Exception { - pub name: *const c_char, // or typeinfo - pub file: *const c_char, - pub line: u32, - pub column: u32, - pub function: *const c_char, - pub message: *const c_char, - pub param: [u64; 3], - } - - pub unsafe fn from_c_str_len<'a>(ptr: *const c_char, len: size_t) -> &'a str { - str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, len)) - } - - pub unsafe fn from_c_str<'a>(ptr: *const c_char) -> &'a str { - from_c_str_len(ptr, strlen(ptr)) - } -} diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs index bbdf30f48..0c05f4a5c 100644 --- a/artiq/runtime.rs/src/session.rs +++ b/artiq/runtime.rs/src/session.rs @@ -1,6 +1,7 @@ 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 logger::BufferLogger; @@ -75,6 +76,15 @@ impl<'a> Session<'a> { KernelState::Running | KernelState::RpcWait => true } } + + fn flush_log_buffer(&mut self) { + if &self.log_buffer[self.log_buffer.len() - 1..] == "\n" { + for line in self.log_buffer.lines() { + info!(target: "kernel", "{}", line); + } + self.log_buffer.clear() + } + } } impl<'a> Drop for Session<'a> { @@ -109,24 +119,44 @@ fn host_write(stream: &mut TcpStream, reply: host::Reply) -> io::Result<()> { reply.write_to(stream) } -fn kern_send<'a>(waiter: Waiter, request: kern::Message<'a>) -> io::Result<()> { - match &request { +fn kern_send(waiter: Waiter, request: &kern::Message) -> io::Result<()> { + match request { &kern::LoadRequest(_) => trace!("comm->kern LoadRequest(...)"), _ => trace!("comm->kern {:?}", request) } - request.send_and_wait(waiter) + unsafe { mailbox::send(request as *const _ as usize) } + waiter.until(mailbox::acknowledged) } +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")) + } + + f(unsafe { mem::transmute::(mailbox::receive()) }) +} + +fn kern_recv_dotrace(reply: &kern::Message) { + match reply { + &kern::Log(_) => trace!("comm<-kern Log(...)"), + &kern::LogSlice(_) => trace!("comm<-kern LogSlice(...)"), + _ => trace!("comm<-kern {:?}", reply) + } +} + +#[inline(always)] fn kern_recv(waiter: Waiter, f: F) -> io::Result - where F: FnOnce(kern::Message) -> io::Result { - kern::Message::wait_and_receive(waiter, |reply| { - trace!("comm<-kern {:?}", reply); + where F: FnOnce(&kern::Message) -> io::Result { + kern_recv_notrace(waiter, |reply| { + kern_recv_dotrace(reply); f(reply) }) } fn kern_acknowledge() -> io::Result<()> { - kern::Message::acknowledge(); + mailbox::acknowledge(); Ok(()) } @@ -137,15 +167,15 @@ unsafe fn kern_load(waiter: Waiter, session: &mut Session, library: &[u8]) -> io kernel::start(); - try!(kern_send(waiter, kern::LoadRequest(&library))); + try!(kern_send(waiter, &kern::LoadRequest(&library))); kern_recv(waiter, |reply| { match reply { - kern::LoadReply { error: None } => { + &kern::LoadReply(Ok(())) => { session.kernel_state = KernelState::Loaded; Ok(()) } - kern::LoadReply { error: Some(cause) } => - unexpected!("cannot load kernel: {}", cause), + &kern::LoadReply(Err(error)) => + unexpected!("cannot load kernel: {}", error), other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } @@ -224,7 +254,10 @@ fn process_host_message(waiter: Waiter, host::Request::LoadKernel(kernel) => match unsafe { kern_load(waiter, session, &kernel) } { Ok(()) => host_write(stream, host::Reply::LoadCompleted), - Err(_) => host_write(stream, host::Reply::LoadFailed) + Err(_) => { + try!(kern_acknowledge()); + host_write(stream, host::Reply::LoadFailed) + } }, host::Request::RunKernel => @@ -240,23 +273,20 @@ fn process_host_message(waiter: Waiter, let slot = try!(kern_recv(waiter, |reply| { match reply { - kern::RpcRecvRequest { slot } => Ok(slot), - other => - unexpected!("unexpected reply from kernel CPU: {:?}", other) + &kern::RpcRecvRequest(slot) => Ok(slot), + other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } })); try!(rpc::recv_return(stream, &tag, slot, &|size| { - try!(kern_send(waiter, kern::RpcRecvReply { - alloc_size: size, exception: None - })); + try!(kern_send(waiter, &kern::RpcRecvReply(Ok(size)))); kern_recv(waiter, |reply| { match reply { - kern::RpcRecvRequest { slot } => Ok(slot), - _ => unreachable!() + &kern::RpcRecvRequest(slot) => Ok(slot), + other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } }) })); - try!(kern_send(waiter, kern::RpcRecvReply { alloc_size: 0, exception: None })); + try!(kern_send(waiter, &kern::RpcRecvReply(Ok(0)))); session.kernel_state = KernelState::Running; Ok(()) @@ -271,23 +301,30 @@ fn process_host_message(waiter: Waiter, try!(kern_recv(waiter, |reply| { match reply { - kern::RpcRecvRequest { .. } => Ok(()), + &kern::RpcRecvRequest(_) => Ok(()), other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } })); - try!(kern_send(waiter, kern::RpcRecvReply { - alloc_size: 0, - exception: Some(kern::Exception { - name: &name, - message: &message, - param: param, - file: &file, - line: line, - column: column, - function: &function - }) - })); + + // FIXME: gross. + fn into_c_str(s: String) -> *const u8 { + let s = s + "\0"; + let p = s.as_bytes().as_ptr(); + mem::forget(s); + p + } + let exn = kern::Exception { + name: into_c_str(name), + message: into_c_str(message), + param: param, + file: into_c_str(file), + line: line, + column: column, + function: into_c_str(function), + phantom: ::core::marker::PhantomData + }; + try!(kern_send(waiter, &kern::RpcRecvReply(Err(exn)))); session.kernel_state = KernelState::Running; Ok(()) @@ -298,10 +335,10 @@ fn process_host_message(waiter: Waiter, fn process_kern_message(waiter: Waiter, mut stream: Option<&mut TcpStream>, session: &mut Session) -> io::Result { - kern::Message::wait_and_receive(waiter, |request| { - match (&request, session.kernel_state) { - (&kern::LoadReply { .. }, KernelState::Loaded) | - (&kern::RpcRecvRequest { .. }, KernelState::RpcWait) => { + kern_recv_notrace(waiter, |request| { + match (request, session.kernel_state) { + (&kern::LoadReply(_), KernelState::Loaded) | + (&kern::RpcRecvRequest(_), KernelState::RpcWait) => { // We're standing by; ignore the message. return Ok(false) } @@ -312,41 +349,41 @@ fn process_kern_message(waiter: Waiter, } } - trace!("comm<-kern {:?}", request); + kern_recv_dotrace(request); match request { - kern::Log(log) => { - session.log_buffer += log; - try!(kern_acknowledge()); - - if &log[log.len() - 1..] == "\n" { - for line in session.log_buffer.lines() { - info!(target: "kernel", "{}", line); - } - session.log_buffer.clear() - } - Ok(()) + &kern::Log(args) => { + try!(session.log_buffer.write_fmt(args) + .map_err(|_| io_error("cannot append to session log buffer"))); + session.flush_log_buffer(); + kern_acknowledge() } - kern::NowInitRequest => - kern_send(waiter, kern::NowInitReply(session.congress.now)), + &kern::LogSlice(arg) => { + session.log_buffer += arg; + session.flush_log_buffer(); + kern_acknowledge() + } - kern::NowSave(now) => { + &kern::NowInitRequest => + kern_send(waiter, &kern::NowInitReply(session.congress.now)), + + &kern::NowSave(now) => { session.congress.now = now; kern_acknowledge() } - kern::WatchdogSetRequest { ms } => { + &kern::WatchdogSetRequest { ms } => { let id = try!(session.watchdog_set.set_ms(ms) .map_err(|()| io_error("out of watchdogs"))); - kern_send(waiter, kern::WatchdogSetReply { id: id }) + kern_send(waiter, &kern::WatchdogSetReply { id: id }) } - kern::WatchdogClear { id } => { + &kern::WatchdogClear { id } => { session.watchdog_set.clear(id); kern_acknowledge() } - kern::RpcSend { service, batch, tag, data } => { + &kern::RpcSend { service, batch, tag, data } => { match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { @@ -362,19 +399,19 @@ fn process_kern_message(waiter: Waiter, } } - kern::CacheGetRequest { key } => { + &kern::CacheGetRequest { key } => { let value = session.congress.cache.get(key); - kern_send(waiter, kern::CacheGetReply { + kern_send(waiter, &kern::CacheGetReply { value: unsafe { mem::transmute::<*const [u32], &'static [u32]>(value) } }) } - kern::CachePutRequest { key, value } => { + &kern::CachePutRequest { key, value } => { let succeeded = session.congress.cache.put(key, value).is_ok(); - kern_send(waiter, kern::CachePutReply { succeeded: succeeded }) + kern_send(waiter, &kern::CachePutReply { succeeded: succeeded }) } - kern::RunFinished => { + &kern::RunFinished => { kernel::stop(); session.kernel_state = KernelState::Absent; unsafe { session.congress.cache.unborrow() } @@ -386,27 +423,38 @@ fn process_kern_message(waiter: Waiter, } } - kern::RunException { exception: ref exn, backtrace } => { + &kern::RunException { exception: ref exn, backtrace } => { kernel::stop(); session.kernel_state = KernelState::Absent; unsafe { session.congress.cache.unborrow() } + unsafe fn from_c_str<'a>(s: *const u8) -> &'a str { + use ::libc::{c_char, size_t}; + use core::slice; + extern { fn strlen(s: *const c_char) -> size_t; } + let s = slice::from_raw_parts(s, strlen(s as *const c_char)); + str::from_utf8_unchecked(s) + } + let name = unsafe { from_c_str(exn.name) }; + let message = unsafe { from_c_str(exn.message) }; + let file = unsafe { from_c_str(exn.file) }; + let function = unsafe { from_c_str(exn.function) }; match stream { None => { error!("exception in flash kernel"); - error!("{}: {} {:?}", exn.name, exn.message, exn.param); - error!("at {}:{}:{} in {}", exn.file, exn.line, exn.column, exn.function); + error!("{}: {} {:?}", name, message, exn.param); + error!("at {}:{}:{} in {}", file, exn.line, exn.column, function); return Ok(true) }, Some(ref mut stream) => host_write(stream, host::Reply::KernelException { - name: exn.name, - message: exn.message, + name: name, + message: message, param: exn.param, - file: exn.file, + file: file, line: exn.line, column: exn.column, - function: exn.function, + function: function, backtrace: backtrace }) } diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 2990c9b23..1c37c4ec0 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -3,11 +3,13 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 -OBJECTS := flash_storage.o ksupport_data.o main.o -OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - rtio.o dds.o i2c.o +OBJECTS := flash_storage.o main.o +OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o -CFLAGS += -I$(LIBALLOC_DIRECTORY) \ +RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug + +CFLAGS += \ + -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ -I$(LIBDYLD_DIRECTORY)/include \ -I$(LIBUNWIND_DIRECTORY) \ @@ -16,8 +18,57 @@ CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(LIBLWIP_DIRECTORY) CFLAGS += -DNDEBUG +LDFLAGS += --gc-sections \ + -L../libcompiler-rt \ + -L../libbase \ + -L../libm \ + -L../liballoc \ + -L../libunwind \ + -L../libdyld \ + -L../liblwip + all: runtime.bin runtime.fbi +.PHONY: $(RUSTOUT_DIRECTORY)/libruntime.a +$(RUSTOUT_DIRECTORY)/libruntime.a: ksupport.elf + CARGO_TARGET_DIR=$(realpath ./cargo) \ + cargo rustc --verbose \ + --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml) \ + --target=or1k-unknown-none -- \ + $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ + -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ + -L../libcompiler-rt + +runtime.elf: $(OBJECTS) $(RUSTOUT_DIRECTORY)/libruntime.a + $(LD) $(LDFLAGS) \ + -T $(RUNTIME_DIRECTORY)/runtime.ld \ + -o $@ \ + $^ \ + -lbase-nofloat -lcompiler-rt -lalloc -llwip + @chmod -x $@ + +.PHONY: $(RUSTOUT_DIRECTORY)/libksupport.a +$(RUSTOUT_DIRECTORY)/libksupport.a: + CARGO_TARGET_DIR=$(realpath ./cargo) \ + cargo rustc --verbose \ + --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \ + --target=or1k-unknown-none -- \ + $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ + -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ + -L../libcompiler-rt + +ksupport.elf: $(OBJECTS_KSUPPORT) $(RUSTOUT_DIRECTORY)/libksupport.a + $(LD) $(LDFLAGS) \ + --eh-frame-hdr \ + -T $(RUNTIME_DIRECTORY)/ksupport.ld \ + -o $@ \ + $^ \ + -lbase -lm -lcompiler-rt -ldyld -lunwind + @chmod -x $@ + +%.o: $(RUNTIME_DIRECTORY)/%.c + $(compile) + %.bin: %.elf $(OBJCOPY) -O binary $< $@ @chmod -x $@ @@ -25,57 +76,10 @@ all: runtime.bin runtime.fbi %.fbi: %.bin @echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< -runtime.elf: $(OBJECTS) libruntime.a - $(LD) $(LDFLAGS) \ - --gc-sections \ - -T $(RUNTIME_DIRECTORY)/runtime.ld \ - -N -o $@ \ - ../libbase/crt0-$(CPU).o \ - $(OBJECTS) \ - -L../libcompiler-rt \ - -L../libbase \ - -L../liballoc \ - -L../liblwip \ - -Lcargo/or1k-unknown-none/debug/ \ - -lruntime -lbase-nofloat -lcompiler-rt -lalloc -llwip - @chmod -x $@ - -ksupport.elf: $(OBJECTS_KSUPPORT) - $(LD) $(LDFLAGS) \ - --eh-frame-hdr \ - -T $(RUNTIME_DIRECTORY)/ksupport.ld \ - -N -o $@ \ - ../libbase/crt0-$(CPU).o \ - $^ \ - -L../libbase \ - -L../libm \ - -L../libcompiler-rt \ - -L../libunwind \ - -L../libdyld \ - -lbase -lm -lcompiler-rt -ldyld -lunwind - @chmod -x $@ - -ksupport_data.o: ksupport.elf - $(LD) -r -b binary -o $@ $< - -libruntime.a: - CARGO_TARGET_DIR="./cargo" \ - cargo rustc --verbose \ - --manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \ - --target=or1k-unknown-none -- \ - $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ - -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ - -L../libcompiler-rt - -%.o: $(RUNTIME_DIRECTORY)/%.c - $(compile) - -%.o: $(RUNTIME_DIRECTORY)/%.S - $(assemble) - clean: $(RM) $(OBJECTS) $(OBJECTS_KSUPPORT) $(RM) runtime.elf runtime.bin runtime.fbi .*~ *~ $(RM) ksupport.elf ksupport.bin + $(RM) -rf cargo .PHONY: all clean diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c deleted file mode 100644 index aaa313373..000000000 --- a/artiq/runtime/ksupport.c +++ /dev/null @@ -1,509 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ksupport.h" -#include "mailbox.h" -#include "messages.h" -#include "artiq_personality.h" -#include "rtio.h" -#include "dds.h" -#include "i2c.h" - -#define KERNELCPU_EXEC_ADDRESS 0x40400000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x40420000 -#define KERNELCPU_LAST_ADDRESS 0x4fffffff -#define KSUPPORT_HEADER_SIZE 0x80 - -long lround(double x); - -void ksupport_abort(void); -static void attribute_writeback(void *); - -int64_t now; - -/* compiler-rt symbols */ -extern void __divsi3, __modsi3, __ledf2, __gedf2, __unorddf2, __eqdf2, __ltdf2, - __nedf2, __gtdf2, __negsf2, __negdf2, __addsf3, __subsf3, __mulsf3, - __divsf3, __lshrdi3, __muldi3, __divdi3, __ashldi3, __ashrdi3, - __udivmoddi4, __floatsisf, __floatunsisf, __fixsfsi, __fixunssfsi, - __adddf3, __subdf3, __muldf3, __divdf3, __floatsidf, __floatunsidf, - __floatdidf, __fixdfsi, __fixdfdi, __fixunsdfsi, __clzsi2, __ctzsi2, - __udivdi3, __umoddi3, __moddi3, __powidf2; - -/* artiq_personality symbols */ -extern void __artiq_personality; - -struct symbol { - const char *name; - void *addr; -}; - -static const struct symbol runtime_exports[] = { - /* compiler-rt */ - {"__divsi3", &__divsi3}, - {"__modsi3", &__modsi3}, - {"__ledf2", &__ledf2}, - {"__gedf2", &__gedf2}, - {"__unorddf2", &__unorddf2}, - {"__eqdf2", &__eqdf2}, - {"__ltdf2", &__ltdf2}, - {"__nedf2", &__nedf2}, - {"__gtdf2", &__gtdf2}, - {"__negsf2", &__negsf2}, - {"__negdf2", &__negdf2}, - {"__addsf3", &__addsf3}, - {"__subsf3", &__subsf3}, - {"__mulsf3", &__mulsf3}, - {"__divsf3", &__divsf3}, - {"__lshrdi3", &__lshrdi3}, - {"__muldi3", &__muldi3}, - {"__divdi3", &__divdi3}, - {"__ashldi3", &__ashldi3}, - {"__ashrdi3", &__ashrdi3}, - {"__udivmoddi4", &__udivmoddi4}, - {"__floatsisf", &__floatsisf}, - {"__floatunsisf", &__floatunsisf}, - {"__fixsfsi", &__fixsfsi}, - {"__fixunssfsi", &__fixunssfsi}, - {"__adddf3", &__adddf3}, - {"__subdf3", &__subdf3}, - {"__muldf3", &__muldf3}, - {"__divdf3", &__divdf3}, - {"__floatsidf", &__floatsidf}, - {"__floatunsidf", &__floatunsidf}, - {"__floatdidf", &__floatdidf}, - {"__fixdfsi", &__fixdfsi}, - {"__fixdfdi", &__fixdfdi}, - {"__fixunsdfsi", &__fixunsdfsi}, - {"__clzsi2", &__clzsi2}, - {"__ctzsi2", &__ctzsi2}, - {"__udivdi3", &__udivdi3}, - {"__umoddi3", &__umoddi3}, - {"__moddi3", &__moddi3}, - {"__powidf2", &__powidf2}, - - /* libm */ - {"sqrt", &sqrt}, - {"lround", &lround}, - - /* exceptions */ - {"_Unwind_Resume", &_Unwind_Resume}, - {"__artiq_personality", &__artiq_personality}, - {"__artiq_raise", &__artiq_raise}, - {"__artiq_reraise", &__artiq_reraise}, - {"strcmp", &strcmp}, - {"strlen", &strlen}, - {"abort", &ksupport_abort}, - - /* proxified syscalls */ - {"core_log", &core_log}, - - {"now", &now}, - - {"watchdog_set", &watchdog_set}, - {"watchdog_clear", &watchdog_clear}, - - {"printf", &core_log}, - {"send_rpc", &send_rpc}, - {"recv_rpc", &recv_rpc}, - - /* direct syscalls */ - {"rtio_init", &rtio_init}, - {"rtio_get_counter", &rtio_get_counter}, - {"rtio_log", &rtio_log}, - {"rtio_output", &rtio_output}, - {"rtio_input_timestamp", &rtio_input_timestamp}, - {"rtio_input_data", &rtio_input_data}, - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - {"dds_init", &dds_init}, - {"dds_init_sync", &dds_init_sync}, - {"dds_batch_enter", &dds_batch_enter}, - {"dds_batch_exit", &dds_batch_exit}, - {"dds_set", &dds_set}, -#endif - - {"i2c_init", &i2c_init}, - {"i2c_start", &i2c_start}, - {"i2c_stop", &i2c_stop}, - {"i2c_write", &i2c_write}, - {"i2c_read", &i2c_read}, - - {"cache_get", &cache_get}, - {"cache_put", &cache_put}, - - /* end */ - {NULL, NULL} -}; - -long lround(double x) -{ - return x < 0 ? floor(x) : ceil(x); -} - -/* called by libunwind */ -int fprintf(FILE *stream, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - char buf[256]; - int len = vscnprintf(buf, sizeof(buf), fmt, args); - - va_end(args); - - struct msg_log request; - request.type = MESSAGE_TYPE_LOG; - request.buf = buf; - request.len = len; - mailbox_send_and_wait(&request); - - return 0; -} - -/* called by libunwind */ -int dladdr (const void *address, Dl_info *info) -{ - /* we don't try to resolve names */ - return 0; -} - -/* called by libunwind */ -int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) -{ - Elf32_Ehdr *ehdr; - struct dl_phdr_info phdr_info; - int retval; - - ehdr = (Elf32_Ehdr *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE); - phdr_info = (struct dl_phdr_info){ - .dlpi_addr = 0, /* absolutely linked */ - .dlpi_name = "", - .dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff), - .dlpi_phnum = ehdr->e_phnum, - }; - retval = callback(&phdr_info, sizeof(phdr_info), data); - if(retval) - return retval; - - ehdr = (Elf32_Ehdr *)KERNELCPU_PAYLOAD_ADDRESS; - phdr_info = (struct dl_phdr_info){ - .dlpi_addr = KERNELCPU_PAYLOAD_ADDRESS, - .dlpi_name = "", - .dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff), - .dlpi_phnum = ehdr->e_phnum, - }; - retval = callback(&phdr_info, sizeof(phdr_info), data); - return retval; -} - -static Elf32_Addr resolve_runtime_export(const char *name) -{ - const struct symbol *sym = runtime_exports; - while(sym->name) { - if(!strcmp(sym->name, name)) - return (Elf32_Addr)sym->addr; - ++sym; - } - return 0; -} - -void exception_handler(unsigned long vect, unsigned long *regs, - unsigned long pc, unsigned long ea); -void exception_handler(unsigned long vect, unsigned long *regs, - unsigned long pc, unsigned long ea) -{ - artiq_raise_from_c("InternalError", - "Hardware exception {0} at PC 0x{1:08x}, EA 0x{2:08x}", - vect, pc, ea); -} - -static void now_init(void) -{ - struct msg_base request; - struct msg_now_init_reply *reply; - - request.type = MESSAGE_TYPE_NOW_INIT_REQUEST; - mailbox_send_and_wait(&request); - - reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_NOW_INIT_REPLY) { - core_log("Malformed MESSAGE_TYPE_NOW_INIT_REQUEST reply type %d\n", - reply->type); - while(1); - } - now = reply->now; - mailbox_acknowledge(); -} - -static void now_save(void) -{ - struct msg_now_save request; - - request.type = MESSAGE_TYPE_NOW_SAVE; - request.now = now; - mailbox_send_and_wait(&request); -} - -int main(void); -int main(void) -{ - static struct dyld_info library_info; - - struct msg_load_request *request = mailbox_wait_and_receive(); - struct msg_load_reply load_reply = { - .type = MESSAGE_TYPE_LOAD_REPLY, - .error = NULL - }; - - if(!dyld_load(request->library, KERNELCPU_PAYLOAD_ADDRESS, - resolve_runtime_export, &library_info, - &load_reply.error)) { - mailbox_send(&load_reply); - while(1); - } - - void *__bss_start = dyld_lookup("__bss_start", &library_info); - void *_end = dyld_lookup("_end", &library_info); - memset(__bss_start, 0, _end - __bss_start); - - void (*kernel_run)() = library_info.init; - void *typeinfo = dyld_lookup("typeinfo", &library_info); - - mailbox_send_and_wait(&load_reply); - - now_init(); - kernel_run(); - now_save(); - - attribute_writeback(typeinfo); - - struct msg_base finished_reply; - finished_reply.type = MESSAGE_TYPE_FINISHED; - mailbox_send_and_wait(&finished_reply); - - while(1); -} - -/* called from __artiq_personality */ -void __artiq_terminate(struct artiq_exception *artiq_exn, - uintptr_t *backtrace, - size_t backtrace_size) -{ - struct msg_exception msg; - - now_save(); - - uintptr_t *cursor = backtrace; - - // Remove all backtrace items belonging to ksupport and subtract - // shared object base from the addresses. - for(int i = 0; i < backtrace_size; i++) { - if(backtrace[i] > KERNELCPU_PAYLOAD_ADDRESS) { - backtrace[i] -= KERNELCPU_PAYLOAD_ADDRESS; - *cursor++ = backtrace[i]; - } - } - - backtrace_size = cursor - backtrace; - - msg.type = MESSAGE_TYPE_EXCEPTION; - msg.exception = artiq_exn; - msg.backtrace = backtrace; - msg.backtrace_size = backtrace_size; - mailbox_send(&msg); - - while(1); -} - -void ksupport_abort() -{ - artiq_raise_from_c("InternalError", "abort() called; check device log for details", - 0, 0, 0); -} - -int watchdog_set(int ms) -{ - struct msg_watchdog_set_request request; - struct msg_watchdog_set_reply *reply; - int id; - - request.type = MESSAGE_TYPE_WATCHDOG_SET_REQUEST; - request.ms = ms; - mailbox_send_and_wait(&request); - - reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_WATCHDOG_SET_REPLY) { - core_log("Malformed MESSAGE_TYPE_WATCHDOG_SET_REQUEST reply type %d\n", - reply->type); - while(1); - } - id = reply->id; - mailbox_acknowledge(); - - return id; -} - -void watchdog_clear(int id) -{ - struct msg_watchdog_clear request; - - request.type = MESSAGE_TYPE_WATCHDOG_CLEAR; - request.id = id; - mailbox_send_and_wait(&request); -} - -void send_rpc(int service, const char *tag, void **data) -{ - struct msg_rpc_send request; - - if(service != 0) - request.type = MESSAGE_TYPE_RPC_SEND; - else - request.type = MESSAGE_TYPE_RPC_BATCH; - request.service = service; - request.tag = tag; - request.data = data; - mailbox_send_and_wait(&request); -} - -int recv_rpc(void *slot) -{ - struct msg_rpc_recv_request request; - struct msg_rpc_recv_reply *reply; - - request.type = MESSAGE_TYPE_RPC_RECV_REQUEST; - request.slot = slot; - mailbox_send_and_wait(&request); - - reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_RPC_RECV_REPLY) { - core_log("Malformed MESSAGE_TYPE_RPC_RECV_REQUEST reply type %d\n", - reply->type); - while(1); - } - - if(reply->exception) { - struct artiq_exception exception; - memcpy(&exception, reply->exception, - sizeof(struct artiq_exception)); - mailbox_acknowledge(); - __artiq_raise(&exception); - } else { - int alloc_size = reply->alloc_size; - mailbox_acknowledge(); - return alloc_size; - } -} - -struct attr_desc { - uint32_t offset; - const char *tag; - const char *name; -}; - -struct type_desc { - struct attr_desc **attributes; - void **objects; -}; - -void attribute_writeback(void *utypes) -{ - struct type_desc **types = (struct type_desc **)utypes; - while(*types) { - struct type_desc *type = *types++; - - size_t attr_count = 0; - for(struct attr_desc **attr = type->attributes; *attr; attr++) - attr_count++; - - void **objects = type->objects; - while(*objects) { - void *object = *objects++; - - struct attr_desc **attrs = type->attributes; - while(*attrs) { - struct attr_desc *attr = *attrs++; - - if(attr->tag) { - uintptr_t value = (uintptr_t)object + attr->offset; - void *args[] = { - &object, - &attr->name, - (void*)value - }; - send_rpc(0, attr->tag, args); - } - } - } - } -} - -struct artiq_list cache_get(const char *key) -{ - struct msg_cache_get_request request; - struct msg_cache_get_reply *reply; - - request.type = MESSAGE_TYPE_CACHE_GET_REQUEST; - request.key = key; - mailbox_send_and_wait(&request); - - reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_CACHE_GET_REPLY) { - core_log("Malformed MESSAGE_TYPE_CACHE_GET_REQUEST reply type %d\n", - reply->type); - while(1); - } - - return (struct artiq_list) { reply->length, reply->elements }; -} - -void cache_put(const char *key, struct artiq_list value) -{ - struct msg_cache_put_request request; - struct msg_cache_put_reply *reply; - - request.type = MESSAGE_TYPE_CACHE_PUT_REQUEST; - request.key = key; - request.elements = value.elements; - request.length = value.length; - mailbox_send_and_wait(&request); - - reply = mailbox_wait_and_receive(); - if(reply->type != MESSAGE_TYPE_CACHE_PUT_REPLY) { - core_log("Malformed MESSAGE_TYPE_CACHE_PUT_REQUEST reply type %d\n", - reply->type); - while(1); - } - - if(!reply->succeeded) { - artiq_raise_from_c("CacheError", - "cannot put into a busy cache row", - 0, 0, 0); - } -} - -void core_log(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - char buf[256]; - int len = vscnprintf(buf, sizeof(buf), fmt, args); - - va_end(args); - - struct msg_log request; - request.type = MESSAGE_TYPE_LOG; - request.buf = buf; - request.len = len; - mailbox_send_and_wait(&request); -} diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h deleted file mode 100644 index 6624ffcfa..000000000 --- a/artiq/runtime/ksupport.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __KSTARTUP_H -#define __KSTARTUP_H - -struct artiq_list { - int32_t length; - int32_t *elements; -}; - -int watchdog_set(int ms); -void watchdog_clear(int id); -void send_rpc(int service, const char *tag, void **data); -int recv_rpc(void *slot); -struct artiq_list cache_get(const char *key); -void cache_put(const char *key, struct artiq_list value); -void core_log(const char *fmt, ...); - -#endif /* __KSTARTUP_H */ diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index 4985009fe..de2267304 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -1,14 +1,15 @@ INCLUDE generated/output_format.ld +STARTUP(crt0-or1k.o) ENTRY(_start) INCLUDE generated/regions.ld /* First 4M of main memory are reserved for runtime * code/data/heap, then comes kernel memory. - * First 128K of kernel memory are for support code. + * First 256K of kernel memory are for support code. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x40400000, LENGTH = 0x20000 + ksupport (RWX) : ORIGIN = 0x40400000, LENGTH = 0x40000 } /* Kernel stack is at the end of main RAM. */ @@ -30,6 +31,16 @@ SECTIONS _etext = .; } :text + /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ + .got : { + _GLOBAL_OFFSET_TABLE_ = .; + *(.got) + } :text + + .got.plt : { + *(.got.plt) + } :text + .rodata : { . = ALIGN(4); @@ -41,12 +52,12 @@ SECTIONS .eh_frame : { - *(.eh_frame) + KEEP(*(.eh_frame)) } :text .eh_frame_hdr : { - *(.eh_frame_hdr) + KEEP(*(.eh_frame_hdr)) } :text :eh_frame .data : @@ -72,4 +83,9 @@ SECTIONS . = ALIGN(4); _ebss = .; } + + /DISCARD/ : + { + *(.debug*) + } } diff --git a/artiq/runtime/ksupport_glue.c b/artiq/runtime/ksupport_glue.c new file mode 100644 index 000000000..9de97b75d --- /dev/null +++ b/artiq/runtime/ksupport_glue.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +#include +#include + +void send_to_log(const char *ptr, size_t length); + +#define KERNELCPU_EXEC_ADDRESS 0x40400000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x40440000 +#define KERNELCPU_LAST_ADDRESS 0x4fffffff +#define KSUPPORT_HEADER_SIZE 0x80 + +/* called by libunwind */ +int fprintf(FILE *stream, const char *fmt, ...) +{ + size_t size; + char *buf; + va_list args; + + va_start(args, fmt); + size = vsnprintf(NULL, 0, fmt, args); + buf = __builtin_alloca(size + 1); + va_end(args); + + va_start(args, fmt); + vsnprintf(buf, size + 1, fmt, args); + va_end(args); + + send_to_log(buf, size); + return 0; +} + +/* called by libunwind */ +int dladdr (const void *address, Dl_info *info) +{ + /* we don't try to resolve names */ + return 0; +} + +/* called by libunwind */ +int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) +{ + Elf32_Ehdr *ehdr; + struct dl_phdr_info phdr_info; + int retval; + + ehdr = (Elf32_Ehdr *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE); + phdr_info = (struct dl_phdr_info){ + .dlpi_addr = 0, /* absolutely linked */ + .dlpi_name = "", + .dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff), + .dlpi_phnum = ehdr->e_phnum, + }; + retval = callback(&phdr_info, sizeof(phdr_info), data); + if(retval) + return retval; + + ehdr = (Elf32_Ehdr *)KERNELCPU_PAYLOAD_ADDRESS; + phdr_info = (struct dl_phdr_info){ + .dlpi_addr = KERNELCPU_PAYLOAD_ADDRESS, + .dlpi_name = "", + .dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff), + .dlpi_phnum = ehdr->e_phnum, + }; + retval = callback(&phdr_info, sizeof(phdr_info), data); + return retval; +} + +/* called by kernel */ +long lround(double x); +long lround(double x) +{ + return x < 0 ? floor(x) : ceil(x); +} + +/* called by kernel */ +int core_log(const char *fmt, ...); +int core_log(const char *fmt, ...) +{ + size_t size; + char *buf; + va_list args; + + va_start(args, fmt); + size = vsnprintf(NULL, 0, fmt, args); + buf = __builtin_alloca(size + 1); + va_end(args); + + va_start(args, fmt); + vsnprintf(buf, size + 1, fmt, args); + va_end(args); + + send_to_log(buf, size); + return 0; +} diff --git a/artiq/runtime/mailbox.c b/artiq/runtime/mailbox.c deleted file mode 100644 index 1faa6d374..000000000 --- a/artiq/runtime/mailbox.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include - -#include "mailbox.h" - -#define KERNELCPU_MAILBOX MMPTR(MAILBOX_BASE) - -static unsigned int last_transmission; - -void mailbox_send(void *ptr) -{ - last_transmission = (unsigned int)ptr; - KERNELCPU_MAILBOX = last_transmission; -} - -int mailbox_acknowledged(void) -{ - unsigned int m; - - m = KERNELCPU_MAILBOX; - return !m || (m != last_transmission); -} - -void mailbox_send_and_wait(void *ptr) -{ - mailbox_send(ptr); - while(!mailbox_acknowledged()); -} - -void *mailbox_receive(void) -{ - unsigned int r; - - r = KERNELCPU_MAILBOX; - if(r == last_transmission) - return NULL; - else { - if(r) { - flush_cpu_dcache(); - } - return (void *)r; - } -} - -void *mailbox_wait_and_receive(void) -{ - void *r; - - while(!(r = mailbox_receive())); - return r; -} - -void mailbox_acknowledge(void) -{ - KERNELCPU_MAILBOX = 0; -} diff --git a/artiq/runtime/mailbox.h b/artiq/runtime/mailbox.h deleted file mode 100644 index f1d40c973..000000000 --- a/artiq/runtime/mailbox.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MAILBOX_H -#define __MAILBOX_H - -void mailbox_send(void *ptr); -int mailbox_acknowledged(void); -void mailbox_send_and_wait(void *ptr); - -void *mailbox_receive(void); -void *mailbox_wait_and_receive(void); -void mailbox_acknowledge(void); - -#endif /* __MAILBOX_H */ diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h deleted file mode 100644 index a19190cfd..000000000 --- a/artiq/runtime/messages.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef __MESSAGES_H -#define __MESSAGES_H - -#include -#include -#include - -enum { - MESSAGE_TYPE_LOAD_REQUEST, - MESSAGE_TYPE_LOAD_REPLY, - MESSAGE_TYPE_NOW_INIT_REQUEST, - MESSAGE_TYPE_NOW_INIT_REPLY, - MESSAGE_TYPE_NOW_SAVE, - MESSAGE_TYPE_FINISHED, - MESSAGE_TYPE_EXCEPTION, - MESSAGE_TYPE_WATCHDOG_SET_REQUEST, - MESSAGE_TYPE_WATCHDOG_SET_REPLY, - MESSAGE_TYPE_WATCHDOG_CLEAR, - MESSAGE_TYPE_RPC_SEND, - MESSAGE_TYPE_RPC_RECV_REQUEST, - MESSAGE_TYPE_RPC_RECV_REPLY, - MESSAGE_TYPE_RPC_BATCH, - MESSAGE_TYPE_CACHE_GET_REQUEST, - MESSAGE_TYPE_CACHE_GET_REPLY, - MESSAGE_TYPE_CACHE_PUT_REQUEST, - MESSAGE_TYPE_CACHE_PUT_REPLY, - MESSAGE_TYPE_LOG, -}; - -struct msg_base { - int type; -}; - -/* kernel messages */ - -struct msg_load_request { - int type; - const void *library; -}; - -struct msg_load_reply { - int type; - const char *error; -}; - -struct msg_now_init_reply { - int type; - long long int now; -}; - -struct msg_now_save { - int type; - long long int now; -}; - -struct msg_exception { - int type; - struct artiq_exception *exception; - uintptr_t *backtrace; - size_t backtrace_size; -}; - -struct msg_watchdog_set_request { - int type; - int ms; -}; - -struct msg_watchdog_set_reply { - int type; - int id; -}; - -struct msg_watchdog_clear { - int type; - int id; -}; - -struct msg_rpc_send { - int type; - int service; - const char *tag; - void **data; -}; - -struct msg_rpc_recv_request { - int type; - void *slot; -}; - -struct msg_rpc_recv_reply { - int type; - int alloc_size; - struct artiq_exception *exception; -}; - -struct msg_cache_get_request { - int type; - const char *key; -}; - -struct msg_cache_get_reply { - int type; - size_t length; - int32_t *elements; -}; - -struct msg_cache_put_request { - int type; - const char *key; - size_t length; - int32_t *elements; -}; - -struct msg_cache_put_reply { - int type; - int succeeded; -}; - -struct msg_log { - int type; - const char *buf; - size_t len; -}; - -#endif /* __MESSAGES_H */ diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index abcc69e91..5d3d00e08 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -1,4 +1,5 @@ INCLUDE generated/output_format.ld +STARTUP(crt0-or1k.o) ENTRY(_start) INCLUDE generated/regions.ld @@ -73,7 +74,9 @@ SECTIONS .heap : { _fheap = .; - . = ORIGIN(runtime) + LENGTH(runtime) - 0x1000; + . = ORIGIN(runtime) + LENGTH(runtime) + /* Leave room for ksupport headers. */ + - 0x1000; _eheap = .; } > runtime From 7618907cad93430e5bb626706d4fe48b9b2ad2e9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 16 Oct 2016 16:32:02 +0000 Subject: [PATCH 059/127] runtime: use i64 for watchdog timeout, not i32. --- artiq/compiler/transforms/artiq_ir_generator.py | 2 +- artiq/compiler/transforms/llvm_ir_generator.py | 2 +- artiq/runtime.rs/libksupport/Cargo.lock | 1 + artiq/runtime.rs/libksupport/lib.rs | 7 +++++-- artiq/test/libartiq_support/artiq_time.c | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 0c753e984..330ff0ecf 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -821,7 +821,7 @@ class ARTIQIRGenerator(algorithm.Visitor): timeout = self.visit(context_expr_node.args[0]) timeout_ms = self.append(ir.Arith(ast.Mult(loc=None), timeout, ir.Constant(1000, builtins.TFloat()))) - timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt32())) + timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt64())) watchdog_id = self.append(ir.Builtin("watchdog_set", [timeout_ms_int], builtins.TInt32())) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 1e5dfcde3..371d6ca29 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -356,7 +356,7 @@ class LLVMIRGenerator: elif name == "now": llty = lli64 elif name == "watchdog_set": - llty = ll.FunctionType(lli32, [lli32]) + llty = ll.FunctionType(lli32, [lli64]) elif name == "watchdog_clear": llty = ll.FunctionType(llvoid, [lli32]) else: diff --git a/artiq/runtime.rs/libksupport/Cargo.lock b/artiq/runtime.rs/libksupport/Cargo.lock index 042a9a750..da7d2b081 100644 --- a/artiq/runtime.rs/libksupport/Cargo.lock +++ b/artiq/runtime.rs/libksupport/Cargo.lock @@ -1,3 +1,4 @@ [root] name = "ksupport" version = "0.0.0" + diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs index 4163482ab..5b12c360e 100644 --- a/artiq/runtime.rs/libksupport/lib.rs +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -142,8 +142,11 @@ pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception, loop {} } -extern fn watchdog_set(ms: i32) -> usize { - // FIXME: fix ms +extern fn watchdog_set(ms: i64) -> usize { + if ms < 0 { + artiq_raise!("ValueError", "cannot set a watchdog with a negative timeout") + } + send(&WatchdogSetRequest { ms: ms as u64 }); recv!(&WatchdogSetReply { id } => id) } diff --git a/artiq/test/libartiq_support/artiq_time.c b/artiq/test/libartiq_support/artiq_time.c index f60e46e47..07c81c9ac 100644 --- a/artiq/test/libartiq_support/artiq_time.c +++ b/artiq/test/libartiq_support/artiq_time.c @@ -3,7 +3,7 @@ int64_t now = 0; -int watchdog_set(int ms) +int watchdog_set(long long ms) { printf("watchdog_set %d\n", ms); return ms; From 2a9e370840f6a9226353c1533c669171955bedc4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 16 Oct 2016 16:43:03 +0000 Subject: [PATCH 060/127] llvm_ir_generator: make sure RPC allocations are not underaligned. --- artiq/compiler/transforms/llvm_ir_generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 371d6ca29..f935ef2b7 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1289,6 +1289,7 @@ class LLVMIRGenerator: self.llbuilder.position_at_end(llalloc) llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc") + llalloca.align = 4 # maximum alignment required by OR1K ABI llphi.add_incoming(llalloca, llalloc) self.llbuilder.branch(llhead) From f10a4498c7ff8e39a39ad51eea45b21b45e22563 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 16 Oct 2016 23:54:29 +0000 Subject: [PATCH 061/127] test: fix printf specifier. --- artiq/test/libartiq_support/artiq_time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/libartiq_support/artiq_time.c b/artiq/test/libartiq_support/artiq_time.c index 07c81c9ac..18de56424 100644 --- a/artiq/test/libartiq_support/artiq_time.c +++ b/artiq/test/libartiq_support/artiq_time.c @@ -5,7 +5,7 @@ int64_t now = 0; int watchdog_set(long long ms) { - printf("watchdog_set %d\n", ms); + printf("watchdog_set %lld\n", ms); return ms; } From 6da1f3967052df19c6e8708aa2a1ad6f5f363e02 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 17 Oct 2016 00:11:02 +0000 Subject: [PATCH 062/127] runtime: fix use of $(realpath) in Makefile. --- artiq/runtime/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 1c37c4ec0..c543b7d66 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -31,7 +31,7 @@ all: runtime.bin runtime.fbi .PHONY: $(RUSTOUT_DIRECTORY)/libruntime.a $(RUSTOUT_DIRECTORY)/libruntime.a: ksupport.elf - CARGO_TARGET_DIR=$(realpath ./cargo) \ + CARGO_TARGET_DIR=$(realpath .)/cargo \ cargo rustc --verbose \ --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml) \ --target=or1k-unknown-none -- \ @@ -49,7 +49,7 @@ runtime.elf: $(OBJECTS) $(RUSTOUT_DIRECTORY)/libruntime.a .PHONY: $(RUSTOUT_DIRECTORY)/libksupport.a $(RUSTOUT_DIRECTORY)/libksupport.a: - CARGO_TARGET_DIR=$(realpath ./cargo) \ + CARGO_TARGET_DIR=$(realpath .)/cargo \ cargo rustc --verbose \ --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \ --target=or1k-unknown-none -- \ From 85834976d9d3e2d08df5fef403e31124f2902451 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 17 Oct 2016 14:06:35 +0800 Subject: [PATCH 063/127] gateware/spi: fix import --- artiq/gateware/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 17ab523ad..23e52915f 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -2,7 +2,7 @@ from itertools import product from migen import * from misoc.interconnect import wishbone -from misoc.cores.spi.core import SPIMachine +from misoc.cores.spi import SPIMachine class SPIMaster(Module): From 02adccf4a26782497c9ca4041f7a4ca4814623f4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 17 Oct 2016 19:57:59 +0800 Subject: [PATCH 064/127] dashboard/datasets: use scientific spinbox and increase number of decimals, closes #572 --- artiq/dashboard/datasets.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index cdb8d3ef6..5e353d4c9 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -7,6 +7,7 @@ from PyQt5 import QtCore, QtWidgets from artiq.tools import short_format from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel from artiq.gui.models import DictSyncTreeSepModel +from artiq.gui.scientific_spinbox import ScientificSpinBox logger = logging.getLogger(__name__) @@ -50,10 +51,10 @@ class Editor(QtWidgets.QDialog): class NumberEditor(Editor): def get_edit_widget(self, initial_value): - self.edit_widget = QtWidgets.QDoubleSpinBox() - self.edit_widget.setMinimum(float("-inf")) - self.edit_widget.setMaximum(float("+inf")) - self.edit_widget.setDecimals(8) + self.edit_widget = ScientificSpinBox() + self.edit_widget.setDecimals(13) + self.edit_widget.setPrecision() + self.edit_widget.setRelativeStep() self.edit_widget.setValue(float(initial_value)) return self.edit_widget From 69099691f71cf7a4c0a608cc9671ca8420a12c14 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 17 Oct 2016 20:07:18 +0800 Subject: [PATCH 065/127] doc: clarify usage of pause/check_pause, closes #571 --- artiq/master/scheduler.py | 3 +++ doc/manual/management_system.rst | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/artiq/master/scheduler.py b/artiq/master/scheduler.py index 2ae575c95..842fda853 100644 --- a/artiq/master/scheduler.py +++ b/artiq/master/scheduler.py @@ -446,6 +446,9 @@ class Scheduler: whether returning control to the host and pausing would have an effect, in order to avoid the cost of switching kernels in the common case where ``pause`` does nothing. + + This function does not have side effects, and does not have to be + followed by a call to ``pause``. """ for pipeline in self._pipelines.values(): if rid in pipeline.pool.runs: diff --git a/doc/manual/management_system.rst b/doc/manual/management_system.rst index bb51aa699..c28e612cf 100644 --- a/doc/manual/management_system.rst +++ b/doc/manual/management_system.rst @@ -72,7 +72,9 @@ To check whether ``pause()`` would in fact *not* return immediately, use :meth:` The experiment must place the hardware in a safe state and disconnect from the core device (typically, by using ``self.core.comm.close()``) before calling ``pause``. -Accessing the ``pause`` method is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``. +Accessing the ``pause`` and ``check_pause`` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``. + +``check_pause`` can be called (via RPC) from a kernel, but ``pause`` must not. Multiple pipelines ------------------ From 0e41725e2df8733050819f024a0615e63bdf4fb1 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 17 Oct 2016 12:40:10 +0200 Subject: [PATCH 066/127] pdq2: sync with pdq2 --- artiq/devices/pdq2/driver.py | 200 +++++++++++++++++++++++++++++++---- 1 file changed, 180 insertions(+), 20 deletions(-) diff --git a/artiq/devices/pdq2/driver.py b/artiq/devices/pdq2/driver.py index ea16a205b..f15928c84 100644 --- a/artiq/devices/pdq2/driver.py +++ b/artiq/devices/pdq2/driver.py @@ -13,6 +13,17 @@ logger = logging.getLogger(__name__) class Segment: + """Serialize the lines for a single Segment. + + Attributes: + max_time (int): Maximum duration of a line. + max_val (int): Maximum absolute value (scale) of the DAC output. + max_out (float): Output voltage at :attr:`max_val`. In Volt. + out_scale (float): Steps per Volt. + cordic_gain (float): CORDIC amplitude gain. + addr (int): Address assigned to this segment. + data (bytes): Serialized segment data. + """ max_time = 1 << 16 # uint16 timer max_val = 1 << 15 # int16 DAC max_out = 10. # Volt @@ -27,6 +38,24 @@ class Segment: def line(self, typ, duration, data, trigger=False, silence=False, aux=False, shift=0, jump=False, clear=False, wait=False): + """Append a line to this segment. + + Args: + typ (int): Output module to target with this line. + duration (int): Duration of the line in units of + ``clock_period*2**shift``. + data (bytes): Opaque data for the output module. + trigger (bool): Wait for trigger assertion before executing + this line. + silence (bool): Disable DAC clocks for the duration of this line. + aux (bool): Assert the AUX (F5 TTL) output during this line. + shift (int): Duration and spline evolution exponent. + jump (bool): Return to the frame address table after this line. + clear (bool): Clear the DDS phase accumulator when starting to + exectute this line. + wait (bool): Wait for trigger assertion before executing the next + line. + """ assert len(data) % 2 == 0, data assert len(data)//2 <= 14 # assert dt*(1 << shift) > 1 + len(data)//2 @@ -39,6 +68,15 @@ class Segment: @staticmethod def pack(widths, values): + """Pack spline data. + + Args: + widths (list[int]): Widths of values in multiples of 16 bits. + values (list[int]): Values to pack. + + Returns: + data (bytes): Packed data. + """ fmt = "<" ud = [] for width, value in zip(widths, values): @@ -60,7 +98,11 @@ class Segment: def bias(self, amplitude=[], **kwargs): """Append a bias line to this segment. - Amplitude in volts + Args: + amplitude (list[float]): Amplitude coefficients in in Volts and + increasing powers of ``1/(2**shift*clock_period)``. + Discrete time compensation will be applied. + **kwargs: Passed to :meth:`line`. """ coef = [self.out_scale*a for a in amplitude] discrete_compensate(coef) @@ -68,12 +110,18 @@ class Segment: self.line(typ=0, data=data, **kwargs) def dds(self, amplitude=[], phase=[], **kwargs): - """Append a dds line to this segment. + """Append a DDS line to this segment. - Amplitude in volts, - phase[0] in turns, - phase[1] in turns*sample_rate, - phase[2] in turns*(sample_rate/2**shift)**2 + Args: + amplitude (list[float]): Amplitude coefficients in in Volts and + increasing powers of ``1/(2**shift*clock_period)``. + Discrete time compensation and CORDIC gain compensation + will be applied by this method. + phase (list[float]): Phase/frequency/chirp coefficients. + ``phase[0]`` in ``turns``, + ``phase[1]`` in ``turns/clock_period``, + ``phase[2]`` in ``turns/(clock_period**2*2**shift)``. + **kwargs: Passed to :meth:`line`. """ scale = self.out_scale/self.cordic_gain coef = [scale*a for a in amplitude] @@ -86,6 +134,13 @@ class Segment: class Channel: + """PDQ2 Channel. + + Attributes: + num_frames (int): Number of frames supported. + max_data (int): Number of 16 bit data words per channel. + segments (list[Segment]): Segments added to this channel. + """ num_frames = 8 max_data = 4*(1 << 10) # 8kx16 8kx16 4kx16 @@ -93,14 +148,27 @@ class Channel: self.segments = [] def clear(self): + """Remove all segments.""" self.segments.clear() def new_segment(self): + """Create and attach a new :class:`Segment` to this channel. + + Returns: + :class:`Segment` + """ segment = Segment() self.segments.append(segment) return segment def place(self): + """Place segments contiguously. + + Assign segment start addresses and determine length of data. + + Returns: + addr (int): Amount of memory in use on this channel. + """ addr = self.num_frames for segment in self.segments: segment.addr = addr @@ -109,6 +177,23 @@ class Channel: return addr def table(self, entry=None): + """Generate the frame address table. + + Unused frame indices are assigned the zero address in the frame address + table. + This will cause the memory parser to remain in the frame address table + until another frame is selected. + + The frame entry segments can be any segments in the channel. + + Args: + entry (list[Segment]): List of initial segments for each frame. + If not specified, the first :attr:`num_frames` segments are + used as frame entry points. + + Returns: + table (bytes): Frame address table. + """ table = [0] * self.num_frames if entry is None: entry = self.segments @@ -118,6 +203,18 @@ class Channel: return struct.pack("<" + "H"*self.num_frames, *table) def serialize(self, entry=None): + """Serialize the memory for this channel. + + Places the segments contiguously in memory after the frame table. + Allocates and assigns segment and frame table addresses. + Serializes segment data and prepends frame address table. + + Args: + entry (list[Segment]): See :meth:`table`. + + Returns: + data (bytes): Channel memory data. + """ self.place() data = b"".join([segment.data for segment in self.segments]) return self.table(entry) + data @@ -125,7 +222,22 @@ class Channel: class Pdq2: """ - PDQ DAC (a.k.a. QC_Waveform) + PDQ stack. + + Args: + url (str): Pyserial device URL. Can be ``hwgrep://`` style + (search for serial number, bus topology, USB VID:PID combination), + ``COM15`` for a Windows COM port number, + ``/dev/ttyUSB0`` for a Linux serial port. + dev (file-like): File handle to use as device. If passed, ``url`` is + ignored. + num_boards (int): Number of boards in this stack. + + Attributes: + num_dacs (int): Number of DAC outputs per board. + num_channels (int): Number of channels in this stack. + num_boards (int): Number of boards in this stack. + channels (list[Channel]): List of :class:`Channel` in this stack. """ num_dacs = 3 freq = 50e6 @@ -154,42 +266,58 @@ class Pdq2: self.freq = float(freq) def close(self): + """Close the USB device handle.""" self.dev.close() del self.dev def write(self, data): + """Write data to the PDQ2 board. + + Args: + data (bytes): Data to write. + """ logger.debug("> %r", data) written = self.dev.write(data) if isinstance(written, int): assert written == len(data) def cmd(self, cmd, enable): + """Execute a command. + + Args: + cmd (str): Command to execute. One of (``RESET``, ``TRIGGER``, + ``ARM``, ``DCM``, ``START``). + enable (bool): Enable (``True``) or disable (``False``) the + feature. + """ cmd = self._commands.index(cmd) << 1 if not enable: cmd |= 1 self.write(struct.pack("cb", self._escape, cmd)) def write_mem(self, channel, data, start_addr=0): + """Write to channel memory. + + Args: + channel (int): Channel index to write to. Assumes every board in + the stack has :attr:`num_dacs` DAC outputs. + data (bytes): Data to write to memory. + start_addr (int): Start address to write data to. + """ board, dac = divmod(channel, self.num_dacs) data = struct.pack(" Date: Tue, 18 Oct 2016 13:49:43 +0800 Subject: [PATCH 067/127] scheduler: default submission arguments, closes #577 --- .../repository/flopping_f_simulation.py | 3 +- artiq/frontend/artiq_run.py | 2 +- artiq/master/scheduler.py | 8 +++-- artiq/master/worker_impl.py | 35 +++++++++++-------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/artiq/examples/master/repository/flopping_f_simulation.py b/artiq/examples/master/repository/flopping_f_simulation.py index d6e875be7..1af331999 100644 --- a/artiq/examples/master/repository/flopping_f_simulation.py +++ b/artiq/examples/master/repository/flopping_f_simulation.py @@ -52,8 +52,7 @@ class FloppingF(EnvExperiment): self.mutate_dataset("flopping_f_frequency", i, f) self.mutate_dataset("flopping_f_brightness", i, m_brightness) time.sleep(0.1) - self.scheduler.submit(self.scheduler.pipeline_name, self.scheduler.expid, - self.scheduler.priority, time.time() + 20, False) + self.scheduler.submit(due_date=time.time() + 20) def analyze(self): # Use get_dataset so that analyze can be run stand-alone. diff --git a/artiq/frontend/artiq_run.py b/artiq/frontend/artiq_run.py index 94e853edc..4972a5cd7 100755 --- a/artiq/frontend/artiq_run.py +++ b/artiq/frontend/artiq_run.py @@ -95,7 +95,7 @@ class DummyScheduler: self._next_rid = 1 - def submit(self, pipeline_name, expid, priority, due_date, flush): + def submit(self, pipeline_name=None, expid=None, priority=None, due_date=None, flush=False): rid = self._next_rid self._next_rid += 1 logger.info("Submitting: %s, RID=%s", expid, rid) diff --git a/artiq/master/scheduler.py b/artiq/master/scheduler.py index 842fda853..53d06cac9 100644 --- a/artiq/master/scheduler.py +++ b/artiq/master/scheduler.py @@ -402,8 +402,12 @@ class Scheduler: if self._pipelines: logger.warning("some pipelines were not garbage-collected") - def submit(self, pipeline_name, expid, priority, due_date, flush): - """Submits a new run.""" + def submit(self, pipeline_name, expid, priority=0, due_date=None, flush=False): + """Submits a new run. + + When called through an experiment, the default values of + ``pipeline_name``, ``expid`` and ``priority`` correspond to those of + the current run.""" # mutates expid to insert head repository revision if None if self._terminated: return diff --git a/artiq/master/worker_impl.py b/artiq/master/worker_impl.py index 3c4003a21..0c99c5238 100644 --- a/artiq/master/worker_impl.py +++ b/artiq/master/worker_impl.py @@ -79,32 +79,39 @@ set_watchdog_factory(Watchdog) class Scheduler: - pause_noexc = staticmethod(make_parent_action("pause")) - - @host_only - def pause(self): - if self.pause_noexc(): - raise TerminationRequested - - submit = staticmethod(make_parent_action("scheduler_submit")) - delete = staticmethod(make_parent_action("scheduler_delete")) - request_termination = staticmethod( - make_parent_action("scheduler_request_termination")) - get_status = staticmethod(make_parent_action("scheduler_get_status")) - def set_run_info(self, rid, pipeline_name, expid, priority): self.rid = rid self.pipeline_name = pipeline_name self.expid = expid self.priority = priority - _check_pause = staticmethod(make_parent_action("scheduler_check_pause")) + pause_noexc = staticmethod(make_parent_action("pause")) + @host_only + def pause(self): + if self.pause_noexc(): + raise TerminationRequested + _check_pause = staticmethod(make_parent_action("scheduler_check_pause")) def check_pause(self, rid=None) -> TBool: if rid is None: rid = self.rid return self._check_pause(rid) + _submit = staticmethod(make_parent_action("scheduler_submit")) + def submit(self, pipeline_name=None, expid=None, priority=None, due_date=None, flush=False): + if pipeline_name is None: + pipeline_name = self.pipeline_name + if expid is None: + expid = self.expid + if priority is None: + priority = self.priority + return self._submit(pipeline_name, expid, priority, due_date, flush) + + delete = staticmethod(make_parent_action("scheduler_delete")) + request_termination = staticmethod( + make_parent_action("scheduler_request_termination")) + get_status = staticmethod(make_parent_action("scheduler_get_status")) + class CCB: issue = staticmethod(make_parent_action("ccb_issue")) From ed2624545f2ecb1d878b58909cb9ec3fdb32f0c4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 18 Oct 2016 14:54:59 +0800 Subject: [PATCH 068/127] master: ensure same dataset is in broadcast and local when mutating --- artiq/master/worker_db.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/master/worker_db.py b/artiq/master/worker_db.py index 11d6bf9b6..e00da8a1e 100644 --- a/artiq/master/worker_db.py +++ b/artiq/master/worker_db.py @@ -198,6 +198,8 @@ class DatasetManager: if key in self.local: target = self.local[key] if key in self.broadcast.read: + if target is not None: + assert target is self.broadcast.read[key][1] target = self.broadcast[key][1] if target is None: raise KeyError("Cannot mutate non-existing dataset") From 69d96b015801266f89a08c8dc59f5aa1d98dcf35 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 18 Oct 2016 17:08:36 +0800 Subject: [PATCH 069/127] master: archive input datasets. Closes #587 --- RELEASE_NOTES.rst | 3 +++ artiq/browser/files.py | 10 ++++++++- .../repository/flopping_f_simulation.py | 5 +++-- artiq/language/environment.py | 14 ++++++++---- artiq/master/worker_db.py | 22 ++++++++++++++++--- artiq/master/worker_impl.py | 2 +- 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index ca452af70..16bda10f0 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -13,6 +13,9 @@ Release notes * Dynamic __getattr__'s returning RPC target methods are not supported anymore. Controller driver classes must define all their methods intended for RPC as members. +* Datasets requested by experiments are by default archived into their HDF5 + output. If this behavior is undesirable, turn it off by passing + ``archive=False`` to ``get_dataset``. 2.0 diff --git a/artiq/browser/files.py b/artiq/browser/files.py index 1a45c7938..1dfbac28e 100644 --- a/artiq/browser/files.py +++ b/artiq/browser/files.py @@ -187,8 +187,16 @@ class FilesDock(QtWidgets.QDockWidget): except: logger.warning("unable to read metadata from %s", info.filePath(), exc_info=True) + rd = dict() + if "archive" in f: + rd = {k: (True, v.value) for k, v in f["archive"].items()} if "datasets" in f: - rd = {k: (True, v.value) for k, v in f["datasets"].items()} + for k, v in f["datasets"].items(): + if k in rd: + logger.warning("dataset '%s' is both in archive and " + "outputs", k) + rd[k] = (True, v.value) + if rd: self.datasets.init(rd) self.dataset_changed.emit(info.filePath()) diff --git a/artiq/examples/master/repository/flopping_f_simulation.py b/artiq/examples/master/repository/flopping_f_simulation.py index 1af331999..1693ab201 100644 --- a/artiq/examples/master/repository/flopping_f_simulation.py +++ b/artiq/examples/master/repository/flopping_f_simulation.py @@ -58,7 +58,7 @@ class FloppingF(EnvExperiment): # Use get_dataset so that analyze can be run stand-alone. brightness = self.get_dataset("flopping_f_brightness") try: - frequency = self.get_dataset("flopping_f_frequency") + frequency = self.get_dataset("flopping_f_frequency", archive=False) except KeyError: # Since flopping_f_frequency is not saved, it is missing if # analyze() is run on HDF5 data. But assuming that the arguments @@ -68,7 +68,8 @@ class FloppingF(EnvExperiment): self.set_dataset("flopping_f_frequency", frequency, broadcast=True, save=False) popt, pcov = curve_fit(model, frequency, brightness, - p0=[self.get_dataset("flopping_freq", 1500.0)]) + p0=[self.get_dataset("flopping_freq", 1500.0, + archive=False)]) perr = np.sqrt(np.diag(pcov)) if perr < 0.1: F0 = float(popt) diff --git a/artiq/language/environment.py b/artiq/language/environment.py index 2fd2b39b1..822279e48 100644 --- a/artiq/language/environment.py +++ b/artiq/language/environment.py @@ -303,7 +303,7 @@ class HasEnvironment: as ``slice(*sub_tuple)`` (multi-dimensional slicing).""" self.__dataset_mgr.mutate(key, index, value) - def get_dataset(self, key, default=NoDefault): + def get_dataset(self, key, default=NoDefault, archive=True): """Returns the contents of a dataset. The local storage is searched first, followed by the master storage @@ -312,19 +312,25 @@ class HasEnvironment: If the dataset does not exist, returns the default value. If no default is provided, raises ``KeyError``. + + By default, datasets obtained by this method are archived into the output + HDF5 file of the experiment. If an archived dataset is requested more + than one time (and therefore its value has potentially changed) or is + modified, a warning is emitted. Archival can be turned off by setting + the ``archive`` argument to ``False``. """ try: - return self.__dataset_mgr.get(key) + return self.__dataset_mgr.get(key, archive) except KeyError: if default is NoDefault: raise else: return default - def setattr_dataset(self, key, default=NoDefault): + def setattr_dataset(self, key, default=NoDefault, archive=True): """Sets the contents of a dataset as attribute. The names of the dataset and of the attribute are the same.""" - setattr(self, key, self.get_dataset(key, default)) + setattr(self, key, self.get_dataset(key, default, archive)) class Experiment: diff --git a/artiq/master/worker_db.py b/artiq/master/worker_db.py index e00da8a1e..29d4f11c7 100644 --- a/artiq/master/worker_db.py +++ b/artiq/master/worker_db.py @@ -181,11 +181,17 @@ class DatasetManager: def __init__(self, ddb): self.broadcast = Notifier(dict()) self.local = dict() + self.archive = dict() self.ddb = ddb self.broadcast.publish = ddb.update def set(self, key, value, broadcast=False, persist=False, save=True): + if key in self.archive: + logger.warning("Modifying dataset '%s' which is in archive, " + "archive will remain untouched", + key, stack_info=True) + if persist: broadcast = True if broadcast: @@ -211,12 +217,22 @@ class DatasetManager: index = slice(*index) setitem(target, index, value) - def get(self, key): + def get(self, key, archive): if key in self.local: return self.local[key] else: - return self.ddb.get(key) + data = self.ddb.get(key) + if archive: + if key in self.archive: + logger.warning("Dataset '%s' is already in archive, " + "overwriting", key, stack_info=True) + self.archive[key] = data + return data def write_hdf5(self, f): + datasets_group = f.create_group("datasets") for k, v in self.local.items(): - f[k] = v + datasets_group[k] = v + archive_group = f.create_group("archive") + for k, v in self.archive.items(): + archive_group[k] = v diff --git a/artiq/master/worker_impl.py b/artiq/master/worker_impl.py index 0c99c5238..854d1a31f 100644 --- a/artiq/master/worker_impl.py +++ b/artiq/master/worker_impl.py @@ -245,7 +245,7 @@ def main(): elif action == "write_results": filename = "{:09}-{}.h5".format(rid, exp.__name__) with h5py.File(filename, "w") as f: - dataset_mgr.write_hdf5(f.create_group("datasets")) + dataset_mgr.write_hdf5(f) f["artiq_version"] = artiq_version f["rid"] = rid f["start_time"] = int(time.mktime(start_time)) From 5d184f8061dcfe064691f082e04f9cd2b3a021a1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 18 Oct 2016 17:10:18 +0800 Subject: [PATCH 070/127] master: keep dataset manager consistent when set_dataset is called with contradictory attributes --- artiq/master/worker_db.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/artiq/master/worker_db.py b/artiq/master/worker_db.py index 29d4f11c7..f0d87c086 100644 --- a/artiq/master/worker_db.py +++ b/artiq/master/worker_db.py @@ -196,8 +196,12 @@ class DatasetManager: broadcast = True if broadcast: self.broadcast[key] = persist, value + elif key in self.broadcast.read: + del self.broadcast[key] if save: self.local[key] = value + elif key in self.local: + del self.local[key] def mutate(self, key, index, value): target = None From 6aa13fbf250f78f05e6bbd136510d5be537dc66f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 19 Oct 2016 20:12:16 +0800 Subject: [PATCH 071/127] master/worker_db: set default value for archive --- artiq/master/worker_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/master/worker_db.py b/artiq/master/worker_db.py index f0d87c086..1a4ef61e6 100644 --- a/artiq/master/worker_db.py +++ b/artiq/master/worker_db.py @@ -221,7 +221,7 @@ class DatasetManager: index = slice(*index) setitem(target, index, value) - def get(self, key, archive): + def get(self, key, archive=False): if key in self.local: return self.local[key] else: From 68720174490e18494476157d62a3ff144e36dd84 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 21 Oct 2016 12:09:14 +0000 Subject: [PATCH 072/127] gateware: extend mailbox to 3 entries. --- artiq/gateware/amp/mailbox.py | 8 ++++---- artiq/gateware/soc.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/amp/mailbox.py b/artiq/gateware/amp/mailbox.py index 1addb50ba..6f4a8987f 100644 --- a/artiq/gateware/amp/mailbox.py +++ b/artiq/gateware/amp/mailbox.py @@ -3,19 +3,19 @@ from misoc.interconnect import wishbone class Mailbox(Module): - def __init__(self): + def __init__(self, size=1): self.i1 = wishbone.Interface() self.i2 = wishbone.Interface() # # # - value = Signal(32) + values = Array([Signal(32) for _ in range(size)]) for i in self.i1, self.i2: self.sync += [ - i.dat_r.eq(value), + i.dat_r.eq(values[i.adr]), i.ack.eq(0), If(i.cyc & i.stb & ~i.ack, i.ack.eq(1), - If(i.we, value.eq(i.dat_w)) + If(i.we, values[i.adr].eq(i.dat_w)) ) ] diff --git a/artiq/gateware/soc.py b/artiq/gateware/soc.py index cd567b305..68ae4066a 100644 --- a/artiq/gateware/soc.py +++ b/artiq/gateware/soc.py @@ -28,7 +28,7 @@ class AMPSoC: self.add_cpulevel_sdram_if(self.kernel_cpu.wb_sdram) self.csr_devices.append("kernel_cpu") - self.submodules.mailbox = amp.Mailbox() + self.submodules.mailbox = amp.Mailbox(size=3) self.add_wb_slave(mem_decoder(self.mem_map["mailbox"]), self.mailbox.i1) self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["mailbox"]), From 65b2e4464ccbd769711861accedd0195fc80258c Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Thu, 27 Oct 2016 14:14:56 +0200 Subject: [PATCH 073/127] phaser: sysref/sync diff term --- artiq/gateware/phaser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/gateware/phaser.py b/artiq/gateware/phaser.py index d72e6c153..4673b7b90 100644 --- a/artiq/gateware/phaser.py +++ b/artiq/gateware/phaser.py @@ -25,16 +25,19 @@ fmc_adapter_io = [ Subsignal("p", Pins("HPC:LA00_CC_P")), Subsignal("n", Pins("HPC:LA00_CC_N")), IOStandard("LVDS_25"), + Misc("DIFF_TERM=TRUE"), ), ("ad9154_sync", 0, Subsignal("p", Pins("HPC:LA01_CC_P")), Subsignal("n", Pins("HPC:LA01_CC_N")), IOStandard("LVDS_25"), + Misc("DIFF_TERM=TRUE"), ), ("ad9154_sync", 1, Subsignal("p", Pins("HPC:LA02_P")), Subsignal("n", Pins("HPC:LA02_N")), IOStandard("LVDS_25"), + Misc("DIFF_TERM=TRUE"), ), ("ad9154_jesd", 0, # AD9154's SERIND7 Subsignal("txp", Pins("HPC:DP0_C2M_P")), From c428800caf52a77a22a9be0d967715cccb4e0b63 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Thu, 27 Oct 2016 15:39:39 +0200 Subject: [PATCH 074/127] phaser: spi, sma_gpio: 2.5 V --- artiq/gateware/phaser.py | 6 +++--- artiq/gateware/targets/kc705.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/phaser.py b/artiq/gateware/phaser.py index 4673b7b90..ca6185764 100644 --- a/artiq/gateware/phaser.py +++ b/artiq/gateware/phaser.py @@ -13,10 +13,10 @@ fmc_adapter_io = [ Subsignal("mosi", Pins("HPC:LA03_N")), Subsignal("miso", Pins("HPC:LA04_P")), Subsignal("en", Pins("HPC:LA05_N")), - IOStandard("LVTTL"), + IOStandard("LVCMOS25"), ), - ("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVTTL")), - ("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVTTL")), + ("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVCMOS25")), + ("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVCMOS25")), ("ad9154_refclk", 0, Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")), Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")), diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index f3abcd3ec..d6b1fede2 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -537,7 +537,7 @@ class Phaser(_NIST_Ions): rtio_channels = [] phy = ttl_serdes_7series.Inout_8X( - platform.request("user_sma_gpio_n_33")) + platform.request("user_sma_gpio_n")) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128)) From 8eff8586bb73c4af322693de3b5c544e5466e080 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 28 Oct 2016 01:57:55 +0200 Subject: [PATCH 075/127] moninj.rs: force u32 dds_ftws --- artiq/runtime.rs/src/moninj.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime.rs/src/moninj.rs b/artiq/runtime.rs/src/moninj.rs index 13c8ef4cd..4db0aca0f 100644 --- a/artiq/runtime.rs/src/moninj.rs +++ b/artiq/runtime.rs/src/moninj.rs @@ -18,7 +18,7 @@ fn worker(socket: &mut UdpSocket) -> io::Result<()> { match request { Request::Monitor => { - let mut dds_ftws = [0; (csr::CONFIG_RTIO_DDS_COUNT as usize * + let mut dds_ftws = [0u32; (csr::CONFIG_RTIO_DDS_COUNT as usize * csr::CONFIG_DDS_CHANNELS_PER_BUS as usize)]; let mut reply = Reply::default(); @@ -54,7 +54,7 @@ fn worker(socket: &mut UdpSocket) -> io::Result<()> { csr::rtio_moninj::mon_probe_sel_write(i as u8); csr::rtio_moninj::mon_value_update_write(1); dds_ftws[(csr::CONFIG_DDS_CHANNELS_PER_BUS * j + i) as usize] = - csr::rtio_moninj::mon_value_read(); + csr::rtio_moninj::mon_value_read() as u32; } } } From 2a1e529dcf144f60e9277cbcb7be55153e347513 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 28 Oct 2016 01:58:08 +0200 Subject: [PATCH 076/127] phaser: DDS config dummies --- artiq/gateware/targets/kc705.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index d6b1fede2..57a31da9c 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -565,6 +565,15 @@ class Phaser(_NIST_Ions): self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.LogChannel()) + + # FIXME: dummy + self.config["RTIO_FIRST_DDS_CHANNEL"] = 0 + self.config["RTIO_DDS_COUNT"] = 1 + self.config["DDS_CHANNELS_PER_BUS"] = 1 + self.config["DDS_AD9914"] = True + self.config["DDS_ONEHOT_SEL"] = True + self.config["DDS_RTIO_CLK_RATIO"] = 3 + self.add_rtio(rtio_channels, _PhaserCRG(platform, self.crg.cd_sys.clk)) self.comb += self.rtio_crg.refclk.eq(self.ad9154.jesd.cd_jesd.clk) From b14fcd41e4cfe388248958920e38e9291f0f3e18 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 28 Oct 2016 02:39:24 +0200 Subject: [PATCH 077/127] ksupport: add ad9154* --- artiq/runtime.rs/libksupport/api.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs index 46570feab..4670a20ea 100644 --- a/artiq/runtime.rs/libksupport/api.rs +++ b/artiq/runtime.rs/libksupport/api.rs @@ -117,4 +117,18 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(i2c_stop), api!(i2c_write), api!(i2c_read), + +// #if (defined CONFIG_AD9154_CS) + api!(ad9154_init), + api!(ad9154_write), + api!(ad9154_read), + + api!(ad9516_write), + api!(ad9516_read), + + api!(ad9154_jesd_enable), + api!(ad9154_jesd_ready), + api!(ad9154_jesd_prbs), + api!(ad9154_jesd_stpl), +// #endif ]; From ed4d57c6385c7ae7c739b0d88643ccdb2a6976b4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 29 Oct 2016 21:17:13 +0800 Subject: [PATCH 078/127] 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 079/127] 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 080/127] 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 081/127] 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 082/127] 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 083/127] 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 084/127] 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 085/127] 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 086/127] 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 087/127] 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 088/127] 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 089/127] 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 090/127] 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 091/127] 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 092/127] 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 093/127] 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 094/127] 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 095/127] 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 096/127] 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 097/127] 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 098/127] 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 d158c69be0ec23910d98ffc11a4065d3fca0634b Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 5 Nov 2016 16:54:23 +0100 Subject: [PATCH 099/127] phaser: fix frequency comment --- 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 57a31da9c..0e1e33c16 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -415,7 +415,7 @@ class _PhaserCRG(Module, AutoCSR): # Warning: CLKINSEL=0 means CLKIN2 is selected i_CLKINSEL=~self._clock_sel.storage, - # VCO @ 1GHz when using 125MHz input + # VCO @ 1GHz when using 250MHz input p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=2, i_CLKFBIN=self.cd_rtio.clk, i_RST=self._pll_reset.storage, From 453e8b7eb3a660575112b5d56f82614179837ca3 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 6 Nov 2016 23:52:27 +0800 Subject: [PATCH 100/127] 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 101/127] 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 102/127] 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 103/127] 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 104/127] 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 105/127] 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 106/127] 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 107/127] 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 108/127] 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 109/127] 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 110/127] 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 111/127] 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 112/127] 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 113/127] 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 114/127] 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 115/127] 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 116/127] 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 117/127] 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 118/127] =?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 119/127] 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 120/127] 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 121/127] 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 122/127] 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 123/127] 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 124/127] 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 125/127] 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); From 2e482505c67b3693ba16c8db4fc1d40a0269e337 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 13 Nov 2016 17:08:59 +0100 Subject: [PATCH 126/127] phaser: fix DDS dummy cfg --- artiq/gateware/targets/kc705.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index b8c73d825..b96450816 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -580,22 +580,25 @@ class Phaser(MiniSoC, AMPSoC): self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.LogChannel()) - self.config["RTIO_FIRST_DDS_CHANNEL"] = 0 + self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_DDS_COUNT"] = 1 self.config["DDS_CHANNELS_PER_BUS"] = 1 - self.config["DDS_ONEHOT_SEL"] = 1 + self.config["DDS_AD9914"] = None + self.config["DDS_ONEHOT_SEL"] = None self.config["DDS_RTIO_CLK_RATIO"] = 8 - self.submodules.rtio_crg = _PhaserCRG(platform, self.ad9154.jesd.cd_jesd.clk) + self.submodules.rtio_crg = _PhaserCRG( + platform, self.ad9154.jesd.cd_jesd.clk) self.csr_devices.append("rtio_crg") self.submodules.rtio = rtio.RTIO(rtio_channels) self.register_kernel_cpu_csrdevice("rtio") self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.csr_devices.append("rtio_moninj") - self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, - self.get_native_sdram_if()) + self.submodules.rtio_analyzer = rtio.Analyzer( + self.rtio, self.get_native_sdram_if()) self.csr_devices.append("rtio_analyzer") + self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width platform.add_false_path_constraints( self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk) From 70a70320bd9b74a0766a40405ca909fbc3149971 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 13 Nov 2016 17:29:38 +0100 Subject: [PATCH 127/127] phaser: use misoc cordic --- artiq/gateware/dsp/cordic.py | 358 ----------------------------------- artiq/gateware/dsp/sawg.py | 2 +- 2 files changed, 1 insertion(+), 359 deletions(-) delete mode 100644 artiq/gateware/dsp/cordic.py diff --git a/artiq/gateware/dsp/cordic.py b/artiq/gateware/dsp/cordic.py deleted file mode 100644 index f09ca5031..000000000 --- a/artiq/gateware/dsp/cordic.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright 2014-2015 Robert Jordens -# -# This file is part of redpid. -# -# redpid is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# redpid is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with redpid. If not, see . - -from math import atan, atanh, log, sqrt, pi - -from migen import * - - -class TwoQuadrantCordic(Module): - """Coordinate rotation digital computer - - Trigonometric, and arithmetic functions implemented using - additions/subtractions and shifts. - - http://eprints.soton.ac.uk/267873/1/tcas1_cordic_review.pdf - - http://www.andraka.com/files/crdcsrvy.pdf - - http://zatto.free.fr/manual/Volder_CORDIC.pdf - - The way the CORDIC is executed is controlled by `eval_mode`. - If `"iterative"` the stages are iteratively evaluated, one per clock - cycle. This mode uses the least amount of registers, but has the - lowest throughput and highest latency. If `"pipelined"` all stages - are executed in every clock cycle but separated by registers. This - mode has full throughput but uses many registers and has large - latency. If `"combinatorial"`, there are no registers, throughput is - maximal and latency is zero. `"pipelined"` and `"combinatorial"` use - the same number of shifters and adders. - - The type of trigonometric/arithmetic function is determined by - `cordic_mode` and `func_mode`. :math:`g` is the gain of the CORDIC. - - * rotate-circular: rotate the vector `(xi, yi)` by an angle `zi`. - Used to calculate trigonometric functions, `sin(), cos(), - tan() = sin()/cos()`, or to perform polar-to-cartesian coordinate - transformation: - - .. math:: - x_o = g \\cos(z_i) x_i - g \\sin(z_i) y_i - - y_o = g \\sin(z_i) x_i + g \\cos(z_i) y_i - - * vector-circular: determine length and angle of the vector - `(xi, yi)`. Used to calculate `arctan(), sqrt()` or - to perform cartesian-to-polar transformation: - - .. math:: - x_o = g\\sqrt{x_i^2 + y_i^2} - - z_o = z_i + \\tan^{-1}(y_i/x_i) - - * rotate-hyperbolic: hyperbolic functions of `zi`. Used to - calculate hyperbolic functions, `sinh, cosh, tanh = cosh/sinh, - exp = cosh + sinh`: - - .. math:: - x_o = g \\cosh(z_i) x_i + g \\sinh(z_i) y_i - - y_o = g \\sinh(z_i) x_i + g \\cosh(z_i) z_i - - * vector-hyperbolic: natural logarithm `ln(), arctanh()`, and - `sqrt()`. Use `x_i = a + b` and `y_i = a - b` to obtain `2* - sqrt(a*b)` and `ln(a/b)/2`: - - .. math:: - x_o = g\\sqrt{x_i^2 - y_i^2} - - z_o = z_i + \\tanh^{-1}(y_i/x_i) - - * rotate-linear: multiply and accumulate (not a very good - multiplier implementation): - - .. math:: - y_o = g(y_i + x_i z_i) - - * vector-linear: divide and accumulate: - - .. math:: - z_o = g(z_i + y_i/x_i) - - Parameters - ---------- - width : int - Bit width of the input and output signals. Defaults to 16. Input - and output signals are signed. - widthz : int - Bit with of `zi` and `zo`. Defaults to the `width`. - stages : int or None - Number of CORDIC incremental rotation stages. Defaults to - `width + min(1, guard)`. - guard : int or None - Add guard bits to the intermediate signals. If `None`, - defaults to `guard = log2(width)` which guarantees accuracy - to `width` bits. - eval_mode : str, {"iterative", "pipelined", "combinatorial"} - cordic_mode : str, {"rotate", "vector"} - func_mode : str, {"circular", "linear", "hyperbolic"} - Evaluation and arithmetic mode. See above. - - Attributes - ---------- - xi, yi, zi : Signal(width), in - Input values, signed. - xo, yo, zo : Signal(width), out - Output values, signed. - new_out : Signal(1), out - Asserted if output values are freshly updated in the current - cycle. - new_in : Signal(1), out - Asserted if new input values are being read in the next cycle. - zmax : float - `zi` and `zo` normalization factor. Floating point `zmax` - corresponds to `1<<(widthz - 1)`. `x` and `y` are scaled such - that floating point `1` corresponds to `1<<(width - 1)`. - gain : float - Cumulative, intrinsic gain and scaling factor. In circular mode - `sqrt(xi**2 + yi**2)` should be no larger than `2**(width - 1)/gain` - to prevent overflow. Additionally, in hyperbolic and linear mode, - the operation itself can cause overflow. - interval : int - Output interval in clock cycles. Inverse throughput. - latency : int - Input-to-output latency. The result corresponding to the inputs - appears at the outputs `latency` cycles later. - - Notes - ----- - - Each stage `i` in the CORDIC performs the following operation: - - .. math:: - x_{i+1} = x_i - m d_i y_i r^{-s_{m,i}}, - - y_{i+1} = y_i + d_i x_i r^{-s_{m,i}}, - - z_{i+1} = z_i - d_i a_{m,i}, - - where: - - * :math:`d_i`: clockwise or counterclockwise, determined by - `sign(z_i)` in rotate mode or `sign(-y_i)` in vector mode. - - * :math:`r`: radix of the number system (2) - - * :math:`m`: 1: circular, 0: linear, -1: hyperbolic - - * :math:`s_{m,i}`: non decreasing integer shift sequence - - * :math:`a_{m,i}`: elemetary rotation angle: :math:`a_{m,i} = - \\tan^{-1}(\\sqrt{m} s_{m,i})/\\sqrt{m}`. - """ - def __init__(self, width=16, widthz=None, stages=None, guard=0, - eval_mode="iterative", cordic_mode="rotate", - func_mode="circular"): - # validate parameters - assert eval_mode in ("combinatorial", "pipelined", "iterative") - assert cordic_mode in ("rotate", "vector") - assert func_mode in ("circular", "linear", "hyperbolic") - self.cordic_mode = cordic_mode - self.func_mode = func_mode - if guard is None: - # guard bits to guarantee "width" accuracy - guard = int(log(width)/log(2)) - if widthz is None: - widthz = width - if stages is None: - stages = width + min(1, guard) # cuts error below LSB - - # input output interface - self.xi = Signal((width, True)) - self.yi = Signal((width, True)) - self.zi = Signal((widthz, True)) - self.xo = Signal((width, True)) - self.yo = Signal((width, True)) - self.zo = Signal((widthz, True)) - self.new_in = Signal() - self.new_out = Signal() - - ### - - a, s, self.zmax, self.gain = self._constants(stages, widthz + guard) - stages = len(a) # may have increased due to repetitions - - if eval_mode == "iterative": - num_sig = 3 - self.interval = stages + 1 - self.latency = stages + 2 - else: - num_sig = stages + 1 - self.interval = 1 - if eval_mode == "pipelined": - self.latency = stages - else: # combinatorial - self.latency = 0 - - # inter-stage signals - x = [Signal((width + guard, True)) for i in range(num_sig)] - y = [Signal((width + guard, True)) for i in range(num_sig)] - z = [Signal((widthz + guard, True)) for i in range(num_sig)] - - # hook up inputs and outputs to the first and last inter-stage - # signals - self.comb += [ - x[0].eq(self.xi << guard), - y[0].eq(self.yi << guard), - z[0].eq(self.zi << guard), - self.xo.eq(x[-1] >> guard), - self.yo.eq(y[-1] >> guard), - self.zo.eq(z[-1] >> guard), - ] - - if eval_mode == "iterative": - # We afford one additional iteration for in/out. - i = Signal(max=stages + 1) - self.comb += [ - self.new_in.eq(i == stages), - self.new_out.eq(i == 1), - ] - ai = Signal((widthz + guard, True)) - self.sync += ai.eq(Array(a)[i]) - if range(stages) == s: - si = i - 1 # shortcut if no stage repetitions - else: - si = Signal(max=stages + 1) - self.sync += si.eq(Array(s)[i]) - xi, yi, zi = x[1], y[1], z[1] - self.sync += [ - self._stage(xi, yi, zi, xi, yi, zi, si, ai), - i.eq(i + 1), - If(i == stages, - i.eq(0), - ), - If(i == 0, - x[2].eq(xi), y[2].eq(yi), z[2].eq(zi), - xi.eq(x[0]), yi.eq(y[0]), zi.eq(z[0]), - ) - ] - else: - self.comb += [ - self.new_out.eq(1), - self.new_in.eq(1), - ] - for i, si in enumerate(s): - stmt = self._stage(x[i], y[i], z[i], - x[i + 1], y[i + 1], z[i + 1], - si, a[i]) - if eval_mode == "pipelined": - self.sync += stmt - else: # combinatorial - self.comb += stmt - - def _constants(self, stages, bits): - if self.func_mode == "circular": - s = range(stages) - a = [atan(2**-i) for i in s] - g = [sqrt(1 + 2**(-2*i)) for i in s] - #zmax = sum(a) - # use pi anyway as the input z can cause overflow - # and we need the range for quadrant mapping - zmax = pi - elif self.func_mode == "linear": - s = range(stages) - a = [2**-i for i in s] - g = [1 for i in s] - #zmax = sum(a) - # use 2 anyway as this simplifies a and scaling - zmax = 2. - else: # hyperbolic - s = [] - # need to repeat some stages: - j = 4 - for i in range(stages): - if i == j: - s.append(j) - j = 3*j + 1 - s.append(i + 1) - a = [atanh(2**-i) for i in s] - g = [sqrt(1 - 2**(-2*i)) for i in s] - zmax = sum(a)*2 - # round here helps the width=2**i - 1 case but hurts the - # important width=2**i case - cast = int - if log(bits)/log(2) % 1: - cast = round - a = [cast(ai*2**(bits - 1)/zmax) for ai in a] - gain = 1. - for gi in g: - gain *= gi - return a, s, zmax, gain - - def _stage(self, xi, yi, zi, xo, yo, zo, i, ai): - dir = Signal() - if self.cordic_mode == "rotate": - self.comb += dir.eq(zi < 0) - else: # vector - self.comb += dir.eq(yi >= 0) - dx = yi >> i - dy = xi >> i - dz = ai - if self.func_mode == "linear": - dx = 0 - elif self.func_mode == "hyperbolic": - dx = -dx - stmt = [ - xo.eq(xi + Mux(dir, dx, -dx)), - yo.eq(yi + Mux(dir, -dy, dy)), - zo.eq(zi + Mux(dir, dz, -dz)) - ] - return stmt - - -class Cordic(TwoQuadrantCordic): - """Four-quadrant CORDIC - - Same as :class:`TwoQuadrantCordic` but with support and convergence - for `abs(zi) > pi/2 in circular rotate mode or `xi < 0` in circular - vector mode. - """ - def __init__(self, **kwargs): - TwoQuadrantCordic.__init__(self, **kwargs) - if self.func_mode != "circular": - return # no need to remap quadrants - - cxi, cyi, czi = self.xi, self.yi, self.zi - self.xi = xi = Signal.like(cxi) - self.yi = yi = Signal.like(cyi) - self.zi = zi = Signal.like(czi) - - ### - - q = Signal() - if self.cordic_mode == "rotate": - self.comb += q.eq(zi[-2] ^ zi[-1]) - else: # vector - self.comb += q.eq(xi < 0) - self.comb += [ - If(q, - Cat(cxi, cyi, czi).eq( - Cat(-xi, -yi, zi + (1 << len(zi) - 1))) - ).Else( - Cat(cxi, cyi, czi).eq(Cat(xi, yi, zi)) - ) - ] diff --git a/artiq/gateware/dsp/sawg.py b/artiq/gateware/dsp/sawg.py index 6d6fdb241..d23aa7865 100644 --- a/artiq/gateware/dsp/sawg.py +++ b/artiq/gateware/dsp/sawg.py @@ -1,7 +1,7 @@ from migen import * from misoc.interconnect.stream import Endpoint +from misoc.cores.cordic import Cordic -from .cordic import Cordic from .accu import PhasedAccu from .tools import eqh