1
0
Fork 0

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
This commit is contained in:
morgan 2024-11-27 13:29:59 +08:00
parent 55f2cab99a
commit e30a499b05
1 changed files with 219 additions and 0 deletions

View File

@ -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<ProtoError> 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<Option<DownConnPacket>, 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<DownConnPacket, Error> {
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<u8>) -> 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<u8> = 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<u32, Error> {
// 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<u32, Error> {
let tag: Option<u8> = 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)
}