Add grabber module to release-7 #271

Merged
sb10q merged 1 commits from esavkin/artiq-zynq:264-grabber-backport into release-7 2023-10-19 14:24:03 +08:00
5 changed files with 195 additions and 4 deletions

View File

@ -77,7 +77,7 @@ class RTIOCRG(Module, AutoCSR):
MultiReg(pll_locked, self.pll_locked.status)
]
eem_iostandard_dict = {
0: "LVDS_25",
1: "LVDS_25",
@ -114,6 +114,7 @@ class SMAClkinForward(Module):
class GenericStandalone(SoCCore):
def __init__(self, description, acpki=False):
self.acpki = acpki
sys_clk_freq = 125e6
platform = kasli_soc.Platform()
platform.toolchain.bitstream_commands.extend([
@ -142,6 +143,7 @@ class GenericStandalone(SoCCore):
self.ps7.cd_sys.clk,
self.rtio_crg.cd_rtio.clk)
fix_serdes_timing_path(platform)
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)

Looks incorrect. On softcore platforms this is set by MiSoC which does not know anything about RTIO, so this has to be the system clock frequency.

Looks incorrect. On softcore platforms this is set by MiSoC which does not know anything about RTIO, so this has to be the system clock frequency.

Is that that is defined by such line in GenericMaster?

sys_clk_freq = 125e6
Is that that is defined by such line in GenericMaster? ``` sys_clk_freq = 125e6 ```

I'm just talking about the value of self.config["CLOCK_FREQUENCY"] here.

I'm just talking about the value of ``self.config["CLOCK_FREQUENCY"]`` here.

I'm just talking about the value of self.config["CLOCK_FREQUENCY"] here.

Yes, I mean so this has to be the system clock frequency refers to sys_clk_freq = 125e6 ? Or it is another variable generated somewhere else?

> I'm just talking about the value of ``self.config["CLOCK_FREQUENCY"]`` here. Yes, I mean `so this has to be the system clock frequency` refers to `sys_clk_freq = 125e6` ? Or it is another variable generated somewhere else?

sys_clk_freq is the one that should be used, as grabber clock is based off the system clock domain (deserializer_7series.py#L22) , and as RTIO clock (theoretically) can be of a different frequency.

And yes it's defined right there. Migen-axi doesn't redefine the sys clock.

``sys_clk_freq`` is the one that should be used, as grabber clock is based off the system clock domain ([deserializer_7series.py#L22](https://github.com/m-labs/artiq/blob/release-7/artiq/gateware/grabber/deserializer_7series.py#L22)) , and as RTIO clock (theoretically) can be of a different frequency. And yes it's defined right there. Migen-axi doesn't redefine the sys clock.
self.rtio_channels = []
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
@ -226,6 +228,7 @@ class GenericMaster(SoCCore):
pads=data_pads,
sys_clk_freq=sys_clk_freq)
self.csr_devices.append("drtio_transceiver")
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
self.crg = self.ps7 # HACK for eem_7series to find the clock
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
@ -330,7 +333,7 @@ class GenericMaster(SoCCore):
class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False):
sys_clk_freq = 125e6
sys_clk_freq = 125e6
rtio_clk_freq = description["rtio_frequency"]
self.acpki = acpki
@ -353,7 +356,7 @@ class GenericSatellite(SoCCore):
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
self.csr_devices.append("rtio_crg")
fix_serdes_timing_path(platform)
data_pads = [platform.request("sfp", i) for i in range(4)]
self.submodules.drtio_transceiver = gtx_7series.GTX(
@ -458,6 +461,7 @@ class GenericSatellite(SoCCore):
rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
self.config["CLOCK_FREQUENCY"] = int(sys_clk_freq)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk"),
@ -485,7 +489,7 @@ class GenericSatellite(SoCCore):
self.config["HAS_GRABBER"] = None
self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here
self.submodules.virtual_leds = virtual_leds.VirtualLeds()
self.csr_devices.append("virtual_leds")

View File

@ -0,0 +1,163 @@
use log::info;
use crate::pl::csr;
#[derive(PartialEq, Clone, Copy)]
enum State {
Reset,
ExitReset,
Lock,
Align,
Watch,
}
#[derive(Clone, Copy)]
struct Info {
state: State,
frame_size: (u16, u16),
}
static mut INFO: [Info; csr::GRABBER_LEN] = [Info {
state: State::Reset,
frame_size: (0, 0),
}; csr::GRABBER_LEN];
fn get_pll_reset(g: usize) -> bool {
unsafe { (csr::GRABBER[g].pll_reset_read)() != 0 }
}
fn set_pll_reset(g: usize, reset: bool) {
let val = if reset { 1 } else { 0 };
unsafe { (csr::GRABBER[g].pll_reset_write)(val) }
}
fn pll_locked(g: usize) -> bool {
unsafe { (csr::GRABBER[g].pll_locked_read)() != 0 }
}
fn clock_pattern_ok(g: usize) -> bool {
unsafe { (csr::GRABBER[g].clk_sampled_read)() == 0b1100011 }
}
fn clock_pattern_ok_filter(g: usize) -> bool {
for _ in 0..128 {
if !clock_pattern_ok(g) {
return false;
}
}
true
}
fn phase_shift(g: usize, direction: u8) {
unsafe {
(csr::GRABBER[g].phase_shift_write)(direction);
while (csr::GRABBER[g].phase_shift_done_read)() == 0 {}
}
}
fn clock_align(g: usize) -> bool {
while clock_pattern_ok_filter(g) {
phase_shift(g, 1);
}
phase_shift(g, 1);
let mut count = 0;
while !clock_pattern_ok_filter(g) {
phase_shift(g, 1);
count += 1;
if count > 1024 {
return false;
}
}
let mut window = 1;
phase_shift(g, 1);
while clock_pattern_ok_filter(g) {
phase_shift(g, 1);
window += 1;
}
for _ in 0..window / 2 {
phase_shift(g, 0);
}
true
}
fn get_last_pixels(g: usize) -> (u16, u16) {
unsafe { ((csr::GRABBER[g].last_x_read)(), (csr::GRABBER[g].last_y_read)()) }
}
fn get_video_clock(g: usize) -> u32 {
let freq_count = unsafe { (csr::GRABBER[g].freq_count_read)() } as u32;
2 * freq_count * (csr::CONFIG_CLOCK_FREQUENCY / 1000) / (511 * 1000)
}
pub fn tick() {
for g in 0..csr::GRABBER.len() {
let next = match unsafe { INFO[g].state } {
State::Reset => {
set_pll_reset(g, true);
unsafe {
INFO[g].frame_size = (0, 0);
}
State::ExitReset
}
State::ExitReset => {
if get_pll_reset(g) {
set_pll_reset(g, false);
State::Lock
} else {
State::ExitReset
}
}
State::Lock => {
if pll_locked(g) {
info!("grabber{} locked: {}MHz", g, get_video_clock(g));
State::Align
} else {
State::Lock
}
}
State::Align => {
if pll_locked(g) {
if clock_align(g) {
info!("grabber{} alignment success", g);
State::Watch
} else {
info!("grabber{} alignment failure", g);
State::Reset
}
} else {
info!("grabber{} lock lost", g);
State::Reset
}
}
State::Watch => {
if pll_locked(g) {
if clock_pattern_ok(g) {
let last_xy = get_last_pixels(g);
if last_xy != unsafe { INFO[g].frame_size } {
// x capture is on ~LVAL which is after
// the last increment on DVAL
// y capture is on ~FVAL which coincides with the
// last increment on ~LVAL
info!("grabber{} frame size: {}x{}", g, last_xy.0, last_xy.1 + 1);
unsafe { INFO[g].frame_size = last_xy }
}
State::Watch
} else {
info!("grabber{} alignment lost", g);
State::Reset
}
} else {
info!("grabber{} lock lost", g);
State::Reset
}
}
};
unsafe {
INFO[g].state = next;
}
}
}

View File

@ -29,6 +29,8 @@ pub mod drtioaux_async;
pub mod mem;
#[cfg(feature = "target_kasli_soc")]
pub mod io_expander;
#[cfg(has_grabber)]
pub mod grabber;
use core::{cmp, str};
use libboard_zynq::slcr;

View File

@ -110,6 +110,21 @@ async fn io_expanders_service(
.expect("I2C I/O expander #1 service failed");
}
}
#[cfg(has_grabber)]
mod grabber {
use libasync::delay;
use libboard_artiq::grabber;
use libboard_zynq::time::Milliseconds;
use crate::GlobalTimer;
pub async fn grabber_thread(timer: GlobalTimer) {
let mut countdown = timer.countdown();
loop {
grabber::tick();
delay(&mut countdown, Milliseconds(200)).await;
}
}
}
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
#[no_mangle]
@ -168,6 +183,9 @@ pub fn main_core0() {
task::spawn(report_async_rtio_errors());
#[cfg(has_grabber)]
task::spawn(grabber::grabber_thread(timer));
#[cfg(feature = "target_kasli_soc")]
task::spawn(io_expanders_service(
RefCell::new(i2c_bus),

View File

@ -20,6 +20,8 @@ extern crate alloc;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libsupport_zynq::ram;
#[cfg(has_grabber)]
use libboard_artiq::grabber;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
#[cfg(feature = "target_kasli_soc")]
@ -417,6 +419,8 @@ fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
if now > ts_ms {
ts_ms = now + Milliseconds(200);
*ts = ts_ms.0;
#[cfg(has_grabber)]
grabber::tick();
}
}