int w3_node_init() { int status; u8 CMswitch; XTmrCtr *TmrCtrInstancePtr = &TimerCounter; int ret = XST_SUCCESS; microblaze_enable_exceptions(); //Initialize the AD9512 clock buffers (RF reference and sampling clocks) CMswitch = clk_config_read_clkmod_status(CLK_BASEADDR); status = clk_init(CLK_BASEADDR, 2); if(status != XST_SUCCESS) { xil_printf("w3_node_init: Error in clk_init (%d)\n", status); ret = XST_FAILURE; } //Configure this node's clock inputs and outputs, based on the state of the CM-MMCX switch // If no CM-MMCX is present, CMswitch will read as 0x3 (on-board clock sources, off-board outputs disabled) switch(CMswitch){ // RF | Sample | Outputs case 0x0: // Off-Board | Off-Board | Off clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_OFF, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); xil_printf("\nClock config %d:\n RF: Off-board\n Samp: Off-board\n Off-board Outputs: Disabled\n\n", CMswitch); break; case 0x1: // Off-Board | On-Board | Off clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_OFF, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); xil_printf("\nClock config %d:\n RF: Off-board\n Samp: On-board\n Off-board Outputs: Disabled\n\n", CMswitch); break; case 0x2: // On-Board | On-Board | On clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); clk_config_dividers(CLK_BASEADDR, 1, CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR); clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_ONBOARD); xil_printf("\nClock config %d:\n RF: On-board\n Samp: On-board\n Off-board Outputs: Enabled\n\n", CMswitch); break; case 0x3: // On-Board | On-Board | Off clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_OFF, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_ONBOARD); //xil_printf("\nClock config %d:\n RF: On-board\n Samp: On-board\n Off-board Outputs: Disabled\n\n", CMswitch); break; } #ifdef WLAN_4RF_EN //Turn on clocks to FMC clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_FMC | CLK_RFREF_OUTSEL_FMC)); //FMC samp clock divider = 2 (40MHz sampling reference, same as on-board AD9963 ref clk) clk_config_dividers(CLK_BASEADDR, 2, CLK_SAMP_OUTSEL_FMC); //FMC RF ref clock divider = 2 (40MHz RF reference, same as on-board MAX2829 ref clk) clk_config_dividers(CLK_BASEADDR, 2, CLK_RFREF_OUTSEL_FMC); #endif //Initialize the AD9963 ADCs/DACs for on-board RF interfaces ad_init(AD_BASEADDR, AD_ALL_RF, 3); xil_printf("AD Readback: 0x%08x\n", ad_spi_read(AD_BASEADDR, RFA_AD_CS, 0x32)); if(status != XST_SUCCESS) { xil_printf("w3_node_init: Error in ad_init (%d)\n", status); ret = XST_FAILURE; } //Initialize the radio_controller core and MAX2829 transceivers for on-board RF interfaces status = radio_controller_init(RC_BASEADDR, RC_ALL_RF, 1, 1); if(status != XST_SUCCESS) { xil_printf("w3_node_init: Error in radioController_initialize (%d)\n", status); //Comment out allow boot even if an RF interfce doesn't lock (hack for debugging - not for reference release) ret = XST_FAILURE; } //Initialize the EEPROM read/write core iic_eeprom_init(EEPROM_BASEADDR, 0x64); #ifdef WLAN_4RF_EN iic_eeprom_init(FMC_EEPROM_BASEADDR, 0x64); #endif //Initialize the timer counter status = XTmrCtr_Initialize(TmrCtrInstancePtr, TMRCTR_DEVICE_ID); if (status != XST_SUCCESS) { xil_printf("w3_node_init: Error in XtmrCtr_Initialize (%d)\n", status); ret = XST_FAILURE; } // Set timer 0 to into a "count down" mode XTmrCtr_SetOptions(TmrCtrInstancePtr, 0, (XTC_DOWN_COUNT_OPTION)); //Give the PHY control of the red user LEDs (PHY counts 1-hot on SIGNAL errors) //Note: Uncommenting this line will make the RED LEDs controlled by hardware. //This will move the LEDs on PHY bad signal events //userio_set_ctrlSrc_hw(USERIO_BASEADDR, W3_USERIO_CTRLSRC_LEDS_RED); return ret; }
/** \brief Initializes the AD controller. This function must be called once at boot before any AD or RF operations will work \param baseaddr Base memory address of w3_ad_controller pcore \param clkdiv Clock divider for SPI serial clock (set to 3 for 160MHz bus) */ int ad_init(u32 baseaddr, u32 adSel, u8 clkdiv) { u32 rstMask, reg5c, reg72, reg5c_check, reg72_check; if((adSel & AD_CTRL_ALL_RF_CS) == 0) { print("ad_init: Invalid adSel parameter!\n"); return -1; } rstMask = 0; reg5c_check = 0; reg72_check = 0; if(adSel & RFA_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFA_AD_RESET; reg5c_check |= 0x00000008; reg72_check |= 0x00000001;} if(adSel & RFB_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFB_AD_RESET; reg5c_check |= 0x00000800; reg72_check |= 0x00000100;} if(adSel & RFC_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFC_AD_RESET; reg5c_check |= 0x00080000; reg72_check |= 0x00010000;} if(adSel & RFD_AD_CS) {rstMask |= ADCTRL_REG_CONFIG_MASK_RFD_AD_RESET; reg5c_check |= 0x08000000; reg72_check |= 0x01000000;} //Toggle AD resets (active low), Set SPI clock divider Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0); Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, (clkdiv & ADCTRL_REG_CONFIG_MASK_CLKDIV) | rstMask); //Toggle soft reset, set SDIO pin to bidirectional (only way to do SPI reads) ad_spi_write(baseaddr, (adSel), 0x00, 0xBD); //SDIO=1, LSB_first=0, reset=1 ad_spi_write(baseaddr, (adSel), 0x00, 0x99); //SDIO=1, LSB_first=0, reset=0 //Confirm the SPI ports are working //AD9963 reg5C should be 0x08 always, reg72 is 0x1 on boot reg5c = (ad_spi_read(baseaddr, (adSel), 0x5C))®5c_check; reg72 = (ad_spi_read(baseaddr, (adSel), 0x72))®72_check; if((reg5c != reg5c_check) || (reg72 != reg72_check)) { 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); print("Asserting AD9963 resets\n"); Xil_Out32(baseaddr + ADCTRL_REG_CONFIG, 0); return -1; } /* Default AD9963 configuration: -External ref resistor (NOTE: apparent datasheet bug!) -Full-duplex mode (Tx data on TXD port, Rx data on TRXD port) -Power up everything except: -DAC12A, DAC12B, AUXADC (all unconnected on PCB) -DLL -Clocking: -DLL disabled -ADC clock = DAC clock = ext clock (nominally 80MHz) -Tx path: -Data in 2's complement (NOTE: datasheet bug!) -TXCLK is input at TXD sample rate -TXD is DDR, I/Q interleaved, I first -Tx interpolation filters bypassed -Tx gains: -Linear gain set to 100% -Linear-in-dB gain set to -3dB -DAC RSET set to 100% -Tx DCO DACs: -Enabled, configured for [0,+2]v range -Set to mid-scale output (approx equal to common mode voltage of I/Q diff pairs) -Rx path: -Data in 2's complement (NOTE: datasheet bug!) -TRXCLK is output at TRXD sample rate -TRXD is DDR, I/Q interleaved, I first -Decimation filter bypassed -RXCML output enabled (used by ADC driver diff amp) -ADC common mode buffer off (required for DC coupled inputs) -Rx I path negated digitally (to match swap of p/n traces on PCB) */ //Power on/off blocks ad_spi_write(baseaddr, (adSel), 0x40, 0x00); //DAC12A, DAC12B off ad_spi_write(baseaddr, (adSel), 0x60, 0x00); //DLL off, everything else on ad_spi_write(baseaddr, (adSel), 0x61, 0x03); //LDOs on, AUXADC off //xil_printf("AD TEST: reg61=0x%08x\n", ad_spi_read(baseaddr, (adSel), 0x61)); //Clocking setup // [7]=0: ADCCLK=ext clk // [6]=0: DACCLK=ext clk // [4]=0: disable DLL input ref // [3:0]: DLL divide ratio (only 1, 2, 3, 4, 5, 6, 8 valid) ad_spi_write(baseaddr, (adSel), 0x71, 0x01); //DLL ref input off, ADCCLK=extCLK, DACCLK=extCLK, DLL_DIV=1 //Reference resistor selection // Datasheet says reg62[0]=0 for internal resistor, reg62[0]=1 for external resistor // But experimentally DAC currents are much more stable with temperature when reg62[0]=0 // I'm guessing this bit is flipped in the datasheet ad_spi_write(baseaddr, (adSel), 0x62, 0x00); //Clock disables and DCS // [7:3]=0: Enable internal clocks to ADCs/DACs // [1:0]=0: Set ADCCLK=ext clock (no division) // [2]=0: Enable ADC duty cycle stabilizer (recommended for ADC rates > 75MHz) ad_spi_write(baseaddr, (adSel), 0x66, 0x00); //Enable internal clocks, enable DCS //Aux DACs (Tx DC offset adjustment) // DAC10B=Q DCO, DAC10A=I DCO // DAC outputs update after LSB write (configured by reg40[0]) ad_spi_write(baseaddr, (adSel), 0x45, 0x88); //DAC10B on, full scale = [0,+2]v ad_spi_write(baseaddr, (adSel), 0x46, 0x80); //DAC10B data[9:2] ad_spi_write(baseaddr, (adSel), 0x47, 0x00); //DAC10B {6'b0, data[1:0]} ad_spi_write(baseaddr, (adSel), 0x48, 0x88); //DAC10A on, full scale = [0,+2]v ad_spi_write(baseaddr, (adSel), 0x49, 0x80); //DAC10A data[9:2] ad_spi_write(baseaddr, (adSel), 0x50, 0x00); //DAC10A {6'b0, data[1:0]} //ADC common mode buffer: disabled for DC coupled inputs ad_spi_write(baseaddr, (adSel), 0x7E, 0x01); //Spectral inversion // Invert RxI (reg3D[2]=1) to match PCB // TxI, TxQ also swapped on PCB, but ignored here since -1*(a+jb) is just one of many phase shifts the Tx signal sees ad_spi_write(baseaddr, (adSel), 0x3D, 0x04); //Invert RxI to match PCB (board swaps +/- for better routing) //Rx clock and data order/format // [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) // [0]=1 for 2's compliment (Datasheet says reg32[0]=0 for 2's compliment, but experiments show 0 is really straight-binary) ad_spi_write(baseaddr, (adSel), 0x32, 0x2B); //Full-duplex mode (DACs/TXD and ADCs/TRXD both active all the time) ad_spi_write(baseaddr, (adSel), 0x3F, 0x01); //FD mode //Tx data format (datasheet bug! reg[31][0] is flipped; 1=2's complement, 0=straight binary) //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)) ad_spi_write(baseaddr, (adSel), 0x31, 0x17); //Txdata=DDR two's complement, //Tx/Rx data paths ad_spi_write(baseaddr, (adSel), 0x30, 0x3F); //Bypass all rate change filters, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x37); //INT0 on, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x27); //INT0+INT1 on, enable Tx/Rx clocks // ad_spi_write(baseaddr, (adSel), 0x30, 0x23); //INT0+INT1+SRCC on, enable Tx/Rx clocks //ADC RXCML output buffer requires special register process (see AD9963 datasheet pg. 21 "sub serial interface communications") ad_spi_write(baseaddr, (adSel), 0x05, 0x03); //Address both ADCs ad_spi_write(baseaddr, (adSel), 0x0F, 0x02); //Enable RXCML output ad_spi_write(baseaddr, (adSel), 0x10, 0x00); //Set I/Q offset to 0 ad_spi_write(baseaddr, (adSel), 0xFF, 0x01); //Trigger ADC param update ad_spi_write(baseaddr, (adSel), 0x05, 0x00); //De-Address both ADCs //REFIO adjustment: set to default of 0.8v ad_spi_write(baseaddr, (adSel), 0x6E, 0x40); //Tx gains (it seems these registers default to non-zero, and maybe non-I/Q-matched values; safest to set them explicitly after reset) //I/Q GAIN1[5:0]: Fix5_0 value, Linear-in-dB, 0.25dB per bit // 0-25=>0dB-+6dB, 25-32:+6dB, 33-41:-6dB, 41-63=>-6dB-0dB ad_spi_write(baseaddr, (adSel), 0x68, 0); //IGAIN1 ad_spi_write(baseaddr, (adSel), 0x6B, 0); //QGAIN1 //I/Q GAIN2[5:0]: Fix5_0 value, Linear +/-2.5%, 0.08% per bit // 0:+0, 31:+max, 32:-max, 63:-0 ad_spi_write(baseaddr, (adSel), 0x69, 0); //IGAIN2 ad_spi_write(baseaddr, (adSel), 0x6C, 0); //QGAIN2 //I/Q RSET[5:0]: Fix5_0, Linear +/-20%, 0.625% per bit // 0:-0, 31:-max, 32:+max, 63:+0 ad_spi_write(baseaddr, (adSel), 0x6A, 0); //IRSET ad_spi_write(baseaddr, (adSel), 0x6D, 0); //QRSET //Digital output drive strengths: all 8mA ad_spi_write(baseaddr, (adSel), 0x63, 0x55); //2 bits each: TRXD TRXIQ TRXCLK TXCLK //Disable Tx and Rx BIST modes ad_spi_write(baseaddr, (adSel), 0x50, 0x00); //Tx BIST control ad_spi_write(baseaddr, (adSel), 0x51, 0x00); //Rx BIST control //Finally enable TX CLK output from ad_bridge // This is a register bit in the ad_controller, connected to the ad_bridge in the XPS project // that controls the OBUFT for the TXCLK output, to avoid drive fights pre-init Xil_Out32((baseaddr + ADCTRL_REG_CONFIG), (Xil_In32((baseaddr + ADCTRL_REG_CONFIG)) | ADCTRL_REG_CONFIG_TXCLK_OUT_EN)); return 0; }