forked from M-Labs/artiq
drtio: implement Si5324 phaser gateware and partial firmware support
This commit is contained in:
parent
994ceca9ff
commit
c34d00cbc9
|
@ -1,6 +1,6 @@
|
|||
use core::result;
|
||||
use board::clock;
|
||||
#[cfg(not(si5324_soft_reset))]
|
||||
#[cfg(any(not(si5324_soft_reset), has_si_phaser))]
|
||||
use board::csr;
|
||||
use i2c;
|
||||
|
||||
|
@ -47,6 +47,11 @@ pub struct FrequencySettings {
|
|||
pub crystal_ref: bool
|
||||
}
|
||||
|
||||
pub enum Input {
|
||||
Ckin1,
|
||||
Ckin2,
|
||||
}
|
||||
|
||||
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> {
|
||||
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 {
|
||||
return Err("NC1_LS must be 0 or even")
|
||||
|
@ -160,8 +165,11 @@ fn has_xtal() -> Result<bool> {
|
|||
Ok((read(129)? & 0x01) == 0) // LOSX_INT=0
|
||||
}
|
||||
|
||||
fn has_clkin2() -> Result<bool> {
|
||||
Ok((read(129)? & 0x04) == 0) // LOS2_INT=0
|
||||
fn has_ckin(input: Input) -> Result<bool> {
|
||||
match input {
|
||||
Input::Ckin1 => Ok((read(129)? & 0x02) == 0), // LOS1_INT=0
|
||||
Input::Ckin2 => Ok((read(129)? & 0x04) == 0), // LOS2_INT=0
|
||||
}
|
||||
}
|
||||
|
||||
fn locked() -> Result<bool> {
|
||||
|
@ -180,7 +188,7 @@ fn monitor_lock() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup(settings: &FrequencySettings) -> Result<()> {
|
||||
pub fn setup(settings: &FrequencySettings, input: Input) -> Result<()> {
|
||||
let s = map_frequency_settings(settings)?;
|
||||
|
||||
#[cfg(not(si5324_soft_reset))]
|
||||
|
@ -203,12 +211,16 @@ pub fn setup(settings: &FrequencySettings) -> Result<()> {
|
|||
#[cfg(si5324_soft_reset)]
|
||||
soft_reset()?;
|
||||
|
||||
let cksel_reg = match input {
|
||||
Input::Ckin1 => 0b00,
|
||||
Input::Ckin2 => 0b01,
|
||||
};
|
||||
if settings.crystal_ref {
|
||||
write(0, read(0)? | 0x40)?; // FREE_RUN=1
|
||||
}
|
||||
write(2, (read(2)? & 0x0f) | (s.bwsel << 4))?;
|
||||
write(21, read(21)? & 0xfe)?; // CKSEL_PIN=0
|
||||
write(3, (read(3)? & 0x3f) | (0b01 << 6) | 0x10)?; // CKSEL_REG=b01 SQ_ICAL=1
|
||||
write(3, (read(3)? & 0x3f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1
|
||||
write(4, (read(4)? & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||
write(6, (read(6)? & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||
write(25, (s.n1_hs << 5 ) as u8)?;
|
||||
|
@ -233,20 +245,40 @@ pub fn setup(settings: &FrequencySettings) -> Result<()> {
|
|||
if !has_xtal()? {
|
||||
return Err("Si5324 misses XA/XB signal");
|
||||
}
|
||||
if !has_clkin2()? {
|
||||
return Err("Si5324 misses CLKIN2 signal");
|
||||
if !has_ckin(input)? {
|
||||
return Err("Si5324 misses clock input signal");
|
||||
}
|
||||
|
||||
monitor_lock()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_ext_input(external: bool) -> Result<()> {
|
||||
if external {
|
||||
write(3, (read(3)? & 0x3f) | (0b00 << 6))?; // CKSEL_REG=b00
|
||||
} else {
|
||||
write(3, (read(3)? & 0x3f) | (0b01 << 6))?; // CKSEL_REG=b01
|
||||
pub fn select_input(input: Input) -> Result<()> {
|
||||
let cksel_reg = match input {
|
||||
Input::Ckin1 => 0b00,
|
||||
Input::Ckin2 => 0b01,
|
||||
};
|
||||
write(3, (read(3)? & 0x3f) | (cksel_reg << 6))?;
|
||||
if !has_ckin(input)? {
|
||||
return Err("Si5324 misses clock input signal");
|
||||
}
|
||||
monitor_lock()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(has_si_phaser)]
|
||||
pub fn select_recovered_clock(rc: bool) -> Result<()> {
|
||||
write(3, (read(3)? & 0xdf) | (1 << 5))?; // DHOLD=1
|
||||
unsafe {
|
||||
csr::si_phaser::switch_clocks_write(if rc { 1 } else { 0 });
|
||||
}
|
||||
write(3, (read(3)? & 0xdf) | (1 << 5))?; // DHOLD=0
|
||||
monitor_lock()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(has_si_phaser)]
|
||||
pub fn calibrate_skew() -> Result<()> {
|
||||
// TODO: implement
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ fn setup_si5324_as_synthesizer()
|
|||
bwsel : 3,
|
||||
crystal_ref: true
|
||||
};
|
||||
board_artiq::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324");
|
||||
board_artiq::si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin2).expect("cannot initialize Si5324");
|
||||
}
|
||||
|
||||
#[cfg(has_ethmac)]
|
||||
|
|
|
@ -195,14 +195,14 @@ fn process_errors() {
|
|||
|
||||
#[cfg(rtio_frequency = "150.0")]
|
||||
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||
= si5324::FrequencySettings {
|
||||
n1_hs : 9,
|
||||
nc1_ls : 4,
|
||||
= si5324::FrequencySettings {
|
||||
n1_hs : 6,
|
||||
nc1_ls : 6,
|
||||
n2_hs : 10,
|
||||
n2_ls : 33732,
|
||||
n31 : 9370,
|
||||
n32 : 7139,
|
||||
bwsel : 3,
|
||||
n2_ls : 270,
|
||||
n31 : 75,
|
||||
n32 : 75,
|
||||
bwsel : 4,
|
||||
crystal_ref: true
|
||||
};
|
||||
|
||||
|
@ -225,7 +225,7 @@ fn startup() {
|
|||
/* must be the first SPI init because of HMC830 SPI mode selection */
|
||||
hmc830_7043::init().expect("cannot initialize HMC830/7043");
|
||||
i2c::init();
|
||||
si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324");
|
||||
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||
unsafe {
|
||||
csr::drtio_transceiver::stable_clkin_write(1);
|
||||
}
|
||||
|
@ -235,7 +235,8 @@ fn startup() {
|
|||
process_errors();
|
||||
}
|
||||
info!("link is up, switching to recovered clock");
|
||||
si5324::select_ext_input(true).expect("failed to switch clocks");
|
||||
si5324::select_recovered_clock(true).expect("failed to switch clocks");
|
||||
si5324::calibrate_skew().expect("failed to calibrate skew");
|
||||
drtio_reset(false);
|
||||
drtio_reset_phy(false);
|
||||
while drtio_link_rx_up() {
|
||||
|
@ -245,7 +246,7 @@ fn startup() {
|
|||
drtio_reset_phy(true);
|
||||
drtio_reset(true);
|
||||
info!("link is down, switching to local crystal clock");
|
||||
si5324::select_ext_input(false).expect("failed to switch clocks");
|
||||
si5324::select_recovered_clock(false).expect("failed to switch clocks");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
from migen import *
|
||||
from migen.genlib.cdc import MultiReg
|
||||
|
||||
from misoc.interconnect.csr import *
|
||||
|
||||
|
||||
# This code assumes 125MHz system clock and 150MHz RTIO frequency.
|
||||
|
||||
class SiPhaser7Series(Module, AutoCSR):
|
||||
def __init__(self, si5324_clkin, si5324_clkout_fabric):
|
||||
self.switch_clocks = CSRStorage()
|
||||
self.phase_shift = CSR()
|
||||
self.phase_shift_done = CSRStatus()
|
||||
self.sample_result = CSRStatus()
|
||||
|
||||
# 125MHz system clock to 150MHz. VCO @ 625MHz.
|
||||
# Used to provide a startup clock to the transceiver through the Si,
|
||||
# we do not use the crystal reference so that the PFD (f3) frequency
|
||||
# can be high.
|
||||
mmcm_freerun_fb = Signal()
|
||||
mmcm_freerun_output = Signal()
|
||||
self.specials += \
|
||||
Instance("MMCME2_BASE",
|
||||
p_CLKIN1_PERIOD=1e9/125e6,
|
||||
i_CLKIN1=ClockSignal("sys"),
|
||||
i_RST=ResetSignal("sys"),
|
||||
|
||||
p_CLKFBOUT_MULT_F=6.0, p_DIVCLK_DIVIDE=1,
|
||||
|
||||
o_CLKFBOUT=mmcm_freerun_fb, i_CLKFBIN=mmcm_freerun_fb,
|
||||
|
||||
p_CLKOUT0_DIVIDE_F=5.0, o_CLKOUT0=mmcm_freerun_output,
|
||||
)
|
||||
|
||||
# 150MHz to 150MHz with controllable phase shift, VCO @ 1200MHz.
|
||||
# Inserted between CDR and output to Si, used to correct
|
||||
# non-determinstic skew of Si5324.
|
||||
mmcm_ps_fb = Signal()
|
||||
mmcm_ps_output = Signal()
|
||||
self.specials += \
|
||||
Instance("MMCME2_ADV",
|
||||
p_CLKIN1_PERIOD=1e9/150e6,
|
||||
i_CLKIN1=ClockSignal("rtio_rx0"),
|
||||
i_RST=ResetSignal("rtio_rx0"),
|
||||
i_CLKINSEL=1, # yes, 1=CLKIN1 0=CLKIN2
|
||||
|
||||
p_CLKFBOUT_MULT_F=8.0,
|
||||
p_CLKOUT0_DIVIDE_F=8.0,
|
||||
p_DIVCLK_DIVIDE=1,
|
||||
|
||||
o_CLKFBOUT=mmcm_ps_fb, i_CLKFBIN=mmcm_ps_fb,
|
||||
|
||||
p_CLKOUT0_USE_FINE_PS="TRUE",
|
||||
o_CLKOUT0=mmcm_ps_output,
|
||||
|
||||
i_PSCLK=ClockSignal(),
|
||||
i_PSEN=self.phase_shift.re,
|
||||
i_PSINCDEC=self.phase_shift.r,
|
||||
o_PSDONE=self.phase_shift_done.status,
|
||||
)
|
||||
|
||||
si5324_clkin_se = Signal()
|
||||
self.specials += [
|
||||
Instance("BUFGMUX",
|
||||
i_I0=mmcm_freerun_output,
|
||||
i_I1=mmcm_ps_output,
|
||||
i_S=self.switch_clocks.storage,
|
||||
o_O=si5324_clkin_se
|
||||
),
|
||||
Instance("OBUFDS",
|
||||
i_I=si5324_clkin_se,
|
||||
o_O=si5324_clkin.p, o_OB=si5324_clkin.n
|
||||
)
|
||||
]
|
||||
|
||||
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)
|
|
@ -21,6 +21,7 @@ from artiq.gateware.amp import AMPSoC
|
|||
from artiq.gateware import rtio
|
||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, spi2
|
||||
from artiq.gateware.drtio.transceiver import gtp_7series
|
||||
from artiq.gateware.drtio.si_phaser import SiPhaser7Series
|
||||
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||
from artiq.gateware.drtio import DRTIOMaster, DRTIOSatellite
|
||||
from artiq.build_soc import build_artiq_soc
|
||||
|
@ -573,12 +574,11 @@ class Satellite(BaseSoC):
|
|||
self.add_memory_group("drtio_aux", ["drtio0_aux"])
|
||||
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
si5324_clkin = platform.request("si5324_clkin")
|
||||
self.specials += \
|
||||
Instance("OBUFDS",
|
||||
i_I=ClockSignal("rtio_rx0"),
|
||||
o_O=si5324_clkin.p, o_OB=si5324_clkin.n
|
||||
)
|
||||
self.submodules.si_phaser = SiPhaser7Series(
|
||||
si5324_clkin=platform.request("si5324_clkin"),
|
||||
si5324_clkout_fabric=platform.request("si5324_clkout_fabric")
|
||||
)
|
||||
self.csr_devices.append("si_phaser")
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
|
|
Loading…
Reference in New Issue