mirror of https://github.com/m-labs/artiq.git
417 lines
14 KiB
Rust
417 lines
14 KiB
Rust
#![no_std]
|
|
|
|
extern crate byteorder;
|
|
extern crate crc;
|
|
#[macro_use]
|
|
extern crate std_artiq as std;
|
|
extern crate board;
|
|
|
|
mod proto;
|
|
|
|
use std::io::{self, Read, Write};
|
|
#[cfg(has_drtio)]
|
|
use core::slice;
|
|
use proto::*;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Packet {
|
|
EchoRequest,
|
|
EchoReply,
|
|
ResetRequest { phy: bool },
|
|
ResetAck,
|
|
|
|
RtioErrorRequest,
|
|
RtioNoErrorReply,
|
|
RtioErrorSequenceErrorReply { channel: u16 },
|
|
RtioErrorCollisionReply { channel: u16 },
|
|
RtioErrorBusyReply { channel: u16 },
|
|
|
|
MonitorRequest { channel: u16, probe: u8 },
|
|
MonitorReply { value: u32 },
|
|
InjectionRequest { channel: u16, overrd: u8, value: u8 },
|
|
InjectionStatusRequest { channel: u16, overrd: u8 },
|
|
InjectionStatusReply { value: u8 },
|
|
|
|
I2cStartRequest { busno: u8 },
|
|
I2cRestartRequest { busno: u8 },
|
|
I2cStopRequest { busno: u8 },
|
|
I2cWriteRequest { busno: u8, data: u8 },
|
|
I2cWriteReply { succeeded: bool, ack: bool },
|
|
I2cReadRequest { busno: u8, ack: bool },
|
|
I2cReadReply { succeeded: bool, data: u8 },
|
|
I2cBasicReply { succeeded: bool },
|
|
|
|
SpiSetConfigRequest { busno: u8, flags: u8, length: u8, div: u8, cs: u8 },
|
|
SpiWriteRequest { busno: u8, data: u32 },
|
|
SpiReadRequest { busno: u8 },
|
|
SpiReadReply { succeeded: bool, data: u32 },
|
|
SpiBasicReply { succeeded: bool },
|
|
}
|
|
|
|
impl Packet {
|
|
pub fn read_from(reader: &mut Read) -> io::Result<Packet> {
|
|
Ok(match read_u8(reader)? {
|
|
0x00 => Packet::EchoRequest,
|
|
0x01 => Packet::EchoReply,
|
|
0x02 => Packet::ResetRequest {
|
|
phy: read_bool(reader)?
|
|
},
|
|
0x03 => Packet::ResetAck,
|
|
|
|
0x20 => Packet::RtioErrorRequest,
|
|
0x21 => Packet::RtioNoErrorReply,
|
|
0x22 => Packet::RtioErrorSequenceErrorReply {
|
|
channel: read_u16(reader)?
|
|
},
|
|
0x23 => Packet::RtioErrorCollisionReply {
|
|
channel: read_u16(reader)?
|
|
},
|
|
0x24 => Packet::RtioErrorBusyReply {
|
|
channel: read_u16(reader)?
|
|
},
|
|
|
|
0x40 => Packet::MonitorRequest {
|
|
channel: read_u16(reader)?,
|
|
probe: read_u8(reader)?
|
|
},
|
|
0x41 => Packet::MonitorReply {
|
|
value: read_u32(reader)?
|
|
},
|
|
0x50 => Packet::InjectionRequest {
|
|
channel: read_u16(reader)?,
|
|
overrd: read_u8(reader)?,
|
|
value: read_u8(reader)?
|
|
},
|
|
0x51 => Packet::InjectionStatusRequest {
|
|
channel: read_u16(reader)?,
|
|
overrd: read_u8(reader)?
|
|
},
|
|
0x52 => Packet::InjectionStatusReply {
|
|
value: read_u8(reader)?
|
|
},
|
|
|
|
0x80 => Packet::I2cStartRequest {
|
|
busno: read_u8(reader)?
|
|
},
|
|
0x81 => Packet::I2cRestartRequest {
|
|
busno: read_u8(reader)?
|
|
},
|
|
0x82 => Packet::I2cStopRequest {
|
|
busno: read_u8(reader)?
|
|
},
|
|
0x83 => Packet::I2cWriteRequest {
|
|
busno: read_u8(reader)?,
|
|
data: read_u8(reader)?
|
|
},
|
|
0x84 => Packet::I2cWriteReply {
|
|
succeeded: read_bool(reader)?,
|
|
ack: read_bool(reader)?
|
|
},
|
|
0x85 => Packet::I2cReadRequest {
|
|
busno: read_u8(reader)?,
|
|
ack: read_bool(reader)?
|
|
},
|
|
0x86 => Packet::I2cReadReply {
|
|
succeeded: read_bool(reader)?,
|
|
data: read_u8(reader)?
|
|
},
|
|
0x87 => Packet::I2cBasicReply {
|
|
succeeded: read_bool(reader)?
|
|
},
|
|
|
|
0x90 => Packet::SpiSetConfigRequest {
|
|
busno: read_u8(reader)?,
|
|
flags: read_u8(reader)?,
|
|
length: read_u8(reader)?,
|
|
div: read_u8(reader)?,
|
|
cs: read_u8(reader)?
|
|
},
|
|
/* 0x91: was Packet::SpiSetXferRequest */
|
|
0x92 => Packet::SpiWriteRequest {
|
|
busno: read_u8(reader)?,
|
|
data: read_u32(reader)?
|
|
},
|
|
0x93 => Packet::SpiReadRequest {
|
|
busno: read_u8(reader)?
|
|
},
|
|
0x94 => Packet::SpiReadReply {
|
|
succeeded: read_bool(reader)?,
|
|
data: read_u32(reader)?
|
|
},
|
|
0x95 => Packet::SpiBasicReply {
|
|
succeeded: read_bool(reader)?
|
|
},
|
|
|
|
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown packet type"))
|
|
})
|
|
}
|
|
|
|
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
|
match *self {
|
|
Packet::EchoRequest => write_u8(writer, 0x00)?,
|
|
Packet::EchoReply => write_u8(writer, 0x01)?,
|
|
Packet::ResetRequest { phy } => {
|
|
write_u8(writer, 0x02)?;
|
|
write_bool(writer, phy)?;
|
|
},
|
|
Packet::ResetAck => write_u8(writer, 0x03)?,
|
|
|
|
Packet::RtioErrorRequest => write_u8(writer, 0x20)?,
|
|
Packet::RtioNoErrorReply => write_u8(writer, 0x21)?,
|
|
Packet::RtioErrorSequenceErrorReply { channel } => {
|
|
write_u8(writer, 0x22)?;
|
|
write_u16(writer, channel)?;
|
|
},
|
|
Packet::RtioErrorCollisionReply { channel } => {
|
|
write_u8(writer, 0x23)?;
|
|
write_u16(writer, channel)?;
|
|
},
|
|
Packet::RtioErrorBusyReply { channel } => {
|
|
write_u8(writer, 0x24)?;
|
|
write_u16(writer, channel)?;
|
|
},
|
|
|
|
Packet::MonitorRequest { channel, probe } => {
|
|
write_u8(writer, 0x40)?;
|
|
write_u16(writer, channel)?;
|
|
write_u8(writer, probe)?;
|
|
},
|
|
Packet::MonitorReply { value } => {
|
|
write_u8(writer, 0x41)?;
|
|
write_u32(writer, value)?;
|
|
},
|
|
Packet::InjectionRequest { channel, overrd, value } => {
|
|
write_u8(writer, 0x50)?;
|
|
write_u16(writer, channel)?;
|
|
write_u8(writer, overrd)?;
|
|
write_u8(writer, value)?;
|
|
},
|
|
Packet::InjectionStatusRequest { channel, overrd } => {
|
|
write_u8(writer, 0x51)?;
|
|
write_u16(writer, channel)?;
|
|
write_u8(writer, overrd)?;
|
|
},
|
|
Packet::InjectionStatusReply { value } => {
|
|
write_u8(writer, 0x52)?;
|
|
write_u8(writer, value)?;
|
|
},
|
|
|
|
Packet::I2cStartRequest { busno } => {
|
|
write_u8(writer, 0x80)?;
|
|
write_u8(writer, busno)?;
|
|
},
|
|
Packet::I2cRestartRequest { busno } => {
|
|
write_u8(writer, 0x81)?;
|
|
write_u8(writer, busno)?;
|
|
},
|
|
Packet::I2cStopRequest { busno } => {
|
|
write_u8(writer, 0x82)?;
|
|
write_u8(writer, busno)?;
|
|
},
|
|
Packet::I2cWriteRequest { busno, data } => {
|
|
write_u8(writer, 0x83)?;
|
|
write_u8(writer, busno)?;
|
|
write_u8(writer, data)?;
|
|
},
|
|
Packet::I2cWriteReply { succeeded, ack } => {
|
|
write_u8(writer, 0x84)?;
|
|
write_bool(writer, succeeded)?;
|
|
write_bool(writer, ack)?;
|
|
},
|
|
Packet::I2cReadRequest { busno, ack } => {
|
|
write_u8(writer, 0x85)?;
|
|
write_u8(writer, busno)?;
|
|
write_bool(writer, ack)?;
|
|
},
|
|
Packet::I2cReadReply { succeeded, data } => {
|
|
write_u8(writer, 0x86)?;
|
|
write_bool(writer, succeeded)?;
|
|
write_u8(writer, data)?;
|
|
},
|
|
Packet::I2cBasicReply { succeeded } => {
|
|
write_u8(writer, 0x87)?;
|
|
write_bool(writer, 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)?;
|
|
},
|
|
Packet::SpiWriteRequest { busno, data } => {
|
|
write_u8(writer, 0x92)?;
|
|
write_u8(writer, busno)?;
|
|
write_u32(writer, data)?;
|
|
},
|
|
Packet::SpiReadRequest { busno } => {
|
|
write_u8(writer, 0x93)?;
|
|
write_u8(writer, busno)?;
|
|
},
|
|
Packet::SpiReadReply { succeeded, data } => {
|
|
write_u8(writer, 0x94)?;
|
|
write_bool(writer, succeeded)?;
|
|
write_u32(writer, data)?;
|
|
},
|
|
Packet::SpiBasicReply { succeeded } => {
|
|
write_u8(writer, 0x95)?;
|
|
write_bool(writer, succeeded)?;
|
|
},
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(has_drtio)]
|
|
pub mod hw {
|
|
use super::*;
|
|
use std::io::Cursor;
|
|
|
|
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<RxBuffer> {
|
|
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<Option<Packet>> {
|
|
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<u64>) -> io::Result<Packet> {
|
|
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<u8> {
|
|
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<Option<Packet>> {
|
|
let linkno = get_linkno(nodeno)?;
|
|
recv_link(linkno)
|
|
}
|
|
|
|
pub fn recv_timeout(nodeno: u8, timeout_ms: Option<u64>) -> io::Result<Packet> {
|
|
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)
|
|
}
|
|
}
|