forked from M-Labs/artiq
1
0
Fork 0

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)
This commit is contained in:
hartytp 2018-06-12 12:37:17 +01:00 committed by GitHub
parent a9d97101fc
commit 7a0140ecb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 62 additions and 46 deletions

View File

@ -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 { mod clock_mux {
use board_misoc::csr; use board_misoc::csr;
@ -30,27 +18,6 @@ mod clock_mux {
mod hmc830 { mod hmc830 {
use board_misoc::{csr, clock}; 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() { fn spi_setup() {
unsafe { unsafe {
while csr::converter_spi::idle_read() == 0 {} while csr::converter_spi::idle_read() == 0 {}
@ -112,19 +79,65 @@ mod hmc830 {
Ok(()) 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(); spi_setup();
info!("loading configuration..."); info!("loading HMC830 configuration...");
for &(addr, data) in HMC830_WRITES.iter() {
write(addr, data);
}
info!(" ...done");
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(); let t = clock::get_ms();
info!("waiting for lock...");
while read(0x12) & 0x02 == 0 { while read(0x12) & 0x02 == 0 {
if clock::get_ms() > t + 2000 { if clock::get_ms() > t + 2000 {
error!(" lock timeout. Register dump:"); error!("lock timeout. Register dump:");
for addr in 0x00..0x14 { for addr in 0x00..0x14 {
// These registers don't exist (in the data sheet at least) // These registers don't exist (in the data sheet at least)
if addr == 0x0d || addr == 0x0e { continue; } if addr == 0x0d || addr == 0x0e { continue; }
@ -142,10 +155,11 @@ mod hmc830 {
pub mod hmc7043 { pub mod hmc7043 {
use board_misoc::csr; use board_misoc::csr;
const DAC_CLK_DIV: u32 = 2; // All frequencies assume 1.2GHz HMC830 output
const FPGA_CLK_DIV: u32 = 8; const DAC_CLK_DIV: u32 = 2; // 600MHz
const SYSREF_DIV: u32 = 128; const FPGA_CLK_DIV: u32 = 8; // 150MHz
const HMC_SYSREF_DIV: u32 = SYSREF_DIV*8; // Must be <= 4MHz 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 // enabled, divider, analog phase shift, digital phase shift
const OUTPUT_CONFIG: [(bool, u32, u8, u8); 14] = [ 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 */ /* do not use other SPI devices before HMC830 SPI mode selection */
hmc830::select_spi_mode(); hmc830::select_spi_mode();
hmc830::detect()?; hmc830::detect()?;
hmc830::init()?; hmc830::init();
hmc830::set_dividers(1, 24, 0, 2); // 100MHz ref, 1.2GHz out
hmc830::check_locked()?;
hmc7043::enable()?; hmc7043::enable()?;
hmc7043::detect()?; hmc7043::detect()?;