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

Last change on this file was 1901, checked in by chunter, 11 years ago
File size: 25.2 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    return 0;
212}
213
214/**
215\brief Reads the specified register from both AD9963s
216\param baseaddr Base memory address of w3_ad_controller pcore
217\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
218\param regAddr Address of register to read, in [0x00, 0x82]
219\return Returns concatenation of current values of the specified register for both AD9963s; RFA is LSB
220*/
221u32 ad_spi_read(u32 baseaddr,  u32 csMask, u8 regAddr) {
222//NOTE: reads from multiple SPI devices simultaneously
223// return value is 4 bytes
224// least-significant byte is RFA_AD, next is RFB_AD
225    u32 txWord, rxWord;
226
227    //SPI Tx register is 4 bytes:
228    // [3]: chip selects (bitwise OR'd ADCTRL_REG_SPITX_ADx_CS)
229    // [2]: {rnw n1 n0 5'b0}, rnw=1 for SPI write, n1=n0=0 for 1 byte write
230    // [1]: reg addr[7:0]
231    // [0]: ignored for read (read value captured in Rx register)
232    txWord = (csMask & (AD_CTRL_ALL_RF_CS)) | //chip selects
233             (ADCTRL_REG_SPITX_RNW) | //spi_tx_byte[2] = {rnw n1 n0 5'b0}
234             ((regAddr & 0xFF)<<8) | //spi_tx_byte[1] = addr[7:0]
235             (0x00); //spi_tx_byte[0] = ignored for read (AD drives data pin during this byte)
236
237    Xil_Out32(baseaddr + ADCTRL_REG_SPITX, txWord);
238
239    rxWord = Xil_In32(baseaddr + ADCTRL_REG_SPIRX);
240
241    return(rxWord);
242}
243
244/**
245\brief Writes the specified register in selected AD9963s. Multiple AD9963s can be selected for simultaneous writes.
246\param baseaddr Base memory address of w3_ad_controller pcore
247\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
248\param regAddr Address of register to write, in [0x00, 0xFF]
249\param txByte Byte value to write
250*/
251void ad_spi_write(u32 baseaddr, u32 csMask, u8 regAddr, u8 txByte) {
252    u32 txWord;
253
254    //SPI read process:
255    // -Write full SPI word with RNW=1 and address of desired register
256    // -Capture register value in last byte of SPI write process (handled automatically in logic)
257   
258    //SPI Tx register is 4 bytes:
259    // [3]: chip selects (bitwise OR'd ADCTRL_REG_SPITX_ADx_CS)
260    // [2]: {rnw n1 n0 5'b0}, rnw=0 for SPI write, n1=n0=0 for 1 byte write
261    // [1]: reg addr[7:0]
262    // [0]: reg data[7:0]
263    txWord = (csMask & (AD_CTRL_ALL_RF_CS)) |  //byte[3]
264             (0x00) |
265             ((regAddr & 0xFF)<<8) |
266             (txByte & 0xFF); //byte[0]
267
268    Xil_Out32(baseaddr + ADCTRL_REG_SPITX, txWord);
269   
270    return;
271}
272
273
274/**
275\brief Sets the DC offset for the selected path (I or Q) in the selected AD9963s
276\param baseaddr Base memory address of w3_ad_controller pcore
277\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
278\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
279\param dco DC offset to apply, in [0,1024]
280*/
281int ad_set_TxDCO(u32 baseaddr, u32 csMask, u8 iqSel, u16 dco) {
282
283    //Sanity check inputs
284    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0) || (dco>1023))
285        return -1;
286   
287    if(iqSel == AD_CHAN_I) {
288        //AUXIO2=DAC10A=I DAC TxDCO
289        ad_spi_write(baseaddr, csMask, 0x49, ((dco & 0x3FF) >> 2)); //DAC10A data[9:2] - 8 MSB
290        ad_spi_write(baseaddr, csMask, 0x4A, (dco & 0x3)); //DAC10A {6'b0, data[1:0]} - 2 LSB
291    }
292    else if(iqSel == AD_CHAN_Q) {
293        //AUXIO3=DAC10B=Q DAC TxDCO
294        ad_spi_write(baseaddr, csMask, 0x46, ((dco & 0x3FF) >> 2)); //DAC10B data[9:2] - 8 MSB
295        ad_spi_write(baseaddr, csMask, 0x47, (dco & 0x3)); //DAC10B {6'b0, data[1:0]} - 2 LSB
296    }
297
298    return 0;
299}
300
301/**
302\brief Sets the GAIN1 value (linear-in-dB adjustment +/- 6dB) for the selected path (I or Q) in the selected AD9963s.
303Changing this gain value also changes the common mode voltage and DC offset of the selected path. We recommend leaving
304this gain setting unchanged for optimal performance.
305\param baseaddr Base memory address of w3_ad_controller pcore
306\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
307\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
308\param gain 6-bit gain value; [0:25] = [0:+6dB], [41,63] = [-6dB:0dB]
309*/
310int ad_set_TxGain1(u32 baseaddr, u32 csMask, u8 iqSel, u8 gain) {
311    //6-bit Linear-in-dB gain +/- 6dB
312
313    //Sanity check inputs
314    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0) || (gain>63))
315        return -1;
316   
317    if(iqSel == AD_CHAN_I) {
318        ad_spi_write(baseaddr, csMask, 0x68, (gain&0x3F)); //IGAIN1
319    }
320    else if(iqSel == AD_CHAN_Q) {
321        ad_spi_write(baseaddr, csMask, 0x6B, (gain&0x3F)); //QGAIN1
322    }
323
324    return 0;
325}
326
327/**
328\brief Sets the GAIN2 value (linear adjustment +/- 2.5%) for the selected path (I or Q) in the selected AD9963s
329Changing this gain value also changes the common mode voltage and DC offset of the selected path. We recommend leaving
330this gain setting unchanged for optimal performance.
331\param baseaddr Base memory address of w3_ad_controller pcore
332\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
333\param iqSel Select I or Q path; must be AD_CHAN_I or AD_CHAN_Q
334\param gain 6-bit gain value; [0:25] = [0:+2.5%], [41,63] = [-2.5%:0]
335*/
336int ad_set_TxGain2(u32 baseaddr, u32 csMask, u8 iqSel, u8 gain) {
337//6-bit Linear gain +/- 2.5%
338
339    //Sanity check inputs
340    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0) || (gain>63))
341        return -1;
342   
343    if(iqSel == AD_CHAN_I) {
344        ad_spi_write(baseaddr, csMask, 0x69, (gain&0x3F)); //IGAIN2
345    }
346    else if(iqSel == AD_CHAN_Q) {
347        ad_spi_write(baseaddr, csMask, 0x6C, (gain&0x3F)); //QGAIN2
348    }
349
350    return 0;
351}
352
353/**
354\brief Configures the digital rate-change filters in the AD9963. Changing filter settings affects the require data rate
355at the TXD and TRXD ports. You must ensure all related paramters (AD9963 filters, I/Q rate in FPGA, AD9512 dividers) are consistent.
356\param baseaddr Base memory address of w3_ad_controller pcore
357\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
358\param interpRate Desired interpolation rate in AD9963; must be one of [1, 2, 4, 8]
359\param decimationRate Desired decimation rate in AD9963; must be one of [1, 2]
360\return Returns 0 on success, -1 for invalid paramters
361*/
362int ad_config_filters(u32 baseaddr, u32 csMask, u8 interpRate, u8 decimationRate) {
363
364    //Sanity check inputs
365    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0))
366        return -1;
367       
368    /* AD9963 register 0x30:
369        7:6 Reserved
370        5: DEC_BP Bypass Rx decimation filter   0x20
371        4: INT1_BP Bypass Tx INT1 filter        0x10
372        3: INT0_BP Bypass Tx INT0 filter        0x08
373        2: SRRC_BP Bypass Tx SRRC filter        0x04
374        1: TXCLK_EN Enable Tx datapath clocks   0x02
375        0: RXCLK_EN Enable Rx datapath clocks   0x01
376   
377        Tx filter config:
378        1x: All Tx filters bypased
379        2x: INT0 enabled
380        4x: INT0, INT1 enbaled
381        8x: All Tx fitlers enabled
382    */
383   
384    u8 regVal;
385   
386    //Enable Tx/Rx clocks by default
387    regVal = 0x3;
388   
389    switch(interpRate) {
390        case 1:
391            regVal = regVal | 0x1C;
392            break;
393        case 2:
394            regVal = regVal | 0x14;
395            break;
396        case 4:
397            regVal = regVal | 0x04;
398            break;
399        case 8:
400            break;
401        default:
402            //Invalid interp rate; return error
403            return -1;
404            break;
405    }
406
407    if(decimationRate == 1) {
408        regVal = regVal | 0x20;
409    } else if(decimationRate != 2) {
410        //Invalid decimation rate; return error
411        return -1;
412    }
413   
414    //Write reg 0x30 in selected AD9963's
415    ad_spi_write(baseaddr, csMask, 0x30, regVal);
416   
417    return 0;
418}
419
420/**
421\brief Configures the ADC and DAC clock sources in the AD9963. Refer to the WARP v3 user guide and AD9963 for details
422on various clocking modes
423\param baseaddr Base memory address of w3_ad_controller pcore
424\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
425\param DAC_clkSrc DAC clock source; must be AD_DACCLKSRC_DLL (use DLL clock) or AD_DACCLKSRC_EXT (use external reference clock)
426\param ADC_clkSrc ADC clock source; must be AD_ADCCLKSRC_DLL (use DLL clock) or AD_ADCCLKSRC_EXT (use external reference clock)
427\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]
428\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.
429\return Returns 0 on success, -1 for invalid paramters
430*/
431int ad_config_clocks(u32 baseaddr, u32 csMask, u8 DAC_clkSrc, u8 ADC_clkSrc, u8 ADC_clkDiv, u8 ADC_DCS) {
432
433    u8 regVal;
434    u8 bitsToSet_reg66, bitsToSet_reg71;
435
436    //Sanity check inputs
437    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0))
438        return -1;
439
440    /* AD9963 reg 0x66:
441        7:6 Disable DAC clocks
442        4:3 Disable ADC clocks
443        2: Disable DCS
444        1:0 ADCDIV
445   
446      AD9963 reg 0x71:
447        7: ADC clock selection (1=DLL, 0=ext)
448        6: DAC clock selection (1=DLL, 0=ext)
449        4:0 DLL config
450    */
451   
452    //Assert sane default bits, and any config bits user options require
453    bitsToSet_reg66 = (ADC_DCS & AD_DCS_OFF) | (ADC_clkDiv & (AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4));
454    bitsToSet_reg71 = (DAC_clkSrc & AD_DACCLKSRC_DLL) | (ADC_clkSrc & AD_ADCCLKSRC_DLL);
455   
456    //For RFA and RFB, clear-then-set affected bits in clock config registers (0x66 and 0x71)
457    if(csMask & RFA_AD_CS) {
458        regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x66);
459        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
460        regVal = regVal | bitsToSet_reg66;
461        ad_spi_write(baseaddr, RFA_AD_CS, 0x66, regVal);
462
463        regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x71);
464        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
465        regVal = regVal | bitsToSet_reg71;
466        ad_spi_write(baseaddr, RFA_AD_CS, 0x71, regVal);
467    }
468
469    if(csMask & RFB_AD_CS) {
470        regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x66)>>8);
471        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
472        regVal = regVal | bitsToSet_reg66;
473        ad_spi_write(baseaddr, RFB_AD_CS, 0x66, regVal);
474
475        regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x71)>>8);
476        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
477        regVal = regVal | bitsToSet_reg71;
478        ad_spi_write(baseaddr, RFB_AD_CS, 0x71, regVal);
479    }
480
481    if(csMask & RFC_AD_CS) {
482        regVal = (u8)(ad_spi_read(baseaddr, RFC_AD_CS, 0x66)>>8);
483        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
484        regVal = regVal | bitsToSet_reg66;
485        ad_spi_write(baseaddr, RFC_AD_CS, 0x66, regVal);
486
487        regVal = (u8)(ad_spi_read(baseaddr, RFC_AD_CS, 0x71)>>8);
488        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
489        regVal = regVal | bitsToSet_reg71;
490        ad_spi_write(baseaddr, RFC_AD_CS, 0x71, regVal);
491    }
492
493    if(csMask & RFD_AD_CS) {
494        regVal = (u8)(ad_spi_read(baseaddr, RFD_AD_CS, 0x66)>>8);
495        regVal = regVal & ~(AD_DCS_OFF | AD_ADCCLKDIV_1 | AD_ADCCLKDIV_2 | AD_ADCCLKDIV_4);
496        regVal = regVal | bitsToSet_reg66;
497        ad_spi_write(baseaddr, RFD_AD_CS, 0x66, regVal);
498
499        regVal = (u8)(ad_spi_read(baseaddr, RFD_AD_CS, 0x71)>>8);
500        regVal = regVal & ~(AD_DACCLKSRC_DLL | AD_ADCCLKSRC_DLL);
501        regVal = regVal | bitsToSet_reg71;
502        ad_spi_write(baseaddr, RFD_AD_CS, 0x71, regVal);
503    }
504   
505    return 0;
506
507}
508
509/**
510\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.
511\param baseaddr Base memory address of w3_ad_controller pcore
512\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
513\param DLL_En DLL Enable (1=DLL enabled, 0=DLL disabled). Other arguments are ignored when DLL_En=0
514\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
515\param DLL_N DLL division (N) parameter; must be one of [1,2,3,4,5,6,8]
516\param DLL_DIV Secondary DLL divider; must be one of [1,2,4]
517\return Returns 0 on success, -1 for invalid paramters, -2 if DLLs fail to lock with new settings
518*/
519int ad_config_DLL(u32 baseaddr, u32 csMask, u8 DLL_En, u8 DLL_M, u8 DLL_N, u8 DLL_DIV) {
520
521    u8 regVal;
522    u8 bitsToSet_reg71;
523    u8 lockAttempts = 100;
524
525    //Sanity check inputs
526    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0))
527        return -1;
528
529    if( (DLL_N == 7) || (DLL_N == 0) || (DLL_N > 8) || (DLL_M > 31) || (DLL_DIV == 0) || (DLL_DIV == 3) || (DLL_DIV > 4))
530        return -1;
531       
532       
533    /*
534    AD9963 reg 0x60:
535        7: DLL_EN (1=enable DLL)
536        6:0 other block disables
537       
538    AD9963 reg 0x71:
539        7:6 ADC/DAC clock selection
540        5: reserved
541        4: 1=enable DLL clock input
542        3:0 DLL N (only valid values: 1,2,3,4,5,6,8)
543
544    AD9963 reg 0x72:
545        7: DLL locked (read-only)
546        6:5 DLLDIV (secondary divider value)
547        4:0 DLL M ([1:32] all valid)
548
549    AD9963 reg 0x75:
550        7:4 must be 0
551        3: DLL_RESB: DLL reset (must transition low-to-high after any DLL parameter change)* see NOTE below
552        2:0 must be 0
553
554    */
555   
556    //NOTE! The AD9963 datasheet claims DLL_RESB (reg 0x75[3]) is active low. I'm pretty sure
557    // that's wrong (yet another AD9963 datasheet bit-flip). The code below treats DLL_RESB as
558    // active high, the only interpreation we've seen work in hardware.
559   
560
561    if(DLL_En == 0) {
562        //Assert DLL reset
563        ad_spi_write(baseaddr, csMask, 0x75, 0x08);
564
565        //Power down DLL block, leave all other blocks powered on
566        ad_spi_write(baseaddr, csMask, 0x60, 0x00);
567
568        //Disable DLL clock input, set ADC/DAC clock sources to ext ref clock
569        ad_spi_write(baseaddr, csMask, 0x71, 0xC0);
570       
571        return 0;
572
573    } else {
574        //Assert DLL reset
575        ad_spi_write(baseaddr, csMask, 0x75, 0x08);
576
577        //Power up DLL block, leave all other blocks powered on
578        ad_spi_write(baseaddr, csMask, 0x60, 0x80);
579
580        //Assert DLL clock enable, set DLL_N
581        bitsToSet_reg71 = 0x10 | (DLL_N & 0xF);
582       
583        //reg71 has bits to preserve, so handle it separately for each AD
584        if(csMask & RFA_AD_CS) {
585            regVal = (u8)ad_spi_read(baseaddr, RFA_AD_CS, 0x71);
586            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
587            ad_spi_write(baseaddr, RFA_AD_CS, 0x71, regVal);
588        }
589        if(csMask & RFB_AD_CS) {
590            regVal = (u8)(ad_spi_read(baseaddr, RFB_AD_CS, 0x71)>>8);
591            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
592            ad_spi_write(baseaddr, RFB_AD_CS, 0x71, regVal);
593        }
594        if(csMask & RFC_AD_CS) {
595            regVal = (u8)(ad_spi_read(baseaddr, RFC_AD_CS, 0x71)>>8);
596            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
597            ad_spi_write(baseaddr, RFC_AD_CS, 0x71, regVal);
598        }
599        if(csMask & RFD_AD_CS) {
600            regVal = (u8)(ad_spi_read(baseaddr, RFD_AD_CS, 0x71)>>8);
601            regVal = (regVal & ~0x1F) | bitsToSet_reg71;
602            ad_spi_write(baseaddr, RFD_AD_CS, 0x71, regVal);
603        }
604
605        //Other registers are DLL-only, so we can write both ADs together
606       
607        //Set DLL_DIV and DLL_M
608        ad_spi_write(baseaddr, csMask, 0x72, ((DLL_DIV&0x3)<<5) | (DLL_M & 0x1F));
609
610        //Release DLL reset (treating as active high)
611        rc_usleep(100);
612        ad_spi_write(baseaddr, csMask, 0x75, 0x00);
613
614        //Wait for both DLLs to lock
615        while( (lockAttempts > 0) && ( (ad_spi_read(baseaddr, csMask, 0x72) & 0x8080) != 0x8080) ) {lockAttempts--; print(".");}
616       
617        //If the wait-for-lock loop timed out, return an error
618        if(lockAttempts == 0) return -2;
619        else return 0;
620    }
621   
622   
623}
624
625/**
626\brief Shuts down or enables the selected AD9963. Starting up from shutdown is not instantaneous, so this function should only be used to
627disable 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().
628<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
629if your design uses the DLL clock for ADCs or DACs.
630\param baseaddr Base memory address of w3_ad_controller pcore
631\param csMask OR'd combination of RFA_AD_CS and RFB_AD_CS
632\param pwrState Desired AD9963 power state; must be one of [AD_PWR_ALLOFF, AD_PWR_ALLON]
633\return Returns 0 on success, -1 for invalid paramters
634*/
635int ad_config_power(u32 baseaddr, u32 csMask, u8 pwrState) {
636    u8 regVal;
637   
638    //Sanity check inputs
639    if( ((csMask & (AD_CTRL_ALL_RF_CS)) == 0))
640        return -1;
641
642    /* AD9963 reg 0x60:
643        7: 0=power down DLL
644        [6:0] 1=power down [DAC refrerence, IDAC, QDAC, clock input, ADC reference, QADC, IADC]
645    */
646
647    //This funtion intentionally powers off the DLL, whether the user asks for all-on or all-off
648    // This seemed like the safest approach, since the DLL requries an explicit reset whenever its config
649    // changes or when it is powered up. This reset is done in the ad_config_DLL() function in the correct
650    // order to bring the DLL up in a good state.
651
652    if(pwrState == AD_PWR_ALLOFF) regVal = 0x3F; //everything powered down
653    else if(pwrState == AD_PWR_ALLON) regVal = 0x00; //DLL off, everything else on
654    else return -1; //invalid input
655
656    ad_spi_write(baseaddr, csMask, 0x60, regVal);
657       
658    return 0;
659}
660/** @}*/ //END group user_functions
Note: See TracBrowser for help on using the repository browser.