Compare commits

...

10 Commits

7 changed files with 233 additions and 42 deletions

161
cxp_img_kernel.py Normal file
View File

@ -0,0 +1,161 @@
from artiq.language.core import kernel
from artiq.experiment import *
from artiq.coredevice.cxp_grabber import write_pgm, write_file
from numpy import array
# boA2448-250cm XML specific
_USER_SET_SELECTOR = 0x10000050
_REAL_ACQ_MODE = 0x10000BB4
_REAL_ACQ_START = 0x10000498
_REAL_ACQ_STOP = 0x100004A4
_REAL_ACQ_ABORT = 0x100004B0
_BSL_SENSOR_ON = 0x100004D4
_BSL_SENSOR_STAND_BY = 0x100004C8
_BSL_SENSOR_OFF = 0x100004BC
_BSL_POWER_MODE = 0x100000B4
_TRIG_MODE_INDEX = 0x10001424
_TRIG_SRC_INDEX = 0x100081AC
_TRIG_ACT_INDEX = 0x1000293C
_TRIG_SOFTWARE_INDEX = 0x10000C34
_TEST_PATTERN = 0x10003500
_PIXEL_FORMAT = 0x100078B4
class CXP_Kernel(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led0")
self.setattr_device("cxp")
self.gate_mask = 0b1111_1111
self.cnt = [0] * self.gate_mask.bit_count()
self.width, self.height = 2448, 3
self.frame = array([[0] * self.width])
@kernel
def camera_sensor_off(self):
# Abort any acq as it cannot accept sensor off control message in acq mode
self.cxp.write32(_REAL_ACQ_ABORT, 1)
self.cxp.write32(_BSL_SENSOR_OFF, 1)
@kernel
def camera_trigger_nonrealtime_setup(self, pixel_format, test_pattern):
# acq ABORT to clear any previous untrigger acq
self.cxp.write32(_REAL_ACQ_ABORT, 1)
self.cxp.write32(_PIXEL_FORMAT, pixel_format)
# TEST pattern setup
self.cxp.write32(_TEST_PATTERN, test_pattern)
# TRIGGER setup
self.cxp.write32(_TRIG_MODE_INDEX, 1) # ON
self.cxp.write32(_TRIG_SRC_INDEX, 7) # CXPTrigger0
self.cxp.write32(_TRIG_ACT_INDEX, 2) # trig on any-edge
# Ready to accept trigger
self.cxp.write32(_REAL_ACQ_MODE, 1) # set single frame mode
self.cxp.write32(_REAL_ACQ_START, 1) # single acq start
# Print out current transfer bit depth
_BSL_TRANSFER_BITDEPTH_MODE = 0x10001B8C # d_49
_BSL_TRANSFER_BITDEPTH = 0x10001C10 # d_50
# print(self.cxp.read32(_BSL_TRANSFER_BITDEPTH))
@kernel
def camera_trig_and_get_roi(self, standalone=False):
if standalone:
self.core.reset()
self.cxp.setup_roi(0, 0, 0, 1, 1)
self.cxp.setup_roi(1, 0, 2047, 1, 2048)
self.cxp.setup_roi(2, 2443, 2047, 2448, 2048)
self.cxp.setup_roi(3, 2443, 0, 2448, 1)
self.cxp.setup_roi(4, 1, 1, 5, 5)
self.cxp.setup_roi(5, 1, 1, 4, 4)
self.cxp.setup_roi(6, 1, 1, 3, 3)
self.cxp.setup_roi(7, 1, 1, 2, 2)
self.cxp.gate_roi(self.gate_mask)
delay(1 * ms)
self.cxp.send_cxp_linktrigger(0) # CXPTrigger0
self.core.wait_until_mu(now_mu() + 10_000_000)
self.cxp.input_mu(self.cnt, -1)
@kernel
def download_xml(self):
buffer = [0] * (102400 // 4)
self.cxp.read_local_xml(buffer)
write_file(buffer, "camera_setting.zip")
@kernel
def capture_full_frame(self, pixel_format, test_pattern):
width, height = 2448, 2048
frame_matrix = array([[0] * width] * height)
line_buffer = array([[0] * width])
self.cxp.write32(_REAL_ACQ_ABORT, 1)
self.cxp.write32(_PIXEL_FORMAT, pixel_format)
self.cxp.write32(_TEST_PATTERN, test_pattern)
# TRIGGER setup
self.cxp.write32(_TRIG_MODE_INDEX, 1) # ON
self.cxp.write32(_TRIG_SRC_INDEX, 7) # CXPTrigger0
self.cxp.write32(_TRIG_ACT_INDEX, 2) # trig on any-edge
# Ready to accept trigger
# self.cxp.write32(_REAL_ACQ_MODE, 1) # set single frame mode
self.cxp.write32(_REAL_ACQ_MODE, 0) # Continuously accept trigger
self.cxp.write32(_REAL_ACQ_START, 1)
# Send trigger
self.core.reset()
pixel_width = 0
for line in range(height):
self.cxp.start_roi_viewer(0, line, 2448, line + 1)
delay(30 * ms)
self.cxp.send_cxp_linktrigger(0) # CXPTrigger0
# Wait until fifo is full
# MONO8 = 10 milliseconds
# MONO10 = 11 milliseconds
# MONO12 = 12 milliseconds
self.core.wait_until_mu(now_mu() + 12_000_000)
pixel_width = self.cxp.read_roi_viewer_frame(line_buffer)
for x in range(width):
frame_matrix[line][x] = (
line_buffer[0][x]
if pixel_width == 8
else line_buffer[0][x] << 16 - pixel_width
)
self.cxp.write32(_REAL_ACQ_STOP, 1)
write_pgm(frame_matrix, "./test.pgm", 8 if pixel_width == 8 else 16)
def run(self):
# self.download_xml()
# Following GenICam PFNC standard
pixel_formats = {
"MONO8": 17301505,
"MONO10": 17825795, # MONO10 unpacked
"MONO12": 17825797, # MONO12 unpacked
}
# see https://docs.baslerweb.com/test-patterns
test_patterns = {
"OFF": 0, # normal operation
"BLACK": 1, # all pixels set to 0
"WHITE": 2, # all pixels set to (2^N)-1
"TESTIMAGE1": 3, # fixed diagonal gray gradients ranging from 0 to 255
"TESTIMAGE2": 4,
"TESTIMAGE3": 5, # moving gradient ranging from 0 to 4095
}
if True:
pix_fmt, test_pat = "MONO8", "TESTIMAGE1"
print(f"Capturing full frame {pix_fmt} {test_pat} pattern")
self.capture_full_frame(pixel_formats[pix_fmt], test_patterns[test_pat])
else:
for fmt_name, fmt_code in pixel_formats.items():
self.camera_trigger_nonrealtime_setup(fmt_code, test_patterns["WHITE"])
self.camera_trig_and_get_roi(True)
print(f"{fmt_name} count list = {self.cnt}")
self.camera_sensor_off()

16
flake.lock generated
View File

@ -11,11 +11,11 @@
"src-pythonparser": "src-pythonparser"
},
"locked": {
"lastModified": 1741925484,
"narHash": "sha256-sovpN0Ii+GOVY4Qku8eal5hWATScCc1VbYnfdwc88dQ=",
"lastModified": 1742274039,
"narHash": "sha256-K0Np2ZvtB37Gq1ZbKf1mfSWfMbtPjZVdKtOHAS/Pauk=",
"ref": "refs/heads/master",
"rev": "cdacbf71c569b0968c81fb358838e9b69946af26",
"revCount": 9192,
"rev": "9e655332e35bac5c365e80c0c595767f5953bcb3",
"revCount": 9198,
"type": "git",
"url": "https://github.com/m-labs/artiq.git"
},
@ -229,11 +229,11 @@
"rust-overlay": "rust-overlay_2"
},
"locked": {
"lastModified": 1741773807,
"narHash": "sha256-pywveAL5C0Jl+qo8XmgAL11RfrnSW/Afu3Yg1K/lAcU=",
"lastModified": 1742368409,
"narHash": "sha256-da8QiftpLR/UsNFc0vGczg+EGAHgxuQCvy2QWhRfYVk=",
"ref": "refs/heads/master",
"rev": "905b97431de37ccc61f8babd7def99627578a843",
"revCount": 694,
"rev": "671cfc894c0c285008a8830b3c32cd62cce881a0",
"revCount": 695,
"type": "git",
"url": "https://git.m-labs.hk/m-labs/zynq-rs"
},

