source: PlatformSupport/CustomPeripherals/pcores/w3_ad_controller_v3_00_b/src/w3_ad_controller.c

Last change on this file was 1792, checked in by murphpo, 12 years ago
File size: 23.7 KB
Line 
1/*****************************************************************
2* File: w3_ad_controller.c
3* Copyright (c) 2012 Mango Communications, all rights reseved
4* Released under the WARP License
5* See http://warp.rice.edu/license for details
6*****************************************************************/
7
8/** \file w3_ad_controller.c
9
10\mainpage
11This is the driver for the w3_ad_controller core, which implements an SPI master for controlling
12the AD9963 analog converters for the WARP v3 RF interfaces.
13
14@version 3.00.b
15@author Patrick Murphy
16@copyright (c) 2012 Mango Communications, Inc. All rights reserved.<br>
17Released under the WARP open source license (see http://warp.rice.edu/license)
18
19\brief Main source for w3_ad_controller driver
20
21*/
22
23#include "w3_ad_controller.h"
24
25/**
26\defgroup user_functions Functions
27\brief Functions to call from user code
28\addtogroup user_functions
29
30Example:
31\code{.c}
32//Assumes user code sets AD_BASEADDR to base address of w3_ad_controller core, as set in xparameters.h
33
34//Initialize the AD9963s
35ad_init(AD_CONTROLLER, 3);
36
37\endcode
38
39@{
40*/
41
42/**
43\brief Initializes the AD controller. This function must be called once at boot before any AD or RF operations will work
44\param baseaddr Base memory address of w3_ad_controller pcore
45\param clkdiv Clock divider for SPI serial clock (set to 3 for 160MHz bus)
46*/
47int ad_init(u32 baseaddr, u8 clkdiv)
48{
49    u32 x, y;
50   
51    //Toggle AD resets (active low), Set SPI clock divider
52    Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0);
53    Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, (clkdiv & ADCTRL_REG_CONFIG_MASK_CLKDIV) | ADCTRL_REG_CONFIG_MASK_RFA_AD_RESET | ADCTRL_REG_CONFIG_MASK_RFB_AD_RESET);
54
55    //Toggle soft reset, set SDIO pin to bidirectional (only way to do SPI reads)
56    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x00, 0xBD); //SDIO=1, LSB_first=0, reset=1
57    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x00, 0x99); //SDIO=1, LSB_first=0, reset=0
58
59    //Confirm the SPI ports are working
60    //AD9963 reg5C should be 0x08 always, reg72 is 0x1 on boot
61    x = (ad_spi_read(baseaddr,  (RFA_AD_CS|RFB_AD_CS), 0x5C)&0xFFFF);
62    y = (ad_spi_read(baseaddr,  (RFA_AD_CS|RFB_AD_CS), 0x72)&0xFFFF);
63    if((x != 0x0808) || (y != 0x0101)) {
64        xil_printf("First AD SPI read was wrong: addr[5C]=0x%04x (should be 0x0808), addr[72]=0x%04x (should be 0x0101)\n", x, y);
65        print("Asserting AD9963 resets\n");
66        Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0);
67
68        return -1;
69    }
70   
71    /* Default AD9963 configuration:
72        -External ref resistor (NOTE: apparent datasheet bug!)
73        -Full-duplex mode (Tx data on TXD port, Rx data on TRXD port)
74        -Power up everything except:
75            -DAC12A, DAC12B, AUXADC (all unconnected on PCB)
76            -DLL
77        -Clocking:
78            -DLL disabled
79            -ADC clock = DAC clock = ext clock (nominally 80MHz)
80        -Tx path:
81            -Data in 2's complement (NOTE: datasheet bug!)
82            -TXCLK is input at TXD sample rate
83            -TXD is DDR, I/Q interleaved, I first
84            -Tx interpolation filters bypassed
85            -Tx gains:
86                -Linear gain set to 100%
87                -Linear-in-dB gain set to -3dB
88                -DAC RSET set to 100%
89            -Tx DCO DACs:
90                -Enabled, configured for [0,+2]v range
91                -Set to mid-scale output (approx equal to common mode voltage of I/Q diff pairs)
92        -Rx path:
93            -Data in 2's complement (NOTE: datasheet bug!)
94            -TRXCLK is output at TRXD sample rate
95            -TRXD is DDR, I/Q interleaved, I first
96            -Decimation filter bypassed
97            -RXCML output enabled (used by ADC driver diff amp)
98            -ADC common mode buffer off (required for DC coupled inputs)
99            -Rx I path negated digitally (to match swap of p/n traces on PCB)
100    */
101       
102    //Power on/off blocks
103    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x40, 0x00); //DAC12A, DAC12B off
104    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x60, 0x00); //DLL off, everything else on
105    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x61, 0x03); //LDOs on, AUXADC off
106    //xil_printf("AD TEST: reg61=0x%08x\n", ad_spi_read(baseaddr,  (RFA_AD_CS|RFB_AD_CS), 0x61));
107   
108    //Clocking setup
109    // [7]=0: ADCCLK=ext clk
110    // [6]=0: DACCLK=ext clk
111    // [4]=0: disable DLL input ref
112    // [3:0]: DLL divide ratio (only 1, 2, 3, 4, 5, 6, 8 valid)
113    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x71, 0x01); //DLL ref input off, ADCCLK=extCLK, DACCLK=extCLK, DLL_DIV=1
114
115    //Reference resistor selection
116    // Datasheet says reg62[0]=0 for internal resistor, reg62[0]=1 for external resistor
117    // But experimentally DAC currents are much more stable with temperature when reg62[0]=0
118    // I'm guessing this bit is flipped in the datasheet
119    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x62, 0x00);
120
121    //Clock disables and DCS
122    // [7:3]=0: Enable internal clocks to ADCs/DACs
123    // [1:0]=0: Set ADCCLK=ext clock (no division)
124    // [2]=0: Enable ADC duty cycle stabilizer (recommended for ADC rates > 75MHz)
125    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x66, 0x00); //Enable internal clocks, enable DCS
126
127    //Aux DACs (Tx DC offset adjustment)
128    // DAC10B=Q DCO, DAC10A=I DCO
129    // DAC outputs update after LSB write (configured by reg40[0])
130    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x45, 0x88); //DAC10B on, full scale = [0,+2]v
131    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x46, 0x80); //DAC10B data[9:2]
132    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x47, 0x00); //DAC10B {6'b0, data[1:0]}
133
134    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x48, 0x88); //DAC10A on, full scale = [0,+2]v
135    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x49, 0x80); //DAC10A data[9:2]
136    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x50, 0x00); //DAC10A {6'b0, data[1:0]}
137
138    //ADC common mode buffer: disabled for DC coupled inputs
139    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x7E, 0x01);
140
141    //Spectral inversion
142    // Invert RxI (reg3D[2]=1) to match PCB
143    // TxI, TxQ also swapped on PCB, but ignored here since -1*(a+jb) is just one of many phase shifts the Tx signal sees
144    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x3D, 0x04); //Invert RxI to match PCB (board swaps +/- for better routing)
145
146    //Rx clock and data order/format
147    // [7:1]=[0 x 1 0 1 0 1] to match design of ad_bridge input registers (TRXD DDR relative to TRXCLK, I data first)
148    // [0]=1 for 2's compliment (Datasheet says reg32[0]=0 for 2's compliment, but experiments show 0 is really straight-binary)
149    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x32, 0x2B);
150
151    //Full-duplex mode (DACs/TXD and ADCs/TRXD both active all the time)
152    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x3F, 0x01); //FD mode
153
154    //Tx data format (datasheet bug! reg[31][0] is flipped; 1=2's complement, 0=straight binary)
155    //0x17 worked great with latest ad_bridge (where TXCLK is ODDR(D1=1,D0=0,C=ad_ref_clk_90) and TXD are ODDR (D1=I,D2=Q,C=ad_ref_clk_0))
156    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x31, 0x17); //Txdata=DDR two's complement,
157
158    //Tx/Rx data paths
159    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x30, 0x3F); //Bypass all rate change filters, enable Tx/Rx clocks
160//  ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x30, 0x37); //INT0 on, enable Tx/Rx clocks
161//  ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x30, 0x27); //INT0+INT1 on, enable Tx/Rx clocks
162//  ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x30, 0x23); //INT0+INT1+SRCC on, enable Tx/Rx clocks
163
164    //ADC RXCML output buffer requires special register process (see AD9963 datasheet pg. 21 "sub serial interface communications")
165    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x05, 0x03); //Address both ADCs
166    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x0F, 0x02); //Enable RXCML output
167    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x10, 0x00); //Set I/Q offset to 0
168    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0xFF, 0x01); //Trigger ADC param update
169    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x05, 0x00); //De-Address both ADCs
170
171    //REFIO adjustment: set to default of 0.8v
172    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x6E, 0x40);
173
174    //Tx gains (it seems these registers default to non-zero, and maybe non-I/Q-matched values; safest to set them explicitly after reset)
175    //I/Q GAIN1[5:0]: Fix5_0 value, Linear-in-dB, 0.25dB per bit
176    //  0-25=>0dB-+6dB, 25-32:+6dB, 33-41:-6dB, 41-63=>-6dB-0dB
177    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x68, 0); //IGAIN1
178    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x6B, 0); //QGAIN1
179
180    //I/Q GAIN2[5:0]: Fix5_0 value, Linear +/-2.5%, 0.08% per bit
181    // 0:+0, 31:+max, 32:-max, 63:-0
182    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x69, 0); //IGAIN2
183    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x6C, 0); //QGAIN2
184
185    //I/Q RSET[5:0]: Fix5_0, Linear +/-20%, 0.625% per bit
186    // 0:-0, 31:-max, 32:+max, 63:+0
187    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x6A, 0); //IRSET
188    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x6D, 0); //QRSET
189
190    //Digital output drive strengths: all 8mA
191    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x63, 0x55); //2 bits each: TRXD TRXIQ TRXCLK TXCLK
192   
193    //Disable Tx and Rx BIST modes
194    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x50, 0x00); //Tx BIST control
195    ad_spi_write(baseaddr, (RFA_AD_CS|RFB_AD_CS), 0x51, 0x00); //Rx BIST control
196   
197    return 0;
198}
199
200/**
201\brief Reads the specified register from both AD9963s
202\param baseaddr Base memory address of w3_ad_controller pcore
203\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
204\param regAddr Address of register to read, in [0x00, 0x82]
205\return Returns concatenation of current values of the specified register for both AD9963s; RFA is LSB
206*/
207u32 ad_spi_read(u32 baseaddr,  u32 csMask, u8 regAddr) {
208//NOTE: reads from multiple SPI devices simultaneously
209// return value is 4 bytes
210// least-significant byte is RFA_AD, next is RFB_AD
211    u32 txWord, rxWord;
212
213    //SPI Tx register is 4 bytes:
214    // [3]: chip selects (bitwise OR'd ADCTRL_REG_SPITX_ADx_CS)
215    // [2]: {rnw n1 n0 5'b0}, rnw=1 for SPI write, n1=n0=0 for 1 byte write
216    // [1]: reg addr[7:0]
217    // [0]: ignored for read (read value captured in Rx register)
218    txWord = (csMask & (RFA_AD_CS | RFB_AD_CS)) | //chip selects
219             (ADCTRL_REG_SPITX_RNW) | //spi_tx_byte[2] = {rnw n1 n0 5'b0}
220             ((regAddr & 0xFF)<<8) | //spi_tx_byte[1] = addr[7:0]
221             (0x00); //spi_tx_byte[0] = ignored for read (AD drives data pin during this byte)
222
223    Xil_Out32(baseaddr + ADCTRL_REG_SPITX, txWord);
224
225    rxWord = Xil_In32(baseaddr + ADCTRL_REG_SPIRX);
226
227    return(rxWord);
228}
229
230/**
231\brief Writes the specified register in selected AD9963s. Multiple AD9963s can be selected for simultaneous writes.
232\param baseaddr Base memory address of w3_ad_controller pcore
233\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
234\param regAddr Address of register to write, in [0x00, 0xFF]
235\param txByte Byte value to write
236*/
237void ad_spi_write(u32 baseaddr, u32 csMask, u8 regAddr, u8 txByte) {
238    u32 txWord;
239
240    //SPI read process:
241    // -Write full SPI word with RNW=1 and address of desired register
242    // -Capture register value in last byte of SPI write process (handled automatically in logic)
243   
244    //SPI Tx register is 4 bytes:
245    // [3]: chip selects (bitwise OR'd ADCTRL_REG_SPITX_ADx_CS)
246    // [2]: {rnw n1 n0 5'b0}, rnw=0 for SPI write, n1=n0=0 for 1 byte write
247    // [1]: reg addr[7:0]
248    // [0]: reg data[7:0]
249    txWord = (csMask & (RFA_AD_CS | RFB_AD_CS)) |  //byte[3]
250             (0x00) |
251             ((regAddr & 0xFF)<<8) |
252             (txByte & 0xFF); //byte[0]
253
254    Xil_Out32(baseaddr + ADCTRL_REG_SPITX, txWord);
255   
256    return;
257}
258
259
260/**
261\brief Sets the DC offset for the selected path (I or Q) in the selected AD9963s
262\param baseaddr Base memory address of w3_ad_controller pcore
263\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
264\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
265\param dco DC offset to apply, in [0,1024]
266*/
267int ad_set_TxDCO(u32 baseaddr, u32 csMask, u8 iqSel, u16 dco) {
268
269    //Sanity check inputs
270    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0) || (dco>1023))
271        return -1;
272   
273    if(iqSel == AD_CHAN_I) {
274        //AUXIO2=DAC10A=I DAC TxDCO
275        ad_spi_write(baseaddr, csMask, 0x49, ((dco & 0x3FF) >> 2)); //DAC10A data[9:2] - 8 MSB
276        ad_spi_write(baseaddr, csMask, 0x4A, (dco & 0x3)); //DAC10A {6'b0, data[1:0]} - 2 LSB
277    }
278    else if(iqSel == AD_CHAN_Q) {
279        //AUXIO3=DAC10B=Q DAC TxDCO
280        ad_spi_write(baseaddr, csMask, 0x46, ((dco & 0x3FF) >> 2)); //DAC10B data[9:2] - 8 MSB
281        ad_spi_write(baseaddr, csMask, 0x47, (dco & 0x3)); //DAC10B {6'b0, data[1:0]} - 2 LSB
282    }
283
284    return 0;
285}
286
287/**
288\brief Sets the GAIN1 value (linear-in-dB adjustment +/- 6dB) for the selected path (I or Q) in the selected AD9963s.
289Changing this gain value also changes the common mode voltage and DC offset of the selected path. We recommend leaving
290this gain setting unchanged for optimal performance.
291\param baseaddr Base memory address of w3_ad_controller pcore
292\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
293\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
294\param gain 6-bit gain value; [0:25] = [0:+6dB], [41,63] = [-6dB:0dB]
295*/
296int ad_set_TxGain1(u32 baseaddr, u32 csMask, u8 iqSel, u8 gain) {
297    //6-bit Linear-in-dB gain +/- 6dB
298
299    //Sanity check inputs
300    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0) || (gain>63))
301        return -1;
302   
303    if(iqSel == AD_CHAN_I) {
304        ad_spi_write(baseaddr, csMask, 0x68, (gain&0x3F)); //IGAIN1
305    }
306    else if(iqSel == AD_CHAN_Q) {
307        ad_spi_write(baseaddr, csMask, 0x6B, (gain&0x3F)); //QGAIN1
308    }
309
310    return 0;
311}
312
313/**
314\brief Sets the GAIN2 value (linear adjustment +/- 2.5%) for the selected path (I or Q) in the selected AD9963s
315Changing this gain value also changes the common mode voltage and DC offset of the selected path. We recommend leaving
316this gain setting unchanged for optimal performance.
317\param baseaddr Base memory address of w3_ad_controller pcore
318\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
319\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
320\param gain 6-bit gain value; [0:25] = [0:+2.5%], [41,63] = [-2.5%:0]
321*/
322int ad_set_TxGain2(u32 baseaddr, u32 csMask, u8 iqSel, u8 gain) {
323//6-bit Linear gain +/- 2.5%
324
325    //Sanity check inputs
326    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0) || (gain>63))
327        return -1;
328   
329    if(iqSel == AD_CHAN_I) {
330        ad_spi_write(baseaddr, csMask, 0x69, (gain&0x3F)); //IGAIN2
331    }
332    else if(iqSel == AD_CHAN_Q) {
333        ad_spi_write(baseaddr, csMask, 0x6C, (gain&0x3F)); //QGAIN2
334    }
335
336    return 0;
337}
338
339/**
340\brief Configures the digital rate-change filters in the AD9963. Changing filter settings affects the require data rate
341at the TXD and TRXD ports. You must ensure all related paramters (AD9963 filters, I/Q rate in FPGA, AD9512 dividers) are consistent.
342\param baseaddr Base memory address of w3_ad_controller pcore
343\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
344\param interpRate Desired interpolation rate in AD9963; must be one of [1, 2, 4, 8]
345\param decimationRate Desired decimation rate in AD9963; must be one of [1, 2]
346\return Returns 0 on success, -1 for invalid paramters
347*/
348int ad_config_filters(u32 baseaddr, u32 csMask, u8 interpRate, u8 decimationRate) {
349
350    //Sanity check inputs
351    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0))
352        return -1;
353       
354    /* AD9963 register 0x30:
355        7:6 Reserved
356        5: DEC_BP Bypass Rx decimation filter   0x20
357        4: INT1_BP Bypass Tx INT1 filter        0x10
358        3: INT0_BP Bypass Tx INT0 filter        0x08
359        2: SRRC_BP Bypass Tx SRRC filter        0x04
360        1: TXCLK_EN Enable Tx datapath clocks   0x02
361        0: RXCLK_EN Enable Rx datapath clocks   0x01
362   
363        Tx filter config:
364        1x: All Tx filters bypased
365        2x: INT0 enabled
366        4x: INT0, INT1 enbaled
367        8x: All Tx fitlers enabled
368    */
369   
370    u8 regVal;
371   
372    //Enable Tx/Rx clocks by default
373    regVal = 0x3;
374   
375    switch(interpRate) {
376        case 1:
377            regVal = regVal | 0x1C;
378            break;
379        case 2:
380            regVal = regVal | 0x14;
381            break;
382        case 4:
383            regVal = regVal | 0x04;
384            break;
385        case 8:
386            break;
387        default:
388            //Invalid interp rate; return error
389            return -1;
390            break;
391    }
392
393    if(decimationRate == 1) {
394        regVal = regVal | 0x20;
395    } else if(decimationRate != 2) {
396        //Invalid decimation rate; return error
397        return -1;
398    }
399   
400    //Write reg 0x30 in selected AD9963's
401    ad_spi_write(baseaddr, csMask, 0x30, regVal);
402   
403    return 0;
404}
405
406/**
407\brief Configures the ADC and DAC clock sources in the AD9963. Refer to the WARP v3 user guide and AD9963 for details
408on various clocking modes
409\param baseaddr Base memory address of w3_ad_controller pcore
410\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
411\param DAC_clkSrc DAC clock source; must be AD_DACCLKSRC_DLL (use DLL clock) or AD_DACCLKSRC_EXT (use external reference clock)
412\param ADC_clkSrc ADC clock source; must be AD_ADCCLKSRC_DLL (use DLL clock) or AD_ADCCLKSRC_EXT (use external reference clock)
413\param ADC_clkDiv ADC clock divider; must be one of [AD_ADCCLKDIV_1, AD_ADCCLKDIV_2, AD_ADCCLKDIV_4] for divide-by of [1, 2, 4]
414\param ADC_DCS ADC duty cycle stabilizer; must be AD_DCS_ON or AD_DCS_OFF. AD9963 datasheet recommends DCS be enabled only for ADC rates above 75MHz.
415\return Returns 0 on success, -1 for invalid paramters
416*/
417int ad_config_clocks(u32 baseaddr, u32 csMask, u8 DAC_clkSrc, u8 ADC_clkSrc, u8 ADC_clkDiv, u8 ADC_DCS) {
418
419    u8 regVal;
420    u8 bitsToSet_reg66, bitsToSet_reg71;
421
422    //Sanity check inputs
423    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0))
424        return -1;
425
426    /* AD9963 reg 0x66:
427        7:6 Disable DAC clocks
428        4:3 Disable ADC clocks
429        2: Disable DCS
430        1:0 ADCDIV
431   
432      AD9963 reg 0x71:
433        7: ADC clock selection (1=DLL, 0=ext)
434        6: DAC clock selection (1=DLL, 0=ext)
435        4:0 DLL config
436    */
437   
438    //Assert sane default bits, and any config bits user options require
439    bitsToSet_reg66 = (ADC_DCS & AD_DCS_OFF) | (ADC_clkDiv & (AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4));
440    bitsToSet_reg71 = (DAC_clkSrc & AD_DACCLKSRC_DLL) | (ADC_clkSrc & AD_ADCCLKSRC_DLL);
441   
442    //For RFA and RFB, clear-then-set affected bits in clock config registers (0x66 and 0x71)
443    if(csMask & RFA_AD_CS) {
444        regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x66);
445        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
446        regVal = regVal | bitsToSet_reg66;
447        ad_spi_write(baseaddr, RFA_AD_CS, 0x66, regVal);
448
449        regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x71);
450        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
451        regVal = regVal | bitsToSet_reg71;
452        ad_spi_write(baseaddr, RFA_AD_CS, 0x71, regVal);
453    }
454
455    if(csMask & RFB_AD_CS) {
456        regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x66)>>8);
457        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
458        regVal = regVal | bitsToSet_reg66;
459        ad_spi_write(baseaddr, RFB_AD_CS, 0x66, regVal);
460
461        regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x71)>>8);
462        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
463        regVal = regVal | bitsToSet_reg71;
464        ad_spi_write(baseaddr, RFB_AD_CS, 0x71, regVal);
465    }
466
467    return 0;
468
469}
470
471/**
472\brief Configures the AD9963 DLL block. DLL output clock is REFCLK*M/(N*DLL_DIV). REFCLK*M must be in [100,310]MHz. See the AD9963 for more details.
473\param baseaddr Base memory address of w3_ad_controller pcore
474\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
475\param DLL_En DLL Enable (1=DLL enabled, 0=DLL disabled). Other arguments are ignored when DLL_En=0
476\param DLL_M DLL multiplication (M) parameter; must be in [0,1,...,31] for multiplications of [1,2,...,32], constrained by M*REFCLK in [100, 310]MHz
477\param DLL_N DLL division (N) parameter; must be one of [1,2,3,4,5,6,8]
478\param DLL_DIV Secondary DLL divider; must be one of [1,2,4]
479\return Returns 0 on success, -1 for invalid paramters, -2 if DLLs fail to lock with new settings
480*/
481int ad_config_DLL(u32 baseaddr, u32 csMask, u8 DLL_En, u8 DLL_M, u8 DLL_N, u8 DLL_DIV) {
482
483    u8 regVal;
484    u8 bitsToSet_reg71;
485    u8 lockAttempts = 100;
486
487    //Sanity check inputs
488    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0))
489        return -1;
490
491    if( (DLL_N == 7) || (DLL_N == 0) || (DLL_N > 8) || (DLL_M > 31) || (DLL_DIV == 0) || (DLL_DIV == 3) || (DLL_DIV > 4))
492        return -1;
493       
494       
495    /*
496    AD9963 reg 0x60:
497        7: DLL_EN (1=enable DLL)
498        6:0 other block disables
499       
500    AD9963 reg 0x71:
501        7:6 ADC/DAC clock selection
502        5: reserved
503        4: 1=enable DLL clock input
504        3:0 DLL N (only valid values: 1,2,3,4,5,6,8)
505
506    AD9963 reg 0x72:
507        7: DLL locked (read-only)
508        6:5 DLLDIV (secondary divider value)
509        4:0 DLL M ([1:32] all valid)
510
511    AD9963 reg 0x75:
512        7:4 must be 0
513        3: DLL_RESB: DLL reset (must transition low-to-high after any DLL parameter change)* see NOTE below
514        2:0 must be 0
515
516    */
517   
518    //NOTE! The AD9963 datasheet claims DLL_RESB (reg 0x75[3]) is active low. I'm pretty sure
519    // that's wrong (yet another AD9963 datasheet bit-flip). The code below treats DLL_RESB as
520    // active high, the only interpreation we've seen work in hardware.
521   
522
523    if(DLL_En == 0) {
524        //Assert DLL reset
525        ad_spi_write(baseaddr, csMask, 0x75, 0x08);
526
527        //Power down DLL block, leave all other blocks powered on
528        ad_spi_write(baseaddr, csMask, 0x60, 0x00);
529
530        //Disable DLL clock input, set ADC/DAC clock sources to ext ref clock
531        ad_spi_write(baseaddr, csMask, 0x71, 0xC0);
532       
533        return 0;
534
535    } else {
536        //Assert DLL reset
537        ad_spi_write(baseaddr, csMask, 0x75, 0x08);
538
539        //Power up DLL block, leave all other blocks powered on
540        ad_spi_write(baseaddr, csMask, 0x60, 0x80);
541
542        //Assert DLL clock enable, set DLL_N
543        bitsToSet_reg71 = 0x10 | (DLL_N & 0xF);
544       
545        //reg71 has bits to preserve, so handle it separately for each AD
546        if(csMask & RFA_AD_CS) {
547            regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x71);
548            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
549            ad_spi_write(baseaddr, RFA_AD_CS, 0x71, regVal);
550        }
551        if(csMask & RFB_AD_CS) {
552            regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x71)>>8);
553            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
554            ad_spi_write(baseaddr, RFB_AD_CS, 0x71, regVal);
555        }
556
557        //Other registers are DLL-only, so we can write both ADs together
558       
559        //Set DLL_DIV and DLL_M
560        ad_spi_write(baseaddr, csMask, 0x72, ((DLL_DIV&0x3)<<5) | (DLL_M & 0x1F));
561
562        //Release DLL reset (treating as active high)
563        rc_usleep(100);
564        ad_spi_write(baseaddr, csMask, 0x75, 0x00);
565
566        //Wait for both DLLs to lock
567        while( (lockAttempts > 0) && ( (ad_spi_read(baseaddr, csMask, 0x72) & 0x8080) != 0x8080) ) {lockAttempts--; print(".");}
568       
569        //If the wait-for-lock loop timed out, return an error
570        if(lockAttempts == 0) return -2;
571        else return 0;
572    }
573   
574   
575}
576
577/**
578\brief Shuts down or enables the selected AD9963. Starting up from shutdown is not instantaneous, so this function should only be used to
579disable an AD9963 that will be unsed for a while. If you shutdown a AD9963, you should also shutdown the corresponding MAX2829 with radio_controller_setMode_shutdown().
580<b>Note:</b> this function will always leave the AD9963 DLL shutdown. You must call ad_config_DLL() again to re-configure and re-enable the DLL
581if your design uses the DLL clock for ADCs or DACs.
582\param baseaddr Base memory address of w3_ad_controller pcore
583\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
584\param pwrState Desired AD9963 power state; must be one of [AD_PWR_ALLOFF, AD_PWR_ALLON]
585\return Returns 0 on success, -1 for invalid paramters
586*/
587int ad_config_power(u32 baseaddr, u32 csMask, u8 pwrState) {
588    u8 regVal;
589   
590    //Sanity check inputs
591    if( ((csMask & (RFA_AD_CS | RFB_AD_CS)) == 0))
592        return -1;
593
594    /* AD9963 reg 0x60:
595        7: 0=power down DLL
596        [6:0] 1=power down [DAC refrerence, IDAC, QDAC, clock input, ADC reference, QADC, IADC]
597    */
598
599    //This funtion intentionally powers off the DLL, whether the user asks for all-on or all-off
600    // This seemed like the safest approach, since the DLL requries an explicit reset whenever its config
601    // changes or when it is powered up. This reset is done in the ad_config_DLL() function in the correct
602    // order to bring the DLL up in a good state.
603
604    if(pwrState == AD_PWR_ALLOFF) regVal = 0x3F; //everything powered down
605    else if(pwrState == AD_PWR_ALLON) regVal = 0x00; //DLL off, everything else on
606    else return -1; //invalid input
607
608    ad_spi_write(baseaddr, csMask, 0x60, regVal);
609       
610    return 0;
611}
612/** @}*/ //END group user_functions
Note: See TracBrowser for help on using the repository browser.