artiq/artiq/firmware/satman/main.rs

694 lines
26 KiB
Rust

#![feature(never_type, panic_info_message, llvm_asm)]
#![no_std]
#[macro_use]
extern crate log;
#[macro_use]
extern crate board_misoc;
extern crate board_artiq;
extern crate riscv;
use core::convert::TryFrom;
use board_misoc::{csr, ident, clock, uart_logger, i2c};
#[cfg(has_si5324)]
use board_artiq::si5324;
#[cfg(has_wrpll)]
use board_artiq::wrpll;
use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing;
#[cfg(has_hmc830_7043)]
use board_artiq::hmc830_7043;
use riscv::register::{mcause, mepc};
mod repeater;
#[cfg(has_jdcg)]
mod jdcg;
#[cfg(any(has_ad9154, 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);
#[cfg(has_ad9154)]
let (succeeded, retval) = {
#[cfg(rtio_frequency = "125.0")]
const LINERATE: u64 = 5_000_000_000;
#[cfg(rtio_frequency = "150.0")]
const LINERATE: u64 = 6_000_000_000;
match _reqno {
jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0),
jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) },
jdac_common::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0),
jdac_common::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0),
jdac_common::SYSREF_DELAY_DAC => { board_artiq::hmc830_7043::hmc7043::sysref_delay_dac(_dacno, _param); (true, 0) },
jdac_common::SYSREF_SLIP => { board_artiq::hmc830_7043::hmc7043::sysref_slip(); (true, 0) },
jdac_common::SYNC => {
match board_artiq::ad9154::sync(_dacno) {
Ok(false) => (true, 0),
Ok(true) => (true, 1),
Err(e) => {
error!("DAC sync failed: {}", e);
(false, 0)
}
}
},
jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8),
jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8),
_ => (false, 0)
}
};
#[cfg(not(has_ad9154))]
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();
uart_logger::ConsoleLogger::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_hmc830_7043)]
/* must be the first SPI init because of HMC830 SPI mode selection */
hmc830_7043::init().expect("cannot initialize HMC830/7043");
#[cfg(has_ad9154)]
{
jdac_common::init_ddmtd().expect("failed to initialize SYSREF DDMTD core");
for dacno in 0..csr::CONFIG_AD9154_COUNT {
board_artiq::ad9154::reset_and_detect(dacno as u8).expect("AD9154 DAC not detected");
}
}
#[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 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 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(_regs: *const u32) {
let pc = mepc::read();
let cause = mcause::read().cause();
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(u32::try_from(pc).unwrap());
panic!("exception {:?} at PC 0x{:x}", cause, u32::try_from(pc).unwrap())
}
#[no_mangle]
pub extern fn abort() {
println!("aborted");
loop {}
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_handler]
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 {}
}