diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index 448b10f9e..0b198afc3 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -26,8 +26,6 @@ pub mod rpc_queue; #[cfg(has_si5324)] pub mod si5324; -#[cfg(has_wrpll)] -pub mod wrpll; #[cfg(has_hmc830_7043)] pub mod hmc830_7043; diff --git a/artiq/firmware/libboard_artiq/wrpll.rs b/artiq/firmware/libboard_artiq/wrpll.rs deleted file mode 100644 index 4b0fa9754..000000000 --- a/artiq/firmware/libboard_artiq/wrpll.rs +++ /dev/null @@ -1,538 +0,0 @@ -use board_misoc::{csr, clock}; - -mod i2c { - use board_misoc::{csr, clock}; - - #[derive(Debug, Clone, Copy)] - pub enum Dcxo { - Main, - Helper - } - - fn half_period() { clock::spin_us(1) } - const SDA_MASK: u8 = 2; - const SCL_MASK: u8 = 1; - - fn sda_i(dcxo: Dcxo) -> bool { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_in_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_in_read() }, - }; - reg & SDA_MASK != 0 - } - - fn sda_oe(dcxo: Dcxo, oe: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, - }; - let reg = if oe { reg | SDA_MASK } else { reg & !SDA_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } - } - } - - fn sda_o(dcxo: Dcxo, o: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, - }; - let reg = if o { reg | SDA_MASK } else { reg & !SDA_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } - } - } - - fn scl_oe(dcxo: Dcxo, oe: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, - }; - let reg = if oe { reg | SCL_MASK } else { reg & !SCL_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } - } - } - - fn scl_o(dcxo: Dcxo, o: bool) { - let reg = match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, - }; - let reg = if o { reg | SCL_MASK } else { reg & !SCL_MASK }; - match dcxo { - Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, - Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } - } - } - - pub fn init(dcxo: Dcxo) -> Result<(), &'static str> { - // Set SCL as output, and high level - scl_o(dcxo, true); - scl_oe(dcxo, true); - // Prepare a zero level on SDA so that sda_oe pulls it down - sda_o(dcxo, false); - // Release SDA - sda_oe(dcxo, false); - - // Check the I2C bus is ready - half_period(); - half_period(); - if !sda_i(dcxo) { - // Try toggling SCL a few times - for _bit in 0..8 { - scl_o(dcxo, false); - half_period(); - scl_o(dcxo, true); - half_period(); - } - } - - if !sda_i(dcxo) { - return Err("SDA is stuck low and doesn't get unstuck"); - } - Ok(()) - } - - pub fn start(dcxo: Dcxo) { - // Set SCL high then SDA low - scl_o(dcxo, true); - half_period(); - sda_oe(dcxo, true); - half_period(); - } - - pub fn stop(dcxo: Dcxo) { - // First, make sure SCL is low, so that the target releases the SDA line - scl_o(dcxo, false); - half_period(); - // Set SCL high then SDA high - sda_oe(dcxo, true); - scl_o(dcxo, true); - half_period(); - sda_oe(dcxo, false); - half_period(); - } - - pub fn write(dcxo: Dcxo, data: u8) -> bool { - // MSB first - for bit in (0..8).rev() { - // Set SCL low and set our bit on SDA - scl_o(dcxo, false); - sda_oe(dcxo, data & (1 << bit) == 0); - half_period(); - // Set SCL high ; data is shifted on the rising edge of SCL - scl_o(dcxo, true); - half_period(); - } - // Check ack - // Set SCL low, then release SDA so that the I2C target can respond - scl_o(dcxo, false); - half_period(); - sda_oe(dcxo, false); - // Set SCL high and check for ack - scl_o(dcxo, true); - half_period(); - // returns true if acked (I2C target pulled SDA low) - !sda_i(dcxo) - } - - pub fn read(dcxo: Dcxo, ack: bool) -> u8 { - // Set SCL low first, otherwise setting SDA as input may cause a transition - // on SDA with SCL high which will be interpreted as START/STOP condition. - scl_o(dcxo, false); - half_period(); // make sure SCL has settled low - sda_oe(dcxo, false); - - let mut data: u8 = 0; - - // MSB first - for bit in (0..8).rev() { - scl_o(dcxo, false); - half_period(); - // Set SCL high and shift data - scl_o(dcxo, true); - half_period(); - if sda_i(dcxo) { data |= 1 << bit } - } - // Send ack - // Set SCL low and pull SDA low when acking - scl_o(dcxo, false); - if ack { sda_oe(dcxo, true) } - half_period(); - // then set SCL high - scl_o(dcxo, true); - half_period(); - - data - } -} - -mod si549 { - use board_misoc::clock; - use super::i2c; - - #[cfg(any(soc_platform = "metlino", soc_platform = "sayma_amc", soc_platform = "sayma_rtm"))] - pub const ADDRESS: u8 = 0x55; - #[cfg(soc_platform = "kasli")] - pub const ADDRESS: u8 = 0x67; - - pub fn write(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - if !i2c::write(dcxo, val) { - return Err("Si549 failed to ack value") - } - i2c::stop(dcxo); - Ok(()) - } - - pub fn write_no_ack_value(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - i2c::write(dcxo, val); - i2c::stop(dcxo); - Ok(()) - } - - pub fn read(dcxo: i2c::Dcxo, reg: u8) -> Result { - i2c::start(dcxo); - if !i2c::write(dcxo, ADDRESS << 1) { - return Err("Si549 failed to ack write address") - } - if !i2c::write(dcxo, reg) { - return Err("Si549 failed to ack register") - } - i2c::stop(dcxo); - - i2c::start(dcxo); - if !i2c::write(dcxo, (ADDRESS << 1) | 1) { - return Err("Si549 failed to ack read address") - } - let val = i2c::read(dcxo, false); - i2c::stop(dcxo); - - Ok(val) - } - - pub fn program(dcxo: i2c::Dcxo, hsdiv: u16, lsdiv: u8, fbdiv: u64) -> Result<(), &'static str> { - i2c::init(dcxo)?; - - write(dcxo, 255, 0x00)?; // PAGE - write_no_ack_value(dcxo, 7, 0x80)?; // RESET - clock::spin_us(100_000); // required? not specified in datasheet. - - write(dcxo, 255, 0x00)?; // PAGE - write(dcxo, 69, 0x00)?; // Disable FCAL override. - // Note: Value 0x00 from Table 5.6 is inconsistent with Table 5.7, - // which shows bit 0 as reserved and =1. - write(dcxo, 17, 0x00)?; // Synchronously disable output - - // The Si549 has no ID register, so we check that it responds correctly - // by writing values to a RAM-like register and reading them back. - for test_value in 0..255 { - write(dcxo, 23, test_value)?; - let readback = read(dcxo, 23)?; - if readback != test_value { - return Err("Si549 detection failed"); - } - } - - write(dcxo, 23, hsdiv as u8)?; - write(dcxo, 24, (hsdiv >> 8) as u8 | (lsdiv << 4))?; - write(dcxo, 26, fbdiv as u8)?; - write(dcxo, 27, (fbdiv >> 8) as u8)?; - write(dcxo, 28, (fbdiv >> 16) as u8)?; - write(dcxo, 29, (fbdiv >> 24) as u8)?; - write(dcxo, 30, (fbdiv >> 32) as u8)?; - write(dcxo, 31, (fbdiv >> 40) as u8)?; - - write(dcxo, 7, 0x08)?; // Start FCAL - write(dcxo, 17, 0x01)?; // Synchronously enable output - - Ok(()) - } - - // Si549 digital frequency trim ("all-digital PLL" register) - // ∆ f_out = adpll * 0.0001164e-6 (0.1164 ppb/lsb) - // max trim range is +- 950 ppm - pub fn set_adpll(dcxo: i2c::Dcxo, adpll: i32) -> Result<(), &'static str> { - write(dcxo, 231, adpll as u8)?; - write(dcxo, 232, (adpll >> 8) as u8)?; - write(dcxo, 233, (adpll >> 16) as u8)?; - clock::spin_us(100); - Ok(()) - } - - pub fn get_adpll(dcxo: i2c::Dcxo) -> Result { - let b1 = read(dcxo, 231)? as i32; - let b2 = read(dcxo, 232)? as i32; - let b3 = read(dcxo, 233)? as i8 as i32; - Ok(b3 << 16 | b2 << 8 | b1) - } -} - -// to do: load from gateware config -const DDMTD_COUNTER_N: u32 = 15; -const DDMTD_COUNTER_M: u32 = (1 << DDMTD_COUNTER_N); -const F_SYS: f64 = csr::CONFIG_CLOCK_FREQUENCY as f64; - -const F_MAIN: f64 = 125.0e6; -const F_HELPER: f64 = F_MAIN * DDMTD_COUNTER_M as f64 / (DDMTD_COUNTER_M + 1) as f64; -const F_BEAT: f64 = F_MAIN - F_HELPER; -const TIME_STEP: f32 = 1./F_BEAT as f32; - -fn ddmtd_tag_to_s(mu: f32) -> f32 { - return (mu as f32)*TIME_STEP; -} - -fn get_frequencies() -> (u32, u32, u32) { - unsafe { - csr::wrpll::frequency_counter_update_en_write(1); - // wait for at least one full update cycle (> 2 timer periods) - clock::spin_us(200_000); - csr::wrpll::frequency_counter_update_en_write(0); - let helper = csr::wrpll::frequency_counter_counter_helper_read(); - let main = csr::wrpll::frequency_counter_counter_rtio_read(); - let cdr = csr::wrpll::frequency_counter_counter_rtio_rx0_read(); - (helper, main, cdr) - } -} - -fn log_frequencies() -> (u32, u32, u32) { - let (f_helper, f_main, f_cdr) = get_frequencies(); - let conv_khz = |f| 4*(f as u64)*(csr::CONFIG_CLOCK_FREQUENCY as u64)/(1000*(1 << 23)); - info!("helper clock frequency: {}kHz ({})", conv_khz(f_helper), f_helper); - info!("main clock frequency: {}kHz ({})", conv_khz(f_main), f_main); - info!("CDR clock frequency: {}kHz ({})", conv_khz(f_cdr), f_cdr); - (f_helper, f_main, f_cdr) -} - -fn get_tags() -> (i32, i32, u16, u16) { - unsafe { - csr::wrpll::tag_arm_write(1); - while csr::wrpll::tag_arm_read() != 0 {} - - let main_diff = csr::wrpll::main_diff_tag_read() as i32; - let helper_diff = csr::wrpll::helper_diff_tag_read() as i32; - let ref_tag = csr::wrpll::ref_tag_read(); - let main_tag = csr::wrpll::main_tag_read(); - (main_diff, helper_diff, ref_tag, main_tag) - } -} - -fn print_tags() { - const NUM_TAGS: usize = 30; - let mut main_diffs = [0; NUM_TAGS]; // input to main loop filter - let mut helper_diffs = [0; NUM_TAGS]; // input to helper loop filter - let mut ref_tags = [0; NUM_TAGS]; - let mut main_tags = [0; NUM_TAGS]; - let mut jitter = [0 as f32; NUM_TAGS]; - - for i in 0..NUM_TAGS { - let (main_diff, helper_diff, ref_tag, main_tag) = get_tags(); - main_diffs[i] = main_diff; - helper_diffs[i] = helper_diff; - ref_tags[i] = ref_tag; - main_tags[i] = main_tag; - } - info!("DDMTD ref tags: {:?}", ref_tags); - info!("DDMTD main tags: {:?}", main_tags); - info!("DDMTD main diffs: {:?}", main_diffs); - info!("DDMTD helper diffs: {:?}", helper_diffs); - - // look at the difference between the main DCXO and reference... - let t0 = main_diffs[0]; - main_diffs.iter_mut().for_each(|x| *x -= t0); - - // crude estimate of the max difference across our sample set (assumes no unwrapping issues...) - let delta = main_diffs[main_diffs.len()-1] as f32 / (main_diffs.len()-1) as f32; - info!("detla: {:?} tags", delta); - let delta_f: f32 = delta/DDMTD_COUNTER_M as f32 * F_BEAT as f32; - info!("MAIN <-> ref frequency difference: {:?} Hz ({:?} ppm)", delta_f, delta_f/F_HELPER as f32 * 1e6); - - jitter.iter_mut().enumerate().for_each(|(i, x)| *x = main_diffs[i] as f32 - delta*(i as f32)); - info!("jitter: {:?} tags", jitter); - - let var = jitter.iter().map(|x| x*x).fold(0 as f32, |acc, x| acc + x as f32) / NUM_TAGS as f32; - info!("variance: {:?} tags^2", var); -} - -pub fn init() { - info!("initializing WR PLL..."); - - unsafe { csr::wrpll::helper_reset_write(1); } - - unsafe { - csr::wrpll::helper_dcxo_i2c_address_write(si549::ADDRESS); - csr::wrpll::main_dcxo_i2c_address_write(si549::ADDRESS); - } - - #[cfg(rtio_frequency = "125.0")] - let (h_hsdiv, h_lsdiv, h_fbdiv) = (0x05c, 0, 0x04b5badb98a); - #[cfg(rtio_frequency = "125.0")] - let (m_hsdiv, m_lsdiv, m_fbdiv) = (0x05c, 0, 0x04b5c447213); - - si549::program(i2c::Dcxo::Main, m_hsdiv, m_lsdiv, m_fbdiv) - .expect("cannot initialize main Si549"); - si549::program(i2c::Dcxo::Helper, h_hsdiv, h_lsdiv, h_fbdiv) - .expect("cannot initialize helper Si549"); - // Si549 Settling Time for Large Frequency Change. - // Datasheet said 10ms but it lied. - clock::spin_us(50_000); - - unsafe { csr::wrpll::helper_reset_write(0); } - clock::spin_us(1); -} - -pub fn diagnostics() { - info!("WRPLL diagnostics..."); - info!("Untrimmed oscillator frequencies:"); - log_frequencies(); - - info!("Increase helper DCXO frequency by +10ppm (1.25kHz):"); - si549::set_adpll(i2c::Dcxo::Helper, 85911).expect("ADPLL write failed"); - // to do: add check on frequency? - log_frequencies(); -} - -fn trim_dcxos(f_helper: u32, f_main: u32, f_cdr: u32) -> Result<(i32, i32), &'static str> { - info!("Trimming oscillator frequencies..."); - const DCXO_STEP: i64 = (1.0e6/0.0001164) as i64; - const ADPLL_MAX: i64 = (950.0/0.0001164) as i64; - - const TIMER_WIDTH: u32 = 23; - const COUNTER_DIV: u32 = 2; - - // how many counts we expect to measure - const SYS_COUNTS: i64 = (1 << (TIMER_WIDTH - COUNTER_DIV)) as i64; - const EXP_MAIN_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_MAIN/F_SYS)) as i64; - const EXP_HELPER_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_HELPER/F_SYS)) as i64; - - // calibrate the SYS clock to the CDR clock and correct the measured counts - // assume frequency errors are small so we can make an additive correction - // positive error means sys clock is too fast - let sys_err: i64 = EXP_MAIN_COUNTS - (f_cdr as i64); - let main_err: i64 = EXP_MAIN_COUNTS - (f_main as i64) - sys_err; - let helper_err: i64 = EXP_HELPER_COUNTS - (f_helper as i64) - sys_err; - - info!("sys count err {}", sys_err); - info!("main counts err {}", main_err); - info!("helper counts err {}", helper_err); - - // calculate required adjustment to the ADPLL register see - // https://www.silabs.com/documents/public/data-sheets/si549-datasheet.pdf - // section 5.6 - let helper_adpll: i64 = helper_err*DCXO_STEP/EXP_HELPER_COUNTS; - let main_adpll: i64 = main_err*DCXO_STEP/EXP_MAIN_COUNTS; - if helper_adpll.abs() > ADPLL_MAX { - return Err("helper DCXO offset too large"); - } - if main_adpll.abs() > ADPLL_MAX { - return Err("main DCXO offset too large"); - } - - info!("ADPLL offsets: helper={} main={}", helper_adpll, main_adpll); - Ok((helper_adpll as i32, main_adpll as i32)) -} - -fn statistics(data: &[u16]) -> (f32, f32) { - let sum = data.iter().fold(0 as u32, |acc, x| acc + *x as u32); - let mean = sum as f32 / data.len() as f32; - - let squared_sum = data.iter().fold(0 as u32, |acc, x| acc + (*x as u32).pow(2)); - let variance = (squared_sum as f32 / data.len() as f32) - mean; - return (mean, variance) -} - -fn select_recovered_clock_int(rc: bool) -> Result<(), &'static str> { - info!("Untrimmed oscillator frequencies:"); - let (f_helper, f_main, f_cdr) = log_frequencies(); - if rc { - let (helper_adpll, main_adpll) = trim_dcxos(f_helper, f_main, f_cdr)?; - // to do: add assertion on max frequency shift here? - si549::set_adpll(i2c::Dcxo::Helper, helper_adpll).expect("ADPLL write failed"); - si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed"); - - log_frequencies(); - clock::spin_us(100_000); // TO DO: remove/reduce! - print_tags(); - - info!("increasing main DCXO by 1ppm (125Hz):"); - si549::set_adpll(i2c::Dcxo::Main, main_adpll + 8591).expect("ADPLL write failed"); - clock::spin_us(100_000); - print_tags(); - - si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed"); - - unsafe { - csr::wrpll::adpll_offset_helper_write(helper_adpll as u32); - csr::wrpll::adpll_offset_main_write(main_adpll as u32); - csr::wrpll::helper_dcxo_gpio_enable_write(0); - csr::wrpll::main_dcxo_gpio_enable_write(0); - csr::wrpll::helper_dcxo_errors_write(0xff); - csr::wrpll::main_dcxo_errors_write(0xff); - csr::wrpll::collector_reset_write(0); - } - clock::spin_us(1_000); // wait for the collector to produce meaningful output - unsafe { - csr::wrpll::filter_reset_write(0); - } - - clock::spin_us(100_000); - - print_tags(); -// let mut tags = [0; 10]; -// for i in 0..tags.len() { -// tags[i] = get_ddmtd_helper_tag(); -// } -// info!("DDMTD helper tags: {:?}", tags); - - unsafe { - csr::wrpll::filter_reset_write(1); - csr::wrpll::collector_reset_write(1); - } - clock::spin_us(50_000); - unsafe { - csr::wrpll::helper_dcxo_gpio_enable_write(1); - csr::wrpll::main_dcxo_gpio_enable_write(1); - } - unsafe { - info!("error {} {}", - csr::wrpll::helper_dcxo_errors_read(), - csr::wrpll::main_dcxo_errors_read()); - } - info!("new ADPLL: {} {}", - si549::get_adpll(i2c::Dcxo::Helper)?, - si549::get_adpll(i2c::Dcxo::Main)?); - } else { - si549::set_adpll(i2c::Dcxo::Helper, 0).expect("ADPLL write failed"); - si549::set_adpll(i2c::Dcxo::Main, 0).expect("ADPLL write failed"); - } - Ok(()) -} - -pub fn select_recovered_clock(rc: bool) { - if rc { - info!("switching to recovered clock"); - } else { - info!("switching to local XO clock"); - } - match select_recovered_clock_int(rc) { - Ok(()) => info!("clock transition completed"), - Err(e) => error!("clock transition failed: {}", e) - } -} diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 0b6d1cccb..0f448a1bd 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -11,8 +11,6 @@ use core::convert::TryFrom; use board_misoc::{csr, irq, ident, clock, uart_logger, i2c}; #[cfg(has_si5324)] use board_artiq::si5324; -#[cfg(has_wrpll)] -use board_artiq::wrpll; use board_artiq::{spi, drtioaux}; use board_artiq::drtio_routing; #[cfg(has_hmc830_7043)] @@ -464,17 +462,6 @@ pub extern fn main() -> i32 { io_expander1 = board_misoc::io_expander::IoExpander::new(1); io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander1.init().expect("I2C I/O expander #1 initialization failed"); - #[cfg(has_wrpll)] - { - io_expander0.set_oe(1, 1 << 7).unwrap(); - io_expander0.set(1, 7, true); - io_expander0.service().unwrap(); - io_expander1.set_oe(0, 1 << 7).unwrap(); - io_expander1.set_oe(1, 1 << 7).unwrap(); - io_expander1.set(0, 7, true); - io_expander1.set(1, 7, true); - io_expander1.service().unwrap(); - } // Actively drive TX_DISABLE to false on SFP0..3 io_expander0.set_oe(0, 1 << 1).unwrap(); @@ -491,8 +478,6 @@ pub extern fn main() -> i32 { #[cfg(has_si5324)] si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); - #[cfg(has_wrpll)] - wrpll::init(); unsafe { csr::drtio_transceiver::stable_clkin_write(1); @@ -502,8 +487,6 @@ pub extern fn main() -> i32 { unsafe { csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); } - #[cfg(has_wrpll)] - wrpll::diagnostics(); init_rtio_crg(); #[cfg(has_hmc830_7043)] @@ -554,8 +537,6 @@ pub extern fn main() -> i32 { si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); } - #[cfg(has_wrpll)] - wrpll::select_recovered_clock(true); drtioaux::reset(0); drtiosat_reset(false); @@ -604,7 +585,7 @@ pub extern fn main() -> i32 { if is_up && !was_up { /* * One side of the JESD204 elastic buffer is clocked by the jitter filter - * (Si5324 or WRPLL), the other by the RTM. + * (Si5324), the other by the RTM. * The elastic buffer can operate only when those two clocks are derived from * the same oscillator. * This is the case when either of those conditions is true: @@ -636,8 +617,6 @@ pub extern fn main() -> i32 { info!("uplink is down, switching to local oscillator clock"); #[cfg(has_si5324)] si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks"); - #[cfg(has_wrpll)] - wrpll::select_recovered_clock(false); } } diff --git a/artiq/gateware/drtio/wrpll/__init__.py b/artiq/gateware/drtio/wrpll/__init__.py deleted file mode 100644 index 25e510f4c..000000000 --- a/artiq/gateware/drtio/wrpll/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from artiq.gateware.drtio.wrpll.core import WRPLL -from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP diff --git a/artiq/gateware/drtio/wrpll/core.py b/artiq/gateware/drtio/wrpll/core.py deleted file mode 100644 index 52bc91ab7..000000000 --- a/artiq/gateware/drtio/wrpll/core.py +++ /dev/null @@ -1,156 +0,0 @@ -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.genlib.cdc import MultiReg, PulseSynchronizer -from misoc.interconnect.csr import * - -from artiq.gateware.drtio.wrpll.si549 import Si549 -from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector -from artiq.gateware.drtio.wrpll import thls, filters - - -class FrequencyCounter(Module, AutoCSR): - def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]): - for domain in domains: - name = "counter_" + domain - counter = CSRStatus(counter_width, name=name) - setattr(self, name, counter) - self.update_en = CSRStorage() - - timer = Signal(timer_width) - timer_tick = Signal() - self.sync += Cat(timer, timer_tick).eq(timer + 1) - - for domain in domains: - sync_domain = getattr(self.sync, domain) - divider = Signal(2) - sync_domain += divider.eq(divider + 1) - - divided = Signal() - divided.attr.add("no_retiming") - sync_domain += divided.eq(divider[-1]) - divided_sys = Signal() - self.specials += MultiReg(divided, divided_sys) - - divided_sys_r = Signal() - divided_tick = Signal() - self.sync += divided_sys_r.eq(divided_sys) - self.comb += divided_tick.eq(divided_sys & ~divided_sys_r) - - counter = Signal(counter_width) - counter_csr = getattr(self, "counter_" + domain) - self.sync += [ - If(timer_tick, - If(self.update_en.storage, counter_csr.status.eq(counter)), - counter.eq(0), - ).Else( - If(divided_tick, counter.eq(counter + 1)) - ) - ] - - -class WRPLL(Module, AutoCSR): - def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15): - self.helper_reset = CSRStorage(reset=1) - self.collector_reset = CSRStorage(reset=1) - self.filter_reset = CSRStorage(reset=1) - self.adpll_offset_helper = CSRStorage(24) - self.adpll_offset_main = CSRStorage(24) - - self.tag_arm = CSR() - self.main_diff_tag = CSRStatus(32) - self.helper_diff_tag = CSRStatus(32) - self.ref_tag = CSRStatus(N) - self.main_tag = CSRStatus(N) - - main_diff_tag_32 = Signal((32, True)) - helper_diff_tag_32 = Signal((32, True)) - self.comb += [ - self.main_diff_tag.status.eq(main_diff_tag_32), - self.helper_diff_tag.status.eq(helper_diff_tag_32) - ] - - self.clock_domains.cd_helper = ClockDomain() - self.clock_domains.cd_collector = ClockDomain() - self.clock_domains.cd_filter = ClockDomain() - self.helper_reset.storage.attr.add("no_retiming") - self.filter_reset.storage.attr.add("no_retiming") - self.specials += Instance("IBUFGDS", - i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n, - o_O=self.cd_helper.clk) - self.comb += [ - self.cd_collector.clk.eq(self.cd_collector.clk), - self.cd_filter.clk.eq(self.cd_helper.clk), - ] - self.specials += [ - AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage), - AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage), - AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage) - ] - - self.submodules.helper_dcxo = Si549(helper_dxco_i2c) - self.submodules.main_dcxo = Si549(main_dcxo_i2c) - - # for diagnostics and PLL initialization - self.submodules.frequency_counter = FrequencyCounter() - - ddmtd_counter = Signal(N) - self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1) - self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk) - self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo) - - collector_cd = ClockDomainsRenamer("collector") - filter_cd = ClockDomainsRenamer("filter") - self.submodules.collector = collector_cd(Collector(N)) - self.submodules.filter_helper = filter_cd( - thls.make(filters.helper, data_width=48)) - self.submodules.filter_main = filter_cd( - thls.make(filters.main, data_width=48)) - - self.comb += [ - self.collector.tag_ref.eq(self.ddmtd_ref.h_tag), - self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update), - self.collector.tag_main.eq(self.ddmtd_main.h_tag), - self.collector.main_stb.eq(self.ddmtd_main.h_tag_update) - ] - - collector_stb_ps = PulseSynchronizer("helper", "sys") - self.submodules += collector_stb_ps - self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb) - collector_stb_sys = Signal() - self.sync += collector_stb_sys.eq(collector_stb_ps.o) - - main_diff_tag_sys = Signal((N+2, True)) - helper_diff_tag_sys = Signal((N+2, True)) - ref_tag_sys = Signal(N) - main_tag_sys = Signal(N) - self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys) - self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys) - self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys) - self.specials += MultiReg(self.collector.tag_main, main_tag_sys) - - self.sync += [ - If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)), - If(collector_stb_sys, - self.tag_arm.w.eq(0), - If(self.tag_arm.w, - main_diff_tag_32.eq(main_diff_tag_sys), - helper_diff_tag_32.eq(helper_diff_tag_sys), - self.ref_tag.status.eq(ref_tag_sys), - self.main_tag.status.eq(main_tag_sys) - ) - ) - ] - - self.comb += [ - self.filter_helper.input.eq(self.collector.out_helper << 22), - self.filter_helper.input_stb.eq(self.collector.out_stb), - self.filter_main.input.eq(self.collector.out_main), - self.filter_main.input_stb.eq(self.collector.out_stb) - ] - - self.sync.helper += [ - self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb), - self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage), - self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb), - self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage) - ] diff --git a/artiq/gateware/drtio/wrpll/ddmtd.py b/artiq/gateware/drtio/wrpll/ddmtd.py deleted file mode 100644 index ddeeac54e..000000000 --- a/artiq/gateware/drtio/wrpll/ddmtd.py +++ /dev/null @@ -1,221 +0,0 @@ -from migen import * -from migen.genlib.cdc import PulseSynchronizer, MultiReg -from migen.genlib.fsm import FSM -from misoc.interconnect.csr import * - - -class DDMTDSamplerExtFF(Module): - def __init__(self, ddmtd_inputs): - self.rec_clk = Signal() - self.main_xo = Signal() - - # # # - - # TODO: s/h timing at FPGA pads - if hasattr(ddmtd_inputs, "rec_clk"): - rec_clk_1 = ddmtd_inputs.rec_clk - else: - rec_clk_1 = Signal() - self.specials += Instance("IBUFDS", - i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n, - o_O=rec_clk_1) - if hasattr(ddmtd_inputs, "main_xo"): - main_xo_1 = ddmtd_inputs.main_xo - else: - main_xo_1 = Signal() - self.specials += Instance("IBUFDS", - i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n, - o_O=main_xo_1) - self.specials += [ - Instance("FD", i_C=ClockSignal("helper"), - i_D=rec_clk_1, o_Q=self.rec_clk, - attr={("IOB", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_1, o_Q=self.main_xo, - attr={("IOB", "TRUE")}), - ] - - -class DDMTDSamplerGTP(Module): - def __init__(self, gtp, main_xo_pads): - self.rec_clk = Signal() - self.main_xo = Signal() - - # # # - - # Getting the main XO signal from IBUFDS_GTE2 is problematic because - # the transceiver PLL craps out if an improper clock signal is applied, - # so we are disabling the buffer until the clock is stable. - main_xo_se = Signal() - rec_clk_1 = Signal() - main_xo_1 = Signal() - self.specials += [ - Instance("IBUFDS", - i_I=main_xo_pads.p, i_IB=main_xo_pads.n, - o_O=main_xo_se), - Instance("FD", i_C=ClockSignal("helper"), - i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1, - attr={("DONT_TOUCH", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=rec_clk_1, o_Q=self.rec_clk, - attr={("DONT_TOUCH", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_se, o_Q=main_xo_1, - attr={("IOB", "TRUE")}), - Instance("FD", i_C=ClockSignal("helper"), - i_D=main_xo_1, o_Q=self.main_xo, - attr={("DONT_TOUCH", "TRUE")}), - ] - - -class DDMTDDeglitcherFirstEdge(Module): - def __init__(self, input_signal, blind_period=128): - self.detect = Signal() - self.tag_correction = 0 - - rising = Signal() - input_signal_r = Signal() - self.sync.helper += [ - input_signal_r.eq(input_signal), - rising.eq(input_signal & ~input_signal_r) - ] - - blind_counter = Signal(max=blind_period) - self.sync.helper += [ - If(blind_counter != 0, blind_counter.eq(blind_counter - 1)), - If(input_signal_r, blind_counter.eq(blind_period - 1)), - self.detect.eq(rising & (blind_counter == 0)) - ] - - -class DDMTD(Module): - def __init__(self, counter, input_signal): - - # in helper clock domain - self.h_tag = Signal(len(counter)) - self.h_tag_update = Signal() - - # # # - - deglitcher = DDMTDDeglitcherFirstEdge(input_signal) - self.submodules += deglitcher - - self.sync.helper += [ - self.h_tag_update.eq(0), - If(deglitcher.detect, - self.h_tag_update.eq(1), - self.h_tag.eq(counter + deglitcher.tag_correction) - ) - ] - - -class Collector(Module): - """Generates loop filter inputs from DDMTD outputs. - - The input to the main DCXO lock loop filter is the difference between the - reference and main tags after unwrapping (see below). - - The input to the helper DCXO lock loop filter is the difference between the - current reference tag and the previous reference tag after unwrapping. - - When the WR PLL is locked, the following ideally (no noise/jitter) obtain: - - f_main = f_ref - - f_helper = f_ref * 2^N/(2^N+1) - - f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat) - - the reference and main DCXO tags are equal to each other at every cycle - (the main DCXO lock drives this difference to 0) - - the reference and main DCXO tags both have the same value at each cycle - (the tag difference for each DDMTD is given by - f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter - to wrap around and come back to its previous value) - - Note that we currently lock the frequency of the helper DCXO to the - reference clock, not it's phase. As a result, while the tag differences are - controlled, their absolute values are arbitrary. We could consider moving - the helper lock to a phase lock at some point in the future... - - Since the DDMTD counter is only N bits, it is possible for tag values to - wrap around. This will happen frequently if the locked tags happens to be - near the edges of the counter, so that jitter can easily cause a phase wrap. - But, it can also easily happen during lock acquisition or other transients. - To avoid glitches in the output, we unwrap the tag differences. Currently - we do this in hardware, but we should consider extending the processor to - allow us to do it inside the filters. Since the processor uses wider - signals, this would significantly extend the overall glitch-free - range of the PLL and may aid lock acquisition. - """ - def __init__(self, N): - self.ref_stb = Signal() - self.main_stb = Signal() - self.tag_ref = Signal(N) - self.tag_main = Signal(N) - - self.out_stb = Signal() - self.out_main = Signal((N+2, True)) - self.out_helper = Signal((N+2, True)) - self.out_tag_ref = Signal(N) - self.out_tag_main = Signal(N) - - tag_ref_r = Signal(N) - tag_main_r = Signal(N) - main_tag_diff = Signal((N+2, True)) - helper_tag_diff = Signal((N+2, True)) - - # # # - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - NextValue(self.out_stb, 0), - If(self.ref_stb & self.main_stb, - NextValue(tag_ref_r, self.tag_ref), - NextValue(tag_main_r, self.tag_main), - NextState("DIFF") - ).Elif(self.ref_stb, - NextValue(tag_ref_r, self.tag_ref), - NextState("WAITMAIN") - ).Elif(self.main_stb, - NextValue(tag_main_r, self.tag_main), - NextState("WAITREF") - ) - ) - fsm.act("WAITREF", - If(self.ref_stb, - NextValue(tag_ref_r, self.tag_ref), - NextState("DIFF") - ) - ) - fsm.act("WAITMAIN", - If(self.main_stb, - NextValue(tag_main_r, self.tag_main), - NextState("DIFF") - ) - ) - fsm.act("DIFF", - NextValue(main_tag_diff, tag_main_r - tag_ref_r), - NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref), - NextState("UNWRAP") - ) - fsm.act("UNWRAP", - If(main_tag_diff - self.out_main > 2**(N-1), - NextValue(main_tag_diff, main_tag_diff - 2**N) - ).Elif(self.out_main - main_tag_diff > 2**(N-1), - NextValue(main_tag_diff, main_tag_diff + 2**N) - ), - - If(helper_tag_diff - self.out_helper > 2**(N-1), - NextValue(helper_tag_diff, helper_tag_diff - 2**N) - ).Elif(self.out_helper - helper_tag_diff > 2**(N-1), - NextValue(helper_tag_diff, helper_tag_diff + 2**N) - ), - NextState("OUTPUT") - ) - fsm.act("OUTPUT", - NextValue(self.out_tag_ref, tag_ref_r), - NextValue(self.out_tag_main, tag_main_r), - NextValue(self.out_main, main_tag_diff), - NextValue(self.out_helper, helper_tag_diff), - NextValue(self.out_stb, 1), - NextState("IDLE") - ) diff --git a/artiq/gateware/drtio/wrpll/filters.py b/artiq/gateware/drtio/wrpll/filters.py deleted file mode 100644 index 470f17bf3..000000000 --- a/artiq/gateware/drtio/wrpll/filters.py +++ /dev/null @@ -1,61 +0,0 @@ -helper_xn1 = 0 -helper_xn2 = 0 -helper_yn0 = 0 -helper_yn1 = 0 -helper_yn2 = 0 -helper_out = 0 - -main_xn1 = 0 -main_xn2 = 0 -main_yn0 = 0 -main_yn1 = 0 -main_yn2 = 0 - - -def helper(tag_diff): - global helper_xn1, helper_xn2, helper_yn0, \ - helper_yn1, helper_yn2, helper_out - - helper_xn0 = 0 - tag_diff # *(2**22) - - helper_yr = 4294967296 - - helper_yn2 = helper_yn1 - helper_yn1 = helper_yn0 - - helper_yn0 = (284885690 * (helper_xn0 - + (217319150 * helper_xn1 >> 44) - - (17591968725108 * helper_xn2 >> 44) - ) >> 44 - ) + (35184372088832*helper_yn1 >> 44) - helper_yn2 - - helper_xn2 = helper_xn1 - helper_xn1 = helper_xn0 - - helper_out = 268435456*helper_yn0 >> 44 - helper_out = min(helper_out, helper_yr) - helper_out = max(helper_out, 0 - helper_yr) - - return helper_out - - -def main(main_xn0): - global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2 - - main_yr = 4294967296 - - main_yn2 = main_yn1 - main_yn1 = main_yn0 - main_yn0 = ( - ((133450380908*(((35184372088832*main_xn0) >> 44) + - ((17592186044417*main_xn1) >> 44))) >> 44) + - ((29455872930889*main_yn1) >> 44) - - ((12673794781453*main_yn2) >> 44)) - - main_xn2 = main_xn1 - main_xn1 = main_xn0 - - main_yn0 = min(main_yn0, main_yr) - main_yn0 = max(main_yn0, 0 - main_yr) - - return main_yn0 diff --git a/artiq/gateware/drtio/wrpll/si549.py b/artiq/gateware/drtio/wrpll/si549.py deleted file mode 100644 index 46ce0c138..000000000 --- a/artiq/gateware/drtio/wrpll/si549.py +++ /dev/null @@ -1,340 +0,0 @@ -from migen import * -from migen.genlib.fsm import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer - -from misoc.interconnect.csr import * - - -class I2CClockGen(Module): - def __init__(self, width): - self.load = Signal(width) - self.clk2x = Signal() - - cnt = Signal.like(self.load) - self.comb += [ - self.clk2x.eq(cnt == 0), - ] - self.sync += [ - If(self.clk2x, - cnt.eq(self.load), - ).Else( - cnt.eq(cnt - 1), - ) - ] - - -class I2CMasterMachine(Module): - def __init__(self, clock_width): - self.scl = Signal(reset=1) - self.sda_o = Signal(reset=1) - self.sda_i = Signal() - - self.submodules.cg = CEInserter()(I2CClockGen(clock_width)) - self.start = Signal() - self.stop = Signal() - self.write = Signal() - self.ack = Signal() - self.data = Signal(8) - self.ready = Signal() - - ### - - bits = Signal(4) - data = Signal(8) - - fsm = CEInserter()(FSM("IDLE")) - self.submodules += fsm - - fsm.act("IDLE", - self.ready.eq(1), - If(self.start, - NextState("START0"), - ).Elif(self.stop, - NextState("STOP0"), - ).Elif(self.write, - NextValue(bits, 8), - NextValue(data, self.data), - NextState("WRITE0") - ) - ) - - fsm.act("START0", - NextValue(self.scl, 1), - NextState("START1") - ) - fsm.act("START1", - NextValue(self.sda_o, 0), - NextState("IDLE") - ) - - fsm.act("STOP0", - NextValue(self.scl, 0), - NextState("STOP1") - ) - fsm.act("STOP1", - NextValue(self.sda_o, 0), - NextState("STOP2") - ) - fsm.act("STOP2", - NextValue(self.scl, 1), - NextState("STOP3") - ) - fsm.act("STOP3", - NextValue(self.sda_o, 1), - NextState("IDLE") - ) - - fsm.act("WRITE0", - NextValue(self.scl, 0), - NextState("WRITE1") - ) - fsm.act("WRITE1", - If(bits == 0, - NextValue(self.sda_o, 1), - NextState("READACK0"), - ).Else( - NextValue(self.sda_o, data[7]), - NextState("WRITE2"), - ) - ) - fsm.act("WRITE2", - NextValue(self.scl, 1), - NextValue(data[1:], data[:-1]), - NextValue(bits, bits - 1), - NextState("WRITE0"), - ) - fsm.act("READACK0", - NextValue(self.scl, 1), - NextState("READACK1"), - ) - fsm.act("READACK1", - NextValue(self.ack, ~self.sda_i), - NextState("IDLE") - ) - - run = Signal() - idle = Signal() - self.comb += [ - run.eq((self.start | self.stop | self.write) & self.ready), - idle.eq(~run & fsm.ongoing("IDLE")), - self.cg.ce.eq(~idle), - fsm.ce.eq(run | self.cg.clk2x), - ] - - -class ADPLLProgrammer(Module): - def __init__(self): - self.i2c_divider = Signal(16) - self.i2c_address = Signal(7) - - self.adpll = Signal(24) - self.stb = Signal() - self.busy = Signal() - self.nack = Signal() - - self.scl = Signal() - self.sda_i = Signal() - self.sda_o = Signal() - - self.scl.attr.add("no_retiming") - self.sda_o.attr.add("no_retiming") - - # # # - - master = I2CMasterMachine(16) - self.submodules += master - - self.comb += [ - master.cg.load.eq(self.i2c_divider), - self.scl.eq(master.scl), - master.sda_i.eq(self.sda_i), - self.sda_o.eq(master.sda_o) - ] - - fsm = FSM() - self.submodules += fsm - - adpll = Signal.like(self.adpll) - - fsm.act("IDLE", - If(self.stb, - NextValue(adpll, self.adpll), - NextState("START") - ) - ) - fsm.act("START", - master.start.eq(1), - If(master.ready, NextState("DEVADDRESS")) - ) - fsm.act("DEVADDRESS", - master.data.eq(self.i2c_address << 1), - master.write.eq(1), - If(master.ready, NextState("REGADRESS")) - ) - fsm.act("REGADRESS", - master.data.eq(231), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA0") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA0", - master.data.eq(adpll[0:8]), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA1") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA1", - master.data.eq(adpll[8:16]), - master.write.eq(1), - If(master.ready, - If(master.ack, - NextState("DATA2") - ).Else( - self.nack.eq(1), - NextState("STOP") - ) - ) - ) - fsm.act("DATA2", - master.data.eq(adpll[16:24]), - master.write.eq(1), - If(master.ready, - If(~master.ack, self.nack.eq(1)), - NextState("STOP") - ) - ) - fsm.act("STOP", - master.stop.eq(1), - If(master.ready, - If(~master.ack, self.nack.eq(1)), - NextState("IDLE") - ) - ) - - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - - -def simulate_programmer(): - from migen.sim.core import run_simulation - - dut = ADPLLProgrammer() - - def generator(): - yield dut.i2c_divider.eq(4) - yield dut.i2c_address.eq(0x55) - yield - yield dut.adpll.eq(0x123456) - yield dut.stb.eq(1) - yield - yield dut.stb.eq(0) - yield - while (yield dut.busy): - yield - for _ in range(20): - yield - - run_simulation(dut, generator(), vcd_name="tb.vcd") - - -class Si549(Module, AutoCSR): - def __init__(self, pads): - self.gpio_enable = CSRStorage(reset=1) - self.gpio_in = CSRStatus(2) - self.gpio_out = CSRStorage(2) - self.gpio_oe = CSRStorage(2) - - self.i2c_divider = CSRStorage(16, reset=75) - self.i2c_address = CSRStorage(7) - self.errors = CSR(2) - - # in helper clock domain - self.adpll = Signal(24) - self.adpll_stb = Signal() - - # # # - - programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer()) - self.submodules += programmer - - self.i2c_divider.storage.attr.add("no_retiming") - self.i2c_address.storage.attr.add("no_retiming") - self.specials += [ - MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"), - MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper") - ] - self.comb += [ - programmer.adpll.eq(self.adpll), - programmer.stb.eq(self.adpll_stb) - ] - - self.gpio_enable.storage.attr.add("no_retiming") - self.gpio_out.storage.attr.add("no_retiming") - self.gpio_oe.storage.attr.add("no_retiming") - - # SCL GPIO and mux - ts_scl = TSTriple(1) - self.specials += ts_scl.get_tristate(pads.scl) - - status = Signal() - self.comb += self.gpio_in.status[0].eq(status) - - self.specials += MultiReg(ts_scl.i, status) - self.comb += [ - If(self.gpio_enable.storage, - ts_scl.o.eq(self.gpio_out.storage[0]), - ts_scl.oe.eq(self.gpio_oe.storage[0]) - ).Else( - ts_scl.o.eq(0), - ts_scl.oe.eq(~programmer.scl) - ) - ] - - # SDA GPIO and mux - ts_sda = TSTriple(1) - self.specials += ts_sda.get_tristate(pads.sda) - - status = Signal() - self.comb += self.gpio_in.status[1].eq(status) - - self.specials += MultiReg(ts_sda.i, status) - self.comb += [ - If(self.gpio_enable.storage, - ts_sda.o.eq(self.gpio_out.storage[1]), - ts_sda.oe.eq(self.gpio_oe.storage[1]) - ).Else( - ts_sda.o.eq(0), - ts_sda.oe.eq(~programmer.sda_o) - ) - ] - self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper") - - # Error reporting - collision_cdc = BlindTransfer("helper", "sys") - self.submodules += collision_cdc - self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy) - - nack_cdc = PulseSynchronizer("helper", "sys") - self.submodules += nack_cdc - self.comb += nack_cdc.i.eq(programmer.nack) - - for n, trig in enumerate([collision_cdc.o, nack_cdc.o]): - self.sync += [ - If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)), - If(trig, self.errors.w[n].eq(1)) - ] - - -if __name__ == "__main__": - simulate_programmer() diff --git a/artiq/gateware/drtio/wrpll/thls.py b/artiq/gateware/drtio/wrpll/thls.py deleted file mode 100644 index b11459692..000000000 --- a/artiq/gateware/drtio/wrpll/thls.py +++ /dev/null @@ -1,618 +0,0 @@ -import inspect -import ast -from copy import copy -import operator -from functools import reduce -from collections import OrderedDict - -from migen import * -from migen.genlib.fsm import * - - -class Isn: - def __init__(self, immediate=None, inputs=None, outputs=None): - if inputs is None: - inputs = [] - if outputs is None: - outputs = [] - self.immediate = immediate - self.inputs = inputs - self.outputs = outputs - - def __repr__(self): - r = "<" - r += self.__class__.__name__ - if self.immediate is not None: - r += " (" + str(self.immediate) + ")" - for inp in self.inputs: - r += " r" + str(inp) - if self.outputs: - r += " ->" - for outp in self.outputs: - r += " r" + str(outp) - r += ">" - return r - - -class NopIsn(Isn): - opcode = 0 - -class AddIsn(Isn): - opcode = 1 - -class SubIsn(Isn): - opcode = 2 - -class MulShiftIsn(Isn): - opcode = 3 - -# opcode = 4: MulShift with alternate shift - -class MinIsn(Isn): - opcode = 5 - -class MaxIsn(Isn): - opcode = 6 - -class CopyIsn(Isn): - opcode = 7 - -class InputIsn(Isn): - opcode = 8 - -class OutputIsn(Isn): - opcode = 9 - -class EndIsn(Isn): - opcode = 10 - - -class ASTCompiler: - def __init__(self): - self.program = [] - self.data = [] - self.next_ssa_reg = -1 - self.constants = dict() - self.names = dict() - self.globals = OrderedDict() - - def get_ssa_reg(self): - r = self.next_ssa_reg - self.next_ssa_reg -= 1 - return r - - def add_global(self, name): - if name not in self.globals: - r = len(self.data) - self.data.append(0) - self.names[name] = r - self.globals[name] = r - - def input(self, name): - target = self.get_ssa_reg() - self.program.append(InputIsn(outputs=[target])) - self.names[name] = target - - def emit(self, node): - if isinstance(node, ast.BinOp): - if isinstance(node.op, ast.RShift): - if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult): - raise NotImplementedError - if not isinstance(node.right, ast.Num): - raise NotImplementedError - left = self.emit(node.left.left) - right = self.emit(node.left.right) - cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs) - else: - left = self.emit(node.left) - right = self.emit(node.right) - if isinstance(node.op, ast.Add): - cons = AddIsn - elif isinstance(node.op, ast.Sub): - cons = SubIsn - elif isinstance(node.op, ast.Mult): - cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs) - else: - raise NotImplementedError - output = self.get_ssa_reg() - self.program.append(cons(inputs=[left, right], outputs=[output])) - return output - elif isinstance(node, ast.Call): - if not isinstance(node.func, ast.Name): - raise NotImplementedError - funcname = node.func.id - if node.keywords: - raise NotImplementedError - inputs = [self.emit(x) for x in node.args] - if funcname == "min": - cons = MinIsn - elif funcname == "max": - cons = MaxIsn - else: - raise NotImplementedError - output = self.get_ssa_reg() - self.program.append(cons(inputs=inputs, outputs=[output])) - return output - elif isinstance(node, (ast.Num, ast.UnaryOp)): - if isinstance(node, ast.UnaryOp): - if not isinstance(node.operand, ast.Num): - raise NotImplementedError - if isinstance(node.op, ast.UAdd): - transform = lambda x: x - elif isinstance(node.op, ast.USub): - transform = operator.neg - elif isinstance(node.op, ast.Invert): - transform = operator.invert - else: - raise NotImplementedError - node = node.operand - else: - transform = lambda x: x - n = transform(node.n) - if n in self.constants: - return self.constants[n] - else: - r = len(self.data) - self.data.append(n) - self.constants[n] = r - return r - elif isinstance(node, ast.Name): - return self.names[node.id] - elif isinstance(node, ast.Assign): - output = self.emit(node.value) - for target in node.targets: - assert isinstance(target, ast.Name) - self.names[target.id] = output - elif isinstance(node, ast.Return): - value = self.emit(node.value) - self.program.append(OutputIsn(inputs=[value])) - elif isinstance(node, ast.Global): - pass - else: - raise NotImplementedError - - -class Processor: - def __init__(self, data_width=32, multiplier_stages=2): - self.data_width = data_width - self.multiplier_stages = multiplier_stages - self.multiplier_shifts = [] - self.program_rom_size = None - self.data_ram_size = None - self.opcode_bits = 4 - self.reg_bits = None - - def get_instruction_latency(self, isn): - return { - AddIsn: 2, - SubIsn: 2, - MulShiftIsn: 1 + self.multiplier_stages, - MinIsn: 2, - MaxIsn: 2, - CopyIsn: 1, - InputIsn: 1 - }[isn.__class__] - - def encode_instruction(self, isn, exit): - opcode = isn.opcode - if isn.immediate is not None and not isinstance(isn, MulShiftIsn): - r0 = isn.immediate - if len(isn.inputs) >= 1: - r1 = isn.inputs[0] - else: - r1 = 0 - else: - if len(isn.inputs) >= 1: - r0 = isn.inputs[0] - else: - r0 = 0 - if len(isn.inputs) >= 2: - r1 = isn.inputs[1] - else: - r1 = 0 - r = 0 - for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)): - r <<= bits - r |= value - return r - - def instruction_bits(self): - return 3*self.reg_bits + self.opcode_bits - - def implement(self, program, data): - return ProcessorImpl(self, program, data) - - -class Scheduler: - def __init__(self, processor, reserved_data, program): - self.processor = processor - self.reserved_data = reserved_data - self.used_registers = set(range(self.reserved_data)) - self.exits = dict() - self.program = program - self.remaining = copy(program) - self.output = [] - - def allocate_register(self): - r = min(set(range(max(self.used_registers) + 2)) - self.used_registers) - self.used_registers.add(r) - return r - - def free_register(self, r): - assert r >= self.reserved_data - self.used_registers.discard(r) - - def find_inputs(self, cycle, isn): - mapped_inputs = [] - for inp in isn.inputs: - if inp >= 0: - mapped_inputs.append(inp) - else: - found = False - for i in range(cycle): - if i in self.exits: - r, rm = self.exits[i] - if r == inp: - mapped_inputs.append(rm) - found = True - break - if not found: - return None - return mapped_inputs - - def schedule_one(self, isn): - cycle = len(self.output) - mapped_inputs = self.find_inputs(cycle, isn) - if mapped_inputs is None: - return False - - if isn.outputs: - # check that exit slot is free - latency = self.processor.get_instruction_latency(isn) - exit = cycle + latency - if exit in self.exits: - return False - - # avoid RAW hazard with global writeback - for output in isn.outputs: - if output >= 0: - for risn in self.remaining: - for inp in risn.inputs: - if inp == output: - return False - - # Instruction can be scheduled - - self.remaining.remove(isn) - - for inp, minp in zip(isn.inputs, mapped_inputs): - can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs) - if can_free: - self.free_register(minp) - - if isn.outputs: - assert len(isn.outputs) == 1 - if isn.outputs[0] < 0: - output = self.allocate_register() - else: - output = isn.outputs[0] - self.exits[exit] = (isn.outputs[0], output) - self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs)) - - return True - - def schedule(self): - while self.remaining: - success = False - for isn in self.remaining: - if self.schedule_one(isn): - success = True - break - if not success: - self.output.append(NopIsn()) - self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1) - return self.output - - -class CompiledProgram: - def __init__(self, processor, program, exits, data, glbs): - self.processor = processor - self.program = program - self.exits = exits - self.data = data - self.globals = glbs - - def pretty_print(self): - for cycle, isn in enumerate(self.program): - l = "{:4d} {:15}".format(cycle, str(isn)) - if cycle in self.exits: - l += " -> r{}".format(self.exits[cycle]) - print(l) - - def dimension_processor(self): - self.processor.program_rom_size = len(self.program) - self.processor.data_ram_size = len(self.data) - self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length() - for isn in self.program: - if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts: - self.processor.multiplier_shifts.append(isn.immediate) - - def encode(self): - r = [] - for i, isn in enumerate(self.program): - exit = self.exits.get(i, 0) - r.append(self.processor.encode_instruction(isn, exit)) - return r - - -def compile(processor, function): - node = ast.parse(inspect.getsource(function)) - assert isinstance(node, ast.Module) - assert len(node.body) == 1 - node = node.body[0] - assert isinstance(node, ast.FunctionDef) - assert len(node.args.args) == 1 - arg = node.args.args[0].arg - body = node.body - - astcompiler = ASTCompiler() - for node in body: - if isinstance(node, ast.Global): - for name in node.names: - astcompiler.add_global(name) - arg_r = astcompiler.input(arg) - for node in body: - astcompiler.emit(node) - if isinstance(node, ast.Return): - break - for glbl, location in astcompiler.globals.items(): - new_location = astcompiler.names[glbl] - if new_location != location: - astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location])) - - scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program) - scheduler.schedule() - - program = copy(scheduler.output) - program.append(EndIsn()) - - max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items())) - - return CompiledProgram( - processor=processor, - program=program, - exits={k: v[1] for k, v in scheduler.exits.items()}, - data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1), - glbs=astcompiler.globals) - - -class BaseUnit(Module): - def __init__(self, data_width): - self.stb_i = Signal() - self.i0 = Signal((data_width, True)) - self.i1 = Signal((data_width, True)) - self.stb_o = Signal() - self.o = Signal((data_width, True)) - - -class NopUnit(BaseUnit): - pass - - -class OpUnit(BaseUnit): - def __init__(self, op, data_width, stages, op_data_width=None): - BaseUnit.__init__(self, data_width) - # work around Migen's mishandling of Verilog's cretinous operator width rules - if op_data_width is None: - op_data_width = data_width - - if stages > 1: - # Vivado backward retiming for DSP does not work correctly if DSP inputs - # are not registered. - i0 = Signal.like(self.i0) - i1 = Signal.like(self.i1) - stb_i = Signal() - self.sync += [ - i0.eq(self.i0), - i1.eq(self.i1), - stb_i.eq(self.stb_i) - ] - output_stages = stages - 1 - else: - i0, i1, stb_i = self.i0, self.i1, self.stb_i - output_stages = stages - - o = Signal((op_data_width, True)) - self.comb += o.eq(op(i0, i1)) - stb_o = stb_i - for i in range(output_stages): - n_o = Signal((data_width, True)) - if stages > 1: - n_o.attr.add(("retiming_backward", 1)) - n_stb_o = Signal() - self.sync += [ - n_o.eq(o), - n_stb_o.eq(stb_o) - ] - o = n_o - stb_o = n_stb_o - self.comb += [ - self.o.eq(o), - self.stb_o.eq(stb_o) - ] - - -class SelectUnit(BaseUnit): - def __init__(self, op, data_width): - BaseUnit.__init__(self, data_width) - - self.sync += [ - self.stb_o.eq(self.stb_i), - If(op(self.i0, self.i1), - self.o.eq(self.i0) - ).Else( - self.o.eq(self.i1) - ) - ] - - -class CopyUnit(BaseUnit): - def __init__(self, data_width): - BaseUnit.__init__(self, data_width) - - self.comb += [ - self.stb_o.eq(self.stb_i), - self.o.eq(self.i0) - ] - - -class InputUnit(BaseUnit): - def __init__(self, data_width, input_stb, input): - BaseUnit.__init__(self, data_width) - self.buffer = Signal(data_width) - - self.comb += [ - self.stb_o.eq(self.stb_i), - self.o.eq(self.buffer) - ] - - -class OutputUnit(BaseUnit): - def __init__(self, data_width, output_stb, output): - BaseUnit.__init__(self, data_width) - - self.sync += [ - output_stb.eq(self.stb_i), - output.eq(self.i0) - ] - - -class ProcessorImpl(Module): - def __init__(self, pd, program, data): - self.input_stb = Signal() - self.input = Signal((pd.data_width, True)) - - self.output_stb = Signal() - self.output = Signal((pd.data_width, True)) - - self.busy = Signal() - - # # # - - program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program) - data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data) - data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data) - self.specials += program_mem, data_mem0, data_mem1 - - pc = Signal(pd.instruction_bits()) - pc_next = Signal.like(pc) - pc_en = Signal() - self.sync += pc.eq(pc_next) - self.comb += [ - If(pc_en, - pc_next.eq(pc + 1) - ).Else( - pc_next.eq(0) - ) - ] - program_mem_port = program_mem.get_port() - self.specials += program_mem_port - self.comb += program_mem_port.adr.eq(pc_next) - - s = 0 - opcode = Signal(pd.opcode_bits) - self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits]) - s += pd.opcode_bits - r0 = Signal(pd.reg_bits) - self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - s += pd.reg_bits - r1 = Signal(pd.reg_bits) - self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - s += pd.reg_bits - exit = Signal(pd.reg_bits) - self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits]) - - data_read_port0 = data_mem0.get_port() - data_read_port1 = data_mem1.get_port() - self.specials += data_read_port0, data_read_port1 - self.comb += [ - data_read_port0.adr.eq(r0), - data_read_port1.adr.eq(r1) - ] - - data_write_port = data_mem0.get_port(write_capable=True) - data_write_port_dup = data_mem1.get_port(write_capable=True) - self.specials += data_write_port, data_write_port_dup - self.comb += [ - data_write_port_dup.we.eq(data_write_port.we), - data_write_port_dup.adr.eq(data_write_port.adr), - data_write_port_dup.dat_w.eq(data_write_port.dat_w), - data_write_port.adr.eq(exit) - ] - - nop = NopUnit(pd.data_width) - adder = OpUnit(operator.add, pd.data_width, 1) - subtractor = OpUnit(operator.sub, pd.data_width, 1) - if pd.multiplier_shifts: - if len(pd.multiplier_shifts) != 1: - raise NotImplementedError - multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0], - pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width) - else: - multiplier = NopUnit(pd.data_width) - minu = SelectUnit(operator.lt, pd.data_width) - maxu = SelectUnit(operator.gt, pd.data_width) - copier = CopyUnit(pd.data_width) - inu = InputUnit(pd.data_width, self.input_stb, self.input) - outu = OutputUnit(pd.data_width, self.output_stb, self.output) - units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu] - self.submodules += units - - for unit in units: - self.sync += unit.stb_i.eq(0) - self.comb += [ - unit.i0.eq(data_read_port0.dat_r), - unit.i1.eq(data_read_port1.dat_r), - If(unit.stb_o, - data_write_port.we.eq(1), - data_write_port.dat_w.eq(unit.o) - ) - ] - - decode_table = [ - (NopIsn.opcode, nop), - (AddIsn.opcode, adder), - (SubIsn.opcode, subtractor), - (MulShiftIsn.opcode, multiplier), - (MulShiftIsn.opcode + 1, multiplier), - (MinIsn.opcode, minu), - (MaxIsn.opcode, maxu), - (CopyIsn.opcode, copier), - (InputIsn.opcode, inu), - (OutputIsn.opcode, outu) - ] - for allocated_opcode, unit in decode_table: - self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1)) - - fsm = FSM() - self.submodules += fsm - fsm.act("IDLE", - pc_en.eq(0), - NextValue(inu.buffer, self.input), - If(self.input_stb, NextState("PROCESSING")) - ) - fsm.act("PROCESSING", - self.busy.eq(1), - pc_en.eq(1), - If(opcode == EndIsn.opcode, - pc_en.eq(0), - NextState("IDLE") - ) - ) - - -def make(function, **kwargs): - proc = Processor(**kwargs) - cp = compile(proc, function) - cp.dimension_processor() - return proc.implement(cp.encode(), cp.data) diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 91c6fb9a0..3df8211a4 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -20,7 +20,6 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter from artiq.gateware import eem from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.siphaser import SiPhaser7Series -from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import * @@ -472,7 +471,7 @@ class SatelliteBase(BaseSoC): } mem_map.update(BaseSoC.mem_map) - def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, **kwargs): + def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, gateware_identifier_str=None, **kwargs): BaseSoC.__init__(self, cpu_type="or1k", sdram_controller_type="minicon", @@ -583,35 +582,18 @@ class SatelliteBase(BaseSoC): rtio_clk_period = 1e9/rtio_clk_freq self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - if with_wrpll: - self.submodules.wrpll_sampler = DDMTDSamplerGTP( - self.drtio_transceiver, - platform.request("cdr_clk_clean_fabric")) - helper_clk_pads = platform.request("ddmtd_helper_clk") - self.submodules.wrpll = WRPLL( - helper_clk_pads=helper_clk_pads, - main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), - helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"), - ddmtd_inputs=self.wrpll_sampler) - self.csr_devices.append("wrpll") - # note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with: - # critical warning: create_clock attempting to set clock on an unknown port/pin - # command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name - # helper_clk [get_xlnx_outside_genome_inst_pin 20 0] - platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99) - platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p) - else: - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0" - else platform.request("si5324_clkin"), - 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( - self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) - self.csr_devices.append("siphaser") - self.config["HAS_SI5324"] = None - self.config["SI5324_SOFT_RESET"] = None + + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0" + else platform.request("si5324_clkin"), + 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( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.config["HAS_SI5324"] = None + self.config["SI5324_SOFT_RESET"] = None gtp = self.drtio_transceiver.gtps[0] platform.add_period_constraint(gtp.txoutclk, rtio_clk_period) @@ -619,9 +601,6 @@ class SatelliteBase(BaseSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, gtp.txoutclk, gtp.rxoutclk) - if with_wrpll: - platform.add_false_path_constraints( - helper_clk_pads.p, gtp.rxoutclk) for gtp in self.drtio_transceiver.gtps[1:]: platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_false_path_constraints( @@ -700,14 +679,11 @@ def main(): parser.add_argument("-V", "--variant", default="tester", help="variant: {} (default: %(default)s)".format( "/".join(sorted(VARIANTS.keys())))) - parser.add_argument("--with-wrpll", default=False, action="store_true") parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") args = parser.parse_args() argdict = dict() - if args.with_wrpll: - argdict["with_wrpll"] = True argdict["gateware_identifier_str"] = args.gateware_identifier_str variant = args.variant.lower() diff --git a/artiq/gateware/targets/sayma_amc.py b/artiq/gateware/targets/sayma_amc.py index f46942e67..d49c593f9 100755 --- a/artiq/gateware/targets/sayma_amc.py +++ b/artiq/gateware/targets/sayma_amc.py @@ -21,7 +21,6 @@ from artiq.gateware import fmcdio_vhdci_eem from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg from artiq.gateware.drtio.transceiver import gth_ultrascale from artiq.gateware.drtio.siphaser import SiPhaser7Series -from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerExtFF from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import * @@ -54,7 +53,7 @@ class SatelliteBase(MiniSoC): } mem_map.update(MiniSoC.mem_map) - def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs): + def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, **kwargs): MiniSoC.__init__(self, cpu_type="or1k", sdram_controller_type="minicon", @@ -68,10 +67,6 @@ class SatelliteBase(MiniSoC): platform = self.platform - if with_wrpll: - clock_recout_pads = platform.request("ddmtd_rec_clk") - else: - clock_recout_pads = None if with_sfp: # Use SFP0 to connect to master (Kasli) self.comb += platform.request("sfp_tx_disable", 0).eq(0) @@ -83,7 +78,7 @@ class SatelliteBase(MiniSoC): data_pads=[drtio_uplink, platform.request("rtm_amc_link")], sys_clk_freq=self.clk_freq, rtio_clk_freq=rtio_clk_freq, - clock_recout_pads=clock_recout_pads) + clock_recout_pads=None) self.csr_devices.append("drtio_transceiver") self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3) @@ -133,39 +128,22 @@ class SatelliteBase(MiniSoC): rtio_clk_period = 1e9/rtio_clk_freq self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - if with_wrpll: - self.comb += [ - platform.request("filtered_clk_sel").eq(0), - platform.request("ddmtd_main_dcxo_oe").eq(1), - platform.request("ddmtd_helper_dcxo_oe").eq(1) - ] - self.submodules.wrpll_sampler = DDMTDSamplerExtFF( - platform.request("ddmtd_inputs")) - self.submodules.wrpll = WRPLL( - helper_clk_pads=platform.request("ddmtd_helper_clk"), - main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), - helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"), - ddmtd_inputs=self.wrpll_sampler) - self.csr_devices.append("wrpll") - platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99) - platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk) - else: - self.comb += platform.request("filtered_clk_sel").eq(1) - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("si5324_clkin"), - rx_synchronizer=self.rx_synchronizer, - ultrascale=True, - rtio_clk_freq=rtio_clk_freq) - platform.add_false_path_constraints( - self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) - self.csr_devices.append("siphaser") - self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) - self.csr_devices.append("si5324_rst_n") - i2c = self.platform.request("i2c") - self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) - self.csr_devices.append("i2c") - self.config["I2C_BUS_COUNT"] = 1 - self.config["HAS_SI5324"] = None + self.comb += platform.request("filtered_clk_sel").eq(1) + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("si5324_clkin"), + rx_synchronizer=self.rx_synchronizer, + ultrascale=True, + rtio_clk_freq=rtio_clk_freq) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) + self.csr_devices.append("si5324_rst_n") + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_SI5324"] = None gth = self.drtio_transceiver.gths[0] platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2) @@ -432,7 +410,6 @@ def main(): default="sawg", help="Change type of signal generator. This is used exclusively for " "development and debugging.") - parser.add_argument("--with-wrpll", default=False, action="store_true") parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") args = parser.parse_args() @@ -442,13 +419,11 @@ def main(): soc = Satellite( with_sfp=args.sfp, jdcg_type=args.jdcg_type, - with_wrpll=args.with_wrpll, gateware_identifier_str=args.gateware_identifier_str, **soc_sayma_amc_argdict(args)) elif variant == "simplesatellite": soc = SimpleSatellite( with_sfp=args.sfp, - with_wrpll=args.with_wrpll, gateware_identifier_str=args.gateware_identifier_str, **soc_sayma_amc_argdict(args)) else: diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 294a17823..9e8571636 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -20,7 +20,6 @@ from artiq.gateware import jesd204_tools from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.siphaser import SiPhaser7Series -from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import * from artiq.build_soc import add_identifier @@ -75,7 +74,7 @@ class _SatelliteBase(BaseSoC): } mem_map.update(BaseSoC.mem_map) - def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs): + def __init__(self, rtio_clk_freq, *, gateware_identifier_str, **kwargs): BaseSoC.__init__(self, cpu_type="or1k", **kwargs) @@ -136,41 +135,23 @@ class _SatelliteBase(BaseSoC): gtp = self.drtio_transceiver.gtps[0] rtio_clk_period = 1e9/rtio_clk_freq self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - if with_wrpll: - self.comb += [ - platform.request("filtered_clk_sel").eq(0), - platform.request("ddmtd_main_dcxo_oe").eq(1), - platform.request("ddmtd_helper_dcxo_oe").eq(1) - ] - self.submodules.wrpll_sampler = DDMTDSamplerGTP( - self.drtio_transceiver, - platform.request("cdr_clk_clean_fabric")) - self.submodules.wrpll = WRPLL( - helper_clk_pads=platform.request("ddmtd_helper_clk"), - main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), - helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"), - ddmtd_inputs=self.wrpll_sampler) - self.csr_devices.append("wrpll") - platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99) - platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk) - platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk) - else: - self.comb += platform.request("filtered_clk_sel").eq(1) - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("si5324_clkin"), - rx_synchronizer=self.rx_synchronizer, - ref_clk=self.crg.cd_sys.clk, ref_div2=True, - rtio_clk_freq=rtio_clk_freq) - platform.add_false_path_constraints( - self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) - self.csr_devices.append("siphaser") - self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) - self.csr_devices.append("si5324_rst_n") - i2c = self.platform.request("i2c") - self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) - self.csr_devices.append("i2c") - self.config["I2C_BUS_COUNT"] = 1 - self.config["HAS_SI5324"] = None + + self.comb += platform.request("filtered_clk_sel").eq(1) + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("si5324_clkin"), + rx_synchronizer=self.rx_synchronizer, + ref_clk=self.crg.cd_sys.clk, ref_div2=True, + rtio_clk_freq=rtio_clk_freq) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) + self.csr_devices.append("si5324_rst_n") + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_SI5324"] = None platform.add_period_constraint(gtp.txoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) @@ -298,7 +279,6 @@ def main(): soc_sayma_rtm_args(parser) parser.add_argument("--rtio-clk-freq", default=150, type=int, help="RTIO clock frequency in MHz") - parser.add_argument("--with-wrpll", default=False, action="store_true") parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm")) @@ -306,7 +286,6 @@ def main(): soc = Satellite( rtio_clk_freq=1e6*args.rtio_clk_freq, - with_wrpll=args.with_wrpll, gateware_identifier_str=args.gateware_identifier_str, **soc_sayma_rtm_argdict(args)) builder = SatmanSoCBuilder(soc, **builder_argdict(args)) diff --git a/artiq/gateware/test/wrpll/__init__.py b/artiq/gateware/test/wrpll/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/artiq/gateware/test/wrpll/test_dsp.py b/artiq/gateware/test/wrpll/test_dsp.py deleted file mode 100644 index 033e69853..000000000 --- a/artiq/gateware/test/wrpll/test_dsp.py +++ /dev/null @@ -1,158 +0,0 @@ -import unittest - -import numpy as np - -from migen import * - -from artiq.gateware.drtio.wrpll.ddmtd import Collector -from artiq.gateware.drtio.wrpll import thls, filters - - -class HelperChainTB(Module): - def __init__(self, N): - self.tag_ref = Signal(N) - self.input_stb = Signal() - self.adpll = Signal((24, True)) - self.out_stb = Signal() - - ### - - self.submodules.collector = Collector(N) - self.submodules.loop_filter = thls.make(filters.helper, data_width=48) - - self.comb += [ - self.collector.tag_ref.eq(self.tag_ref), - self.collector.ref_stb.eq(self.input_stb), - self.collector.main_stb.eq(self.input_stb), - self.loop_filter.input.eq(self.collector.out_helper << 22), - self.loop_filter.input_stb.eq(self.collector.out_stb), - self.adpll.eq(self.loop_filter.output), - self.out_stb.eq(self.loop_filter.output_stb), - ] - - -class TestDSP(unittest.TestCase): - def test_main_collector(self): - N = 2 - collector = Collector(N=N) - # check collector phase unwrapping - tags = [(0, 0, 0), - (0, 1, 1), - (2, 1, -1), - (3, 1, -2), - (0, 1, -3), - (1, 1, -4), - (2, 1, -5), - (3, 1, -6), - (3, 3, -4), - (0, 0, -4), - (0, 1, -3), - (0, 2, -2), - (0, 3, -1), - (0, 0, 0)] - for i in range(10): - tags.append((i % (2**N), (i+1) % (2**N), 1)) - - def generator(): - for tag_ref, tag_main, out in tags: - yield collector.tag_ref.eq(tag_ref) - yield collector.tag_main.eq(tag_main) - yield collector.main_stb.eq(1) - yield collector.ref_stb.eq(1) - - yield - - yield collector.main_stb.eq(0) - yield collector.ref_stb.eq(0) - - while not (yield collector.out_stb): - yield - - out_main = yield collector.out_main - self.assertEqual(out_main, out) - - run_simulation(collector, generator()) - - def test_helper_collector(self): - N = 3 - collector = Collector(N=N) - # check collector phase unwrapping - tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)] - tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)] - tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)] - - def generator(): - for tag_ref, out in tags: - yield collector.tag_ref.eq(tag_ref) - yield collector.main_stb.eq(1) - yield collector.ref_stb.eq(1) - - yield - - yield collector.main_stb.eq(0) - yield collector.ref_stb.eq(0) - - while not (yield collector.out_stb): - yield - - out_helper = yield collector.out_helper - self.assertEqual(out_helper, out) - - run_simulation(collector, generator()) - - # test helper collector + filter against output from MATLAB model - def test_helper_chain(self): - pll = HelperChainTB(15) - - initial_helper_out = -8000 - ref_tags = np.array([ - 24778, 16789, 8801, 814, 25596, 17612, 9628, 1646, - 26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160, - 20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005, - 14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944, - 7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981, - 2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113, - 28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106, - 23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429, - 17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848, - 11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361, - 6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971, - 1096 - ]) - adpll_sim = np.array([ - 8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173, - 190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355, - 372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537, - 553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718, - 734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899, - 915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080, - 1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260, - 1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440, - 1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620, - 1636]) - - def sim(): - yield pll.collector.out_helper.eq(initial_helper_out) - for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim): - # feed collector - yield pll.tag_ref.eq(int(ref_tag)) - yield pll.input_stb.eq(1) - - yield - - yield pll.input_stb.eq(0) - - while not (yield pll.collector.out_stb): - yield - - tag_diff = yield pll.collector.out_helper - - while not (yield pll.loop_filter.output_stb): - yield - - adpll_migen = yield pll.adpll - self.assertEqual(adpll_migen, adpll_matlab) - - yield - - run_simulation(pll, [sim()]) diff --git a/artiq/gateware/test/wrpll/test_thls.py b/artiq/gateware/test/wrpll/test_thls.py deleted file mode 100644 index c1013de30..000000000 --- a/artiq/gateware/test/wrpll/test_thls.py +++ /dev/null @@ -1,55 +0,0 @@ -import unittest - -from migen import * - -from artiq.gateware.drtio.wrpll import thls - - -a = 0 - -def simple_test(x): - global a - a = a + (x*4 >> 1) - return a - - -class TestTHLS(unittest.TestCase): - def test_thls(self): - global a - - proc = thls.Processor() - a = 0 - cp = thls.compile(proc, simple_test) - print("Program:") - cp.pretty_print() - cp.dimension_processor() - print("Encoded program:", cp.encode()) - proc_impl = proc.implement(cp.encode(), cp.data) - - def send_values(values): - for value in values: - yield proc_impl.input.eq(value) - yield proc_impl.input_stb.eq(1) - yield - yield proc_impl.input.eq(0) - yield proc_impl.input_stb.eq(0) - yield - while (yield proc_impl.busy): - yield - @passive - def receive_values(callback): - while True: - while not (yield proc_impl.output_stb): - yield - callback((yield proc_impl.output)) - yield - - send_list = [42, 40, 10, 10] - receive_list = [] - - run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)]) - print("Execution:", send_list, "->", receive_list) - - a = 0 - expected_list = [simple_test(x) for x in send_list] - self.assertEqual(receive_list, expected_list) diff --git a/doc/wrpll_diagram.png b/doc/wrpll_diagram.png deleted file mode 100644 index 1085e0b15..000000000 Binary files a/doc/wrpll_diagram.png and /dev/null differ