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

Last change on this file was 6210, checked in by murphpo, 6 years ago

Added print in error check

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