parent
35250b3f56
commit
83a5ed69d6
@ -1,123 +0,0 @@ |
||||
use core::cell::Cell; |
||||
use core::fmt::Write; |
||||
use log::{Log, LevelFilter}; |
||||
use log_buffer::LogBuffer; |
||||
use libcortex_a9::mutex::{Mutex, MutexGuard}; |
||||
use libboard_zynq::{println, timer::GlobalTimer}; |
||||
|
||||
pub struct LogBufferRef<'a> { |
||||
buffer: MutexGuard<'a, LogBuffer<&'static mut [u8]>>, |
||||
old_log_level: LevelFilter |
||||
} |
||||
|
||||
impl<'a> LogBufferRef<'a> { |
||||
fn new(buffer: MutexGuard<'a, LogBuffer<&'static mut [u8]>>) -> LogBufferRef<'a> { |
||||
let old_log_level = log::max_level(); |
||||
log::set_max_level(LevelFilter::Off); |
||||
LogBufferRef { buffer, old_log_level } |
||||
} |
||||
|
||||
pub fn is_empty(&self) -> bool { |
||||
self.buffer.is_empty() |
||||
} |
||||
|
||||
pub fn clear(&mut self) { |
||||
self.buffer.clear() |
||||
} |
||||
|
||||
pub fn extract(&mut self) -> &str { |
||||
self.buffer.extract() |
||||
} |
||||
} |
||||
|
||||
impl<'a> Drop for LogBufferRef<'a> { |
||||
fn drop(&mut self) { |
||||
log::set_max_level(self.old_log_level) |
||||
} |
||||
} |
||||
|
||||
pub struct BufferLogger { |
||||
buffer: Mutex<LogBuffer<&'static mut [u8]>>, |
||||
uart_filter: Cell<LevelFilter>, |
||||
buffer_filter: Cell<LevelFilter>, |
||||
} |
||||
|
||||
static mut LOGGER: Option<BufferLogger> = None; |
||||
|
||||
impl BufferLogger { |
||||
pub fn new(buffer: &'static mut [u8]) -> BufferLogger { |
||||
BufferLogger { |
||||
buffer: Mutex::new(LogBuffer::new(buffer)), |
||||
uart_filter: Cell::new(LevelFilter::Info), |
||||
buffer_filter: Cell::new(LevelFilter::Trace), |
||||
} |
||||
} |
||||
|
||||
pub fn register(self) { |
||||
unsafe { |
||||
LOGGER = Some(self); |
||||
log::set_logger(LOGGER.as_ref().unwrap()) |
||||
.expect("global logger can only be initialized once"); |
||||
} |
||||
} |
||||
|
||||
pub unsafe fn get_logger() -> &'static mut Option<BufferLogger> { |
||||
&mut LOGGER |
||||
} |
||||
|
||||
pub fn buffer<'a>(&'a self) -> Option<LogBufferRef<'a>> { |
||||
self.buffer |
||||
.try_lock() |
||||
.map(LogBufferRef::new) |
||||
} |
||||
|
||||
pub fn uart_log_level(&self) -> LevelFilter { |
||||
self.uart_filter.get() |
||||
} |
||||
|
||||
pub fn set_uart_log_level(&self, max_level: LevelFilter) { |
||||
self.uart_filter.set(max_level) |
||||
} |
||||
|
||||
pub fn buffer_log_level(&self) -> LevelFilter { |
||||
self.buffer_filter.get() |
||||
} |
||||
|
||||
/// this should be reserved for mgmt module
|
||||
pub fn set_buffer_log_level(&self, max_level: LevelFilter) { |
||||
self.buffer_filter.set(max_level) |
||||
} |
||||
} |
||||
|
||||
// required for impl Log
|
||||
unsafe impl Sync for BufferLogger {} |
||||
|
||||
impl Log for BufferLogger { |
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool { |
||||
true |
||||
} |
||||
|
||||
fn log(&self, record: &log::Record) { |
||||
if self.enabled(record.metadata()) { |
||||
let timestamp = unsafe { |
||||
GlobalTimer::get() |
||||
}.get_us().0; |
||||
let seconds = timestamp / 1_000_000; |
||||
let micros = timestamp % 1_000_000; |
||||
|
||||
if record.level() <= self.buffer_log_level() { |
||||
let mut buffer = self.buffer.lock(); |
||||
writeln!(buffer, "[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, |
||||
record.level(), record.target(), record.args()).unwrap(); |
||||
} |
||||
|
||||
if record.level() <= self.uart_log_level() { |
||||
println!("[{:6}.{:06}s] {:>5}({}): {}", seconds, micros, |
||||
record.level(), record.target(), record.args()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn flush(&self) { |
||||
} |
||||
} |
@ -1,158 +0,0 @@ |
||||
use core::str::Utf8Error; |
||||
use byteorder::{ByteOrder, NativeEndian}; |
||||
use alloc::vec; |
||||
use alloc::string::String; |
||||
|
||||
use core_io::{Read, Write, Error as IoError}; |
||||
|
||||
#[allow(dead_code)] |
||||
#[derive(Debug, Clone, PartialEq)] |
||||
pub enum ReadStringError<T> { |
||||
Utf8(Utf8Error), |
||||
Other(T) |
||||
} |
||||
|
||||
pub trait ProtoRead { |
||||
type ReadError; |
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>; |
||||
|
||||
#[inline] |
||||
fn read_u8(&mut self) -> Result<u8, Self::ReadError> { |
||||
let mut bytes = [0; 1]; |
||||
self.read_exact(&mut bytes)?; |
||||
Ok(bytes[0]) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_u16(&mut self) -> Result<u16, Self::ReadError> { |
||||
let mut bytes = [0; 2]; |
||||
self.read_exact(&mut bytes)?; |
||||
Ok(NativeEndian::read_u16(&bytes)) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_u32(&mut self) -> Result<u32, Self::ReadError> { |
||||
let mut bytes = [0; 4]; |
||||
self.read_exact(&mut bytes)?; |
||||
Ok(NativeEndian::read_u32(&bytes)) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_u64(&mut self) -> Result<u64, Self::ReadError> { |
||||
let mut bytes = [0; 8]; |
||||
self.read_exact(&mut bytes)?; |
||||
Ok(NativeEndian::read_u64(&bytes)) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_bool(&mut self) -> Result<bool, Self::ReadError> { |
||||
Ok(self.read_u8()? != 0) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_bytes(&mut self) -> Result<::alloc::vec::Vec<u8>, Self::ReadError> { |
||||
let length = self.read_u32()?; |
||||
let mut value = vec![0; length as usize]; |
||||
self.read_exact(&mut value)?; |
||||
Ok(value) |
||||
} |
||||
|
||||
#[inline] |
||||
fn read_string(&mut self) -> Result<::alloc::string::String, ReadStringError<Self::ReadError>> { |
||||
let bytes = self.read_bytes().map_err(ReadStringError::Other)?; |
||||
String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error())) |
||||
} |
||||
} |
||||
|
||||
pub trait ProtoWrite { |
||||
type WriteError; |
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>; |
||||
|
||||
#[inline] |
||||
fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> { |
||||
let bytes = [value; 1]; |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> { |
||||
let bytes = [value as u8; 1]; |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 2]; |
||||
NativeEndian::write_u16(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 2]; |
||||
NativeEndian::write_i16(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 4]; |
||||
NativeEndian::write_u32(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 4]; |
||||
NativeEndian::write_i32(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 8]; |
||||
NativeEndian::write_u64(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> { |
||||
let mut bytes = [0; 8]; |
||||
NativeEndian::write_i64(&mut bytes, value); |
||||
self.write_all(&bytes) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> { |
||||
self.write_u8(value as u8) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> { |
||||
self.write_u32(value.len() as u32)?; |
||||
self.write_all(value) |
||||
} |
||||
|
||||
#[inline] |
||||
fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> { |
||||
self.write_bytes(value.as_bytes()) |
||||
} |
||||
} |
||||
|
||||
impl<T> ProtoRead for T where T: Read + ?Sized { |
||||
type ReadError = IoError; |
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> { |
||||
T::read_exact(self, buf) |
||||
} |
||||
} |
||||
|
||||
impl<T> ProtoWrite for T where T: Write + ?Sized { |
||||
type WriteError = IoError; |
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> { |
||||
T::write_all(self, buf) |
||||
} |
||||
} |
@ -0,0 +1,351 @@ |
||||
use core::cell::RefCell; |
||||
use alloc::rc::Rc; |
||||
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds}; |
||||
use libboard_artiq::{pl::csr, drtio_routing}; |
||||
use libcortex_a9::mutex::Mutex; |
||||
|
||||
|
||||
#[cfg(has_drtio)] |
||||
pub mod drtio { |
||||
use super::*; |
||||
use libboard_artiq::drtioaux_async; |
||||
use libboard_artiq::drtioaux_async::Packet; |
||||
use libboard_artiq::drtioaux::Error; |
||||
use log::{warn, error, info}; |
||||
use embedded_hal::blocking::delay::DelayMs; |
||||
use libasync::{task, delay}; |
||||
|
||||
pub fn startup(aux_mutex: &Rc<Mutex<bool>>, |
||||
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>, |
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, |
||||
timer: GlobalTimer) { |
||||
let aux_mutex = aux_mutex.clone(); |
||||
let routing_table = routing_table.clone(); |
||||
let up_destinations = up_destinations.clone(); |
||||
task::spawn(async move { |
||||
let routing_table = routing_table.borrow(); |
||||
link_thread(&aux_mutex, &routing_table, &up_destinations, timer).await; |
||||
}); |
||||
} |
||||
|
||||
async fn link_rx_up(linkno: u8) -> bool { |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
(csr::DRTIO[linkno].rx_up_read)() == 1 |
||||
} |
||||
} |
||||
|
||||
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> { |
||||
if !link_rx_up(linkno).await { |
||||
return Err("link went down"); |
||||
} |
||||
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await { |
||||
Ok(packet) => return Ok(packet), |
||||
Err(Error::TimedOut) => return Err("timed out"), |
||||
Err(_) => return Err("aux packet error"), |
||||
} |
||||
} |
||||
|
||||
pub async fn aux_transact(aux_mutex: &Mutex<bool>, linkno: u8, request: &Packet, |
||||
timer: GlobalTimer) -> Result<Packet, &'static str> { |
||||
let _lock = aux_mutex.lock(); |
||||
drtioaux_async::send(linkno, request).await.unwrap(); |
||||
recv_aux_timeout(linkno, 200, timer).await |
||||
} |
||||
|
||||
async fn drain_buffer(linkno: u8, draining_time: Milliseconds, timer: GlobalTimer) { |
||||
let max_time = timer.get_time() + draining_time; |
||||
loop { |
||||
if timer.get_time() > max_time { |
||||
return; |
||||
} //could this be cut short?
|
||||
let _ = drtioaux_async::recv(linkno).await; |
||||
} |
||||
} |
||||
|
||||
async fn ping_remote(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> u32 { |
||||
let mut count = 0; |
||||
loop { |
||||
if !link_rx_up(linkno).await { |
||||
return 0 |
||||
} |
||||
count += 1; |
||||
if count > 100 { |
||||
return 0; |
||||
} |
||||
let reply = aux_transact(aux_mutex, linkno, &Packet::EchoRequest, timer).await; |
||||
match reply { |
||||
Ok(Packet::EchoReply) => { |
||||
// make sure receive buffer is drained
|
||||
let draining_time = Milliseconds(200); |
||||
drain_buffer(linkno, draining_time, timer).await; |
||||
return count; |
||||
} |
||||
_ => {} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async fn sync_tsc(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, timer: GlobalTimer) -> Result<(), &'static str> { |
||||
let _lock = aux_mutex.lock(); |
||||
|
||||
unsafe { |
||||
(csr::DRTIO[linkno as usize].set_time_write)(1); |
||||
while (csr::DRTIO[linkno as usize].set_time_read)() == 1 {} |
||||
} |
||||
// TSCAck is the only aux packet that is sent spontaneously
|
||||
// by the satellite, in response to a TSC set on the RT link.
|
||||
let reply = recv_aux_timeout(linkno, 10000, timer).await?; |
||||
if reply == Packet::TSCAck { |
||||
return Ok(()); |
||||
} else { |
||||
return Err("unexpected reply"); |
||||
} |
||||
} |
||||
|
||||
async fn load_routing_table(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, routing_table: &drtio_routing::RoutingTable, |
||||
timer: GlobalTimer) -> Result<(), &'static str> { |
||||
for i in 0..drtio_routing::DEST_COUNT { |
||||
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetPath { |
||||
destination: i as u8, |
||||
hops: routing_table.0[i] |
||||
}, timer).await?; |
||||
if reply != Packet::RoutingAck { |
||||
return Err("unexpected reply"); |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
async fn set_rank(aux_mutex: &Rc<Mutex<bool>>, linkno: u8, rank: u8, timer: GlobalTimer) -> Result<(), &'static str> { |
||||
let reply = aux_transact(aux_mutex, linkno, &Packet::RoutingSetRank { |
||||
rank: rank |
||||
}, timer).await?; |
||||
if reply != Packet::RoutingAck { |
||||
return Err("unexpected reply"); |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
async fn init_buffer_space(destination: u8, linkno: u8) { |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
(csr::DRTIO[linkno].destination_write)(destination); |
||||
(csr::DRTIO[linkno].force_destination_write)(1); |
||||
(csr::DRTIO[linkno].o_get_buffer_space_write)(1); |
||||
while (csr::DRTIO[linkno].o_wait_read)() == 1 {} |
||||
info!("[DEST#{}] buffer space is {}", |
||||
destination, (csr::DRTIO[linkno].o_dbg_buffer_space_read)()); |
||||
(csr::DRTIO[linkno].force_destination_write)(0); |
||||
} |
||||
} |
||||
|
||||
async fn process_unsolicited_aux(aux_mutex: &Rc<Mutex<bool>>, linkno: u8) { |
||||
let _lock = aux_mutex.lock(); |
||||
match drtioaux_async::recv(linkno).await { |
||||
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet), |
||||
Ok(None) => (), |
||||
Err(_) => warn!("[LINK#{}] aux packet error", linkno) |
||||
} |
||||
} |
||||
|
||||
async fn process_local_errors(linkno: u8) { |
||||
let errors; |
||||
let linkidx = linkno as usize; |
||||
unsafe { |
||||
errors = (csr::DRTIO[linkidx].protocol_error_read)(); |
||||
(csr::DRTIO[linkidx].protocol_error_write)(errors); |
||||
} |
||||
if errors != 0 { |
||||
error!("[LINK#{}] error(s) found (0x{:02x}):", linkno, errors); |
||||
if errors & 1 != 0 { |
||||
error!("[LINK#{}] received packet of an unknown type", linkno); |
||||
} |
||||
if errors & 2 != 0 { |
||||
error!("[LINK#{}] received truncated packet", linkno); |
||||
} |
||||
if errors & 4 != 0 { |
||||
error!("[LINK#{}] timeout attempting to get remote buffer space", linkno); |
||||
} |
||||
} |
||||
} |
||||
|
||||
async fn destination_set_up(routing_table: &drtio_routing::RoutingTable, |
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, |
||||
destination: u8, up: bool) { |
||||
let mut up_destinations = up_destinations.borrow_mut(); |
||||
up_destinations[destination as usize] = up; |
||||
if up { |
||||
drtio_routing::interconnect_enable(routing_table, 0, destination); |
||||
info!("[DEST#{}] destination is up", destination); |
||||
} else { |
||||
drtio_routing::interconnect_disable(destination); |
||||
info!("[DEST#{}] destination is down", destination); |
||||
} |
||||
} |
||||
|
||||
async fn destination_up(up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, destination: u8) -> bool { |
||||
let up_destinations = up_destinations.borrow(); |
||||
up_destinations[destination as usize] |
||||
} |
||||
|
||||
async fn destination_survey(aux_mutex: &Rc<Mutex<bool>>, routing_table: &drtio_routing::RoutingTable, |
||||
up_links: &[bool], |
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, |
||||
timer: GlobalTimer) { |
||||
for destination in 0..drtio_routing::DEST_COUNT { |
||||
let hop = routing_table.0[destination][0]; |
||||
let destination = destination as u8; |
||||
|
||||
if hop == 0 { |
||||
/* local RTIO */ |
||||
if !destination_up(up_destinations, destination).await { |
||||
destination_set_up(routing_table, up_destinations, destination, true).await; |
||||
} |
||||
} else if hop as usize <= csr::DRTIO.len() { |
||||
let linkno = hop - 1; |
||||
if destination_up(up_destinations, destination).await { |
||||
if up_links[linkno as usize] { |
||||
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest { |
||||
destination: destination |
||||
}, timer).await; |
||||
match reply { |
||||
Ok(Packet::DestinationDownReply) => |
||||
destination_set_up(routing_table, up_destinations, destination, false).await, |
||||
Ok(Packet::DestinationOkReply) => (), |
||||
Ok(Packet::DestinationSequenceErrorReply { channel }) => |
||||
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel), |
||||
Ok(Packet::DestinationCollisionReply { channel }) => |
||||
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel), |
||||
Ok(Packet::DestinationBusyReply { channel }) => |
||||
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel), |
||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), |
||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e) |
||||
} |
||||
} else { |
||||
destination_set_up(routing_table, up_destinations, destination, false).await; |
||||
} |
||||
} else { |
||||
if up_links[linkno as usize] { |
||||
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest { |
||||
destination: destination |
||||
}, timer).await; |
||||
match reply { |
||||
Ok(Packet::DestinationDownReply) => (), |
||||
Ok(Packet::DestinationOkReply) => { |
||||
destination_set_up(routing_table, up_destinations, destination, true).await; |
||||
init_buffer_space(destination as u8, linkno).await; |
||||
}, |
||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet), |
||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub async fn link_thread(aux_mutex: &Rc<Mutex<bool>>, |
||||
routing_table: &drtio_routing::RoutingTable, |
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, |
||||
timer: GlobalTimer) { |
||||
let mut up_links = [false; csr::DRTIO.len()]; |
||||
loop { |
||||
for linkno in 0..csr::DRTIO.len() { |
||||
let linkno = linkno as u8; |
||||
if up_links[linkno as usize] { |
||||
/* link was previously up */ |
||||
if link_rx_up(linkno).await { |
||||
process_unsolicited_aux(aux_mutex, linkno).await; |
||||
process_local_errors(linkno).await; |
||||
} else { |
||||
info!("[LINK#{}] link is down", linkno); |
||||
up_links[linkno as usize] = false; |
||||
} |
||||
} else { |
||||
/* link was previously down */ |
||||
if link_rx_up(linkno).await { |
||||
info!("[LINK#{}] link RX became up, pinging", linkno); |
||||
let ping_count = ping_remote(aux_mutex, linkno, timer).await; |
||||
if ping_count > 0 { |
||||
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count); |
||||
up_links[linkno as usize] = true; |
||||
if let Err(e) = sync_tsc(aux_mutex, linkno, timer).await { |
||||
error!("[LINK#{}] failed to sync TSC ({})", linkno, e); |
||||
} |
||||
if let Err(e) = load_routing_table(aux_mutex, linkno, routing_table, timer).await { |
||||
error!("[LINK#{}] failed to load routing table ({})", linkno, e); |
||||
} |
||||
if let Err(e) = set_rank(aux_mutex, linkno, 1 as u8, timer).await { |
||||
error!("[LINK#{}] failed to set rank ({})", linkno, e); |
||||
} |
||||
info!("[LINK#{}] link initialization completed", linkno); |
||||
} else { |
||||
error!("[LINK#{}] ping failed", linkno); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
destination_survey(aux_mutex, routing_table, &up_links, up_destinations, timer).await; |
||||
let mut countdown = timer.countdown(); |
||||
delay(&mut countdown, Milliseconds(200)).await; |
||||
} |
||||
} |
||||
|
||||
#[allow(dead_code)] |
||||
pub fn reset(aux_mutex: Rc<Mutex<bool>>, mut timer: GlobalTimer) { |
||||
for linkno in 0..csr::DRTIO.len() { |
||||
unsafe { |
||||
(csr::DRTIO[linkno].reset_write)(1); |
||||
} |
||||
} |
||||
timer.delay_ms(1); |
||||
for linkno in 0..csr::DRTIO.len() { |
||||
unsafe { |
||||
(csr::DRTIO[linkno].reset_write)(0); |
||||
} |
||||
} |
||||
|
||||
for linkno in 0..csr::DRTIO.len() { |
||||
let linkno = linkno as u8; |
||||
if task::block_on(link_rx_up(linkno)) { |
||||
let reply = task::block_on(aux_transact(&aux_mutex, linkno, |
||||
&Packet::ResetRequest, timer)); |
||||
match reply { |
||||
Ok(Packet::ResetAck) => (), |
||||
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno), |
||||
Err(e) => error!("[LINK#{}] reset failed, aux packet error ({})", linkno, e) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[cfg(not(has_drtio))] |
||||
pub mod drtio { |
||||
use super::*; |
||||
|
||||
pub fn startup(_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>, |
||||
_up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _timer: GlobalTimer) {} |
||||
|
||||
#[allow(dead_code)] |
||||
pub fn reset(_aux_mutex: Rc<Mutex<bool>>, mut _timer: GlobalTimer) {} |
||||
} |
||||
|
||||
pub fn startup(aux_mutex: &Rc<Mutex<bool>>, |
||||
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>, |
||||
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||
timer: GlobalTimer) { |
||||
drtio::startup(aux_mutex, routing_table, up_destinations, timer); |
||||
unsafe { |
||||
csr::rtio_core::reset_phy_write(1); |
||||
} |
||||
} |
||||
|
||||
#[allow(dead_code)] |
||||
pub fn reset(aux_mutex: Rc<Mutex<bool>>, timer: GlobalTimer) { |
||||
unsafe { |
||||
csr::rtio_core::reset_write(1); |
||||
} |
||||
drtio::reset(aux_mutex, timer) |
||||
} |
@ -1,273 +0,0 @@ |
||||
use core::result; |
||||
use log::info; |
||||
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds}; |
||||
use embedded_hal::blocking::delay::DelayUs; |
||||
#[cfg(not(si5324_soft_reset))] |
||||
use pl::csr; |
||||
|
||||
type Result<T> = result::Result<T, &'static str>; |
||||
|
||||
const ADDRESS: u8 = 0x68; |
||||
|
||||
#[cfg(not(si5324_soft_reset))] |
||||
fn hard_reset(timer: GlobalTimer) { |
||||
unsafe { csr::si5324_rst_n::out_write(0); } |
||||
timer.delay_us(1_000); |
||||
unsafe { csr::si5324_rst_n::out_write(1); } |
||||
timer.delay_us(10_000); |
||||
} |
||||
|
||||
// NOTE: the logical parameters DO NOT MAP to physical values written
|
||||
// into registers. They have to be mapped; see the datasheet.
|
||||
// DSPLLsim reports the logical parameters in the design summary, not
|
||||
// the physical register values.
|
||||
pub struct FrequencySettings { |
||||
pub n1_hs: u8, |
||||
pub nc1_ls: u32, |
||||
pub n2_hs: u8, |
||||
pub n2_ls: u32, |
||||
pub n31: u32, |
||||
pub n32: u32, |
||||
pub bwsel: u8, |
||||
pub crystal_ref: bool |
||||
} |
||||
|
||||
pub enum Input { |
||||
Ckin1, |
||||
Ckin2, |
||||
} |
||||
|
||||
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> { |
||||
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 { |
||||
return Err("NC1_LS must be 0 or even") |
||||
} |
||||
if settings.nc1_ls > (1 << 20) { |
||||
return Err("NC1_LS is too high") |
||||
} |
||||
if (settings.n2_ls % 2) == 1 { |
||||
return Err("N2_LS must be even") |
||||
} |
||||
if settings.n2_ls > (1 << 20) { |
||||
return Err("N2_LS is too high") |
||||
} |
||||
if settings.n31 > (1 << 19) { |
||||
return Err("N31 is too high") |
||||
} |
||||
if settings.n32 > (1 << 19) { |
||||
return Err("N32 is too high") |
||||
} |
||||
let r = FrequencySettings { |
||||
n1_hs: match settings.n1_hs { |
||||
4 => 0b000, |
||||
5 => 0b001, |
||||
6 => 0b010, |
||||
7 => 0b011, |
||||
8 => 0b100, |
||||
9 => 0b101, |
||||
10 => 0b110, |
||||
11 => 0b111, |
||||
_ => return Err("N1_HS has an invalid value") |
||||
}, |
||||
nc1_ls: settings.nc1_ls - 1, |
||||
n2_hs: match settings.n2_hs { |
||||
4 => 0b000, |
||||
5 => 0b001, |
||||
6 => 0b010, |
||||
7 => 0b011, |
||||
8 => 0b100, |
||||
9 => 0b101, |
||||
10 => 0b110, |
||||
11 => 0b111, |
||||
_ => return Err("N2_HS has an invalid value") |
||||
}, |
||||
n2_ls: settings.n2_ls - 1, |
||||
n31: settings.n31 - 1, |
||||
n32: settings.n32 - 1, |
||||
bwsel: settings.bwsel, |
||||
crystal_ref: settings.crystal_ref |
||||
}; |
||||
Ok(r) |
||||
} |
||||
|
||||
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> { |
||||
i2c.start().unwrap(); |
||||
if !i2c.write(ADDRESS << 1).unwrap() { |
||||
return Err("Si5324 failed to ack write address") |
||||
} |
||||
if !i2c.write(reg).unwrap() { |
||||
return Err("Si5324 failed to ack register") |
||||
} |
||||
if !i2c.write(val).unwrap() { |
||||
return Err("Si5324 failed to ack value") |
||||
} |
||||
i2c.stop().unwrap(); |
||||
Ok(()) |
||||
} |
||||
|
||||
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> { |
||||
i2c.start().unwrap(); |
||||
if !i2c.write(ADDRESS << 1).unwrap() { |
||||
return Err("Si5324 failed to ack write address") |
||||
} |
||||
if !i2c.write(reg).unwrap() { |
||||
return Err("Si5324 failed to ack register") |
||||
} |
||||
i2c.write(val).unwrap(); |
||||
i2c.stop().unwrap(); |
||||
Ok(()) |
||||
} |
||||
|
||||
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> { |
||||
i2c.start().unwrap(); |
||||
if !i2c.write(ADDRESS << 1).unwrap() { |
||||
return Err("Si5324 failed to ack write address") |
||||
} |
||||