View File

@ -122,7 +122,11 @@ impl IoExpander {
// Check for ack from io expander
self.select(i2c)?;
i2c.start()?;
let ack = i2c.write(self.address)?;
let ack = match i2c.write(self.address) {
Ok(()) => true,
Err(i2c::Error::Nack) => false,
Err(e) => return Err(e.into()),
};
i2c.stop()?;
Ok(ack)
}

View File

@ -1,7 +1,9 @@
use core::result;
use embedded_hal::blocking::delay::DelayUs;
use libboard_zynq::{i2c::I2c, time::Milliseconds, timer::GlobalTimer};
use libboard_zynq::{i2c::{Error as I2cError, I2c},
time::Milliseconds,
timer::GlobalTimer};
use log::info;
#[cfg(not(si5324_soft_reset))]
@ -97,15 +99,18 @@ fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySetti
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address");
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register");
}
if !i2c.write(val).unwrap() {
return Err("Si5324 failed to ack value");
}
i2c.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
i2c.write(val).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack value",
err => err.into(),
})?;
i2c.stop().unwrap();
Ok(())
}
@ -113,29 +118,37 @@ fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
#[allow(dead_code)]
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address");
i2c.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
match i2c.write(val) {
Ok(()) | Err(I2cError::Nack) => (),
Err(e) => return Err(e.into()),
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register");
}
i2c.write(val).unwrap();
i2c.stop().unwrap();
Ok(())
}
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
i2c.start().unwrap();
if !i2c.write(ADDRESS << 1).unwrap() {
return Err("Si5324 failed to ack write address");
}
if !i2c.write(reg).unwrap() {
return Err("Si5324 failed to ack register");
}
i2c.write(ADDRESS << 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack write address",
err => err.into(),
})?;
i2c.write(reg).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack register",
err => err.into(),
})?;
i2c.restart().unwrap();
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
return Err("Si5324 failed to ack read address");
}
i2c.write((ADDRESS << 1) | 1).map_err(|err| match err {
I2cError::Nack => "Si5324 failed to ack read address",
err => err.into(),
})?;
let val = i2c.read(false).unwrap();
i2c.stop().unwrap();
Ok(val)

