Skip to content

Commit

Permalink
siphaser: autocalibrate skew using RX synchronizer
Browse files Browse the repository at this point in the history
* removes the hardcoded, (poorly) manually determined skew value
* does not need si5324_clkout_fabric anymore (broken on Sayma RTM due to wrong IO voltage)
  • Loading branch information
sbourdeauducq committed Jan 2, 2019
1 parent f5cda36 commit cc58318
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 67 deletions.
73 changes: 26 additions & 47 deletions artiq/firmware/libboard_artiq/si5324.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,66 +291,45 @@ pub mod siphaser {
clock::spin_us(500);
}

fn get_phaser_sample() -> bool {
let mut sample = true;
for _ in 0..32 {
if unsafe { csr::siphaser::sample_result_read() } == 0 {
sample = false;
}
fn has_error() -> bool {
unsafe {
csr::siphaser::error_write(1);
}
clock::spin_us(5000);
unsafe {
csr::siphaser::error_read() != 0
}
sample
}

const PS_MARGIN: u32 = 28;
fn find_edge(target: bool) -> Result<u16> {
let mut nshifts = 0;

fn get_stable_phaser_sample() -> (bool, u32) {
let mut nshifts: u32 = 0;
let mut previous = has_error();
loop {
let s1 = get_phaser_sample();
for _ in 0..PS_MARGIN {
phase_shift(1);
}
let s2 = get_phaser_sample();
for _ in 0..PS_MARGIN {
phase_shift(1);
phase_shift(1);
nshifts += 1;
let current = has_error();
if previous != target && current == target {
return Ok(nshifts);
}
let s3 = get_phaser_sample();
nshifts += 2*PS_MARGIN;
if s1 == s2 && s2 == s3 {
for _ in 0..PS_MARGIN {
phase_shift(0);
}
nshifts -= PS_MARGIN;
return (s2, nshifts);
if nshifts > 5000 {
return Err("failed to find timing error edge");
}
previous = current;
}
}

pub fn calibrate_skew(skew: u16) -> Result<()> {
// Get into a 0 region
let (s1, mut nshifts) = get_stable_phaser_sample();
if s1 {
while get_phaser_sample() {
phase_shift(1);
nshifts += 1;
}
for _ in 0..PS_MARGIN {
phase_shift(1);
}
nshifts += PS_MARGIN;
}
pub fn calibrate_skew() -> Result<()> {
let lead = find_edge(false)?;
let width = find_edge(true)?;
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));

// Get to the 0->1 transition
while !get_phaser_sample() {
phase_shift(1);
nshifts += 1;
// Apply reverse phase shift for half the width to get into the
// middle of the working region.
for _ in 0..width/2 {
phase_shift(0);
}
info!("nshifts to 0->1 siphaser transition: {} ({}deg)", nshifts, nshifts*360/(56*8));

// Apply specified skew referenced to that transition
for _ in 0..skew {
phase_shift(1);
}
Ok(())
}
}
4 changes: 1 addition & 3 deletions artiq/firmware/satman/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,6 @@ const SI5324_SETTINGS: si5324::FrequencySettings
crystal_ref: true
};

const SIPHASER_PHASE: u16 = 32;

#[no_mangle]
pub extern fn main() -> i32 {
clock::init();
Expand Down Expand Up @@ -443,7 +441,7 @@ pub extern fn main() -> i32 {

info!("uplink is up, switching to recovered clock");
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(SIPHASER_PHASE).expect("failed to calibrate skew");
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");

#[cfg(has_ad9154)]
{
Expand Down
4 changes: 2 additions & 2 deletions artiq/gateware/drtio/rx_synchronizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class XilinxRXSynchronizer(Module):
"""Deterministic RX synchronizer using a relatively placed macro
to put the clock-domain-crossing FFs right next to each other.
To meet setup/hold constraints receiving FFs, adjust the phase shift
of the Si5324.
To meet setup/hold constraints at the receiving FFs, adjust the phase shift
of the jitter cleaner.
We assume that FPGA routing variations are small enough to be negligible.
"""
Expand Down
34 changes: 21 additions & 13 deletions artiq/gateware/drtio/siphaser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from migen import *
from migen.genlib.cdc import MultiReg
from migen.genlib.cdc import MultiReg, PulseSynchronizer

from misoc.interconnect.csr import *

Expand All @@ -8,12 +8,12 @@
# frequency.

class SiPhaser7Series(Module, AutoCSR):
def __init__(self, si5324_clkin, si5324_clkout_fabric,
def __init__(self, si5324_clkin, rx_synchronizer,
ref_clk=None, ref_div2=False, rtio_clk_freq=150e6):
self.switch_clocks = CSRStorage()
self.phase_shift = CSR()
self.phase_shift_done = CSRStatus(reset=1)
self.sample_result = CSRStatus()
self.error = CSR()

assert rtio_clk_freq in (125e6, 150e6)

Expand Down Expand Up @@ -85,16 +85,24 @@ def __init__(self, si5324_clkin, si5324_clkout_fabric,
)
]

si5324_clkout_se = Signal()
self.specials += \
Instance("IBUFDS",
p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="TRUE",
i_I=si5324_clkout_fabric.p, i_IB=si5324_clkout_fabric.n,
o_O=si5324_clkout_se),

clkout_sample1 = Signal() # IOB register
self.sync.rtio_rx0 += clkout_sample1.eq(si5324_clkout_se)
self.specials += MultiReg(clkout_sample1, self.sample_result.status)
# The RX synchronizer is tested for setup/hold violations by feeding it a
# toggling pattern and checking that the same toggling pattern comes out.
toggle_in = Signal()
self.sync.rtio_rx0 += toggle_in.eq(~toggle_in)
toggle_out = rx_synchronizer.resync(toggle_in)

toggle_out_expected = Signal()
self.sync.rtio += toggle_out_expected.eq(~toggle_out)

error = Signal()
error_clear = PulseSynchronizer("sys", "rtio")
self.submodules += error_clear
self.sync.rtio += [
If(toggle_out != toggle_out_expected, error.eq(1)),
If(error_clear.o, error.eq(0))
]
self.specials += MultiReg(error, self.error.w)
self.comb += error_clear.i.eq(self.error.re)

# expose MMCM outputs - used for clock constraints
self.mmcm_freerun_output = mmcm_freerun_output
Expand Down
2 changes: 1 addition & 1 deletion artiq/gateware/targets/kasli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ def __init__(self, rtio_clk_freq=125e6, **kwargs):
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
si5324_clkout_fabric=platform.request("si5324_clkout_fabric"),
rx_synchronizer=self.rx_synchronizer,
ref_clk=self.crg.clk125_div2, ref_div2=True,
rtio_clk_freq=rtio_clk_freq)
platform.add_false_path_constraints(
Expand Down
2 changes: 1 addition & 1 deletion artiq/gateware/targets/sayma_amc.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ def __init__(self, with_sawg, **kwargs):
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"),
si5324_clkout_fabric=platform.request("si5324_clkout_fabric"))
rx_synchronizer=self.rx_synchronizer)
platform.add_platform_command("set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {mmcm_ps}]",
mmcm_ps=self.siphaser.mmcm_ps_output)
platform.add_false_path_constraints(
Expand Down

0 comments on commit cc58318

Please sign in to comment.