From 527b1e986cbe738a38849db4cf0df997526ec255 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 16 Jan 2017 14:15:24 +0000 Subject: [PATCH] firmware: integrate smoltcp instead of lwip. --- .gitmodules | 4 - artiq/firmware/Cargo.lock | 35 +- artiq/firmware/libksupport/Cargo.toml | 2 +- artiq/firmware/liblwip-sys/Cargo.toml | 8 - artiq/firmware/liblwip-sys/lib.rs | 166 ------- artiq/firmware/liblwip/Cargo.toml | 16 - artiq/firmware/liblwip/lib.rs | 645 -------------------------- artiq/firmware/runtime/Cargo.toml | 11 +- artiq/firmware/runtime/analyzer.rs | 28 +- artiq/firmware/runtime/ethmac.rs | 77 +++ artiq/firmware/runtime/lib.rs | 179 ++++--- artiq/firmware/runtime/logger.rs | 24 +- artiq/firmware/runtime/moninj.rs | 18 +- artiq/firmware/runtime/rtio_mgt.rs | 32 +- artiq/firmware/runtime/sched.rs | 558 +++++++++++----------- artiq/firmware/runtime/session.rs | 167 +++---- artiq/gateware/amp/soc.py | 2 - artiq/runtime/Makefile | 13 +- artiq/runtime/liblwip/Makefile | 83 ---- artiq/runtime/liblwip/arch/cc.h | 35 -- artiq/runtime/liblwip/arch/perf.h | 11 - artiq/runtime/liblwip/liteethif.c | 130 ------ artiq/runtime/liblwip/liteethif.h | 13 - artiq/runtime/liblwip/lwipopts.h | 192 -------- artiq/runtime/lwip | 1 - artiq/runtime/main.c | 197 -------- 26 files changed, 639 insertions(+), 2008 deletions(-) delete mode 100644 artiq/firmware/liblwip-sys/Cargo.toml delete mode 100644 artiq/firmware/liblwip-sys/lib.rs delete mode 100644 artiq/firmware/liblwip/Cargo.toml delete mode 100644 artiq/firmware/liblwip/lib.rs create mode 100644 artiq/firmware/runtime/ethmac.rs delete mode 100644 artiq/runtime/liblwip/Makefile delete mode 100644 artiq/runtime/liblwip/arch/cc.h delete mode 100644 artiq/runtime/liblwip/arch/perf.h delete mode 100644 artiq/runtime/liblwip/liteethif.c delete mode 100644 artiq/runtime/liblwip/liteethif.h delete mode 100644 artiq/runtime/liblwip/lwipopts.h delete mode 160000 artiq/runtime/lwip delete mode 100644 artiq/runtime/main.c diff --git a/.gitmodules b/.gitmodules index ef4965b5f..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +0,0 @@ -[submodule "artiq/runtime/lwip"] - path = artiq/runtime/lwip - url = https://github.com/m-labs/lwip - ignore = untracked diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 3e14fd830..1dc27064f 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "byteorder" -version = "0.5.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -45,7 +45,7 @@ version = "0.0.0" dependencies = [ "alloc_none 0.0.0", "board 0.0.0", - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "std_artiq 0.0.0", ] @@ -65,16 +65,9 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "lwip" -version = "0.0.0" -dependencies = [ - "lwip-sys 0.0.0", - "std_artiq 0.0.0", -] - -[[package]] -name = "lwip-sys" -version = "0.0.0" +name = "managed" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "runtime" @@ -82,11 +75,11 @@ version = "0.0.0" dependencies = [ "alloc_artiq 0.0.0", "board 0.0.0", - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (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.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lwip 0.0.0", + "smoltcp 0.1.0 (git+https://github.com/m-labs/smoltcp?rev=d57b42c)", "std_artiq 0.0.0", "walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -103,6 +96,16 @@ dependencies = [ "walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "smoltcp" +version = "0.1.0" +source = "git+https://github.com/m-labs/smoltcp?rev=d57b42c#d57b42ca93677e392990df3517c7548853da5192" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "walkdir" version = "1.0.3" @@ -123,12 +126,14 @@ 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 byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "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.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493" +"checksum managed 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5981b4c6de5ce272aaf2caaa56adb8f6fd24a73206b38302db572ab9374aab10" +"checksum smoltcp 0.1.0 (git+https://github.com/m-labs/smoltcp?rev=d57b42c)" = "" "checksum walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7c16466ecc507c7cb5988db03e6eab4aaeab89a5c37a29251fcfd3ac9b7afe" "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/firmware/libksupport/Cargo.toml b/artiq/firmware/libksupport/Cargo.toml index 47c27a97d..be23c936f 100644 --- a/artiq/firmware/libksupport/Cargo.toml +++ b/artiq/firmware/libksupport/Cargo.toml @@ -13,4 +13,4 @@ crate-type = ["staticlib"] alloc_none = { path = "../liballoc_none" } std_artiq = { path = "../libstd_artiq" } board = { path = "../libboard" } -byteorder = { version = "0.5", default-features = false } +byteorder = { version = "1.0", default-features = false } diff --git a/artiq/firmware/liblwip-sys/Cargo.toml b/artiq/firmware/liblwip-sys/Cargo.toml deleted file mode 100644 index b3051166b..000000000 --- a/artiq/firmware/liblwip-sys/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -authors = ["M-Labs"] -name = "lwip-sys" -version = "0.0.0" - -[lib] -name = "lwip_sys" -path = "lib.rs" diff --git a/artiq/firmware/liblwip-sys/lib.rs b/artiq/firmware/liblwip-sys/lib.rs deleted file mode 100644 index 912b7c381..000000000 --- a/artiq/firmware/liblwip-sys/lib.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![no_std] -#![feature(libc)] -#![allow(non_camel_case_types)] - -extern crate libc; - -pub use err::*; -pub use pbuf_layer::*; -pub use pbuf_type::*; -pub use ip_addr_type::*; - -use libc::{c_void, c_int}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(i8)] -pub enum err { - ERR_OK = 0, - ERR_MEM = -1, - ERR_BUF = -2, - ERR_TIMEOUT = -3, - ERR_RTE = -4, - ERR_INPROGRESS = -5, - ERR_VAL = -6, - ERR_WOULDBLOCK = -7, - ERR_USE = -8, - ERR_ALREADY = -9, - ERR_ISCONN = -10, - ERR_CONN = -11, - ERR_IF = -12, - ERR_ABRT = -13, - ERR_RST = -14, - ERR_CLSD = -15, - ERR_ARG = -16, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum pbuf_layer { - PBUF_TRANSPORT, - PBUF_IP, - PBUF_LINK, - PBUF_RAW_TX, - PBUF_RAW -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum pbuf_type { - PBUF_RAM, - PBUF_ROM, - PBUF_REF, - PBUF_POOL, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum ip_addr_type { - IPADDR_TYPE_V4 = 0, - IPADDR_TYPE_V6 = 6, - IPADDR_TYPE_ANY = 46, -} - -#[repr(C)] -pub struct pbuf { - pub next: *mut pbuf, - pub payload: *mut c_void, - pub tot_len: u16, - pub len: u16, - pub type_: pbuf_type, - pub flags: u8, - pub ref_: u16 -} - -#[derive(Clone)] -#[repr(C)] -pub struct ip4_addr { - pub addr: u32 -} - -#[derive(Clone)] -#[repr(C)] -pub struct ip6_addr { - pub addr: [u32; 4] -} - -#[derive(Clone)] -#[repr(C)] -pub struct ip_addr { - pub data: [u32; 4], - pub type_: ip_addr_type -} - -#[repr(C)] -pub struct tcp_pcb { - __opaque: c_void -} - -#[repr(C)] -pub struct udp_pcb { - __opaque: c_void -} - -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); - pub fn pbuf_ref(p: *mut pbuf); - pub fn pbuf_free(p: *mut pbuf); - pub fn pbuf_cat(head: *mut pbuf, tail: *mut pbuf); - pub fn pbuf_chain(head: *mut pbuf, tail: *mut pbuf); - pub fn pbuf_dechain(p: *mut pbuf) -> *mut pbuf; - pub fn pbuf_copy(p_to: *mut pbuf, p_from: *mut pbuf) -> err; - pub fn pbuf_copy_partial(p: *mut pbuf, dataptr: *mut c_void, len: u16, offset: u16) -> u16; - pub fn pbuf_take(p: *mut pbuf, dataptr: *const c_void, len: u16) -> err; - pub fn pbuf_take_at(p: *mut pbuf, dataptr: *const c_void, len: u16, offset: u16) -> err; - pub fn pbuf_skip(in_: *mut pbuf, in_offset: u16, out_offset: *mut u16) -> *mut pbuf; - - pub fn tcp_new() -> *mut tcp_pcb; - pub fn tcp_arg(pcb: *mut tcp_pcb, arg: *mut c_void); - 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: 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: Option err>); - pub fn tcp_recv(pcb: *mut tcp_pcb, - recv: Option err>); - pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16); - pub fn tcp_poll(pcb: *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: Option); - - // nonstandard - pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; - pub fn tcp_so_options_(pcb: *mut tcp_pcb) -> *mut u8; - pub fn tcp_nagle_disable_(pcb: *mut tcp_pcb); - - pub fn udp_new() -> *mut udp_pcb; - pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb; - pub fn udp_remove(pcb: *mut udp_pcb); - pub fn udp_bind(pcb: *mut udp_pcb, ipaddr: *mut ip_addr, port: u16) -> err; - pub fn udp_connect(pcb: *mut udp_pcb, ipaddr: *mut ip_addr, port: u16) -> err; - pub fn udp_disconnect(pcb: *mut udp_pcb) -> err; - 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: Option, - recv_arg: *mut c_void); -} diff --git a/artiq/firmware/liblwip/Cargo.toml b/artiq/firmware/liblwip/Cargo.toml deleted file mode 100644 index 7fc81caba..000000000 --- a/artiq/firmware/liblwip/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -authors = ["M-Labs"] -name = "lwip" -version = "0.0.0" - -[lib] -name = "lwip" -path = "lib.rs" - -[dependencies] -lwip-sys = { path = "../liblwip-sys" } -std_artiq = { path = "../libstd_artiq", features = ["alloc"] } - -[features] -default = ["preemption"] -preemption = [] diff --git a/artiq/firmware/liblwip/lib.rs b/artiq/firmware/liblwip/lib.rs deleted file mode 100644 index 5beda5e92..000000000 --- a/artiq/firmware/liblwip/lib.rs +++ /dev/null @@ -1,645 +0,0 @@ -#![feature(alloc, collections, libc)] -#![no_std] - -extern crate alloc; -extern crate collections; -extern crate libc; -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; -use collections::LinkedList; -use libc::c_void; -use std::error; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Error { - OutOfMemory, - Buffer, - Timeout, - Routing, - InProgress, - IllegalValue, - WouldBlock, - AddressInUse, - AlreadyConnecting, - AlreadyConnected, - NotConnected, - Interface, - ConnectionAborted, - ConnectionReset, - ConnectionClosed, - IllegalArgument, - // Not used by lwip; added for building blocking interfaces. - Interrupted -} - -impl Error { - fn as_str(&self) -> &str { - match *self { - Error::OutOfMemory => "out of memory error", - Error::Buffer => "buffer error", - Error::Timeout => "timeout", - Error::Routing => "routing error", - Error::InProgress => "operation in progress", - Error::IllegalValue => "illegal value", - Error::WouldBlock => "operation would block", - Error::AddressInUse => "address in use", - Error::AlreadyConnecting => "already connecting", - Error::AlreadyConnected => "already connected", - Error::NotConnected => "not connected", - Error::Interface => "low-level netif error", - Error::ConnectionAborted => "connection aborted", - Error::ConnectionReset => "connection reset", - Error::ConnectionClosed => "connection closed", - Error::IllegalArgument => "illegal argument", - Error::Interrupted => "interrupted" - } - } -} - -impl core::fmt::Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - self.as_str() - } -} - -impl From for std::io::Error { - fn from(lower: Error) -> std::io::Error { - use std::io; - - match lower { - Error::Interrupted => io::Error::new(io::ErrorKind::Interrupted, "interrupted"), - err => io::Error::new(io::ErrorKind::Other, err) - } - } -} - -pub type Result = core::result::Result; - -fn result_from(err: lwip_sys::err, f: F) -> Result - where F: FnOnce() -> T { - match err { - lwip_sys::ERR_OK => Ok(f()), - lwip_sys::ERR_MEM => Err(Error::OutOfMemory), - lwip_sys::ERR_BUF => Err(Error::Buffer), - lwip_sys::ERR_TIMEOUT => Err(Error::Timeout), - lwip_sys::ERR_RTE => Err(Error::Routing), - lwip_sys::ERR_INPROGRESS => Err(Error::InProgress), - lwip_sys::ERR_VAL => Err(Error::IllegalValue), - lwip_sys::ERR_WOULDBLOCK => Err(Error::WouldBlock), - lwip_sys::ERR_USE => Err(Error::AddressInUse), - lwip_sys::ERR_ALREADY => Err(Error::AlreadyConnecting), - lwip_sys::ERR_ISCONN => Err(Error::AlreadyConnected), - lwip_sys::ERR_CONN => Err(Error::NotConnected), - lwip_sys::ERR_IF => Err(Error::Interface), - lwip_sys::ERR_ABRT => Err(Error::ConnectionAborted), - lwip_sys::ERR_RST => Err(Error::ConnectionReset), - lwip_sys::ERR_CLSD => Err(Error::ConnectionClosed), - lwip_sys::ERR_ARG => Err(Error::IllegalArgument), - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum IpAddr { - V4([u8; 4]), - V6([u16; 8]), - Any -} - -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::V4(octets) => - lwip_sys::ip_addr { - data: [(octets[0] as u32) << 24 | - (octets[1] as u32) << 16 | - (octets[2] as u32) << 8 | - (octets[3] as u32) << 0, - 0, 0, 0], - type_: lwip_sys::IPADDR_TYPE_V4 - }, - 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), - (segments[4] as u32) << 16 | (segments[5] as u32), - (segments[6] as u32) << 16 | (segments[7] as u32)], - type_: lwip_sys::IPADDR_TYPE_V6 - }, - IpAddr::Any => - lwip_sys::ip_addr { - data: [0; 4], - type_: lwip_sys::IPADDR_TYPE_ANY - } - } - } - - 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::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::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::Any - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct SocketAddr { - pub ip: IpAddr, - 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 } - } -} - -#[derive(Debug)] -pub struct Pbuf<'payload> { - raw: *mut lwip_sys::pbuf, - 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 } - } - - fn as_raw(&self) -> *mut lwip_sys::pbuf { - self.raw - } - - #[allow(dead_code)] - fn into_raw(self) -> *mut lwip_sys::pbuf { - let raw = self.raw; - core::mem::forget(self); - raw - } - - fn from_slice_with_type<'a>(slice: &'a [u8], type_: lwip_sys::pbuf_type) -> Pbuf<'a> { - assert!(slice.len() <= core::u16::MAX as usize); - unsafe { - let raw = lwip_sys::pbuf_alloc(lwip_sys::PBUF_RAW, slice.len() as u16, type_); - (*raw).payload = slice.as_ptr() as *mut u8 as *mut c_void; - Pbuf { raw: raw, phantom: PhantomData } - } - } - - pub fn from_slice(slice: &'payload [u8]) -> Pbuf<'payload> { - Self::from_slice_with_type(slice, lwip_sys::PBUF_REF) - } - - pub fn from_static_slice(slice: &'static [u8]) -> Pbuf<'static> { - // Avoids a copy. - Self::from_slice_with_type(slice, lwip_sys::PBUF_ROM) - } - - pub fn len(&self) -> usize { - unsafe { (*self.raw).len as usize } - } - - pub fn as_slice(&self) -> &[u8] { - unsafe { - core::slice::from_raw_parts((*self.raw).payload as *const u8, - (*self.raw).len as usize) - } - } - - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { - core::slice::from_raw_parts_mut((*self.raw).payload as *mut u8, - (*self.raw).len as usize) - } - } - - pub fn concat(&mut self, tail: Pbuf<'payload>) { - unsafe { lwip_sys::pbuf_cat(self.raw, tail.raw) } - } - - pub fn chain(&mut self, tail: &mut Pbuf<'payload>) { - unsafe { lwip_sys::pbuf_chain(self.raw, tail.raw) } - } -} - -impl<'a> Drop for Pbuf<'a> { - fn drop(&mut self) { - unsafe { lwip_sys::pbuf_free(self.raw) } - } -} - -#[derive(Debug)] -pub struct UdpSocketState { - recv_buffer: LinkedList<(Pbuf<'static>, SocketAddr)> -} - -impl UdpSocketState { - pub fn readable(&self) -> bool { - !self.recv_buffer.is_empty() - } -} - -#[derive(Debug)] -pub struct UdpSocket { - raw: *mut lwip_sys::udp_pcb, - 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, - pbuf: *mut lwip_sys::pbuf, - addr: *mut lwip_sys::ip_addr, port: u16) { - unsafe { - let state = arg as *mut RefCell; - let socket_addr = SocketAddr { ip: IpAddr::from_raw(addr), port: port }; - (*state).borrow_mut().recv_buffer.push_back((Pbuf::from_raw(pbuf), socket_addr)); - } - } - - unsafe { - let raw = lwip_sys::udp_new(); - if raw.is_null() { return Err(Error::OutOfMemory) } - - let mut state = Box::new(RefCell::new(UdpSocketState { - recv_buffer: LinkedList::new() - })); - let arg = &mut *state as *mut RefCell as *mut _; - lwip_sys::udp_recv(raw, Some(recv), arg); - Ok(UdpSocket { raw: raw, state: state }) - } - } - - pub fn state(&self) -> &RefCell { - &*self.state - } - - pub fn bind(&self, addr: SocketAddr) -> Result<()> { - result_from(unsafe { - lwip_sys::udp_bind(self.raw, &mut addr.ip.into_raw(), addr.port) - }, || ()) - } - - pub fn connect(&self, addr: SocketAddr) -> Result<()> { - result_from(unsafe { - lwip_sys::udp_connect(self.raw, &mut addr.ip.into_raw(), addr.port) - }, || ()) - } - - pub fn disconnect(&self) -> Result<()> { - result_from(unsafe { - lwip_sys::udp_disconnect(self.raw) - }, || ()) - } - - pub fn send<'a>(&'a self, pbuf: Pbuf<'a>) -> Result<()> { - result_from(unsafe { - lwip_sys::udp_send(self.raw, pbuf.as_raw()) - }, || ()) - } - - pub fn send_to<'a>(&'a self, pbuf: Pbuf<'a>, addr: SocketAddr) -> Result<()> { - result_from(unsafe { - lwip_sys::udp_sendto(self.raw, pbuf.as_raw(), - &mut addr.ip.into_raw(), addr.port) - }, || ()) - } - - pub fn try_recv(&self) -> Option<(Pbuf<'static>, SocketAddr)> { - self.state.borrow_mut().recv_buffer.pop_front() - } -} - -impl Drop for UdpSocket { - fn drop(&mut self) { - unsafe { lwip_sys::udp_remove(self.raw) } - } -} - -#[derive(Debug)] -pub struct TcpListenerState { - backlog: LinkedList -} - -impl TcpListenerState { - pub fn acceptable(&self) -> bool { - !self.backlog.is_empty() - } -} - -#[derive(Debug)] -pub struct TcpListener { - raw: *mut lwip_sys::tcp_pcb, - 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, - err: lwip_sys::err) -> lwip_sys::err { - if err != lwip_sys::ERR_OK { return err } - unsafe { - let state = arg as *mut RefCell; - (*state).borrow_mut().backlog.push_back(TcpStream::from_raw(newpcb)); - } - lwip_sys::ERR_OK - } - - unsafe { - let raw = lwip_sys::tcp_new(); - if raw.is_null() { return Err(Error::OutOfMemory) } - try!(result_from(lwip_sys::tcp_bind(raw, &mut addr.ip.into_raw(), addr.port), - || ())); - - let raw2 = lwip_sys::tcp_listen_with_backlog(raw, 0xff); - if raw2.is_null() { - lwip_sys::tcp_abort(raw); - return Err(Error::OutOfMemory) - } - - let mut state = Box::new(RefCell::new(TcpListenerState { - backlog: LinkedList::new() - })); - let arg = &mut *state as *mut RefCell as *mut _; - lwip_sys::tcp_arg(raw2, arg); - lwip_sys::tcp_accept(raw2, Some(accept)); - Ok(TcpListener { raw: raw2, state: state }) - } - } - - pub fn state(&self) -> &RefCell { - &*self.state - } - - 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 { - fn drop(&mut self) { - unsafe { - // tcp_close never fails on listening sockets - let _ = lwip_sys::tcp_close(self.raw); - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Shutdown { - Read, - Write, - Both, -} - -#[derive(Debug)] -pub struct TcpStreamState { - recv_buffer: LinkedList>>, - send_avail: usize, - total_sent: usize -} - -impl TcpStreamState { - pub fn readable(&self) -> bool { - !self.recv_buffer.is_empty() - } - - pub fn writeable(&self) -> bool { - !(self.send_avail == 0) - } -} - -#[derive(Debug)] -pub struct TcpStream { - raw: *mut lwip_sys::tcp_pcb, - 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, - pbuf: *mut lwip_sys::pbuf, err: lwip_sys::err) -> lwip_sys::err { - if err != lwip_sys::ERR_OK { return err } - unsafe { - let state = arg as *mut RefCell; - if pbuf.is_null() { - (*state).borrow_mut().recv_buffer.push_back(Err(Error::ConnectionClosed)) - } else { - (*state).borrow_mut().recv_buffer.push_back(Ok(Pbuf::from_raw(pbuf))) - } - } - lwip_sys::ERR_OK - } - - extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb, - len: u16) -> lwip_sys::err { - unsafe { - let state = arg as *mut RefCell; - 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 - } - - extern fn err(arg: *mut c_void, err: lwip_sys::err) { - unsafe { - let state = arg as *mut RefCell; - (*state).borrow_mut().recv_buffer.push_back(result_from(err, || unreachable!())) - } - } - - unsafe { - let mut state = Box::new(RefCell::new(TcpStreamState { - recv_buffer: LinkedList::new(), - 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); - lwip_sys::tcp_recv(raw, Some(recv)); - lwip_sys::tcp_sent(raw, Some(sent)); - lwip_sys::tcp_err(raw, Some(err)); - lwip_sys::tcp_nagle_disable_(raw); - TcpStream { raw: raw, state: state } - } - } - - pub fn state(&self) -> &RefCell { - &*self.state - } - - 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({ - lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16, - lwip_sys::TCP_WRITE_FLAG_MORE | - if copy { lwip_sys::TCP_WRITE_FLAG_COPY } else { 0 }) - }, || len); - 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<()> { - result_from(unsafe { - lwip_sys::tcp_write(self.raw, ptr::null(), 0, 0) - }, || ()) - } - - pub fn try_read(&self) -> Result>> { - let mut state = self.state.borrow_mut(); - match state.recv_buffer.front() { - None => return Ok(None), - Some(&Err(err)) => return Err(err), - Some(_) => () - } - match state.recv_buffer.pop_front() { - Some(Ok(pbuf)) => { - unsafe { lwip_sys::tcp_recved(self.raw, pbuf.len() as u16) } - return Ok(Some(pbuf)) - }, - _ => unreachable!() - } - } - - pub fn shutdown(&self, how: Shutdown) -> Result<()> { - let (shut_rx, shut_tx) = match how { - Shutdown::Read => (1, 0), - Shutdown::Write => (0, 1), - Shutdown::Both => (1, 1) - }; - result_from(unsafe { - lwip_sys::tcp_shutdown(self.raw, shut_rx, shut_tx) - }, || ()) - } - - pub fn close(self) -> Result<()> { - let result = result_from(unsafe { - lwip_sys::tcp_close(self.raw) - }, || ()); - core::mem::forget(self); // closing twice is illegal - result - } -} - -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/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index bffe58cc6..b086cc1ab 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -15,9 +15,14 @@ path = "lib.rs" [dependencies] alloc_artiq = { path = "../liballoc_artiq" } std_artiq = { path = "../libstd_artiq", features = ["alloc"] } -lwip = { path = "../liblwip", default-features = false } board = { path = "../libboard" } 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 = [] } log_buffer = { version = "1.0" } -byteorder = { version = "0.5", default-features = false } +byteorder = { version = "1.0", default-features = false } + +[dependencies.smoltcp] +git = "https://github.com/m-labs/smoltcp" +rev = "d57b42c" +default-features = false +features = ["use_alloc", "use_collections", "use_log"]#, "verbose"] diff --git a/artiq/firmware/runtime/analyzer.rs b/artiq/firmware/runtime/analyzer.rs index 098866db0..24b0d6520 100644 --- a/artiq/firmware/runtime/analyzer.rs +++ b/artiq/firmware/runtime/analyzer.rs @@ -1,7 +1,6 @@ use std::io::{self, Write}; use board::{self, csr}; -use sched::{Waiter, Spawner}; -use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY}; +use sched::{Io, TcpSocket}; use analyzer_proto::*; const BUFFER_SIZE: usize = 512 * 1024; @@ -41,7 +40,7 @@ fn disarm() { } } -fn worker(mut stream: TcpStream) -> io::Result<()> { +fn worker(socket: &mut TcpSocket) -> 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() }; @@ -57,36 +56,35 @@ fn worker(mut stream: TcpStream) -> io::Result<()> { }; trace!("{:?}", header); - try!(header.write_to(&mut stream)); + try!(header.write_to(socket)); if wraparound { - try!(stream.write(&data[pointer..])); - try!(stream.write(&data[..pointer])); + try!(socket.write_all(&data[pointer..])); + try!(socket.write_all(&data[..pointer])); } else { - try!(stream.write(&data[..pointer])); + try!(socket.write_all(&data[..pointer])); } Ok(()) } -pub fn thread(waiter: Waiter, _spawner: Spawner) { +pub fn thread(io: Io) { // 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); - + let mut socket = TcpSocket::with_buffer_size(&io, 65535); loop { arm(); - let (stream, addr) = listener.accept().expect("cannot accept client"); - info!("connection from {}", addr); + socket.listen(1382).expect("analyzer: cannot listen"); + socket.accept().expect("analyzer: cannot accept"); + info!("connection from {}", socket.remote_endpoint()); disarm(); - match worker(stream) { + match worker(&mut socket) { Ok(()) => (), Err(err) => error!("analyzer aborted: {}", err) } + socket.close().expect("analyzer: cannot close"); } } diff --git a/artiq/firmware/runtime/ethmac.rs b/artiq/firmware/runtime/ethmac.rs new file mode 100644 index 000000000..5f232d536 --- /dev/null +++ b/artiq/firmware/runtime/ethmac.rs @@ -0,0 +1,77 @@ +use core::slice; +use board::{csr, mem}; +use smoltcp::Error; +use smoltcp::phy::Device; + +const RX0_BASE: usize = mem::ETHMAC_BASE + 0x0000; +const RX1_BASE: usize = mem::ETHMAC_BASE + 0x0800; +const TX0_BASE: usize = mem::ETHMAC_BASE + 0x1000; +const TX1_BASE: usize = mem::ETHMAC_BASE + 0x1800; + +const RX_BUFFERS: [*mut u8; 2] = [RX0_BASE as *mut u8, RX1_BASE as *mut u8]; +const TX_BUFFERS: [*mut u8; 2] = [TX0_BASE as *mut u8, TX1_BASE as *mut u8]; + +pub struct EthernetDevice; + +impl Device for EthernetDevice { + type RxBuffer = RxBuffer; + type TxBuffer = TxBuffer; + + fn mtu(&self) -> usize { 1500 } + + fn receive(&mut self) -> Result { + unsafe { + if csr::ethmac::sram_writer_ev_pending_read() != 0 { + let slot = csr::ethmac::sram_writer_slot_read(); + let length = csr::ethmac::sram_writer_length_read(); + Ok(RxBuffer(slice::from_raw_parts(RX_BUFFERS[slot as usize], + length as usize))) + } else { + Err(Error::Exhausted) + } + } + } + + fn transmit(&mut self, length: usize) -> Result { + unsafe { + if csr::ethmac::sram_reader_ready_read() != 0 { + let slot = csr::ethmac::sram_reader_slot_read(); + let slot = (slot + 1) % (TX_BUFFERS.len() as u8); + csr::ethmac::sram_reader_slot_write(slot); + csr::ethmac::sram_reader_length_write(length as u16); + Ok(TxBuffer(slice::from_raw_parts_mut(TX_BUFFERS[slot as usize], + length as usize))) + } else { + Err(Error::Exhausted) + } + } + } +} + +pub struct RxBuffer(&'static [u8]); + +impl AsRef<[u8]> for RxBuffer { + fn as_ref(&self) -> &[u8] { self.0 } +} + +impl Drop for RxBuffer { + fn drop(&mut self) { + unsafe { csr::ethmac::sram_writer_ev_pending_write(1) } + } +} + +pub struct TxBuffer(&'static mut [u8]); + +impl AsRef<[u8]> for TxBuffer { + fn as_ref(&self) -> &[u8] { self.0 } +} + +impl AsMut<[u8]> for TxBuffer { + fn as_mut(&mut self) -> &mut [u8] { self.0 } +} + +impl Drop for TxBuffer { + fn drop(&mut self) { + unsafe { csr::ethmac::sram_reader_start_write(1) } + } +} diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index 717ecd601..9a99fb599 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -10,11 +10,11 @@ extern crate log; extern crate log_buffer; extern crate byteorder; extern crate fringe; -extern crate lwip; +extern crate smoltcp; extern crate board; use core::fmt::Write; -use logger::BufferLogger; +use std::boxed::Box; extern { fn putchar(c: libc::c_int) -> libc::c_int; @@ -57,7 +57,17 @@ pub extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, li } } +macro_rules! borrow_mut { + ($x:expr) => ({ + match $x.try_borrow_mut() { + Ok(x) => x, + Err(_) => panic!("cannot borrow mutably at {}:{}", file!(), line!()) + } + }) +} + mod config; +mod ethmac; mod rtio_mgt; mod mailbox; mod rpc_queue; @@ -83,12 +93,102 @@ mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; -extern { - fn network_init(); - fn lwip_service(); +include!(concat!(env!("OUT_DIR"), "/git_info.rs")); + +fn startup() { + board::uart::set_speed(921600); + board::clock::init(); + info!("booting ARTIQ"); + info!("software version {}", GIT_COMMIT); + info!("gateware version {}", board::ident(&mut [0; 64])); + + let t = board::clock::get_ms(); + info!("press 'e' to erase startup and idle kernels..."); + while board::clock::get_ms() < t + 1000 { + if unsafe { readchar_nonblock() != 0 && readchar() == b'e' as libc::c_char } { + config::remove("startup_kernel"); + config::remove("idle_kernel"); + info!("startup and idle kernels erased"); + break + } + } + info!("continuing boot"); + + #[cfg(has_i2c)] + board::i2c::init(); + #[cfg(has_ad9516)] + board::ad9516::init().expect("cannot initialize ad9516"); + #[cfg(has_converter_spi)] + board::ad9154::init().expect("cannot initialize ad9154"); + + fn _net_trace_writer(printer: smoltcp::wire::PrettyPrinter) + where U: smoltcp::wire::pretty_print::PrettyPrint { + print!("\x1b[37m{}\x1b[0m", printer) + } + + let net_device = ethmac::EthernetDevice; + // let net_device = smoltcp::phy::Tracer::<_, smoltcp::wire::EthernetFrame<&[u8]>> + // ::new(net_device, _net_trace_writer); + let arp_cache = smoltcp::iface::SliceArpCache::new([Default::default(); 8]); + let hardware_addr = smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); + let protocol_addrs = [smoltcp::wire::IpAddress::v4(192, 168, 1, 50)]; + let mut interface = smoltcp::iface::EthernetInterface::new( + Box::new(net_device), Box::new(arp_cache) as Box, + hardware_addr, protocol_addrs); + + let mut scheduler = sched::Scheduler::new(); + let io = scheduler.io(); + rtio_mgt::startup(&io); + io.spawn(16384, session::thread); + #[cfg(has_rtio_moninj)] + io.spawn(4096, moninj::thread); + #[cfg(has_rtio_analyzer)] + io.spawn(4096, analyzer::thread); + + loop { + scheduler.run(); + + match interface.poll(&mut *borrow_mut!(scheduler.sockets()), + board::clock::get_ms()) { + Ok(()) => (), + Err(smoltcp::Error::Exhausted) => (), + Err(smoltcp::Error::Unrecognized) => (), + Err(e) => warn!("network error: {}", e) + } + } } -include!(concat!(env!("OUT_DIR"), "/git_info.rs")); +use board::{irq, csr}; +extern { + fn uart_init(); + fn uart_isr(); + + fn alloc_give(ptr: *mut u8, length: usize); + static mut _fheap: u8; + static mut _eheap: u8; +} + +#[no_mangle] +pub unsafe extern fn main() -> i32 { + irq::set_mask(0); + irq::set_ie(true); + uart_init(); + + alloc_give(&mut _fheap as *mut u8, + &_eheap as *const u8 as usize - &_fheap as *const u8 as usize); + + static mut LOG_BUFFER: [u8; 65536] = [0; 65536]; + logger::BufferLogger::new(&mut LOG_BUFFER[..]).register(startup); + 0 +} + +#[no_mangle] +pub unsafe extern fn isr() { + let irqs = irq::pending() & irq::get_mask(); + if irqs & (1 << csr::UART_INTERRUPT) != 0 { + uart_isr() + } +} // Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort. // This is never called. @@ -97,70 +197,3 @@ include!(concat!(env!("OUT_DIR"), "/git_info.rs")); pub extern "C" fn _Unwind_Resume() -> ! { loop {} } - -#[no_mangle] -pub unsafe extern fn rust_main() { - static mut LOG_BUFFER: [u8; 65536] = [0; 65536]; - BufferLogger::new(&mut LOG_BUFFER[..]) - .register(move || { - board::uart::set_speed(921600); - board::clock::init(); - info!("ARTIQ runtime starting..."); - info!("software version {}", GIT_COMMIT); - info!("gateware version {}", board::ident(&mut [0; 64])); - - let t = board::clock::get_ms(); - info!("press 'e' to erase startup and idle kernels..."); - while board::clock::get_ms() < t + 1000 { - if readchar_nonblock() != 0 && readchar() == b'e' as libc::c_char { - config::remove("startup_kernel"); - config::remove("idle_kernel"); - info!("startup and idle kernels erased"); - break - } - } - info!("continuing boot"); - - #[cfg(has_i2c)] - board::i2c::init(); - #[cfg(has_ad9516)] - board::ad9516::init().unwrap(); - #[cfg(has_converter_spi)] - board::ad9154::init().unwrap(); - network_init(); - - let mut scheduler = sched::Scheduler::new(); - rtio_mgt::startup(scheduler.spawner()); - scheduler.spawner().spawn(16384, session::thread); - #[cfg(has_rtio_moninj)] - scheduler.spawner().spawn(4096, moninj::thread); - #[cfg(has_rtio_analyzer)] - scheduler.spawner().spawn(4096, analyzer::thread); - - loop { - scheduler.run(); - lwip_service(); - } - }) -} - -#[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 { - board::clock::get_ms() as u32 -} - -#[no_mangle] -pub fn sys_jiffies() -> u32 { - board::clock::get_ms() as u32 -} diff --git a/artiq/firmware/runtime/logger.rs b/artiq/firmware/runtime/logger.rs index 4177f8e24..6555a8811 100644 --- a/artiq/firmware/runtime/logger.rs +++ b/artiq/firmware/runtime/logger.rs @@ -44,11 +44,11 @@ impl BufferLogger { } pub fn clear(&self) { - self.buffer.borrow_mut().clear() + borrow_mut!(self.buffer).clear() } pub fn extract R>(&self, f: F) -> R { - f(self.buffer.borrow_mut().extract()) + f(borrow_mut!(self.buffer).extract()) } pub fn disable_trace_to_uart(&self) { @@ -67,14 +67,24 @@ impl Log for BufferLogger { fn log(&self, record: &LogRecord) { if self.enabled(record.metadata()) { - use core::fmt::Write; - writeln!(self.buffer.borrow_mut(), - "[{:12}us] {:>5}({}): {}", - board::clock::get_us(), record.level(), record.target(), record.args()).unwrap(); + let force_uart = match self.buffer.try_borrow_mut() { + Ok(mut buffer) => { + use core::fmt::Write; + writeln!(buffer, "[{:12}us] {:>5}({}): {}", + board::clock::get_us(), record.level(), + record.target(), record.args()).unwrap(); + false + } + Err(_) => { + // we're trying to log something while sending the log somewhere, + // probably over the network. just let it go to UART. + true + } + }; // Printing to UART is really slow, so avoid doing that when we have an alternative // route to retrieve the debug messages. - if self.trace_to_uart.get() || record.level() <= LogLevel::Info { + if self.trace_to_uart.get() || record.level() <= LogLevel::Info || force_uart { println!("[{:12}us] {:>5}({}): {}", board::clock::get_us(), record.level(), record.target(), record.args()); } diff --git a/artiq/firmware/runtime/moninj.rs b/artiq/firmware/runtime/moninj.rs index 16759cff9..9b52d414f 100644 --- a/artiq/firmware/runtime/moninj.rs +++ b/artiq/firmware/runtime/moninj.rs @@ -1,8 +1,6 @@ -use std::vec::Vec; use std::io; use board::csr; -use sched::{Waiter, Spawner}; -use sched::{UdpSocket, SocketAddr, IP_ANY}; +use sched::{Io, UdpSocket}; use moninj_proto::*; const MONINJ_TTL_OVERRIDE_ENABLE: u8 = 0; @@ -10,17 +8,17 @@ 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(); + let mut buf = vec![0; 512]; loop { - let addr = try!(socket.recv_from(&mut buf)); - let request = try!(Request::read_from(&mut io::Cursor::new(&buf))); + let (size, addr) = try!(socket.recv_from(&mut buf)); + let request = try!(Request::read_from(&mut io::Cursor::new(&buf[..size]))); trace!("{} -> {:?}", addr, request); match request { Request::Monitor => { #[cfg(has_dds)] let mut dds_ftws = [0u32; (csr::CONFIG_RTIO_DDS_COUNT as usize * - csr::CONFIG_DDS_CHANNELS_PER_BUS 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 { @@ -115,9 +113,9 @@ fn worker(socket: &mut UdpSocket) -> io::Result<()> { } } -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"); +pub fn thread(io: Io) { + let mut socket = UdpSocket::with_buffer_size(&io, 1, 512); + socket.bind(3250); loop { match worker(&mut socket) { diff --git a/artiq/firmware/runtime/rtio_mgt.rs b/artiq/firmware/runtime/rtio_mgt.rs index 8698fb5fe..5b5c0d8e2 100644 --- a/artiq/firmware/runtime/rtio_mgt.rs +++ b/artiq/firmware/runtime/rtio_mgt.rs @@ -1,6 +1,6 @@ use config; use board::csr; -use sched::Spawner; +use sched::Io; #[cfg(has_rtio_crg)] pub mod crg { @@ -38,12 +38,11 @@ pub mod crg { #[cfg(has_drtio)] mod drtio { - use board::csr; - use sched::{Waiter, Spawner}; + use super::*; - pub fn startup(spawner: &Spawner) { - spawner.spawn(4096, link_thread); - spawner.spawn(4096, error_thread); + pub fn startup(io: &Io) { + io.spawn(4096, link_thread); + io.spawn(4096, error_thread); } fn link_is_up() -> bool { @@ -92,12 +91,12 @@ mod drtio { } } - pub fn link_thread(waiter: Waiter, _spawner: Spawner) { + pub fn link_thread(io: Io) { loop { - waiter.until(link_is_up).unwrap(); + io.until(link_is_up).unwrap(); info!("link RX is up"); - waiter.sleep(600).unwrap(); + io.sleep(600).unwrap(); info!("wait for remote side done"); init(); // clear all FIFOs first @@ -105,7 +104,7 @@ mod drtio { sync_tsc(); info!("link initialization completed"); - waiter.until(|| !link_is_up()).unwrap(); + io.until(|| !link_is_up()).unwrap(); info!("link is down"); } } @@ -138,22 +137,21 @@ mod drtio { false } - pub fn error_thread(waiter: Waiter, _spawner: Spawner) { + pub fn error_thread(io: Io) { // HACK - waiter.until(poll_errors).unwrap(); + io.until(poll_errors).unwrap(); } - } #[cfg(not(has_drtio))] mod drtio { - use sched::Spawner; + use super::*; - pub fn startup(_spawner: &Spawner) {} + pub fn startup(_io: &Io) {} pub fn init() {} } -pub fn startup(spawner: &Spawner) { +pub fn startup(io: &Io) { crg::init(); let mut opt = [b'i']; @@ -179,7 +177,7 @@ pub fn startup(spawner: &Spawner) { warn!("fix clocking and reset the device"); } - drtio::startup(spawner); + drtio::startup(io); init_core() } diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index 920767664..22e67895a 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -1,20 +1,28 @@ #![allow(dead_code)] -use std::cell::RefCell; +use std::mem; +use std::cell::{RefCell, RefMut}; use std::vec::Vec; use std::io::{Read, Write, Result, Error, ErrorKind}; use fringe::OwnedStack; use fringe::generator::{Generator, Yielder, State as GeneratorState}; -use lwip; + +use smoltcp::wire::IpEndpoint; +use smoltcp::socket::AsSocket; +use smoltcp::socket::SocketHandle; +type SocketSet = ::smoltcp::socket::SocketSet<'static, 'static, 'static>; + use board; use urc::Urc; #[derive(Debug)] struct WaitRequest { - timeout: Option, - event: Option + event: Option<*const (Fn() -> bool + 'static)>, + timeout: Option } +unsafe impl Send for WaitRequest {} + #[derive(Debug)] enum WaitResult { Completed, @@ -24,22 +32,29 @@ enum WaitResult { #[derive(Debug)] struct Thread { - generator: Generator, + generator: Generator, waiting_for: WaitRequest, interrupted: bool } impl Thread { - unsafe fn new(spawner: Spawner, stack_size: usize, f: F) -> ThreadHandle - where F: 'static + FnOnce(Waiter, Spawner) + Send { + unsafe fn new(io: &Io, stack_size: usize, f: F) -> ThreadHandle + where F: 'static + FnOnce(Io) + Send { + let spawned = io.spawned.clone(); + let sockets = io.sockets.clone(); + let stack = OwnedStack::new(stack_size); ThreadHandle::new(Thread { generator: Generator::unsafe_new(stack, |yielder, _| { - f(Waiter(yielder), spawner) + f(Io { + yielder: Some(yielder), + spawned: spawned, + sockets: sockets + }) }), waiting_for: WaitRequest { - timeout: None, - event: None + event: None, + timeout: None }, interrupted: false }) @@ -58,7 +73,7 @@ impl Thread { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ThreadHandle(Urc>); impl ThreadHandle { @@ -81,52 +96,59 @@ impl ThreadHandle { } } -#[derive(Debug)] pub struct Scheduler { threads: Vec, - index: usize, - spawner: Spawner + spawned: Urc>>, + sockets: Urc>, + run_idx: usize, } impl Scheduler { pub fn new() -> Scheduler { Scheduler { threads: Vec::new(), - index: 0, - spawner: Spawner::new() + spawned: Urc::new(RefCell::new(Vec::new())), + sockets: Urc::new(RefCell::new(SocketSet::new(Vec::new()))), + run_idx: 0, } } - pub fn spawner(&self) -> &Spawner { - &self.spawner + pub fn io(&self) -> Io<'static> { + Io { + yielder: None, + spawned: self.spawned.clone(), + sockets: self.sockets.clone() + } } pub fn run(&mut self) { - self.threads.append(&mut *self.spawner.queue.borrow_mut()); + self.sockets.borrow_mut().prune(); + self.threads.append(&mut *borrow_mut!(self.spawned)); if self.threads.len() == 0 { return } let now = board::clock::get_ms(); - - let start_index = self.index; + let start_idx = self.run_idx; loop { - self.index = (self.index + 1) % self.threads.len(); + self.run_idx = (self.run_idx + 1) % self.threads.len(); let result = { - let thread = &mut *self.threads[self.index].0.borrow_mut(); + let mut thread = borrow_mut!(self.threads[self.run_idx].0); match thread.waiting_for { _ if thread.interrupted => { thread.interrupted = false; thread.generator.resume(WaitResult::Interrupted) } - WaitRequest { timeout: Some(instant), .. } if now >= instant => + WaitRequest { event: Some(_), timeout: Some(instant) } if now >= instant => thread.generator.resume(WaitResult::TimedOut), - WaitRequest { event: Some(ref event), .. } if event.completed() => + WaitRequest { event: None, timeout: Some(instant) } if now >= instant => thread.generator.resume(WaitResult::Completed), - WaitRequest { timeout: None, event: None } => + WaitRequest { event: Some(event), timeout: _ } if unsafe { (*event)() } => + thread.generator.resume(WaitResult::Completed), + WaitRequest { event: None, timeout: None } => thread.generator.resume(WaitResult::Completed), _ => { - if self.index == start_index { + if self.run_idx == start_idx { // We've checked every thread and none of them are runnable. break } else { @@ -139,12 +161,12 @@ impl Scheduler { match result { None => { // The thread has terminated. - self.threads.remove(self.index); - self.index = 0 + self.threads.remove(self.run_idx); + self.run_idx = 0 }, Some(wait_request) => { // The thread has suspended itself. - let thread = &mut *self.threads[self.index].0.borrow_mut(); + let mut thread = borrow_mut!(self.threads[self.run_idx].0); thread.waiting_for = wait_request } } @@ -152,75 +174,38 @@ impl Scheduler { break } } -} -#[derive(Debug, Clone)] -pub struct Spawner { - queue: Urc>> -} - -impl Spawner { - fn new() -> Spawner { - Spawner { queue: Urc::new(RefCell::new(Vec::new())) } + pub fn sockets(&self) -> &RefCell { + &*self.sockets } +} +#[derive(Clone)] +pub struct Io<'a> { + yielder: Option<&'a Yielder>, + spawned: Urc>>, + sockets: Urc>, +} + +impl<'a> Io<'a> { 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()); + where F: 'static + FnOnce(Io) + Send { + let handle = unsafe { Thread::new(self, stack_size, f) }; + borrow_mut!(self.spawned).push(handle.clone()); handle } -} -enum WaitEvent { - Completion(*const (Fn() -> bool + 'static)), - Termination(*const RefCell), - UdpReadable(*const RefCell), - TcpAcceptable(*const RefCell), - TcpWriteable(*const RefCell), - TcpReadable(*const RefCell), -} - -impl WaitEvent { - fn completed(&self) -> bool { - match *self { - WaitEvent::Completion(f) => - unsafe { (*f)() }, - WaitEvent::Termination(thread) => - unsafe { (*thread).borrow().terminated() }, - WaitEvent::UdpReadable(state) => - unsafe { (*state).borrow().readable() }, - WaitEvent::TcpAcceptable(state) => - unsafe { (*state).borrow().acceptable() }, - WaitEvent::TcpWriteable(state) => - unsafe { (*state).borrow().writeable() }, - WaitEvent::TcpReadable(state) => - unsafe { (*state).borrow().readable() }, - } + fn yielder(&self) -> &'a Yielder { + self.yielder.expect("cannot suspend the scheduler thread") } -} -// *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 sleep(&self, duration_ms: u64) -> Result<()> { let request = WaitRequest { timeout: Some(board::clock::get_ms() + duration_ms), event: None }; - match self.0.suspend(request) { + match self.yielder().suspend(request) { WaitResult::TimedOut => Ok(()), WaitResult::Interrupted => Err(Error::new(ErrorKind::Interrupted, "")), _ => unreachable!() @@ -228,7 +213,7 @@ impl<'a> Waiter<'a> { } fn suspend(&self, request: WaitRequest) -> Result<()> { - match self.0.suspend(request) { + match self.yielder().suspend(request) { WaitResult::Completed => Ok(()), WaitResult::TimedOut => Err(Error::new(ErrorKind::TimedOut, "")), WaitResult::Interrupted => Err(Error::new(ErrorKind::Interrupted, "")) @@ -242,230 +227,251 @@ 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, - event: Some(WaitEvent::Completion(&f as *const _)) + event: Some(&f as *const _) }) } - pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> { - self.suspend(WaitRequest { - timeout: None, - event: Some(WaitEvent::UdpReadable(socket.state())) - }) - } - - pub fn tcp_acceptable(&self, socket: &lwip::TcpListener) -> Result<()> { - self.suspend(WaitRequest { - timeout: None, - event: Some(WaitEvent::TcpAcceptable(socket.state())) - }) - } - - pub fn tcp_writeable(&self, socket: &lwip::TcpStream) -> Result<()> { - self.suspend(WaitRequest { - timeout: None, - event: Some(WaitEvent::TcpWriteable(socket.state())) - }) - } - - pub fn tcp_readable(&self, socket: &lwip::TcpStream) -> Result<()> { - self.suspend(WaitRequest { - timeout: None, - event: Some(WaitEvent::TcpReadable(socket.state())) - }) + pub fn join(&self, handle: ThreadHandle) -> Result<()> { + self.until(move || handle.terminated()) } } -// Wrappers around lwip +macro_rules! until { + ($socket:expr, $ty:ty, |$var:ident| $cond:expr) => ({ + let (sockets, handle) = ($socket.io.sockets.clone(), $socket.handle); + $socket.io.until(move || { + let mut sockets = borrow_mut!(sockets); + let $var = sockets.get_mut(handle).as_socket() as &mut $ty; + $cond + }) + }) +} -pub use lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr}; +type UdpPacketBuffer = ::smoltcp::socket::UdpPacketBuffer<'static>; +type UdpSocketBuffer = ::smoltcp::socket::UdpSocketBuffer<'static, 'static>; +type UdpSocketLower = ::smoltcp::socket::UdpSocket<'static, 'static>; -#[derive(Debug)] pub struct UdpSocket<'a> { - waiter: Waiter<'a>, - lower: lwip::UdpSocket + io: &'a Io<'a>, + handle: SocketHandle } impl<'a> UdpSocket<'a> { - pub fn new(waiter: Waiter<'a>) -> Result { - Ok(UdpSocket { - waiter: waiter, - lower: try!(lwip::UdpSocket::new()) - }) + pub fn new(io: &'a Io<'a>, rx_buffer: UdpSocketBuffer, tx_buffer: UdpSocketBuffer) -> + UdpSocket<'a> { + let handle = borrow_mut!(io.sockets) + .add(UdpSocketLower::new(rx_buffer, tx_buffer)); + UdpSocket { + io: io, + handle: handle + } } - pub fn into_lower(self) -> lwip::UdpSocket { - self.lower + pub fn with_buffer_size(io: &'a Io<'a>, buffer_depth: usize, buffer_width: usize) -> + UdpSocket<'a> { + let mut rx_buffer = vec![]; + let mut tx_buffer = vec![]; + for _ in 0..buffer_depth { + rx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width])); + tx_buffer.push(UdpPacketBuffer::new(vec![0; buffer_width])); + } + Self::new(io, + UdpSocketBuffer::new(rx_buffer), + UdpSocketBuffer::new(tx_buffer)) } - pub fn from_lower(waiter: Waiter<'a>, inner: lwip::UdpSocket) -> UdpSocket { - UdpSocket { waiter: waiter, lower: inner } + fn as_lower<'b>(&'b self) -> RefMut<'b, UdpSocketLower> { + RefMut::map(borrow_mut!(self.io.sockets), + |sockets| sockets.get_mut(self.handle).as_socket()) } - pub fn bind(&self, addr: SocketAddr) -> Result<()> { - Ok(try!(self.lower.bind(addr))) + pub fn bind>(&self, endpoint: T) { + self.as_lower().bind(endpoint) } - pub fn connect(&self, addr: SocketAddr) -> Result<()> { - Ok(try!(self.lower.connect(addr))) - } - - pub fn disconnect(&self) -> Result<()> { - Ok(try!(self.lower.disconnect())) - } - - pub fn send_to(&self, buf: &[u8], addr: SocketAddr) -> Result { - try!(self.lower.send_to(lwip::Pbuf::from_slice(buf), addr)); - Ok(buf.len()) - } - - pub fn recv_from(&self, buf: &mut Vec) -> Result { - try!(self.waiter.udp_readable(&self.lower)); - let (pbuf, addr) = self.lower.try_recv().unwrap(); - buf.clear(); - buf.extend_from_slice(&pbuf.as_slice()); - Ok(addr) - } - - pub fn send(&self, buf: &[u8]) -> Result { - try!(self.lower.send(lwip::Pbuf::from_slice(buf))); - Ok(buf.len()) - } - - pub fn recv(&self, buf: &mut [u8]) -> Result { - try!(self.waiter.udp_readable(&self.lower)); - let (pbuf, _addr) = self.lower.try_recv().unwrap(); - // lwip checks that addr matches the bind/connect call - let len = ::std::cmp::min(buf.len(), pbuf.len()); - (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); - Ok(len) - } - - pub fn readable(&self) -> bool { - self.lower.state().borrow().readable() - } -} - -#[derive(Debug)] -pub struct TcpListener<'a> { - waiter: Waiter<'a>, - lower: lwip::TcpListener -} - -impl<'a> TcpListener<'a> { - pub fn bind(waiter: Waiter<'a>, addr: SocketAddr) -> Result { - Ok(TcpListener { - waiter: waiter, - lower: try!(lwip::TcpListener::bind(addr)) - }) - } - - 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(); - let addr = SocketAddr::new(IP_ANY, 0); // FIXME: coax lwip into giving real addr here - Ok((TcpStream { - waiter: self.waiter, - lower: stream_lower, - buffer: None - }, addr)) - } - - 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; - -pub struct TcpStreamInner(lwip::TcpStream, Option<(lwip::Pbuf<'static>, usize)>); - -#[derive(Debug)] -pub struct TcpStream<'a> { - waiter: Waiter<'a>, - lower: lwip::TcpStream, - buffer: Option<(lwip::Pbuf<'static>, usize)> -} - -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))) - } - - 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> { - fn read(&mut self, buf: &mut [u8]) -> Result { - if self.buffer.is_none() { - try!(self.waiter.tcp_readable(&self.lower)); - 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)) + pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint)> { + try!(until!(self, UdpSocketLower, |s| s.can_recv())); + match self.as_lower().recv_slice(buf) { + Ok(r) => Ok(r), + Err(()) => { + // No data in the buffer--should never happen after the wait above. + unreachable!() } } + } - let (pbuf, pos) = self.buffer.take().unwrap(); - let remaining = pbuf.len() - pos; - let len = ::std::cmp::min(buf.len(), remaining); - buf[..len].copy_from_slice(&pbuf.as_slice()[pos..pos + len]); - if len < remaining { - self.buffer = Some((pbuf, pos + len)) + pub fn send_to(&self, buf: &[u8], addr: IpEndpoint) -> Result { + try!(until!(self, UdpSocketLower, |s| s.can_send())); + match self.as_lower().send_slice(buf, addr) { + Ok(r) => Ok(r), + Err(()) => { + // No space in the buffer--should never happen after the wait above. + unreachable!() + } } - Ok(len) } } -impl<'a> Write for TcpStream<'a> { +impl<'a> Drop for UdpSocket<'a> { + fn drop(&mut self) { + borrow_mut!(self.io.sockets).release(self.handle) + } +} + +type TcpSocketBuffer = ::smoltcp::socket::TcpSocketBuffer<'static>; +type TcpSocketLower = ::smoltcp::socket::TcpSocket<'static>; + +pub struct TcpSocketHandle(SocketHandle); + +pub struct TcpSocket<'a> { + io: &'a Io<'a>, + handle: SocketHandle +} + +impl<'a> TcpSocket<'a> { + pub fn new(io: &'a Io<'a>, rx_buffer: TcpSocketBuffer, tx_buffer: TcpSocketBuffer) -> + TcpSocket<'a> { + let handle = borrow_mut!(io.sockets) + .add(TcpSocketLower::new(rx_buffer, tx_buffer)); + TcpSocket { + io: io, + handle: handle + } + } + + pub fn with_buffer_size(io: &'a Io<'a>, buffer_size: usize) -> TcpSocket<'a> { + let rx_buffer = vec![0; buffer_size]; + let tx_buffer = vec![0; buffer_size]; + Self::new(io, + TcpSocketBuffer::new(rx_buffer), + TcpSocketBuffer::new(tx_buffer)) + } + + pub fn into_handle(self) -> TcpSocketHandle { + let handle = self.handle; + mem::forget(self); + TcpSocketHandle(handle) + } + + pub fn from_handle(io: &'a Io<'a>, handle: TcpSocketHandle) -> TcpSocket<'a> { + TcpSocket { + io: io, + handle: handle.0 + } + } + + fn as_lower<'b>(&'b self) -> RefMut<'b, TcpSocketLower> { + RefMut::map(borrow_mut!(self.io.sockets), + |sockets| sockets.get_mut(self.handle).as_socket()) + } + + pub fn is_open(&self) -> bool { + self.as_lower().is_open() + } + + pub fn is_listening(&self) -> bool { + self.as_lower().is_listening() + } + + pub fn is_active(&self) -> bool { + self.as_lower().is_active() + } + + pub fn may_send(&self) -> bool { + self.as_lower().may_send() + } + + pub fn may_recv(&self) -> bool { + self.as_lower().may_recv() + } + + pub fn can_send(&self) -> bool { + self.as_lower().can_send() + } + + pub fn can_recv(&self) -> bool { + self.as_lower().can_recv() + } + + pub fn local_endpoint(&self) -> IpEndpoint { + self.as_lower().local_endpoint() + } + + pub fn remote_endpoint(&self) -> IpEndpoint { + self.as_lower().remote_endpoint() + } + + pub fn listen>(&self, endpoint: T) -> Result<()> { + self.as_lower().listen(endpoint) + .map_err(|()| Error::new(ErrorKind::Other, + "cannot listen: already connected")) + } + + pub fn accept(&self) -> Result<()> { + // We're waiting until at least one half of the connection becomes open. + // This handles the case where a remote socket immediately sends a FIN-- + // that still counts as accepting even though nothing may be sent. + until!(self, TcpSocketLower, |s| s.may_send() || s.may_recv()) + } + + pub fn close(&self) -> Result<()> { + self.as_lower().close(); + try!(until!(self, TcpSocketLower, |s| !s.is_open())); + // right now the socket may be in TIME-WAIT state. if we don't give it a chance to send + // a packet, and the user code executes a loop { s.listen(); s.read(); s.close(); } + // then the last ACK will never be sent. + self.io.relinquish() + } +} + +impl<'a> Read for TcpSocket<'a> { + fn read(&mut self, buf: &mut [u8]) -> Result { + // fast path + let result = self.as_lower().recv_slice(buf); + match result { + Ok(0) | Err(()) => { + // slow path + if !self.as_lower().may_recv() { return Ok(0) } + try!(until!(self, TcpSocketLower, |s| s.can_recv())); + Ok(self.as_lower().recv_slice(buf) + .expect("may_recv implies that data was available")) + } + Ok(length) => Ok(length) + } + } +} + +impl<'a> Write for TcpSocket<'a> { fn write(&mut self, buf: &[u8]) -> Result { - try!(self.waiter.tcp_writeable(&self.lower)); - Ok(try!(self.lower.write_in_place(buf, - || self.waiter.relinquish() - .map_err(|_| lwip::Error::Interrupted)))) + // fast path + let result = self.as_lower().send_slice(buf); + match result { + Ok(0) | Err(()) => { + // slow path + if !self.as_lower().may_send() { return Ok(0) } + try!(until!(self, TcpSocketLower, |s| s.can_send())); + Ok(self.as_lower().send_slice(buf) + .expect("may_send implies that data was available")) + } + Ok(length) => Ok(length) + } } fn flush(&mut self) -> Result<()> { - Ok(try!(self.lower.flush())) + // smoltcp always sends all available data when it's possible; nothing to do + Ok(()) + } +} + +impl<'a> Drop for TcpSocket<'a> { + fn drop(&mut self) { + if self.is_open() { + // scheduler will remove any closed sockets with zero references. + self.as_lower().close() + } + borrow_mut!(self.io.sockets).release(self.handle) } } diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index 30cead501..b073bbb4f 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -1,14 +1,14 @@ use std::prelude::v1::*; use std::{mem, str}; use std::cell::RefCell; -use std::io::{self, Read, Write, BufWriter}; +use std::io::{self, Read, Write}; use std::btree_set::BTreeSet; use {config, rtio_mgt, 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 sched::{ThreadHandle, Io}; +use sched::{TcpSocket}; use byteorder::{ByteOrder, NetworkEndian}; use board; @@ -97,7 +97,7 @@ impl<'a> Drop for Session<'a> { } } -fn check_magic(stream: &mut TcpStream) -> io::Result<()> { +fn check_magic(stream: &mut TcpSocket) -> io::Result<()> { const MAGIC: &'static [u8] = b"ARTIQ coredev\n"; let mut magic: [u8; 14] = [0; 14]; @@ -109,7 +109,7 @@ fn check_magic(stream: &mut TcpStream) -> io::Result<()> { } } -fn host_read(stream: &mut TcpStream) -> io::Result { +fn host_read(stream: &mut TcpSocket) -> io::Result { let request = try!(host::Request::read_from(stream)); match &request { &host::Request::LoadKernel(_) => trace!("comm<-host LoadLibrary(...)"), @@ -123,18 +123,18 @@ fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> { reply.write_to(stream) } -fn kern_send(waiter: Waiter, request: &kern::Message) -> io::Result<()> { +fn kern_send(io: &Io, request: &kern::Message) -> io::Result<()> { match request { &kern::LoadRequest(_) => trace!("comm->kern LoadRequest(...)"), _ => trace!("comm->kern {:?}", request) } unsafe { mailbox::send(request as *const _ as usize) } - waiter.until(mailbox::acknowledged) + io.until(mailbox::acknowledged) } -fn kern_recv_notrace(waiter: Waiter, f: F) -> io::Result +fn kern_recv_notrace(io: &Io, f: F) -> io::Result where F: FnOnce(&kern::Message) -> io::Result { - try!(waiter.until(|| mailbox::receive() != 0)); + try!(io.until(|| mailbox::receive() != 0)); if !kernel::validate(mailbox::receive()) { let message = format!("invalid kernel CPU pointer 0x{:x}", mailbox::receive()); return Err(io::Error::new(io::ErrorKind::InvalidData, message)) @@ -152,9 +152,9 @@ fn kern_recv_dotrace(reply: &kern::Message) { } #[inline(always)] -fn kern_recv(waiter: Waiter, f: F) -> io::Result +fn kern_recv(io: &Io, f: F) -> io::Result where F: FnOnce(&kern::Message) -> io::Result { - kern_recv_notrace(waiter, |reply| { + kern_recv_notrace(io, |reply| { kern_recv_dotrace(reply); f(reply) }) @@ -165,15 +165,15 @@ fn kern_acknowledge() -> io::Result<()> { Ok(()) } -unsafe fn kern_load(waiter: Waiter, session: &mut Session, library: &[u8]) -> io::Result<()> { +unsafe fn kern_load(io: &Io, 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| { + try!(kern_send(io, &kern::LoadRequest(&library))); + kern_recv(io, |reply| { match reply { &kern::LoadReply(Ok(())) => { session.kernel_state = KernelState::Loaded; @@ -197,8 +197,8 @@ fn kern_run(session: &mut Session) -> io::Result<()> { kern_acknowledge() } -fn process_host_message(waiter: Waiter, - stream: &mut TcpStream, +fn process_host_message(io: &Io, + stream: &mut TcpSocket, session: &mut Session) -> io::Result<()> { match try!(host_read(stream)) { host::Request::Ident => @@ -257,7 +257,7 @@ fn process_host_message(waiter: Waiter, } host::Request::LoadKernel(kernel) => - match unsafe { kern_load(waiter, session, &kernel) } { + match unsafe { kern_load(io, session, &kernel) } { Ok(()) => host_write(stream, host::Reply::LoadCompleted), Err(_) => { try!(kern_acknowledge()); @@ -276,22 +276,22 @@ fn process_host_message(waiter: Waiter, unexpected!("unsolicited RPC reply") } - let slot = try!(kern_recv(waiter, |reply| { + let slot = try!(kern_recv(io, |reply| { match reply { &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(Ok(size)))); - kern_recv(waiter, |reply| { + try!(kern_send(io, &kern::RpcRecvReply(Ok(size)))); + kern_recv(io, |reply| { match reply { &kern::RpcRecvRequest(slot) => Ok(slot), other => unexpected!("unexpected reply from kernel CPU: {:?}", other) } }) })); - try!(kern_send(waiter, &kern::RpcRecvReply(Ok(0)))); + try!(kern_send(io, &kern::RpcRecvReply(Ok(0)))); session.kernel_state = KernelState::Running; Ok(()) @@ -304,7 +304,7 @@ fn process_host_message(waiter: Waiter, unexpected!("unsolicited RPC reply") } - try!(kern_recv(waiter, |reply| { + try!(kern_recv(io, |reply| { match reply { &kern::RpcRecvRequest(_) => Ok(()), other => @@ -329,7 +329,7 @@ fn process_host_message(waiter: Waiter, function: into_c_str(&mut session.interner, function), phantom: ::core::marker::PhantomData }; - try!(kern_send(waiter, &kern::RpcRecvReply(Err(exn)))); + try!(kern_send(io, &kern::RpcRecvReply(Err(exn)))); session.kernel_state = KernelState::Running; Ok(()) @@ -337,10 +337,10 @@ fn process_host_message(waiter: Waiter, } } -fn process_kern_message(waiter: Waiter, - mut stream: Option<&mut TcpStream>, +fn process_kern_message(io: &Io, + mut stream: Option<&mut TcpSocket>, session: &mut Session) -> io::Result { - kern_recv_notrace(waiter, |request| { + kern_recv_notrace(io, |request| { match (request, session.kernel_state) { (&kern::LoadReply(_), KernelState::Loaded) | (&kern::RpcRecvRequest(_), KernelState::RpcWait) => { @@ -371,7 +371,7 @@ fn process_kern_message(waiter: Waiter, } &kern::NowInitRequest => - kern_send(waiter, &kern::NowInitReply(session.congress.now)), + kern_send(io, &kern::NowInitReply(session.congress.now)), &kern::NowSave(now) => { session.congress.now = now; @@ -386,7 +386,7 @@ fn process_kern_message(waiter: Waiter, &kern::DRTIOChannelStateRequest { channel } => { let (fifo_space, last_timestamp) = rtio_mgt::drtio_dbg::get_channel_state(channel); - kern_send(waiter, &kern::DRTIOChannelStateReply { fifo_space: fifo_space, + kern_send(io, &kern::DRTIOChannelStateReply { fifo_space: fifo_space, last_timestamp: last_timestamp }) } &kern::DRTIOResetChannelStateRequest { channel } => { @@ -399,17 +399,17 @@ fn process_kern_message(waiter: Waiter, } &kern::DRTIOPacketCountRequest => { let (tx_cnt, rx_cnt) = rtio_mgt::drtio_dbg::get_packet_counts(); - kern_send(waiter, &kern::DRTIOPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt }) + kern_send(io, &kern::DRTIOPacketCountReply { tx_cnt: tx_cnt, rx_cnt: rx_cnt }) } &kern::DRTIOFIFOSpaceReqCountRequest => { let cnt = rtio_mgt::drtio_dbg::get_fifo_space_req_count(); - kern_send(waiter, &kern::DRTIOFIFOSpaceReqCountReply { cnt: cnt }) + kern_send(io, &kern::DRTIOFIFOSpaceReqCountReply { cnt: cnt }) } &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(io, &kern::WatchdogSetReply { id: id }) } &kern::WatchdogClear { id } => { @@ -421,9 +421,8 @@ fn process_kern_message(waiter: Waiter, match stream { None => unexpected!("unexpected RPC in flash kernel"), Some(ref mut stream) => { - let writer = &mut BufWriter::new(stream); - try!(host_write(writer, host::Reply::RpcRequest { async: async })); - try!(rpc::send_args(writer, service, tag, data)); + try!(host_write(stream, host::Reply::RpcRequest { async: async })); + try!(rpc::send_args(stream, service, tag, data)); if !async { session.kernel_state = KernelState::RpcWait } @@ -434,14 +433,14 @@ fn process_kern_message(waiter: Waiter, &kern::CacheGetRequest { key } => { let value = session.congress.cache.get(key); - kern_send(waiter, &kern::CacheGetReply { + kern_send(io, &kern::CacheGetReply { value: unsafe { mem::transmute::<*const [i32], &'static [i32]>(value) } }) } &kern::CachePutRequest { key, value } => { let succeeded = session.congress.cache.put(key, value).is_ok(); - kern_send(waiter, &kern::CachePutReply { succeeded: succeeded }) + kern_send(io, &kern::CachePutReply { succeeded: succeeded }) } #[cfg(has_i2c)] @@ -457,12 +456,12 @@ fn process_kern_message(waiter: Waiter, #[cfg(has_i2c)] &kern::I2CWriteRequest { busno, data } => { let ack = board::i2c::write(busno, data); - kern_send(waiter, &kern::I2CWriteReply { ack: ack }) + kern_send(io, &kern::I2CWriteReply { ack: ack }) } #[cfg(has_i2c)] &kern::I2CReadRequest { busno, ack } => { let data = board::i2c::read(busno, ack); - kern_send(waiter, &kern::I2CReadReply { data: data }) + kern_send(io, &kern::I2CReadReply { data: data }) } #[cfg(not(has_i2c))] @@ -475,11 +474,11 @@ fn process_kern_message(waiter: Waiter, } #[cfg(not(has_i2c))] &kern::I2CWriteRequest { .. } => { - kern_send(waiter, &kern::I2CWriteReply { ack: false }) + kern_send(io, &kern::I2CWriteReply { ack: false }) } #[cfg(not(has_i2c))] &kern::I2CReadRequest { .. } => { - kern_send(waiter, &kern::I2CReadReply { data: 0xff }) + kern_send(io, &kern::I2CReadReply { data: 0xff }) } &kern::RunFinished => { @@ -536,7 +535,7 @@ fn process_kern_message(waiter: Waiter, }) } -fn process_kern_queued_rpc(stream: &mut TcpStream, +fn process_kern_queued_rpc(stream: &mut TcpSocket, _session: &mut Session) -> io::Result<()> { rpc_queue::dequeue(|slice| { trace!("comm<-kern (async RPC)"); @@ -548,8 +547,8 @@ fn process_kern_queued_rpc(stream: &mut TcpStream, }) } -fn host_kernel_worker(waiter: Waiter, - stream: &mut TcpStream, +fn host_kernel_worker(io: &Io, + stream: &mut TcpSocket, congress: &mut Congress) -> io::Result<()> { let mut session = Session::new(congress); @@ -558,12 +557,14 @@ fn host_kernel_worker(waiter: Waiter, try!(process_kern_queued_rpc(stream, &mut session)) } - if stream.readable() { - try!(process_host_message(waiter, stream, &mut session)); + if stream.can_recv() { + try!(process_host_message(io, stream, &mut session)) + } else if !stream.may_recv() { + return Ok(()) } if mailbox::receive() != 0 { - try!(process_kern_message(waiter, Some(stream), &mut session)); + try!(process_kern_message(io, Some(stream), &mut session)); } if session.kernel_state == KernelState::Running { @@ -578,11 +579,11 @@ fn host_kernel_worker(waiter: Waiter, } } - try!(waiter.relinquish()) + try!(io.relinquish()) } } -fn flash_kernel_worker(waiter: Waiter, +fn flash_kernel_worker(io: &Io, congress: &mut Congress, config_key: &str) -> io::Result<()> { let mut session = Session::new(congress); @@ -592,7 +593,7 @@ fn flash_kernel_worker(waiter: Waiter, return Err(io::Error::new(io::ErrorKind::NotFound, "kernel not found")) } - try!(unsafe { kern_load(waiter, &mut session, &kernel) }); + try!(unsafe { kern_load(io, &mut session, &kernel) }); try!(kern_run(&mut session)); loop { @@ -601,7 +602,7 @@ fn flash_kernel_worker(waiter: Waiter, } if mailbox::receive() != 0 { - if try!(process_kern_message(waiter, None, &mut session)) { + if try!(process_kern_message(io, None, &mut session)) { return Ok(()) } } @@ -614,32 +615,31 @@ fn flash_kernel_worker(waiter: Waiter, return Err(io_error("RTIO clock failure")) } - try!(waiter.relinquish()) + try!(io.relinquish()) } } -fn respawn(spawner: Spawner, waiter: Waiter, - handle: &mut Option, - f: F) where F: 'static + FnOnce(Waiter, Spawner) + Send { +fn respawn(io: &Io, handle: &mut Option, f: F) + where F: 'static + FnOnce(Io) + Send { match handle.take() { None => (), Some(handle) => { if !handle.terminated() { info!("terminating running kernel"); handle.interrupt(); - waiter.join(handle).expect("cannot join interrupt thread") + io.join(handle).expect("cannot join interrupt thread") } } } - *handle = Some(spawner.spawn(16384, f)) + *handle = Some(io.spawn(16384, f)) } -pub fn thread(waiter: Waiter, spawner: Spawner) { +pub fn thread(io: Io) { let congress = Urc::new(RefCell::new(Congress::new())); info!("running startup kernel"); - match flash_kernel_worker(waiter, &mut congress.borrow_mut(), "startup_kernel") { + match flash_kernel_worker(&io, &mut *borrow_mut!(congress), "startup_kernel") { Ok(()) => info!("startup kernel finished"), Err(err) => { if err.kind() == io::ErrorKind::NotFound { @@ -652,27 +652,36 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { BufferLogger::with_instance(|logger| logger.disable_trace_to_uart()); - let addr = SocketAddr::new(IP_ANY, 1381); - let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket"); - listener.set_keepalive(true); + const BUFFER_SIZE: usize = 65535; + let mut listener = TcpSocket::with_buffer_size(&io, BUFFER_SIZE); info!("accepting network sessions"); 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); + if !listener.is_open() { + listener.listen(1381).expect("session: cannot listen") + } + + if listener.is_active() { + listener.accept().expect("session: cannot accept"); + match check_magic(&mut listener) { + Ok(()) => (), + Err(_) => { + warn!("wrong magic from {}", listener.remote_endpoint()); + listener.close().expect("session: cannot close"); + continue + } + } + info!("new connection from {}", listener.remote_endpoint()); + + let socket = listener.into_handle(); + listener = TcpSocket::with_buffer_size(&io, BUFFER_SIZE); - 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) { + respawn(&io, &mut kernel_thread, move |io| { + let mut congress = borrow_mut!(congress); + let mut socket = TcpSocket::from_handle(&io, socket); + match host_kernel_worker(&io, &mut socket, &mut *congress) { Ok(()) => (), Err(err) => { if err.kind() == io::ErrorKind::UnexpectedEof { @@ -682,16 +691,16 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { } } } - }) + }); } 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, "idle_kernel") { + respawn(&io, &mut kernel_thread, move |io| { + let mut congress = borrow_mut!(congress); + match flash_kernel_worker(&io, &mut *congress, "idle_kernel") { Ok(()) => info!("idle kernel finished, standing by"), Err(err) => { @@ -699,7 +708,7 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { info!("idle kernel interrupted"); } else if err.kind() == io::ErrorKind::NotFound { info!("no idle kernel found"); - while waiter.relinquish().is_ok() {} + while io.relinquish().is_ok() {} } else { error!("idle kernel aborted: {}", err); } @@ -708,6 +717,6 @@ pub fn thread(waiter: Waiter, spawner: Spawner) { }) } - let _ = waiter.relinquish(); + let _ = io.relinquish(); } } diff --git a/artiq/gateware/amp/soc.py b/artiq/gateware/amp/soc.py index 4bd9e0f9b..859c7acc0 100644 --- a/artiq/gateware/amp/soc.py +++ b/artiq/gateware/amp/soc.py @@ -46,7 +46,5 @@ class AMPSoC: def build_artiq_soc(soc, argdict): builder = Builder(soc, **argdict) builder.add_extra_software_packages() - builder.add_software_package("liblwip", os.path.join(artiq_dir, "runtime", - "liblwip")) builder.add_software_package("runtime", os.path.join(artiq_dir, "runtime")) builder.build() diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 1b6b09833..6204706ea 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,13 +7,9 @@ RUSTOUT := cargo/or1k-unknown-none/debug RUSTOUT_KSUPPORT := cargo-ksupport/or1k-unknown-none/debug CFLAGS += \ - -I$(LIBALLOC_DIRECTORY) \ - -I$(MISOC_DIRECTORY)/software/include/dyld \ - -I$(LIBDYLD_DIRECTORY)/include \ -I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ - -I$(LIBLWIP_DIRECTORY)/../lwip/src/include \ - -I$(LIBLWIP_DIRECTORY) + -I$(MISOC_DIRECTORY)/software/include/dyld CFLAGS += -DNDEBUG LDFLAGS += --gc-sections \ @@ -22,8 +18,7 @@ LDFLAGS += --gc-sections \ -L../libm \ -L../liballoc \ -L../libunwind \ - -L../libdyld \ - -L../liblwip + -L../libdyld all: runtime.bin runtime.fbi @@ -34,12 +29,12 @@ $(RUSTOUT)/libruntime.a: cargo build --target=or1k-unknown-none \ --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../firmware/runtime/Cargo.toml) -runtime.elf: $(RUSTOUT)/libruntime.a flash_storage.o main.o ksupport_data.o +runtime.elf: $(RUSTOUT)/libruntime.a flash_storage.o ksupport_data.o $(LD) $(LDFLAGS) \ -T $(RUNTIME_DIRECTORY)/runtime.ld \ -o $@ \ $^ \ - -lbase-nofloat -lcompiler-rt -lalloc -llwip + -lbase-nofloat -lcompiler-rt -lalloc @chmod -x $@ .PHONY: $(RUSTOUT_KSUPPORT)/libksupport.a diff --git a/artiq/runtime/liblwip/Makefile b/artiq/runtime/liblwip/Makefile deleted file mode 100644 index 88de6a717..000000000 --- a/artiq/runtime/liblwip/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -include ../include/generated/variables.mak -include $(MISOC_DIRECTORY)/software/common.mak - -LWIPDIR=$(LIBLWIP_DIRECTORY)/../lwip/src - -CFLAGS += $(CPPFLAGS) -I. \ - -I$(LWIPDIR)/include \ - -I$(LWIPDIR)/include/ipv4 - -# COREFILES, CORE4FILES: The minimum set of files needed for lwIP. -COREFILES=core/mem.c \ - core/memp.c \ - core/netif.c \ - core/pbuf.c \ - core/raw.c \ - core/stats.c \ - core/sys.c \ - core/ip.c \ - core/tcp.c \ - core/tcp_in.c \ - core/tcp_out.c \ - core/udp.c \ - core/inet_chksum.c \ - core/timeouts.c \ - core/init.c - -CORE4FILES=core/ipv4/etharp.c \ - core/ipv4/icmp.c \ - core/ipv4/ip4.c \ - core/ipv4/ip4_addr.c \ - core/ipv4/ip4_frag.c - -# NETIFFILES: Files implementing various generic network interface functions. -NETIFFILES=netif/ethernet.c - -PPPFILES=netif/ppp/auth.c \ - netif/ppp/fsm.c \ - netif/ppp/ipcp.c \ - netif/ppp/lcp.c \ - netif/ppp/magic.c \ - netif/ppp/ppp.c \ - netif/ppp/pppos.c \ - netif/ppp/utils.c \ - netif/ppp/vj.c - -# LWIPFILES: All the above. -LWIPFILES=$(COREFILES) $(CORE4FILES) $(NETIFFILES) $(PPPFILES) - -LWIPOBJS:=$(LWIPFILES:.c=.o) liteethif.o - -all: prepare liblwip.a - -prepare: - rm -f lwipopts.h - rm -f arch - ln -s $(LIBLWIP_DIRECTORY)/lwipopts.h lwipopts.h - ln -s $(LIBLWIP_DIRECTORY)/arch arch - mkdir -p core/ipv4 - mkdir -p netif - mkdir -p netif/ppp - -core/%.o: $(LWIPDIR)/core/%.c - $(compile) - -core/ipv4/%.o: $(LWIPDIR)/core/ipv4/%.c - $(compile) - -netif/%.o: $(LWIPDIR)/netif/%.c - $(compile) - -netif/ppp/%.o: $(LWIPDIR)/netif/ppp/%.c - $(compile) - -%.o: $(LIBLWIP_DIRECTORY)/%.c - $(compile) - -.PHONY: all clean prepare - -clean: - rm -f $(LWIPOBJS) liblwip.a - -liblwip.a: $(LWIPOBJS) - $(AR) clr liblwip.a $(LWIPOBJS) diff --git a/artiq/runtime/liblwip/arch/cc.h b/artiq/runtime/liblwip/arch/cc.h deleted file mode 100644 index fffd246ff..000000000 --- a/artiq/runtime/liblwip/arch/cc.h +++ /dev/null @@ -1,35 +0,0 @@ -// This file is Copyright (c) 2015 Florent Kermarrec -// LiteETH lwIP port for ARTIQ -// License: BSD - -#ifndef __ARCH_CC_H__ -#define __ARCH_CC_H__ - -/* Include some files for defining library routines */ -#define BYTE_ORDER BIG_ENDIAN - -/* Compiler hints for packing structures */ -#define PACK_STRUCT_FIELD(x) x -#define PACK_STRUCT_STRUCT __attribute__((packed)) -#define PACK_STRUCT_BEGIN -#define PACK_STRUCT_END - -/* prototypes for printf() and abort() */ -#include -#include -#include "console.h" -#define pp_printf printf - -/* Definitions for ASSERT/DIAG */ -#ifdef LWIP_NOASSERT -#define LWIP_PLATFORM_ASSERT(x) -#else -#define LWIP_PLATFORM_ASSERT(x) do {pp_printf("Assertion \"%s\" failed at line %d in %s\n", \ - x, __LINE__, __FILE__); } while(0) -#endif - -#ifdef LWIP_DEBUG -#define LWIP_PLATFORM_DIAG(x) do {pp_printf x;} while(0) -#endif - -#endif /* __ARCH_CC_H__ */ diff --git a/artiq/runtime/liblwip/arch/perf.h b/artiq/runtime/liblwip/arch/perf.h deleted file mode 100644 index 37e52e4db..000000000 --- a/artiq/runtime/liblwip/arch/perf.h +++ /dev/null @@ -1,11 +0,0 @@ -// This file is Copyright (c) 2015 Florent Kermarrec -// LiteETH lwIP port for ARTIQ -// License: BSD - -#ifndef __ARCH_PERF_H__ -#define __ARCH_PERF_H__ - -#define PERF_START /* null definition */ -#define PERF_STOP(x) /* null definition */ - -#endif /* __ARCH_PERF_H__ */ diff --git a/artiq/runtime/liblwip/liteethif.c b/artiq/runtime/liblwip/liteethif.c deleted file mode 100644 index 5b7f4898a..000000000 --- a/artiq/runtime/liblwip/liteethif.c +++ /dev/null @@ -1,130 +0,0 @@ -// This file is Copyright (c) 2015 Florent Kermarrec -// LiteETH lwIP port for ARTIQ -// License: BSD - -#include - -#ifdef CSR_ETHMAC_BASE - -#include -#include - -#include -#include "liteethif.h" - -#include -#include - -static char *rxbuffer0; -static char *rxbuffer1; -static unsigned int txslot; -static unsigned int txlen; -static char *txbuffer; -static char *txbuffer0; -static char *txbuffer1; - -#define IFNAME0 'e' -#define IFNAME1 't' - -static err_t liteeth_low_level_output(struct netif *netif, struct pbuf *p) -{ - struct pbuf *q; - - txlen = 0; - q = p; - while(q) { - memcpy(txbuffer, q->payload, q->len); - txbuffer += q->len; - txlen += q->len; - if(q->tot_len != q->len) - q = q->next; - else - q = NULL; - } - - ethmac_sram_reader_slot_write(txslot); - ethmac_sram_reader_length_write(txlen); - while(!ethmac_sram_reader_ready_read()); - ethmac_sram_reader_start_write(1); - - txslot = (txslot + 1) % 2; - if(txslot) - txbuffer = txbuffer1; - else - txbuffer = txbuffer0; - - return ERR_OK; -} - -static struct pbuf *liteeth_low_level_input(struct netif *netif) -{ - unsigned int rxslot; - unsigned int rxlen; - char *rxbuffer; - struct pbuf *p, *q; - - p = NULL; - - if(ethmac_sram_writer_ev_pending_read() & ETHMAC_EV_SRAM_WRITER) { - rxslot = ethmac_sram_writer_slot_read(); - rxlen = ethmac_sram_writer_length_read(); - /* dest MAC + source MAC + 802.1Q + ethertype + payload (MTU) */ - if(rxlen <= (netif->mtu + 18)) { - if(rxslot) - rxbuffer = rxbuffer1; - else - rxbuffer = rxbuffer0; - - p = pbuf_alloc(PBUF_RAW, rxlen, PBUF_POOL); - q = p; - while(q) { - memcpy(q->payload, rxbuffer, q->len); - rxbuffer += q->len; - if(q->tot_len != q->len) - q = q->next; - else - q = NULL; - } - } - ethmac_sram_writer_ev_pending_write(ETHMAC_EV_SRAM_WRITER); - } - return p; -} - -void liteeth_input(struct netif *netif) -{ - struct pbuf *p; - p = liteeth_low_level_input(netif); - if(p != NULL) - netif->input(p, netif); -} - -err_t liteeth_init(struct netif *netif) -{ - int i; - - netif->hwaddr_len = 6; - for(i=0;ihwaddr_len;i++) - netif->hwaddr[i] = macadr[i]; - netif->name[0] = IFNAME0; - netif->name[1] = IFNAME1; - netif->output = etharp_output; - netif->linkoutput = liteeth_low_level_output; - netif->mtu = 1500; - netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; - - ethmac_sram_reader_ev_pending_write(ETHMAC_EV_SRAM_READER); - ethmac_sram_writer_ev_pending_write(ETHMAC_EV_SRAM_WRITER); - - rxbuffer0 = (char *)ETHMAC_RX0_BASE; - rxbuffer1 = (char *)ETHMAC_RX1_BASE; - txbuffer0 = (char *)ETHMAC_TX0_BASE; - txbuffer1 = (char *)ETHMAC_TX1_BASE; - - txslot = 0; - txbuffer = txbuffer0; - - return ERR_OK; -} - -#endif /* CSR_ETHMAC_BASE */ diff --git a/artiq/runtime/liblwip/liteethif.h b/artiq/runtime/liblwip/liteethif.h deleted file mode 100644 index 369de3fcd..000000000 --- a/artiq/runtime/liblwip/liteethif.h +++ /dev/null @@ -1,13 +0,0 @@ -// This file is Copyright (c) 2015 Florent Kermarrec -// LiteETH lwIP port for ARTIQ -// License: BSD - -#ifndef __LITEETHIF_H__ -#define __LITEETHIF_H__ - -extern unsigned char macadr[]; - -void liteeth_input(struct netif *netif); -err_t liteeth_init(struct netif *netif); - -#endif /* __LITEETH_IF_H__ */ diff --git a/artiq/runtime/liblwip/lwipopts.h b/artiq/runtime/liblwip/lwipopts.h deleted file mode 100644 index 36ebf7234..000000000 --- a/artiq/runtime/liblwip/lwipopts.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2001, 2002 Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - * - * This file is part of the lwIP TCP/IP stack. - * - * Author: Adam Dunkels - * - */ - -#ifndef __LWIPOPTS_H__ -#define __LWIPOPTS_H__ - -#define NO_SYS 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_IPV6 0 - -#define LWIP_CALLBACK_API 1 - -#define SYS_LIGHTWEIGHT_PROT 0 - -/* -------- TCP Timer Intervals ------- */ -#define TCP_TMR_INTERVAL 1 /* The TCP timer interval in - milliseconds. */ - -#define TCP_FAST_INTERVAL 2 /* the fine grained timeout in - milliseconds */ - -#define TCP_SLOW_INTERVAL 5 /* the coarse grained timeout in - milliseconds */ - -/* ---------- Memory options ---------- */ -/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which - lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 - byte alignment -> define MEM_ALIGNMENT to 2. */ -#define MEM_ALIGNMENT 4 - -/* MEM_SIZE: the size of the heap memory. If the application will send -a lot of data that needs to be copied, this should be set high. */ -#define MEM_SIZE 32 * 1024 - -/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high. */ -#define MEMP_NUM_PBUF 64 -/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB 2 -/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections. */ -#define MEMP_NUM_TCP_PCB 8 -/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections. */ -#define MEMP_NUM_TCP_PCB_LISTEN 16 -/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments. */ -#define MEMP_NUM_TCP_SEG 255 - -/* The following four are used only with the sequential API and can be - set to 0 if the application only will use the raw API. */ -/* MEMP_NUM_NETBUF: the number of struct netbufs. */ -#define MEMP_NUM_NETBUF 0 -/* MEMP_NUM_NETCONN: the number of struct netconns. */ -#define MEMP_NUM_NETCONN 0 -/* MEMP_NUM_TCPIP_MSG: the number of struct tcpip_msg, which is used - for sequential API communication and incoming packets. Used in - src/api/tcpip.c. */ -#define MEMP_NUM_TCPIP_MSG_API 0 -#define MEMP_NUM_TCPIP_MSG_INPKT 0 -/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts. */ -#define MEMP_NUM_SYS_TIMEOUT 5 - -/* ---------- Pbuf options ---------- */ -/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ -#define PBUF_POOL_SIZE 512 - -/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ -#define PBUF_POOL_BUFSIZE 1536 - -/* PBUF_LINK_HLEN: the number of bytes that should be allocated for a - link level header. */ -#define PBUF_LINK_HLEN 16 - -/* ---------- TCP options ---------- */ -#define LWIP_TCP 1 -#define LWIP_TCP_KEEPALIVE 1 -#define TCP_KEEPIDLE_DEFAULT 1250 -#define TCP_KEEPINTVL_DEFAULT 1000 -#define TCP_KEEPCNT_DEFAULT 3 -#define TCP_TTL 255 - -/* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory. */ -#define TCP_QUEUE_OOSEQ 1 - -/* TCP Maximum segment size. */ -#define TCP_MSS 1476 - -/* TCP sender buffer space (bytes). */ -#define TCP_SND_BUF 32 * 1024 - -/* TCP sender buffer space (pbufs). */ -#define TCP_SND_QUEUELEN 3 * TCP_SND_BUF/TCP_MSS - -/* TCP receive window. */ -#define TCP_WND 16 * 1024 - -/* Maximum number of retransmissions of data segments. */ -#define TCP_MAXRTX 12 - -/* Maximum number of retransmissions of SYN segments. */ -#define TCP_SYNMAXRTX 4 - -/* ---------- ARP options ---------- */ -#define ARP_TABLE_SIZE 10 -#define ARP_QUEUEING 1 - -/* ---------- IP options ---------- */ -/* Define IP_FORWARD to 1 if you wish to have the ability to forward - IP packets across network interfaces. If you are going to run lwIP - on a device with only one network interface, define this to 0. */ -#define IP_FORWARD 0 - -/* If defined to 1, IP options are allowed (but not parsed). If - defined to 0, all packets with IP options are dropped. */ -#define IP_OPTIONS 1 - -/* ---------- ICMP options ---------- */ -#define ICMP_TTL 255 - - -/* ---------- DHCP options ---------- */ -/* Define LWIP_DHCP to 1 if you want DHCP configuration of - interfaces. DHCP is not implemented in lwIP 0.5.1, however, so - turning this on does currently not work. */ -#define LWIP_DHCP 0 - -/* 1 if you want to do an ARP check on the offered address - (recommended). */ -#define DHCP_DOES_ARP_CHECK 0 - -/* ---------- UDP options ---------- */ -#define LWIP_UDP 1 -#define UDP_TTL 255 - - -/* ---------- Statistics options ---------- */ -/*#define STATS*/ - -#ifdef STATS -#define LINK_STATS -#define IP_STATS -#define ICMP_STATS -#define UDP_STATS -#define TCP_STATS -#define MEM_STATS -#define MEMP_STATS -#define PBUF_STATS -#define SYS_STATS -#endif /* STATS */ - -/* ---------- PPP ---------- */ - -#define PPP_SUPPORT 1 -#define PPPOS_SUPPORT 1 -#define PPP_IPV4_SUPPORT 1 - -#endif /* __LWIPOPTS_H__ */ diff --git a/artiq/runtime/lwip b/artiq/runtime/lwip deleted file mode 160000 index 216bf8949..000000000 --- a/artiq/runtime/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 216bf89491815029aa15463a18744afa04df58fe diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c deleted file mode 100644 index 30c130830..000000000 --- a/artiq/runtime/main.c +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#ifdef CSR_ETHMAC_BASE -#include -#include -#else -#include -#include -#endif - -#include "flash_storage.h" - -static struct netif netif; - -#ifndef CSR_ETHMAC_BASE -static ppp_pcb *ppp; -#endif - -void lwip_service(void); -void lwip_service(void) -{ - sys_check_timeouts(); -#ifdef CSR_ETHMAC_BASE - liteeth_input(&netif); -#else - if(uart_read_nonblock()) { - u8_t c; - c = uart_read(); - pppos_input(ppp, &c, 1); - } -#endif -} - -#ifdef CSR_ETHMAC_BASE -unsigned char macadr[6]; - -static int hex2nib(int c) -{ - if((c >= '0') && (c <= '9')) - return c - '0'; - if((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - if((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - return -1; -} - -static void init_macadr(void) -{ - static const unsigned char default_macadr[6] = {0x10, 0xe2, 0xd5, 0x32, 0x50, 0x00}; -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - char b[32]; - char fs_macadr[6]; - int i, r, s; -#endif - - memcpy(macadr, default_macadr, 6); -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - r = fs_read("mac", b, sizeof(b) - 1, NULL); - if(r <= 0) - return; - b[r] = 0; - for(i=0;i<6;i++) { - r = hex2nib(b[3*i]); - s = hex2nib(b[3*i + 1]); - if((r < 0) || (s < 0)) - return; - fs_macadr[i] = (r << 4) | s; - } - for(i=0;i<5;i++) - if(b[3*i + 2] != ':') - return; - memcpy(macadr, fs_macadr, 6); -#endif -} - -static void fsip_or_default(struct ip4_addr *d, char *key, int i1, int i2, int i3, int i4) -{ - int r; -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - char cp[32]; -#endif - - IP4_ADDR(d, i1, i2, i3, i4); -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - r = fs_read(key, cp, sizeof(cp) - 1, NULL); - if(r <= 0) - return; - cp[r] = 0; - if(!ip4addr_aton(cp, d)) - return; -#endif -} - -void network_init(void); -void network_init(void) -{ - struct ip4_addr local_ip; - struct ip4_addr netmask; - struct ip4_addr gateway_ip; - - init_macadr(); - fsip_or_default(&local_ip, "ip", 192, 168, 1, 50); - fsip_or_default(&netmask, "netmask", 255, 255, 255, 0); - fsip_or_default(&gateway_ip, "gateway", 192, 168, 1, 1); - - lwip_init(); - - netif_add(&netif, &local_ip, &netmask, &gateway_ip, 0, liteeth_init, ethernet_input); - netif_set_default(&netif); - netif_set_up(&netif); - netif_set_link_up(&netif); -} -#else /* CSR_ETHMAC_BASE */ - -static int ppp_connected; - -static u32_t ppp_output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) -{ - for(int i = 0; i < len; i++) - uart_write(data[i]); - return len; -} - -static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) -{ - if (err_code == PPPERR_NONE) { - ppp_connected = 1; - return; - } else if (err_code == PPPERR_USER) { - return; - } else { - ppp_connect(pcb, 1); - } -} - -void network_init(void) -{ - lwip_init(); - - ppp_connected = 0; - ppp = pppos_create(&netif, ppp_output_cb, ppp_status_cb, NULL); - ppp_set_auth(ppp, PPPAUTHTYPE_NONE, "", ""); - ppp_set_default(ppp); - ppp_connect(ppp, 0); - - while (!ppp_connected) - lwip_service(); -} - -#endif /* CSR_ETHMAC_BASE */ - - -extern void _fheap, _eheap; - -extern void rust_main(); - -u16_t tcp_sndbuf_(struct tcp_pcb *pcb); -u16_t tcp_sndbuf_(struct tcp_pcb *pcb) { - return tcp_sndbuf(pcb); -} - -u8_t* tcp_so_options_(struct tcp_pcb *pcb); -u8_t* tcp_so_options_(struct tcp_pcb *pcb) { - return &pcb->so_options; -} - -void tcp_nagle_disable_(struct tcp_pcb *pcb); -void tcp_nagle_disable_(struct tcp_pcb *pcb) { - tcp_nagle_disable(pcb); -} - -int main(void) -{ - irq_setmask(0); - irq_setie(1); - uart_init(); - - alloc_give(&_fheap, &_eheap - &_fheap); - - rust_main(); - - return 0; -}