forked from M-Labs/artiq-zynq
CXP firmware: camera setup init
cxp FW: add camera discovery cxp FW: add multi channel discovery cxp FW: add CXP version negotiate cxp FW: add CXP operation linerate setter cxp FW: add ConnectReset, CXP version readout cxp FW: add connection tester cxp FW: add master channel & topology checker cxp FW: set packet max size to 2KiB cxp FW: add frame buffer cfg & hostconnid
This commit is contained in:
parent
f91ad862bd
commit
387f122b1d
388
src/libboard_artiq/src/cxp.rs
Normal file
388
src/libboard_artiq/src/cxp.rs
Normal file
@ -0,0 +1,388 @@
|
||||
use core::{fmt, result::Result};
|
||||
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
use libboard_zynq::timer::GlobalTimer;
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::{cxp_ctrl::{read_u32, read_u64, reset_tag, send_test_packet, write_bytes_no_ack, write_u32, write_u64},
|
||||
cxp_phys::{self, CXP_CHANNELS, CXP_SPEED},
|
||||
cxp_proto::Error as ProtoError,
|
||||
pl::csr::{cxp_frame_pipeline, CXP}};
|
||||
|
||||
// Bootstrap registers address
|
||||
const STANDARD: u32 = 0x0000;
|
||||
const REVISION: u32 = 0x0004;
|
||||
const CONNECTION_RESET: u32 = 0x4000;
|
||||
const DEVICE_CONNECTION_ID: u32 = 0x4004;
|
||||
const MASTER_HOST_CONNECTION_ID: u32 = 0x4008;
|
||||
|
||||
const CONTROL_PACKET_SIZE_MAX: u32 = 0x400C;
|
||||
const STREAM_PACKET_SIZE_MAX: u32 = 0x4010;
|
||||
const CONNECTION_CFG: u32 = 0x4014;
|
||||
const CONNECTION_CFG_DEFAULT: u32 = 0x4018;
|
||||
|
||||
const TESTMODE: u32 = 0x401C;
|
||||
const TEST_ERROR_COUNT_SELECTOR: u32 = 0x4020;
|
||||
const TEST_ERROR_COUNT: u32 = 0x4024;
|
||||
const TEST_PACKET_COUNT_TX: u32 = 0x4028;
|
||||
const TEST_PACKET_COUNT_RX: u32 = 0x4030;
|
||||
|
||||
const VERSION_SUPPORTED: u32 = 0x4044;
|
||||
const VERSION_USED: u32 = 0x4048;
|
||||
|
||||
// Setup const
|
||||
const MAX_STREAM_PAK_SIZE: u32 = 0x800; // 2 KiB
|
||||
const TX_TEST_CNT: u8 = 10;
|
||||
pub const MASTER_CHANNEL: u8 = 0;
|
||||
const HOST_CONNECTION_ID: u32 = 0xC001C0DE; // TODO: rename this
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
CameraNotDetected,
|
||||
ConnectionLost,
|
||||
UnstableDownConn,
|
||||
UnstableUpConn,
|
||||
UnsupportedSpeed(u32),
|
||||
UnsupportedTopology,
|
||||
UnsupportedVersion,
|
||||
Protocol(ProtoError),
|
||||
}
|
||||
|
||||
impl From<ProtoError> for Error {
|
||||
fn from(value: ProtoError) -> Error {
|
||||
Error::Protocol(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::CameraNotDetected => write!(f, "CameraNotDetected"),
|
||||
&Error::ConnectionLost => write!(f, "ConnectionLost Some active channels cannot be detected"),
|
||||
&Error::UnstableDownConn => write!(f, "UnstableDownConn DownConnection test failed"),
|
||||
&Error::UnstableUpConn => write!(f, "UnstableUpConn UpConnection test failed"),
|
||||
&Error::UnsupportedSpeed(linerate_code) => write!(
|
||||
f,
|
||||
"UnsupportedSpeed {:#X} linerate code is not supported",
|
||||
linerate_code
|
||||
),
|
||||
&Error::UnsupportedTopology => write!(
|
||||
f,
|
||||
"UnsupportedTopology CH#1 should be the master channel while CH#2-4 should be connected to extension \
|
||||
#1-3 respectively"
|
||||
),
|
||||
&Error::UnsupportedVersion => write!(
|
||||
f,
|
||||
"UnsupportedVersion Cannot find a compatible protocol version between the grabber & camera"
|
||||
),
|
||||
&Error::Protocol(ref err) => write!(f, "ProtocolError {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup() -> Result<bool, Error> {
|
||||
// assume timer was initialized successfully
|
||||
let mut timer = unsafe { GlobalTimer::get() };
|
||||
camera_setup(&mut timer)
|
||||
}
|
||||
|
||||
fn scan_active_channels() -> u8 {
|
||||
let mut active_channels: u8 = 0;
|
||||
for ch in 0..CXP_CHANNELS {
|
||||
if unsafe { (CXP[ch as usize].downconn_rx_ready_read)() } == 1 {
|
||||
info!("ch#{} is up <---------------------------------", ch);
|
||||
active_channels += 1;
|
||||
}
|
||||
}
|
||||
active_channels
|
||||
}
|
||||
|
||||
fn discover_camera(timer: &mut GlobalTimer) -> Result<(), Error> {
|
||||
// Section 7.6 (CXP-001-2021)
|
||||
// 1.25Gbps (CXP_1) and 3.125Gbps (CXP_3) are the discovery rate
|
||||
// both linerate need to be checked as camera only support ONE of discovery rates
|
||||
for speed in [CXP_SPEED::CXP_1, CXP_SPEED::CXP_3].iter() {
|
||||
cxp_phys::change_linerate(*speed);
|
||||
|
||||
// Section 12.1.2 (CXP-001-2021)
|
||||
// send ConnectionReset on all channels -> wait 200ms -> scan for active channels
|
||||
for ch in 0..CXP_CHANNELS {
|
||||
write_bytes_no_ack(ch, CONNECTION_RESET, &1_u32.to_be_bytes(), false)?;
|
||||
}
|
||||
timer.delay_ms(200);
|
||||
|
||||
if scan_active_channels() > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(Error::CameraNotDetected)
|
||||
}
|
||||
|
||||
fn check_master_channel() -> Result<(), Error> {
|
||||
if read_u32(MASTER_CHANNEL, DEVICE_CONNECTION_ID, false)? == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
warn!(
|
||||
"Channel #{} is not connected to master channel of the camera",
|
||||
MASTER_CHANNEL
|
||||
);
|
||||
Err(Error::UnsupportedTopology)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_connection_topology(active_channels: u8) -> Result<(), Error> {
|
||||
// only the simple topology MASTER:ch0, extension:ch1,2,3 is supported right now
|
||||
for ch in 0..active_channels {
|
||||
if read_u32(ch, DEVICE_CONNECTION_ID, false)? != ch as u32 {
|
||||
warn!("Channel #{} is not connected to the right port of the camera", ch);
|
||||
return Err(Error::UnsupportedTopology);
|
||||
};
|
||||
match ch {
|
||||
0 => info!("CHANNEL #{} is active - master connection", ch),
|
||||
_ => info!("CHANNEL #{} is active - extension connection", ch),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn negotiate_active_channels(timer: &mut GlobalTimer) -> Result<u8, Error> {
|
||||
let mut available_chs = read_u32(MASTER_CHANNEL, CONNECTION_CFG_DEFAULT, false)? >> 16;
|
||||
available_chs = available_chs.min(CXP_CHANNELS as u32);
|
||||
|
||||
// activate channels on camera but preserve the discovery linerate
|
||||
let current_cfg = read_u32(MASTER_CHANNEL, CONNECTION_CFG, false)?;
|
||||
write_u32(
|
||||
MASTER_CHANNEL,
|
||||
CONNECTION_CFG,
|
||||
current_cfg & 0xFFFF | available_chs << 16,
|
||||
false,
|
||||
)?;
|
||||
|
||||
timer.delay_ms(200);
|
||||
let active_channels = scan_active_channels();
|
||||
check_connection_topology(active_channels)?;
|
||||
|
||||
if available_chs > active_channels as u32 {
|
||||
info!(
|
||||
"Only detected {} channel(s), disabling excess channels on camera",
|
||||
active_channels
|
||||
);
|
||||
write_u32(
|
||||
MASTER_CHANNEL,
|
||||
CONNECTION_CFG,
|
||||
current_cfg & 0xFFFF | ((active_channels as u32) << 16),
|
||||
false,
|
||||
)?;
|
||||
|
||||
timer.delay_ms(200);
|
||||
// check no active channels are down after the cfg change
|
||||
if active_channels != scan_active_channels() {
|
||||
return Err(Error::ConnectionLost);
|
||||
}
|
||||
}
|
||||
Ok(active_channels)
|
||||
}
|
||||
|
||||
fn set_host_connection_id() -> Result<(), Error> {
|
||||
// TODO: not mandatory??
|
||||
write_u32(MASTER_CHANNEL, MASTER_HOST_CONNECTION_ID, HOST_CONNECTION_ID, false)?;
|
||||
let reg = read_u32(MASTER_CHANNEL, CONNECTION_CFG, false)?;
|
||||
info!("host connection id set as = {}", reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn negotiate_cxp_version() -> Result<bool, Error> {
|
||||
let rev = read_u32(MASTER_CHANNEL, REVISION, false)?;
|
||||
|
||||
let mut major_rev: u32 = rev >> 16;
|
||||
let mut minor_rev: u32 = rev & 0xFF;
|
||||
info!("Camera's CXP revision is {}.{}", major_rev, minor_rev);
|
||||
|
||||
// Section 12.1.4 (CXP-001-2021)
|
||||
// For CXP 2.0 and onward, Host need to check the VersionSupported register to determine
|
||||
// the highest common version that supported by both device & host
|
||||
if major_rev >= 2 {
|
||||
let reg = read_u32(MASTER_CHANNEL, VERSION_SUPPORTED, false)?;
|
||||
|
||||
if ((reg >> 3) & 1) == 1 {
|
||||
major_rev = 2;
|
||||
minor_rev = 1;
|
||||
} else if ((reg >> 2) & 1) == 1 {
|
||||
major_rev = 2;
|
||||
minor_rev = 0;
|
||||
} else {
|
||||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
|
||||
write_u32(MASTER_CHANNEL, VERSION_USED, major_rev << 16 | minor_rev, false)?;
|
||||
}
|
||||
info!(
|
||||
"Camera supports CXP {}.{}, using CXP {}.{} protcol now",
|
||||
major_rev, minor_rev, major_rev, minor_rev
|
||||
);
|
||||
|
||||
Ok(major_rev >= 2)
|
||||
}
|
||||
|
||||
fn negotiate_pak_max_size(with_tag: bool) -> Result<(), Error> {
|
||||
let reg = read_u32(MASTER_CHANNEL, CONTROL_PACKET_SIZE_MAX, with_tag)?;
|
||||
info!("Max CTRL PAK size = {:#010X}", reg);
|
||||
|
||||
let reg = read_u32(MASTER_CHANNEL, STREAM_PACKET_SIZE_MAX, with_tag)?;
|
||||
info!("Max STREAM PAK size = {:#010X}", reg);
|
||||
// TODO: set stream packet size
|
||||
write_u32(MASTER_CHANNEL, STREAM_PACKET_SIZE_MAX, MAX_STREAM_PAK_SIZE, with_tag)?;
|
||||
let reg = read_u32(MASTER_CHANNEL, STREAM_PACKET_SIZE_MAX, with_tag)?;
|
||||
info!("Max STREAM PAK size = {:#010X}", reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_magic_word() -> Result<(), Error> {
|
||||
let reg = read_u32(MASTER_CHANNEL, STANDARD, true)?;
|
||||
info!("CoaXPress magic WORD = {:#010X} !!!", reg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_cxp_speed(linerate_code: u32) -> Option<CXP_SPEED> {
|
||||
match linerate_code {
|
||||
0x28 => Some(CXP_SPEED::CXP_1),
|
||||
0x30 => Some(CXP_SPEED::CXP_2),
|
||||
0x38 => Some(CXP_SPEED::CXP_3),
|
||||
0x40 => Some(CXP_SPEED::CXP_5),
|
||||
0x48 => Some(CXP_SPEED::CXP_6),
|
||||
0x50 => Some(CXP_SPEED::CXP_10),
|
||||
0x58 => Some(CXP_SPEED::CXP_12),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_operation_linerate(active_channels: u8, with_tag: bool, timer: &mut GlobalTimer) -> Result<(), Error> {
|
||||
let current_cfg = read_u32(MASTER_CHANNEL, CONNECTION_CFG, with_tag)?;
|
||||
info!("Current connection cfg = {:#010X}", current_cfg);
|
||||
|
||||
let recommended_linerate_code = read_u32(MASTER_CHANNEL, CONNECTION_CFG_DEFAULT, with_tag)? & 0xFFFF;
|
||||
info!("recommended_linerate_code = {:#010X}", recommended_linerate_code);
|
||||
|
||||
if let Some(speed) = decode_cxp_speed(recommended_linerate_code) {
|
||||
// preserve the number of active channels
|
||||
write_u32(
|
||||
MASTER_CHANNEL,
|
||||
CONNECTION_CFG,
|
||||
current_cfg & 0xFFFF0000 | recommended_linerate_code,
|
||||
with_tag,
|
||||
)?;
|
||||
|
||||
cxp_phys::change_linerate(speed);
|
||||
timer.delay_ms(200);
|
||||
|
||||
// check no active channels are down after linerate change
|
||||
if scan_active_channels() == active_channels {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ConnectionLost)
|
||||
}
|
||||
} else {
|
||||
Err(Error::UnsupportedSpeed(recommended_linerate_code))
|
||||
}
|
||||
}
|
||||
|
||||
fn test_counter_reset(channel: u8, with_tag: bool) -> Result<(), Error> {
|
||||
unsafe { (CXP[channel as usize].downconn_bootstrap_test_counts_reset_write)(1) };
|
||||
write_u32(MASTER_CHANNEL, TEST_ERROR_COUNT_SELECTOR, channel as u32, with_tag)?;
|
||||
write_u32(MASTER_CHANNEL, TEST_ERROR_COUNT, 0, with_tag)?;
|
||||
write_u64(MASTER_CHANNEL, TEST_PACKET_COUNT_TX, 0, with_tag)?;
|
||||
write_u64(MASTER_CHANNEL, TEST_PACKET_COUNT_RX, 0, with_tag)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_test_result(channel: u8, with_tag: bool) -> Result<(), Error> {
|
||||
write_u32(MASTER_CHANNEL, TEST_ERROR_COUNT_SELECTOR, channel as u32, with_tag)?;
|
||||
|
||||
// Section 9.9.3 (CXP-001-2021)
|
||||
// verify grabber -> camera connection test result
|
||||
if read_u64(MASTER_CHANNEL, TEST_PACKET_COUNT_RX, with_tag)? != TX_TEST_CNT as u64 {
|
||||
return Err(Error::UnstableUpConn);
|
||||
};
|
||||
if read_u32(MASTER_CHANNEL, TEST_ERROR_COUNT, with_tag)? > 0 {
|
||||
return Err(Error::UnstableUpConn);
|
||||
};
|
||||
|
||||
// Section 9.9.4 (CXP-001-2021)
|
||||
// verify camera -> grabber connection test result
|
||||
let camera_test_pak_cnt = read_u64(MASTER_CHANNEL, TEST_PACKET_COUNT_TX, true)?;
|
||||
unsafe {
|
||||
if (CXP[channel as usize].downconn_bootstrap_test_packet_counter_read)() != camera_test_pak_cnt as u16 {
|
||||
info!(
|
||||
"CHANNEL #{} test packet cnt = {}",
|
||||
channel,
|
||||
(CXP[channel as usize].downconn_bootstrap_test_packet_counter_read)()
|
||||
);
|
||||
return Err(Error::UnstableDownConn);
|
||||
};
|
||||
if (CXP[channel as usize].downconn_bootstrap_test_error_counter_read)() > 0 {
|
||||
info!(
|
||||
"CHANNEL #{} test packet error cnt = {}",
|
||||
channel,
|
||||
(CXP[channel as usize].downconn_bootstrap_test_error_counter_read)()
|
||||
);
|
||||
return Err(Error::UnstableDownConn);
|
||||
};
|
||||
};
|
||||
info!("CHANNEL #{} pass testing", channel);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_channels_stability(active_channels: u8, with_tag: bool, timer: &mut GlobalTimer) -> Result<(), Error> {
|
||||
// let active_channels = active_channels as usize;
|
||||
for ch in 0..active_channels {
|
||||
test_counter_reset(ch, with_tag)?;
|
||||
}
|
||||
|
||||
// grabber -> camera connection test
|
||||
for ch in 0..active_channels {
|
||||
for _ in 0..TX_TEST_CNT {
|
||||
send_test_packet(ch)?;
|
||||
// sending the whole test sequence @ 20.833Mbps will take a minimum of 1.972ms
|
||||
// and leave some room to send IDLE word
|
||||
timer.delay_ms(2);
|
||||
}
|
||||
}
|
||||
|
||||
// camera -> grabber connection test
|
||||
// enabling the TESTMODE on master channel will send test packets on all channels
|
||||
// and ctrl packet write overhead is used as a delay
|
||||
write_u32(MASTER_CHANNEL, TESTMODE, 1, with_tag)?;
|
||||
write_u32(MASTER_CHANNEL, TESTMODE, 0, with_tag)?;
|
||||
|
||||
for ch in 0..active_channels {
|
||||
verify_test_result(ch, with_tag)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn camera_setup(timer: &mut GlobalTimer) -> Result<bool, Error> {
|
||||
reset_tag();
|
||||
discover_camera(timer)?;
|
||||
check_master_channel()?;
|
||||
|
||||
let active_channels = negotiate_active_channels(timer)?;
|
||||
set_host_connection_id()?;
|
||||
let cxp_v2_or_greater = negotiate_cxp_version()?;
|
||||
|
||||
negotiate_pak_max_size(cxp_v2_or_greater)?;
|
||||
set_operation_linerate(active_channels, cxp_v2_or_greater, timer)?;
|
||||
|
||||
// DEBUG: print
|
||||
check_magic_word()?;
|
||||
|
||||
test_channels_stability(active_channels, cxp_v2_or_greater, timer)?;
|
||||
|
||||
// unsafe{
|
||||
// // don't set two identical routingid, the router cannot handle it
|
||||
// // it can handle it after adding the priority decoder
|
||||
// cxp_frame_pipeline::buffer_1_routingid_write(0x00);
|
||||
// }
|
||||
|
||||
Ok(cxp_v2_or_greater)
|
||||
}
|
Loading…
Reference in New Issue
Block a user