From e30a499b0550c5b686ef862ce94ce0e895f7d372 Mon Sep 17 00:00:00 2001 From: morgan Date: Wed, 27 Nov 2024 13:29:59 +0800 Subject: [PATCH] cxp comms: Control packet handler ctrl: add buildin delay_ms ctrl: add proper RX Downconn packet handling ctrl: add send, receive & receive with timeout ctrl: add write/read u32 ctrl: add cxp error enum --- src/libboard_artiq/src/cxp_comms.rs | 219 ++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 src/libboard_artiq/src/cxp_comms.rs diff --git a/src/libboard_artiq/src/cxp_comms.rs b/src/libboard_artiq/src/cxp_comms.rs new file mode 100644 index 0000000..bd5d9ba --- /dev/null +++ b/src/libboard_artiq/src/cxp_comms.rs @@ -0,0 +1,219 @@ +use core::slice; + +use byteorder::{ByteOrder, NetworkEndian}; +use embedded_hal::blocking::delay::DelayMs; +use io::Cursor; +use libboard_zynq::{println, time::Milliseconds, timer::GlobalTimer}; +use log::info; + +use crate::{cxp_proto::{print_packet, DownConnPacket, Error as ProtoError, UpConnPacket, DATA_MAXSIZE}, + mem::mem::{CXP_RX_MEM, CXP_TX_MEM}, + pl::{csr, csr::CXP}}; + +#[derive(Debug)] +pub enum Error { + LinkDown, + TimedOut, + UnexpectedReply, + UnsupportedVersion, + UnsupportedSpeed, + WrongTag(u8), + Protocol(ProtoError), +} +impl From for Error { + fn from(value: ProtoError) -> Error { + Error::Protocol(value) + } +} + +const BUF_LEN: usize = 0x800; +const TRANSMISSION_TIMEOUT: u64 = 200; +pub const MASTER_CHANNEL: u8 = 0; + +fn packet_pending(channel: u8) -> bool { + unsafe { (CXP[channel as usize].downconn_pending_packet_read)() == 1 } +} + +fn receive(channel: u8) -> Result, Error> { + if packet_pending(channel) { + let channel = channel as usize; + unsafe { + let read_buffer_ptr = (CXP[channel].downconn_read_ptr_read)() as usize; + println!("buffer ptr = {}", read_buffer_ptr); + let ptr = (CXP_RX_MEM[channel].base + read_buffer_ptr * BUF_LEN) as *mut u32; + + let mut reader = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN)); + let packet_type = (CXP[channel].downconn_packet_type_read)(); + + let packet = DownConnPacket::read_from(&mut reader, packet_type); + println!("{:X?}", packet); + + (CXP[channel].downconn_pending_packet_write)(1); + Ok(Some(packet?)) + } + } else { + Ok(None) + } +} + +fn receive_timeout(channel: u8, timeout_ms: u64) -> Result { + let timer = unsafe { GlobalTimer::get() }; + let limit = timer.get_time() + Milliseconds(timeout_ms); + while timer.get_time() < limit { + match receive(channel)? { + None => (), + Some(packet) => return Ok(packet), + } + } + Err(Error::TimedOut) +} + +fn send_data_packet(channel: u8, packet: &UpConnPacket) -> Result<(), Error> { + let channel = channel as usize; + unsafe { + if csr::cxp_phys::upconn_tx_enable_read() == 0 { + return Err(Error::LinkDown); + } + + while (CXP[channel].upconn_bootstrap_tx_busy_read)() == 1 {} + let ptr = CXP_TX_MEM[0].base as *mut u32; + let mut writer = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN)); + + packet.write_to(&mut writer)?; + // DEBUG: + // println!("TX MEM after writing"); + // print_packet(&writer.get_ref()[0..40]); + + (CXP[channel].upconn_bootstrap_tx_word_len_write)(writer.position() as u16 / 4); + (CXP[channel].upconn_bootstrap_tx_write)(1); + } + + Ok(()) +} + +fn send_test_packet(channel: u8) -> Result<(), Error> { + let channel = channel as usize; + unsafe { + if csr::cxp_phys::upconn_tx_enable_read() == 0 { + return Err(Error::LinkDown); + } + + while (CXP[channel].upconn_bootstrap_tx_busy_read)() == 1 {} + (CXP[channel].upconn_bootstrap_tx_testseq_write)(1); + } + Ok(()) +} + +// +// +// DEBUG: +// +// +pub fn downconn_debug_mem_print(channel: u8) { + unsafe { + let ptr = CXP_RX_MEM[channel as usize].base as *mut u32; + let arr = slice::from_raw_parts_mut(ptr as *mut u8, BUF_LEN * 4); + print_packet(arr); + } +} + +// +// +// CTRL packet +// +// + +static mut TAG: u8 = 0; + +pub fn reset_tag() { + unsafe { TAG = 0 } +} + +pub fn delay_ms(ms: u64) { + // assume timer was initialized successfully + // CXP comms api calls need to have delay + unsafe { GlobalTimer::get() }.delay_ms(ms); +} + +fn check_tag(tag: Option) -> Result<(), Error> { + unsafe { + if tag.is_some() && tag != Some(TAG) { + Err(Error::WrongTag(tag.unwrap())) + } else { + Ok(()) + } + } +} + +fn process_ack_packet(channel: u8, timeout: u64) -> Result<(), Error> { + match receive_timeout(channel, timeout) { + Ok(DownConnPacket::CtrlDelay { tag, time }) => { + check_tag(tag)?; + + info!("delaying by {} ms ....", time); + process_ack_packet(channel, time as u64) + } + Ok(DownConnPacket::CtrlAck { .. }) => Ok(()), + Ok(_) => Err(Error::UnexpectedReply), + Err(e) => Err(e), + } +} + +pub fn write_u32_no_ack(channel: u8, addr: u32, val: u32, with_tag: bool) -> Result<(), Error> { + let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE]; + NetworkEndian::write_u32(&mut data[..4], val); + + let tag: Option = if with_tag { Some(unsafe { TAG }) } else { None }; + send_data_packet( + channel, + &UpConnPacket::CtrlWrite { + tag, + addr, + length: 4, + data, + }, + ) +} + +pub fn write_u32(channel: u8, addr: u32, val: u32, with_tag: bool) -> Result<(), Error> { + write_u32_no_ack(channel, addr, val, with_tag)?; + process_ack_packet(channel, TRANSMISSION_TIMEOUT)?; + + if with_tag { + unsafe { TAG = TAG.wrapping_add(1) }; + }; + Ok(()) +} + +fn process_reply_packet(channel: u8) -> Result { + // TODO: turn this into macro?? + match receive_timeout(channel, TRANSMISSION_TIMEOUT) { + Ok(DownConnPacket::CtrlReply { tag, length, data }) => { + check_tag(tag)?; + + if length != 4 { + return Err(Error::UnexpectedReply); + } + + // TODO: how to deal with tag? + let mut bytes = [0; 4]; + bytes.clone_from_slice(&data[0..4]); + + Ok(NetworkEndian::read_u32(&bytes)) + } + Ok(_) => Err(Error::UnexpectedReply), + Err(e) => Err(e), + } +} + +pub fn read_u32(channel: u8, addr: u32, with_tag: bool) -> Result { + let tag: Option = if with_tag { Some(unsafe { TAG }) } else { None }; + send_data_packet(channel, &UpConnPacket::CtrlRead { tag, addr, length: 4 })?; + + let val = process_reply_packet(channel)?; + + if with_tag { + unsafe { TAG = TAG.wrapping_add(1) }; + }; + Ok(val) +}