DRTIO port - satman firmware #136
|
@ -20,7 +20,7 @@ let
|
||||||
name = "firmware";
|
name = "firmware";
|
||||||
|
|
||||||
src = ./src;
|
src = ./src;
|
||||||
cargoSha256 = "0p9d2j7qp00wpxm48phl5rq26simzry6w0m673lyhrlbzqdz4frb";
|
cargoSha256 = "sha256-uiwESZNwPdVnDkA1n0v1DQHp3rTazDkgIYscVTpgNq0=";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.gnumake
|
pkgs.gnumake
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "libboard_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "libboard_artiq"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
target_zc706 = []
|
||||||
|
target_kasli_soc = []
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
log_buffer = { version = "1.2" }
|
||||||
|
crc = { version = "1.7", default-features = false }
|
||||||
|
core_io = { version = "0.1", features = ["collections"] }
|
||||||
|
embedded-hal = "0.2"
|
||||||
|
nb = "1.0"
|
||||||
|
void = { version = "1", default-features = false }
|
||||||
|
|
||||||
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
|
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
|
||||||
|
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
|
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"}
|
||||||
|
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
|
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate build_zynq;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_zynq::cfg();
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
use libconfig::Config;
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
use crate::pl::csr;
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use log::{warn, info};
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub const DEST_COUNT: usize = 256;
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
pub const DEST_COUNT: usize = 0;
|
||||||
|
pub const MAX_HOPS: usize = 32;
|
||||||
|
pub const INVALID_HOP: u8 = 0xff;
|
||||||
|
|
||||||
|
pub struct RoutingTable(pub [[u8; MAX_HOPS]; DEST_COUNT]);
|
||||||
|
|
||||||
|
impl RoutingTable {
|
||||||
|
// default routing table is for star topology with no repeaters
|
||||||
|
pub fn default_master(default_n_links: usize) -> RoutingTable {
|
||||||
|
let mut ret = RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT]);
|
||||||
|
let n_entries = default_n_links + 1; // include local RTIO
|
||||||
|
for i in 0..n_entries {
|
||||||
|
ret.0[i][0] = i as u8;
|
||||||
|
}
|
||||||
|
for i in 1..n_entries {
|
||||||
|
ret.0[i][1] = 0x00;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// use this by default on satellite, as they receive
|
||||||
|
// the routing table from the master
|
||||||
|
pub fn default_empty() -> RoutingTable {
|
||||||
|
RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RoutingTable {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "RoutingTable {{")?;
|
||||||
|
for i in 0..DEST_COUNT {
|
||||||
|
if self.0[i][0] != INVALID_HOP {
|
||||||
|
write!(f, " {}:", i)?;
|
||||||
|
for j in 0..MAX_HOPS {
|
||||||
|
if self.0[i][j] == INVALID_HOP {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write!(f, " {}", self.0[i][j])?;
|
||||||
|
}
|
||||||
|
write!(f, ";")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, " }}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_routing_table(default_n_links: usize, cfg: &Config) -> RoutingTable {
|
||||||
|
let mut ret = RoutingTable::default_master(default_n_links);
|
||||||
|
if let Ok(data) = cfg.read("routing_table") {
|
||||||
|
if data.len() == DEST_COUNT*MAX_HOPS
|
||||||
|
{
|
||||||
|
for i in 0..DEST_COUNT {
|
||||||
|
for j in 0..MAX_HOPS {
|
||||||
|
ret.0[i][j] = data[i*MAX_HOPS+j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
warn!("length of the routing table is incorrect, using default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
warn!("could not read routing table from configuration, using default");
|
||||||
|
}
|
||||||
|
info!("routing table: {}", ret);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub fn interconnect_enable(routing_table: &RoutingTable, rank: u8, destination: u8) {
|
||||||
|
let hop = routing_table.0[destination as usize][rank as usize];
|
||||||
|
unsafe {
|
||||||
|
csr::routing_table::destination_write(destination);
|
||||||
|
csr::routing_table::hop_write(hop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub fn interconnect_disable(destination: u8) {
|
||||||
|
unsafe {
|
||||||
|
csr::routing_table::destination_write(destination);
|
||||||
|
csr::routing_table::hop_write(INVALID_HOP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub fn interconnect_enable_all(routing_table: &RoutingTable, rank: u8) {
|
||||||
|
for i in 0..DEST_COUNT {
|
||||||
|
interconnect_enable(routing_table, rank, i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
pub fn interconnect_disable_all() {
|
||||||
|
for i in 0..DEST_COUNT {
|
||||||
|
interconnect_disable(i as u8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
use crc;
|
||||||
|
|
||||||
|
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
|
||||||
|
|
||||||
|
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
|
||||||
|
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
|
||||||
|
use crate::mem::mem::DRTIOAUX_MEM;
|
||||||
|
use crate::pl::csr::DRTIOAUX;
|
||||||
|
use crate::drtioaux_proto::Error as ProtocolError;
|
||||||
|
|
||||||
|
pub use crate::drtioaux_proto::Packet;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
GatewareError,
|
||||||
|
CorruptedPacket,
|
||||||
|
|
||||||
|
LinkDown,
|
||||||
|
TimedOut,
|
||||||
|
UnexpectedReply,
|
||||||
|
|
||||||
|
RoutingError,
|
||||||
|
|
||||||
|
Protocol(ProtocolError)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_with_swap(src: *mut u8, dst: *mut u8, len: isize) {
|
||||||
|
// for some reason, everything except checksum arrives
|
||||||
|
// with byte order swapped. and it must be sent as such too.
|
||||||
|
unsafe {
|
||||||
|
for i in (0..(len-4)).step_by(4) {
|
||||||
|
*dst.offset(i) = *src.offset(i+3);
|
||||||
|
*dst.offset(i+1) = *src.offset(i+2);
|
||||||
|
*dst.offset(i+2) = *src.offset(i+1);
|
||||||
|
*dst.offset(i+3) = *src.offset(i);
|
||||||
|
}
|
||||||
|
// checksum untouched
|
||||||
|
// unrolled for performance
|
||||||
|
*dst.offset(len-4) = *src.offset(len-4);
|
||||||
|
*dst.offset(len-3) = *src.offset(len-3);
|
||||||
|
*dst.offset(len-2) = *src.offset(len-2);
|
||||||
|
*dst.offset(len-1) = *src.offset(len-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
|
||||||
|
where F: FnOnce(&[u8]) -> Result<T, Error>
|
||||||
|
{
|
||||||
|
let linkidx = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
||||||
|
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u8;
|
||||||
|
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
|
||||||
|
// work buffer, as byte order will need to be swapped, cannot be in place
|
||||||
|
let mut buf: [u8; 1024] = [0; 1024];
|
||||||
|
copy_with_swap(ptr, buf.as_mut_ptr(), len as isize);
|
||||||
|
let result = f(&buf[0..len]);
|
||||||
|
(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) {
|
||||||
|
return Err(Error::GatewareError)
|
||||||
|
}
|
||||||
|
|
||||||
|
receive(linkno, |buffer| {
|
||||||
|
if buffer.len() < 8 {
|
||||||
|
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(buffer);
|
||||||
|
|
||||||
|
let checksum_at = buffer.len() - 4;
|
||||||
|
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
||||||
|
reader.set_position(checksum_at);
|
||||||
|
if reader.read_u32()? != checksum {
|
||||||
|
return Err(Error::CorruptedPacket)
|
||||||
|
}
|
||||||
|
reader.set_position(0);
|
||||||
|
|
||||||
|
Ok(Packet::read_from(&mut reader)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
|
||||||
|
timer: GlobalTimer) -> Result<Packet, Error>
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
|
||||||
|
{
|
||||||
|
let linkno = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {}
|
||||||
|
let ptr = DRTIOAUX_MEM[linkno].base as *mut u8;
|
||||||
|
let len = DRTIOAUX_MEM[linkno].size / 2;
|
||||||
|
// work buffer, works with unaligned mem access
|
||||||
|
let mut buf: [u8; 1024] = [0; 1024];
|
||||||
|
let len = f(&mut buf[0..len])?;
|
||||||
|
copy_with_swap(buf.as_mut_ptr(), ptr, len as isize);
|
||||||
|
(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)?;
|
||||||
|
|
||||||
|
let padding = 4 - (writer.position() % 4);
|
||||||
|
if padding != 4 {
|
||||||
|
for _ in 0..padding {
|
||||||
|
writer.write_u8(0)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
|
||||||
|
writer.write_u32(checksum)?;
|
||||||
|
|
||||||
|
Ok(writer.position())
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
use crc;
|
||||||
|
|
||||||
|
use core_io::{ErrorKind as IoErrorKind, Error as IoError};
|
||||||
|
use void::Void;
|
||||||
|
use nb;
|
||||||
|
|
||||||
|
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds};
|
||||||
|
use libasync::{task, block_async};
|
||||||
|
|
||||||
|
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor};
|
||||||
|
use crate::mem::mem::DRTIOAUX_MEM;
|
||||||
|
use crate::pl::csr::DRTIOAUX;
|
||||||
|
use crate::drtioaux::{Error, has_rx_error, copy_with_swap};
|
||||||
|
|
||||||
|
pub use crate::drtioaux_proto::Packet;
|
||||||
|
|
||||||
|
pub async 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tx_ready(linkno: usize) -> nb::Result<(), Void> {
|
||||||
|
unsafe {
|
||||||
|
if (DRTIOAUX[linkno].aux_tx_read)() != 0 {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error>
|
||||||
|
where F: FnOnce(&[u8]) -> Result<T, Error>
|
||||||
|
{
|
||||||
|
let linkidx = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 {
|
||||||
|
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u8;
|
||||||
|
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize;
|
||||||
|
// work buffer, as byte order will need to be swapped, cannot be in place
|
||||||
|
let mut buf: [u8; 1024] = [0; 1024];
|
||||||
|
copy_with_swap(ptr, buf.as_mut_ptr(), len as isize);
|
||||||
|
let result = f(&buf[0..len]);
|
||||||
|
(DRTIOAUX[linkidx].aux_rx_present_write)(1);
|
||||||
|
Ok(Some(result?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> {
|
||||||
|
if has_rx_error(linkno) {
|
||||||
|
return Err(Error::GatewareError)
|
||||||
|
}
|
||||||
|
|
||||||
|
receive(linkno, |buffer| {
|
||||||
|
if buffer.len() < 8 {
|
||||||
|
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(buffer);
|
||||||
|
|
||||||
|
let checksum_at = buffer.len() - 4;
|
||||||
|
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
||||||
|
reader.set_position(checksum_at);
|
||||||
|
if reader.read_u32()? != checksum {
|
||||||
|
return Err(Error::CorruptedPacket)
|
||||||
|
}
|
||||||
|
reader.set_position(0);
|
||||||
|
|
||||||
|
Ok(Packet::read_from(&mut reader)?)
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv_timeout(linkno: u8, timeout_ms: Option<u64>,
|
||||||
|
timer: GlobalTimer) -> Result<Packet, Error>
|
||||||
|
{
|
||||||
|
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10));
|
||||||
|
let limit = timer.get_time() + timeout_ms;
|
||||||
|
let mut would_block = false;
|
||||||
|
while timer.get_time() < limit {
|
||||||
|
// to ensure one last time recv would run one last time
|
||||||
|
// in case async would return after timeout
|
||||||
|
if would_block {
|
||||||
|
task::r#yield().await;
|
||||||
|
}
|
||||||
|
match recv(linkno).await? {
|
||||||
|
None => { would_block = true; },
|
||||||
|
Some(packet) => return Ok(packet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::TimedOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transmit<F>(linkno: u8, f: F) -> Result<(), Error>
|
||||||
|
where F: FnOnce(&mut [u8]) -> Result<usize, Error>
|
||||||
|
{
|
||||||
|
let linkno = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
let _ = block_async!(tx_ready(linkno)).await;
|
||||||
|
let ptr = DRTIOAUX_MEM[linkno].base as *mut u8;
|
||||||
|
let len = DRTIOAUX_MEM[linkno].size / 2;
|
||||||
|
// work buffer, works with unaligned mem access
|
||||||
|
let mut buf: [u8; 1024] = [0; 1024];
|
||||||
|
let len = f(&mut buf[0..len])?;
|
||||||
|
copy_with_swap(buf.as_mut_ptr(), ptr, len as isize);
|
||||||
|
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16);
|
||||||
|
(DRTIOAUX[linkno].aux_tx_write)(1);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(linkno: u8, packet: &Packet) -> Result<(), Error> {
|
||||||
|
transmit(linkno, |buffer| {
|
||||||
|
let mut writer = Cursor::new(buffer);
|
||||||
|
|
||||||
|
packet.write_to(&mut writer)?;
|
||||||
|
|
||||||
|
let padding = 4 - (writer.position() % 4);
|
||||||
|
if padding != 4 {
|
||||||
|
for _ in 0..padding {
|
||||||
|
writer.write_u8(0)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
|
||||||
|
writer.write_u32(checksum)?;
|
||||||
|
|
||||||
|
Ok(writer.position())
|
||||||
|
}).await
|
||||||
|
}
|
|
@ -0,0 +1,364 @@
|
||||||
|
use core_io::{Write, Read, Error as IoError};
|
||||||
|
|
||||||
|
use io::proto::{ProtoWrite, ProtoRead};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
UnknownPacket(u8),
|
||||||
|
Io(IoError)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IoError> for Error {
|
||||||
|
fn from(value: IoError) -> Error {
|
||||||
|
Error::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum Packet {
|
||||||
|
EchoRequest,
|
||||||
|
EchoReply,
|
||||||
|
ResetRequest,
|
||||||
|
ResetAck,
|
||||||
|
TSCAck,
|
||||||
|
|
||||||
|
DestinationStatusRequest { destination: u8 },
|
||||||
|
DestinationDownReply,
|
||||||
|
DestinationOkReply,
|
||||||
|
DestinationSequenceErrorReply { channel: u16 },
|
||||||
|
DestinationCollisionReply { channel: u16 },
|
||||||
|
DestinationBusyReply { channel: u16 },
|
||||||
|
|
||||||
|
RoutingSetPath { destination: u8, hops: [u8; 32] },
|
||||||
|
RoutingSetRank { rank: u8 },
|
||||||
|
RoutingAck,
|
||||||
|
|
||||||
|
MonitorRequest { destination: u8, channel: u16, probe: u8 },
|
||||||
|
MonitorReply { value: u32 },
|
||||||
|
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 },
|
||||||
|
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 },
|
||||||
|
InjectionStatusReply { value: u8 },
|
||||||
|
|
||||||
|
I2cStartRequest { destination: u8, busno: u8 },
|
||||||
|
I2cRestartRequest { destination: u8, busno: u8 },
|
||||||
|
I2cStopRequest { destination: u8, busno: u8 },
|
||||||
|
I2cWriteRequest { destination: u8, busno: u8, data: u8 },
|
||||||
|
I2cWriteReply { succeeded: bool, ack: bool },
|
||||||
|
I2cReadRequest { destination: u8, busno: u8, ack: bool },
|
||||||
|
I2cReadReply { succeeded: bool, data: u8 },
|
||||||
|
I2cBasicReply { succeeded: bool },
|
||||||
|
|
||||||
|
SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 },
|
||||||
|
SpiWriteRequest { destination: u8, busno: u8, data: u32 },
|
||||||
|
SpiReadRequest { destination: u8, busno: u8 },
|
||||||
|
SpiReadReply { succeeded: bool, data: u32 },
|
||||||
|
SpiBasicReply { succeeded: bool },
|
||||||
|
|
||||||
|
JdacBasicRequest { destination: u8, dacno: u8, reqno: u8, param: u8 },
|
||||||
|
JdacBasicReply { succeeded: bool, retval: u8 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
pub fn read_from<R>(reader: &mut R) -> Result<Self, Error>
|
||||||
|
where R: Read + ?Sized
|
||||||
|
{
|
||||||
|
Ok(match reader.read_u8()? {
|
||||||
|
0x00 => Packet::EchoRequest,
|
||||||
|
0x01 => Packet::EchoReply,
|
||||||
|
0x02 => Packet::ResetRequest,
|
||||||
|
0x03 => Packet::ResetAck,
|
||||||
|
0x04 => Packet::TSCAck,
|
||||||
|
|
||||||
|
0x20 => Packet::DestinationStatusRequest {
|
||||||
|
destination: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x21 => Packet::DestinationDownReply,
|
||||||
|
0x22 => Packet::DestinationOkReply,
|
||||||
|
0x23 => Packet::DestinationSequenceErrorReply {
|
||||||
|
channel: reader.read_u16()?
|
||||||
|
},
|
||||||
|
0x24 => Packet::DestinationCollisionReply {
|
||||||
|
channel: reader.read_u16()?
|
||||||
|
},
|
||||||
|
0x25 => Packet::DestinationBusyReply {
|
||||||
|
channel: reader.read_u16()?
|
||||||
|
},
|
||||||
|
|
||||||
|
0x30 => {
|
||||||
|
let destination = reader.read_u8()?;
|
||||||
|
let mut hops = [0; 32];
|
||||||
|
reader.read_exact(&mut hops)?;
|
||||||
|
Packet::RoutingSetPath {
|
||||||
|
destination: destination,
|
||||||
|
hops: hops
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0x31 => Packet::RoutingSetRank {
|
||||||
|
rank: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x32 => Packet::RoutingAck,
|
||||||
|
|
||||||
|
0x40 => Packet::MonitorRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
channel: reader.read_u16()?,
|
||||||
|
probe: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x41 => Packet::MonitorReply {
|
||||||
|
value: reader.read_u32()?
|
||||||
|
},
|
||||||
|
0x50 => Packet::InjectionRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
channel: reader.read_u16()?,
|
||||||
|
overrd: reader.read_u8()?,
|
||||||
|
value: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x51 => Packet::InjectionStatusRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
channel: reader.read_u16()?,
|
||||||
|
overrd: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x52 => Packet::InjectionStatusReply {
|
||||||
|
value: reader.read_u8()?
|
||||||
|
},
|
||||||
|
|
||||||
|
0x80 => Packet::I2cStartRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x81 => Packet::I2cRestartRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x82 => Packet::I2cStopRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x83 => Packet::I2cWriteRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?,
|
||||||
|
data: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x84 => Packet::I2cWriteReply {
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
ack: reader.read_bool()?
|
||||||
|
},
|
||||||
|
0x85 => Packet::I2cReadRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?,
|
||||||
|
ack: reader.read_bool()?
|
||||||
|
},
|
||||||
|
0x86 => Packet::I2cReadReply {
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
data: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x87 => Packet::I2cBasicReply {
|
||||||
|
succeeded: reader.read_bool()?
|
||||||
|
},
|
||||||
|
|
||||||
|
0x90 => Packet::SpiSetConfigRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?,
|
||||||
|
flags: reader.read_u8()?,
|
||||||
|
length: reader.read_u8()?,
|
||||||
|
div: reader.read_u8()?,
|
||||||
|
cs: reader.read_u8()?
|
||||||
|
},
|
||||||
|
/* 0x91: was Packet::SpiSetXferRequest */
|
||||||
|
0x92 => Packet::SpiWriteRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?,
|
||||||
|
data: reader.read_u32()?
|
||||||
|
},
|
||||||
|
0x93 => Packet::SpiReadRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
busno: reader.read_u8()?
|
||||||
|
},
|
||||||
|
0x94 => Packet::SpiReadReply {
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
data: reader.read_u32()?
|
||||||
|
},
|
||||||
|
0x95 => Packet::SpiBasicReply {
|
||||||
|
succeeded: reader.read_bool()?
|
||||||
|
},
|
||||||
|
|
||||||
|
0xa0 => Packet::JdacBasicRequest {
|
||||||
|
destination: reader.read_u8()?,
|
||||||
|
dacno: reader.read_u8()?,
|
||||||
|
reqno: reader.read_u8()?,
|
||||||
|
param: reader.read_u8()?,
|
||||||
|
},
|
||||||
|
0xa1 => Packet::JdacBasicReply {
|
||||||
|
succeeded: reader.read_bool()?,
|
||||||
|
retval: reader.read_u8()?
|
||||||
|
},
|
||||||
|
|
||||||
|
ty => return Err(Error::UnknownPacket(ty))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError>
|
||||||
|
where W: Write + ?Sized
|
||||||
|
{
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Packet::EchoRequest =>
|
||||||
|
writer.write_u8(0x00)?,
|
||||||
|
Packet::EchoReply =>
|
||||||
|
writer.write_u8(0x01)?,
|
||||||
|
Packet::ResetRequest =>
|
||||||
|
writer.write_u8(0x02)?,
|
||||||
|
Packet::ResetAck =>
|
||||||
|
writer.write_u8(0x03)?,
|
||||||
|
Packet::TSCAck =>
|
||||||
|
writer.write_u8(0x04)?,
|
||||||
|
|
||||||
|
Packet::DestinationStatusRequest { destination } => {
|
||||||
|
writer.write_u8(0x20)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
},
|
||||||
|
Packet::DestinationDownReply =>
|
||||||
|
writer.write_u8(0x21)?,
|
||||||
|
Packet::DestinationOkReply =>
|
||||||
|
writer.write_u8(0x22)?,
|
||||||
|
Packet::DestinationSequenceErrorReply { channel } => {
|
||||||
|
writer.write_u8(0x23)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
},
|
||||||
|
Packet::DestinationCollisionReply { channel } => {
|
||||||
|
writer.write_u8(0x24)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
},
|
||||||
|
Packet::DestinationBusyReply { channel } => {
|
||||||
|
writer.write_u8(0x25)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
},
|
||||||
|
|
||||||
|
Packet::RoutingSetPath { destination, hops } => {
|
||||||
|
writer.write_u8(0x30)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_all(&hops)?;
|
||||||
|
},
|
||||||
|
Packet::RoutingSetRank { rank } => {
|
||||||
|
writer.write_u8(0x31)?;
|
||||||
|
writer.write_u8(rank)?;
|
||||||
|
},
|
||||||
|
Packet::RoutingAck =>
|
||||||
|
writer.write_u8(0x32)?,
|
||||||
|
|
||||||
|
Packet::MonitorRequest { destination, channel, probe } => {
|
||||||
|
writer.write_u8(0x40)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
writer.write_u8(probe)?;
|
||||||
|
},
|
||||||
|
Packet::MonitorReply { value } => {
|
||||||
|
writer.write_u8(0x41)?;
|
||||||
|
writer.write_u32(value)?;
|
||||||
|
},
|
||||||
|
Packet::InjectionRequest { destination, channel, overrd, value } => {
|
||||||
|
writer.write_u8(0x50)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
writer.write_u8(overrd)?;
|
||||||
|
writer.write_u8(value)?;
|
||||||
|
},
|
||||||
|
Packet::InjectionStatusRequest { destination, channel, overrd } => {
|
||||||
|
writer.write_u8(0x51)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u16(channel)?;
|
||||||
|
writer.write_u8(overrd)?;
|
||||||
|
},
|
||||||
|
Packet::InjectionStatusReply { value } => {
|
||||||
|
writer.write_u8(0x52)?;
|
||||||
|
writer.write_u8(value)?;
|
||||||
|
},
|
||||||
|
|
||||||
|
Packet::I2cStartRequest { destination, busno } => {
|
||||||
|
writer.write_u8(0x80)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
},
|
||||||
|
Packet::I2cRestartRequest { destination, busno } => {
|
||||||
|
writer.write_u8(0x81)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
},
|
||||||
|
Packet::I2cStopRequest { destination, busno } => {
|
||||||
|
writer.write_u8(0x82)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
},
|
||||||
|
Packet::I2cWriteRequest { destination, busno, data } => {
|
||||||
|
writer.write_u8(0x83)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
writer.write_u8(data)?;
|
||||||
|
},
|
||||||
|
Packet::I2cWriteReply { succeeded, ack } => {
|
||||||
|
writer.write_u8(0x84)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
writer.write_bool(ack)?;
|
||||||
|
},
|
||||||
|
Packet::I2cReadRequest { destination, busno, ack } => {
|
||||||
|
writer.write_u8(0x85)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
writer.write_bool(ack)?;
|
||||||
|
},
|
||||||
|
Packet::I2cReadReply { succeeded, data } => {
|
||||||
|
writer.write_u8(0x86)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
writer.write_u8(data)?;
|
||||||
|
},
|
||||||
|
Packet::I2cBasicReply { succeeded } => {
|
||||||
|
writer.write_u8(0x87)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
},
|
||||||
|
|
||||||
|
Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => {
|
||||||
|
writer.write_u8(0x90)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
writer.write_u8(flags)?;
|
||||||
|
writer.write_u8(length)?;
|
||||||
|
writer.write_u8(div)?;
|
||||||
|
writer.write_u8(cs)?;
|
||||||
|
},
|
||||||
|
Packet::SpiWriteRequest { destination, busno, data } => {
|
||||||
|
writer.write_u8(0x92)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
writer.write_u32(data)?;
|
||||||
|
},
|
||||||
|
Packet::SpiReadRequest { destination, busno } => {
|
||||||
|
writer.write_u8(0x93)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(busno)?;
|
||||||
|
},
|
||||||
|
Packet::SpiReadReply { succeeded, data } => {
|
||||||
|
writer.write_u8(0x94)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
writer.write_u32(data)?;
|
||||||
|
},
|
||||||
|
Packet::SpiBasicReply { succeeded } => {
|
||||||
|
writer.write_u8(0x95)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
},
|
||||||
|
|
||||||
|
Packet::JdacBasicRequest { destination, dacno, reqno, param } => {
|
||||||
|
writer.write_u8(0xa0)?;
|
||||||
|
writer.write_u8(destination)?;
|
||||||
|
writer.write_u8(dacno)?;
|
||||||
|
writer.write_u8(reqno)?;
|
||||||
|
writer.write_u8(param)?;
|
||||||
|
}
|
||||||
|
Packet::JdacBasicReply { succeeded, retval } => {
|
||||||
|
writer.write_u8(0xa1)?;
|
||||||
|
writer.write_bool(succeeded)?;
|
||||||
|
writer.write_u8(retval)?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(never_type)]
|
||||||
|
|
||||||
|
extern crate log;
|
||||||
|
extern crate crc;
|
||||||
|
extern crate embedded_hal;
|
||||||
|
extern crate core_io;
|
||||||
|
extern crate io;
|
||||||
|
extern crate libboard_zynq;
|
||||||
|
extern crate libregister;
|
||||||
|
extern crate libconfig;
|
||||||
|
extern crate libcortex_a9;
|
||||||
|
extern crate libasync;
|
||||||
|
extern crate log_buffer;
|
||||||
|
|
||||||
|
#[path = "../../../build/pl.rs"]
|
||||||
|
pub mod pl;
|
||||||
|
pub mod drtioaux_proto;
|
||||||
|
pub mod drtio_routing;
|
||||||
|
pub mod logger;
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
pub mod si5324;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub mod drtioaux;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub mod drtioaux_async;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
#[path = "../../../build/mem.rs"]
|
||||||
|
pub mod mem;
|
||||||
|
|
||||||
|
use core::{cmp, str};
|
||||||
|
use libboard_zynq::slcr;
|
||||||
|
use libregister::RegisterW;
|
||||||
|
|
||||||
|
pub fn identifier_read(buf: &mut [u8]) -> &str {
|
||||||
|
unsafe {
|
||||||
|
pl::csr::identifier::address_write(0);
|
||||||
|
let len = pl::csr::identifier::data_read();
|
||||||
|
let len = cmp::min(len, buf.len() as u8);
|
||||||
|
for i in 0..len {
|
||||||
|
pl::csr::identifier::address_write(1 + i);
|
||||||
|
buf[i as usize] = pl::csr::identifier::data_read();
|
||||||
|
}
|
||||||
|
str::from_utf8_unchecked(&buf[..len as usize])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_gateware() {
|
||||||
|
// Set up PS->PL clocks
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
// As we are touching the mux, the clock may glitch, so reset the PL.
|
||||||
|
slcr.fpga_rst_ctrl.write(
|
||||||
|
slcr::FpgaRstCtrl::zeroed()
|
||||||
|
.fpga0_out_rst(true)
|
||||||
|
.fpga1_out_rst(true)
|
||||||
|
.fpga2_out_rst(true)
|
||||||
|
.fpga3_out_rst(true)
|
||||||
|
);
|
||||||
|
slcr.fpga0_clk_ctrl.write(
|
||||||
|
slcr::Fpga0ClkCtrl::zeroed()
|
||||||
|
.src_sel(slcr::PllSource::IoPll)
|
||||||
|
.divisor0(8)
|
||||||
|
.divisor1(1)
|
||||||
|
);
|
||||||
|
slcr.fpga_rst_ctrl.write(
|
||||||
|
slcr::FpgaRstCtrl::zeroed()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -3,14 +3,14 @@ use log::info;
|
||||||
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds};
|
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds};
|
||||||
use embedded_hal::blocking::delay::DelayUs;
|
use embedded_hal::blocking::delay::DelayUs;
|
||||||
#[cfg(not(si5324_soft_reset))]
|
#[cfg(not(si5324_soft_reset))]
|
||||||
use pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
type Result<T> = result::Result<T, &'static str>;
|
type Result<T> = result::Result<T, &'static str>;
|
||||||
|
|
||||||
const ADDRESS: u8 = 0x68;
|
const ADDRESS: u8 = 0x68;
|
||||||
|
|
||||||
#[cfg(not(si5324_soft_reset))]
|
#[cfg(not(si5324_soft_reset))]
|
||||||
fn hard_reset(timer: GlobalTimer) {
|
fn hard_reset(timer: &mut GlobalTimer) {
|
||||||
unsafe { csr::si5324_rst_n::out_write(0); }
|
unsafe { csr::si5324_rst_n::out_write(0); }
|
||||||
timer.delay_us(1_000);
|
timer.delay_us(1_000);
|
||||||
unsafe { csr::si5324_rst_n::out_write(1); }
|
unsafe { csr::si5324_rst_n::out_write(1); }
|
||||||
|
@ -104,6 +104,7 @@ fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||||
i2c.start().unwrap();
|
i2c.start().unwrap();
|
||||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||||
|
@ -146,8 +147,9 @@ fn ident(i2c: &mut I2c) -> Result<u16> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(si5324_soft_reset)]
|
#[cfg(si5324_soft_reset)]
|
||||||
fn soft_reset(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
write_no_ack_value(i2c, 136, read(i2c, 136)? | 0x80)?;
|
let val = read(i2c, 136)?;
|
||||||
|
write_no_ack_value(i2c, 136, val | 0x80)?;
|
||||||
timer.delay_us(10_000);
|
timer.delay_us(10_000);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -167,7 +169,7 @@ fn locked(i2c: &mut I2c) -> Result<bool> {
|
||||||
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
|
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn monitor_lock(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
fn monitor_lock(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
info!("waiting for Si5324 lock...");
|
info!("waiting for Si5324 lock...");
|
||||||
let timeout = timer.get_time() + Milliseconds(20_000);
|
let timeout = timer.get_time() + Milliseconds(20_000);
|
||||||
while !locked(i2c)? {
|
while !locked(i2c)? {
|
||||||
|
@ -180,7 +182,7 @@ fn monitor_lock(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
#[cfg(not(si5324_soft_reset))]
|
#[cfg(not(si5324_soft_reset))]
|
||||||
hard_reset(timer);
|
hard_reset(timer);
|
||||||
|
|
||||||
|
@ -189,6 +191,10 @@ fn init(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
||||||
i2c.pca9548_select(0x70, 0)?;
|
i2c.pca9548_select(0x70, 0)?;
|
||||||
i2c.pca9548_select(0x71, 1 << 3)?;
|
i2c.pca9548_select(0x71, 1 << 3)?;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
{
|
||||||
|
i2c.pca9548_select(0x74, 1 << 4)?;
|
||||||
|
}
|
||||||
|
|
||||||
if ident(i2c)? != 0x0182 {
|
if ident(i2c)? != 0x0182 {
|
||||||
return Err("Si5324 does not have expected product number");
|
return Err("Si5324 does not have expected product number");
|
||||||
|
@ -199,7 +205,7 @@ fn init(i2c: &mut I2c, timer: GlobalTimer) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bypass(i2c: &mut I2c, input: Input, timer: GlobalTimer) -> Result<()> {
|
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
let cksel_reg = match input {
|
let cksel_reg = match input {
|
||||||
Input::Ckin1 => 0b00,
|
Input::Ckin1 => 0b00,
|
||||||
Input::Ckin2 => 0b01,
|
Input::Ckin2 => 0b01,
|
||||||
|
@ -213,7 +219,7 @@ pub fn bypass(i2c: &mut I2c, input: Input, timer: GlobalTimer) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: GlobalTimer) -> Result<()> {
|
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
let s = map_frequency_settings(settings)?;
|
let s = map_frequency_settings(settings)?;
|
||||||
let cksel_reg = match input {
|
let cksel_reg = match input {
|
||||||
Input::Ckin1 => 0b00,
|
Input::Ckin1 => 0b00,
|
||||||
|
@ -259,7 +265,7 @@ pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: G
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_input(i2c: &mut I2c, input: Input, timer: GlobalTimer) -> Result<()> {
|
pub fn select_input(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
let cksel_reg = match input {
|
let cksel_reg = match input {
|
||||||
Input::Ckin1 => 0b00,
|
Input::Ckin1 => 0b00,
|
||||||
Input::Ckin2 => 0b01,
|
Input::Ckin2 => 0b01,
|
||||||
|
@ -270,4 +276,78 @@ pub fn select_input(i2c: &mut I2c, input: Input, timer: GlobalTimer) -> Result<(
|
||||||
}
|
}
|
||||||
monitor_lock(i2c, timer)?;
|
monitor_lock(i2c, timer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_siphaser)]
|
||||||
|
pub mod siphaser {
|
||||||
|
use super::*;
|
||||||
|
use crate::pl::csr;
|
||||||
|
|
||||||
|
pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let val = read(i2c, 3)?;
|
||||||
|
write(i2c, 3, (val & 0xdf) | (1 << 5))?; // DHOLD=1
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
let val = read(i2c, 3)?;
|
||||||
|
write(i2c, 3, (val & 0xdf) | (0 << 5))?; // DHOLD=0
|
||||||
|
monitor_lock(i2c, timer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phase_shift(direction: u8, timer: &mut GlobalTimer) {
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::phase_shift_write(direction);
|
||||||
|
while csr::siphaser::phase_shift_done_read() == 0 {}
|
||||||
|
}
|
||||||
|
// wait for the Si5324 loop to stabilize
|
||||||
|
timer.delay_us(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_error(timer: &mut GlobalTimer) -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::error_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_us(5_000);
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::error_read() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32> {
|
||||||
|
let mut nshifts = 0;
|
||||||
|
|
||||||
|
let mut previous = has_error(timer);
|
||||||
|
loop {
|
||||||
|
phase_shift(1, timer);
|
||||||
|
nshifts += 1;
|
||||||
|
let current = has_error(timer);
|
||||||
|
if previous != target && current == target {
|
||||||
|
return Ok(nshifts);
|
||||||
|
}
|
||||||
|
if nshifts > 5000 {
|
||||||
|
return Err("failed to find timing error edge");
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let jitter_margin = 32;
|
||||||
|
let lead = find_edge(false, timer)?;
|
||||||
|
for _ in 0..jitter_margin {
|
||||||
|
phase_shift(1, timer);
|
||||||
|
}
|
||||||
|
let width = find_edge(true, timer)? + jitter_margin;
|
||||||
|
// width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter
|
||||||
|
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));
|
||||||
|
|
||||||
|
// Apply reverse phase shift for half the width to get into the
|
||||||
|
// middle of the working region.
|
||||||
|
for _ in 0..width/2 {
|
||||||
|
phase_shift(0, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "build_zynq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "build_zynq"
|
||||||
|
path = "lib.rs"
|
|
@ -0,0 +1,13 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
|
pub fn cfg() {
|
||||||
|
// Handle rustc-cfg file
|
||||||
|
let cfg_path = "../../build/rustc-cfg";
|
||||||
|
println!("cargo:rerun-if-changed={}", cfg_path);
|
||||||
|
|
||||||
|
let f = BufReader::new(File::open(cfg_path).unwrap());
|
||||||
|
for line in f.lines() {
|
||||||
|
println!("cargo:rustc-cfg={}", line.unwrap());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "io"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "io"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
core_io = { version = "0.1", features = ["collections"] }
|
||||||
|
byteorder = { version = "1.0", default-features = false, optional = true }
|
||||||
|
|
||||||
|
libsupport_zynq = { default-features = false, features = ["alloc_core"], git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = []
|
|
@ -0,0 +1,81 @@
|
||||||
|
use core_io::{Read, Write, Error as IoError};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Cursor<T> {
|
||||||
|
inner: T,
|
||||||
|
pos: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cursor<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(inner: T) -> Cursor<T> {
|
||||||
|
Cursor { inner, pos: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_ref(&self) -> &T {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn position(&self) -> usize {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_position(&mut self, pos: usize) {
|
||||||
|
self.pos = pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<[u8]>> Read for Cursor<T> {
|
||||||
|
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
|
||||||
|
let data = &self.inner.as_ref()[self.pos..];
|
||||||
|
let len = buf.len().min(data.len());
|
||||||
|
buf[..len].copy_from_slice(&data[..len]);
|
||||||
|
self.pos += len;
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Cursor<&mut [u8]> {
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
||||||
|
let data = &mut self.inner[self.pos..];
|
||||||
|
let len = buf.len().min(data.len());
|
||||||
|
data[..len].copy_from_slice(&buf[..len]);
|
||||||
|
self.pos += len;
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<(), IoError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl Write for Cursor<::alloc::Vec<u8>> {
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
|
||||||
|
self.inner.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<(), IoError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(never_type)]
|
||||||
|
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate core_io;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[macro_use]
|
||||||
|
use alloc;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
|
pub mod cursor;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
pub mod proto;
|
||||||
|
|
||||||
|
pub use cursor::Cursor;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
pub use proto::{ProtoRead, ProtoWrite};
|
||||||
|
#[cfg(all(feature = "byteorder", feature = "alloc"))]
|
||||||
|
pub use proto::ReadStringError;
|
|
@ -6,10 +6,13 @@ authors = ["M-Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
||||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
||||||
default = ["target_zc706"]
|
default = ["target_zc706"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
|
@ -37,3 +40,5 @@ dyld = { path = "../libdyld" }
|
||||||
dwarf = { path = "../libdwarf" }
|
dwarf = { path = "../libdwarf" }
|
||||||
unwind = { path = "../libunwind" }
|
unwind = { path = "../libunwind" }
|
||||||
libc = { path = "../libc" }
|
libc = { path = "../libc" }
|
||||||
|
io = { path = "../libio" }
|
||||||
|
libboard_artiq = { path = "../libboard_artiq" }
|
|
@ -1,9 +1,10 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
extern crate build_zynq;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Put the linker script somewhere the linker can find it
|
// Put the linker script somewhere the linker can find it
|
||||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
@ -16,13 +17,5 @@ fn main() {
|
||||||
// Only re-run the build script when link.x is changed,
|
// Only re-run the build script when link.x is changed,
|
||||||
// instead of when any part of the source code changes.
|
// instead of when any part of the source code changes.
|
||||||
println!("cargo:rerun-if-changed=link.x");
|
println!("cargo:rerun-if-changed=link.x");
|
||||||
|
build_zynq::cfg();
|
||||||
// Handle rustc-cfg file
|
|
||||||
let cfg_path = "../../build/rustc-cfg";
|
|
||||||
println!("cargo:rerun-if-changed={}", cfg_path);
|
|
||||||
|
|
||||||
let f = BufReader::new(File::open(cfg_path).unwrap());
|
|
||||||
for line in f.lines() {
|
|
||||||
println!("cargo:rustc-cfg={}", line.unwrap());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Re
|
||||||
use futures::{select_biased, future::FutureExt};
|
use futures::{select_biased, future::FutureExt};
|
||||||
use libasync::{smoltcp::{Sockets, TcpStream}, task};
|
use libasync::{smoltcp::{Sockets, TcpStream}, task};
|
||||||
use libconfig::{Config, net_settings};
|
use libconfig::{Config, net_settings};
|
||||||
|
use libboard_artiq::drtio_routing;
|
||||||
|
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use crate::kernel;
|
use crate::kernel;
|
||||||
|
@ -28,7 +29,9 @@ use crate::rpc;
|
||||||
use crate::moninj;
|
use crate::moninj;
|
||||||
use crate::mgmt;
|
use crate::mgmt;
|
||||||
use crate::analyzer;
|
use crate::analyzer;
|
||||||
|
use crate::rtio_mgt;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use crate::pl;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -387,8 +390,21 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
|
|
||||||
Sockets::init(32);
|
Sockets::init(32);
|
||||||
|
|
||||||
|
// before, mutex was on io, but now that io isn't used...?
|
||||||
|
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
let drtio_routing_table = Rc::new(RefCell::new(
|
||||||
|
drtio_routing::config_routing_table(pl::csr::DRTIO.len(), &cfg)));
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::RoutingTable::default_empty()));
|
||||||
|
let up_destinations = Rc::new(RefCell::new([false; drtio_routing::DEST_COUNT]));
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
drtio_routing::interconnect_disable_all();
|
||||||
|
|
||||||
|
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
|
||||||
|
|
||||||
analyzer::start();
|
analyzer::start();
|
||||||
moninj::start(timer);
|
moninj::start(timer, aux_mutex, drtio_routing_table);
|
||||||
|
|
||||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
||||||
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
||||||
|
|
|
@ -11,82 +11,43 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use core::{cmp, str};
|
|
||||||
use log::{info, warn, error};
|
use log::{info, warn, error};
|
||||||
|
|
||||||
use libboard_zynq::{timer::GlobalTimer, mpcore, gic, slcr};
|
use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
|
||||||
use libasync::{task, block_async};
|
use libasync::{task, block_async};
|
||||||
use libsupport_zynq::ram;
|
use libsupport_zynq::ram;
|
||||||
use nb;
|
use nb;
|
||||||
use void::Void;
|
use void::Void;
|
||||||
use embedded_hal::blocking::delay::DelayMs;
|
use embedded_hal::blocking::delay::DelayMs;
|
||||||
use libconfig::Config;
|
use libconfig::Config;
|
||||||
use libregister::RegisterW;
|
|
||||||
use libcortex_a9::l2c::enable_l2_cache;
|
use libcortex_a9::l2c::enable_l2_cache;
|
||||||
|
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
use libboard_artiq::si5324;
|
||||||
|
|
||||||
mod proto_core_io;
|
|
||||||
mod proto_async;
|
mod proto_async;
|
||||||
mod comms;
|
mod comms;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
#[path = "../../../build/pl.rs"]
|
|
||||||
mod pl;
|
|
||||||
#[cfg(ki_impl = "csr")]
|
#[cfg(ki_impl = "csr")]
|
||||||
#[path = "rtio_csr.rs"]
|
#[path = "rtio_csr.rs"]
|
||||||
mod rtio;
|
mod rtio;
|
||||||
#[cfg(ki_impl = "acp")]
|
#[cfg(ki_impl = "acp")]
|
||||||
#[path = "rtio_acp.rs"]
|
#[path = "rtio_acp.rs"]
|
||||||
mod rtio;
|
mod rtio;
|
||||||
|
mod rtio_mgt;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
mod moninj;
|
mod moninj;
|
||||||
mod eh_artiq;
|
mod eh_artiq;
|
||||||
mod panic;
|
mod panic;
|
||||||
mod logger;
|
|
||||||
mod mgmt;
|
mod mgmt;
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
mod irq;
|
mod irq;
|
||||||
mod i2c;
|
mod i2c;
|
||||||
#[cfg(has_si5324)]
|
|
||||||
mod si5324;
|
|
||||||
|
|
||||||
fn init_gateware() {
|
fn init_rtio(timer: &mut GlobalTimer, _cfg: &Config) {
|
||||||
// Set up PS->PL clocks
|
#[cfg(has_rtio_crg_clock_sel)]
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
// As we are touching the mux, the clock may glitch, so reset the PL.
|
|
||||||
slcr.fpga_rst_ctrl.write(
|
|
||||||
slcr::FpgaRstCtrl::zeroed()
|
|
||||||
.fpga0_out_rst(true)
|
|
||||||
.fpga1_out_rst(true)
|
|
||||||
.fpga2_out_rst(true)
|
|
||||||
.fpga3_out_rst(true)
|
|
||||||
);
|
|
||||||
slcr.fpga0_clk_ctrl.write(
|
|
||||||
slcr::Fpga0ClkCtrl::zeroed()
|
|
||||||
.src_sel(slcr::PllSource::IoPll)
|
|
||||||
.divisor0(8)
|
|
||||||
.divisor1(1)
|
|
||||||
);
|
|
||||||
slcr.fpga_rst_ctrl.write(
|
|
||||||
slcr::FpgaRstCtrl::zeroed()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn identifier_read(buf: &mut [u8]) -> &str {
|
|
||||||
unsafe {
|
|
||||||
pl::csr::identifier::address_write(0);
|
|
||||||
let len = pl::csr::identifier::data_read();
|
|
||||||
let len = cmp::min(len, buf.len() as u8);
|
|
||||||
for i in 0..len {
|
|
||||||
pl::csr::identifier::address_write(1 + i);
|
|
||||||
buf[i as usize] = pl::csr::identifier::data_read();
|
|
||||||
}
|
|
||||||
str::from_utf8_unchecked(&buf[..len as usize])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_rtio(timer: &mut GlobalTimer, cfg: &Config) {
|
|
||||||
let clock_sel =
|
let clock_sel =
|
||||||
if let Ok(rtioclk) = cfg.read_str("rtioclk") {
|
if let Ok(rtioclk) = _cfg.read_str("rtioclk") {
|
||||||
match rtioclk.as_ref() {
|
match rtioclk.as_ref() {
|
||||||
"internal" => {
|
"internal" => {
|
||||||
info!("using internal RTIO clock");
|
info!("using internal RTIO clock");
|
||||||
|
@ -130,6 +91,19 @@ fn init_rtio(timer: &mut GlobalTimer, cfg: &Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
fn init_drtio(timer: &mut GlobalTimer)
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
pl::csr::drtio_transceiver::stable_clkin_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_ms(2); // wait for CPLL/QPLL lock
|
||||||
|
unsafe {
|
||||||
|
pl::csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if pl::csr::rtio_core::async_error_read() != 0 {
|
if pl::csr::rtio_core::async_error_read() != 0 {
|
||||||
|
@ -201,7 +175,7 @@ pub fn main_core0() {
|
||||||
i2c::init();
|
i2c::init();
|
||||||
#[cfg(has_si5324)]
|
#[cfg(has_si5324)]
|
||||||
si5324::setup(unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() },
|
si5324::setup(unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() },
|
||||||
&SI5324_SETTINGS, si5324::Input::Ckin2, timer).expect("cannot initialize Si5324");
|
&SI5324_SETTINGS, si5324::Input::Ckin2, &mut timer).expect("cannot initialize Si5324");
|
||||||
|
|
||||||
let cfg = match Config::new() {
|
let cfg = match Config::new() {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
|
@ -211,6 +185,9 @@ pub fn main_core0() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
init_drtio(&mut timer);
|
||||||
|
|
||||||
init_rtio(&mut timer, &cfg);
|
init_rtio(&mut timer, &cfg);
|
||||||
task::spawn(report_async_rtio_errors());
|
task::spawn(report_async_rtio_errors());
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use core::cell::RefCell;
|
||||||
use alloc::{rc::Rc, vec::Vec, string::String};
|
use alloc::{rc::Rc, vec::Vec, string::String};
|
||||||
use log::{self, info, debug, warn, error, LevelFilter};
|
use log::{self, info, debug, warn, error, LevelFilter};
|
||||||
|
|
||||||
use crate::logger::{BufferLogger, LogBufferRef};
|
use libboard_artiq::logger::{BufferLogger, LogBufferRef};
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
use core::fmt;
|
use core::{fmt, cell::RefCell};
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::{collections::BTreeMap, rc::Rc};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use void::Void;
|
use void::Void;
|
||||||
|
|
||||||
|
use libboard_artiq::drtio_routing;
|
||||||
|
|
||||||
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
|
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
|
||||||
use libasync::{task, smoltcp::TcpStream, block_async, nb};
|
use libasync::{task, smoltcp::TcpStream, block_async, nb};
|
||||||
|
use libcortex_a9::mutex::Mutex;
|
||||||
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
use futures::{pin_mut, select_biased, FutureExt};
|
use futures::{pin_mut, select_biased, FutureExt};
|
||||||
|
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use crate::pl::csr;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -19,7 +21,6 @@ pub enum Error {
|
||||||
NetworkError(smoltcp::Error),
|
NetworkError(smoltcp::Error),
|
||||||
UnexpectedPattern,
|
UnexpectedPattern,
|
||||||
UnrecognizedPacket,
|
UnrecognizedPacket,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
@ -54,32 +55,109 @@ enum DeviceMessage {
|
||||||
InjectionStatus = 1
|
InjectionStatus = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_probe(channel: i32, probe: i8) -> i32 {
|
#[cfg(has_drtio)]
|
||||||
unsafe {
|
mod remote_moninj {
|
||||||
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
use super::*;
|
||||||
csr::rtio_moninj::mon_probe_sel_write(probe as _);
|
use libboard_artiq::drtioaux;
|
||||||
csr::rtio_moninj::mon_value_update_write(1);
|
use crate::rtio_mgt::drtio;
|
||||||
csr::rtio_moninj::mon_value_read() as i32
|
use log::error;
|
||||||
|
|
||||||
|
pub fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i32 {
|
||||||
|
let reply = task::block_on(drtio::aux_transact(aux_mutex, linkno, &drtioaux::Packet::MonitorRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
probe: probe as _},
|
||||||
|
timer));
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::MonitorReply { value }) => return value as i32,
|
||||||
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inject(aux_mutex: &Rc<Mutex<bool>>, _timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
|
||||||
|
let _lock = aux_mutex.lock();
|
||||||
|
drtioaux::send(linkno, &drtioaux::Packet::InjectionRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
overrd: overrd as _,
|
||||||
|
value: value as _
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
|
||||||
|
let reply = task::block_on(drtio::aux_transact(aux_mutex,
|
||||||
|
linkno,
|
||||||
|
&drtioaux::Packet::InjectionStatusRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
overrd: overrd as _},
|
||||||
|
timer));
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::InjectionStatusReply { value }) => return value as i8,
|
||||||
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inject(channel: i32, overrd: i8, value: i8) {
|
mod local_moninj {
|
||||||
unsafe {
|
use libboard_artiq::pl::csr;
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
pub fn read_probe(channel: i32, probe: i8) -> i32 {
|
||||||
csr::rtio_moninj::inj_value_write(value as _);
|
unsafe {
|
||||||
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::mon_probe_sel_write(probe as _);
|
||||||
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
|
csr::rtio_moninj::mon_value_read() as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inject(channel: i32, overrd: i8, value: i8) {
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
||||||
|
csr::rtio_moninj::inj_value_write(value as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_injection_status(channel: i32, overrd: i8) -> i8 {
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
||||||
|
csr::rtio_moninj::inj_value_read() as i8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_injection_status(channel: i32, overrd: i8) -> i8 {
|
#[cfg(has_drtio)]
|
||||||
unsafe {
|
macro_rules! dispatch {
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
let destination = ($channel >> 16) as u8;
|
||||||
csr::rtio_moninj::inj_value_read() as i8
|
let channel = $channel;
|
||||||
}
|
let routing_table = $routing_table.borrow_mut();
|
||||||
|
let hop = routing_table.0[destination as usize][0];
|
||||||
|
if hop == 0 {
|
||||||
|
local_moninj::$func(channel.into(), $($param, )*)
|
||||||
|
} else {
|
||||||
|
let linkno = hop - 1 as u8;
|
||||||
|
remote_moninj::$func($aux_mutex, $timer, linkno, destination, channel, $($param, )*)
|
||||||
|
}
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()> {
|
#[cfg(not(has_drtio))]
|
||||||
|
macro_rules! dispatch {
|
||||||
|
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||||
|
let channel = $channel as u16;
|
||||||
|
local_moninj::$func(channel.into(), $($param, )*)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
|
||||||
|
_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>) -> Result<()> {
|
||||||
if !expect(&stream, b"ARTIQ moninj\n").await? {
|
if !expect(&stream, b"ARTIQ moninj\n").await? {
|
||||||
return Err(Error::UnexpectedPattern);
|
return Err(Error::UnexpectedPattern);
|
||||||
}
|
}
|
||||||
|
@ -135,13 +213,13 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
let channel = read_i32(&stream).await?;
|
let channel = read_i32(&stream).await?;
|
||||||
let overrd = read_i8(&stream).await?;
|
let overrd = read_i8(&stream).await?;
|
||||||
let value = read_i8(&stream).await?;
|
let value = read_i8(&stream).await?;
|
||||||
inject(channel, overrd, value);
|
dispatch!(timer, _aux_mutex, _routing_table, channel, inject, overrd, value);
|
||||||
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
|
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
|
||||||
},
|
},
|
||||||
HostMessage::GetInjectionStatus => {
|
HostMessage::GetInjectionStatus => {
|
||||||
let channel = read_i32(&stream).await?;
|
let channel = read_i32(&stream).await?;
|
||||||
let overrd = read_i8(&stream).await?;
|
let overrd = read_i8(&stream).await?;
|
||||||
let value = read_injection_status(channel, overrd);
|
let value = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||||
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
write_i8(&stream, overrd).await?;
|
write_i8(&stream, overrd).await?;
|
||||||
|
@ -151,7 +229,7 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
},
|
},
|
||||||
_ = timeout_f => {
|
_ = timeout_f => {
|
||||||
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
||||||
let current = read_probe(channel, probe);
|
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_probe, probe);
|
||||||
if previous.is_none() || previous.unwrap() != current {
|
if previous.is_none() || previous.unwrap() != current {
|
||||||
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
|
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
|
@ -161,7 +239,7 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
|
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
|
||||||
let current = read_injection_status(channel, overrd);
|
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||||
if previous.is_none() || previous.unwrap() != current {
|
if previous.is_none() || previous.unwrap() != current {
|
||||||
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
|
@ -176,13 +254,15 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(timer: GlobalTimer) {
|
pub fn start(timer: GlobalTimer, aux_mutex: Rc<Mutex<bool>>, routing_table: Rc<RefCell<drtio_routing::RoutingTable>>) {
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
let aux_mutex = aux_mutex.clone();
|
||||||
|
let routing_table = routing_table.clone();
|
||||||
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
|
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
info!("received connection");
|
info!("received connection");
|
||||||
let result = handle_connection(&stream, timer).await;
|
let result = handle_connection(&stream, timer, &aux_mutex, &routing_table).await;
|
||||||
match result {
|
match result {
|
||||||
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"),
|
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"),
|
||||||
Err(error) => warn!("connection terminated: {}", error),
|
Err(error) => warn!("connection terminated: {}", error),
|
||||||
|
|
|
@ -10,7 +10,7 @@ use libasync::smoltcp::TcpStream;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
|
||||||
use crate::proto_core_io::ProtoWrite;
|
use io::proto::ProtoWrite;
|
||||||
use crate::proto_async;
|
use crate::proto_async;
|
||||||
use self::tag::{Tag, TagIterator, split_tag};
|
use self::tag::{Tag, TagIterator, split_tag};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
|
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};
|
||||||
|
use libboard_zynq::time::Milliseconds;
|
||||||
|
|
||||||
|
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_task(&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_task(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)
|
||||||
|
}
|
Loading…
Reference in New Issue