From 7a0140ecb291df209fc49170d9374b57eb8adbdd Mon Sep 17 00:00:00 2001 From: hartytp Date: Tue, 12 Jun 2018 12:37:17 +0100 Subject: [PATCH] Sayma HMC830: update interface and register writes. (#1068) * Break the HMC830 init into separate functions for general purpose (but, integer-N) init, setting dividers and checking lock * Use 1.6mA ICP (which the loop filter was optimized for) * Go through the data sheet carefully and set all registers to the correct value (e.g. ensure that all settings are correctly optimized for integer-N usage) * Change divider values (now using 100MHz PFD, which should give lower noise in theory) --- artiq/firmware/libboard_artiq/hmc830_7043.rs | 108 +++++++++++-------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/artiq/firmware/libboard_artiq/hmc830_7043.rs b/artiq/firmware/libboard_artiq/hmc830_7043.rs index dbbf1409b..af4aaa279 100644 --- a/artiq/firmware/libboard_artiq/hmc830_7043.rs +++ b/artiq/firmware/libboard_artiq/hmc830_7043.rs @@ -1,15 +1,3 @@ -/* - * HMC830 config: - * 100MHz input, 1.2GHz output - * fvco = (refclk / r_divider) * n_divider - * fout = fvco/2 - * - * HMC7043 config: - * dac clock: 600MHz (div=2) - * fpga clock: 150MHz (div=8) - * sysref clock: 9.375MHz (div=128) - */ - mod clock_mux { use board_misoc::csr; @@ -30,27 +18,6 @@ mod clock_mux { mod hmc830 { use board_misoc::{csr, clock}; - const HMC830_WRITES: [(u8, u32); 18] = [ - (0x0, 0x20), - (0x1, 0x2), - (0x2, 0x2), // r_divider - (0x5, 0x1628), - (0x5, 0x60a0), - (0x5, 0xe110), - (0x5, 0x2818), - (0x5, 0xf88), - (0x5, 0x7fb0), - (0x5, 0x0), - (0x6, 0x303ca), - (0x7, 0x14d), - (0x8, 0xc1beff), - (0x9, 0x153fff), - (0xa, 0x2046), - (0xb, 0x7c061), - (0xf, 0x81), - (0x3, 0x30), // n_divider - ]; - fn spi_setup() { unsafe { while csr::converter_spi::idle_read() == 0 {} @@ -112,19 +79,65 @@ mod hmc830 { Ok(()) } - pub fn init() -> Result<(), &'static str> { + pub fn init() { + // Configure HMC830 for integer-N operation + // See "PLLs with integrated VCO- RF Applications Product & Operating + // Guide" spi_setup(); - info!("loading configuration..."); - for &(addr, data) in HMC830_WRITES.iter() { - write(addr, data); - } - info!(" ...done"); + info!("loading HMC830 configuration..."); + write(0x0, 0x20); // software reset + write(0x0, 0x00); // normal operation + write(0x6, 0x307ca); // integer-N mode (NB data sheet table 5.8 not self-consistent) + write(0x7, 0x4d); // digital lock detect, 1/2 cycle window (6.5ns window) + write(0x9, 0x2850); // charge pump: 1.6mA, no offset + write(0xa, 0x2045); // for wideband devices like the HMC830 + write(0xb, 0x7c061); // for HMC830 + + // VCO subsystem registers + // NB software reset does not seem to reset these registers, so always + // program them all! + write(0x5, 0xf88); // 1: defaults + write(0x5, 0x6010); // 2: mute output until output divider set + write(0x5, 0x2818); // 3: wideband PLL defaults + write(0x5, 0x60a0); // 4: HMC830 magic value + write(0x5, 0x1628); // 5: HMC830 magic value + write(0x5, 0x7fb0); // 6: HMC830 magic value + write(0x5, 0x0); // ready for VCO auto-cal + + info!(" ...done"); + } + + pub fn set_dividers(r_div: u32, n_div: u32, m_div: u32, out_div: u32) { + // VCO frequency: f_vco = (f_ref/r_div)*(n_int + n_frac/2**24) + // VCO frequency range [1.5GHz, 3GHz] + // Output frequency: f_out = f_vco/out_div + // Max PFD frequency: 125MHz for integer-N, 100MHz for fractional + // (mode B) + // Max reference frequency: 350MHz, however f_ref >= 200MHz requires + // setting 0x08[21]=1 + // + // :param r_div: reference divider [1, 16383] + // :param n_div: VCO divider, integer part. Integer-N mode: [16, 2**19-1] + // fractional mode: [20, 2**19-4] + // :param m_div: VCO divider, fractional part [0, 2**24-1] + // :param out_div: output divider [1, 62] (0 mutes output) + info!("setting HMC830 dividers..."); + write(0x5, 0x6010 + (out_div << 7) + (((out_div <= 2) as u32) << 15)); + write(0x5, 0x0); // ready for VCO auto-cal + write(0x2, r_div); + write(0x4, m_div); + write(0x3, n_div); + + info!(" ...done"); + } + + pub fn check_locked() -> Result<(), &'static str> { + info!("waiting for HMC830 lock..."); let t = clock::get_ms(); - info!("waiting for lock..."); while read(0x12) & 0x02 == 0 { if clock::get_ms() > t + 2000 { - error!(" lock timeout. Register dump:"); + error!("lock timeout. Register dump:"); for addr in 0x00..0x14 { // These registers don't exist (in the data sheet at least) if addr == 0x0d || addr == 0x0e { continue; } @@ -142,10 +155,11 @@ mod hmc830 { pub mod hmc7043 { use board_misoc::csr; - const DAC_CLK_DIV: u32 = 2; - const FPGA_CLK_DIV: u32 = 8; - const SYSREF_DIV: u32 = 128; - const HMC_SYSREF_DIV: u32 = SYSREF_DIV*8; // Must be <= 4MHz + // All frequencies assume 1.2GHz HMC830 output + const DAC_CLK_DIV: u32 = 2; // 600MHz + const FPGA_CLK_DIV: u32 = 8; // 150MHz + const SYSREF_DIV: u32 = 128; // 9.375MHz + const HMC_SYSREF_DIV: u32 = SYSREF_DIV*8; // 1.171875MHz (must be <= 4MHz) // enabled, divider, analog phase shift, digital phase shift const OUTPUT_CONFIG: [(bool, u32, u8, u8); 14] = [ @@ -315,7 +329,9 @@ pub fn init() -> Result<(), &'static str> { /* do not use other SPI devices before HMC830 SPI mode selection */ hmc830::select_spi_mode(); hmc830::detect()?; - hmc830::init()?; + hmc830::init(); + hmc830::set_dividers(1, 24, 0, 2); // 100MHz ref, 1.2GHz out + hmc830::check_locked()?; hmc7043::enable()?; hmc7043::detect()?;