add libboard_artiq (to be shared between runtime and satman)

Reviewed-on: M-Labs/artiq-zynq#139
Co-authored-by: mwojcik <mw@m-labs.hk>
Co-committed-by: mwojcik <mw@m-labs.hk>
This commit is contained in:
mwojcik 2021-10-06 13:02:28 +08:00 committed by sb10q
parent d7f45d473e
commit e6863263b4
9 changed files with 1366 additions and 0 deletions

View File

@ -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" }

View File

@ -0,0 +1,5 @@
extern crate build_zynq;
fn main() {
build_zynq::cfg();
}

View File

@ -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);
}
}

View File

@ -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())
})
}

View File

@ -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
}

View File

@ -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(())
}
}

View File

@ -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()
);
});
}

View 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) {
}
}

View File

@ -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");
}
if !has_ckin(i2c, input)? {
return Err("Si5324 misses clock input signal");
}
monitor_lock(i2c, timer)?;
Ok(())
}
pub fn select_input(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> 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, timer)?;
Ok(())
}
#[cfg(has_siphaser)]
pub mod siphaser {
use super::*;
use crate::pl::csr;
pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: &mut GlobalTimer) -> Result<()> {
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (1 << 5))?; // DHOLD=1
unsafe {
csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 });
}
let val = read(i2c, 3)?;
write(i2c, 3, (val & 0xdf) | (0 << 5))?; // DHOLD=0
monitor_lock(i2c, timer)?;
Ok(())
}
fn phase_shift(direction: u8, timer: &mut GlobalTimer) {
unsafe {
csr::siphaser::phase_shift_write(direction);
while csr::siphaser::phase_shift_done_read() == 0 {}
}
// wait for the Si5324 loop to stabilize
timer.delay_us(500);
}
fn has_error(timer: &mut GlobalTimer) -> bool {
unsafe {
csr::siphaser::error_write(1);
}
timer.delay_us(5_000);
unsafe {
csr::siphaser::error_read() != 0
}
}
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32> {
let mut nshifts = 0;
let mut previous = has_error(timer);
loop {
phase_shift(1, timer);
nshifts += 1;
let current = has_error(timer);
if previous != target && current == target {
return Ok(nshifts);
}
if nshifts > 5000 {
return Err("failed to find timing error edge");
}
previous = current;
}
}
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<()> {
let jitter_margin = 32;
let lead = find_edge(false, timer)?;
for _ in 0..jitter_margin {
phase_shift(1, timer);
}
let width = find_edge(true, timer)? + jitter_margin;
// width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));
// Apply reverse phase shift for half the width to get into the
// middle of the working region.
for _ in 0..width/2 {
phase_shift(0, timer);
}
Ok(())
}
}