parent
35250b3f56
commit
f1bf6b1108
@ -0,0 +1,31 @@ |
||||
[package] |
||||
name = "libboard_artiq" |
||||
version = "0.0.0" |
||||
authors = ["M-Labs"] |
||||
edition = "2018" |
||||
|
||||
[lib] |
||||
name = "libboard_artiq" |
||||
|
||||
[features] |
||||
target_zc706 = [] |
||||
target_kasli_soc = [] |
||||
|
||||
[build-dependencies] |
||||
build_zynq = { path = "../libbuild_zynq" } |
||||
|
||||
[dependencies] |
||||
log = "0.4" |
||||
log_buffer = { version = "1.2" } |
||||
crc = { version = "1.7", default-features = false } |
||||
core_io = { version = "0.1", features = ["collections"] } |
||||
embedded-hal = "0.2" |
||||
nb = "1.0" |
||||
void = { version = "1", default-features = false } |
||||
|
||||
io = { path = "../libio", features = ["byteorder"] } |
||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"} |
||||
libregister = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } |
||||
libconfig = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"} |
||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } |
||||
libasync = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } |
@ -0,0 +1,5 @@ |
||||
extern crate build_zynq; |
||||
|
||||
fn main() { |
||||
build_zynq::cfg(); |
||||
} |
@ -0,0 +1,109 @@ |
||||
use libconfig::Config; |
||||
#[cfg(has_drtio_routing)] |
||||
use crate::pl::csr; |
||||
use core::fmt; |
||||
|
||||
use log::{warn, info}; |
||||
|
||||
#[cfg(has_drtio_routing)] |
||||
pub const DEST_COUNT: usize = 256; |
||||
#[cfg(not(has_drtio_routing))] |
||||
pub const DEST_COUNT: usize = 0; |
||||
pub const MAX_HOPS: usize = 32; |
||||
pub const INVALID_HOP: u8 = 0xff; |
||||
|
||||
pub struct RoutingTable(pub [[u8; MAX_HOPS]; DEST_COUNT]); |
||||
|
||||
impl RoutingTable { |
||||
// default routing table is for star topology with no repeaters
|
||||
pub fn default_master(default_n_links: usize) -> RoutingTable { |
||||
let mut ret = RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT]); |
||||
let n_entries = default_n_links + 1; // include local RTIO
|
||||
for i in 0..n_entries { |
||||
ret.0[i][0] = i as u8; |
||||
} |
||||
for i in 1..n_entries { |
||||
ret.0[i][1] = 0x00; |
||||
} |
||||
ret |
||||
} |
||||
|
||||
// use this by default on satellite, as they receive
|
||||
// the routing table from the master
|
||||
pub fn default_empty() -> RoutingTable { |
||||
RoutingTable([[INVALID_HOP; MAX_HOPS]; DEST_COUNT]) |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for RoutingTable { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
write!(f, "RoutingTable {{")?; |
||||
for i in 0..DEST_COUNT { |
||||
if self.0[i][0] != INVALID_HOP { |
||||
write!(f, " {}:", i)?; |
||||
for j in 0..MAX_HOPS { |
||||
if self.0[i][j] == INVALID_HOP { |
||||
break; |
||||
} |
||||
write!(f, " {}", self.0[i][j])?; |
||||
} |
||||
write!(f, ";")?; |
||||
} |
||||
} |
||||
write!(f, " }}")?; |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
pub fn config_routing_table(default_n_links: usize, cfg: &Config) -> RoutingTable { |
||||
let mut ret = RoutingTable::default_master(default_n_links); |
||||
if let Ok(data) = cfg.read("routing_table") { |
||||
if data.len() == DEST_COUNT*MAX_HOPS |
||||
{ |
||||
for i in 0..DEST_COUNT { |
||||
for j in 0..MAX_HOPS { |
||||
ret.0[i][j] = data[i*MAX_HOPS+j]; |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
warn!("length of the routing table is incorrect, using default"); |
||||
} |
||||
} |
||||
else { |
||||
warn!("could not read routing table from configuration, using default"); |
||||
} |
||||
info!("routing table: {}", ret); |
||||
ret |
||||
} |
||||
|
||||
#[cfg(has_drtio_routing)] |
||||
pub fn interconnect_enable(routing_table: &RoutingTable, rank: u8, destination: u8) { |
||||
let hop = routing_table.0[destination as usize][rank as usize]; |
||||
unsafe { |
||||
csr::routing_table::destination_write(destination); |
||||
csr::routing_table::hop_write(hop); |
||||
} |
||||
} |
||||
|
||||
#[cfg(has_drtio_routing)] |
||||
pub fn interconnect_disable(destination: u8) { |
||||
unsafe { |
||||
csr::routing_table::destination_write(destination); |
||||
csr::routing_table::hop_write(INVALID_HOP); |
||||
} |
||||
} |
||||
|
||||
#[cfg(has_drtio_routing)] |
||||
pub fn interconnect_enable_all(routing_table: &RoutingTable, rank: u8) { |
||||
for i in 0..DEST_COUNT { |
||||
interconnect_enable(routing_table, rank, i as u8); |
||||
} |
||||
} |
||||
|
||||
#[cfg(has_drtio_routing)] |
||||
pub fn interconnect_disable_all() { |
||||
for i in 0..DEST_COUNT { |
||||
interconnect_disable(i as u8); |
||||
} |
||||
} |
@ -0,0 +1,174 @@ |
||||
use crc; |
||||
|
||||
use core_io::{ErrorKind as IoErrorKind, Error as IoError}; |
||||
|
||||
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor}; |
||||
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds}; |
||||
use crate::mem::mem::DRTIOAUX_MEM; |
||||
use crate::pl::csr::DRTIOAUX; |
||||
use crate::drtioaux_proto::Error as ProtocolError; |
||||
|
||||
pub use crate::drtioaux_proto::Packet; |
||||
|
||||
#[derive(Debug)] |
||||
pub enum Error { |
||||
GatewareError, |
||||
CorruptedPacket, |
||||
|
||||
LinkDown, |
||||
TimedOut, |
||||
UnexpectedReply, |
||||
|
||||
RoutingError, |
||||
|
||||
Protocol(ProtocolError) |
||||
} |
||||
|
||||
impl From<ProtocolError> for Error { |
||||
fn from(value: ProtocolError) -> Error { |
||||
Error::Protocol(value) |
||||
} |
||||
} |
||||
|
||||
impl From<IoError> for Error { |
||||
fn from(value: IoError) -> Error { |
||||
Error::Protocol(ProtocolError::Io(value)) |
||||
} |
||||
} |
||||
|
||||
pub fn reset(linkno: u8) { |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
// clear buffer first to limit race window with buffer overflow
|
||||
// error. We assume the CPU is fast enough so that no two packets
|
||||
// will be received between the buffer and the error flag are cleared.
|
||||
(DRTIOAUX[linkno].aux_rx_present_write)(1); |
||||
(DRTIOAUX[linkno].aux_rx_error_write)(1); |
||||
} |
||||
} |
||||
|
||||
pub fn has_rx_error(linkno: u8) -> bool { |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
let error = (DRTIOAUX[linkno].aux_rx_error_read)() != 0; |
||||
if error { |
||||
(DRTIOAUX[linkno].aux_rx_error_write)(1) |
||||
} |
||||
error |
||||
} |
||||
} |
||||
|
||||
pub fn copy_with_swap(src: *mut u8, dst: *mut u8, len: isize) { |
||||
// for some reason, everything except checksum arrives
|
||||
// with byte order swapped. and it must be sent as such too.
|
||||
unsafe { |
||||
for i in (0..(len-4)).step_by(4) { |
||||
*dst.offset(i) = *src.offset(i+3); |
||||
*dst.offset(i+1) = *src.offset(i+2); |
||||
*dst.offset(i+2) = *src.offset(i+1); |
||||
*dst.offset(i+3) = *src.offset(i); |
||||
} |
||||
// checksum untouched
|
||||
// unrolled for performance
|
||||
*dst.offset(len-4) = *src.offset(len-4); |
||||
*dst.offset(len-3) = *src.offset(len-3); |
||||
*dst.offset(len-2) = *src.offset(len-2); |
||||
*dst.offset(len-1) = *src.offset(len-1); |
||||
} |
||||
} |
||||
|
||||
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error> |
||||
where F: FnOnce(&[u8]) -> Result<T, Error> |
||||
{ |
||||
let linkidx = linkno as usize; |
||||
unsafe { |
||||
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 { |
||||
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u8; |
||||
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize; |
||||
// work buffer, as byte order will need to be swapped, cannot be in place
|
||||
let mut buf: [u8; 1024] = [0; 1024]; |
||||
copy_with_swap(ptr, buf.as_mut_ptr(), len as isize); |
||||
let result = f(&buf[0..len]); |
||||
(DRTIOAUX[linkidx].aux_rx_present_write)(1); |
||||
Ok(Some(result?)) |
||||
} else { |
||||
Ok(None) |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub fn recv(linkno: u8) -> Result<Option<Packet>, Error> { |
||||
if has_rx_error(linkno) { |
||||
return Err(Error::GatewareError) |
||||
} |
||||
|
||||
receive(linkno, |buffer| { |
||||
if buffer.len() < 8 { |
||||
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into()) |
||||
} |
||||
|
||||
let mut reader = Cursor::new(buffer); |
||||
|
||||
let checksum_at = buffer.len() - 4; |
||||
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]); |
||||
reader.set_position(checksum_at); |
||||
if reader.read_u32()? != checksum { |
||||
return Err(Error::CorruptedPacket) |
||||
} |
||||
reader.set_position(0); |
||||
|
||||
Ok(Packet::read_from(&mut reader)?) |
||||
}) |
||||
} |
||||
|
||||
pub fn recv_timeout(linkno: u8, timeout_ms: Option<u64>, |
||||
timer: GlobalTimer) -> Result<Packet, Error>
|
||||
{ |
||||
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10)); |
||||
let limit = timer.get_time() + timeout_ms; |
||||
while timer.get_time() < limit { |
||||
match recv(linkno)? { |
||||
None => (), |
||||
Some(packet) => return Ok(packet), |
||||
} |
||||
} |
||||
Err(Error::TimedOut) |
||||
} |
||||
|
||||
fn transmit<F>(linkno: u8, f: F) -> Result<(), Error> |
||||
where F: FnOnce(&mut [u8]) -> Result<usize, Error> |
||||
{ |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
while (DRTIOAUX[linkno].aux_tx_read)() != 0 {} |
||||
let ptr = DRTIOAUX_MEM[linkno].base as *mut u8; |
||||
let len = DRTIOAUX_MEM[linkno].size / 2; |
||||
// work buffer, works with unaligned mem access
|
||||
let mut buf: [u8; 1024] = [0; 1024];
|
||||
let len = f(&mut buf[0..len])?; |
||||
copy_with_swap(buf.as_mut_ptr(), ptr, len as isize); |
||||
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16); |
||||
(DRTIOAUX[linkno].aux_tx_write)(1); |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
pub fn send(linkno: u8, packet: &Packet) -> Result<(), Error> { |
||||
transmit(linkno, |buffer| { |
||||
let mut writer = Cursor::new(buffer); |
||||
|
||||
packet.write_to(&mut writer)?; |
||||
|
||||
let padding = 4 - (writer.position() % 4); |
||||
if padding != 4 { |
||||
for _ in 0..padding { |
||||
writer.write_u8(0)?; |
||||
} |
||||
} |
||||
|
||||
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]); |
||||
writer.write_u32(checksum)?; |
||||
|
||||
Ok(writer.position()) |
||||
}) |
||||
} |
@ -0,0 +1,139 @@ |
||||
use crc; |
||||
|
||||
use core_io::{ErrorKind as IoErrorKind, Error as IoError}; |
||||
use void::Void; |
||||
use nb; |
||||
|
||||
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds}; |
||||
use libasync::{task, block_async}; |
||||
|
||||
use io::{proto::ProtoRead, proto::ProtoWrite, Cursor}; |
||||
use crate::mem::mem::DRTIOAUX_MEM; |
||||
use crate::pl::csr::DRTIOAUX; |
||||
use crate::drtioaux::{Error, has_rx_error, copy_with_swap}; |
||||
|
||||
pub use crate::drtioaux_proto::Packet; |
||||
|
||||
pub async fn reset(linkno: u8) { |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
// clear buffer first to limit race window with buffer overflow
|
||||
// error. We assume the CPU is fast enough so that no two packets
|
||||
// will be received between the buffer and the error flag are cleared.
|
||||
(DRTIOAUX[linkno].aux_rx_present_write)(1); |
||||
(DRTIOAUX[linkno].aux_rx_error_write)(1); |
||||
} |
||||
} |
||||
|
||||
fn tx_ready(linkno: usize) -> nb::Result<(), Void> { |
||||
unsafe { |
||||
if (DRTIOAUX[linkno].aux_tx_read)() != 0 { |
||||
Err(nb::Error::WouldBlock) |
||||
} |
||||
else { |
||||
Ok(()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
async fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, Error> |
||||
where F: FnOnce(&[u8]) -> Result<T, Error> |
||||
{ |
||||
let linkidx = linkno as usize; |
||||
unsafe { |
||||
if (DRTIOAUX[linkidx].aux_rx_present_read)() == 1 { |
||||
let ptr = (DRTIOAUX_MEM[linkidx].base + DRTIOAUX_MEM[linkidx].size / 2) as *mut u8; |
||||
let len = (DRTIOAUX[linkidx].aux_rx_length_read)() as usize; |
||||
// work buffer, as byte order will need to be swapped, cannot be in place
|
||||
let mut buf: [u8; 1024] = [0; 1024]; |
||||
copy_with_swap(ptr, buf.as_mut_ptr(), len as isize); |
||||
let result = f(&buf[0..len]); |
||||
(DRTIOAUX[linkidx].aux_rx_present_write)(1); |
||||
Ok(Some(result?)) |
||||
} else { |
||||
Ok(None) |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub async fn recv(linkno: u8) -> Result<Option<Packet>, Error> { |
||||
if has_rx_error(linkno) { |
||||
return Err(Error::GatewareError) |
||||
} |
||||
|
||||
receive(linkno, |buffer| { |
||||
if buffer.len() < 8 { |
||||
return Err(IoError::new(IoErrorKind::UnexpectedEof, "Unexpected end").into()) |
||||
} |
||||
|
||||
let mut reader = Cursor::new(buffer); |
||||
|
||||
let checksum_at = buffer.len() - 4; |
||||
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]); |
||||
reader.set_position(checksum_at); |
||||
if reader.read_u32()? != checksum { |
||||
return Err(Error::CorruptedPacket) |
||||
} |
||||
reader.set_position(0); |
||||
|
||||
Ok(Packet::read_from(&mut reader)?) |
||||
}).await |
||||
} |
||||
|
||||
pub async fn recv_timeout(linkno: u8, timeout_ms: Option<u64>, |
||||
timer: GlobalTimer) -> Result<Packet, Error>
|
||||
{ |
||||
let timeout_ms = Milliseconds(timeout_ms.unwrap_or(10)); |
||||
let limit = timer.get_time() + timeout_ms; |
||||
let mut would_block = false; |
||||
while timer.get_time() < limit { |
||||
// to ensure one last time recv would run one last time
|
||||
// in case async would return after timeout
|
||||
if would_block { |
||||
task::r#yield().await; |
||||
} |
||||
match recv(linkno).await? { |
||||
None => { would_block = true; }, |
||||
Some(packet) => return Ok(packet), |
||||
} |
||||
} |
||||
Err(Error::TimedOut) |
||||
} |
||||
|
||||
async fn transmit<F>(linkno: u8, f: F) -> Result<(), Error> |
||||
where F: FnOnce(&mut [u8]) -> Result<usize, Error> |
||||
{ |
||||
let linkno = linkno as usize; |
||||
unsafe { |
||||
let _ = block_async!(tx_ready(linkno)).await; |
||||
let ptr = DRTIOAUX_MEM[linkno].base as *mut u8; |
||||
let len = DRTIOAUX_MEM[linkno].size / 2; |
||||
// work buffer, works with unaligned mem access
|
||||
let mut buf: [u8; 1024] = [0; 1024];
|
||||
let len = f(&mut buf[0..len])?; |
||||
copy_with_swap(buf.as_mut_ptr(), ptr, len as isize); |
||||
(DRTIOAUX[linkno].aux_tx_length_write)(len as u16); |
||||
(DRTIOAUX[linkno].aux_tx_write)(1); |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
pub async fn send(linkno: u8, packet: &Packet) -> Result<(), Error> { |
||||
transmit(linkno, |buffer| { |
||||
let mut writer = Cursor::new(buffer); |
||||
|
||||
packet.write_to(&mut writer)?; |
||||
|
||||
let padding = 4 - (writer.position() % 4); |
||||
if padding != 4 { |
||||
for _ in 0..padding { |
||||
writer.write_u8(0)?; |
||||
} |
||||
} |
||||
|
||||
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]); |
||||
writer.write_u32(checksum)?; |
||||
|
||||
Ok(writer.position()) |
||||
}).await |
||||
} |
@ -0,0 +1,364 @@ |
||||
use core_io::{Write, Read, Error as IoError}; |
||||
|
||||
use io::proto::{ProtoWrite, ProtoRead}; |
||||
|
||||
#[derive(Debug)] |
||||
pub enum Error { |
||||
UnknownPacket(u8), |
||||
Io(IoError) |
||||
} |
||||
|
||||
impl From<IoError> for Error { |
||||
fn from(value: IoError) -> Error { |
||||
Error::Io(value) |
||||
} |
||||
} |
||||
|
||||
#[derive(PartialEq, Debug)] |
||||
pub enum Packet { |
||||
EchoRequest, |
||||
EchoReply, |
||||
ResetRequest, |
||||
ResetAck, |
||||
TSCAck, |
||||
|
||||
DestinationStatusRequest { destination: u8 }, |
||||
DestinationDownReply, |
||||
DestinationOkReply, |
||||
DestinationSequenceErrorReply { channel: u16 }, |
||||
DestinationCollisionReply { channel: u16 }, |
||||
DestinationBusyReply { channel: u16 }, |
||||
|
||||
RoutingSetPath { destination: u8, hops: [u8; 32] }, |
||||
RoutingSetRank { rank: u8 }, |
||||
RoutingAck, |
||||
|
||||
MonitorRequest { destination: u8, channel: u16, probe: u8 }, |
||||
MonitorReply { value: u32 }, |
||||
InjectionRequest { destination: u8, channel: u16, overrd: u8, value: u8 }, |
||||
InjectionStatusRequest { destination: u8, channel: u16, overrd: u8 }, |
||||
InjectionStatusReply { value: u8 }, |
||||
|
||||
I2cStartRequest { destination: u8, busno: u8 }, |
||||
I2cRestartRequest { destination: u8, busno: u8 }, |
||||
I2cStopRequest { destination: u8, busno: u8 }, |
||||
I2cWriteRequest { destination: u8, busno: u8, data: u8 }, |
||||
I2cWriteReply { succeeded: bool, ack: bool }, |
||||
I2cReadRequest { destination: u8, busno: u8, ack: bool }, |
||||
I2cReadReply { succeeded: bool, data: u8 }, |
||||
I2cBasicReply { succeeded: bool }, |
||||
|
||||
SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 }, |
||||
SpiWriteRequest { destination: u8, busno: u8, data: u32 }, |
||||
SpiReadRequest { destination: u8, busno: u8 }, |
||||
SpiReadReply { succeeded: bool, data: u32 }, |
||||
SpiBasicReply { succeeded: bool }, |
||||
|
||||
JdacBasicRequest { destination: u8, dacno: u8, reqno: u8, param: u8 }, |
||||
JdacBasicReply { succeeded: bool, retval: u8 }, |
||||
} |
||||
|
||||
impl Packet { |
||||
pub fn read_from<R>(reader: &mut R) -> Result<Self, Error> |
||||
where R: Read + ?Sized |
||||
{ |
||||
Ok(match reader.read_u8()? { |
||||
0x00 => Packet::EchoRequest, |
||||
0x01 => Packet::EchoReply, |
||||
0x02 => Packet::ResetRequest, |
||||
0x03 => Packet::ResetAck, |
||||
0x04 => Packet::TSCAck, |
||||
|
||||
0x20 => Packet::DestinationStatusRequest { |
||||
destination: reader.read_u8()? |
||||
}, |
||||
0x21 => Packet::DestinationDownReply, |
||||
0x22 => Packet::DestinationOkReply, |
||||
0x23 => Packet::DestinationSequenceErrorReply { |
||||
channel: reader.read_u16()? |
||||
}, |
||||
0x24 => Packet::DestinationCollisionReply { |
||||
channel: reader.read_u16()? |
||||
}, |
||||
0x25 => Packet::DestinationBusyReply { |
||||
channel: reader.read_u16()? |
||||
}, |
||||
|
||||
0x30 => { |
||||
let destination = reader.read_u8()?; |
||||
let mut hops = [0; 32]; |
||||
reader.read_exact(&mut hops)?; |
||||
Packet::RoutingSetPath { |
||||
destination: destination, |
||||
hops: hops |
||||
} |
||||
}, |
||||
0x31 => Packet::RoutingSetRank { |
||||
rank: reader.read_u8()? |
||||
}, |
||||
0x32 => Packet::RoutingAck, |
||||
|
||||
0x40 => Packet::MonitorRequest { |
||||
destination: reader.read_u8()?, |
||||
channel: reader.read_u16()?, |
||||
probe: reader.read_u8()? |
||||
}, |
||||
0x41 => Packet::MonitorReply { |
||||
value: reader.read_u32()? |
||||
}, |
||||
0x50 => Packet::InjectionRequest { |
||||
destination: reader.read_u8()?, |
||||
channel: reader.read_u16()?, |
||||
overrd: reader.read_u8()?, |
||||
value: reader.read_u8()? |
||||
}, |
||||
0x51 => Packet::InjectionStatusRequest { |
||||
destination: reader.read_u8()?, |
||||
channel: reader.read_u16()?, |
||||
overrd: reader.read_u8()? |
||||
}, |
||||
0x52 => Packet::InjectionStatusReply { |
||||
value: reader.read_u8()? |
||||
}, |
||||
|
||||
0x80 => Packet::I2cStartRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()? |
||||
}, |
||||
0x81 => Packet::I2cRestartRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()? |
||||
}, |
||||
0x82 => Packet::I2cStopRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()? |
||||
}, |
||||
0x83 => Packet::I2cWriteRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()?, |
||||
data: reader.read_u8()? |
||||
}, |
||||
0x84 => Packet::I2cWriteReply { |
||||
succeeded: reader.read_bool()?, |
||||
ack: reader.read_bool()? |
||||
}, |
||||
0x85 => Packet::I2cReadRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()?, |
||||
ack: reader.read_bool()? |
||||
}, |
||||
0x86 => Packet::I2cReadReply { |
||||
succeeded: reader.read_bool()?, |
||||
data: reader.read_u8()? |
||||
}, |
||||
0x87 => Packet::I2cBasicReply { |
||||
succeeded: reader.read_bool()? |
||||
}, |
||||
|
||||
0x90 => Packet::SpiSetConfigRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()?, |
||||
flags: reader.read_u8()?, |
||||
length: reader.read_u8()?, |
||||
div: reader.read_u8()?, |
||||
cs: reader.read_u8()? |
||||
}, |
||||
/* 0x91: was Packet::SpiSetXferRequest */ |
||||
0x92 => Packet::SpiWriteRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()?, |
||||
data: reader.read_u32()? |
||||
}, |
||||
0x93 => Packet::SpiReadRequest { |
||||
destination: reader.read_u8()?, |
||||
busno: reader.read_u8()? |
||||
}, |
||||
0x94 => Packet::SpiReadReply { |
||||
succeeded: reader.read_bool()?, |
||||
data: reader.read_u32()? |
||||
}, |
||||
0x95 => Packet::SpiBasicReply { |
||||
succeeded: reader.read_bool()? |
||||
}, |
||||
|
||||
0xa0 => Packet::JdacBasicRequest { |
||||
destination: reader.read_u8()?, |
||||
dacno: reader.read_u8()?, |
||||
reqno: reader.read_u8()?, |
||||
param: reader.read_u8()?, |
||||
}, |
||||
0xa1 => Packet::JdacBasicReply { |
||||
succeeded: reader.read_bool()?, |
||||
retval: reader.read_u8()? |
||||
}, |
||||
|
||||
ty => return Err(Error::UnknownPacket(ty)) |
||||
}) |
||||
} |
||||
|
||||
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError> |
||||
where W: Write + ?Sized |
||||
{ |
||||
|
||||
match *self { |
||||
Packet::EchoRequest => |
||||
writer.write_u8(0x00)?, |
||||
Packet::EchoReply => |
||||
writer.write_u8(0x01)?, |
||||
Packet::ResetRequest => |
||||
writer.write_u8(0x02)?, |
||||
Packet::ResetAck => |
||||
writer.write_u8(0x03)?, |
||||
Packet::TSCAck => |
||||
writer.write_u8(0x04)?, |
||||
|
||||
Packet::DestinationStatusRequest { destination } => { |
||||
writer.write_u8(0x20)?; |
||||
writer.write_u8(destination)?; |
||||
}, |
||||
Packet::DestinationDownReply => |
||||
writer.write_u8(0x21)?, |
||||
Packet::DestinationOkReply => |
||||
writer.write_u8(0x22)?, |
||||
Packet::DestinationSequenceErrorReply { channel } => { |
||||
writer.write_u8(0x23)?; |
||||
writer.write_u16(channel)?; |
||||
}, |
||||
Packet::DestinationCollisionReply { channel } => { |
||||
writer.write_u8(0x24)?; |
||||
writer.write_u16(channel)?; |
||||
}, |
||||
Packet::DestinationBusyReply { channel } => { |
||||
writer.write_u8(0x25)?; |
||||
writer.write_u16(channel)?; |
||||
}, |
||||
|
||||
Packet::RoutingSetPath { destination, hops } => { |
||||
writer.write_u8(0x30)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_all(&hops)?; |
||||
}, |
||||
Packet::RoutingSetRank { rank } => { |
||||
writer.write_u8(0x31)?; |
||||
writer.write_u8(rank)?; |
||||
}, |
||||
Packet::RoutingAck => |
||||
writer.write_u8(0x32)?, |
||||
|
||||
Packet::MonitorRequest { destination, channel, probe } => { |
||||
writer.write_u8(0x40)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u16(channel)?; |
||||
writer.write_u8(probe)?; |
||||
}, |
||||
Packet::MonitorReply { value } => { |
||||
writer.write_u8(0x41)?; |
||||
writer.write_u32(value)?; |
||||
}, |
||||
Packet::InjectionRequest { destination, channel, overrd, value } => { |
||||
writer.write_u8(0x50)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u16(channel)?; |
||||
writer.write_u8(overrd)?; |
||||
writer.write_u8(value)?; |
||||
}, |
||||
Packet::InjectionStatusRequest { destination, channel, overrd } => { |
||||
writer.write_u8(0x51)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u16(channel)?; |
||||
writer.write_u8(overrd)?; |
||||
}, |
||||
Packet::InjectionStatusReply { value } => { |
||||
writer.write_u8(0x52)?; |
||||
writer.write_u8(value)?; |
||||
}, |
||||
|
||||
Packet::I2cStartRequest { destination, busno } => { |
||||
writer.write_u8(0x80)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
}, |
||||
Packet::I2cRestartRequest { destination, busno } => { |
||||
writer.write_u8(0x81)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
}, |
||||
Packet::I2cStopRequest { destination, busno } => { |
||||
writer.write_u8(0x82)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
}, |
||||
Packet::I2cWriteRequest { destination, busno, data } => { |
||||
writer.write_u8(0x83)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
writer.write_u8(data)?; |
||||
}, |
||||
Packet::I2cWriteReply { succeeded, ack } => { |
||||
writer.write_u8(0x84)?; |
||||
writer.write_bool(succeeded)?; |
||||
writer.write_bool(ack)?; |
||||
}, |
||||
Packet::I2cReadRequest { destination, busno, ack } => { |
||||
writer.write_u8(0x85)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
writer.write_bool(ack)?; |
||||
}, |
||||
Packet::I2cReadReply { succeeded, data } => { |
||||
writer.write_u8(0x86)?; |
||||
writer.write_bool(succeeded)?; |
||||
writer.write_u8(data)?; |
||||
}, |
||||
Packet::I2cBasicReply { succeeded } => { |
||||
writer.write_u8(0x87)?; |
||||
writer.write_bool(succeeded)?; |
||||
}, |
||||
|
||||
Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => { |
||||
writer.write_u8(0x90)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
writer.write_u8(flags)?; |
||||
writer.write_u8(length)?; |
||||
writer.write_u8(div)?; |
||||
writer.write_u8(cs)?; |
||||
}, |
||||
Packet::SpiWriteRequest { destination, busno, data } => { |
||||
writer.write_u8(0x92)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
writer.write_u32(data)?; |
||||
}, |
||||
Packet::SpiReadRequest { destination, busno } => { |
||||
writer.write_u8(0x93)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(busno)?; |
||||
}, |
||||
Packet::SpiReadReply { succeeded, data } => { |
||||
writer.write_u8(0x94)?; |
||||
writer.write_bool(succeeded)?; |
||||
writer.write_u32(data)?; |
||||
}, |
||||
Packet::SpiBasicReply { succeeded } => { |
||||
writer.write_u8(0x95)?; |
||||
writer.write_bool(succeeded)?; |
||||
}, |
||||
|
||||
Packet::JdacBasicRequest { destination, dacno, reqno, param } => { |
||||
writer.write_u8(0xa0)?; |
||||
writer.write_u8(destination)?; |
||||
writer.write_u8(dacno)?; |
||||
writer.write_u8(reqno)?; |
||||
writer.write_u8(param)?; |
||||
} |
||||
Packet::JdacBasicReply { succeeded, retval } => { |
||||
writer.write_u8(0xa1)?; |
||||
writer.write_bool(succeeded)?; |
||||
writer.write_u8(retval)?; |
||||
}, |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,68 @@ |
||||
#![no_std] |
||||
#![feature(never_type)] |
||||
|
||||
extern crate log; |
||||
extern crate crc; |
||||
extern crate embedded_hal; |
||||
extern crate core_io; |
||||
extern crate io; |
||||
extern crate libboard_zynq; |
||||
extern crate libregister; |
||||
extern crate libconfig; |
||||
extern crate libcortex_a9; |
||||
extern crate libasync; |
||||
extern crate log_buffer; |
||||
|
||||
#[path = "../../../build/pl.rs"] |
||||
pub mod pl; |
||||
pub mod drtioaux_proto; |
||||
pub mod drtio_routing; |
||||
pub mod logger; |
||||
#[cfg(has_si5324)] |
||||
pub mod si5324; |
||||
#[cfg(has_drtio)] |
||||
pub mod drtioaux; |
||||
#[cfg(has_drtio)] |
||||
pub mod drtioaux_async; |
||||
#[path = "../../../build/mem.rs"] |
||||
pub mod mem; |
||||
|
||||
use core::{cmp, str}; |
||||
use libboard_zynq::slcr; |
||||
use libregister::RegisterW; |
||||
|
||||
pub fn identifier_read(buf: &mut [u8]) -> &str { |
||||
unsafe { |
||||
pl::csr::identifier::address_write(0); |
||||
let len = pl::csr::identifier::data_read(); |
||||
let len = cmp::min(len, buf.len() as u8); |
||||
for i in 0..len { |
||||
pl::csr::identifier::address_write(1 + i); |
||||
buf[i as usize] = pl::csr::identifier::data_read(); |
||||
} |
||||
str::from_utf8_unchecked(&buf[..len as usize]) |
||||
} |
||||
} |
||||
|
||||
pub fn init_gateware() { |
||||
// Set up PS->PL clocks
|
||||
slcr::RegisterBlock::unlocked(|slcr| { |
||||
// As we are touching the mux, the clock may glitch, so reset the PL.
|
||||
slcr.fpga_rst_ctrl.write( |
||||
slcr::FpgaRstCtrl::zeroed() |
||||
.fpga0_out_rst(true) |
||||
.fpga1_out_rst(true) |
||||
.fpga2_out_rst(true) |
||||
.fpga3_out_rst(true) |
||||
); |
||||
slcr.fpga0_clk_ctrl.write( |
||||
slcr::Fpga0ClkCtrl::zeroed() |
||||
.src_sel(slcr::PllSource::IoPll) |
||||
.divisor0(8) |
||||
.divisor1(1) |
||||
); |
||||
slcr.fpga_rst_ctrl.write( |
||||
slcr::FpgaRstCtrl::zeroed() |
||||
); |
||||
}); |
||||
} |
@ -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) { |
||||
} |
||||
} |
@ -0,0 +1,353 @@ |
||||
use core::result; |
||||
use log::info; |
||||
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds}; |
||||
use embedded_hal::blocking::delay::DelayUs; |
||||
#[cfg(not(si5324_soft_reset))] |
||||
use crate::pl::csr; |
||||
|
||||
type Result<T> = result::Result<T, &'static str>; |
||||
|
||||
const ADDRESS: u8 = 0x68; |
||||
|
||||
#[cfg(not(si5324_soft_reset))] |
||||
fn hard_reset(timer: &mut GlobalTimer) { |
||||
unsafe { csr::si5324_rst_n::out_write(0); } |
||||
timer.delay_us(1_000); |
||||
unsafe { csr::si5324_rst_n::out_write(1); } |
||||
timer.delay_us(10_000); |
||||
} |
||||
|
||||
// NOTE: the logical parameters DO NOT MAP to physical values written
|
||||
// into registers. They have to be mapped; see the datasheet.
|
||||
// DSPLLsim reports the logical parameters in the design summary, not
|
||||
// the physical register values.
|
||||
pub struct FrequencySettings { |
||||
pub n1_hs: u8, |
||||
pub nc1_ls: u32, |
||||
pub n2_hs: u8, |
||||
pub n2_ls: u32, |
||||
pub n31: u32, |
||||
pub n32: u32, |
||||
pub bwsel: u8, |
||||
pub crystal_ref: bool |
||||
} |
||||
|
||||
pub enum Input { |
||||
Ckin1, |
||||
Ckin2, |
||||
} |
||||
|
||||
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> { |
||||
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 { |
||||
return Err("NC1_LS must be 0 or even") |
||||
} |
||||
if settings.nc1_ls > (1 << 20) { |
||||
return Err("NC1_LS is too high") |
||||
} |
||||
if (settings.n2_ls % 2) == 1 { |
||||
return Err("N2_LS must be even") |
||||
} |
||||
if settings.n2_ls > (1 << 20) { |
||||
return Err("N2_LS is too high") |
||||
} |
||||
if settings.n31 > (1 << 19) { |
||||
return Err("N31 is too high") |
||||
} |
||||
if settings.n32 > (1 << 19) { |
||||
return Err("N32 is too high") |
||||
} |
||||
let r = FrequencySettings { |
||||
n1_hs: match settings.n1_hs { |
||||
4 => 0b000, |
||||
5 => 0b001, |
||||
6 => 0b010, |
||||
7 => 0b011, |
||||
8 => 0b100, |
||||
9 => 0b101, |
||||
10 => 0b110, |
||||
11 => 0b111, |
||||
_ => return Err("N1_HS has an invalid value") |
||||
}, |
||||
nc1_ls: settings.nc1_ls - 1, |
||||
n2_hs: match settings.n2_hs { |
||||
4 => 0b000, |
||||
5 => 0b001, |
||||
6 => 0b010, |
||||
7 => 0b011, |
||||
8 => 0b100, |
||||
9 => 0b101, |
||||
10 => 0b110, |
||||
11 => 0b111, |
||||
_ => return Err("N2_HS has an invalid value") |
||||
}, |
||||
n2_ls: settings.n2_ls - 1, |
||||
n31: settings.n31 - 1, |
||||
n32: settings.n32 - 1, |
||||
bwsel: settings.bwsel, |
||||
crystal_ref: settings.crystal_ref |
||||
}; |
||||
Ok(r) |
||||
} |
||||
|
||||
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> { |
||||
i2c.start().unwrap(); |
||||
if !i2c.write(ADDRESS << 1).unwrap() { |
||||
return Err("Si5324 failed to ack write address") |
||||
} |
||||
if !i2c.write(reg).unwrap() { |
||||
return Err("Si5324 failed to ack register") |
||||
} |
||||
if !i2c.write(val).unwrap() { |
||||
return Err("Si5324 failed to ack value") |
||||
} |
||||
i2c.stop().unwrap(); |
||||
Ok(()) |
||||
} |
||||
|
||||
#[allow(dead_code)] |
||||
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)) |
||||
} |
||||
|
||||
#[cfg(si5324_soft_reset)] |
||||
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> { |
||||
let val = read(i2c, 136)?; |
||||
write_no_ack_value(i2c, 136, val | 0x80)?; |
||||
timer.delay_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, timer: &mut GlobalTimer) -> Result<()> { |
||||
info!("waiting for Si5324 lock..."); |
||||
let timeout = timer.get_time() + Milliseconds(20_000); |
||||
while !locked(i2c)? { |
||||
// Yes, lock can be really slow.
|
||||
if timer.get_time() > timeout { |
||||
return Err("Si5324 lock timeout"); |
||||
} |
||||
} |
||||
info!(" ...locked"); |
||||
Ok(()) |
||||
} |
||||
|
||||
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> { |
||||
#[cfg(not(si5324_soft_reset))] |
||||
hard_reset(timer); |
||||
|
||||
#[cfg(feature = "target_kasli_soc")] |
||||
{ |
||||
i2c.pca9548_select(0x70, 0)?; |
||||
i2c.pca9548_select(0x71, 1 << 3)?; |
||||
} |
||||
#[cfg(feature = "target_zc706")] |
||||
{ |
||||
i2c.pca9548_select(0x74, 1 << 4)?; |
||||
} |
||||
|
||||
if ident(i2c)? != 0x0182 { |
||||
return Err("Si5324 does not have expected product number"); |
||||
} |
||||
|
||||
#[cfg(si5324_soft_reset)] |
||||
soft_reset(i2c, timer)?; |
||||
Ok(()) |
||||
} |
||||
|
||||
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> { |
||||
let cksel_reg = match input { |
||||
Input::Ckin1 => 0b00, |
||||
Input::Ckin2 => 0b01, |
||||
}; |
||||
init(i2c, timer)?; |
||||
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, timer: &mut GlobalTimer) -> Result<()> { |
||||
let s = map_frequency_settings(settings)?; |
||||
let cksel_reg = match input { |
||||
Input::Ckin1 => 0b00, |
||||
Input::Ckin2 => 0b01, |
||||
}; |
||||
|
||||
init(i2c, timer)?; |
||||
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"); |
||||