Compare commits
6 Commits
master
...
5fa575ce4c
Author | SHA1 | Date | |
---|---|---|---|
5fa575ce4c | |||
2647ef7249 | |||
60a8861a22 | |||
731f52992f | |||
7e97e86446 | |||
3656fcc510 |
16
src/libboard_artiqzynq/Cargo.toml
Normal file
16
src/libboard_artiqzynq/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "libboard_artiqzynq"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "board_artiqzynq"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
log_buffer = { version = "1.2" }
|
||||||
|
|
||||||
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
|
libboard_zynq = { 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" }
|
38
src/libboard_artiqzynq/clock.rs
Normal file
38
src/libboard_artiqzynq/clock.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use core::i64;
|
||||||
|
use csr; // <- port
|
||||||
|
|
||||||
|
const INIT: u64 = i64::MAX as u64;
|
||||||
|
const FREQ: u64 = csr::CONFIG_CLOCK_FREQUENCY as u64;
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
unsafe {
|
||||||
|
csr::timer0::en_write(0);
|
||||||
|
csr::timer0::load_write(INIT);
|
||||||
|
csr::timer0::reload_write(INIT);
|
||||||
|
csr::timer0::en_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_us() -> u64 {
|
||||||
|
unsafe {
|
||||||
|
csr::timer0::update_value_write(1);
|
||||||
|
(INIT - csr::timer0::value_read()) / (FREQ / 1_000_000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ms() -> u64 {
|
||||||
|
unsafe {
|
||||||
|
csr::timer0::update_value_write(1);
|
||||||
|
(INIT - csr::timer0::value_read()) / (FREQ / 1_000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spin_us(interval: u64) {
|
||||||
|
unsafe {
|
||||||
|
csr::timer0::update_value_write(1);
|
||||||
|
let threshold = csr::timer0::value_read() - interval * (FREQ / 1_000_000);
|
||||||
|
while csr::timer0::value_read() > threshold {
|
||||||
|
csr::timer0::update_value_write(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
src/libboard_artiqzynq/drtio_routing.rs
Normal file
101
src/libboard_artiqzynq/drtio_routing.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use libconfig::Config;
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
use pl::csr; // <- check if it works in the same way
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[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").ok() && 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!("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);
|
||||||
|
}
|
||||||
|
}
|
154
src/libboard_artiqzynq/drtioaux.rs
Normal file
154
src/libboard_artiqzynq/drtioaux.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use core::slice;
|
||||||
|
use crc;
|
||||||
|
|
||||||
|
use io::{ProtoRead, ProtoWrite, Cursor, Error as IoError};
|
||||||
|
use board_misoc::{csr::DRTIOAUX, mem::DRTIOAUX_MEM}; // <- port
|
||||||
|
use clock;
|
||||||
|
use proto_artiq::drtioaux_proto::Error as ProtocolError;
|
||||||
|
|
||||||
|
pub use proto_artiq::drtioaux_proto::Packet;
|
||||||
|
|
||||||
|
// this is parametric over T because there's no impl Fail for !.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum Error<T> {
|
||||||
|
#[fail(display = "gateware reported error")]
|
||||||
|
GatewareError,
|
||||||
|
#[fail(display = "packet CRC failed")]
|
||||||
|
CorruptedPacket,
|
||||||
|
|
||||||
|
#[fail(display = "link is down")]
|
||||||
|
LinkDown,
|
||||||
|
#[fail(display = "timed out waiting for data")]
|
||||||
|
TimedOut,
|
||||||
|
#[fail(display = "unexpected reply")]
|
||||||
|
UnexpectedReply,
|
||||||
|
|
||||||
|
#[fail(display = "routing error")]
|
||||||
|
RoutingError,
|
||||||
|
|
||||||
|
#[fail(display = "protocol error: {}", _0)]
|
||||||
|
Protocol(#[cause] ProtocolError<T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<ProtocolError<T>> for Error<T> {
|
||||||
|
fn from(value: ProtocolError<T>) -> Error<T> {
|
||||||
|
Error::Protocol(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<IoError<T>> for Error<T> {
|
||||||
|
fn from(value: IoError<T>) -> Error<T> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
let len = (DRTIOAUX[linkidx].aux_rx_length_read)();
|
||||||
|
let result = f(slice::from_raw_parts(ptr as *mut u8, len as usize));
|
||||||
|
(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::UnexpectedEnd.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>) -> Result<Packet, Error<!>> {
|
||||||
|
let timeout_ms = timeout_ms.unwrap_or(10);
|
||||||
|
// only place with clock
|
||||||
|
let limit = clock::get_ms() + timeout_ms;
|
||||||
|
while clock::get_ms() < 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;
|
||||||
|
let len = DRTIOAUX_MEM[linkno].size / 2;
|
||||||
|
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, len))?;
|
||||||
|
(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())
|
||||||
|
})
|
||||||
|
}
|
11
src/libboard_artiqzynq/lib.rs
Normal file
11
src/libboard_artiqzynq/lib.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
pub mod clock;
|
||||||
|
|
||||||
|
// has csr; taken from runtime main
|
||||||
|
#[path = "../../../build/pl.rs"]
|
||||||
|
pub mod pl;
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub mod drtioaux;
|
||||||
|
pub mod drtio_routing;
|
||||||
|
pub mod si5324;
|
||||||
|
pub mod logger;
|
123
src/libboard_artiqzynq/logger.rs
Normal file
123
src/libboard_artiqzynq/logger.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
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) {
|
||||||
|
}
|
||||||
|
}
|
258
src/libboard_artiqzynq/si5324.rs
Normal file
258
src/libboard_artiqzynq/si5324.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
use core::result;
|
||||||
|
use log::info;
|
||||||
|
use libboard_zynq::i2c::I2c;
|
||||||
|
|
||||||
|
type Result<T> = result::Result<T, &'static str>;
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 0x68;
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
if !i2c.write(reg).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack register")
|
||||||
|
}
|
||||||
|
i2c.restart().unwrap();
|
||||||
|
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack read address")
|
||||||
|
}
|
||||||
|
let val = i2c.read(false).unwrap();
|
||||||
|
i2c.stop().unwrap();
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rmw<F>(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where
|
||||||
|
F: Fn(u8) -> u8 {
|
||||||
|
let value = read(i2c, reg)?;
|
||||||
|
write(i2c, reg, f(value))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(i2c: &mut I2c) -> Result<u16> {
|
||||||
|
Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn soft_reset(i2c: &mut I2c) -> Result<()> {
|
||||||
|
//TODO write_no_ack_value(i2c, 136, read(136)? | 0x80)?;
|
||||||
|
//TODO clock::spin_us(10_000);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_xtal(i2c: &mut I2c) -> Result<bool> {
|
||||||
|
Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ckin(i2c: &mut I2c, input: Input) -> Result<bool> {
|
||||||
|
match input {
|
||||||
|
Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0
|
||||||
|
Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locked(i2c: &mut I2c) -> Result<bool> {
|
||||||
|
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn monitor_lock(i2c: &mut I2c) -> Result<()> {
|
||||||
|
info!("waiting for Si5324 lock...");
|
||||||
|
// TODO let t = clock::get_ms();
|
||||||
|
while !locked(i2c)? {
|
||||||
|
// Yes, lock can be really slow.
|
||||||
|
/*if clock::get_ms() > t + 20000 {
|
||||||
|
return Err("Si5324 lock timeout");
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
info!(" ...locked");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(i2c: &mut I2c) -> Result<()> {
|
||||||
|
info!("init test");
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
{
|
||||||
|
i2c.pca9548_select(0x70, 0)?;
|
||||||
|
i2c.pca9548_select(0x71, 1 << 3)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ident(i2c)? != 0x0182 {
|
||||||
|
return Err("Si5324 does not have expected product number");
|
||||||
|
}
|
||||||
|
|
||||||
|
soft_reset(i2c)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bypass(i2c: &mut I2c, input: Input) -> Result<()> {
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
init(i2c)?;
|
||||||
|
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
|
||||||
|
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG
|
||||||
|
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||||
|
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||||
|
rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input) -> Result<()> {
|
||||||
|
let s = map_frequency_settings(settings)?;
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
|
||||||
|
init(i2c)?;
|
||||||
|
if settings.crystal_ref {
|
||||||
|
rmw(i2c, 0, |v| v | 0x40)?; // FREE_RUN=1
|
||||||
|
}
|
||||||
|
rmw(i2c, 2, |v| (v & 0x0f) | (s.bwsel << 4))?;
|
||||||
|
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
|
||||||
|
rmw(i2c, 3, |v| (v & 0x2f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1
|
||||||
|
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||||
|
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||||
|
write(i2c, 25, (s.n1_hs << 5 ) as u8)?;
|
||||||
|
write(i2c, 31, (s.nc1_ls >> 16) as u8)?;
|
||||||
|
write(i2c, 32, (s.nc1_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 33, (s.nc1_ls) as u8)?;
|
||||||
|
write(i2c, 34, (s.nc1_ls >> 16) as u8)?; // write to NC2_LS as well
|
||||||
|
write(i2c, 35, (s.nc1_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 36, (s.nc1_ls) as u8)?;
|
||||||
|
write(i2c, 40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?;
|
||||||
|
write(i2c, 41, (s.n2_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 42, (s.n2_ls) as u8)?;
|
||||||
|
write(i2c, 43, (s.n31 >> 16) as u8)?;
|
||||||
|
write(i2c, 44, (s.n31 >> 8) as u8)?;
|
||||||
|
write(i2c, 45, (s.n31) as u8)?;
|
||||||
|
write(i2c, 46, (s.n32 >> 16) as u8)?;
|
||||||
|
write(i2c, 47, (s.n32 >> 8) as u8)?;
|
||||||
|
write(i2c, 48, (s.n32) as u8)?;
|
||||||
|
rmw(i2c, 137, |v| v | 0x01)?; // FASTLOCK=1
|
||||||
|
rmw(i2c, 136, |v| v | 0x40)?; // ICAL=1
|
||||||
|
|
||||||
|
if !has_xtal(i2c)? {
|
||||||
|
return Err("Si5324 misses XA/XB signal");
|
||||||
|
}
|
||||||
|
if !has_ckin(i2c, input)? {
|
||||||
|
return Err("Si5324 misses clock input signal");
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_lock(i2c)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_input(i2c: &mut I2c, input: Input) -> Result<()> {
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?;
|
||||||
|
if !has_ckin(i2c, input)? {
|
||||||
|
return Err("Si5324 misses clock input signal");
|
||||||
|
}
|
||||||
|
monitor_lock(i2c)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
16
src/libio/Cargo.toml
Normal file
16
src/libio/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "io"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "io"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = { version = "0.1", default-features = false }
|
||||||
|
failure_derive = { version = "0.1", default-features = false }
|
||||||
|
byteorder = { version = "1.0", default-features = false, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = []
|
86
src/libio/cursor.rs
Normal file
86
src/libio/cursor.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use {Read, Write};
|
||||||
|
|
||||||
|
#[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> {
|
||||||
|
type ReadError = !;
|
||||||
|
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> {
|
||||||
|
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<'a> Write for Cursor<&'a mut [u8]> {
|
||||||
|
type WriteError = !;
|
||||||
|
type FlushError = !;
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||||
|
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<(), Self::FlushError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl Write for Cursor<::alloc::Vec<u8>> {
|
||||||
|
type WriteError = !;
|
||||||
|
type FlushError = !;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||||
|
self.inner.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<(), Self::FlushError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
147
src/libio/lib.rs
Normal file
147
src/libio/lib.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(never_type)]
|
||||||
|
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||||
|
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
|
mod cursor;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
mod proto;
|
||||||
|
|
||||||
|
pub use cursor::Cursor;
|
||||||
|
#[cfg(feature = "byteorder")]
|
||||||
|
pub use proto::{ProtoRead, ProtoWrite};
|
||||||
|
#[cfg(all(feature = "byteorder", feature = "alloc"))]
|
||||||
|
pub use proto::ReadStringError;
|
||||||
|
|
||||||
|
#[derive(Fail, Debug, Clone, PartialEq)]
|
||||||
|
pub enum Error<T> {
|
||||||
|
#[fail(display = "unexpected end of stream")]
|
||||||
|
UnexpectedEnd,
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
Other(#[cause] T)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Error<T> {
|
||||||
|
fn from(value: T) -> Error<T> {
|
||||||
|
Error::Other(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Read {
|
||||||
|
type ReadError;
|
||||||
|
|
||||||
|
/// Pull some bytes from this source into the specified buffer, returning
|
||||||
|
/// how many bytes were read.
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError>;
|
||||||
|
|
||||||
|
/// Read the exact number of bytes required to fill `buf`.
|
||||||
|
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Error<Self::ReadError>> {
|
||||||
|
while !buf.is_empty() {
|
||||||
|
let read_bytes = self.read(buf)?;
|
||||||
|
if read_bytes == 0 {
|
||||||
|
return Err(Error::UnexpectedEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = &mut { buf }[read_bytes..];
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Read> Read for &'a mut T {
|
||||||
|
type ReadError = T::ReadError;
|
||||||
|
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::ReadError> {
|
||||||
|
T::read(self, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Write {
|
||||||
|
type WriteError;
|
||||||
|
type FlushError;
|
||||||
|
|
||||||
|
/// Write a buffer into this object, returning how many bytes were written.
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError>;
|
||||||
|
|
||||||
|
/// Flush this output stream, ensuring that all intermediately buffered contents
|
||||||
|
/// reach their destination.
|
||||||
|
fn flush(&mut self) -> Result<(), Self::FlushError>;
|
||||||
|
|
||||||
|
/// Attempts to write an entire buffer into `self`.
|
||||||
|
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error<Self::WriteError>> {
|
||||||
|
while buf.len() > 0 {
|
||||||
|
let written_bytes = self.write(buf)?;
|
||||||
|
if written_bytes == 0 {
|
||||||
|
return Err(Error::UnexpectedEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = &buf[written_bytes..];
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hints the writer how much bytes will be written after call to this function.
|
||||||
|
///
|
||||||
|
/// At least `min` bytes should be written after the call to this function and
|
||||||
|
/// if `max` is `Some(x)` than at most `x` bytes should be written.
|
||||||
|
fn size_hint(&mut self, _min: usize, _max: Option<usize>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Write> Write for &'a mut T {
|
||||||
|
type WriteError = T::WriteError;
|
||||||
|
type FlushError = T::FlushError;
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||||
|
T::write(self, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::FlushError> {
|
||||||
|
T::flush(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&mut self, min: usize, max: Option<usize>) {
|
||||||
|
T::size_hint(self, min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Write for &'a mut [u8] {
|
||||||
|
type WriteError = !;
|
||||||
|
type FlushError = !;
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||||
|
let len = buf.len().min(self.len());
|
||||||
|
self[..len].copy_from_slice(&buf[..len]);
|
||||||
|
Ok(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<(), Self::FlushError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<'a> Write for alloc::Vec<u8> {
|
||||||
|
type WriteError = !;
|
||||||
|
type FlushError = !;
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::WriteError> {
|
||||||
|
self.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<(), Self::FlushError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
161
src/libio/proto.rs
Normal file
161
src/libio/proto.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use {core::str::Utf8Error, alloc::String};
|
||||||
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
|
|
||||||
|
use ::{Read, Write, Error as IoError};
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[derive(Fail, Debug, Clone, PartialEq)]
|
||||||
|
pub enum ReadStringError<T> {
|
||||||
|
#[fail(display = "invalid UTF-8: {}", _0)]
|
||||||
|
Utf8(Utf8Error),
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
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(NetworkEndian::read_u16(&bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
|
||||||
|
let mut bytes = [0; 4];
|
||||||
|
self.read_exact(&mut bytes)?;
|
||||||
|
Ok(NetworkEndian::read_u32(&bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
|
||||||
|
let mut bytes = [0; 8];
|
||||||
|
self.read_exact(&mut bytes)?;
|
||||||
|
Ok(NetworkEndian::read_u64(&bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn read_bool(&mut self) -> Result<bool, Self::ReadError> {
|
||||||
|
Ok(self.read_u8()? != 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[inline]
|
||||||
|
fn read_bytes(&mut self) -> Result<::alloc::Vec<u8>, Self::ReadError> {
|
||||||
|
let length = self.read_u32()?;
|
||||||
|
let mut value = vec![0; length as usize];
|
||||||
|
self.read_exact(&mut value)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[inline]
|
||||||
|
fn read_string(&mut self) -> Result<::alloc::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];
|
||||||
|
NetworkEndian::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];
|
||||||
|
NetworkEndian::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];
|
||||||
|
NetworkEndian::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];
|
||||||
|
NetworkEndian::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];
|
||||||
|
NetworkEndian::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];
|
||||||
|
NetworkEndian::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<T::ReadError>;
|
||||||
|
|
||||||
|
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<T::WriteError>;
|
||||||
|
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> {
|
||||||
|
T::write_all(self, buf)
|
||||||
|
}
|
||||||
|
}
|
20
src/libproto_artiq/Cargo.toml
Normal file
20
src/libproto_artiq/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "proto_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "proto_artiq"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
failure = { version = "0.1", default-features = false }
|
||||||
|
failure_derive = { version = "0.1", default-features = false }
|
||||||
|
byteorder = { version = "1.0", default-features = false }
|
||||||
|
cslice = { version = "0.3" }
|
||||||
|
log = { version = "0.4", default-features = false, optional = true }
|
||||||
|
io = { path = "../libio", features = ["byteorder"] }
|
||||||
|
dyld = { path = "../libdyld" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = ["io/alloc"]
|
362
src/libproto_artiq/drtioaux_proto.rs
Normal file
362
src/libproto_artiq/drtioaux_proto.rs
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
use io::{Read, ProtoRead, Write, ProtoWrite, Error as IoError};
|
||||||
|
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum Error<T> {
|
||||||
|
#[fail(display = "unknown packet {:#02x}", _0)]
|
||||||
|
UnknownPacket(u8),
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
Io(#[cause] IoError<T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<IoError<T>> for Error<T> {
|
||||||
|
fn from(value: IoError<T>) -> Error<T> {
|
||||||
|
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<R::ReadError>>
|
||||||
|
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<W::WriteError>>
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
18
src/libproto_artiq/lib.rs
Normal file
18
src/libproto_artiq/lib.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||||
|
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate cslice;
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
extern crate io;
|
||||||
|
extern crate dyld;
|
||||||
|
|
||||||
|
// Internal protocols.
|
||||||
|
pub mod drtioaux_proto;
|
18
src/satman/Cargo.toml
Normal file
18
src/satman/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "satman"
|
||||||
|
version = "0.0.0"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "satman"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
path = "main.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build_misoc = { path = "../libbuild_misoc" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { version = "0.4", default-features = false }
|
||||||
|
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
|
||||||
|
board_artiq = { path = "../libboard_artiq" }
|
21
src/satman/Makefile
Normal file
21
src/satman/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
include ../include/generated/variables.mak
|
||||||
|
include $(MISOC_DIRECTORY)/software/common.mak
|
||||||
|
|
||||||
|
LDFLAGS += -L../libbase
|
||||||
|
|
||||||
|
RUSTFLAGS += -Cpanic=abort
|
||||||
|
|
||||||
|
all:: satman.bin satman.fbi
|
||||||
|
|
||||||
|
.PHONY: $(RUSTOUT)/libsatman.a
|
||||||
|
$(RUSTOUT)/libsatman.a:
|
||||||
|
$(cargo) --manifest-path $(SATMAN_DIRECTORY)/Cargo.toml
|
||||||
|
|
||||||
|
satman.elf: $(RUSTOUT)/libsatman.a
|
||||||
|
$(link) -T $(SATMAN_DIRECTORY)/satman.ld
|
||||||
|
|
||||||
|
%.bin: %.elf
|
||||||
|
$(objcopy) -O binary
|
||||||
|
|
||||||
|
%.fbi: %.bin
|
||||||
|
$(mscimg) -f
|
5
src/satman/build.rs
Normal file
5
src/satman/build.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extern crate build_misoc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_misoc::cfg(); // <- port - see if it's necessary, how necessary, maybe move to board_artiqzync
|
||||||
|
}
|
37
src/satman/jdac_common.rs
Normal file
37
src/satman/jdac_common.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
pub const INIT: u8 = 0x00;
|
||||||
|
pub const PRINT_STATUS: u8 = 0x01;
|
||||||
|
pub const PRBS: u8 = 0x02;
|
||||||
|
pub const STPL: u8 = 0x03;
|
||||||
|
|
||||||
|
pub const SYSREF_DELAY_DAC: u8 = 0x10;
|
||||||
|
pub const SYSREF_SLIP: u8 = 0x11;
|
||||||
|
pub const SYNC: u8 = 0x12;
|
||||||
|
|
||||||
|
pub const DDMTD_SYSREF_RAW: u8 = 0x20;
|
||||||
|
pub const DDMTD_SYSREF: u8 = 0x21;
|
||||||
|
|
||||||
|
|
||||||
|
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {
|
||||||
|
let diff = ((a - b + modulo/2 + modulo) % modulo) - modulo/2;
|
||||||
|
return (modulo + b + diff/2) % modulo;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn average_phases(phases: &[i32], modulo: i32) -> i32 {
|
||||||
|
if phases.len() == 1 {
|
||||||
|
panic!("input array length must be a power of 2");
|
||||||
|
} else if phases.len() == 2 {
|
||||||
|
average_2phases(phases[0], phases[1], modulo)
|
||||||
|
} else {
|
||||||
|
let cut = phases.len()/2;
|
||||||
|
average_2phases(
|
||||||
|
average_phases(&phases[..cut], modulo),
|
||||||
|
average_phases(&phases[cut..], modulo),
|
||||||
|
modulo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RAW_DDMTD_N_SHIFT: i32 = 6;
|
||||||
|
pub const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT;
|
||||||
|
pub const DDMTD_DITHER_BITS: i32 = 1;
|
||||||
|
pub const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS;
|
||||||
|
pub const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT;
|
138
src/satman/jdcg.rs
Normal file
138
src/satman/jdcg.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
pub mod jesd {
|
||||||
|
use board_misoc::{csr, clock}; // <- port
|
||||||
|
|
||||||
|
pub fn reset(reset: bool) {
|
||||||
|
unsafe {
|
||||||
|
csr::jesd_crg::jreset_write(if reset {1} else {0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(dacno: u8, en: bool) {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_enable_write)(if en {1} else {0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phy_done(dacno: u8) -> bool {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_phy_done_read)() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ready(dacno: u8) -> bool {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_ready_read)() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prbs(dacno: u8, en: bool) {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_prbs_config_write)(if en {0b01} else {0b00})
|
||||||
|
}
|
||||||
|
clock::spin_us(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stpl(dacno: u8, en: bool) {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_stpl_enable_write)(if en {1} else {0})
|
||||||
|
}
|
||||||
|
clock::spin_us(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jsync(dacno: u8) -> bool {
|
||||||
|
unsafe {
|
||||||
|
(csr::JDCG[dacno as usize].jesd_control_jsync_read)() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod jdac {
|
||||||
|
use board_misoc::{csr, clock}; // <- port
|
||||||
|
use board_artiq::drtioaux; // <- port
|
||||||
|
|
||||||
|
use super::jesd;
|
||||||
|
use super::super::jdac_common;
|
||||||
|
|
||||||
|
pub fn basic_request(dacno: u8, reqno: u8, param: u8) -> Result<u8, &'static str> {
|
||||||
|
if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest {
|
||||||
|
destination: 0,
|
||||||
|
dacno: dacno,
|
||||||
|
reqno: reqno,
|
||||||
|
param: param
|
||||||
|
}) {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
return Err("aux packet error while sending for JESD DAC basic request");
|
||||||
|
}
|
||||||
|
match drtioaux::recv_timeout(1, Some(1000)) {
|
||||||
|
Ok(drtioaux::Packet::JdacBasicReply { succeeded, retval }) => {
|
||||||
|
if succeeded {
|
||||||
|
Ok(retval)
|
||||||
|
} else {
|
||||||
|
error!("JESD DAC basic request failed (dacno={}, reqno={})", dacno, reqno);
|
||||||
|
Err("remote error status to JESD DAC basic request")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
Err("unexpected aux packet in reply to JESD DAC basic request")
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
Err("aux packet error while waiting for JESD DAC basic reply")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() -> Result<(), &'static str> {
|
||||||
|
for dacno in 0..csr::JDCG.len() {
|
||||||
|
let dacno = dacno as u8;
|
||||||
|
info!("DAC-{} initializing...", dacno);
|
||||||
|
|
||||||
|
jesd::enable(dacno, true);
|
||||||
|
clock::spin_us(10_000);
|
||||||
|
if !jesd::phy_done(dacno) {
|
||||||
|
error!("JESD core PHY not done");
|
||||||
|
return Err("JESD core PHY not done");
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_request(dacno, jdac_common::INIT, 0)?;
|
||||||
|
|
||||||
|
// JESD ready depends on JSYNC being valid, so DAC init needs to happen first
|
||||||
|
if !jesd::ready(dacno) {
|
||||||
|
error!("JESD core reported not ready, sending DAC status print request");
|
||||||
|
basic_request(dacno, jdac_common::PRINT_STATUS, 0)?;
|
||||||
|
return Err("JESD core reported not ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
jesd::prbs(dacno, true);
|
||||||
|
basic_request(dacno, jdac_common::PRBS, 0)?;
|
||||||
|
jesd::prbs(dacno, false);
|
||||||
|
|
||||||
|
basic_request(dacno, jdac_common::INIT, 0)?;
|
||||||
|
clock::spin_us(5000);
|
||||||
|
|
||||||
|
if !jesd::jsync(dacno) {
|
||||||
|
error!("JESD core reported bad SYNC");
|
||||||
|
return Err("JESD core reported bad SYNC");
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(" ...done initializing");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stpl() -> Result<(), &'static str> {
|
||||||
|
for dacno in 0..csr::JDCG.len() {
|
||||||
|
let dacno = dacno as u8;
|
||||||
|
|
||||||
|
info!("Running STPL test on DAC-{}...", dacno);
|
||||||
|
|
||||||
|
jesd::stpl(dacno, true);
|
||||||
|
basic_request(dacno, jdac_common::STPL, 0)?;
|
||||||
|
jesd::stpl(dacno, false);
|
||||||
|
|
||||||
|
info!(" ...done STPL test");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
652
src/satman/main.rs
Normal file
652
src/satman/main.rs
Normal file
@ -0,0 +1,652 @@
|
|||||||
|
#![feature(never_type, panic_implementation, panic_info_message, const_slice_len, try_from)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
use board_misoc::{csr, irq, ident, clock, i2c}; // <- port, use libboard_zynq
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
use board_artiqzynq::si5324; // <- move from runtime
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
use board_artiq::wrpll; // <- port
|
||||||
|
use board_artiq::spi; // <- port?, use libboard_zynq (if spi available/necessary)
|
||||||
|
use board_artiqzynq::{drtio_routing drtioaux}; // <- artiqzync
|
||||||
|
use board_artiqzynq::logger;
|
||||||
|
|
||||||
|
mod repeater;
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
mod jdcg;
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
pub mod jdac_common;
|
||||||
|
|
||||||
|
fn drtiosat_reset(reset: bool) {
|
||||||
|
unsafe {
|
||||||
|
csr::drtiosat::reset_write(if reset { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtiosat_reset_phy(reset: bool) {
|
||||||
|
unsafe {
|
||||||
|
csr::drtiosat::reset_phy_write(if reset { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtiosat_link_rx_up() -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::drtiosat::rx_up_read() == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtiosat_tsc_loaded() -> bool {
|
||||||
|
unsafe {
|
||||||
|
let tsc_loaded = csr::drtiosat::tsc_loaded_read() == 1;
|
||||||
|
if tsc_loaded {
|
||||||
|
csr::drtiosat::tsc_loaded_write(1);
|
||||||
|
}
|
||||||
|
tsc_loaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
macro_rules! forward {
|
||||||
|
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {{
|
||||||
|
let hop = $routing_table.0[$destination as usize][$rank as usize];
|
||||||
|
if hop != 0 {
|
||||||
|
let repno = (hop - 1) as usize;
|
||||||
|
if repno < $repeaters.len() {
|
||||||
|
return $repeaters[repno].aux_forward($packet);
|
||||||
|
} else {
|
||||||
|
return Err(drtioaux::Error::RoutingError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
macro_rules! forward {
|
||||||
|
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||||
|
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
|
||||||
|
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
|
||||||
|
// and u16 otherwise; hence the `as _` conversion.
|
||||||
|
match packet {
|
||||||
|
drtioaux::Packet::EchoRequest =>
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::EchoReply),
|
||||||
|
drtioaux::Packet::ResetRequest => {
|
||||||
|
info!("resetting RTIO");
|
||||||
|
drtiosat_reset(true);
|
||||||
|
clock::spin_us(100);
|
||||||
|
drtiosat_reset(false);
|
||||||
|
for rep in _repeaters.iter() {
|
||||||
|
if let Err(e) = rep.rtio_reset() {
|
||||||
|
error!("failed to issue RTIO reset ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::ResetAck)
|
||||||
|
},
|
||||||
|
|
||||||
|
drtioaux::Packet::DestinationStatusRequest { destination: _destination } => {
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
let hop = _routing_table.0[_destination as usize][*_rank as usize];
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
let hop = 0;
|
||||||
|
|
||||||
|
if hop == 0 {
|
||||||
|
let errors;
|
||||||
|
unsafe {
|
||||||
|
errors = csr::drtiosat::rtio_error_read();
|
||||||
|
}
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = csr::drtiosat::sequence_error_channel_read();
|
||||||
|
csr::drtiosat::rtio_error_write(1);
|
||||||
|
}
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
|
||||||
|
} else if errors & 2 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = csr::drtiosat::collision_channel_read();
|
||||||
|
csr::drtiosat::rtio_error_write(2);
|
||||||
|
}
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DestinationCollisionReply { channel })?;
|
||||||
|
} else if errors & 4 != 0 {
|
||||||
|
let channel;
|
||||||
|
unsafe {
|
||||||
|
channel = csr::drtiosat::busy_channel_read();
|
||||||
|
csr::drtiosat::rtio_error_write(4);
|
||||||
|
}
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::DestinationBusyReply { channel })?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
{
|
||||||
|
if hop != 0 {
|
||||||
|
let hop = hop as usize;
|
||||||
|
if hop <= csr::DRTIOREP.len() {
|
||||||
|
let repno = hop - 1;
|
||||||
|
match _repeaters[repno].aux_forward(&drtioaux::Packet::DestinationStatusRequest {
|
||||||
|
destination: _destination
|
||||||
|
}) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(drtioaux::Error::LinkDown) => drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?,
|
||||||
|
Err(e) => {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
|
||||||
|
error!("aux error when handling destination status request: {}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::DestinationDownReply)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
drtioaux::Packet::RoutingSetPath { destination, hops } => {
|
||||||
|
_routing_table.0[destination as usize] = hops;
|
||||||
|
for rep in _repeaters.iter() {
|
||||||
|
if let Err(e) = rep.set_path(destination, &hops) {
|
||||||
|
error!("failed to set path ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
|
||||||
|
}
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
drtioaux::Packet::RoutingSetRank { rank } => {
|
||||||
|
*_rank = rank;
|
||||||
|
drtio_routing::interconnect_enable_all(_routing_table, rank);
|
||||||
|
|
||||||
|
let rep_rank = rank + 1;
|
||||||
|
for rep in _repeaters.iter() {
|
||||||
|
if let Err(e) = rep.set_rank(rep_rank) {
|
||||||
|
error!("failed to set rank ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("rank: {}", rank);
|
||||||
|
info!("routing table: {}", _routing_table);
|
||||||
|
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
drtioaux::Packet::RoutingSetPath { destination: _, hops: _ } => {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
|
||||||
|
}
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
drtioaux::Packet::RoutingSetRank { rank: _ } => {
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::RoutingAck)
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::MonitorRequest { destination: _destination, channel, probe } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let value;
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::mon_probe_sel_write(probe);
|
||||||
|
csr::rtio_moninj::mon_value_update_write(1);
|
||||||
|
value = csr::rtio_moninj::mon_value_read();
|
||||||
|
}
|
||||||
|
#[cfg(not(has_rtio_moninj))]
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
|
||||||
|
drtioaux::send(0, &reply)
|
||||||
|
},
|
||||||
|
drtioaux::Packet::InjectionRequest { destination: _destination, channel, overrd, value } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd);
|
||||||
|
csr::rtio_moninj::inj_value_write(value);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
drtioaux::Packet::InjectionStatusRequest { destination: _destination, channel, overrd } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let value;
|
||||||
|
#[cfg(has_rtio_moninj)]
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
||||||
|
csr::rtio_moninj::inj_override_sel_write(overrd);
|
||||||
|
value = csr::rtio_moninj::inj_value_read();
|
||||||
|
}
|
||||||
|
#[cfg(not(has_rtio_moninj))]
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::InjectionStatusReply { value: value })
|
||||||
|
},
|
||||||
|
|
||||||
|
drtioaux::Packet::I2cStartRequest { destination: _destination, busno } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = i2c::start(busno).is_ok();
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cRestartRequest { destination: _destination, busno } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = i2c::restart(busno).is_ok();
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cStopRequest { destination: _destination, busno } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = i2c::stop(busno).is_ok();
|
||||||
|
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cWriteRequest { destination: _destination, busno, data } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
match i2c::write(busno, data) {
|
||||||
|
Ok(ack) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
|
||||||
|
Err(_) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drtioaux::Packet::I2cReadRequest { destination: _destination, busno, ack } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
match i2c::read(busno, ack) {
|
||||||
|
Ok(data) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::SpiSetConfigRequest { destination: _destination, busno, flags, length, div, cs } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
|
||||||
|
},
|
||||||
|
drtioaux::Packet::SpiWriteRequest { destination: _destination, busno, data } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let succeeded = spi::write(busno, data).is_ok();
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
|
||||||
|
}
|
||||||
|
drtioaux::Packet::SpiReadRequest { destination: _destination, busno } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
match spi::read(busno) {
|
||||||
|
Ok(data) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
|
||||||
|
Err(_) => drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::Packet::JdacBasicRequest { destination: _destination, dacno: _dacno,
|
||||||
|
reqno: _reqno, param: _param } => {
|
||||||
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
|
let (succeeded, retval) = (false, 0);
|
||||||
|
drtioaux::send(0,
|
||||||
|
&drtioaux::Packet::JdacBasicReply { succeeded: succeeded, retval: retval })
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
warn!("received unexpected aux packet");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_aux_packets(repeaters: &mut [repeater::Repeater],
|
||||||
|
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
|
||||||
|
let result =
|
||||||
|
drtioaux::recv(0).and_then(|packet| {
|
||||||
|
if let Some(packet) = packet {
|
||||||
|
process_aux_packet(repeaters, routing_table, rank, packet)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
match result {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(e) => warn!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drtiosat_process_errors() {
|
||||||
|
let errors;
|
||||||
|
unsafe {
|
||||||
|
errors = csr::drtiosat::protocol_error_read();
|
||||||
|
}
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
error!("received packet of an unknown type");
|
||||||
|
}
|
||||||
|
if errors & 2 != 0 {
|
||||||
|
error!("received truncated packet");
|
||||||
|
}
|
||||||
|
if errors & 4 != 0 {
|
||||||
|
let destination;
|
||||||
|
unsafe {
|
||||||
|
destination = csr::drtiosat::buffer_space_timeout_dest_read();
|
||||||
|
}
|
||||||
|
error!("timeout attempting to get buffer space from CRI, destination=0x{:02x}", destination)
|
||||||
|
}
|
||||||
|
if errors & 8 != 0 {
|
||||||
|
let channel;
|
||||||
|
let timestamp_event;
|
||||||
|
let timestamp_counter;
|
||||||
|
unsafe {
|
||||||
|
channel = csr::drtiosat::underflow_channel_read();
|
||||||
|
timestamp_event = csr::drtiosat::underflow_timestamp_event_read() as i64;
|
||||||
|
timestamp_counter = csr::drtiosat::underflow_timestamp_counter_read() as i64;
|
||||||
|
}
|
||||||
|
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
|
||||||
|
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
|
||||||
|
}
|
||||||
|
if errors & 16 != 0 {
|
||||||
|
error!("write overflow");
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
csr::drtiosat::protocol_error_write(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(has_rtio_crg)]
|
||||||
|
fn init_rtio_crg() {
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_crg::pll_reset_write(0);
|
||||||
|
}
|
||||||
|
clock::spin_us(150);
|
||||||
|
let locked = unsafe { csr::rtio_crg::pll_locked_read() != 0 };
|
||||||
|
if !locked {
|
||||||
|
error!("RTIO clock failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_rtio_crg))]
|
||||||
|
fn init_rtio_crg() { }
|
||||||
|
|
||||||
|
fn hardware_tick(ts: &mut u64) {
|
||||||
|
let now = clock::get_ms();
|
||||||
|
if now > *ts {
|
||||||
|
#[cfg(has_grabber)]
|
||||||
|
board_artiq::grabber::tick();
|
||||||
|
*ts = now + 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(has_si5324, rtio_frequency = "150.0"))]
|
||||||
|
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||||
|
= si5324::FrequencySettings {
|
||||||
|
n1_hs : 6,
|
||||||
|
nc1_ls : 6,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 270,
|
||||||
|
n31 : 75,
|
||||||
|
n32 : 75,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
|
||||||
|
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||||
|
= si5324::FrequencySettings {
|
||||||
|
n1_hs : 5,
|
||||||
|
nc1_ls : 8,
|
||||||
|
n2_hs : 7,
|
||||||
|
n2_ls : 360,
|
||||||
|
n31 : 63,
|
||||||
|
n32 : 63,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn main() -> i32 {
|
||||||
|
clock::init();
|
||||||
|
|
||||||
|
let buffer_logger = unsafe {
|
||||||
|
logger::BufferLogger::new(&mut LOG_BUFFER[..])
|
||||||
|
};
|
||||||
|
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
|
||||||
|
buffer_logger.register();
|
||||||
|
|
||||||
|
info!("ARTIQ satellite manager starting...");
|
||||||
|
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
|
||||||
|
info!("gateware ident {}", ident::read(&mut [0; 64]));
|
||||||
|
|
||||||
|
#[cfg(has_i2c)]
|
||||||
|
i2c::init().expect("I2C initialization failed");
|
||||||
|
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||||
|
let (mut io_expander0, mut io_expander1);
|
||||||
|
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||||
|
{
|
||||||
|
io_expander0 = board_misoc::io_expander::IoExpander::new(0);
|
||||||
|
io_expander1 = board_misoc::io_expander::IoExpander::new(1);
|
||||||
|
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
|
||||||
|
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
{
|
||||||
|
io_expander0.set_oe(1, 1 << 7).unwrap();
|
||||||
|
io_expander0.set(1, 7, true);
|
||||||
|
io_expander0.service().unwrap();
|
||||||
|
io_expander1.set_oe(0, 1 << 7).unwrap();
|
||||||
|
io_expander1.set_oe(1, 1 << 7).unwrap();
|
||||||
|
io_expander1.set(0, 7, true);
|
||||||
|
io_expander1.set(1, 7, true);
|
||||||
|
io_expander1.service().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actively drive TX_DISABLE to false on SFP0..3
|
||||||
|
io_expander0.set_oe(0, 1 << 1).unwrap();
|
||||||
|
io_expander0.set_oe(1, 1 << 1).unwrap();
|
||||||
|
io_expander1.set_oe(0, 1 << 1).unwrap();
|
||||||
|
io_expander1.set_oe(1, 1 << 1).unwrap();
|
||||||
|
io_expander0.set(0, 1, false);
|
||||||
|
io_expander0.set(1, 1, false);
|
||||||
|
io_expander1.set(0, 1, false);
|
||||||
|
io_expander1.set(1, 1, false);
|
||||||
|
io_expander0.service().unwrap();
|
||||||
|
io_expander1.service().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
wrpll::init();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::stable_clkin_write(1);
|
||||||
|
}
|
||||||
|
clock::spin_us(1500); // wait for CPLL/QPLL lock
|
||||||
|
#[cfg(not(has_jdcg))]
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||||
|
}
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
wrpll::diagnostics();
|
||||||
|
init_rtio_crg();
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
let mut repeaters = [repeater::Repeater::default(); 0];
|
||||||
|
for i in 0..repeaters.len() {
|
||||||
|
repeaters[i] = repeater::Repeater::new(i as u8);
|
||||||
|
}
|
||||||
|
let mut routing_table = drtio_routing::RoutingTable::default_empty();
|
||||||
|
let mut rank = 1;
|
||||||
|
|
||||||
|
let mut hardware_tick_ts = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
unsafe {
|
||||||
|
// Hide from uplink until RTM is ready
|
||||||
|
csr::drtio_transceiver::txenable_write(0xfffffffeu32 as _);
|
||||||
|
}
|
||||||
|
while !drtiosat_link_rx_up() {
|
||||||
|
drtiosat_process_errors();
|
||||||
|
for mut rep in repeaters.iter_mut() {
|
||||||
|
rep.service(&routing_table, rank);
|
||||||
|
}
|
||||||
|
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||||
|
{
|
||||||
|
io_expander0.service().expect("I2C I/O expander #0 service failed");
|
||||||
|
io_expander1.service().expect("I2C I/O expander #1 service failed");
|
||||||
|
}
|
||||||
|
hardware_tick(&mut hardware_tick_ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("uplink is up, switching to recovered clock");
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
{
|
||||||
|
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
|
||||||
|
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
|
||||||
|
}
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
wrpll::select_recovered_clock(true);
|
||||||
|
|
||||||
|
drtioaux::reset(0);
|
||||||
|
drtiosat_reset(false);
|
||||||
|
drtiosat_reset_phy(false);
|
||||||
|
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
let mut was_up = false;
|
||||||
|
while drtiosat_link_rx_up() {
|
||||||
|
drtiosat_process_errors();
|
||||||
|
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank);
|
||||||
|
for mut rep in repeaters.iter_mut() {
|
||||||
|
rep.service(&routing_table, rank);
|
||||||
|
}
|
||||||
|
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
|
||||||
|
{
|
||||||
|
io_expander0.service().expect("I2C I/O expander #0 service failed");
|
||||||
|
io_expander1.service().expect("I2C I/O expander #1 service failed");
|
||||||
|
}
|
||||||
|
hardware_tick(&mut hardware_tick_ts);
|
||||||
|
if drtiosat_tsc_loaded() {
|
||||||
|
info!("TSC loaded from uplink");
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
{
|
||||||
|
// We assume that the RTM on repeater0 is up.
|
||||||
|
// Uplink should not send a TSC load command unless the link is
|
||||||
|
// up, and we are hiding when the RTM is down.
|
||||||
|
if let Err(e) = jdcg::jesd204sync::sysref_rtio_align() {
|
||||||
|
error!("failed to align SYSREF with TSC ({})", e);
|
||||||
|
}
|
||||||
|
if let Err(e) = jdcg::jesd204sync::resync_dacs() {
|
||||||
|
error!("DAC resync failed after SYSREF/TSC realignment ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for rep in repeaters.iter() {
|
||||||
|
if let Err(e) = rep.sync_tsc() {
|
||||||
|
error!("failed to sync TSC ({})", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::TSCAck) {
|
||||||
|
error!("aux packet error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
{
|
||||||
|
let is_up = repeaters[0].is_up();
|
||||||
|
if is_up && !was_up {
|
||||||
|
/*
|
||||||
|
* One side of the JESD204 elastic buffer is clocked by the jitter filter
|
||||||
|
* (Si5324 or WRPLL), the other by the RTM.
|
||||||
|
* The elastic buffer can operate only when those two clocks are derived from
|
||||||
|
* the same oscillator.
|
||||||
|
* This is the case when either of those conditions is true:
|
||||||
|
* (1) The DRTIO master and the RTM are clocked directly from a common external
|
||||||
|
* source, *and* the jitter filter has locked to the recovered clock.
|
||||||
|
* This clocking scheme may provide less noise and phase drift at the DACs.
|
||||||
|
* (2) The RTM clock is connected to the jitter filter output.
|
||||||
|
* To handle those cases, we simply keep the JESD204 core in reset unless the
|
||||||
|
* jitter filter is locked to the recovered clock.
|
||||||
|
*/
|
||||||
|
jdcg::jesd::reset(false);
|
||||||
|
let _ = jdcg::jdac::init();
|
||||||
|
jdcg::jesd204sync::sysref_auto_align();
|
||||||
|
jdcg::jdac::stpl();
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); // unhide
|
||||||
|
}
|
||||||
|
}
|
||||||
|
was_up = is_up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_jdcg)]
|
||||||
|
jdcg::jesd::reset(true);
|
||||||
|
|
||||||
|
drtiosat_reset_phy(true);
|
||||||
|
drtiosat_reset(true);
|
||||||
|
drtiosat_tsc_loaded();
|
||||||
|
info!("uplink is down, switching to local oscillator clock");
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
|
||||||
|
#[cfg(has_wrpll)]
|
||||||
|
wrpll::select_recovered_clock(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn exception(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
|
||||||
|
let vect = irq::Exception::try_from(vect).expect("unknown exception");
|
||||||
|
|
||||||
|
fn hexdump(addr: u32) {
|
||||||
|
let addr = (addr - addr % 4) as *const u32;
|
||||||
|
let mut ptr = addr;
|
||||||
|
println!("@ {:08p}", ptr);
|
||||||
|
for _ in 0..4 {
|
||||||
|
print!("+{:04x}: ", ptr as usize - addr as usize);
|
||||||
|
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
|
||||||
|
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
|
||||||
|
print!("{:08x} ", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
|
||||||
|
print!("{:08x}\n", unsafe { *ptr }); ptr = ptr.wrapping_offset(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hexdump(pc);
|
||||||
|
hexdump(ea);
|
||||||
|
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn abort() {
|
||||||
|
println!("aborted");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
|
||||||
|
#[panic_implementation]
|
||||||
|
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
#[cfg(has_error_led)]
|
||||||
|
unsafe {
|
||||||
|
csr::error_led::out_write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(location) = info.location() {
|
||||||
|
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
|
||||||
|
} else {
|
||||||
|
print!("panic at unknown location");
|
||||||
|
}
|
||||||
|
if let Some(message) = info.message() {
|
||||||
|
println!(": {}", message);
|
||||||
|
} else {
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
loop {}
|
||||||
|
}
|
283
src/satman/repeater.rs
Normal file
283
src/satman/repeater.rs
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
use board_artiq::{drtioaux, drtio_routing};
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
use board_misoc::{csr, clock};
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
fn rep_link_rx_up(repno: u8) -> bool {
|
||||||
|
let repno = repno as usize;
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIOREP[repno].rx_up_read)() == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
enum RepeaterState {
|
||||||
|
Down,
|
||||||
|
SendPing { ping_count: u16 },
|
||||||
|
WaitPingReply { ping_count: u16, timeout: u64 },
|
||||||
|
Up,
|
||||||
|
Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
impl Default for RepeaterState {
|
||||||
|
fn default() -> RepeaterState { RepeaterState::Down }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Repeater {
|
||||||
|
repno: u8,
|
||||||
|
auxno: u8,
|
||||||
|
state: RepeaterState
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
impl Repeater {
|
||||||
|
pub fn new(repno: u8) -> Repeater {
|
||||||
|
Repeater {
|
||||||
|
repno: repno,
|
||||||
|
auxno: repno + 1,
|
||||||
|
state: RepeaterState::Down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_up(&self) -> bool {
|
||||||
|
self.state == RepeaterState::Up
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8) {
|
||||||
|
self.process_local_errors();
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
RepeaterState::Down => {
|
||||||
|
if rep_link_rx_up(self.repno) {
|
||||||
|
info!("[REP#{}] link RX became up, pinging", self.repno);
|
||||||
|
self.state = RepeaterState::SendPing { ping_count: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepeaterState::SendPing { ping_count } => {
|
||||||
|
if rep_link_rx_up(self.repno) {
|
||||||
|
drtioaux::send(self.auxno, &drtioaux::Packet::EchoRequest).unwrap();
|
||||||
|
self.state = RepeaterState::WaitPingReply {
|
||||||
|
ping_count: ping_count + 1,
|
||||||
|
timeout: clock::get_ms() + 100
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("[REP#{}] link RX went down during ping", self.repno);
|
||||||
|
self.state = RepeaterState::Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepeaterState::WaitPingReply { ping_count, timeout } => {
|
||||||
|
if rep_link_rx_up(self.repno) {
|
||||||
|
if let Ok(Some(drtioaux::Packet::EchoReply)) = drtioaux::recv(self.auxno) {
|
||||||
|
info!("[REP#{}] remote replied after {} packets", self.repno, ping_count);
|
||||||
|
self.state = RepeaterState::Up;
|
||||||
|
if let Err(e) = self.sync_tsc() {
|
||||||
|
error!("[REP#{}] failed to sync TSC ({})", self.repno, e);
|
||||||
|
self.state = RepeaterState::Failed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(e) = self.load_routing_table(routing_table) {
|
||||||
|
error!("[REP#{}] failed to load routing table ({})", self.repno, e);
|
||||||
|
self.state = RepeaterState::Failed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(e) = self.set_rank(rank + 1) {
|
||||||
|
error!("[REP#{}] failed to set rank ({})", self.repno, e);
|
||||||
|
self.state = RepeaterState::Failed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if clock::get_ms() > timeout {
|
||||||
|
if ping_count > 200 {
|
||||||
|
error!("[REP#{}] ping failed", self.repno);
|
||||||
|
self.state = RepeaterState::Failed;
|
||||||
|
} else {
|
||||||
|
self.state = RepeaterState::SendPing { ping_count: ping_count };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("[REP#{}] link RX went down during ping", self.repno);
|
||||||
|
self.state = RepeaterState::Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepeaterState::Up => {
|
||||||
|
self.process_unsolicited_aux();
|
||||||
|
if !rep_link_rx_up(self.repno) {
|
||||||
|
info!("[REP#{}] link is down", self.repno);
|
||||||
|
self.state = RepeaterState::Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepeaterState::Failed => {
|
||||||
|
if !rep_link_rx_up(self.repno) {
|
||||||
|
info!("[REP#{}] link is down", self.repno);
|
||||||
|
self.state = RepeaterState::Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_unsolicited_aux(&self) {
|
||||||
|
match drtioaux::recv(self.auxno) {
|
||||||
|
Ok(Some(packet)) => warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet),
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(_) => warn!("[REP#{}] aux packet error", self.repno)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_local_errors(&self) {
|
||||||
|
let repno = self.repno as usize;
|
||||||
|
let errors;
|
||||||
|
unsafe {
|
||||||
|
errors = (csr::DRTIOREP[repno].protocol_error_read)();
|
||||||
|
}
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
error!("[REP#{}] received packet of an unknown type", repno);
|
||||||
|
}
|
||||||
|
if errors & 2 != 0 {
|
||||||
|
error!("[REP#{}] received truncated packet", repno);
|
||||||
|
}
|
||||||
|
if errors & 4 != 0 {
|
||||||
|
let cmd;
|
||||||
|
let chan_sel;
|
||||||
|
unsafe {
|
||||||
|
cmd = (csr::DRTIOREP[repno].command_missed_cmd_read)();
|
||||||
|
chan_sel = (csr::DRTIOREP[repno].command_missed_chan_sel_read)();
|
||||||
|
}
|
||||||
|
error!("[REP#{}] CRI command missed, cmd={}, chan_sel=0x{:06x}", repno, cmd, chan_sel)
|
||||||
|
}
|
||||||
|
if errors & 8 != 0 {
|
||||||
|
let destination;
|
||||||
|
unsafe {
|
||||||
|
destination = (csr::DRTIOREP[repno].buffer_space_timeout_dest_read)();
|
||||||
|
}
|
||||||
|
error!("[REP#{}] timeout attempting to get remote buffer space, destination=0x{:02x}", repno, destination);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIOREP[repno].protocol_error_write)(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recv_aux_timeout(&self, timeout: u32) -> Result<drtioaux::Packet, drtioaux::Error<!>> {
|
||||||
|
let max_time = clock::get_ms() + timeout as u64;
|
||||||
|
loop {
|
||||||
|
if !rep_link_rx_up(self.repno) {
|
||||||
|
return Err(drtioaux::Error::LinkDown);
|
||||||
|
}
|
||||||
|
if clock::get_ms() > max_time {
|
||||||
|
return Err(drtioaux::Error::TimedOut);
|
||||||
|
}
|
||||||
|
match drtioaux::recv(self.auxno) {
|
||||||
|
Ok(Some(packet)) => return Ok(packet),
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aux_forward(&self, request: &drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
if self.state != RepeaterState::Up {
|
||||||
|
return Err(drtioaux::Error::LinkDown);
|
||||||
|
}
|
||||||
|
drtioaux::send(self.auxno, request).unwrap();
|
||||||
|
let reply = self.recv_aux_timeout(200)?;
|
||||||
|
drtioaux::send(0, &reply).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sync_tsc(&self) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
if self.state != RepeaterState::Up {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let repno = self.repno as usize;
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIOREP[repno].set_time_write)(1);
|
||||||
|
while (csr::DRTIOREP[repno].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 = self.recv_aux_timeout(10000)?;
|
||||||
|
if reply == drtioaux::Packet::TSCAck {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
return Err(drtioaux::Error::UnexpectedReply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_path(&self, destination: u8, hops: &[u8; drtio_routing::MAX_HOPS]) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
if self.state != RepeaterState::Up {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetPath {
|
||||||
|
destination: destination,
|
||||||
|
hops: *hops
|
||||||
|
}).unwrap();
|
||||||
|
let reply = self.recv_aux_timeout(200)?;
|
||||||
|
if reply != drtioaux::Packet::RoutingAck {
|
||||||
|
return Err(drtioaux::Error::UnexpectedReply);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_routing_table(&self, routing_table: &drtio_routing::RoutingTable) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
for i in 0..drtio_routing::DEST_COUNT {
|
||||||
|
self.set_path(i as u8, &routing_table.0[i])?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_rank(&self, rank: u8) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
if self.state != RepeaterState::Up {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
drtioaux::send(self.auxno, &drtioaux::Packet::RoutingSetRank {
|
||||||
|
rank: rank
|
||||||
|
}).unwrap();
|
||||||
|
let reply = self.recv_aux_timeout(200)?;
|
||||||
|
if reply != drtioaux::Packet::RoutingAck {
|
||||||
|
return Err(drtioaux::Error::UnexpectedReply);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rtio_reset(&self) -> Result<(), drtioaux::Error<!>> {
|
||||||
|
let repno = self.repno as usize;
|
||||||
|
unsafe { (csr::DRTIOREP[repno].reset_write)(1); }
|
||||||
|
clock::spin_us(100);
|
||||||
|
unsafe { (csr::DRTIOREP[repno].reset_write)(0); }
|
||||||
|
|
||||||
|
if self.state != RepeaterState::Up {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
drtioaux::send(self.auxno, &drtioaux::Packet::ResetRequest).unwrap();
|
||||||
|
let reply = self.recv_aux_timeout(200)?;
|
||||||
|
if reply != drtioaux::Packet::ResetAck {
|
||||||
|
return Err(drtioaux::Error::UnexpectedReply);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Repeater {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio_routing))]
|
||||||
|
impl Repeater {
|
||||||
|
pub fn new(_repno: u8) -> Repeater { Repeater::default() }
|
||||||
|
|
||||||
|
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8) { }
|
||||||
|
|
||||||
|
pub fn sync_tsc(&self) -> Result<(), drtioaux::Error<!>> { Ok(()) }
|
||||||
|
|
||||||
|
pub fn rtio_reset(&self) -> Result<(), drtioaux::Error<!>> { Ok(()) }
|
||||||
|
}
|
55
src/satman/satman.ld
Normal file
55
src/satman/satman.ld
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
INCLUDE generated/output_format.ld
|
||||||
|
INCLUDE generated/regions.ld
|
||||||
|
ENTRY(_reset_handler)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.vectors :
|
||||||
|
{
|
||||||
|
*(.vectors)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
*(.text .text.*)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||||
|
.got :
|
||||||
|
{
|
||||||
|
PROVIDE(_GLOBAL_OFFSET_TABLE_ = .);
|
||||||
|
*(.got)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.got.plt :
|
||||||
|
{
|
||||||
|
*(.got.plt)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
_frodata = .;
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
_erodata = .;
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.data .data.*)
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.bss ALIGN(4) :
|
||||||
|
{
|
||||||
|
_fbss = .;
|
||||||
|
*(.bss .bss.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
} > main_ram
|
||||||
|
|
||||||
|
.stack :
|
||||||
|
{
|
||||||
|
_estack = .;
|
||||||
|
. += 0x10000;
|
||||||
|
_fstack = . - 4;
|
||||||
|
} > main_ram
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user