2023-02-22 11:02:43 +08:00
|
|
|
use core_io::{Error as IoError, ErrorKind as IoErrorKind};
|
2021-10-06 13:02:28 +08:00
|
|
|
use crc;
|
2023-02-22 11:02:43 +08:00
|
|
|
use io::{proto::{ProtoRead, ProtoWrite},
|
|
|
|
Cursor};
|
|
|
|
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
2022-04-01 14:15:14 +08:00
|
|
|
use libcortex_a9::asm::dmb;
|
2021-10-06 13:02:28 +08:00
|
|
|
|
|
|
|
pub use crate::drtioaux_proto::Packet;
|
2023-02-22 11:02:43 +08:00
|
|
|
use crate::{drtioaux_proto::Error as ProtocolError, mem::mem::DRTIOAUX_MEM, pl::csr::DRTIOAUX};
|
2021-10-06 13:02:28 +08:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
GatewareError,
|
|
|
|
CorruptedPacket,
|
|
|
|
|
|
|
|
LinkDown,
|
|
|
|
TimedOut,
|
|
|
|
UnexpectedReply,
|
|
|
|
|
|
|
|
RoutingError,
|
|
|
|
|
2023-02-22 11:02:43 +08:00
|
|
|
Protocol(ProtocolError),
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ProtocolError> for Error {
|
|
|
|
fn from(value: ProtocolError) -> Error {
|
|
|
|
Error::Protocol(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<IoError> for Error {
|
|
|
|
fn from(value: IoError) -> Error {
|
|
|
|
Error::Protocol(ProtocolError::Io(value))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
(DRTIOAUX[linkno].aux_rx_present_write)(1);
|
|
|
|
(DRTIOAUX[linkno].aux_rx_error_write)(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_rx_error(linkno: u8) -> bool {
|
|
|
|
let linkno = linkno as usize;
|
|
|
|
unsafe {
|
|
|
|
let error = (DRTIOAUX[linkno].aux_rx_error_read)() != 0;
|
|
|
|
if error {
|
|
|
|
(DRTIOAUX[linkno].aux_rx_error_write)(1)
|
|
|
|
}
|
|
|
|
error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 14:15:14 +08:00
|
|
|
pub fn copy_work_buffer(src: *mut u32, dst: *mut u32, len: isize) {
|
2021-11-24 12:12:40 +08:00
|
|
|
// AXI writes must be 4-byte aligned (drtio proto doesn't care for that),
|
2021-11-29 12:27:27 +08:00
|
|
|
// and AXI burst reads/writes are not implemented yet in gateware
|
2021-11-24 12:12:40 +08:00
|
|
|
// thus the need for a work buffer for transmitting and copying it over
|
2021-10-06 13:02:28 +08:00
|
|
|
unsafe {
|
2023-02-22 11:02:43 +08:00
|
|
|
for i in 0..(len / 4) {
|
2021-11-24 12:12:40 +08:00
|
|
|
*dst.offset(i) = *src.offset(i);
|
2022-04-01 14:15:14 +08:00
|
|
|
//data memory barrier to prevent bursts
|
|
|
|
dmb();
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
|
2023-02-22 11:02:43 +08:00
|
|
|
where F: FnOnce(&[u8]) -> Result<T, Error> {
|
2021-10-06 13:02:28 +08:00
|
|
|
let linkidx = linkno as usize;
|
|
|
|
unsafe {
|
|
|
|
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
2024-04-24 17:13:21 +08:00
|
|
|
let read_ptr = (DRTIOAUX[linkidx].aux_read_pointer_read)() as usize;
|
|
|
|
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2 + read_ptr * 0x400) as *mut u32;
|
2021-11-29 12:27:27 +08:00
|
|
|
// work buffer to accomodate axi burst reads
|
2024-04-24 17:12:57 +08:00
|
|
|
// buffer at maximum proto packet size, not maximum gateware supported size
|
|
|
|
// to minimize copying time
|
|
|
|
const LEN: usize = 512;
|
|
|
|
let mut buf: [u8; LEN] = [0; LEN];
|
|
|
|
copy_work_buffer(ptr, buf.as_mut_ptr() as *mut u32, LEN as isize);
|
|
|
|
let result = f(&buf);
|
2021-10-06 13:02:28 +08:00
|
|
|
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
|
|
|
Ok(Some(result?))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
|
|
|
|
if has_rx_error(linkno) {
|
2023-02-22 11:02:43 +08:00
|
|
|
return Err(Error::GatewareError);
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
receive(linkno, |buffer| {
|
|
|
|
if buffer.len() < 8 {
|
2023-02-22 11:02:43 +08:00
|
|
|
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into());
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut reader = Cursor::new(buffer);
|
|
|
|
|
2024-04-24 17:12:57 +08:00
|
|
|
let packet = Packet::read_from(&mut reader)?;
|
|
|
|
let padding = (12 - (reader.position() % 8)) % 8;
|
|
|
|
let checksum_at = reader.position() + padding;
|
2021-10-06 13:02:28 +08:00
|
|
|
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
|
|
|
reader.set_position(checksum_at);
|
|
|
|
if reader.read_u32()? != checksum {
|
2023-02-22 11:02:43 +08:00
|
|
|
return Err(Error::CorruptedPacket);
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
2024-04-24 17:12:57 +08:00
|
|
|
Ok(packet)
|
2021-10-06 13:02:28 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-22 11:02:43 +08:00
|
|
|
pub fn recv_timeout(linkno: u8, timeout_ms: Option<u64>, timer: GlobalTimer) -> Result<Packet, Error> {
|
2021-10-06 13:02:28 +08:00
|
|
|
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
|
|
|
|
let limit = timer.get_time() + timeout_ms;
|
|
|
|
while timer.get_time() < limit {
|
|
|
|
match recv(linkno)? {
|
|
|
|
None => (),
|
|
|
|
Some(packet) => return Ok(packet),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(Error::TimedOut)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
|
2023-02-22 11:02:43 +08:00
|
|
|
where F: FnOnce(&mut [u8]) -> Result<usize, Error> {
|
2021-10-06 13:02:28 +08:00
|
|
|
let linkno = linkno as usize;
|
|
|
|
unsafe {
|
|
|
|
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
|
2022-04-01 14:15:14 +08:00
|
|
|
let ptr = DRTIOAUX_MEM[linkno].base as *mut u32;
|
2021-10-06 13:02:28 +08:00
|
|
|
// work buffer, works with unaligned mem access
|
2023-02-22 11:02:43 +08:00
|
|
|
let mut buf: [u8; 1024] = [0; 1024];
|
2024-04-24 17:13:21 +08:00
|
|
|
let len = f(&mut buf)?;
|
2022-04-01 14:15:14 +08:00
|
|
|
copy_work_buffer(buf.as_mut_ptr() as *mut u32, ptr, len as isize);
|
2021-10-06 13:02:28 +08:00
|
|
|
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
|
|
|
(DRTIOAUX[linkno].aux_tx_write)(1);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
|
|
|
|
transmit(linkno, |buffer| {
|
|
|
|
let mut writer = Cursor::new(buffer);
|
|
|
|
|
|
|
|
packet.write_to(&mut writer)?;
|
2023-02-22 11:02:43 +08:00
|
|
|
|
2022-05-24 15:43:01 +08:00
|
|
|
// Pad till offset 4, insert checksum there
|
|
|
|
let padding = (12 - (writer.position() % 8)) % 8;
|
|
|
|
for _ in 0..padding {
|
|
|
|
writer.write_u8(0)?;
|
2021-10-06 13:02:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
|
|
|
|
writer.write_u32(checksum)?;
|
|
|
|
|
|
|
|
Ok(writer.position())
|
|
|
|
})
|
|
|
|
}
|