View File

@ -46,7 +46,9 @@ impl fmt::Display for Error {
&Error::ROISizeTooBig(width, height) => {
write!(
f,
"ROISizeTooBig - The maximum ROIViewer size is {} pixels but the ROI is set to {} ({}x{}) pixels",
"ROISizeTooBig - The maximum ROIViewer height and total size are {} and {} pixels respectively \
but the ROI is set to {} ({}x{}) pixels",
ROI_MAX_SIZE / 4,
ROI_MAX_SIZE,
width * height,
width,
@ -170,9 +172,9 @@ pub extern "C" fn write32(addr: i32, val: i32) {
}
}
pub extern "C" fn start_roi_viewer(x0: i32, x1: i32, y0: i32, y1: i32) {
pub extern "C" fn start_roi_viewer(x0: i32, y0: i32, x1: i32, y1: i32) {
let (width, height) = ((x1 - x0) as usize, (y1 - y0) as usize);
if width * height > ROI_MAX_SIZE {
if width * height > ROI_MAX_SIZE || height > ROI_MAX_SIZE / 4 {
artiq_raise!("CXPError", format!("{}", Error::ROISizeTooBig(width, height)));
} else {
unsafe {

View File

@ -1,6 +1,6 @@
use core::mem::MaybeUninit;
use libboard_zynq::i2c::I2c;
use libboard_zynq::i2c::{Error, I2c};
use crate::artiq_raise;
@ -38,7 +38,8 @@ pub extern "C" fn write(busno: i32, data: i32) -> bool {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
match get_bus().write(data as u8) {
Ok(r) => r,
Ok(()) => true,
Err(Error::Nack) => false,
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
}
}

View File

@ -44,7 +44,10 @@ use libboard_artiq::{drtio_routing, drtioaux,
pl::csr};
#[cfg(feature = "target_kasli_soc")]
use libboard_zynq::error_led::ErrorLED;
use libboard_zynq::{i2c::I2c, print, println, slcr, time::Milliseconds, timer::GlobalTimer};
use libboard_zynq::{i2c::{Error as I2cError, I2c},
print, println, slcr,
time::Milliseconds,
timer::GlobalTimer};
use libconfig::Config;
use libcortex_a9::{l2c::enable_l2_cache, regs::MPIDR};
use libregister::RegisterR;
@ -435,11 +438,18 @@ fn process_aux_packet(
timer
);
match i2c.write(data) {
Ok(ack) => drtioaux::send(
Ok(()) => drtioaux::send(
0,
&drtioaux::Packet::I2cWriteReply {
succeeded: true,
ack: ack,
ack: true,
},
),
Err(I2cError::Nack) => drtioaux::send(
0,
&drtioaux::Packet::I2cWriteReply {
succeeded: true,
ack: false,
},
),
Err(_) => drtioaux::send(