From fdadf550fb864622187ae696ad21419d8bd1693a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 24 Sep 2016 00:41:39 +0200 Subject: [PATCH 01/94] RELEASE_NOTES: 2.0 --- RELEASE_NOTES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index cfe6b0e17..ca452af70 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -15,6 +15,12 @@ Release notes members. +2.0 +--- + +No further notes. + + 2.0rc2 ------ From 9562d8d1df5df9d71623b4eb8b44824e8c2b918c Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 23 Sep 2016 08:14:09 +0000 Subject: [PATCH 02/94] 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 03/94] 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 04/94] 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 05/94] 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 06/94] 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 07/94] 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 08/94] 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 09/94] 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 10/94] 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 11/94] 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 12/94] 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 13/94] 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 14/94] 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 15/94] 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 16/94] 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 17/94] 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 18/94] 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 19/94] 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 20/94] 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 21/94] 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 22/94] 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 23/94] 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 24/94] 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 25/94] 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 26/94] 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 27/94] 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 28/94] 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 29/94] 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 30/94] 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 31/94] 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 32/94] 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 33/94] 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 34/94] 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 35/94] 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 36/94] 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 37/94] 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 38/94] 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 39/94] 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 40/94] 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 41/94] 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 42/94] 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 43/94] 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 44/94] 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 45/94] 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 46/94] 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 47/94] 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 48/94] 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 49/94] 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 50/94] 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 51/94] 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 52/94] 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 53/94] 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 54/94] 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 55/94] 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 56/94] 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 57/94] 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 58/94] 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 59/94] 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 60/94] 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 61/94] 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 62/94] 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 63/94] 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 64/94] 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 65/94] 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 66/94] 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 67/94] 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 68/94] 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 69/94] 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 70/94] 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 71/94] 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 72/94] 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 73/94] 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 ed4d57c6385c7ae7c739b0d88643ccdb2a6976b4 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 29 Oct 2016 21:17:13 +0800 Subject: [PATCH 74/94] 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 75/94] 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 76/94] 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 77/94] 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 78/94] 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 79/94] 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 80/94] 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 81/94] 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 82/94] 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 83/94] 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 84/94] 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 85/94] 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 86/94] 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 87/94] 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 88/94] 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 89/94] 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 90/94] 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 91/94] 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 92/94] 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 93/94] 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 94/94] 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