forked from M-Labs/artiq-zynq
Compare commits
10 Commits
599442dc0d
...
4c9c9dfd6c
Author | SHA1 | Date | |
---|---|---|---|
4c9c9dfd6c | |||
e07dad71d5 | |||
d0c34671d7 | |||
82a1b38a19 | |||
db76dfc209 | |||
b0ceac0f3a | |||
a0673f13a1 | |||
6086b867c8 | |||
17f59e2353 | |||
f080bee029 |
3
src/Cargo.lock
generated
3
src/Cargo.lock
generated
@ -273,6 +273,7 @@ name = "libboard_artiq"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"build_zynq",
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"crc",
|
||||
"embedded-hal",
|
||||
@ -546,9 +547,7 @@ name = "satman"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"build_zynq",
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"crc",
|
||||
"cslice",
|
||||
"embedded-hal",
|
||||
"io",
|
||||
|
@ -432,6 +432,7 @@ class GenericSatellite(SoCCore):
|
||||
clk_freq = description["rtio_frequency"]
|
||||
with_wrpll = description["enable_wrpll"]
|
||||
|
||||
has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"])
|
||||
self.acpki = acpki
|
||||
|
||||
platform = kasli_soc.Platform()
|
||||
@ -480,6 +481,8 @@ class GenericSatellite(SoCCore):
|
||||
|
||||
self.rtio_channels = []
|
||||
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||
if has_drtio_over_eem:
|
||||
self.eem_drtio_channels = []
|
||||
if has_grabber:
|
||||
self.grabber_csr_group = []
|
||||
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
|
||||
@ -494,15 +497,15 @@ class GenericSatellite(SoCCore):
|
||||
|
||||
self.submodules.rtio_tsc = rtio.TSC(glbl_fine_ts_width=3)
|
||||
|
||||
drtioaux_csr_group = []
|
||||
drtioaux_memory_group = []
|
||||
drtiorep_csr_group = []
|
||||
self.drtioaux_csr_group = []
|
||||
self.drtioaux_memory_group = []
|
||||
self.drtiorep_csr_group = []
|
||||
self.drtio_cri = []
|
||||
for i in range(len(self.gt_drtio.channels)):
|
||||
coreaux_name = "drtioaux" + str(i)
|
||||
memory_name = "drtioaux" + str(i) + "_mem"
|
||||
drtioaux_csr_group.append(coreaux_name)
|
||||
drtioaux_memory_group.append(memory_name)
|
||||
self.drtioaux_csr_group.append(coreaux_name)
|
||||
self.drtioaux_memory_group.append(memory_name)
|
||||
|
||||
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
||||
|
||||
@ -515,7 +518,7 @@ class GenericSatellite(SoCCore):
|
||||
self.csr_devices.append("drtiosat")
|
||||
else:
|
||||
corerep_name = "drtiorep" + str(i-1)
|
||||
drtiorep_csr_group.append(corerep_name)
|
||||
self.drtiorep_csr_group.append(corerep_name)
|
||||
|
||||
core = cdr(DRTIORepeater(
|
||||
self.rtio_tsc, self.gt_drtio.channels[i]))
|
||||
@ -538,9 +541,9 @@ class GenericSatellite(SoCCore):
|
||||
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
|
||||
self.config["HAS_DRTIO"] = None
|
||||
self.config["HAS_DRTIO_ROUTING"] = None
|
||||
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||
self.add_csr_group("drtiorep", drtiorep_csr_group)
|
||||
if has_drtio_over_eem:
|
||||
self.add_eem_drtio(self.eem_drtio_channels)
|
||||
self.add_drtio_cpuif_groups()
|
||||
|
||||
if self.acpki:
|
||||
self.config["KI_IMPL"] = "acp"
|
||||
@ -625,6 +628,50 @@ class GenericSatellite(SoCCore):
|
||||
self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready)
|
||||
for i, channel in enumerate(self.gt_drtio.channels)]
|
||||
|
||||
def add_eem_drtio(self, eem_drtio_channels):
|
||||
# Must be called before constructing CRIInterconnectShared
|
||||
self.submodules.eem_transceiver = eem_serdes.EEMSerdes(self.platform, eem_drtio_channels)
|
||||
self.csr_devices.append("eem_transceiver")
|
||||
self.config["HAS_DRTIO_EEM"] = None
|
||||
self.config["EEM_DRTIO_COUNT"] = len(eem_drtio_channels)
|
||||
|
||||
for i in range(len(self.eem_transceiver.channels)):
|
||||
channel = i + len(self.gt_drtio.channels)
|
||||
coreaux_name = "drtioaux" + str(channel)
|
||||
memory_name = "drtioaux" + str(channel) + "_mem"
|
||||
self.drtioaux_csr_group.append(coreaux_name)
|
||||
self.drtioaux_memory_group.append(memory_name)
|
||||
|
||||
cdr = ClockDomainsRenamer({"rtio_rx": "sys"})
|
||||
corerep_name = "drtiorep" + str(channel-1)
|
||||
self.drtiorep_csr_group.append(corerep_name)
|
||||
|
||||
core = cdr(DRTIORepeater(
|
||||
self.rtio_tsc, self.eem_transceiver.channels[i]))
|
||||
setattr(self.submodules, corerep_name, core)
|
||||
self.drtio_cri.append(core.cri)
|
||||
self.csr_devices.append(corerep_name)
|
||||
|
||||
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
|
||||
setattr(self.submodules, coreaux_name, coreaux)
|
||||
self.csr_devices.append(coreaux_name)
|
||||
|
||||
mem_size = coreaux.get_mem_size()
|
||||
tx_port = coreaux.get_tx_port()
|
||||
rx_port = coreaux.get_rx_port()
|
||||
memory_address = self.axi2csr.register_port(tx_port, mem_size)
|
||||
# rcv in upper half of the memory, thus added second
|
||||
self.axi2csr.register_port(rx_port, mem_size)
|
||||
# and registered in PS interface
|
||||
# manually, because software refers to rx/tx by halves of entire memory block, not names
|
||||
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, mem_size * 2)
|
||||
|
||||
def add_drtio_cpuif_groups(self):
|
||||
self.add_csr_group("drtiorep", self.drtiorep_csr_group)
|
||||
self.add_csr_group("drtioaux", self.drtioaux_csr_group)
|
||||
self.add_memory_group("drtioaux_mem", self.drtioaux_memory_group)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ARTIQ device binary builder for generic Kasli-SoC systems")
|
||||
|
@ -24,6 +24,7 @@ core_io = { git = "https://git.m-labs.hk/M-Labs/rs-core_io.git", rev = "e9d3edf0
|
||||
embedded-hal = "0.2"
|
||||
nb = "1.0"
|
||||
void = { version = "1", default-features = false }
|
||||
byteorder = { version = "1.3", default-features = false }
|
||||
|
||||
io = { path = "../libio", features = ["byteorder"] }
|
||||
libboard_zynq = { path = "@@ZYNQ_RS@@/libboard_zynq" }
|
||||
|
305
src/libboard_artiq/src/cxp_camera_setup.rs
Normal file
305
src/libboard_artiq/src/cxp_camera_setup.rs
Normal file
@ -0,0 +1,305 @@
|
||||
use core::fmt;
|
||||
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
||||
use log::debug;
|
||||
|
||||
use crate::{cxp_ctrl::Error as CtrlErr,
|
||||
cxp_packet::{read_u32, read_u64, reset_tag, send_test_packet, write_bytes_no_ack, write_u32, write_u64},
|
||||
cxp_phys::{rx, tx, CXPSpeed},
|
||||
pl::csr};
|
||||
|
||||
// Bootstrap registers address
|
||||
const REVISION: u32 = 0x0004;
|
||||
const CONNECTION_RESET: u32 = 0x4000;
|
||||
const DEVICE_CONNECTION_ID: u32 = 0x4004;
|
||||
const MASTER_HOST_CONNECTION_ID: u32 = 0x4008;
|
||||
|
||||
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 CHANNEL_LEN: u8 = 1;
|
||||
const HOST_CONNECTION_ID: u32 = 0x00006303; // TODO: rename to CXP grabber sinara number when it comes out
|
||||
// The MAX_STREAM_PAK_SIZE should be set as large as possible - Section 9.5.2 (CXP-001-2021)
|
||||
// Since the ROI pipeline just consume all pixel data without buffering, any big number will do.
|
||||
const MAX_STREAM_PAK_SIZE: u32 = 16384; // 16 KiB
|
||||
const TX_TEST_CNT: u8 = 10;
|
||||
// From DS191 (v1.18.1), max CDR time lock is 37*10^6 UI,
|
||||
// 37*10^6 UI at lowest CXP linerate of 1.25Gbps = 29.6 ms, double it to account for overhead
|
||||
const MONITOR_TIMEOUT_MS: u64 = 60;
|
||||
|
||||
pub enum Error {
|
||||
CameraNotDetected,
|
||||
ConnectionLost,
|
||||
UnstableRX,
|
||||
UnstableTX,
|
||||
UnsupportedSpeed(u32),
|
||||
UnsupportedTopology,
|
||||
UnsupportedVersion,
|
||||
CtrlPacketError(CtrlErr),
|
||||
}
|
||||
|
||||
impl From<CtrlErr> for Error {
|
||||
fn from(value: CtrlErr) -> Error {
|
||||
Error::CtrlPacketError(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 - Channel #0 cannot be detected"),
|
||||
&Error::UnstableRX => write!(f, "UnstableRX - RX connection test failed"),
|
||||
&Error::UnstableTX => write!(f, "UnstableTX - TX connection test failed"),
|
||||
&Error::UnsupportedSpeed(linerate_code) => write!(
|
||||
f,
|
||||
"UnsupportedSpeed - {:#X} linerate code is not supported",
|
||||
linerate_code
|
||||
),
|
||||
&Error::UnsupportedTopology => {
|
||||
write!(
|
||||
f,
|
||||
"UnsupportedTopology - Channel #0 should be connected to the master channel"
|
||||
)
|
||||
}
|
||||
&Error::UnsupportedVersion => write!(
|
||||
f,
|
||||
"UnsupportedVersion - Cannot find a compatible protocol version between the cxp grabber & camera"
|
||||
),
|
||||
&Error::CtrlPacketError(ref err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn master_channel_ready() -> bool {
|
||||
unsafe { csr::cxp_grabber::core_rx_ready_read() == 1 }
|
||||
}
|
||||
|
||||
fn monitor_channel_status_timeout(timer: GlobalTimer) -> Result<(), Error> {
|
||||
let limit = timer.get_time() + Milliseconds(MONITOR_TIMEOUT_MS);
|
||||
while timer.get_time() < limit {
|
||||
if master_channel_ready() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(Error::ConnectionLost)
|
||||
}
|
||||
|
||||
pub fn discover_camera(mut timer: 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 [CXPSpeed::CXP1, CXPSpeed::CXP3].iter() {
|
||||
// Section 12.1.2 (CXP-001-2021)
|
||||
// set tx linerate -> send ConnectionReset -> wait 200ms -> set rx linerate -> monitor connection status with a timeout
|
||||
tx::change_linerate(*speed);
|
||||
write_bytes_no_ack(CONNECTION_RESET, &1_u32.to_be_bytes(), false)?;
|
||||
timer.delay_ms(200);
|
||||
rx::change_linerate(*speed);
|
||||
|
||||
if monitor_channel_status_timeout(timer).is_ok() {
|
||||
debug!("camera detected at linerate {:}", speed);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(Error::CameraNotDetected)
|
||||
}
|
||||
|
||||
fn check_master_channel() -> Result<(), Error> {
|
||||
if read_u32(DEVICE_CONNECTION_ID, false)? == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::UnsupportedTopology)
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_excess_channels(timer: GlobalTimer) -> Result<(), Error> {
|
||||
let current_cfg = read_u32(CONNECTION_CFG, false)?;
|
||||
let active_camera_chs = current_cfg >> 16;
|
||||
// After camera receive ConnectionReset, only the master connection should be active while
|
||||
// the extension connections shall not be active - Section 12.3.33 (CXP-001-2021)
|
||||
// In case some camera didn't follow the spec properly (e.g. Basler boA2448-250cm),
|
||||
// the grabber need to manually disable the excess channels
|
||||
if active_camera_chs > CHANNEL_LEN as u32 {
|
||||
debug!(
|
||||
"only {} channel(s) is available on cxp grabber, disabling excess channels on camera",
|
||||
CHANNEL_LEN
|
||||
);
|
||||
// disable excess channels and preserve the discovery linerate
|
||||
write_u32(CONNECTION_CFG, current_cfg & 0xFFFF | (CHANNEL_LEN as u32) << 16, false)?;
|
||||
|
||||
// check if the master channel is down after the cfg change
|
||||
monitor_channel_status_timeout(timer)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_host_connection_id() -> Result<(), Error> {
|
||||
debug!("set host connection id to = {:#X}", HOST_CONNECTION_ID);
|
||||
write_u32(MASTER_HOST_CONNECTION_ID, HOST_CONNECTION_ID, false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn negotiate_cxp_version() -> Result<bool, Error> {
|
||||
let rev = read_u32(REVISION, false)?;
|
||||
|
||||
let mut major_rev: u32 = rev >> 16;
|
||||
let mut minor_rev: u32 = rev & 0xFF;
|
||||
debug!("camera's CoaXPress 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(VERSION_SUPPORTED, false)?;
|
||||
|
||||
// grabber support CXP 2.1, 2.0 and 1.1 only
|
||||
if ((reg >> 3) & 1) == 1 {
|
||||
major_rev = 2;
|
||||
minor_rev = 1;
|
||||
} else if ((reg >> 2) & 1) == 1 {
|
||||
major_rev = 2;
|
||||
minor_rev = 0;
|
||||
} else if ((reg >> 1) & 1) == 1 {
|
||||
major_rev = 1;
|
||||
minor_rev = 1;
|
||||
} else {
|
||||
return Err(Error::UnsupportedVersion);
|
||||
}
|
||||
|
||||
write_u32(VERSION_USED, major_rev << 16 | minor_rev, false)?;
|
||||
}
|
||||
debug!(
|
||||
"both camera and cxp grabber support CoaXPress {}.{}, switch to CoaXPress {}.{} protocol now",
|
||||
major_rev, minor_rev, major_rev, minor_rev
|
||||
);
|
||||
|
||||
Ok(major_rev >= 2)
|
||||
}
|
||||
|
||||
fn negotiate_pak_max_size(with_tag: bool) -> Result<(), Error> {
|
||||
write_u32(STREAM_PACKET_SIZE_MAX, MAX_STREAM_PAK_SIZE, with_tag)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_cxp_speed(linerate_code: u32) -> Option<CXPSpeed> {
|
||||
match linerate_code {
|
||||
0x28 => Some(CXPSpeed::CXP1),
|
||||
0x30 => Some(CXPSpeed::CXP2),
|
||||
0x38 => Some(CXPSpeed::CXP3),
|
||||
0x40 => Some(CXPSpeed::CXP5),
|
||||
0x48 => Some(CXPSpeed::CXP6),
|
||||
0x50 => Some(CXPSpeed::CXP10),
|
||||
0x58 => Some(CXPSpeed::CXP12),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_operation_linerate(with_tag: bool, timer: GlobalTimer) -> Result<(), Error> {
|
||||
let recommended_linerate_code = read_u32(CONNECTION_CFG_DEFAULT, with_tag)? & 0xFFFF;
|
||||
|
||||
if let Some(speed) = decode_cxp_speed(recommended_linerate_code) {
|
||||
debug!("changing linerate to {}", speed);
|
||||
|
||||
// preserve the number of active channels
|
||||
let current_cfg = read_u32(CONNECTION_CFG, with_tag)?;
|
||||
write_u32(
|
||||
CONNECTION_CFG,
|
||||
current_cfg & 0xFFFF0000 | recommended_linerate_code,
|
||||
with_tag,
|
||||
)?;
|
||||
|
||||
tx::change_linerate(speed);
|
||||
rx::change_linerate(speed);
|
||||
monitor_channel_status_timeout(timer)
|
||||
} else {
|
||||
Err(Error::UnsupportedSpeed(recommended_linerate_code))
|
||||
}
|
||||
}
|
||||
|
||||
fn test_counter_reset(with_tag: bool) -> Result<(), Error> {
|
||||
unsafe { csr::cxp_grabber::core_rx_test_counts_reset_write(1) };
|
||||
write_u32(TEST_ERROR_COUNT_SELECTOR, 0, with_tag)?;
|
||||
write_u32(TEST_ERROR_COUNT, 0, with_tag)?;
|
||||
write_u64(TEST_PACKET_COUNT_TX, 0, with_tag)?;
|
||||
write_u64(TEST_PACKET_COUNT_RX, 0, with_tag)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_test_result(with_tag: bool) -> Result<(), Error> {
|
||||
write_u32(TEST_ERROR_COUNT_SELECTOR, 0, with_tag)?;
|
||||
|
||||
// Section 9.9.3 (CXP-001-2021)
|
||||
// verify grabber -> camera connection test result
|
||||
if read_u64(TEST_PACKET_COUNT_RX, with_tag)? != TX_TEST_CNT as u64 {
|
||||
return Err(Error::UnstableTX);
|
||||
};
|
||||
if read_u32(TEST_ERROR_COUNT, with_tag)? > 0 {
|
||||
return Err(Error::UnstableTX);
|
||||
};
|
||||
|
||||
// Section 9.9.4 (CXP-001-2021)
|
||||
// verify camera -> grabber connection test result
|
||||
let camera_test_pak_cnt = read_u64(TEST_PACKET_COUNT_TX, true)?;
|
||||
unsafe {
|
||||
if csr::cxp_grabber::core_rx_test_packet_counter_read() != camera_test_pak_cnt as u16 {
|
||||
return Err(Error::UnstableRX);
|
||||
};
|
||||
if csr::cxp_grabber::core_rx_test_error_counter_read() > 0 {
|
||||
return Err(Error::UnstableRX);
|
||||
};
|
||||
};
|
||||
debug!("channel #0 passed connection test");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_channel_stability(with_tag: bool, mut timer: GlobalTimer) -> Result<(), Error> {
|
||||
test_counter_reset(with_tag)?;
|
||||
|
||||
// cxp grabber -> camera connection test
|
||||
for _ in 0..TX_TEST_CNT {
|
||||
send_test_packet()?;
|
||||
// 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(TESTMODE, 1, with_tag)?;
|
||||
write_u32(TESTMODE, 0, with_tag)?;
|
||||
|
||||
verify_test_result(with_tag)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn camera_setup(timer: GlobalTimer) -> Result<bool, Error> {
|
||||
reset_tag();
|
||||
check_master_channel()?;
|
||||
|
||||
disable_excess_channels(timer)?;
|
||||
set_host_connection_id()?;
|
||||
let with_tag = negotiate_cxp_version()?;
|
||||
|
||||
negotiate_pak_max_size(with_tag)?;
|
||||
set_operation_linerate(with_tag, timer)?;
|
||||
|
||||
test_channel_stability(with_tag, timer)?;
|
||||
|
||||
Ok(with_tag)
|
||||
}
|
338
src/libboard_artiq/src/cxp_ctrl.rs
Normal file
338
src/libboard_artiq/src/cxp_ctrl.rs
Normal file
@ -0,0 +1,338 @@
|
||||
use core::fmt;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core_io::{Error as IoError, Read, Write};
|
||||
use crc::crc32::checksum_ieee;
|
||||
use io::Cursor;
|
||||
|
||||
pub const CTRL_PACKET_MAXSIZE: usize = 128; // for compatibility with version1.x compliant Devices - Section 12.1.6 (CXP-001-2021)
|
||||
pub const DATA_MAXSIZE: usize =
|
||||
CTRL_PACKET_MAXSIZE - /*packet start KCodes, data packet types, CMD, Tag, Addr, CRC, packet end KCode*/4*7;
|
||||
|
||||
pub enum Error {
|
||||
CorruptedPacket,
|
||||
CtrlAckError(u8),
|
||||
Io(IoError),
|
||||
LengthOutOfRange(u32),
|
||||
TagMismatch,
|
||||
TimedOut,
|
||||
UnexpectedReply,
|
||||
UnknownPacket(u8),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::CorruptedPacket => write!(f, "CorruptedPacket - Received packet fail CRC test"),
|
||||
&Error::CtrlAckError(ref ack_code) => match ack_code {
|
||||
0x40 => write!(f, "CtrlAckError - Invalid Address"),
|
||||
0x41 => write!(f, "CtrlAckError - Invalid data for the address"),
|
||||
0x42 => write!(f, "CtrlAckError - Invalid operation code"),
|
||||
0x43 => write!(f, "CtrlAckError - Write attempted to a read-only address"),
|
||||
0x44 => write!(f, "CtrlAckError - Read attempted from a write-only address"),
|
||||
0x45 => write!(f, "CtrlAckError - Size field too large, exceed packet size limit"),
|
||||
0x46 => write!(f, "CtrlAckError - Message size is inconsistent with size field"),
|
||||
0x47 => write!(f, "CtrlAckError - Malformed packet"),
|
||||
0x80 => write!(f, "CtrlAckError - Failed CRC test in last received command"),
|
||||
_ => write!(f, "CtrlAckError - Unknown ack code {:#X}", ack_code),
|
||||
},
|
||||
&Error::Io(ref err) => write!(f, "IoError - {:?}", err),
|
||||
&Error::LengthOutOfRange(length) => write!(
|
||||
f,
|
||||
"LengthOutOfRange - Message length {} is not between 1 and {}",
|
||||
length, DATA_MAXSIZE
|
||||
),
|
||||
&Error::TagMismatch => write!(f, "TagMismatch - Received tag is different from the transmitted tag"),
|
||||
&Error::TimedOut => write!(f, "MessageTimedOut"),
|
||||
&Error::UnexpectedReply => write!(f, "UnexpectedReply"),
|
||||
&Error::UnknownPacket(packet_type) => {
|
||||
write!(f, "UnknownPacket - Unknown packet type id {:#X} ", packet_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(value: IoError) -> Error {
|
||||
Error::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cxp_crc(bytes: &[u8]) -> u32 {
|
||||
// Section 9.2.2.2 (CXP-001-2021)
|
||||
// Only Control packet need CRC32 appended in the end of the packet
|
||||
// CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different
|
||||
(!checksum_ieee(bytes)).swap_bytes()
|
||||
}
|
||||
|
||||
trait CxpRead {
|
||||
fn read_u8(&mut self) -> Result<u8, Error>;
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, Error>;
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32, Error>;
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64, Error>;
|
||||
|
||||
fn read_exact_4x(&mut self, buf: &mut [u8]) -> Result<(), Error>;
|
||||
|
||||
fn read_4x_u8(&mut self) -> Result<u8, Error>;
|
||||
|
||||
fn read_4x_u16(&mut self) -> Result<u16, Error>;
|
||||
|
||||
fn read_4x_u32(&mut self) -> Result<u32, Error>;
|
||||
}
|
||||
impl<Cursor: Read> CxpRead for Cursor {
|
||||
fn read_u8(&mut self) -> Result<u8, Error> {
|
||||
let mut bytes = [0; 1];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, Error> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32, Error> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64, Error> {
|
||||
let mut bytes = [0; 8];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
fn read_exact_4x(&mut self, buf: &mut [u8]) -> Result<(), Error> {
|
||||
for byte in buf {
|
||||
// Section 9.2.2.1 (CXP-001-2021)
|
||||
// decoder should immune to single bit errors when handling 4x duplicated characters
|
||||
let a = self.read_u8()?;
|
||||
let b = self.read_u8()?;
|
||||
let c = self.read_u8()?;
|
||||
let d = self.read_u8()?;
|
||||
// vote and return majority
|
||||
*byte = a & b & c | a & b & d | a & c & d | b & c & d;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_4x_u8(&mut self) -> Result<u8, Error> {
|
||||
let mut bytes = [0; 1];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
fn read_4x_u16(&mut self) -> Result<u16, Error> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
fn read_4x_u32(&mut self) -> Result<u32, Error> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact_4x(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RXCTRLPacket {
|
||||
CtrlReply {
|
||||
tag: Option<u8>,
|
||||
length: u32,
|
||||
data: [u8; DATA_MAXSIZE],
|
||||
},
|
||||
CtrlDelay {
|
||||
tag: Option<u8>,
|
||||
time: u32,
|
||||
},
|
||||
CtrlAck {
|
||||
tag: Option<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
impl RXCTRLPacket {
|
||||
pub fn read_from(reader: &mut Cursor<&mut [u8]>) -> Result<Self, Error> {
|
||||
match reader.read_4x_u8()? {
|
||||
0x03 => RXCTRLPacket::get_ctrl_packet(reader, false),
|
||||
0x06 => RXCTRLPacket::get_ctrl_packet(reader, true),
|
||||
ty => Err(Error::UnknownPacket(ty)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ctrl_packet(reader: &mut Cursor<&mut [u8]>, with_tag: bool) -> Result<Self, Error> {
|
||||
let mut tag: Option<u8> = None;
|
||||
if with_tag {
|
||||
tag = Some(reader.read_4x_u8()?);
|
||||
}
|
||||
|
||||
let ackcode = reader.read_4x_u8()?;
|
||||
|
||||
match ackcode {
|
||||
0x00 | 0x04 => {
|
||||
let length = reader.read_u32()?;
|
||||
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
reader.read(&mut data[0..length as usize])?;
|
||||
|
||||
// Section 9.6.3 (CXP-001-2021)
|
||||
// when length is not multiple of 4, dummy bits are padded to align to the word boundary
|
||||
// set position to next word boundary for CRC calculation
|
||||
let padding = (4 - (reader.position() % 4)) % 4;
|
||||
reader.set_position(reader.position() + padding);
|
||||
|
||||
// Section 9.6.3 (CXP-001-2021)
|
||||
// only bytes after the first 4 are used in calculating the checksum
|
||||
let checksum = get_cxp_crc(&reader.get_ref()[4..reader.position()]);
|
||||
if reader.read_u32()? != checksum {
|
||||
return Err(Error::CorruptedPacket);
|
||||
}
|
||||
|
||||
if ackcode == 0x00 {
|
||||
return Ok(RXCTRLPacket::CtrlReply { tag, length, data });
|
||||
} else {
|
||||
return Ok(RXCTRLPacket::CtrlDelay {
|
||||
tag,
|
||||
time: NetworkEndian::read_u32(&data[..4]),
|
||||
});
|
||||
}
|
||||
}
|
||||
0x01 => return Ok(RXCTRLPacket::CtrlAck { tag }),
|
||||
_ => return Err(Error::CtrlAckError(ackcode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait CxpWrite {
|
||||
fn write_u8(&mut self, value: u8) -> Result<(), Error>;
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Error>;
|
||||
|
||||
fn write_all_4x(&mut self, buf: &[u8]) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u8(&mut self, value: u8) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u16(&mut self, value: u16) -> Result<(), Error>;
|
||||
|
||||
fn write_4x_u32(&mut self, value: u32) -> Result<(), Error>;
|
||||
}
|
||||
impl<Cursor: Write> CxpWrite for Cursor {
|
||||
fn write_u8(&mut self, value: u8) -> Result<(), Error> {
|
||||
self.write_all(&[value])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Error> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
self.write_all(&bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all_4x(&mut self, buf: &[u8]) -> Result<(), Error> {
|
||||
for byte in buf {
|
||||
self.write_all(&[*byte; 4])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_4x_u8(&mut self, value: u8) -> Result<(), Error> {
|
||||
self.write_all_4x(&[value])
|
||||
}
|
||||
|
||||
fn write_4x_u16(&mut self, value: u16) -> Result<(), Error> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
self.write_all_4x(&bytes)
|
||||
}
|
||||
|
||||
fn write_4x_u32(&mut self, value: u32) -> Result<(), Error> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
self.write_all_4x(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TXCTRLPacket {
|
||||
CtrlRead {
|
||||
tag: Option<u8>,
|
||||
addr: u32,
|
||||
length: u32,
|
||||
},
|
||||
CtrlWrite {
|
||||
tag: Option<u8>,
|
||||
addr: u32,
|
||||
length: u32,
|
||||
data: [u8; DATA_MAXSIZE],
|
||||
},
|
||||
}
|
||||
|
||||
impl TXCTRLPacket {
|
||||
pub fn write_to(&self, writer: &mut Cursor<&mut [u8]>) -> Result<(), Error> {
|
||||
match *self {
|
||||
TXCTRLPacket::CtrlRead { tag, addr, length } => {
|
||||
match tag {
|
||||
Some(t) => {
|
||||
writer.write_4x_u8(0x05)?;
|
||||
writer.write_4x_u8(t)?;
|
||||
}
|
||||
None => {
|
||||
writer.write_4x_u8(0x02)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut bytes = [0; 3];
|
||||
NetworkEndian::write_u24(&mut bytes, length);
|
||||
writer.write_all(&[0x00, bytes[0], bytes[1], bytes[2]])?;
|
||||
|
||||
writer.write_u32(addr)?;
|
||||
|
||||
// Section 9.6.2 (CXP-001-2021)
|
||||
// only bytes after the first 4 are used in calculating the checksum
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
TXCTRLPacket::CtrlWrite {
|
||||
tag,
|
||||
addr,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
match tag {
|
||||
Some(t) => {
|
||||
writer.write_4x_u8(0x05)?;
|
||||
writer.write_4x_u8(t)?;
|
||||
}
|
||||
None => {
|
||||
writer.write_4x_u8(0x02)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut bytes = [0; 3];
|
||||
NetworkEndian::write_u24(&mut bytes, length);
|
||||
writer.write_all(&[0x01, bytes[0], bytes[1], bytes[2]])?;
|
||||
|
||||
writer.write_u32(addr)?;
|
||||
writer.write_all(&data[0..length as usize])?;
|
||||
|
||||
// Section 9.6.2 (CXP-001-2021)
|
||||
// when length is not multiple of 4, dummy bites are padded to align to the word boundary
|
||||
let padding = (4 - (writer.position() % 4)) % 4;
|
||||
for _ in 0..padding {
|
||||
writer.write_u8(0)?;
|
||||
}
|
||||
|
||||
// Section 9.6.2 (CXP-001-2021)
|
||||
// only bytes after the first 4 are used in calculating the checksum
|
||||
let checksum = get_cxp_crc(&writer.get_ref()[4..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
90
src/libboard_artiq/src/cxp_grabber.rs
Normal file
90
src/libboard_artiq/src/cxp_grabber.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use libboard_zynq::timer::GlobalTimer;
|
||||
use libcortex_a9::mutex::Mutex;
|
||||
use log::{error, info};
|
||||
|
||||
use crate::{cxp_camera_setup::{camera_setup, discover_camera, master_channel_ready},
|
||||
pl::csr};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum State {
|
||||
Connected,
|
||||
Detected,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
// Mutex as they are needed by core1 cxp api calls
|
||||
static mut STATE: Mutex<State> = Mutex::new(State::Disconnected);
|
||||
static mut WITH_TAG: Mutex<bool> = Mutex::new(false);
|
||||
|
||||
pub fn camera_connected() -> bool {
|
||||
unsafe { *STATE.lock() == State::Connected }
|
||||
}
|
||||
|
||||
pub fn with_tag() -> bool {
|
||||
unsafe { *WITH_TAG.lock() }
|
||||
}
|
||||
|
||||
pub fn tick(timer: GlobalTimer) {
|
||||
let mut state_guard = unsafe { STATE.lock() };
|
||||
let mut with_tag_guard = unsafe { WITH_TAG.lock() };
|
||||
*state_guard = match *state_guard {
|
||||
State::Disconnected => match discover_camera(timer) {
|
||||
Ok(_) => {
|
||||
info!("camera detected, setting up camera...");
|
||||
State::Detected
|
||||
}
|
||||
Err(_) => State::Disconnected,
|
||||
},
|
||||
State::Detected => match camera_setup(timer) {
|
||||
Ok(with_tag) => {
|
||||
info!("camera setup complete");
|
||||
*with_tag_guard = with_tag;
|
||||
State::Connected
|
||||
}
|
||||
Err(e) => {
|
||||
error!("camera setup failure: {}", e);
|
||||
*with_tag_guard = false;
|
||||
State::Disconnected
|
||||
}
|
||||
},
|
||||
State::Connected => {
|
||||
if master_channel_ready() {
|
||||
unsafe {
|
||||
if csr::cxp_grabber::stream_decoder_crc_error_read() == 1 {
|
||||
error!("frame packet has CRC error");
|
||||
csr::cxp_grabber::stream_decoder_crc_error_write(1);
|
||||
};
|
||||
|
||||
if csr::cxp_grabber::stream_decoder_stream_type_error_read() == 1 {
|
||||
error!("Non CoaXPress stream type detected, the CXP grabber doesn't support GenDC stream type");
|
||||
csr::cxp_grabber::stream_decoder_stream_type_error_write(1);
|
||||
};
|
||||
|
||||
if csr::cxp_grabber::core_rx_trigger_ack_read() == 1 {
|
||||
info!("received CXP linktrigger ack");
|
||||
csr::cxp_grabber::core_rx_trigger_ack_write(1);
|
||||
};
|
||||
|
||||
if csr::cxp_grabber::stream_decoder_new_frame_read() == 1 {
|
||||
let width = csr::cxp_grabber::stream_decoder_x_size_read();
|
||||
let height = csr::cxp_grabber::stream_decoder_y_size_read();
|
||||
match csr::cxp_grabber::stream_decoder_pixel_format_code_read() {
|
||||
0x0101 => info!("received frame: {}x{} with MONO8 format", width, height),
|
||||
0x0102 => info!("received frame: {}x{} with MONO10 format", width, height),
|
||||
0x0103 => info!("received frame: {}x{} with MONO12 format", width, height),
|
||||
0x0104 => info!("received frame: {}x{} with MONO14 format", width, height),
|
||||
0x0105 => info!("received frame: {}x{} with MONO16 format", width, height),
|
||||
_ => info!("received frame: {}x{} with Unsupported pixel format", width, height),
|
||||
};
|
||||
csr::cxp_grabber::stream_decoder_new_frame_write(1);
|
||||
};
|
||||
}
|
||||
State::Connected
|
||||
} else {
|
||||
*with_tag_guard = false;
|
||||
info!("camera disconnected");
|
||||
State::Disconnected
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
192
src/libboard_artiq/src/cxp_packet.rs
Normal file
192
src/libboard_artiq/src/cxp_packet.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use core::slice;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use io::Cursor;
|
||||
use libboard_zynq::{time::Milliseconds, timer::GlobalTimer};
|
||||
|
||||
use crate::{cxp_ctrl::{Error, RXCTRLPacket, TXCTRLPacket, CTRL_PACKET_MAXSIZE, DATA_MAXSIZE},
|
||||
mem::mem,
|
||||
pl::csr};
|
||||
|
||||
const TRANSMISSION_TIMEOUT: u64 = 200;
|
||||
|
||||
// Section 9.6.1.2 (CXP-001-2021)
|
||||
// CTRL packet need to be tagged for CXP 2.0 or greater
|
||||
static mut TAG: u8 = 0;
|
||||
|
||||
pub fn reset_tag() {
|
||||
unsafe { TAG = 0 };
|
||||
}
|
||||
|
||||
fn increment_tag() {
|
||||
unsafe { TAG = TAG.wrapping_add(1) };
|
||||
}
|
||||
|
||||
fn check_tag(tag: Option<u8>) -> Result<(), Error> {
|
||||
unsafe {
|
||||
if tag.is_some() && tag != Some(TAG) {
|
||||
Err(Error::TagMismatch)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_ctrl_packet() -> Result<Option<RXCTRLPacket>, Error> {
|
||||
if unsafe { csr::cxp_grabber::core_rx_pending_packet_read() == 1 } {
|
||||
unsafe {
|
||||
let read_buffer_ptr = csr::cxp_grabber::core_rx_read_ptr_read() as usize;
|
||||
let ptr = (mem::CXP_MEM_BASE + mem::CXP_MEM_SIZE / 2 + read_buffer_ptr * CTRL_PACKET_MAXSIZE) as *mut u32;
|
||||
|
||||
let mut reader = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, CTRL_PACKET_MAXSIZE));
|
||||
let packet = RXCTRLPacket::read_from(&mut reader);
|
||||
|
||||
csr::cxp_grabber::core_rx_pending_packet_write(1);
|
||||
Ok(Some(packet?))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_ctrl_packet_timeout(timeout_ms: u64) -> Result<RXCTRLPacket, Error> {
|
||||
// assume timer was initialized successfully
|
||||
let timer = unsafe { GlobalTimer::get() };
|
||||
let limit = timer.get_time() + Milliseconds(timeout_ms);
|
||||
while timer.get_time() < limit {
|
||||
match receive_ctrl_packet()? {
|
||||
None => (),
|
||||
Some(packet) => return Ok(packet),
|
||||
}
|
||||
}
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
|
||||
fn send_ctrl_packet(packet: &TXCTRLPacket) -> Result<(), Error> {
|
||||
unsafe {
|
||||
while csr::cxp_grabber::core_tx_writer_busy_read() == 1 {}
|
||||
let ptr = mem::CXP_MEM_BASE as *mut u32;
|
||||
let mut writer = Cursor::new(slice::from_raw_parts_mut(ptr as *mut u8, CTRL_PACKET_MAXSIZE));
|
||||
|
||||
packet.write_to(&mut writer)?;
|
||||
|
||||
csr::cxp_grabber::core_tx_writer_word_len_write((writer.position() / 4) as u8);
|
||||
csr::cxp_grabber::core_tx_writer_stb_write(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_test_packet() -> Result<(), Error> {
|
||||
unsafe {
|
||||
while csr::cxp_grabber::core_tx_writer_busy_read() == 1 {}
|
||||
csr::cxp_grabber::core_tx_writer_stb_testseq_write(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ctrl_ack(timeout: u64) -> Result<(), Error> {
|
||||
match receive_ctrl_packet_timeout(timeout) {
|
||||
Ok(RXCTRLPacket::CtrlAck { tag }) => {
|
||||
check_tag(tag)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(RXCTRLPacket::CtrlDelay { tag, time }) => {
|
||||
check_tag(tag)?;
|
||||
get_ctrl_ack(time as u64)
|
||||
}
|
||||
Ok(_) => Err(Error::UnexpectedReply),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ctrl_reply(timeout: u64, expected_length: u32) -> Result<[u8; DATA_MAXSIZE], Error> {
|
||||
match receive_ctrl_packet_timeout(timeout) {
|
||||
Ok(RXCTRLPacket::CtrlReply { tag, length, data }) => {
|
||||
check_tag(tag)?;
|
||||
if length != expected_length {
|
||||
return Err(Error::UnexpectedReply);
|
||||
};
|
||||
Ok(data)
|
||||
}
|
||||
Ok(RXCTRLPacket::CtrlDelay { tag, time }) => {
|
||||
check_tag(tag)?;
|
||||
get_ctrl_reply(time as u64, expected_length)
|
||||
}
|
||||
Ok(_) => Err(Error::UnexpectedReply),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_length(length: u32) -> Result<(), Error> {
|
||||
if length > DATA_MAXSIZE as u32 || length == 0 {
|
||||
Err(Error::LengthOutOfRange(length))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_bytes_no_ack(addr: u32, val: &[u8], with_tag: bool) -> Result<(), Error> {
|
||||
let length = val.len() as u32;
|
||||
check_length(length)?;
|
||||
|
||||
let mut data: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
data[..length as usize].clone_from_slice(val);
|
||||
|
||||
let tag: Option<u8> = if with_tag { Some(unsafe { TAG }) } else { None };
|
||||
send_ctrl_packet(&TXCTRLPacket::CtrlWrite {
|
||||
tag,
|
||||
addr,
|
||||
length,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_bytes(addr: u32, val: &[u8], with_tag: bool) -> Result<(), Error> {
|
||||
write_bytes_no_ack(addr, val, with_tag)?;
|
||||
get_ctrl_ack(TRANSMISSION_TIMEOUT)?;
|
||||
|
||||
if with_tag {
|
||||
increment_tag();
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_u32(addr: u32, val: u32, with_tag: bool) -> Result<(), Error> {
|
||||
write_bytes(addr, &val.to_be_bytes(), with_tag)
|
||||
}
|
||||
|
||||
pub fn write_u64(addr: u32, val: u64, with_tag: bool) -> Result<(), Error> {
|
||||
write_bytes(addr, &val.to_be_bytes(), with_tag)
|
||||
}
|
||||
|
||||
pub fn read_bytes(addr: u32, bytes: &mut [u8], with_tag: bool) -> Result<(), Error> {
|
||||
let length = bytes.len() as u32;
|
||||
check_length(length)?;
|
||||
let tag: Option<u8> = if with_tag { Some(unsafe { TAG }) } else { None };
|
||||
send_ctrl_packet(&TXCTRLPacket::CtrlRead { tag, addr, length })?;
|
||||
|
||||
let data = get_ctrl_reply(TRANSMISSION_TIMEOUT, length)?;
|
||||
bytes.clone_from_slice(&data[..length as usize]);
|
||||
|
||||
if with_tag {
|
||||
increment_tag();
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_u32(addr: u32, with_tag: bool) -> Result<u32, Error> {
|
||||
let mut bytes: [u8; 4] = [0; 4];
|
||||
read_bytes(addr, &mut bytes, with_tag)?;
|
||||
let val = NetworkEndian::read_u32(&bytes);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn read_u64(addr: u32, with_tag: bool) -> Result<u64, Error> {
|
||||
let mut bytes: [u8; 8] = [0; 8];
|
||||
read_bytes(addr, &mut bytes, with_tag)?;
|
||||
let val = NetworkEndian::read_u64(&bytes);
|
||||
|
||||
Ok(val)
|
||||
}
|
193
src/libboard_artiq/src/cxp_phys.rs
Normal file
193
src/libboard_artiq/src/cxp_phys.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::pl::csr;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CXPSpeed {
|
||||
CXP1,
|
||||
CXP2,
|
||||
CXP3,
|
||||
CXP5,
|
||||
CXP6,
|
||||
CXP10,
|
||||
CXP12,
|
||||
}
|
||||
|
||||
impl fmt::Display for CXPSpeed {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&CXPSpeed::CXP1 => write!(f, "1.25 Gbps"),
|
||||
&CXPSpeed::CXP2 => write!(f, "2.5 Gbps"),
|
||||
&CXPSpeed::CXP3 => write!(f, "3.125 Gbps"),
|
||||
&CXPSpeed::CXP5 => write!(f, "5 Gbps"),
|
||||
&CXPSpeed::CXP6 => write!(f, "6.25 Gbps"),
|
||||
&CXPSpeed::CXP10 => write!(f, "10 Gbps"),
|
||||
&CXPSpeed::CXP12 => write!(f, "12.5 Gbps"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup() {
|
||||
let init_speed = CXPSpeed::CXP1;
|
||||
tx::setup();
|
||||
tx::change_linerate(init_speed);
|
||||
rx::setup();
|
||||
rx::change_linerate(init_speed);
|
||||
}
|
||||
|
||||
pub mod tx {
|
||||
use super::*;
|
||||
|
||||
pub fn setup() {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_tx_enable_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_linerate(speed: CXPSpeed) {
|
||||
unsafe {
|
||||
match speed {
|
||||
CXPSpeed::CXP1 | CXPSpeed::CXP2 | CXPSpeed::CXP3 | CXPSpeed::CXP5 | CXPSpeed::CXP6 => {
|
||||
csr::cxp_grabber::phy_tx_bitrate2x_enable_write(0);
|
||||
}
|
||||
CXPSpeed::CXP10 | CXPSpeed::CXP12 => {
|
||||
csr::cxp_grabber::phy_tx_bitrate2x_enable_write(1);
|
||||
}
|
||||
};
|
||||
csr::cxp_grabber::phy_tx_clk_reset_write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod rx {
|
||||
use super::*;
|
||||
|
||||
pub fn setup() {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_gtx_refclk_stable_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_linerate(speed: CXPSpeed) {
|
||||
change_qpll_fb_divider(speed);
|
||||
change_gtx_divider(speed);
|
||||
change_cdr_cfg(speed);
|
||||
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_qpll_reset_write(1);
|
||||
while csr::cxp_grabber::phy_rx_qpll_locked_read() != 1 {}
|
||||
// Changing RXOUT_DIV via DRP requires a manual reset
|
||||
// https://adaptivesupport.amd.com/s/question/0D52E00006hplwnSAA/re-gtx-line-rate-change
|
||||
csr::cxp_grabber::phy_rx_gtx_restart_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn change_qpll_fb_divider(speed: CXPSpeed) {
|
||||
let qpll_div_reg = match speed {
|
||||
CXPSpeed::CXP1 | CXPSpeed::CXP2 | CXPSpeed::CXP5 | CXPSpeed::CXP10 => 0x0120, // FB_Divider = 80, QPLL VCO @ 10GHz
|
||||
CXPSpeed::CXP3 | CXPSpeed::CXP6 | CXPSpeed::CXP12 => 0x0170, // FB_Divider = 100, QPLL VCO @ 12.5GHz
|
||||
};
|
||||
|
||||
qpll_write(0x36, qpll_div_reg);
|
||||
}
|
||||
|
||||
fn change_gtx_divider(speed: CXPSpeed) {
|
||||
let div_reg = match speed {
|
||||
CXPSpeed::CXP1 => 0x33, // RXOUT_DIV = 8
|
||||
CXPSpeed::CXP2 | CXPSpeed::CXP3 => 0x22, // RXOUT_DIV = 4
|
||||
CXPSpeed::CXP5 | CXPSpeed::CXP6 => 0x11, // RXOUT_DIV = 2
|
||||
CXPSpeed::CXP10 | CXPSpeed::CXP12 => 0x00, // RXOUT_DIV = 1
|
||||
};
|
||||
|
||||
gtx_write(0x88, div_reg);
|
||||
}
|
||||
|
||||
fn change_cdr_cfg(speed: CXPSpeed) {
|
||||
struct CdrConfig {
|
||||
pub cfg_reg0: u16, // addr = 0xA8
|
||||
pub cfg_reg1: u16, // addr = 0xA9
|
||||
pub cfg_reg2: u16, // addr = 0xAA
|
||||
pub cfg_reg3: u16, // addr = 0xAB
|
||||
pub cfg_reg4: u16, // addr = 0xAC
|
||||
}
|
||||
|
||||
let cdr_cfg = match speed {
|
||||
// when RXOUT_DIV = 8
|
||||
CXPSpeed::CXP1 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1008,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV = 4
|
||||
CXPSpeed::CXP2 | CXPSpeed::CXP5 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1010,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV= 2
|
||||
CXPSpeed::CXP3 | CXPSpeed::CXP6 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1020,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x0003,
|
||||
},
|
||||
// when RXOUT_DIV= 1
|
||||
CXPSpeed::CXP10 | CXPSpeed::CXP12 => CdrConfig {
|
||||
cfg_reg0: 0x0020,
|
||||
cfg_reg1: 0x1040,
|
||||
cfg_reg2: 0x23FF,
|
||||
cfg_reg3: 0x0000,
|
||||
cfg_reg4: 0x000B,
|
||||
},
|
||||
};
|
||||
|
||||
gtx_write(0x0A8, cdr_cfg.cfg_reg0);
|
||||
gtx_write(0x0A9, cdr_cfg.cfg_reg1);
|
||||
gtx_write(0x0AA, cdr_cfg.cfg_reg2);
|
||||
gtx_write(0x0AB, cdr_cfg.cfg_reg3);
|
||||
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn gtx_read(address: u16) -> u16 {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_gtx_daddr_write(address);
|
||||
csr::cxp_grabber::phy_rx_gtx_dread_write(1);
|
||||
while csr::cxp_grabber::phy_rx_gtx_dready_read() != 1 {}
|
||||
csr::cxp_grabber::phy_rx_gtx_dout_read()
|
||||
}
|
||||
}
|
||||
|
||||
fn gtx_write(address: u16, value: u16) {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_gtx_daddr_write(address);
|
||||
csr::cxp_grabber::phy_rx_gtx_din_write(value);
|
||||
csr::cxp_grabber::phy_rx_gtx_din_stb_write(1);
|
||||
while csr::cxp_grabber::phy_rx_gtx_dready_read() != 1 {}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn qpll_read(address: u8) -> u16 {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_qpll_daddr_write(address);
|
||||
csr::cxp_grabber::phy_rx_qpll_dread_write(1);
|
||||
while csr::cxp_grabber::phy_rx_qpll_dready_read() != 1 {}
|
||||
csr::cxp_grabber::phy_rx_qpll_dout_read()
|
||||
}
|
||||
}
|
||||
|
||||
fn qpll_write(address: u8, value: u16) {
|
||||
unsafe {
|
||||
csr::cxp_grabber::phy_rx_qpll_daddr_write(address);
|
||||
csr::cxp_grabber::phy_rx_qpll_din_write(value);
|
||||
csr::cxp_grabber::phy_rx_qpll_din_stb_write(1);
|
||||
while csr::cxp_grabber::phy_rx_qpll_dready_read() != 1 {}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ pub mod fiq;
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
pub mod io_expander;
|
||||
pub mod logger;
|
||||
#[cfg(has_drtio)]
|
||||
#[cfg(any(has_drtio, has_cxp_grabber))]
|
||||
#[rustfmt::skip]
|
||||
#[path = "../../../build/mem.rs"]
|
||||
pub mod mem;
|
||||
@ -41,6 +41,17 @@ pub mod si5324;
|
||||
pub mod si549;
|
||||
use core::{cmp, str};
|
||||
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp_camera_setup;
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp_ctrl;
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp_grabber;
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp_packet;
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp_phys;
|
||||
|
||||
pub fn identifier_read(buf: &mut [u8]) -> &str {
|
||||
unsafe {
|
||||
pl::csr::identifier::address_write(0);
|
||||
|
151
src/libksupport/src/cxp.rs
Normal file
151
src/libksupport/src/cxp.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use alloc::{string::{String, ToString},
|
||||
vec::Vec};
|
||||
use core::fmt;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use cslice::CMutSlice;
|
||||
use libboard_artiq::{cxp_ctrl::{Error as CtrlErr, DATA_MAXSIZE},
|
||||
cxp_grabber::{camera_connected, with_tag},
|
||||
cxp_packet::{read_bytes, read_u32, write_u32}};
|
||||
use log::info;
|
||||
|
||||
use crate::artiq_raise;
|
||||
|
||||
enum Error {
|
||||
BufferSizeTooSmall(usize, usize),
|
||||
InvalidLocalUrl(String),
|
||||
CtrlPacketError(CtrlErr),
|
||||
}
|
||||
|
||||
impl From<CtrlErr> for Error {
|
||||
fn from(value: CtrlErr) -> Error {
|
||||
Error::CtrlPacketError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::BufferSizeTooSmall(required_size, buffer_size) => {
|
||||
write!(
|
||||
f,
|
||||
"BufferSizeTooSmall - The required size is {} bytes but the buffer size is {} bytes",
|
||||
required_size, buffer_size
|
||||
)
|
||||
}
|
||||
&Error::InvalidLocalUrl(ref s) => {
|
||||
write!(f, "InvalidLocalUrl - Cannot download xml file locally from {}", s)
|
||||
}
|
||||
&Error::CtrlPacketError(ref err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_xml_url(with_tag: bool) -> Result<String, Error> {
|
||||
let mut addr = read_u32(0x0018, with_tag)?;
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
// Strings stored in the bootstrap and manufacturer-specific registers space shall be NULL-terminated, encoded ASCII - Section 12.3.1 (CXP-001-2021)
|
||||
// String length is not known during runtime, grabber must read 4 bytes at a time until NULL-terminated
|
||||
loop {
|
||||
let mut bytes: [u8; 4] = [0; 4];
|
||||
read_bytes(addr, &mut bytes, with_tag)?;
|
||||
addr += 4;
|
||||
|
||||
for b in bytes {
|
||||
if b == 0 {
|
||||
// UTF-8 is compatible with ASCII encoding
|
||||
// use U+FFFD REPLACEMENT_CHARACTER to represent decoding error
|
||||
return Ok(String::from_utf8_lossy(&buffer).to_string());
|
||||
} else {
|
||||
buffer.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_xml_location(with_tag: bool) -> Result<(String, u32, u32), Error> {
|
||||
let url = read_xml_url(with_tag)?;
|
||||
|
||||
// url example - Section 13.2.3 (CXP-001-2021)
|
||||
// Available on camera - "Local:MyFilename.zip;B8000;33A?SchemaVersion=1.0.0"
|
||||
// => ZIP file starting at address 0xB8000 in the Device with a length of 0x33A bytes
|
||||
//
|
||||
// Available online - "Web:http://www.example.com/xml/MyFilename.xml"
|
||||
// => xml is available at http://www.example.com/xml/MyFilename.xml
|
||||
let mut splitter = url.split(|c| c == ':' || c == ';' || c == '?');
|
||||
let scheme = splitter.next().unwrap();
|
||||
if scheme.eq_ignore_ascii_case("local") {
|
||||
if let (Some(file_name), Some(addr_str), Some(size_str)) = (splitter.next(), splitter.next(), splitter.next()) {
|
||||
let addr = u32::from_str_radix(addr_str, 16).map_err(|_| Error::InvalidLocalUrl(url.to_string()))?;
|
||||
let size = u32::from_str_radix(size_str, 16).map_err(|_| Error::InvalidLocalUrl(url.to_string()))?;
|
||||
return Ok((file_name.to_string(), addr, size));
|
||||
}
|
||||
}
|
||||
Err(Error::InvalidLocalUrl(url.to_string()))
|
||||
}
|
||||
|
||||
fn read_xml_file(buffer: &mut [i32], with_tag: bool) -> Result<u32, Error> {
|
||||
let (file_name, base_addr, size) = read_xml_location(with_tag)?;
|
||||
|
||||
if buffer.len() * 4 < size as usize {
|
||||
return Err(Error::BufferSizeTooSmall(size as usize, buffer.len() * 4));
|
||||
};
|
||||
|
||||
info!("downloading xml file {} with {} bytes...", file_name, size);
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
let mut addr = base_addr;
|
||||
let mut bytesleft = size;
|
||||
let mut bytes: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
|
||||
|
||||
while bytesleft > 0 {
|
||||
let read_len = DATA_MAXSIZE.min(bytesleft as usize);
|
||||
read_bytes(addr, &mut bytes[..read_len], with_tag)?;
|
||||
v.extend(&bytes[..read_len]);
|
||||
addr += read_len as u32;
|
||||
bytesleft -= read_len as u32;
|
||||
}
|
||||
info!("download successful");
|
||||
|
||||
// pad to 32 bit boundary
|
||||
let padding = (4 - (size % 4)) % 4;
|
||||
for _ in 0..padding {
|
||||
v.push(0);
|
||||
}
|
||||
|
||||
NetworkEndian::read_i32_into(&v, &mut buffer[..((size + padding) / 4) as usize]);
|
||||
Ok((size + padding) / 4)
|
||||
}
|
||||
|
||||
pub extern "C" fn download_xml_file(buffer: &mut CMutSlice<i32>) -> i32 {
|
||||
if camera_connected() {
|
||||
match read_xml_file(buffer.as_mut_slice(), with_tag()) {
|
||||
Ok(size_read) => size_read as i32,
|
||||
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
|
||||
}
|
||||
} else {
|
||||
artiq_raise!("CXPError", "Camera is not connected");
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn read32(addr: i32) -> i32 {
|
||||
if camera_connected() {
|
||||
match read_u32(addr as u32, with_tag()) {
|
||||
Ok(result) => result as i32,
|
||||
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
|
||||
}
|
||||
} else {
|
||||
artiq_raise!("CXPError", "Camera is not connected");
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn write32(addr: i32, val: i32) {
|
||||
if camera_connected() {
|
||||
match write_u32(addr as u32, val as u32, with_tag()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => artiq_raise!("CXPError", format!("{}", e)),
|
||||
}
|
||||
} else {
|
||||
artiq_raise!("CXPError", "Camera is not connected");
|
||||
}
|
||||
}
|
@ -482,7 +482,7 @@ extern "C" fn stop_fn(
|
||||
}
|
||||
|
||||
// Must be kept in sync with preallocate_runtime_exception_names() in `artiq.compiler.embedding`
|
||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 22] = [
|
||||
static EXCEPTION_ID_LOOKUP: [(&str, u32); 23] = [
|
||||
("RTIOUnderflow", 0),
|
||||
("RTIOOverflow", 1),
|
||||
("RTIODestinationUnreachable", 2),
|
||||
@ -505,6 +505,7 @@ static EXCEPTION_ID_LOOKUP: [(&str, u32); 22] = [
|
||||
("ZeroDivisionError", 19),
|
||||
("LinAlgError", 20),
|
||||
("UnwrapNoneError", 21),
|
||||
("CXPError", 22),
|
||||
];
|
||||
|
||||
pub fn get_exception_id(name: &str) -> u32 {
|
||||
|
@ -11,6 +11,8 @@ use super::{cache,
|
||||
core1::rtio_get_destination_status,
|
||||
dma, linalg,
|
||||
rpc::{rpc_recv, rpc_send, rpc_send_async}};
|
||||
#[cfg(has_cxp_grabber)]
|
||||
use crate::cxp;
|
||||
use crate::{eh_artiq, i2c, rtio};
|
||||
|
||||
extern "C" {
|
||||
@ -126,6 +128,14 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
#[cfg(has_drtio)]
|
||||
api!(subkernel_await_message = subkernel::await_message),
|
||||
|
||||
// cxp grabber
|
||||
#[cfg(has_cxp_grabber)]
|
||||
api!(cxp_download_xml_file = cxp::download_xml_file),
|
||||
#[cfg(has_cxp_grabber)]
|
||||
api!(cxp_read32 = cxp::read32),
|
||||
#[cfg(has_cxp_grabber)]
|
||||
api!(cxp_write32 = cxp::write32),
|
||||
|
||||
// Double-precision floating-point arithmetic helper functions
|
||||
// RTABI chapter 4.1.2, Table 2
|
||||
api!(__aeabi_dadd),
|
||||
|
@ -34,6 +34,8 @@ pub mod rtio;
|
||||
#[path = "../../../build/pl.rs"]
|
||||
pub mod pl;
|
||||
|
||||
#[cfg(has_cxp_grabber)]
|
||||
pub mod cxp;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RPCException {
|
||||
|
@ -14,6 +14,8 @@ use core::cell::RefCell;
|
||||
|
||||
use ksupport;
|
||||
use libasync::task;
|
||||
#[cfg(has_cxp_grabber)]
|
||||
use libboard_artiq::cxp_phys;
|
||||
#[cfg(has_drtio_eem)]
|
||||
use libboard_artiq::drtio_eem;
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
@ -79,6 +81,23 @@ mod grabber {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_cxp_grabber)]
|
||||
mod cxp {
|
||||
use libasync::delay;
|
||||
use libboard_artiq::cxp_grabber;
|
||||
use libboard_zynq::time::Milliseconds;
|
||||
|
||||
use crate::GlobalTimer;
|
||||
|
||||
pub async fn grabber_thread(timer: GlobalTimer) {
|
||||
let mut countdown = timer.countdown();
|
||||
loop {
|
||||
cxp_grabber::tick(timer);
|
||||
delay(&mut countdown, Milliseconds(200)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
|
||||
|
||||
#[no_mangle]
|
||||
@ -151,5 +170,11 @@ pub fn main_core0() {
|
||||
|
||||
task::spawn(ksupport::report_async_rtio_errors());
|
||||
|
||||
#[cfg(has_cxp_grabber)]
|
||||
{
|
||||
cxp_phys::setup();
|
||||
task::spawn(cxp::grabber_thread(timer));
|
||||
}
|
||||
|
||||
comms::main(timer, cfg);
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ extern crate alloc;
|
||||
use analyzer::Analyzer;
|
||||
use dma::Manager as DmaManager;
|
||||
use embedded_hal::blocking::delay::DelayUs;
|
||||
#[cfg(has_drtio_eem)]
|
||||
use libboard_artiq::drtio_eem;
|
||||
#[cfg(has_grabber)]
|
||||
use libboard_artiq::grabber;
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
@ -1336,6 +1338,12 @@ fn process_aux_packet(
|
||||
unsafe {
|
||||
csr::gt_drtio::txenable_write(0);
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
unsafe {
|
||||
csr::eem_transceiver::txenable_write(0);
|
||||
}
|
||||
|
||||
core_manager.write_image();
|
||||
info!("reboot imminent");
|
||||
slcr::reboot();
|
||||
@ -1569,6 +1577,12 @@ pub extern "C" fn main_core0() -> i32 {
|
||||
unsafe {
|
||||
csr::gt_drtio::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
unsafe {
|
||||
csr::eem_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
|
||||
#[cfg(has_si549)]
|
||||
si549::helper_setup(&mut timer, &SI549_SETTINGS).expect("cannot initialize helper Si549");
|
||||
|
||||
@ -1594,6 +1608,14 @@ pub extern "C" fn main_core0() -> i32 {
|
||||
toggle_sed_spread(0);
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_eem)]
|
||||
{
|
||||
drtio_eem::init(&mut timer, &cfg);
|
||||
unsafe {
|
||||
csr::eem_transceiver::rx_ready_write(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(has_drtio_routing)]
|
||||
let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()];
|
||||
#[cfg(not(has_drtio_routing))]
|
||||
|
Loading…
x
Reference in New Issue
Block a user