artiq/artiq/firmware/runtime/rtio_mgt.rs

305 lines
9.3 KiB
Rust
Raw Normal View History

use board::{csr, config};
use sched::Io;
2016-12-09 19:24:00 +08:00
#[cfg(has_rtio_crg)]
pub mod crg {
2017-01-02 01:23:27 +08:00
use board::{clock, csr};
2016-12-09 19:24:00 +08:00
pub fn init() {
unsafe { csr::rtio_crg::pll_reset_write(0) }
}
pub fn check() -> bool {
unsafe { csr::rtio_crg::pll_locked_read() != 0 }
}
pub fn switch_clock(clk: u8) -> bool {
unsafe {
let cur_clk = csr::rtio_crg::clock_sel_read();
if clk != cur_clk {
csr::rtio_crg::pll_reset_write(1);
csr::rtio_crg::clock_sel_write(clk);
csr::rtio_crg::pll_reset_write(0);
}
}
clock::spin_us(150);
return check()
}
}
#[cfg(not(has_rtio_crg))]
pub mod crg {
pub fn init() {}
pub fn check() -> bool { true }
pub fn switch_clock(_clk: u8) -> bool { true }
}
#[cfg(has_drtio)]
pub mod drtio {
use super::*;
use drtioaux;
2016-12-09 19:24:00 +08:00
pub fn startup(io: &Io) {
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
io.spawn(4096, link_thread);
2016-12-09 19:24:00 +08:00
}
fn link_rx_up(linkno: u8) -> bool {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].rx_up_read)() == 1
}
}
pub fn link_up(linkno: u8) -> bool {
let linkno = linkno as usize;
/* This function may be called by kernels with arbitrary
* linkno values.
*/
if linkno >= csr::DRTIO.len() {
return false;
}
unsafe {
(csr::DRTIO[linkno].link_up_read)() == 1
}
}
fn set_link_up(linkno: u8, up: bool) {
let linkno = linkno as usize;
2016-12-09 19:24:00 +08:00
unsafe {
(csr::DRTIO[linkno].link_up_write)(if up { 1 } else { 0 });
2016-12-09 19:24:00 +08:00
}
}
fn sync_tsc(linkno: u8) {
let linkno = linkno as usize;
unsafe {
(csr::DRTIO[linkno].set_time_write)(1);
while (csr::DRTIO[linkno].set_time_read)() == 1 {}
}
}
fn init_buffer_space(linkno: u8) {
let linkidx = linkno as usize;
2016-12-09 19:24:00 +08:00
unsafe {
2017-09-24 12:23:47 +08:00
(csr::DRTIO[linkidx].o_get_buffer_space_write)(1);
while (csr::DRTIO[linkidx].o_wait_read)() == 1 {}
info!("[LINK#{}] buffer space is {}",
linkno, (csr::DRTIO[linkidx].o_dbg_buffer_space_read)());
2016-12-09 19:24:00 +08:00
}
}
fn ping_remote(linkno: u8, io: &Io) -> u32 {
let mut count = 0;
loop {
if !link_rx_up(linkno) {
return 0
}
count += 1;
drtioaux::hw::send_link(linkno, &drtioaux::Packet::EchoRequest).unwrap();
io.sleep(100).unwrap();
let pr = drtioaux::hw::recv_link(linkno);
match pr {
Ok(Some(drtioaux::Packet::EchoReply)) => return count,
_ => {}
}
}
}
fn process_local_errors(linkno: u8) {
let errors;
let linkidx = linkno as usize;
unsafe {
errors = (csr::DRTIO[linkidx].protocol_error_read)();
(csr::DRTIO[linkidx].protocol_error_write)(errors);
}
if errors != 0 {
error!("[LINK#{}] error(s) found (0x{:02x}):", linkno, errors);
if errors & 1 != 0 {
error!("[LINK#{}] received packet of an unknown type", linkno);
}
if errors & 2 != 0 {
error!("[LINK#{}] received truncated packet", linkno);
}
if errors & 4 != 0 {
2017-09-24 12:23:47 +08:00
error!("[LINK#{}] timeout attempting to get remote buffer space", linkno);
}
2016-12-09 19:24:00 +08:00
}
}
fn process_aux_errors(linkno: u8) {
drtioaux::hw::send_link(linkno, &drtioaux::Packet::RtioErrorRequest).unwrap();
match drtioaux::hw::recv_timeout_link(linkno, None) {
Ok(drtioaux::Packet::RtioNoErrorReply) => (),
Ok(drtioaux::Packet::RtioErrorSequenceErrorReply { channel }) =>
error!("[LINK#{}] RTIO sequence error involving channel {}", linkno, channel),
Ok(drtioaux::Packet::RtioErrorCollisionReply { channel }) =>
error!("[LINK#{}] RTIO collision involving channel {}", linkno, channel),
Ok(drtioaux::Packet::RtioErrorBusyReply { channel }) =>
error!("[LINK#{}] RTIO busy error involving channel {}", linkno, channel),
Ok(_) => error!("[LINK#{}] received unexpected aux packet", linkno),
Err(e) => error!("[LINK#{}] aux packet error ({})", linkno, e)
2016-12-09 19:24:00 +08:00
}
}
2018-02-20 18:48:54 +08:00
pub fn link_thread(io: Io) {
loop {
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
2018-02-20 18:48:54 +08:00
if link_up(linkno) {
/* link was previously up */
if link_rx_up(linkno) {
process_local_errors(linkno);
process_aux_errors(linkno);
} else {
info!("[LINK#{}] link is down", linkno);
2018-02-20 18:48:54 +08:00
set_link_up(linkno, false);
}
} else {
/* link was previously down */
if link_rx_up(linkno) {
info!("[LINK#{}] link RX became up, pinging", linkno);
let ping_count = ping_remote(linkno, &io);
if ping_count > 0 {
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
set_link_up(linkno, true);
init_buffer_space(linkno);
sync_tsc(linkno);
info!("[LINK#{}] link initialization completed", linkno);
} else {
info!("[LINK#{}] ping failed", linkno);
}
}
}
}
io.sleep(200).unwrap();
}
}
pub fn init() {
2018-02-20 18:48:54 +08:00
for linkno in 0..csr::DRTIO.len() {
let linkno = linkno as u8;
if link_up(linkno) {
drtioaux::hw::send_link(linkno,
&drtioaux::Packet::ResetRequest { phy: false }).unwrap();
match drtioaux::hw::recv_timeout_link(linkno, None) {
Ok(drtioaux::Packet::ResetAck) => (),
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno),
Err(e) => error!("[LINK#{}] reset failed, aux packet error ({})", linkno, e)
}
}
}
}
2016-12-09 19:24:00 +08:00
}
#[cfg(not(has_drtio))]
mod drtio {
use super::*;
2016-12-09 19:24:00 +08:00
pub fn startup(_io: &Io) {}
2016-12-12 17:30:41 +08:00
pub fn init() {}
pub fn link_up(_linkno: u8) -> bool { false }
2016-12-09 19:24:00 +08:00
}
fn async_error_thread(io: Io) {
loop {
unsafe {
io.until(|| csr::rtio_core::async_error_read() != 0).unwrap();
let errors = csr::rtio_core::async_error_read();
if errors & 1 != 0 {
error!("RTIO collision involving channel {}",
csr::rtio_core::collision_channel_read());
}
if errors & 2 != 0 {
error!("RTIO busy error involving channel {}",
csr::rtio_core::busy_channel_read());
}
if errors & 4 != 0 {
error!("RTIO sequence error involving channel {}",
csr::rtio_core::sequence_error_channel_read());
}
csr::rtio_core::async_error_write(errors);
}
}
}
pub fn startup(io: &Io) {
2016-12-09 19:24:00 +08:00
crg::init();
#[derive(Debug)]
enum RtioClock {
Internal = 0,
External = 1
2016-12-09 19:24:00 +08:00
};
let clk = config::read("startup_clock", |result| {
match result {
Ok(b"i") => {
info!("using internal startup RTIO clock");
RtioClock::Internal
},
Ok(b"e") => {
info!("using external startup RTIO clock");
RtioClock::External
},
Err(_) => {
info!("using internal startup RTIO clock (by default)");
RtioClock::Internal
},
Ok(_) => {
error!("unrecognized startup_clock configuration entry, \
using internal RTIO clock");
RtioClock::Internal
}
}
});
if !crg::switch_clock(clk as u8) {
2016-12-09 19:24:00 +08:00
error!("startup RTIO clock failed");
warn!("this may cause the system initialization to fail");
warn!("fix clocking and reset the device");
}
drtio::startup(io);
init_core();
io.spawn(4096, async_error_thread);
2016-12-09 19:24:00 +08:00
}
pub fn init_core() {
unsafe {
csr::rtio_core::reset_write(1);
}
2016-12-12 17:30:41 +08:00
drtio::init()
2016-12-09 19:24:00 +08:00
}
2017-01-09 05:06:14 +08:00
#[cfg(has_drtio)]
pub mod drtio_dbg {
use board::csr;
pub fn get_packet_counts(linkno: u8) -> (u32, u32) {
let linkno = linkno as usize;
2017-01-09 05:06:14 +08:00
unsafe {
(csr::DRTIO[linkno].update_packet_cnt_write)(1);
((csr::DRTIO[linkno].packet_cnt_tx_read)(),
(csr::DRTIO[linkno].packet_cnt_rx_read)())
2017-01-09 05:06:14 +08:00
}
}
2017-09-24 12:23:47 +08:00
pub fn get_buffer_space_req_count(linkno: u8) -> u32 {
let linkno = linkno as usize;
unsafe {
2017-09-24 12:23:47 +08:00
(csr::DRTIO[linkno].o_dbg_buffer_space_req_cnt_read)()
}
}
2017-01-09 05:06:14 +08:00
}
#[cfg(not(has_drtio))]
pub mod drtio_dbg {
pub fn get_packet_counts(_linkno: u8) -> (u32, u32) { (0, 0) }
2017-09-24 12:23:47 +08:00
pub fn get_buffer_space_req_count(_linkno: u8) -> u32 { 0 }
}