2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-25 03:08:27 +08:00

siphaser: autocalibrate skew using RX synchronizer

* removes the hardcoded, (poorly) manually determined skew value
* does not need si5324_clkout_fabric anymore (broken on Sayma RTM due to wrong IO voltage)
This commit is contained in:
Sebastien Bourdeauducq 2019-01-02 22:29:27 +08:00
parent f5cda3689e
commit cc58318500
6 changed files with 54 additions and 69 deletions

View File

@ -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);
}
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);
}
}
}
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;
}
// Get to the 0->1 transition
while !get_phaser_sample() {
phase_shift(1);
nshifts += 1;
let current = has_error();
if previous != target && current == target {
return Ok(nshifts);
}
if nshifts > 5000 {
return Err("failed to find timing error edge");
}
previous = current;
}
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);
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));
// 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);
}
Ok(())
}
}

View File

@ -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();
@ -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)]
{

View File

@ -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.
"""

View File

@ -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 *
@ -8,12 +8,12 @@ from misoc.interconnect.csr import *
# 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)
@ -85,16 +85,24 @@ class SiPhaser7Series(Module, AutoCSR):
)
]
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),
# 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)
clkout_sample1 = Signal() # IOB register
self.sync.rtio_rx0 += clkout_sample1.eq(si5324_clkout_se)
self.specials += MultiReg(clkout_sample1, self.sample_result.status)
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

View File

@ -1046,7 +1046,7 @@ class _SatelliteBase(BaseSoC):
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(

View File

@ -641,7 +641,7 @@ class Satellite(BaseSoC, RTMCommon):
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(