forked from m-labs/artiq
/
hmc830_7043.rs
340 lines (295 loc) · 12.1 KB
/
hmc830_7043.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
mod clock_mux {
use board_misoc::csr;
const CLK_SRC_EXT_SEL : u8 = 1 << 0;
const REF_CLK_SRC_SEL : u8 = 1 << 1;
const DAC_CLK_SRC_SEL : u8 = 1 << 2;
pub fn init() {
unsafe {
csr::clock_mux::out_write(
1*CLK_SRC_EXT_SEL | // use ext clk from sma
1*REF_CLK_SRC_SEL |
1*DAC_CLK_SRC_SEL);
}
}
}
mod hmc830 {
use board_misoc::{csr, clock};
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(32 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS);
}
}
pub fn select_spi_mode() {
spi_setup();
unsafe {
// rising egde on CS since cs_polarity still 0
// selects "HMC Mode"
// do a dummy cycle with cs still high to clear CS
csr::converter_spi::length_write(0);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::length_write(32 - 1);
}
}
fn write(addr: u8, data: u32) {
let val = ((addr as u32) << 24) | data;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 1); // last clk cycle loads data
}
}
fn read(addr: u8) -> u32 {
// SDO (miso/read bits) is technically CPHA=1, while SDI is CPHA=0
// trust that the 8.2ns+0.2ns/pF provide enough hold time on top of
// the SPI round trip delay and stick with CPHA=0
write((1 << 6) | addr, 0);
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_read() & 0xffffff
}
}
pub fn detect() -> Result<(), &'static str> {
spi_setup();
let id = read(0x00);
if id != 0xa7975 {
error!("invalid HMC830 ID: 0x{:08x}", id);
return Err("invalid HMC830 identification");
} else {
info!("HMC830 found");
}
Ok(())
}
pub fn init() {
// Configure HMC830 for integer-N operation
// See "PLLs with integrated VCO- RF Applications Product & Operating
// Guide"
spi_setup();
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();
while read(0x12) & 0x02 == 0 {
if clock::get_ms() > t + 2000 {
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; }
error!(" [0x{:02x}] = 0x{:04x}", addr, read(addr));
}
return Err("lock timeout");
}
}
info!("...locked");
Ok(())
}
}
pub mod hmc7043 {
use board_misoc::csr;
// To do: check which output channels we actually need
// 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] = [
(true, DAC_CLK_DIV, 0x0, 0x0), // 0: DAC2_CLK
(true, SYSREF_DIV, 0x0, 0x0), // 1: DAC2_SYSREF
(true, DAC_CLK_DIV, 0x0, 0x0), // 2: DAC1_CLK
(true, SYSREF_DIV, 0x0, 0x0), // 3: DAC1_SYSREF
(false, 0, 0x0, 0x0), // 4: ADC2_CLK
(false, 0, 0x0, 0x0), // 5: ADC2_SYSREF
(true, FPGA_CLK_DIV, 0x0, 0x0), // 6: GTP_CLK2
(true, SYSREF_DIV, 0x0, 0x0), // 7: FPGA_DAC_SYSREF
(true, FPGA_CLK_DIV, 0x0, 0x0), // 8: GTP_CLK1
(true, FPGA_CLK_DIV, 0x0, 0x0), // 9: AMC_MASTER_AUX_CLK
(true, FPGA_CLK_DIV, 0x0, 0x0), // 10: RTM_MASTER_AUX_CLK
(false, 0, 0x0, 0x0), // 11: FPGA_ADC_SYSREF
(false, 0, 0x0, 0x0), // 12: ADC1_CLK
(false, 0, 0x0, 0x0), // 13: ADC1_SYSREF
];
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0); // change mid-transaction for reads
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS);
}
}
fn write(addr: u16, data: u8) {
let cmd = (0 << 15) | addr;
let val = ((cmd as u32) << 8) | data as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 8);
}
}
fn read(addr: u16) -> u8 {
let cmd = (1 << 15) | addr;
let val = cmd as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(0);
csr::converter_spi::length_write(16 - 1);
csr::converter_spi::data_write(val << 16);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(1);
csr::converter_spi::half_duplex_write(1);
csr::converter_spi::length_write(8 - 1);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::data_read() as u8
}
}
pub fn detect() -> Result<(), &'static str> {
spi_setup();
let id = (read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32;
if id != 0xf17904 {
error!("invalid HMC7043 ID: 0x{:08x}", id);
return Err("invalid HMC7043 identification");
} else {
info!("HMC7043 found");
}
Ok(())
}
pub fn enable() -> Result<(), &'static str> {
info!("enabling hmc7043");
unsafe {
csr::hmc7043_reset::out_write(0);
}
spi_setup();
write(0x0, 0x1); // Software reset
write(0x0, 0x0); // Normal operation
write(0x1, 0x48); // mute all outputs
Ok(())
}
pub fn init() -> Result<(), &'static str> {
spi_setup();
info!("loading configuration...");
write(0xA, 0x06); // Disable the REFSYNCIN input
write(0xB, 0x07); // Enable the CLKIN input as LVPECL
write(0x50, 0x1f); // Disable GPO pin
write(0x9F, 0x4d); // Unexplained high-performance mode
write(0xA0, 0xdf); // Unexplained high-performance mode
// Enable required output groups
write(0x4, (1 << 0) |
(1 << 1) |
(1 << 3) |
(1 << 4) |
(1 << 5));
write(0x5c, (HMC_SYSREF_DIV & 0xff) as u8); // Set SYSREF timer divider
write(0x5d, ((HMC_SYSREF_DIV & 0x0f) >> 8) as u8);
for channel in 0..14 {
let channel_base = 0xc8 + 0x0a*(channel as u16);
let (enabled, divider, aphase, dphase) = OUTPUT_CONFIG[channel];
if enabled {
// Only clock channels need to be high-performance
if (channel % 2) == 0 { write(channel_base, 0xd1); }
else { write(channel_base, 0x51); }
}
else { write(channel_base, 0x10); }
write(channel_base + 0x1, (divider & 0xff) as u8);
write(channel_base + 0x2, ((divider & 0x0f) >> 8) as u8);
write(channel_base + 0x3, aphase & 0x1f);
write(channel_base + 0x4, dphase & 0x1f);
// No analog phase shift on clock channels
if (channel % 2) == 0 { write(channel_base + 0x7, 0x00); }
else { write(channel_base + 0x7, 0x01); }
write(channel_base + 0x8, 0x08)
}
write(0x1, 0x4a); // Reset dividers and FSMs
write(0x1, 0x48);
write(0x1, 0xc8); // Synchronize dividers
write(0x1, 0x40); // Unmute, high-performace/low-noise mode
info!(" ...done");
Ok(())
}
pub fn cfg_dac_sysref(dacno: u8, phase: u16) {
spi_setup();
/* Analog delay resolution: 25ps
* Digital delay resolution: 1/2 input clock cycle = 416ps for 1.2GHz
* 16*25ps = 400ps: limit analog delay to 16 steps instead of 32.
*/
if dacno == 0 {
write(0x00d5, (phase & 0xf) as u8);
write(0x00d6, ((phase >> 4) & 0x1f) as u8);
} else if dacno == 1 {
write(0x00e9, (phase & 0xf) as u8);
write(0x00ea, ((phase >> 4) & 0x1f) as u8);
} else {
unimplemented!();
}
}
}
pub fn init() -> Result<(), &'static str> {
clock_mux::init();
/* do not use other SPI devices before HMC830 SPI mode selection */
hmc830::select_spi_mode();
hmc830::detect()?;
hmc830::init();
hmc830::set_dividers(1, 24, 0, 2); // 100MHz ref, 1.2GHz out
hmc830::check_locked()?;
hmc7043::enable()?;
hmc7043::detect()?;
hmc7043::init()
}