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))&reg5c_check;
	reg72 = (ad_spi_read(baseaddr,  (adSel), 0x72))&reg72_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;
}