diff --git a/src/libboard_artiq/src/cxp_proto.rs b/src/libboard_artiq/src/cxp_proto.rs new file mode 100644 index 0000000..8f4898e --- /dev/null +++ b/src/libboard_artiq/src/cxp_proto.rs @@ -0,0 +1,193 @@ +use core_io::{Error as IoError, Write}; +use crc::crc32; +use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs; +use io::Cursor; +use libboard_zynq::{println, timer::GlobalTimer}; + +use crate::pl::csr; + +const MAX_PACKET: usize = 128; +const DATA_MAXSIZE: usize = /*max size*/MAX_PACKET - /*Tag*/4 - /*Op code & length*/4 - /*addr*/4 - /*CRC*/4 ; + +#[derive(Debug)] +pub enum Error { + BufferError, + LinkDown, +} + +impl From for Error { + fn from(_: IoError) -> Error { + Error::BufferError + } +} + +pub enum Packet { + CtrlRead { + addr: u32, + length: u8, + }, + CtrlWrite { + addr: u32, + length: u8, + data: [u8; DATA_MAXSIZE], + }, // max register size is 8 bytes + CtrlReadWithTag { + tag: u8, + addr: u32, + length: u8, + }, + CtrlWriteWithTag { + tag: u8, + addr: u32, + length: u8, + data: [u8; DATA_MAXSIZE], + }, // max register size is 8 bytes + EventAck { + packet_tag: u8, + }, + TestPacket, +} + +impl Packet { + pub fn write_to(&self, writer: &mut Cursor<&mut [u8]>) -> Result<(), Error> { + // CoaXpress use big endian + match *self { + Packet::CtrlRead { addr, length } => { + writer.write(&[0x02; 4])?; + writer.write(&[0x00, 0x00, 0x00, length])?; + writer.write(&addr.to_be_bytes())?; + } + Packet::CtrlWrite { addr, length, data } => { + writer.write(&[0x02; 4])?; + writer.write(&[0x01, 0x00, 0x00, length])?; + writer.write(&addr.to_be_bytes())?; + writer.write(&data[0..length as usize])?; + } + Packet::CtrlReadWithTag { tag, addr, length } => { + writer.write(&[0x05; 4])?; + writer.write(&[tag; 4])?; + writer.write(&[0x00, 0x00, 0x00, length])?; + writer.write(&addr.to_be_bytes())?; + } + Packet::CtrlWriteWithTag { + tag, + addr, + length, + data, + } => { + writer.write(&[0x05; 4])?; + writer.write(&[tag; 4])?; + writer.write(&[0x01, 0x00, 0x00, length])?; + writer.write(&addr.to_be_bytes())?; + writer.write(&data[0..length as usize])?; + } + Packet::EventAck { packet_tag } => { + writer.write(&[0x08; 4])?; + writer.write(&[packet_tag; 4])?; + } + _ => {} + } + // Section 9.2.2.2 (CXP-001-2021) + // Only Control packet need CRC32 appended in the end of the packet + // CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different + // Also, the calculation does not include the first 4 bytes of packet_type + match *self { + Packet::CtrlRead { .. } + | Packet::CtrlWrite { .. } + | Packet::CtrlReadWithTag { .. } + | Packet::CtrlWriteWithTag { .. } => { + let checksum = crc32::checksum_ieee(&writer.get_ref()[4..writer.position()]); + writer.write(&(!checksum).to_le_bytes())?; + } + _ => {} + } + Ok(()) + } +} + +pub fn send(packet: &Packet) -> Result<(), Error> { + if unsafe { csr::cxp::upconn_tx_enable_read() } == 0 { + Err(Error::LinkDown)? + } + + match *packet { + Packet::TestPacket => send_test_packet(), + _ => send_data_packet(packet), + } +} + +fn send_data_packet(packet: &Packet) -> Result<(), Error> { + let mut buffer: [u8; MAX_PACKET] = [0; MAX_PACKET]; + let mut writer = Cursor::new(&mut buffer[..]); + + packet.write_to(&mut writer)?; + + unsafe { + let len = writer.position(); + csr::cxp::upconn_command_len_write(len as u8); + for data in writer.get_ref()[..len].iter() { + while csr::cxp::upconn_command_writeable_read() == 0 {} + csr::cxp::upconn_command_data_write(*data); + } + } + Ok(()) +} + +fn send_test_packet() -> Result<(), Error> { + unsafe { + while csr::cxp::upconn_tx_busy_read() == 1 {} + csr::cxp::upconn_tx_testmode_en_write(1); + // timer.delay_us(2); + csr::cxp::upconn_testseq_stb_write(1); + while csr::cxp::upconn_testseq_busy_read() == 1 {} + csr::cxp::upconn_tx_testmode_en_write(0); + } + Ok(()) +} + +pub fn write_u32(addr: u32, data: u32) -> Result<(), Error> { + let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE]; + data_slice[..4].clone_from_slice(&data.to_be_bytes()); + send(&Packet::CtrlWrite { + addr, + length: 4, + data: data_slice, + })?; + + Ok(()) +} + +pub fn read_u32(addr: u32) -> Result<(), Error> { + send(&Packet::CtrlRead { addr, length: 4 })?; + + Ok(()) +} + +pub fn write_u64(addr: u32, data: u64) -> Result<(), Error> { + let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE]; + data_slice[..8].clone_from_slice(&data.to_be_bytes()); + send(&Packet::CtrlWrite { + addr, + length: 8, + data: data_slice, + })?; + + Ok(()) +} + +// DEBUG: use only +pub fn print_packet(pak: &[u8]) { + println!("pak = ["); + for i in 0..(pak.len() / 4) { + println!( + "{:#03} {:#04X} {:#04X} {:#04X} {:#04X},", + i + 1, + pak[i * 4], + pak[i * 4 + 1], + pak[i * 4 + 2], + pak[i * 4 + 3] + ) + } + println!("]"); + println!("============================================"); +} diff --git a/src/libboard_artiq/src/lib.rs b/src/libboard_artiq/src/lib.rs index 3ffe429..8ca007f 100644 --- a/src/libboard_artiq/src/lib.rs +++ b/src/libboard_artiq/src/lib.rs @@ -47,6 +47,8 @@ pub mod cxp_downconn; #[cfg(has_cxp)] pub mod cxp_upconn; +pub mod cxp_proto; + pub fn identifier_read(buf: &mut [u8]) -> &str { unsafe { pl::csr::identifier::address_write(0);