From 46c8afc56cdbcfafc735e93e1e81fab419cf7a5b Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 14 May 2018 10:02:39 +0000 Subject: [PATCH] firmware: implement libio and use in in drtioaux. --- artiq/firmware/Cargo.lock | 10 +- artiq/firmware/libdrtioaux/Cargo.toml | 3 +- artiq/firmware/libdrtioaux/hw.rs | 139 +++++++++ artiq/firmware/libdrtioaux/lib.rs | 434 ++++++++++---------------- artiq/firmware/libdrtioaux/proto.rs | 106 ------- artiq/firmware/libio/Cargo.toml | 14 + artiq/firmware/libio/lib.rs | 176 +++++++++++ artiq/firmware/libio/proto.rs | 132 ++++++++ 8 files changed, 633 insertions(+), 381 deletions(-) create mode 100644 artiq/firmware/libdrtioaux/hw.rs delete mode 100644 artiq/firmware/libdrtioaux/proto.rs create mode 100644 artiq/firmware/libio/Cargo.toml create mode 100644 artiq/firmware/libio/lib.rs create mode 100644 artiq/firmware/libio/proto.rs diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index b1b82a1b9..95338bef4 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -107,10 +107,9 @@ version = "0.0.0" dependencies = [ "board 0.0.0", "build_misoc 0.0.0", - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "io 0.0.0", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "std_artiq 0.0.0", ] [[package]] @@ -125,6 +124,13 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "io" +version = "0.0.0" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" diff --git a/artiq/firmware/libdrtioaux/Cargo.toml b/artiq/firmware/libdrtioaux/Cargo.toml index 6c4842148..5959b56a8 100644 --- a/artiq/firmware/libdrtioaux/Cargo.toml +++ b/artiq/firmware/libdrtioaux/Cargo.toml @@ -14,6 +14,5 @@ build_misoc = { path = "../libbuild_misoc" } [dependencies] log = { version = "0.4", default-features = false } crc = { version = "1.7", default-features = false } -std_artiq = { path = "../libstd_artiq", features = ["alloc"] } +io = { path = "../libio", features = ["byteorder"] } board = { path = "../libboard" } -byteorder = { version = "1.0", default-features = false } diff --git a/artiq/firmware/libdrtioaux/hw.rs b/artiq/firmware/libdrtioaux/hw.rs new file mode 100644 index 000000000..ea68c24e1 --- /dev/null +++ b/artiq/firmware/libdrtioaux/hw.rs @@ -0,0 +1,139 @@ +use super::*; + +use {Error, Result}; +use io::Cursor; +use io::proto::ProtoRead; + +pub fn reset(linkno: u8) { + let linkno = linkno as usize; + unsafe { + // clear buffer first to limit race window with buffer overflow + // error. We assume the CPU is fast enough so that no two packets + // will be received between the buffer and the error flag are cleared. + (board::csr::DRTIO[linkno].aux_rx_present_write)(1); + (board::csr::DRTIO[linkno].aux_rx_error_write)(1); + } +} + +fn has_rx_error(linkno: u8) -> bool { + let linkno = linkno as usize; + unsafe { + let error = (board::csr::DRTIO[linkno].aux_rx_error_read)() != 0; + if error { + (board::csr::DRTIO[linkno].aux_rx_error_write)(1) + } + error + } +} + +fn receive(linkno: u8, f: F) -> Result, !> + where F: FnOnce(&[u8]) -> Result +{ + let linkidx = linkno as usize; + unsafe { + if (board::csr::DRTIO[linkidx].aux_rx_present_read)() == 1 { + let ptr = board::mem::DRTIO_AUX[linkidx].base + + board::mem::DRTIO_AUX[linkidx].size / 2; + let len = (board::csr::DRTIO[linkidx].aux_rx_length_read)(); + let result = f(slice::from_raw_parts(ptr as *mut u8, len as usize)); + (board::csr::DRTIO[linkidx].aux_rx_present_write)(1); + Ok(Some(result?)) + } else { + Ok(None) + } + } +} + +pub fn recv_link(linkno: u8) -> Result, !> { + if has_rx_error(linkno) { + return Err(Error::GatewareError) + } + + receive(linkno, |buffer| { + if buffer.len() < 8 { + return Err(Error::Io(IoError::UnexpectedEof)) + } + + let mut reader = Cursor::new(buffer); + + let checksum_at = buffer.len() - 4; + let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]); + reader.set_position(checksum_at); + if reader.read_u32()? != checksum { + return Err(Error::CorruptedPacket) + } + reader.set_position(0); + + Packet::read_from(&mut reader) + }) +} + +pub fn recv_timeout_link(linkno: u8, timeout_ms: Option) -> Result { + let timeout_ms = timeout_ms.unwrap_or(10); + let limit = board::clock::get_ms() + timeout_ms; + while board::clock::get_ms() < limit { + match recv_link(linkno)? { + None => (), + Some(packet) => return Ok(packet), + } + } + Err(Error::TimedOut) +} + +fn transmit(linkno: u8, f: F) -> Result<(), !> + where F: FnOnce(&mut [u8]) -> Result +{ + let linkno = linkno as usize; + unsafe { + while (board::csr::DRTIO[linkno].aux_tx_read)() != 0 {} + let ptr = board::mem::DRTIO_AUX[linkno].base; + let len = board::mem::DRTIO_AUX[linkno].size / 2; + let len = f(slice::from_raw_parts_mut(ptr as *mut u8, len))?; + (board::csr::DRTIO[linkno].aux_tx_length_write)(len as u16); + (board::csr::DRTIO[linkno].aux_tx_write)(1); + Ok(()) + } +} + +pub fn send_link(linkno: u8, packet: &Packet) -> Result<(), !> { + transmit(linkno, |buffer| { + let mut writer = Cursor::new(buffer); + + packet.write_to(&mut writer)?; + + let padding = 4 - (writer.position() % 4); + if padding != 4 { + for _ in 0..padding { + writer.write_u8(0)?; + } + } + + let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]); + writer.write_u32(checksum)?; + + Ok(writer.position()) + }) +} + +// TODO: routing +fn get_linkno(nodeno: u8) -> Result { + if nodeno == 0 || nodeno as usize > board::csr::DRTIO.len() { + return Err(Error::NoRoute) + } + Ok(nodeno - 1) +} + +pub fn recv(nodeno: u8) -> Result, !> { + let linkno = get_linkno(nodeno)?; + recv_link(linkno) +} + +pub fn recv_timeout(nodeno: u8, timeout_ms: Option) -> Result { + let linkno = get_linkno(nodeno)?; + recv_timeout_link(linkno, timeout_ms) +} + +pub fn send(nodeno: u8, packet: &Packet) -> Result<(), !> { + let linkno = get_linkno(nodeno)?; + send_link(linkno, packet) +} diff --git a/artiq/firmware/libdrtioaux/lib.rs b/artiq/firmware/libdrtioaux/lib.rs index e818ab22e..97b7f46af 100644 --- a/artiq/firmware/libdrtioaux/lib.rs +++ b/artiq/firmware/libdrtioaux/lib.rs @@ -1,17 +1,63 @@ #![no_std] +#![feature(never_type)] -extern crate byteorder; extern crate crc; -#[macro_use] -extern crate std_artiq as std; + +extern crate io; extern crate board; -mod proto; - -use std::io::{self, Read, Write}; -#[cfg(has_drtio)] use core::slice; -use proto::*; +use core::result; +use core::fmt; + +use io::{Read, Write, Error as IoError}; +use io::proto::{ProtoRead, ProtoWrite}; + +pub type Result = result::Result>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + UnknownPacket(u8), + CorruptedPacket, + TimedOut, + NoRoute, + GatewareError, + Io(IoError), + Other(T) +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Error::UnknownPacket(ty) => + write!(f, "unknown packet type {:#02x}", ty), + &Error::CorruptedPacket => + write!(f, "packet CRC failed"), + &Error::TimedOut => + write!(f, "timed out waiting for data"), + &Error::NoRoute => + write!(f, "invalid node number"), + &Error::GatewareError => + write!(f, "gateware reported error"), + &Error::Io(ref io) => + write!(f, "I/O error ({})", io), + &Error::Other(ref err) => + write!(f, "{}", err) + } + } +} + +impl From for Error { + fn from(value: T) -> Error { + Error::Other(value) + } +} + +impl From> for Error { + fn from(value: IoError) -> Error { + Error::Io(value) + } +} #[derive(Debug)] pub enum Packet { @@ -49,215 +95,220 @@ pub enum Packet { } impl Packet { - pub fn read_from(reader: &mut Read) -> io::Result { - Ok(match read_u8(reader)? { + pub fn read_from(reader: &mut T) -> Result { + Ok(match reader.read_u8()? { 0x00 => Packet::EchoRequest, 0x01 => Packet::EchoReply, 0x02 => Packet::ResetRequest { - phy: read_bool(reader)? + phy: reader.read_bool()? }, 0x03 => Packet::ResetAck, 0x20 => Packet::RtioErrorRequest, 0x21 => Packet::RtioNoErrorReply, 0x22 => Packet::RtioErrorSequenceErrorReply { - channel: read_u16(reader)? + channel: reader.read_u16()? }, 0x23 => Packet::RtioErrorCollisionReply { - channel: read_u16(reader)? + channel: reader.read_u16()? }, 0x24 => Packet::RtioErrorBusyReply { - channel: read_u16(reader)? + channel: reader.read_u16()? }, 0x40 => Packet::MonitorRequest { - channel: read_u16(reader)?, - probe: read_u8(reader)? + channel: reader.read_u16()?, + probe: reader.read_u8()? }, 0x41 => Packet::MonitorReply { - value: read_u32(reader)? + value: reader.read_u32()? }, 0x50 => Packet::InjectionRequest { - channel: read_u16(reader)?, - overrd: read_u8(reader)?, - value: read_u8(reader)? + channel: reader.read_u16()?, + overrd: reader.read_u8()?, + value: reader.read_u8()? }, 0x51 => Packet::InjectionStatusRequest { - channel: read_u16(reader)?, - overrd: read_u8(reader)? + channel: reader.read_u16()?, + overrd: reader.read_u8()? }, 0x52 => Packet::InjectionStatusReply { - value: read_u8(reader)? + value: reader.read_u8()? }, 0x80 => Packet::I2cStartRequest { - busno: read_u8(reader)? + busno: reader.read_u8()? }, 0x81 => Packet::I2cRestartRequest { - busno: read_u8(reader)? + busno: reader.read_u8()? }, 0x82 => Packet::I2cStopRequest { - busno: read_u8(reader)? + busno: reader.read_u8()? }, 0x83 => Packet::I2cWriteRequest { - busno: read_u8(reader)?, - data: read_u8(reader)? + busno: reader.read_u8()?, + data: reader.read_u8()? }, 0x84 => Packet::I2cWriteReply { - succeeded: read_bool(reader)?, - ack: read_bool(reader)? + succeeded: reader.read_bool()?, + ack: reader.read_bool()? }, 0x85 => Packet::I2cReadRequest { - busno: read_u8(reader)?, - ack: read_bool(reader)? + busno: reader.read_u8()?, + ack: reader.read_bool()? }, 0x86 => Packet::I2cReadReply { - succeeded: read_bool(reader)?, - data: read_u8(reader)? + succeeded: reader.read_bool()?, + data: reader.read_u8()? }, 0x87 => Packet::I2cBasicReply { - succeeded: read_bool(reader)? + succeeded: reader.read_bool()? }, 0x90 => Packet::SpiSetConfigRequest { - busno: read_u8(reader)?, - flags: read_u8(reader)?, - length: read_u8(reader)?, - div: read_u8(reader)?, - cs: read_u8(reader)? + busno: reader.read_u8()?, + flags: reader.read_u8()?, + length: reader.read_u8()?, + div: reader.read_u8()?, + cs: reader.read_u8()? }, /* 0x91: was Packet::SpiSetXferRequest */ 0x92 => Packet::SpiWriteRequest { - busno: read_u8(reader)?, - data: read_u32(reader)? + busno: reader.read_u8()?, + data: reader.read_u32()? }, 0x93 => Packet::SpiReadRequest { - busno: read_u8(reader)? + busno: reader.read_u8()? }, 0x94 => Packet::SpiReadReply { - succeeded: read_bool(reader)?, - data: read_u32(reader)? + succeeded: reader.read_bool()?, + data: reader.read_u32()? }, 0x95 => Packet::SpiBasicReply { - succeeded: read_bool(reader)? + succeeded: reader.read_bool()? }, - _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown packet type")) + ty => return Err(Error::UnknownPacket(ty)) }) } - pub fn write_to(&self, writer: &mut Write) -> io::Result<()> { + pub fn write_to(&self, writer: &mut T) -> Result<(), T::WriteError> { match *self { - Packet::EchoRequest => write_u8(writer, 0x00)?, - Packet::EchoReply => write_u8(writer, 0x01)?, + Packet::EchoRequest => + writer.write_u8(0x00)?, + Packet::EchoReply => + writer.write_u8(0x01)?, Packet::ResetRequest { phy } => { - write_u8(writer, 0x02)?; - write_bool(writer, phy)?; + writer.write_u8(0x02)?; + writer.write_bool(phy)?; }, - Packet::ResetAck => write_u8(writer, 0x03)?, + Packet::ResetAck => + writer.write_u8(0x03)?, - Packet::RtioErrorRequest => write_u8(writer, 0x20)?, - Packet::RtioNoErrorReply => write_u8(writer, 0x21)?, + Packet::RtioErrorRequest => + writer.write_u8(0x20)?, + Packet::RtioNoErrorReply => + writer.write_u8(0x21)?, Packet::RtioErrorSequenceErrorReply { channel } => { - write_u8(writer, 0x22)?; - write_u16(writer, channel)?; + writer.write_u8(0x22)?; + writer.write_u16(channel)?; }, Packet::RtioErrorCollisionReply { channel } => { - write_u8(writer, 0x23)?; - write_u16(writer, channel)?; + writer.write_u8(0x23)?; + writer.write_u16(channel)?; }, Packet::RtioErrorBusyReply { channel } => { - write_u8(writer, 0x24)?; - write_u16(writer, channel)?; + writer.write_u8(0x24)?; + writer.write_u16(channel)?; }, Packet::MonitorRequest { channel, probe } => { - write_u8(writer, 0x40)?; - write_u16(writer, channel)?; - write_u8(writer, probe)?; + writer.write_u8(0x40)?; + writer.write_u16(channel)?; + writer.write_u8(probe)?; }, Packet::MonitorReply { value } => { - write_u8(writer, 0x41)?; - write_u32(writer, value)?; + writer.write_u8(0x41)?; + writer.write_u32(value)?; }, Packet::InjectionRequest { channel, overrd, value } => { - write_u8(writer, 0x50)?; - write_u16(writer, channel)?; - write_u8(writer, overrd)?; - write_u8(writer, value)?; + writer.write_u8(0x50)?; + writer.write_u16(channel)?; + writer.write_u8(overrd)?; + writer.write_u8(value)?; }, Packet::InjectionStatusRequest { channel, overrd } => { - write_u8(writer, 0x51)?; - write_u16(writer, channel)?; - write_u8(writer, overrd)?; + writer.write_u8(0x51)?; + writer.write_u16(channel)?; + writer.write_u8(overrd)?; }, Packet::InjectionStatusReply { value } => { - write_u8(writer, 0x52)?; - write_u8(writer, value)?; + writer.write_u8(0x52)?; + writer.write_u8(value)?; }, Packet::I2cStartRequest { busno } => { - write_u8(writer, 0x80)?; - write_u8(writer, busno)?; + writer.write_u8(0x80)?; + writer.write_u8(busno)?; }, Packet::I2cRestartRequest { busno } => { - write_u8(writer, 0x81)?; - write_u8(writer, busno)?; + writer.write_u8(0x81)?; + writer.write_u8(busno)?; }, Packet::I2cStopRequest { busno } => { - write_u8(writer, 0x82)?; - write_u8(writer, busno)?; + writer.write_u8(0x82)?; + writer.write_u8(busno)?; }, Packet::I2cWriteRequest { busno, data } => { - write_u8(writer, 0x83)?; - write_u8(writer, busno)?; - write_u8(writer, data)?; + writer.write_u8(0x83)?; + writer.write_u8(busno)?; + writer.write_u8(data)?; }, Packet::I2cWriteReply { succeeded, ack } => { - write_u8(writer, 0x84)?; - write_bool(writer, succeeded)?; - write_bool(writer, ack)?; + writer.write_u8(0x84)?; + writer.write_bool(succeeded)?; + writer.write_bool(ack)?; }, Packet::I2cReadRequest { busno, ack } => { - write_u8(writer, 0x85)?; - write_u8(writer, busno)?; - write_bool(writer, ack)?; + writer.write_u8(0x85)?; + writer.write_u8(busno)?; + writer.write_bool(ack)?; }, Packet::I2cReadReply { succeeded, data } => { - write_u8(writer, 0x86)?; - write_bool(writer, succeeded)?; - write_u8(writer, data)?; + writer.write_u8(0x86)?; + writer.write_bool(succeeded)?; + writer.write_u8(data)?; }, Packet::I2cBasicReply { succeeded } => { - write_u8(writer, 0x87)?; - write_bool(writer, succeeded)?; + writer.write_u8(0x87)?; + writer.write_bool(succeeded)?; }, Packet::SpiSetConfigRequest { busno, flags, length, div, cs } => { - write_u8(writer, 0x90)?; - write_u8(writer, busno)?; - write_u8(writer, flags)?; - write_u8(writer, length)?; - write_u8(writer, div)?; - write_u8(writer, cs)?; + writer.write_u8(0x90)?; + writer.write_u8(busno)?; + writer.write_u8(flags)?; + writer.write_u8(length)?; + writer.write_u8(div)?; + writer.write_u8(cs)?; }, Packet::SpiWriteRequest { busno, data } => { - write_u8(writer, 0x92)?; - write_u8(writer, busno)?; - write_u32(writer, data)?; + writer.write_u8(0x92)?; + writer.write_u8(busno)?; + writer.write_u32(data)?; }, Packet::SpiReadRequest { busno } => { - write_u8(writer, 0x93)?; - write_u8(writer, busno)?; + writer.write_u8(0x93)?; + writer.write_u8(busno)?; }, Packet::SpiReadReply { succeeded, data } => { - write_u8(writer, 0x94)?; - write_bool(writer, succeeded)?; - write_u32(writer, data)?; + writer.write_u8(0x94)?; + writer.write_bool(succeeded)?; + writer.write_u32(data)?; }, Packet::SpiBasicReply { succeeded } => { - write_u8(writer, 0x95)?; - write_bool(writer, succeeded)?; + writer.write_u8(0x95)?; + writer.write_bool(succeeded)?; }, } Ok(()) @@ -265,163 +316,4 @@ impl Packet { } #[cfg(has_drtio)] -pub mod hw { - use super::*; - use std::io::Cursor; - - pub fn reset(linkno: u8) { - let linkno = linkno as usize; - unsafe { - // clear buffer first to limit race window with buffer overflow - // error. We assume the CPU is fast enough so that no two packets - // will be received between the buffer and the error flag are cleared. - (board::csr::DRTIO[linkno].aux_rx_present_write)(1); - (board::csr::DRTIO[linkno].aux_rx_error_write)(1); - } - } - - fn rx_has_error(linkno: u8) -> bool { - let linkno = linkno as usize; - unsafe { - let error = (board::csr::DRTIO[linkno].aux_rx_error_read)() != 0; - if error { - (board::csr::DRTIO[linkno].aux_rx_error_write)(1) - } - error - } - } - - struct RxBuffer(u8, &'static [u8]); - - impl Drop for RxBuffer { - fn drop(&mut self) { - unsafe { - (board::csr::DRTIO[self.0 as usize].aux_rx_present_write)(1); - } - } - } - - fn rx_get_buffer(linkno: u8) -> Option { - let linkidx = linkno as usize; - unsafe { - if (board::csr::DRTIO[linkidx].aux_rx_present_read)() == 1 { - let length = (board::csr::DRTIO[linkidx].aux_rx_length_read)(); - let base = board::mem::DRTIO_AUX[linkidx].base + board::mem::DRTIO_AUX[linkidx].size/2; - let sl = slice::from_raw_parts(base as *mut u8, length as usize); - Some(RxBuffer(linkno, sl)) - } else { - None - } - } - } - - pub fn recv_link(linkno: u8) -> io::Result> { - if rx_has_error(linkno) { - return Err(io::Error::new(io::ErrorKind::Other, "gateware reported error")) - } - let buffer = rx_get_buffer(linkno); - match buffer { - Some(rxb) => { - let slice = rxb.1; - let mut reader = Cursor::new(slice); - - let len = slice.len(); - if len < 8 { - return Err(io::Error::new(io::ErrorKind::InvalidData, "packet too short")) - } - let computed_crc = crc::crc32::checksum_ieee(&reader.get_ref()[0..len-4]); - reader.set_position((len-4) as u64); - let crc = read_u32(&mut reader)?; - if crc != computed_crc { - return Err(io::Error::new(io::ErrorKind::InvalidData, "packet CRC failed")) - } - reader.set_position(0); - - let packet_r = Packet::read_from(&mut reader); - match packet_r { - Ok(packet) => Ok(Some(packet)), - Err(e) => Err(e) - } - } - None => Ok(None) - } - } - - pub fn recv_timeout_link(linkno: u8, timeout_ms: Option) -> io::Result { - let timeout_ms = timeout_ms.unwrap_or(10); - let limit = board::clock::get_ms() + timeout_ms; - while board::clock::get_ms() < limit { - match recv_link(linkno) { - Ok(None) => (), - Ok(Some(packet)) => return Ok(packet), - Err(e) => return Err(e) - } - } - return Err(io::Error::new(io::ErrorKind::TimedOut, "timed out waiting for data")) - } - - fn tx_get_buffer(linkno: u8) -> &'static mut [u8] { - let linkno = linkno as usize; - unsafe { - while (board::csr::DRTIO[linkno].aux_tx_read)() != 0 {} - let base = board::mem::DRTIO_AUX[linkno].base; - let size = board::mem::DRTIO_AUX[linkno].size/2; - slice::from_raw_parts_mut(base as *mut u8, size) - } - } - - fn tx_ack_buffer(linkno: u8, length: u16) { - let linkno = linkno as usize; - unsafe { - (board::csr::DRTIO[linkno].aux_tx_length_write)(length); - (board::csr::DRTIO[linkno].aux_tx_write)(1) - } - } - - pub fn send_link(linkno: u8, packet: &Packet) -> io::Result<()> { - let sl = tx_get_buffer(linkno); - - let mut writer = Cursor::new(sl); - packet.write_to(&mut writer)?; - let mut len = writer.position(); - - let padding = 4 - (len % 4); - if padding != 4 { - for _ in 0..padding { - write_u8(&mut writer, 0)?; - } - len += padding; - } - - let crc = crc::crc32::checksum_ieee(&writer.get_ref()[0..len as usize]); - write_u32(&mut writer, crc)?; - len += 4; - - tx_ack_buffer(linkno, len as u16); - - Ok(()) - } - - // TODO: routing - fn get_linkno(nodeno: u8) -> io::Result { - if nodeno == 0 || nodeno as usize > board::csr::DRTIO.len() { - return Err(io::Error::new(io::ErrorKind::NotFound, "invalid node number")) - } - Ok(nodeno - 1) - } - - pub fn recv(nodeno: u8) -> io::Result> { - let linkno = get_linkno(nodeno)?; - recv_link(linkno) - } - - pub fn recv_timeout(nodeno: u8, timeout_ms: Option) -> io::Result { - let linkno = get_linkno(nodeno)?; - recv_timeout_link(linkno, timeout_ms) - } - - pub fn send(nodeno: u8, packet: &Packet) -> io::Result<()> { - let linkno = get_linkno(nodeno)?; - send_link(linkno, packet) - } -} +pub mod hw; diff --git a/artiq/firmware/libdrtioaux/proto.rs b/artiq/firmware/libdrtioaux/proto.rs deleted file mode 100644 index 9a89796e0..000000000 --- a/artiq/firmware/libdrtioaux/proto.rs +++ /dev/null @@ -1,106 +0,0 @@ -#![allow(dead_code)] - -use std::io::{self, Read, Write}; -use std::vec::Vec; -use std::string::String; -use byteorder::{ByteOrder, NetworkEndian}; - -// FIXME: replace these with byteorder core io traits once those are in -#[inline(always)] -pub fn read_u8(reader: &mut Read) -> io::Result { - let mut bytes = [0; 1]; - reader.read_exact(&mut bytes)?; - Ok(bytes[0]) -} - -#[inline(always)] -pub fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> { - let bytes = [value; 1]; - writer.write_all(&bytes) -} - -#[inline(always)] -pub fn read_bool(reader: &mut Read) -> io::Result { - if read_u8(reader)? == 0 { - Ok(false) - } else { - Ok(true) - } -} - -#[inline(always)] -pub fn write_bool(writer: &mut Write, value: bool) -> io::Result<()> { - if value { - write_u8(writer, 1) - } else { - write_u8(writer, 0) - } -} - -#[inline(always)] -pub fn read_u16(reader: &mut Read) -> io::Result { - let mut bytes = [0; 2]; - reader.read_exact(&mut bytes)?; - Ok(NetworkEndian::read_u16(&bytes)) -} - -#[inline(always)] -pub fn write_u16(writer: &mut Write, value: u16) -> io::Result<()> { - let mut bytes = [0; 2]; - NetworkEndian::write_u16(&mut bytes, value); - writer.write_all(&bytes) -} - -#[inline(always)] -pub fn read_u32(reader: &mut Read) -> io::Result { - let mut bytes = [0; 4]; - reader.read_exact(&mut bytes)?; - Ok(NetworkEndian::read_u32(&bytes)) -} - -#[inline(always)] -pub fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> { - let mut bytes = [0; 4]; - NetworkEndian::write_u32(&mut bytes, value); - writer.write_all(&bytes) -} - -#[inline(always)] -pub fn read_u64(reader: &mut Read) -> io::Result { - let mut bytes = [0; 8]; - reader.read_exact(&mut bytes)?; - Ok(NetworkEndian::read_u64(&bytes)) -} - -#[inline(always)] -pub fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> { - let mut bytes = [0; 8]; - NetworkEndian::write_u64(&mut bytes, value); - writer.write_all(&bytes) -} - -#[inline(always)] -pub fn read_bytes(reader: &mut Read) -> io::Result> { - let length = read_u32(reader)?; - let mut value = vec![0; length as usize]; - reader.read_exact(&mut value)?; - Ok(value) -} - -#[inline(always)] -pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { - write_u32(writer, value.len() as u32)?; - writer.write_all(value) -} - -#[inline(always)] -pub fn read_string(reader: &mut Read) -> io::Result { - let bytes = read_bytes(reader)?; - String::from_utf8(bytes) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8")) -} - -#[inline(always)] -pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { - write_bytes(writer, value.as_bytes()) -} diff --git a/artiq/firmware/libio/Cargo.toml b/artiq/firmware/libio/Cargo.toml new file mode 100644 index 000000000..9729aca32 --- /dev/null +++ b/artiq/firmware/libio/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["M-Labs"] +name = "io" +version = "0.0.0" + +[lib] +name = "io" +path = "lib.rs" + +[dependencies] +byteorder = { version = "1.0", default-features = false, optional = true } + +[features] +alloc = [] diff --git a/artiq/firmware/libio/lib.rs b/artiq/firmware/libio/lib.rs new file mode 100644 index 000000000..631269ddb --- /dev/null +++ b/artiq/firmware/libio/lib.rs @@ -0,0 +1,176 @@ +#![no_std] +#![feature(never_type)] +#![cfg_attr(feature = "alloc", feature(alloc))] + +#[cfg(feature = "alloc")] +#[macro_use] +extern crate alloc; +#[cfg(feature = "byteorder")] +extern crate byteorder; + +use core::result; +use core::fmt; + +#[cfg(feature = "byteorder")] +pub mod proto; + +pub type Result = result::Result>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + UnexpectedEof, + Other(T) +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Error::UnexpectedEof => + write!(f, "unexpected end of stream"), + &Error::Other(ref err) => + write!(f, "{}", err) + } + } +} + +impl From for Error { + fn from(value: T) -> Error { + Error::Other(value) + } +} + +pub trait Read { + type ReadError; + + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + fn read(&mut self, buf: &mut [u8]) -> result::Result; + + /// Read the exact number of bytes required to fill `buf`. + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::ReadError> { + while !buf.is_empty() { + let read_bytes = self.read(buf)?; + if read_bytes == 0 { + return Err(Error::UnexpectedEof) + } + + buf = &mut { buf }[read_bytes..]; + } + + Ok(()) + } +} + +pub trait Write { + type WriteError; + type FlushError; + + /// Write a buffer into this object, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> result::Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents + /// reach their destination. + fn flush(&mut self) -> result::Result<(), Self::FlushError>; + + /// Attempts to write an entire buffer into `self`. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::WriteError> { + while buf.len() > 0 { + let written_bytes = self.write(buf)?; + if written_bytes == 0 { + return Err(Error::UnexpectedEof) + } + + buf = &buf[written_bytes..]; + } + + Ok(()) + } + + /// Hints the writer how much bytes will be written after call to this function. + /// + /// At least `min` bytes should be written after the call to this function and + /// if `max` is `Some(x)` than at most `x` bytes should be written. + fn size_hint(&mut self, _min: usize, _max: Option) {} +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CursorError { + EndOfBuffer +} + +#[derive(Debug, Clone)] +pub struct Cursor { + inner: T, + pos: usize +} + +impl Cursor { + pub fn new(inner: T) -> Cursor { + Cursor { inner, pos: 0 } + } + + pub fn into_inner(self) -> T { + self.inner + } + + pub fn get_ref(&self) -> &T { + &self.inner + } + + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + pub fn position(&self) -> usize { + self.pos + + } + + pub fn set_position(&mut self, pos: usize) { + self.pos = pos + } +} + +impl> Read for Cursor { + type ReadError = !; + + fn read(&mut self, buf: &mut [u8]) -> result::Result { + let data = &self.inner.as_ref()[self.pos..]; + let len = buf.len().min(data.len()); + buf[..len].copy_from_slice(&data[..len]); + self.pos += len; + Ok(len) + } +} + +impl> Write for Cursor { + type WriteError = !; + type FlushError = !; + + fn write(&mut self, buf: &[u8]) -> result::Result { + let data = &mut self.inner.as_mut()[self.pos..]; + let len = buf.len().min(data.len()); + data[..len].copy_from_slice(&buf[..len]); + self.pos += len; + Ok(len) + } + + fn flush(&mut self) -> result::Result<(), Self::FlushError> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl> Write for Cursor { + type WriteError = !; + type FlushError = !; + + fn write(&mut self, buf: &[u8]) -> result::Result { + self.inner.extend(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> result::Result<(), Self::FlushError> { + Ok(()) + } +} diff --git a/artiq/firmware/libio/proto.rs b/artiq/firmware/libio/proto.rs new file mode 100644 index 000000000..eaf0c3042 --- /dev/null +++ b/artiq/firmware/libio/proto.rs @@ -0,0 +1,132 @@ +use byteorder::{ByteOrder, NetworkEndian}; + +use ::{Read, Write, Error as IoError}; + +pub trait ProtoRead { + type ReadError; + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>; + + fn read_u8(&mut self) -> Result { + let mut bytes = [0; 1]; + self.read_exact(&mut bytes)?; + Ok(bytes[0]) + } + + fn read_u16(&mut self) -> Result { + let mut bytes = [0; 2]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u16(&bytes)) + } + + fn read_u32(&mut self) -> Result { + let mut bytes = [0; 4]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u32(&bytes)) + } + + fn read_u64(&mut self) -> Result { + let mut bytes = [0; 8]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u64(&bytes)) + } + + fn read_bool(&mut self) -> Result { + Ok(self.read_u8()? != 0) + } + + #[cfg(feature = "alloc")] + fn read_bytes(&mut self) -> Result<::alloc::Vec, Self::ReadError> { + let length = self.read_u32()?; + let mut value = vec![0; length as usize]; + self.read_exact(&mut value)?; + Ok(value) + } + +// fn read_string(&mut self) -> Result { +// let bytes = self.read_bytes()?; +// String::from_utf8(bytes) +// .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8")) +// } +} + +pub trait ProtoWrite { + type WriteError; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>; + + fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> { + let bytes = [value; 1]; + self.write_all(&bytes) + } + + fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> { + let bytes = [value as u8; 1]; + self.write_all(&bytes) + } + + fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> { + let mut bytes = [0; 2]; + NetworkEndian::write_u16(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> { + let mut bytes = [0; 2]; + NetworkEndian::write_i16(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> { + let mut bytes = [0; 4]; + NetworkEndian::write_u32(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> { + let mut bytes = [0; 4]; + NetworkEndian::write_i32(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> { + let mut bytes = [0; 8]; + NetworkEndian::write_u64(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> { + let mut bytes = [0; 8]; + NetworkEndian::write_i64(&mut bytes, value); + self.write_all(&bytes) + } + + fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> { + self.write_u8(value as u8) + } + + fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> { + self.write_u32(value.len() as u32)?; + self.write_all(value) + } + + fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> { + self.write_bytes(value.as_bytes()) + } +} + +impl ProtoRead for T where T: Read { + type ReadError = IoError; + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> { + T::read_exact(self, buf) + } +} + +impl ProtoWrite for T where T: Write { + type WriteError = IoError; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> { + T::write_all(self, buf) + } +}